package simcu::otp.core /** * HOTP(HMAC-based One-Time Password, RFC 4226)。 * 公式:HOTP(K,C) = Truncate(HMAC-SHA1(K, C)) mod 10^digits */ public class Hotp { /// 生成一次性密码(默认 6 位)。counter 为 8 字节大端计数。 public static func generate(secret: Array, counter: Int64): String { generate(secret, counter, 6) } /// 生成一次性密码,指定位数。counter 为 8 字节大端计数。 public static func generate(secret: Array, counter: Int64, digits: Int64): String { // counter → 8 字节大端 var counterBytes = Array(8, repeat: 0) var c = counter var i = 7 while (i >= 0) { counterBytes[i] = UInt8(c & 0xff) c = c >> 8 i -= 1 } let hmac = HmacSha1(secret) let hs = hmac.compute(counterBytes) let offset = Int64(hs[19]) & 0x0f let binCode = ((Int64(hs[offset]) & 0x7f) << 24) | (Int64(hs[offset + 1]) << 16) | (Int64(hs[offset + 2]) << 8) | Int64(hs[offset + 3]) let mod = pow10(digits) let code = binCode % mod padLeft(code.toString(), digits) } private static func pow10(n: Int64): Int64 { var r: Int64 = 1 var i = 0 while (i < n) { r *= 10 i += 1 } r } private static func padLeft(s: String, width: Int64): String { var r = s while (r.size < width) { r = "0" + r } r } }