基于散列消息验证码的一次性密码算法

基于HMAC的一次性密码算法（，HOTP）是一种基于散列消息验证码（HMAC）的一次性密码（OTP）算法，同时也是开放验证提案的基础（OATH）。

HOTP在2005年由IETF发布在RFC 4226标准文档中，定义了算法的同时附带有一例基于Java的实现。自此之后，世界上许多公司接纳了HOTP算法，它也成为了可以自由使用的开源标准。

算法 
HOTP算法在验证时使用对称生成的人类可读密码（算法中也称为“运算值”），每个密码仅在一次验证中有效。这种一次性属性直接来自于计数器数值的单次使用。

任何有意愿使用HOTP的一方都必须定义一系列对于验证器而言已明确的参数，无论是否这些参数是否可以被验证器接受，都可能包含：

 加密散列算法，记为H（默认为SHA-1）
 服务器分发的密钥，记为K，可以是任意长度的字符串，但必须保密。
 HOTP运算值长度，记为d，（数值从6至10均可，默认为6，建议为6至8）

HOTP验证中的双方都会运算最终数值，验证器会将本地运算结果与被验证方提供的结果进行比对。

由于验证双方都分别各自自增计数器数值，验证器的计数器可能落后于被验证方，因此定义用于重新同步的协议是意义的。RFC4226标准中给出了相关建议，但并不做强制要求。为解决可能的不同步，验证器会使用当前计数器值起尝试用连续多个值（称为窗口）进行运算，如果验证成功，则验证器将对应的计数器值更新为当前值，无需被验证方做任何操作。窗口大小由参数指定。

上述建议是出于避免HOTP数值验证过程中出现卡死，但是同时也使得数值相对偏小，暴力破解攻击也因此变得容易。有一个建议是在验证失败几次之后就锁定验证过程，或者每次验证失败时都（以线性方式）延长下次操作时间。

专有的硬件密钥通常会提供六位验证码，也就是d的默认值。密钥截留最长长度为31位，由于公式中  ≈ 9.3，也就是d的最大值取10，因此第十位数值可能的取值也就更少，为0、1或者2（也就对应多出的0.3这个小数数值）。

双重重定向验证 
在验证之后，验证器可以通过运算得出下一个HOTP数值来执行验算，得出返回的数值之后，接受验证的一方将计算HOTP数值进行验证。值得注意的是，这一过程中的计数器数值需要保证同步。

HOTP运算值 
HOTP运算值设计上是人类可读的，它是一个位数为d的十进制数字（开头的0不可忽略）：

 HOTP运算值 = HOTP(K, C) mod 10d

上述公式也说明运算值是最小位数为d的十进制数值。

HOTP本质上是散列消息验证码（HMAC）的计数器截留，计数器数值记为C（服务器分发密钥记为K，散列函数记为H）。

 HOTP(K, C) = truncate(HMAC(K, C))

截取首先是位于最小的四位MAC数值当中，截得之后用于作为回调数值，记为i。

 truncate(MAC) = extract31(MAC, MAC[(19 × 8) + 4:(19 × 8) + 7] × 8)

引索数值i用于从MAC当中选中31位数，起始点为i + 1。

 extract31(MAC, i) = MAC[i + 1:i + (4 × 8) − 1]

注意，取得的31位数长度仅有单位值的4字节，可以容纳于4字节短语意味着没有标记位。这是出于避免负数的模数运算，但是这也有许多不同的定义与实现。

密钥 
已有许多供应商（参考下面的列举）提供软件以及硬件密钥。实现开放验证提案的HOTP硬件密钥实现往往比使用专有算法的同类产品便宜许多。 截止至2010年，这些硬件密钥的价格已经很低。 有些产品也可以同时用于生成强密码。

软件密钥在（几乎）所有主要移动平台或智能手机均有提供（如J2ME, Android, iPhone, BlackBerry, Maemo, macOS, 以及Windows Mobile）。

接纳程度 
尽管许多计算机媒体在2004-05年持负面态度，自从IETF在2005年十二月将HOTP接纳为RFC 4226标准之后，许多提供商开始生产兼容HOTP的密钥，甚至是基于此的整套验证解决方案。

2010年（高德纳咨询公司的下辖部门）发表文章，表示“高德纳咨询公司预期基于硬件的一次性密码的市场会有一定程度的增长，对于智能手机来说，一次性密码将会成为默认硬件平台的一部分”。

另请参阅 
 开放验证提案
 S/KEY
 基于时间的一次性密码算法

参考资料

外部链接 
 RFC4226: HOTP: An HMAC-Based One-Time Password Algorithm 
 [rfc:6287 RFC 6287: OCRA: An HMAC-Based OATH Challenge-Response Algorithm]
 Initiative For Open Authentication 
 Step by step Python implementation in a Jupyter Notebook 

计算机访问控制协议
密码算法
网际协议
有未审阅翻译的页面