otp-cj

仓颉(Cangjie)一次性密码(OTP)库,实现 HOTP(RFC 4226)与 TOTP(RFC 6238),提供 Base32 密钥编解码(RFC 4648)与 otpauth:// URI 生成,可直接与 Google Authenticator / Microsoft Authenticator 等常见 OTP App 互通。

组织:simcu | 包名:otp | 版本:1.0.0

特性

  • TOTP:基于时间步长的动态码,默认 6 位 / 30 秒步长,支持自定义位数、步长与时钟偏移窗口
  • HOTP:基于计数器的一次性密码,默认 6 位
  • Base32:RFC 4648 无填充变体编解码,解码时忽略大小写、空格、短横线与 =
  • otpauth:// URI:生成可用于二维码的认证链接,并附带随机密钥生成工具
  • 零三方依赖:仅依赖仓颉标准库 stdx.crypto.digest.SHA1
  • 密钥支持 Base32 字符串(Google Authenticator 惯例)与原始字节两种传入方式

安装

cjpm.toml 中声明依赖(可改用中央仓版本号):

[dependencies]
"simcu::otp" = { path = "../../cangjie/otp-cj" }
# 或发布到中央仓后:"simcu::otp" = "1.0.0"

代码中引用:

import simcu::otp.core.*

快速开始

1. 生成密钥并绑定 OTP

// 生成随机 Base32 密钥(默认 20 字节 = 160 bit)
let secret = OtpUri.generateSecret()        // 例如 "N6C5HNT2F633..."

// 生成 otpauth:// 链接,前端渲染成二维码供用户绑定
let uri = OtpUri.build(issuer: "GamePlatform", account: "18600127718", secretBase32: secret)
// otpauth://totp/GamePlatform%3A18600127718?secret=N6C5HNT2F633...&issuer=GamePlatform&algorithm=SHA1&digits=6&period=30

2. 生成动态码

// TOTP:默认 6 位 / 30s 步长,基于当前时间
let code = Totp.generate(secret)            // "351047"

// 自定义位数与步长
let code2 = Totp.generate(secret, digits: 8, period: 60)

// 原始字节密钥
let raw = Base32.decode(secret)
let code3 = Totp.generateBytes(raw)

// HOTP:基于计数器(第 N 次动态码)
let hotp = Hotp.generate(raw, counter: 0)   // 默认 6 位
let hotp2 = Hotp.generate(raw, counter: 0, digits: 8)

3. 校验动态码

// TOTP 校验:默认允许前后各 1 个时间步的时钟偏移
let ok = Totp.verify(secret, code)          // Bool

// 放宽/收紧偏移窗口
let ok2 = Totp.verify(secret, code, digits: 6, period: 30, window: 2)

// 字节密钥版本
let ok3 = Totp.verifyBytes(raw, code)

4. Base32 编解码

let raw = Base32.decode("N6C5HNT2F633")     // 忽略空格/短横线/大小写
let encoded = Base32.encode(raw)            // 往返后还原 "N6C5HNT2F633"

API 参考

包结构:simcu::otp.core

类型 说明
Base32 RFC 4648 无填充 Base32 编解码;encode(data) / decode(input),非法字符抛 IllegalArgumentException
HmacSha1 RFC 2104 / 4226 HMAC-SHA1(blockSize=64);init(key) / compute(data)
Hotp RFC 4226;generate(secret, counter[, digits])
Totp RFC 6238;generate/generateAt/generateBytes(..., digits, period)verify/verifyBytes(..., digits, period, window) 多种重载;默认 period=30window=1
OtpUri generateSecret([bytes]) 随机密钥;build(issuer, account, secret[, digits, period]) 生成 otpauth:// 链接

常用常量:

  • Totp.DEFAULT_PERIOD = 30
  • Totp.DEFAULT_T0 = 0

说明

  • HMAC-SHA1 为何自实现:仓颉标准库 std.crypto.digest 的 HMAC 目前仅支持 SHA512,而 HOTP/TOTP 标准基于 HMAC-SHA1,因此本库基于 stdx.crypto.digest.SHA1 实现了 RFC 2104 的 HMAC-SHA1。
  • 兼容性:Totp.generate / Totp.verify 的结果与 Google Authenticator 完全一致,可交叉验证。
  • 服务端校验建议:绑定阶段将 Base32 secret 持久化到账号表(otp_secret 字段);校验时用 Totp.verify(secret, code)

运行测试

cjpm test

测试覆盖:Base32 编解码往返、HOTP/TOTP 与 RFC 官方测试向量的对拍、时钟偏移窗口、otpauth:// URI 生成等。

配合 SimApi 使用

在 simapi 后端中作为账号二次验证通道:

// AuthCodeService 示例:绑定 OTP 的账号优先接受 TOTP 动态码
let otpOk = Totp.verify(acc.otpSecret, code)
S
Description
仓颉OTP库
Readme
42 KiB
Languages
Cangjie 100%