重构:core 子包合并到根包,移除 simcu::otp.core 间接层
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package simcu::otp
|
||||
|
||||
import std.random.*
|
||||
import std.convert.*
|
||||
|
||||
/**
|
||||
* otpauth:// URI 生成(Google Authenticator / 常见 OTP App 标准)。
|
||||
* 格式:otpauth://totp/{issuer}:{account}?secret={base32}&issuer={issuer}&algorithm=SHA1&digits=6&period=30
|
||||
* 同时提供随机 Base32 secret 生成工具。
|
||||
*/
|
||||
public class OtpUri {
|
||||
/// 生成随机 Base32 secret(默认 20 字节 = 160 bit,与 RFC 4226 密钥长度一致)
|
||||
public static func generateSecret(): String {
|
||||
generateSecret(20)
|
||||
}
|
||||
|
||||
/// 生成指定字节数的随机 Base32 secret
|
||||
public static func generateSecret(bytes: Int64): String {
|
||||
let rnd = Random()
|
||||
let data = Array<Byte>(bytes, repeat: 0)
|
||||
for (i in 0..bytes) {
|
||||
data[i] = UInt8(rnd.nextUInt64() & 0xff)
|
||||
}
|
||||
Base32.encode(data)
|
||||
}
|
||||
|
||||
/// 生成 otpauth:// 链接(默认 6 位 / 30s 步长)
|
||||
public static func build(issuer: String, account: String, secretBase32: String): String {
|
||||
build(issuer, account, secretBase32, 6, 30)
|
||||
}
|
||||
|
||||
/// 生成 otpauth:// 链接(指定位数)
|
||||
public static func build(issuer: String, account: String, secretBase32: String, digits: Int64): String {
|
||||
build(issuer, account, secretBase32, digits, 30)
|
||||
}
|
||||
|
||||
/// 生成 otpauth:// 链接(指定位数与步长)
|
||||
public static func build(issuer: String, account: String, secretBase32: String, digits: Int64, period: Int64): String {
|
||||
let label = issuer + ":" + account
|
||||
var sb = StringBuilder()
|
||||
sb.append("otpauth://totp/")
|
||||
sb.append(urlEncode(label))
|
||||
sb.append("?secret=")
|
||||
sb.append(secretBase32)
|
||||
sb.append("&issuer=")
|
||||
sb.append(urlEncode(issuer))
|
||||
sb.append("&algorithm=SHA1&digits=")
|
||||
sb.append(digits.toString())
|
||||
sb.append("&period=")
|
||||
sb.append(period.toString())
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
private static func urlEncode(s: String): String {
|
||||
var sb = StringBuilder()
|
||||
for (ch in s) {
|
||||
match (ch) {
|
||||
case ':' => sb.append("%3A")
|
||||
case '/' => sb.append("%2F")
|
||||
case ' ' => sb.append("%20")
|
||||
case _ => sb.append(Rune(ch))
|
||||
}
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user