@
huntzhan 简单原理:
1. 服务器生成一个 secret ,
2. OTP 客户端根据 secret ,与一个计数器生成 HMAC-SHA1 摘要的 hash ,如果是 TOTP ,计数器就是时间戳 / 30 ,所以我们通常会看到 TOTP 的 code 每 30 秒变化一次。hash 只有拥有 secret 的人才能生成出来,所以也就保证了安全性。
3. 由于 hash 太长,不利于输入,所以通常会把 hash 转换为 6 位的数字,方便用户输入。转换方法为:取摘要结果最后一个字节的低 4 位,作为偏移值,然后以该偏移值为下标,从摘要中取从下标为该偏移值开始的 4 个字节,把这几个字节的内容转换为数字。然后把数字转换为 6 位字符串,不足 6 位,前面补 0 。
```
let hash = algorithm.digest(&secret, &counter.to_be_bytes())?;
let offset: usize = (hash[hash.len() - 1] & 0xf) as usize;
let binary = ((hash[offset] as u64) & 0x7f) << 24
| ((hash[offset + 1] as u64) & 0xff) << 16
| ((hash[offset + 2] as u64) & 0xff) << 8
| ((hash[offset + 3] as u64) & 0xff);
let mut token = (binary % 10_u64.pow(digits)).to_string();
while token.len() < (digits as usize) {
token = format!("0{}", token);
}
```
完整代码可以参考这里:
https://github.com/nashaofu/anyotp/blob/master/src/utils.rs相关 RFC 参考:
https://datatracker.ietf.org/doc/html/rfc4226#section-5.1