Skip to content

Instantly share code, notes, and snippets.

@aristotle9
Created January 1, 2014 12:27
Show Gist options
  • Save aristotle9/8207686 to your computer and use it in GitHub Desktop.
Save aristotle9/8207686 to your computer and use it in GitHub Desktop.
Clojure Server of Google Authenticator; Java version: https://github.com/wstrange/GoogleAuth
(ns xxx.totp
(:import org.apache.commons.codec.binary.Base32))
(def ^:const seed "QPA2GEXU3NNZUFKJL3NLZMC5YCA6UGSVDA3TFBJDXCZTQXYGYCYNKKWNU3IQG657CRWNUKNZGA3I2CLBUZYD3K55YZYQ====")
(def ^:const secret-size 10)
(def ^:const random-number-algorithm "SHA1PRNG")
(def ^:const window-size 3)
(defn new-secret
[]
(let [sr (doto
(java.security.SecureRandom/getInstance random-number-algorithm)
(.setSeed (.decode (Base32.) seed)))
sec (.generateSeed sr secret-size)]
(java.lang.String. (.encode (Base32.) sec))))
(defn qrbarcode-url
[user host secret]
(format
"https://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s"
user
host
secret))
(defn verify-code
"raw-key byte[] timestamp-of-ms-div-30000 当前时间戳/ms除以30000"
[raw-key timestamp-of-ms-div-30000]
(let [data (byte-array 8)
value timestamp-of-ms-div-30000
data (loop [i 7
value value]
(if (>= i 0)
(do
(aset-byte data i (let [b (bit-and 0xff value)]
(if (> b 127)
(byte (- b 256))
(byte b))));;cast long to signed byte
(recur (dec i) (bit-shift-right value 8)))
data))
key-spec (javax.crypto.spec.SecretKeySpec. raw-key "HmacSHA1")
mac (javax.crypto.Mac/getInstance "HmacSHA1")
hash-bytes (do
(.init mac key-spec)
(.doFinal mac data))
offset (bit-and 0xf
(aget hash-bytes (dec 20)))
truncated-hash
(loop [truncated-hash 0
i 0]
(if (< i 4)
(let [truncated-hash (bit-shift-left truncated-hash 8)
truncated-hash (bit-or truncated-hash
(bit-and 0xff
(aget hash-bytes (+ offset i))))]
(recur truncated-hash (inc i)))
truncated-hash))
truncated-hash (bit-and truncated-hash 0x7fffffff)
truncated-hash (rem truncated-hash 1000000)]
(int truncated-hash)))
(defn check-code
"timestamp/ms"
([^String secret ^String code timestamp]
(let [code (Integer. code);;convert to int, in clojure zero-leading numeric literal is octal
raw-key (.decode (Base32.) secret)
t (long (/ timestamp 30000))
delta (reduce #(into %1 [(- %2) %2]) [0] (range 1 (inc window-size)))]
(loop [[i & xs] delta]
(if (nil? i)
false
(if (= code (verify-code raw-key (+ i t)))
true
(recur xs))))))
([^String secret ^String code]
(check-code secret code (.getTime (java.util.Date.)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment