关于OTP的两个RFC

[RFC4226]HOTP: An HMAC-Based One-Time Password Algorithm

[RFC6238]TOTP: Time-Based One-Time Password Algorithm

符号定义

符号 描述
C 计数器值(8字节)。计数器值必须在服务端和客户端之间同步
K 服务端和客户端之间的共享密钥。每个客户端拥有唯一的共享密钥
T 次数限制参数。如果用户请求超过T次,服务端拒绝服务
s 重新同步参数。服务端尝试验证s个连续的计数器值
Digit 数字个数

生成HOTP

Step 1

生成SHA-1散列值,长度为20字节

HS = HMAC-SHA-1(K,C)

Step 2

生成4字节的动态截取串

Sbits = DT(HS)

DT(String) // String = String[0]...String[19]
     Let OffsetBits be the low-order 4 bits of String[19]
     Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15
     Let P = String[OffSet]...String[OffSet+3]
     Return the Last 31 bits of P

Step 3

计算HOTP值

Snum = StToNum(Sbits)
// 将二进制串转换为整数(0..2^{31}-1)

D = Snum mod 10^Digit

Digit值应该大于等于7

验证HOTP

客户端自增计数器,生成HOTP值;

服务端收到该值,首先自增计数器,然后计算对比;

如果不匹配,执行重新同步协议,也就是将计数器继续向前试s次;

如果重新同步失败,服务器请求再次认证;

如果验证次数超过T次,那么锁住账号,通知用户。

安全性考虑

对HOTP最可能的攻击方式是暴力破解,所以对sT的选择需要考虑。

攻击成功的概率可以形式化为:

Sec = sT/10^Digit

所以sT值不能太大,Digit值至少为7。

另外为了保护用户隐私,以及防止重放攻击,安全的信道也是必须的,比如HTTPS。

共享密钥管理

确定性生成

通过主密钥生成共享密钥,在需要的时候实时生成。

好处是可以避免暴露共享密钥的风险;缺点是如果主密钥泄露,共享密钥也就暴露了。

随机生成

随机生成密钥,立即持久化存储,以备之后使用。

安全的存储和使用共享密码非常重要。

生成TOTP

TOTP是HOTP的一个变种,也就是用当前时间做为计数器。

TOTP = HOTP(K, T)

T = (Current Unix Time - T0) / X
此处的X表示时间步长

另外HMAC-SHA-1可以考虑更新为HMAC-SHA-256或HMAC-SHA-512。

验证TOTP

服务端收到客户端发来的TOTP值,立即根据当前服务端的时间计算TOTP值,然后进行匹配。

由于存在网络传输的延迟,对于服务端来说客户端发来的值可能是上一个时间窗的,所以我们应该设置一个可以接受的延迟时间窗,我们建议最多允许一个延迟时间窗。

时间步长的选择要综合考虑安全性和可用性,这里建议30秒。

另外,一个时间窗的OTP值一旦验证成功,那么对于该时间窗之后的验证尝试必须拒绝。