otp-cj OTP 库:HOTP/TOTP 实现(RFC 4226/6238)、Base32 编解码、otpauth URI 生成及 README
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
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<Byte>, counter: Int64): String {
|
||||
generate(secret, counter, 6)
|
||||
}
|
||||
|
||||
/// 生成一次性密码,指定位数。counter 为 8 字节大端计数。
|
||||
public static func generate(secret: Array<Byte>, counter: Int64, digits: Int64): String {
|
||||
// counter → 8 字节大端
|
||||
var counterBytes = Array<Byte>(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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user