add docs and test data
This commit is contained in:
339
docs/ML-KEM算法详解.md
Executable file
339
docs/ML-KEM算法详解.md
Executable file
@@ -0,0 +1,339 @@
|
||||
# ML-KEM (FIPS 203) 算法详解
|
||||
|
||||
---
|
||||
|
||||
## 一、定位与层次
|
||||
|
||||
ML-KEM 是一种**后量子密钥封装机制**(KEM),基于 Module Learning With Errors(MLWE)困难问题。整个算法分两层:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ML-KEM 层 (§6-7) │
|
||||
│ KeyGen / Encaps / Decaps (Alg 19-21) │
|
||||
│ + 内部接口 (Alg 16-18, CAVP 测试用) │
|
||||
│ └─ FO 变换: G, H, J 哈希 + 隐式拒绝 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ K-PKE 层 (§5) │
|
||||
│ KeyGen / Encrypt / Decrypt (Alg 13-15) │
|
||||
│ └─ 格密码: A·s+e 噪声线性系统 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ 数学基础 (§4) │
|
||||
│ 环 R_q = Z_q[x]/(x²⁵⁶+1), q=3329 │
|
||||
│ NTT, 采样 (均匀/CBD), 编解码, 压缩 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ SHA-3 哈希层 (FIPS 202) │
|
||||
│ KECCAK-p[1600,24], SHA3-256/512, SHAKE128/256│
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、数学基础
|
||||
|
||||
### 2.1 环 `R_q = Z_q[x]/(x^256 + 1)`
|
||||
|
||||
| 参数 | 值 |
|
||||
|------|-----|
|
||||
| 模数 `q` | **3329**(12 bit 素数) |
|
||||
| 多项式次数 `n` | **256** |
|
||||
| 环定义 | 系数在 `{0,1,...,3328}` 中,乘法模 `x^256+1` |
|
||||
|
||||
### 2.2 NTT(数论变换)
|
||||
|
||||
因为 `x^256 + 1` 在 `Z_q` 上分解为 128 个二次因式:
|
||||
|
||||
```
|
||||
x^256 + 1 = Π_{i=0}^{127} (x² − ζ^{2·BitRev₇(i)+1})
|
||||
```
|
||||
|
||||
- **NTT**(正变换):系数表示 → NTT 域表示(128 对 (a₀, a₁))
|
||||
- **INTT**(逆变换):NTT 域 → 系数域
|
||||
- **MultiplyNTTs**:NTT 域中乘法 = 128 次独立的一次多项式乘法(Algorithm 12 `BaseCaseMultiply`)
|
||||
|
||||
```
|
||||
NTT 域中: 多项式乘法 = O(128) 次 BaseCaseMultiply
|
||||
系数域中: 多项式乘法 = O(256×256) 次模乘 ← NTT 加速
|
||||
```
|
||||
|
||||
### 2.3 采样分布
|
||||
|
||||
| 算法 | 分布 | 说明 |
|
||||
|------|------|------|
|
||||
| **SampleNTT** (Alg 7) | 均匀分布 | SHAKE128 可扩展输出 + 拒绝采样 |
|
||||
| **SamplePolyCBD** (Alg 8) | 中心二项分布 CBD_η | 从 `64η` 字节比特串计算:`x = Σ(b_{2iη+j} − b_{2iη+η+j})`,结果 mod q |
|
||||
|
||||
CBD_η 参数:
|
||||
|
||||
| η | 每系数使用比特数 | 输出范围 | PRF 输出 |
|
||||
|---|---------------|---------|---------|
|
||||
| 2 | 4 bit | `[-2, 2]` → mod q 后 `{3327,3328,0,1,2}` | 128 字节 |
|
||||
| 3 | 6 bit | `[-3, 3]` → mod q 后 `{3326,...,0,...,3}` | 192 字节 |
|
||||
|
||||
CBD 的核心特性:输出值集中在 0 附近(小噪声),且无需高斯采样(硬件友好)。
|
||||
|
||||
### 2.4 编码与压缩
|
||||
|
||||
| 算法 | 功能 | 域映射 |
|
||||
|------|------|--------|
|
||||
| ByteEncode_d / ByteDecode_d | 整数数组 ↔ 字节数组 | `Z_m^256 ↔ B^{32d}`(d=12 时 m=q,否则 m=2^d)|
|
||||
| Compress_d / Decompress_d | 压缩 / 解压 | `Z_q ↔ Z_{2^d}`,舍入到最近整数,**禁止浮点** |
|
||||
|
||||
压缩的数学定义:
|
||||
|
||||
```
|
||||
Compress_d(x) = ⌈(2^d / q) · x⌋ mod 2^d
|
||||
Decompress_d(y) = ⌈(q / 2^d) · y⌋
|
||||
```
|
||||
|
||||
关键性质:
|
||||
|
||||
```
|
||||
Compress_d(Decompress_d(y)) = y (对所有 y ∈ Z_{2^d})
|
||||
Decompress_d(Compress_d(x)) ≈ x (误差很小,当 d 接近 12)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、K-PKE 层(非对称加密,不可单独使用)
|
||||
|
||||
核心思想:**带噪声的线性方程组**。
|
||||
|
||||
```
|
||||
私钥: s ∈ R_q^k (k 个秘密多项式)
|
||||
公钥: (A, t = A·s + e)
|
||||
└─ A ∈ R_q^{k×k}: 伪随机生成(种子 ρ + XOF)
|
||||
└─ e ∈ R_q^k: 噪声(CBD 采样)
|
||||
```
|
||||
|
||||
### Algorithm 13: `K-PKE.KeyGen(d)` — 密钥生成
|
||||
|
||||
```
|
||||
输入: 随机种子 d (32 字节)
|
||||
输出: ek_PKE (384k+32 B), dk_PKE (384k B)
|
||||
|
||||
Step 1: (ρ, σ) ← G(d || k) G = SHA3-512, 域分离用 k
|
||||
Step 3-7: Â[i][j] ← SampleNTT(ρ||j||i) × k² 次
|
||||
└─ Â 是 NTT 域中的矩阵(已在 NTT 域)
|
||||
Step 8-11: s[i] ← SamplePolyCBD_η1(PRF_η1(σ, N++)) × k 次
|
||||
Step 12-15: e[i] ← SamplePolyCBD_η1(PRF_η1(σ, N++)) × k 次
|
||||
Step 16-17: ŝ ← NTT(s), ê ← NTT(e)
|
||||
Step 18: t̂ ← Â ∘ ŝ + ê 矩阵乘 + 加噪声(全在 NTT 域)
|
||||
Step 19-20: 序列化:ByteEncode₁₂(t̂) || ρ → ek
|
||||
ByteEncode₁₂(ŝ) → dk
|
||||
```
|
||||
|
||||
### Algorithm 14: `K-PKE.Encrypt(ek, m, r)` — 加密
|
||||
|
||||
```
|
||||
输入: ek, 明文 m (32B), 随机数 r (32B)
|
||||
输出: 密文 c
|
||||
|
||||
Step 2-3: 从 ek 解析 t̂ 和 ρ
|
||||
Step 4-8: 用 ρ 重生成 Â(与 KeyGen 相同)
|
||||
Step 9-12: y[i] ← CBD_η1(PRF_η1(r, N++)) × k
|
||||
Step 13-16: e1[i] ← CBD_η2(PRF_η2(r, N++)) × k
|
||||
Step 17: e2 ← CBD_η2(PRF_η2(r, N++))
|
||||
Step 18: ŷ ← NTT(y)
|
||||
Step 19: u ← NTT⁻¹(Âᵀ ∘ ŷ) + e1 注意:Â 转置!
|
||||
Step 20: μ ← Decompress₁(ByteDecode₁(m)) 明文编码为 Z_q 多项式
|
||||
Step 21: v ← NTT⁻¹(t̂ᵀ ∘ ŷ) + e2 + μ
|
||||
Step 22-23: c₁ ← ByteEncode_du(Compress_du(u))
|
||||
c₂ ← ByteEncode_dv(Compress_dv(v))
|
||||
Step 24: c ← c₁ || c₂
|
||||
```
|
||||
|
||||
**为什么加密能解密?**
|
||||
|
||||
```
|
||||
解密计算: v' − sᵀ·u'
|
||||
= (tᵀ·y + e2 + μ) − sᵀ·(Aᵀ·y + e1)
|
||||
= (A·s + e)ᵀ·y + e2 + μ − sᵀ·Aᵀ·y − sᵀ·e1
|
||||
= sᵀ·Aᵀ·y + eᵀ·y + e2 + μ − sᵀ·Aᵀ·y − sᵀ·e1
|
||||
= μ + (eᵀ·y + e2 − sᵀ·e1)
|
||||
≈ μ ← 噪声项很小,四舍五入恢复
|
||||
```
|
||||
|
||||
### Algorithm 15: `K-PKE.Decrypt(dk, c)` — 解密
|
||||
|
||||
```
|
||||
Step 5: ŝ ← ByteDecode₁₂(dk)
|
||||
Step 6: w ← v' − NTT⁻¹(ŝᵀ ∘ NTT(u'))
|
||||
Step 7: m ← ByteEncode₁(Compress₁(w))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、ML-KEM 层(CCA 安全 KEM)
|
||||
|
||||
在 K-PKE(CPA 安全)基础上,通过 **Fujisaki-Okamoto 变换** 升级为 CCA 安全。
|
||||
|
||||
### 所用哈希函数
|
||||
|
||||
| 函数 | 底层 | 用途 |
|
||||
|------|------|------|
|
||||
| **G** | SHA3-512 | KeyGen: `(ρ,σ)←G(d‖k)`;Encaps: `(K,r)←G(m‖H(ek))`;Decaps: `(K',r')←G(m'‖h)` |
|
||||
| **H** | SHA3-256 | `H(ek)` 存在 dk 中用于验证;Encaps 中参与 G 输入 |
|
||||
| **J** | SHAKE256(_, 256) | **隐式拒绝**:`K̄←J(z‖c)`,当重加密密文不匹配时使用 |
|
||||
|
||||
### Algorithm 16: `KeyGen_internal(d, z)` — 内部密钥生成
|
||||
|
||||
```
|
||||
dk_PKE ← K-PKE 的解密密钥 (384k B)
|
||||
ek_PKE ← K-PKE 的加密密钥 (384k+32 B)
|
||||
h ← H(ek_PKE) 32 B
|
||||
z ← 随机 (32 B, 隐式拒绝用)
|
||||
|
||||
dk = dk_PKE || ek_PKE || h || z 共 768k+96 B
|
||||
ek = ek_PKE 共 384k+32 B
|
||||
```
|
||||
|
||||
### Algorithm 17/20: `Encaps(ek)` — 封装
|
||||
|
||||
```
|
||||
Step 1: m ←$ B³² 随机 32 字节
|
||||
Step 2: (K, r) ← G(m || H(ek)) K = 共享密钥, r = 加密随机数
|
||||
Step 3: c ← K-PKE.Encrypt(ek, m, r)
|
||||
Output: (K, c)
|
||||
```
|
||||
|
||||
### Algorithm 18/21: `Decaps(dk, c)` — 解封装
|
||||
|
||||
```
|
||||
Step 1-4: 解析 dk → dk_PKE, ek_PKE, h, z
|
||||
Step 5: m' ← K-PKE.Decrypt(dk_PKE, c)
|
||||
Step 6: (K', r') ← G(m' || h)
|
||||
Step 7: K̄ ← J(z || c) 隐式拒绝密钥
|
||||
Step 8: c' ← K-PKE.Encrypt(ek_PKE, m', r')
|
||||
Step 9-11: if c ≠ c' then K' ← K̄ 隐式拒绝!
|
||||
Output: K'
|
||||
```
|
||||
|
||||
**隐式拒绝的作用**:如果攻击者篡改了密文:
|
||||
|
||||
- 解密得到 `m' ≠ m`(噪声导致)
|
||||
- 重加密得到 `c' ≠ c`
|
||||
- 返回 `J(z || c)` 而非 `G(m' || h)`
|
||||
- 攻击者**不知道**解密失败(无显式错误),无法利用侧信道
|
||||
|
||||
**通信流程(7 步)**:
|
||||
|
||||
```
|
||||
Alice Bob
|
||||
───── ───
|
||||
key_gen() → (ek, dk)
|
||||
──── ek ────▶
|
||||
encaps(ek) → (K, ct_kem)
|
||||
xor_crypt(K, "hello world") → encrypted
|
||||
◀─ (ct_kem, encrypted) ──
|
||||
decaps(dk, ct_kem) → K'
|
||||
xor_crypt(K', encrypted) → "hello world" ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、参数集
|
||||
|
||||
| 参数 | ML-KEM-512 | ML-KEM-768 | ML-KEM-1024 |
|
||||
|------|-----------|-----------|-------------|
|
||||
| **k**(矩阵维度) | 2 | 3 | 4 |
|
||||
| **η₁**(s, e, y 噪声) | 3 | 2 | 2 |
|
||||
| **η₂**(e1, e2 噪声) | 2 | 2 | 2 |
|
||||
| **du**(u 压缩) | 10 | 10 | 11 |
|
||||
| **dv**(v 压缩) | 4 | 4 | 5 |
|
||||
| NIST 安全类别 | 1 (≈AES-128) | 3 (≈AES-192) | 5 (≈AES-256) |
|
||||
|
||||
### 密钥与密文大小
|
||||
|
||||
| 对象 | ML-KEM-512 | ML-KEM-768 | ML-KEM-1024 |
|
||||
|------|-----------|-----------|-------------|
|
||||
| ek(公钥) | 800 B | 1184 B | 1568 B |
|
||||
| dk(私钥) | 1632 B | 2400 B | 3168 B |
|
||||
| c(密文) | 768 B | 1088 B | 1568 B |
|
||||
| K(共享密钥) | 32 B | 32 B | 32 B |
|
||||
|
||||
---
|
||||
|
||||
## 六、输入检查(FIPS 203 §7)
|
||||
|
||||
### Encaps(§7.2)— 封装密钥检查
|
||||
|
||||
```
|
||||
1. 类型检查: ek 长度 = 384k+32
|
||||
2. 模数检查: ByteEncode₁₂(ByteDecode₁₂(ek[0:384k])) == ek[0:384k]
|
||||
确保编码的值在 [0, q-1] 范围内,无模归约
|
||||
```
|
||||
|
||||
### Decaps(§7.3)— 解封装输入检查
|
||||
|
||||
```
|
||||
1. 密文类型检查: c 长度 = 32(du·k + dv)
|
||||
2. 私钥类型检查: dk 长度 = 768k+96
|
||||
3. 哈希检查: H(dk[384k:768k+32]) == dk[768k+32:768k+64]
|
||||
确保 dk 中的 ek_PKE 未被篡改
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、Const Generic 参数映射
|
||||
|
||||
| Rust 参数 | 含义 | 512 | 768 | 1024 |
|
||||
|-----------|------|-----|-----|------|
|
||||
| `K` | 矩阵维度 | 2 | 3 | 4 |
|
||||
| `ETA_BYTES` | 64·η₁ | 192 | 128 | 128 |
|
||||
| `ETA2_BYTES` | 64·η₂ | 128 | 128 | 128 |
|
||||
| `DU` | u 压缩 | 10 | 10 | 11 |
|
||||
| `DV` | v 压缩 | 4 | 4 | 5 |
|
||||
|
||||
```rust
|
||||
// Rust 调用示例
|
||||
ml_kem::key_gen::<2, 192>(&d, &z); // ML-KEM-512
|
||||
ml_kem::encaps::<2, 192, 128, 10, 4>(&ek, &m); // ML-KEM-512
|
||||
ml_kem::decaps::<2, 192, 128, 10, 4>(&dk, &c); // ML-KEM-512
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、算法依赖关系图
|
||||
|
||||
```
|
||||
Algorithm 19 key_gen ──────────────┐
|
||||
Algorithm 20 encaps ───────────────┤
|
||||
Algorithm 21 decaps ───────────────┤
|
||||
▼
|
||||
Algorithm 16 keygen_internal ──────┐
|
||||
Algorithm 17 encaps_internal ──────┤
|
||||
Algorithm 18 decaps_internal ──────┤
|
||||
▼
|
||||
Algorithm 13 K-PKE.KeyGen ─────────┐
|
||||
Algorithm 14 K-PKE.Encrypt ────────┤
|
||||
Algorithm 15 K-PKE.Decrypt ────────┤
|
||||
▼
|
||||
Algorithm 7 SampleNTT ────────────┐
|
||||
Algorithm 8 SamplePolyCBD ────────┤
|
||||
Algorithm 9 NTT ──────────────────┤
|
||||
Algorithm 10 INTT ─────────────────┤
|
||||
Algorithm 11 MultiplyNTTs ─────────┤
|
||||
Algorithm 12 BaseCaseMultiply ─────┤
|
||||
Algorithm 5 ByteEncode ───────────┤
|
||||
Algorithm 6 ByteDecode ───────────┤
|
||||
Algorithm 1 Compress ─────────────┤
|
||||
Algorithm 2 Decompress ───────────┤
|
||||
Algorithm 3 BitsToBytes ──────────┤
|
||||
Algorithm 4 BytesToBits ──────────┤
|
||||
▼
|
||||
G / H / J / PRF (SHA-3)
|
||||
│
|
||||
KECCAK-p[1600,24]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、安全性概要
|
||||
|
||||
| 攻击类型 | K-PKE (CPA) | ML-KEM (CCA) |
|
||||
|----------|------------|-------------|
|
||||
| 选择明文攻击 (CPA) | ✓ 安全 | ✓ 安全 |
|
||||
| 选择密文攻击 (CCA) | ✗ 不安全 | ✓ 安全(FO 变换) |
|
||||
| 量子攻击 | 基于 MLWE 困难假设 | 同上 + FO 在 QROM 下安全 |
|
||||
|
||||
**MLWE 安全性**:区分 `(A, A·s+e)` 和 `(A, u)`(u 均匀随机)的计算困难度 ≈ 格上最短向量问题(SVP),已知无高效量子算法。
|
||||
BIN
docs/NIST.FIPS.202_SHA3哈希函数.pdf
Executable file
BIN
docs/NIST.FIPS.202_SHA3哈希函数.pdf
Executable file
Binary file not shown.
BIN
docs/NIST.FIPS.203_ML-KEM密钥封装.pdf
Executable file
BIN
docs/NIST.FIPS.203_ML-KEM密钥封装.pdf
Executable file
Binary file not shown.
376
docs/哈希函数文档.md
Executable file
376
docs/哈希函数文档.md
Executable file
@@ -0,0 +1,376 @@
|
||||
# ML-KEM 哈希函数实现文档
|
||||
|
||||
---
|
||||
|
||||
## 一、整体架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ ML-KEM 专用函数层 │
|
||||
│ mlkem_G mlkem_H mlkem_J mlkem_PRF(_eta2/_eta3) │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│ 调用
|
||||
┌──────────────────────────▼───────────────────────────────────┐
|
||||
│ 一次性便捷函数层 │
|
||||
│ sha3_256 sha3_512 shake128<N> shake256<N> │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│ 调用
|
||||
┌──────────────────────────▼───────────────────────────────────┐
|
||||
│ HashGenerator<RATE_BYTES, D_BYTES> │
|
||||
│ 海绵构造:process() → finish() → 输出 │
|
||||
│ 填充规则:SHA("01"后缀) / SHAKE("1111"后缀) │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│ 底层
|
||||
┌──────────────────────────▼───────────────────────────────────┐
|
||||
│ KECCAK-p[1600, 24] 置换 │
|
||||
│ θ → ρ → π → χ → ι × 24轮 → 状态数组 [[u64;5];5] │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、底层:KECCAK-p[1600, 24] 置换
|
||||
|
||||
对应 FIPS 202 §3。状态为 5×5×64 位三维数组 `[[u64; 5]; 5]`。
|
||||
|
||||
### 五步映射(每轮一轮,共 24 轮)
|
||||
|
||||
| 步映射 | 函数 | 作用 |
|
||||
|--------|------|------|
|
||||
| **θ** (theta) | `theta(A)` | 列奇偶校验扩散:每列 XOR 相邻两列的奇偶校验值 |
|
||||
| **ρ** (rho) | `rho(A)` | 通道旋转:每条通道按固定偏移量循环移位 |
|
||||
| **π** (pi) | `pi(A)` | 通道重排:`A[x][y] ← A[(x+3y) mod 5][x]` |
|
||||
| **χ** (chi) | `chi(A)` | 非线性变换:`A[x][y] ← A[x][y] ⊕ (¬A[x+1][y] ∧ A[x+2][y])` |
|
||||
| **ι** (iota) | `iota(A, ir)` | 轮常数注入:`A[0][0] ⊕= RC[ir]` |
|
||||
|
||||
### 轮常数 RC[24](16 进制)
|
||||
|
||||
| ir | RC[ir] |
|
||||
|----|--------|
|
||||
| 0 | `0x0000000000000001` |
|
||||
| 1 | `0x0000000000008082` |
|
||||
| 2 | `0x800000000000808A` |
|
||||
| 3 | `0x8000000080008000` |
|
||||
| 4 | `0x000000000000808B` |
|
||||
| 5 | `0x0000000080000001` |
|
||||
| 6 | `0x8000000080008081` |
|
||||
| 7 | `0x8000000000008009` |
|
||||
| 8 | `0x000000000000008A` |
|
||||
| 9 | `0x0000000000000088` |
|
||||
| 10 | `0x0000000080008009` |
|
||||
| 11 | `0x000000008000000A` |
|
||||
| 12 | `0x000000008000808B` |
|
||||
| 13 | `0x800000000000008B` |
|
||||
| 14 | `0x8000000000008089` |
|
||||
| 15 | `0x8000000000008003` |
|
||||
| 16 | `0x8000000000008002` |
|
||||
| 17 | `0x8000000000000080` |
|
||||
| 18 | `0x000000000000800A` |
|
||||
| 19 | `0x800000008000000A` |
|
||||
| 20 | `0x8000000080008081` |
|
||||
| 21 | `0x8000000000008080` |
|
||||
| 22 | `0x0000000080000001` |
|
||||
| 23 | `0x8000000080008008` |
|
||||
|
||||
---
|
||||
|
||||
## 三、海绵构造(Sponge Construction)
|
||||
|
||||
对应 FIPS 202 §4。
|
||||
|
||||
```
|
||||
吸收阶段 挤压阶段
|
||||
┌───┬───┬───┐ ┌───┬───┬───┐
|
||||
│P0 │P1 │Pn │ 填充+吸收 │Z0 │Z1 │...│ 输出
|
||||
└─┬─┴─┬─┴─┬─┘ └─┬─┴─┬─┴───┘
|
||||
│ │ │ ╲ ╱ │
|
||||
▼ ▼ ▼ ╲ ╱ ▼
|
||||
┌──────────┐ ┌──────────────┐
|
||||
│ 状态 1600│◄──►│ 状态 1600 │
|
||||
│ (全零) │ f │ │
|
||||
└──────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
- **宽度 b** = 1600 位(固定)
|
||||
- **速率 r** = 1600 − c(每种哈希不同,见下表)
|
||||
- **容量 c** = 2 × 输出长度(SHA-3 固定输出)/ 2 × 安全强度(SHAKE)
|
||||
- **核心置换 f** = KECCAK-p[1600, 24]
|
||||
|
||||
### HashGenerator\<RATE_BYTES, D_BYTES\> 核心方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `new(padding_type)` | 创建哈希器,状态初始化为零 |
|
||||
| `process(data)` | 吸收数据,每满 RATE_BYTES 字节执行一次 Keccak-p |
|
||||
| `finish()` | 填充最后一组数据,执行 Keccak-p,挤压输出 |
|
||||
| `get_hash_bytes()` | 获取结果字节数组引用(需先调用 finish,否则 panic) |
|
||||
| `get_hex_string()` | 获取结果十六进制字符串(需先调用 finish,否则 panic) |
|
||||
| `compute_bytes(data)` | 便捷一次性方法:process → finish → 返回字节数组 → 清零状态 |
|
||||
| `compute_hex_string(data)` | 同上,但返回十六进制字符串 |
|
||||
| `clear()` | 清零状态和缓冲区(复用前调用) |
|
||||
|
||||
---
|
||||
|
||||
## 四、填充规则与域分离
|
||||
|
||||
对应 FIPS 202 §5.1(pad10\*1)和 Table 6。
|
||||
|
||||
两种 `PaddingType` 内含域分离后缀:
|
||||
|
||||
| PaddingType | 域分离后缀 | 用途 | q=1 时 | q>1 时 |
|
||||
|-------------|-----------|------|--------|--------|
|
||||
| **SHA** | `01` (2 bit) | SHA3-224/256/384/512 | `0x86` | `0x06 ... 0x80` |
|
||||
| **SHAKE** | `1111` (4 bit) | SHAKE128/256 | `0x9F` | `0x1F ... 0x80` |
|
||||
|
||||
> `q = RATE_BYTES − buffer_pos`,即当前块剩余可填充字节数。
|
||||
> 域分离后缀 "01" 和 "1111" 已预计算合并到填充字节中,符合 FIPS 202 Table 6。
|
||||
|
||||
---
|
||||
|
||||
## 五、SHA-3 / SHAKE 参数对照表
|
||||
|
||||
| 函数 | 速率 r (位) | 速率 r (字节) | 容量 c (位) | 输出 d (位/字节) | 填充类型 |
|
||||
|------|------------|--------------|------------|-----------------|---------|
|
||||
| SHA3-224 | 1152 | 144 | 448 | 224b / 28B | SHA |
|
||||
| **SHA3-256** | 1088 | **136** | 512 | 256b / **32B** | SHA |
|
||||
| SHA3-384 | 832 | 104 | 768 | 384b / 48B | SHA |
|
||||
| **SHA3-512** | 576 | **72** | 1024 | 512b / **64B** | SHA |
|
||||
| **SHAKE128** | 1344 | **168** | 256 | 任意 (d) | SHAKE |
|
||||
| **SHAKE256** | 1088 | **136** | 512 | 任意 (d) | SHAKE |
|
||||
|
||||
> 加粗的为 ML-KEM 实际使用的函数。
|
||||
|
||||
---
|
||||
|
||||
## 六、工厂函数(构造 HashGenerator 实例)
|
||||
|
||||
```rust
|
||||
// ===== SHA-3 固定输出 =====
|
||||
pub fn get_sha3_224() -> HashGenerator<144, 28> // PaddingType::SHA
|
||||
pub fn get_sha3_256() -> HashGenerator<136, 32> // ← ML-KEM H 底层
|
||||
pub fn get_sha3_384() -> HashGenerator<104, 48>
|
||||
pub fn get_sha3_512() -> HashGenerator<72, 64> // ← ML-KEM G 底层
|
||||
|
||||
// ===== SHAKE 可变输出 =====
|
||||
pub fn get_shake_128<const D_BYTES>() -> HashGenerator<168, D_BYTES> // ← SampleNTT 底层
|
||||
pub fn get_shake_256<const D_BYTES>() -> HashGenerator<136, D_BYTES> // ← ML-KEM J, PRF 底层
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、一次性便捷函数
|
||||
|
||||
无需手动 `process()` → `finish()` → `get_hash_bytes()`,所有均标记 `#[must_use]`:
|
||||
|
||||
```rust
|
||||
pub fn sha3_256(data: &[u8]) -> [u8; 32]
|
||||
|
||||
pub fn sha3_512(data: &[u8]) -> [u8; 64]
|
||||
|
||||
pub fn shake128<const N: usize>(data: &[u8]) -> [u8; N]
|
||||
|
||||
pub fn shake256<const N: usize>(data: &[u8]) -> [u8; N]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、ML-KEM (FIPS 203) 专用哈希函数
|
||||
|
||||
### 8.1 函数总览
|
||||
|
||||
| 函数 | FIPS 203 定义 | 底层实现 | 输入 | 输出 |
|
||||
|------|--------------|---------|------|------|
|
||||
| **G** | §6.2, §5.1 | `SHA3-512` | 变长字节串 | `([u8; 32], [u8; 32])` |
|
||||
| **H** | §6.1 | `SHA3-256` | 变长字节串 | `[u8; 32]` |
|
||||
| **J** | §6.3 | `SHAKE256(_, 256)` | 变长字节串 | `[u8; 32]` |
|
||||
| **PRF(η=2)** | §4.1 | `SHAKE256(σ ‖ N, 128)` | σ: 32B + nonce: 1B | `[u8; 16]` |
|
||||
| **PRF(η=3)** | §4.1 | `SHAKE256(σ ‖ N, 192)` | σ: 32B + nonce: 1B | `[u8; 24]` |
|
||||
| **PRF(泛型)** | §4.1 | `SHAKE256(σ ‖ N, η·64)` | σ: 32B + nonce: 1B | `[u8; ETA_BYTES]` |
|
||||
|
||||
### 8.2 函数签名
|
||||
|
||||
```rust
|
||||
// G: SHA3-512 → 拆分为两个32字节值 (ρ, σ) 或 (K, r)
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_G(input: &[u8]) -> ([u8; 32], [u8; 32])
|
||||
|
||||
// H: SHA3-256 → 32字节哈希值
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_H(input: &[u8]) -> [u8; 32]
|
||||
|
||||
// J: SHAKE256 → 32字节 (用于隐式拒绝)
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_J(input: &[u8]) -> [u8; 32]
|
||||
|
||||
// PRF_η2: SHAKE256(σ || N, 128位=16字节)
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_PRF_eta2(sigma: &[u8; 32], nonce: u8) -> [u8; 16]
|
||||
|
||||
// PRF_η3: SHAKE256(σ || N, 192位=24字节)
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_PRF_eta3(sigma: &[u8; 32], nonce: u8) -> [u8; 24]
|
||||
|
||||
// PRF 泛型: SHAKE256(σ || N, η·64位=ETA_BYTES字节)
|
||||
#[must_use]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn mlkem_PRF<const ETA_BYTES: usize>(sigma: &[u8; 32], nonce: u8) -> [u8; ETA_BYTES]
|
||||
```
|
||||
|
||||
### 8.3 在 ML-KEM 算法中的具体用途
|
||||
|
||||
**密钥生成 `K-PKE.KeyGen` (Algorithm 13):**
|
||||
|
||||
```
|
||||
(ρ, σ) ← G(d || k) ← G 输出64字节 → 前32B=ρ、后32B=σ
|
||||
Â[i][j] ← SampleNTT(ρ||j||i) ← 均匀分布采样,内部用 SHAKE128
|
||||
s[i] ← SamplePolyCBDη1(PRFη1(σ, N)) ← PRF 生成 CBD 采样比特串
|
||||
e[i] ← SamplePolyCBDη1(PRFη1(σ, N)) ← PRF 生成 CBD 采样比特串
|
||||
```
|
||||
|
||||
**封装 `ML-KEM.Encaps_internal` (Algorithm 17):**
|
||||
|
||||
```
|
||||
(K, r) ← G(m || H(ek)) ← H 先哈希 ek,再 G 生成共享密钥 K 和随机数 r
|
||||
c ← K-PKE.Encrypt(ek, m, r)
|
||||
```
|
||||
|
||||
**解封装 `ML-KEM.Decaps_internal` (Algorithm 18):**
|
||||
|
||||
```
|
||||
(K', r') ← G(m' || h) ← G 生成候选 K' 和随机数 r'
|
||||
K̄ ← J(z || c) ← J 用于隐式拒绝时的备选共享密钥
|
||||
```
|
||||
|
||||
**加密 `K-PKE.Encrypt` (Algorithm 14):**
|
||||
|
||||
```
|
||||
Â[i][j] ← SampleNTT(ρ||j||i) ← SHAKE128
|
||||
y[i] ← SamplePolyCBDη1(PRFη1(r, N)) ← PRF 生成 CBD 采样比特串
|
||||
e1[i] ← SamplePolyCBDη2(PRFη2(r, N)) ← PRF 生成 CBD 采样比特串
|
||||
e2 ← SamplePolyCBDη2(PRFη2(r, N)) ← PRF 生成 CBD 采样比特串
|
||||
```
|
||||
|
||||
### 8.4 参数集与 η 值对应
|
||||
|
||||
| 参数集 | k | η₁ | η₂ | PRF(η₁) 输出 | PRF(η₂) 输出 | 安全强度 |
|
||||
|--------|---|----|----|-------------|-------------|---------|
|
||||
| ML-KEM-512 | 2 | 3 | 2 | 24 字节 (192b) | 16 字节 (128b) | 128 位 |
|
||||
| ML-KEM-768 | 3 | 2 | 2 | 16 字节 (128b) | 16 字节 (128b) | 192 位 |
|
||||
| ML-KEM-1024 | 4 | 2 | 2 | 16 字节 (128b) | 16 字节 (128b) | 256 位 |
|
||||
|
||||
---
|
||||
|
||||
## 九、当前 sample.rs 中的使用
|
||||
|
||||
`sample.rs` 实现了 `SampleNTT`(均匀分布采样),对应 FIPS 203 Algorithm 7。
|
||||
|
||||
**当前使用方式**(手动三步):
|
||||
|
||||
```rust
|
||||
let mut hasher = get_shake_128::<1024>();
|
||||
hasher.process(b.as_bytes());
|
||||
hasher.finish();
|
||||
let bytes = hasher.get_hash_bytes();
|
||||
// 然后从 bytes 中进行拒绝采样...
|
||||
```
|
||||
|
||||
**可升级为一次性调用:**
|
||||
|
||||
```rust
|
||||
let bytes = shake128::<1024>(b.as_bytes());
|
||||
// 更简洁,自动完成 process + finish + get_hash_bytes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、公开接口汇总
|
||||
|
||||
```rust
|
||||
// === 类型 ===
|
||||
pub type State = [[u64; 5]; 5]; // 1600位状态数组
|
||||
pub enum PaddingType { SHA, SHAKE } // 填充类型(含域分离后缀)
|
||||
pub struct HashGenerator<RATE_BYTES, D_BYTES> { .. }
|
||||
|
||||
// === 核心方法 (HashGenerator) ===
|
||||
impl HashGenerator<RATE_BYTES, D_BYTES> {
|
||||
pub fn new(padding_type: PaddingType) -> Self
|
||||
pub fn clear(&mut self)
|
||||
pub fn process(&mut self, data: &[u8])
|
||||
pub fn finish(&mut self)
|
||||
pub fn get_hash_bytes(&self) -> &[u8; D_BYTES] // 注意:未 finish 会 panic
|
||||
pub fn get_hex_string(&self) -> String // 注意:未 finish 会 panic
|
||||
pub fn compute_hex_string(&mut self, data: &[u8]) -> String
|
||||
pub fn compute_bytes(&mut self, data: &[u8]) -> [u8; D_BYTES] // 新增
|
||||
}
|
||||
|
||||
// === 工厂函数 ===
|
||||
pub fn get_sha3_224() -> HashGenerator<144, 28>
|
||||
pub fn get_sha3_256() -> HashGenerator<136, 32>
|
||||
pub fn get_sha3_384() -> HashGenerator<104, 48>
|
||||
pub fn get_sha3_512() -> HashGenerator<72, 64>
|
||||
pub fn get_shake_128<const D: usize>() -> HashGenerator<168, D>
|
||||
pub fn get_shake_256<const D: usize>() -> HashGenerator<136, D>
|
||||
|
||||
// === 一次性便捷函数 (新增) ===
|
||||
pub fn sha3_256(data: &[u8]) -> [u8; 32] // #[must_use]
|
||||
pub fn sha3_512(data: &[u8]) -> [u8; 64] // #[must_use]
|
||||
pub fn shake128<const N: usize>(data: &[u8]) -> [u8; N] // #[must_use]
|
||||
pub fn shake256<const N: usize>(data: &[u8]) -> [u8; N] // #[must_use]
|
||||
|
||||
// === ML-KEM (FIPS 203) 专用函数 (新增) ===
|
||||
pub fn mlkem_G (input: &[u8]) -> ([u8; 32], [u8; 32]) // #[must_use]
|
||||
pub fn mlkem_H (input: &[u8]) -> [u8; 32] // #[must_use]
|
||||
pub fn mlkem_J (input: &[u8]) -> [u8; 32] // #[must_use]
|
||||
pub fn mlkem_PRF_eta2 (sigma: &[u8; 32], nonce: u8) -> [u8; 16] // #[must_use]
|
||||
pub fn mlkem_PRF_eta3 (sigma: &[u8; 32], nonce: u8) -> [u8; 24] // #[must_use]
|
||||
pub fn mlkem_PRF<const ETA_BYTES: usize> (sigma: &[u8; 32], nonce: u8) -> [u8; ETA_BYTES]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、测试覆盖
|
||||
|
||||
共 35 项测试全部通过(`cargo test` 0 警告 0 失败):
|
||||
|
||||
| 分类 | 测试数 | 覆盖内容 |
|
||||
|------|--------|---------|
|
||||
| 标准 SHA-3 向量 | 3 | SHA3-256 空串/"abc"、SHA3-512 空串 |
|
||||
| SHAKE 标准向量 | 1 | SHAKE128 空串 → 32 字节 |
|
||||
| 多次 process | 1 | `process("ab") + process("c") == process("abc")` |
|
||||
| compute_bytes | 1 | 验证 `compute_bytes` 返回值 |
|
||||
| 一次性函数正确性 | 5 | sha3_256/512、shake128/256 与手动方式对比 |
|
||||
| ML-KEM G | 3 | 输出拆分正确性、确定性、不同输入不同输出 |
|
||||
| ML-KEM H | 2 | 等于 sha3_256、确定性 |
|
||||
| ML-KEM J | 2 | 输出 32 字节、确定性 |
|
||||
| ML-KEM PRF | 5 | 输出尺寸、不同 nonce 产生不同输出、泛型一致性、确定性 |
|
||||
| 边界情况 | 2 | 空输入、4096 字节大输入 |
|
||||
|
||||
运行方式:
|
||||
```sh
|
||||
cargo test # 运行全部 35 项测试
|
||||
cargo test sha3 # 仅运行哈希模块测试
|
||||
cargo build # 编译(0 警告)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、关键设计说明
|
||||
|
||||
1. **域分离(Domain Separation)**:SHA-3 在消息后自动追加 2 位 `01`,SHAKE 追加 4 位 `1111`。此操作已在 `PaddingType` 的填充字节中预计算合并(`0x86`/`0x06...0x80` 对应 SHA,`0x9F`/`0x1F...0x80` 对应 SHAKE),符合 FIPS 202 Table 6。无需调用方手动追加。
|
||||
|
||||
2. **速率与容量**:`c = 2d`(SHA-3 定长输出)或 `c = 2 × 安全强度`(SHAKE),确保 `r = 1600 − c`。
|
||||
|
||||
3. **`#[must_use]`**:所有公开哈希函数均标记此属性,防止调用方意外丢弃哈希输出。
|
||||
|
||||
4. **`#[allow(non_snake_case)]`**:G/H/J/PRF 命名保持与 FIPS 203 规范一致,便于代码审查时对照规范。
|
||||
|
||||
5. **`clear()` 安全性**:清零所有状态数组(`a`)、缓冲区(`buffer`)和哈希输出(`hash`),防止哈希器复用时残留敏感数据。
|
||||
|
||||
6. **panic 行为**:`get_hash_bytes()` 和 `get_hex_string()` 在 `finish()` 未调用时 panic。一次性函数(`compute_bytes`、`sha3_256` 等)内部保证先调用 `finish()`,因此不会 panic。
|
||||
|
||||
7. **const 泛型**:利用 Rust const generics 在编译期确定速率和输出长度,避免运行时开销。例如 `HashGenerator<136, 32>` 的 136 字节缓冲区和 32 字节输出均在栈上分配。
|
||||
85
test_data/README.md
Normal file
85
test_data/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
## KAT for FIPS-203
|
||||
|
||||
Compliant with FIPS-203, published on August 13, 2024.
|
||||
|
||||
## File format:
|
||||
|
||||
| Field | Meaning |
|
||||
|-----------|--------------------------------------------------------|
|
||||
| ``count`` | Test number |
|
||||
| ``z`` | Random 32-bytes ``z`` (Algorithm 15) |
|
||||
| ``d`` | Random 32-bytes ``d`` (Algorithm 12) |
|
||||
| ``msg`` | Random 32-bytes ``m`` (Algorithm 16) |
|
||||
| ``seed`` | AES-CTR-drbg seed |
|
||||
| ``pk`` | Resulting public key |
|
||||
| ``sk`` | Resulting secret key |
|
||||
| ``ct`` | Resulting KEM ciphertext |
|
||||
| ``ss`` | Resulting KEM shared secret |
|
||||
| ``ct_n`` | Invalid KEM ciphertext |
|
||||
| ``ss_n`` | Shared secret resulting from decapsulation of ``ct_n`` |
|
||||
|
||||
## Differences with the FIPS-203
|
||||
- No tests for key validation
|
||||
|
||||
## How it was generated
|
||||
|
||||
We use DRBG based on based on AES-CTR (see [SP800-90A](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf)) for generating random bytes. For each KAT vector, the DRBG is seeded with the ``seed`` value (personalisation string is not used). The test first generates a secret and public key. Then it encapsulates and decapsulates the shared secret.
|
||||
|
||||
The pseudocode below may make it clearer
|
||||
|
||||
```
|
||||
use aes_ctr_drbg::DrbgCtx; // aes_ctr_drbg = "0.0.2" crate
|
||||
|
||||
fn main() {
|
||||
// Initialize DRBG with the magic value
|
||||
let Ok(mut entropy) = hex::decode("60496cd0a12512800a79161189b055ac3996ad24e578d3c5fc57c1e60fa2eb4e550d08e51e9db7b67f1a616681d9182d") else {
|
||||
... blah ...
|
||||
}
|
||||
let mut drbg: DrbgCtx = DrbgCtx::new();
|
||||
drbg.init(&entropy, Vec::new());
|
||||
for i in 0..100 {
|
||||
drbg.get_random(&mut entropy);
|
||||
kem::kem(i, &entropy);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kem(count: usize, entropy: &[u8]) {
|
||||
let mut buf = Vec::new();
|
||||
let mut drbg: DrbgCtx = DrbgCtx::new();
|
||||
drbg.init(&entropy.clone(), Vec::new());
|
||||
|
||||
println!("count = {}", count);
|
||||
|
||||
// The code generates random strings in the following order: z,d,msg
|
||||
|
||||
// z
|
||||
buf.resize(32, 0);
|
||||
drbg.get_random(&mut buf);
|
||||
println!("z = {}", hex::encode(&buf));
|
||||
|
||||
// d
|
||||
drbg.get_random(&mut buf);
|
||||
println!("d = {}", hex::encode(&buf));
|
||||
|
||||
// msg
|
||||
drbg.get_random(&mut buf);
|
||||
println!("msg = {}", hex::encode(&buf));
|
||||
|
||||
// Re-init
|
||||
drbg.init(&entropy.clone(), Vec::new());
|
||||
|
||||
// Generate keys
|
||||
kyber_keygen(&mut pk, &mut sk);
|
||||
|
||||
// Encapsulate
|
||||
kyber_encaps(&pk, &mut ct, &mut ss);
|
||||
|
||||
// Decapsulate
|
||||
kyber_decaps(&sk, &ct, &mut ss);
|
||||
|
||||
println!("seed = {}", hex::encode(&entropy));
|
||||
println!("pk = {}", hex::encode(&pk));
|
||||
println!("sk = {}", hex::encode(&sk));
|
||||
println!("ct = {}", hex::encode(&ct));
|
||||
println!("ss = {}", hex::encode(&ss1));
|
||||
```
|
||||
11000
test_data/kat_MLKEM_1024.rsp
Normal file
11000
test_data/kat_MLKEM_1024.rsp
Normal file
File diff suppressed because one or more lines are too long
11000
test_data/kat_MLKEM_512.rsp
Normal file
11000
test_data/kat_MLKEM_512.rsp
Normal file
File diff suppressed because it is too large
Load Diff
11000
test_data/kat_MLKEM_768.rsp
Normal file
11000
test_data/kat_MLKEM_768.rsp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user