# 2FA 与 OTP

## 2FA (Two Factor Authentication)

![alt text](/files/e66eIzWee0TpnbZStOsM)

![alt text](/files/IM9ye4n7Ol5E3D6bD6j6)

![alt text](/files/cp09kQ414mMSaTA8uApv)

## OTP (One-Time Password) 生成

**核心原理** OTP 算法基于共享密钥（Secret Key）和动态因子（如时间或计数器）生成一次性密码。算法流程如下：

1. 首选计算 `HMAC = HMAC_SHA(Secret Key, Dynamic Factor)`

### HOTP (HMAC-Based One-Time Password)

TOTP (Time-Based One-Time Password Algorithm)

`HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))`

```
Example of HOTP Computation for Digit = 6

   The following code example describes the extraction of a dynamic
   binary code given that hmac_result is a byte array with the HMAC-
   SHA-1 result:

        int offset   =  hmac_result[19] & 0xf ;
        int bin_code = (hmac_result[offset]  & 0x7f) << 24
           | (hmac_result[offset+1] & 0xff) << 16
           | (hmac_result[offset+2] & 0xff) <<  8
           | (hmac_result[offset+3] & 0xff) ;

   SHA-1 HMAC Bytes (Example)

   -------------------------------------------------------------
   | Byte Number                                               |
   -------------------------------------------------------------
   |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|
   -------------------------------------------------------------
   | Byte Value                                                |
   -------------------------------------------------------------
   |1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|
   -------------------------------***********----------------++|




M'Raihi, et al.              Informational                      [Page 7]

RFC 4226                     HOTP Algorithm                December 2005


   * The last byte (byte 19) has the hex value 0x5a.
   * The value of the lower 4 bits is 0xa (the offset value).
   * The offset value is byte 10 (0xa).
   * The value of the 4 bytes starting at byte 10 is 0x50ef7f19,
     which is the dynamic binary code DBC1.
   * The MSB of DBC1 is 0x50 so DBC2 = DBC1 = 0x50ef7f19 .
   * HOTP = DBC2 modulo 10^6 = 872921.

   We treat the dynamic binary code as a 31-bit, unsigned, big-endian
   integer; the first byte is masked with a 0x7f.

   We then take this number modulo 1,000,000 (10^6) to generate the 6-
   digit HOTP value 872921 decimal.
```

![HOTP](/files/D37bhsByS1qmAOZOkqVb)

图片来源：<https://notes.mengxin.science/2017/05/30/hotp-totp-algorithm-analysis/>

## TOTP (Time-Base One-Time Password)

TOTP 算法作为 HOTP 算法的拓展算法

![alt text](/files/3GBCU2Tg3lWCqQTnHoa2)

图片来源：<https://notes.mengxin.science/2017/05/30/hotp-totp-algorithm-analysis/>

## OTP 校验

### HOTP 校验

同步 counter 步骤

1. 生成 OTP（客户端和服务器端的共同步骤） 客户端请求 OTP 时，使用当前 counter（开始时为 0 或其他初始化值）来生成 OTP。 客户端生成 OTP 后，将 counter 和 OTP 一起发送到服务器。
2. 服务器验证 OTP 服务器接收到 OTP 和 counter 后，检查其与预期的 OTP 是否匹配。 如果匹配，则验证成功，递增 counter 并处理该请求。 如果不匹配，服务器可以尝试通过增加或减少 counter 的值（例如，counter + 1, counter - 1）进行验证，以容忍可能的同步误差。
3. 误差窗口 服务器可能会有一个误差窗口（例如，counter = N, counter = N-1, counter = N+1 等），允许一定范围内的计数器误差。 通过验证多个可能的计数器值，系统可以容忍一些同步问题，确保 OTP 校验的准确性。
4. 客户端和服务器的同步 如果服务器校验失败，并且未能在误差窗口内找到有效的 OTP，客户端需要重新生成 OTP 或者请求新的 OTP。 确保客户端和服务器都更新并同步 counter，防止 OTP 重复使用。

### TOTP 校验

TOTP 的核心公式是将时间戳（秒级）通过除法离散化：

counter = current\_time // time\_step

* current\_time 是从 Unix 纪元（1970-01-01 00:00:00 UTC）起的当前时间戳（以秒为单位）。
* time\_step 是固定的时间间隔，通常为 30 秒。

假设时间戳为 1，时间步长为 30 秒：

在第 1 到 29 时间步内，客户端和服务器的计数器值都是 0 。

**问题1：时间不同步导致校验失败**

如果客户端和服务器的时间有偏差（例如 5 秒偏差），生成的 OTP 会基于不同的 counter，从而导致校验失败。 客户端在时间戳 29 时生成的 OTP，服务器在时间戳 34 校验时可能失败，因为它会只验证计数器 1，而不是计数器 0。

**问题2：严格绑定时间步导致用户体验差** 用户需要在单一时间步内使用 OTP。如果用户生成 OTP（如在时间戳 29）后，延迟输入到服务器（如在时间戳 31），OTP 校验就会失败。 这对于实际操作来说过于严格，容易让用户体验变差。

**解决方案1：引入时间窗口** 在 verify\_totp 函数中，通过在校验时检查当前时间步的前后时间步（T-1, T, T+1），可以引入时间窗口容忍。这是 TOTP 标准推荐的做法。

**解决方案2：显式 counter 校验** 通过显式传递 counter 的方式，客户端和服务器可以协商如何处理时间步，例如允许客户端显式发送自己生成 OTP 时使用的计数器值，服务器据此判断是否接近当前时间步，从而进行更宽松的校验。

## OTP 安全问题

![alt text](/files/Gfnuvle2zYa2L3wS4hoQ)

图片来源：<https://notes.mengxin.science/2017/05/30/hotp-totp-algorithm-analysis/>

## 示例

![alt text](/files/gh62ky5sXlsd9jysUdxH)

二维码识别内容：`otpauth://totp/JumpServer:admin?secret=LZEKNECB4XSNKUD4&issuer=JumpServer`

## 相关参考

1. [RFC4266 (HOTP: An HMAC-Based One-Time Password Algorithm)](https://datatracker.ietf.org/doc/html/rfc4226)
2. [RFC6238 (TOTP: Time-Based One-Time Password Algorithm)](https://datatracker.ietf.org/doc/html/rfc6238)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.youngseaz.com/notes/00-wang-luo-an-quan/mi-ma-xue/2fa-yu-otp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
