Skip to main content

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]