Appendix A. Examples (示例)
Appendix A. Examples (示例)
A.1. Detailed Example (详细示例)
我们在此详细说明在示例消息和密钥上生成 k 期间获得的中间值。我们使用二进制曲线, 因为该特定曲线是标准的, 并且具有不是 8 的倍数的群阶长度 (qlen); 这说明了如何在整数和比特序列之间执行转换的详细细节。
A.1.1. Key Pair (密钥对)
我们考虑 [FIPS-186-4] 中描述的曲线 K-163 上的 ECDSA (在 [X9.62] 中也称为 "ansix9t163k1")。曲线定义在域 GF(2^163) 上: 域元素编码为 163 比特字符串。常规基点的阶是素数值:
q = 0x4000000000000000000020108A2E0CC0D99F8A5EF
其长度为 qlen = 163 比特。
我们的私钥是:
x = 0x09A4D6792295A7F730FC3F2B49CBC0F62E862272F
相应的公钥是曲线点 U = xG。该点有两个坐标, 它们是域 GF(2^163) 的元素。这些元素可以使用 [X9.62] 第 A.5.6 节中描述的过程转换为整数, 产生两个公共点坐标:
Ux = 0x79AEE090DB05EC252D5CB4452F356BE198A4FF96F
Uy = 0x782E29634DDC9A31EF40386E896BAA18B53AFA5A3
A.1.2. Generation of k (k 的生成)
我们要签名的消息 (在本例中, 这是 ASCII 字符串 "sample") 首先使用哈希函数 SHA-256 进行哈希。这产生以下 256 比特的输出 (下面显示的值是十六进制编码):
h1 = SHA-256("sample")
= AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF
请注意, qlen = 163, 而哈希输出长度是 hlen = 256。rlen 是 qlen 向上舍入到下一个 8 的倍数, 因此 rlen = 168。
我们首先通过 bits2octets 运行 h1。这首先通过 bits2int 提取最左边的 qlen = 163 比特, 然后对 q 取模约简, 产生:
h1' = bits2int(h1) mod q
= 0x01795EDF0D54DB760F156D0DAC04C0322B3A204224
然后 h1' 通过 int2octets 编码为 rlen = 168 比特的序列 (21 个八位字节):
bits2octets(h1) = 01795EDF0D54DB760F156D0DAC04C0322B3A204224
根据第 3.2 节中的算法:
步骤 b: V 初始化为包含 32 个值为 0x01 的八位字节的序列 (hlen = 256, 因此 8*ceil(hlen/8) = 256, 产生 32 个八位字节)。
步骤 c: K 初始化为包含 32 个值为 0x00 的八位字节的序列。
步骤 d: 使用密钥 K 和消息 V || 0x00 || int2octets(x) || bits2octets(h1) 计算 HMAC。连接的长度为:
- V: 32 个八位字节
- 0x00: 1 个八位字节
- int2octets(x): 21 个八位字节 (rlen = 168 比特)
- bits2octets(h1): 21 个八位字节
总计 75 个八位字节。生成的 HMAC 输出 (32 个八位字节) 成为 K 的新值:
K = 42D6D3D99099E78B9188E297C9ECB244796FA0B5FF0D49902DC1EE0E7A4FA496
步骤 e: V 更新为 HMAC_K(V):
V = 26F8910FB1A1E7341E3D7E041F7BDF0939E4343A02EA9E36C6FFF2C8DDB3DA8F
步骤 f: K 再次更新, 使用 0x01 而不是 0x00:
K = FB630D677CA87FE7C8C8BC50EC4444AA2C1BD1AF5BD79C69293AD8F0AE9F1F6D
步骤 g: V 再次更新:
V = 90DF5B0C7B518F9E3073CAE0655F55D2450FF05EE21F5BA8A2A443ACDC816BE7
步骤 h: 生成候选 k 值。我们生成 T, 直到其长度达到至少 qlen = 163 比特。由于 V 的长度为 256 比特, 一次迭代就足够了:
V = HMAC_K(V)
= 67C57C9059CAC58C54FB9F4D21D8071BC1EB398BC5D0C3FF077E6A8C8E653476
T = V
从 T 中提取 k:
k = bits2int(T)
= 0x33BEAE6AC9A55D768C92AFE0AB952CAE42AFA2304A
该值在 [1, q-1] 范围内, 因此是可接受的。
A.1.3. Signature (签名)
使用 k 值, 我们计算点 kG。该点的 X 坐标, 转换为整数并对 q 取模约简, 产生:
r = 0x113A63990598A3828C407C0F4D2438D990DF99A7F
然后我们计算:
s = (h + x*r) / k mod q
= 0x1313A2E03F5412DDB296A22E2C455335545672D9F
签名是对 (r, s)。
A.2. Test Vectors (测试向量)
以下小节包含使用各种曲线和哈希函数的测试向量。对于每个组合, 我们提供由相同私钥产生的几个签名, 应用于不同的消息。在所有情况下, 消息本身都是 ASCII 字符串 "sample" 和 "test"。
测试向量的格式:
- 曲线参数
- 私钥 (十六进制)
- 公钥坐标 (十六进制)
- 对于每条消息:
- 消息内容
- 使用的哈希函数
- 生成的 k 值 (十六进制)
- 签名值 r 和 s (十六进制)
注意: 完整的测试向量包含在 RFC 6979 的附录 A.2 中, 涵盖了以下组合:
- A.2.1. DSA, 1024 Bits
- A.2.2. DSA, 2048 Bits
- A.2.3. ECDSA, 192 Bits (Prime Field)
- A.2.4. ECDSA, 224 Bits (Prime Field)
- A.2.5. ECDSA, 256 Bits (Prime Field)
- A.2.6. ECDSA, 384 Bits (Prime Field)
- A.2.7. ECDSA, 521 Bits (Prime Field)
- A.2.8. ECDSA, 163 Bits (Binary Field, Koblitz Curve)
- A.2.9. ECDSA, 233 Bits (Binary Field, Koblitz Curve)
- A.2.10. ECDSA, 283 Bits (Binary Field, Koblitz Curve)
- A.2.11. ECDSA, 409 Bits (Binary Field, Koblitz Curve)
- A.2.12. ECDSA, 571 Bits (Binary Field, Koblitz Curve)
- A.2.13. ECDSA, 163 Bits (Binary Field, Pseudorandom Curve)
- A.2.14. ECDSA, 233 Bits (Binary Field, Pseudorandom Curve)
- A.2.15. ECDSA, 283 Bits (Binary Field, Pseudorandom Curve)
- A.2.16. ECDSA, 409 Bits (Binary Field, Pseudorandom Curve)
- A.2.17. ECDSA, 571 Bits (Binary Field, Pseudorandom Curve)
由于测试向量包含大量十六进制数据, 建议参考原始 RFC 6979 文档以获取完整的测试向量值。
A.3. Sample Code (示例代码)
RFC 6979 的原始文档包含示例实现代码的引用。实现者应参考:
- RFC 6979 原文 获取完整的测试向量
- HMAC 实现: RFC 2104
- HMAC_DRBG 实现: NIST SP 800-90A
实现时应特别注意:
- 正确实现 bits2int 和 bits2octets 转换
- 确保 HMAC 计算正确
- 验证生成的 k 值在有效范围内
- 使用提供的测试向量验证实现
作者地址:
Thomas Pornin
Email: [email protected]