Skip to main content

ferritls_core/
selftest.rs

1//! 上电自检(FIPS 140-3 ISO/IEC 19790 §7.9.2 强制项)。
2//!
3//! 模块首次使用前必须执行**已知答案测试(KAT)**,覆盖全部批准算法:
4//! SHA-256/384/512、HMAC-SHA256、HKDF-SHA256、AES-128-GCM、AES-128-CCM、
5//! ECDSA P-256(RFC 6979 确定性签名逐字节比对 + 验证)、RSA PKCS#1 v1.5
6//! 签名/验证(内建 KAT 密钥)、CTR-DRBG(CAVP 向量流程)。任一失败 →
7//! 模块进入错误状态,此后所有密码操作返回
8//! [`Error::SelfTestFailed`](crate::Error);整个模块拒绝服务,不做部分
9//! 降级(非批准算法亦不例外)。
10//!
11//! **完整性测试决策(M5 定型)**:KAT 全集事实上锁定了关键常量
12//! (S-box、曲线参数、模数、Drbg 常数等)的行为——任何常量损坏都会
13//! 使至少一个 KAT 失败。对模块二进制做签名/摘要校验需要平台特定的
14//! 加载期机制(代码段自校验在纯 Rust/`forbid(unsafe_code)` 边界内
15//! 不可移植实现),属 CMVP 阶段 C 与实验室商定的交付物,此处明确
16//! 记录该决策而非提供形式化的空壳实现。
17//!
18//! 非批准算法(X25519、ChaCha20-Poly1305、Ed25519)不参与 KAT。
19//!
20//! 里程碑:M5。
21
22use std::sync::Mutex;
23use std::sync::atomic::{AtomicU8, Ordering};
24
25use crate::sha2::{Sha256, Sha384, Sha512};
26
27const STATUS_NOT_RUN: u8 = 0;
28const STATUS_PASSED: u8 = 1;
29const STATUS_FAILED: u8 = 2;
30
31static STATUS: AtomicU8 = AtomicU8::new(STATUS_NOT_RUN);
32static FAILED_REASON: Mutex<Option<&'static str>> = Mutex::new(None);
33
34/// 自检状态。
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum SelfTestStatus {
37    /// 尚未运行(首次调用任一密码 API 时懒触发)。
38    NotRun,
39    /// 全部通过。
40    Passed,
41    /// 失败:模块处于错误状态,拒绝服务。
42    Failed(&'static str),
43}
44
45fn set_failed(reason: &'static str) -> SelfTestStatus {
46    STATUS.store(STATUS_FAILED, Ordering::SeqCst);
47    if let Ok(mut slot) = FAILED_REASON.lock() {
48        *slot = Some(reason);
49    }
50    SelfTestStatus::Failed(reason)
51}
52
53/// 执行上电自检(幂等:已运行则直接返回当前状态)。
54pub fn run_power_on_self_tests() -> SelfTestStatus {
55    match STATUS.load(Ordering::SeqCst) {
56        STATUS_PASSED => return SelfTestStatus::Passed,
57        STATUS_FAILED => {
58            return SelfTestStatus::Failed(
59                FAILED_REASON
60                    .lock()
61                    .ok()
62                    .and_then(|g| *g)
63                    .unwrap_or("unknown"),
64            );
65        }
66        _ => {}
67    }
68    // 单飞:并发触发时只跑一次,其余等待结果
69    match SELF_TEST_RUN.lock() {
70        Ok(mut guard) => {
71            if *guard {
72                // 别的线程正在跑或已完成:以状态为准
73                return match STATUS.load(Ordering::SeqCst) {
74                    STATUS_PASSED => SelfTestStatus::Passed,
75                    STATUS_FAILED => SelfTestStatus::Failed("self-test failed"),
76                    _ => SelfTestStatus::NotRun,
77                };
78            }
79            *guard = true;
80        }
81        Err(_) => return SelfTestStatus::Failed("self-test lock poisoned"),
82    }
83    run_kats()
84}
85
86static SELF_TEST_RUN: Mutex<bool> = Mutex::new(false);
87
88/// 单个 KAT 的统一形态。
89type KatFn = fn() -> Result<(), crate::Error>;
90
91fn run_kats() -> SelfTestStatus {
92    let kats: [(&'static str, KatFn); 12] = [
93        ("sha256", kat_sha256),
94        ("sha384", kat_sha384),
95        ("sha512", kat_sha512),
96        ("hmac-sha256", kat_hmac_sha256),
97        ("hkdf-sha256", kat_hkdf_sha256),
98        ("aes128-gcm", kat_aes128_gcm),
99        ("aes128-ccm", kat_aes128_ccm),
100        ("ecdsa-p256", kat_ecdsa_p256),
101        ("rsa-pkcs1v15", kat_rsa_pkcs1v15),
102        ("mlkem512", kat_mlkem512),
103        ("mlkem768", kat_mlkem768),
104        ("mlkem1024", kat_mlkem1024),
105    ];
106    for (name, kat) in kats {
107        if let Err(_e) = kat() {
108            return set_failed(name);
109        }
110    }
111    if let Err(_e) = kat_drbg() {
112        return set_failed("ctr-drbg");
113    }
114    STATUS.store(STATUS_PASSED, Ordering::SeqCst);
115    SelfTestStatus::Passed
116}
117
118/// 查询当前状态(不触发自检)。
119pub fn status() -> SelfTestStatus {
120    match STATUS.load(Ordering::SeqCst) {
121        STATUS_PASSED => SelfTestStatus::Passed,
122        STATUS_FAILED => SelfTestStatus::Failed(
123            FAILED_REASON
124                .lock()
125                .ok()
126                .and_then(|g| *g)
127                .unwrap_or("unknown"),
128        ),
129        _ => SelfTestStatus::NotRun,
130    }
131}
132
133/// 所有密码 API 的入口守卫:自检未通过则拒绝服务。
134pub(crate) fn ensure_passed() -> Result<(), crate::Error> {
135    match status() {
136        SelfTestStatus::Passed => Ok(()),
137        SelfTestStatus::NotRun => match run_power_on_self_tests() {
138            SelfTestStatus::Passed => Ok(()),
139            SelfTestStatus::Failed(which) => Err(crate::Error::SelfTestFailed(which)),
140            SelfTestStatus::NotRun => Err(crate::Error::SelfTestFailed("self-test did not run")),
141        },
142        SelfTestStatus::Failed(which) => Err(crate::Error::SelfTestFailed(which)),
143    }
144}
145
146/// 测试钩子:强制模块进入失败状态(仅测试构建)。
147#[cfg(test)]
148pub(crate) fn force_fail_for_tests(which: &'static str) {
149    set_failed(which);
150}
151
152/// 测试钩子:复位自检状态(仅测试构建,用于失败注入后的恢复)。
153#[cfg(test)]
154pub(crate) fn reset_for_tests() {
155    STATUS.store(STATUS_NOT_RUN, Ordering::SeqCst);
156    if let Ok(mut run) = SELF_TEST_RUN.lock() {
157        *run = false;
158    }
159    if let Ok(mut slot) = FAILED_REASON.lock() {
160        *slot = None;
161    }
162}
163
164// ---------------------------------------------------------------------------
165// KAT:SHA-2(FIPS 180-4,消息 "abc")
166// ---------------------------------------------------------------------------
167
168fn kat_sha256() -> Result<(), crate::Error> {
169    let expect: [u8; 32] = [
170        0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22,
171        0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00,
172        0x15, 0xad,
173    ];
174    if Sha256::one_shot(b"abc") == expect {
175        Ok(())
176    } else {
177        Err(crate::Error::SelfTestFailed("sha256"))
178    }
179}
180
181fn kat_sha384() -> Result<(), crate::Error> {
182    let expect: [u8; 48] = [
183        0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50,
184        0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff,
185        0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34,
186        0xc8, 0x25, 0xa7,
187    ];
188    if Sha384::one_shot(b"abc") == expect {
189        Ok(())
190    } else {
191        Err(crate::Error::SelfTestFailed("sha384"))
192    }
193}
194
195fn kat_sha512() -> Result<(), crate::Error> {
196    let expect: [u8; 64] = [
197        0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41,
198        0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55,
199        0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3,
200        0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
201        0xa5, 0x4c, 0xa4, 0x9f,
202    ];
203    if Sha512::one_shot(b"abc") == expect {
204        Ok(())
205    } else {
206        Err(crate::Error::SelfTestFailed("sha512"))
207    }
208}
209
210// ---------------------------------------------------------------------------
211// KAT:HMAC-SHA256(RFC 4231 Test Case 1)
212// ---------------------------------------------------------------------------
213
214fn kat_hmac_sha256() -> Result<(), crate::Error> {
215    let expect: [u8; 32] = [
216        0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1,
217        0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32,
218        0xcf, 0xf7,
219    ];
220    let key = [0x0bu8; 20];
221    if crate::hmac::HmacSha256::one_shot(&key, b"Hi There") == expect {
222        Ok(())
223    } else {
224        Err(crate::Error::SelfTestFailed("hmac-sha256"))
225    }
226}
227
228// ---------------------------------------------------------------------------
229// KAT:HKDF-SHA256(RFC 5869 Test Case 1)
230// ---------------------------------------------------------------------------
231
232fn kat_hkdf_sha256() -> Result<(), crate::Error> {
233    let expect: [u8; 42] = [
234        0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f,
235        0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4,
236        0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
237    ];
238    let ikm = [0x0bu8; 22];
239    let salt: [u8; 13] = [
240        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
241    ];
242    let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
243    let prk = crate::hkdf::extract_sha256(&salt, &ikm);
244    let mut okm = [0u8; 42];
245    crate::hkdf::expand_sha256(&prk, &info, &mut okm)?;
246    if okm == expect {
247        Ok(())
248    } else {
249        Err(crate::Error::SelfTestFailed("hkdf-sha256"))
250    }
251}
252
253// ---------------------------------------------------------------------------
254// KAT:AES-128-GCM(GCM 规范 Test Case 3:加密 + 打开 + 篡改拒绝)
255// ---------------------------------------------------------------------------
256
257fn kat_aes128_gcm() -> Result<(), crate::Error> {
258    const KEY: [u8; 16] = [0; 16];
259    const NONCE: [u8; 12] = [0; 12];
260    const PT: [u8; 16] = [0; 16];
261    const CT: [u8; 16] = [
262        0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe,
263        0x78,
264    ];
265    const TAG: [u8; 16] = [
266        0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd,
267        0xdf,
268    ];
269    let gcm = crate::gcm::Aes128Gcm::new(&KEY);
270    let sealed = gcm.seal(&NONCE, b"", &PT);
271    if sealed[..16] != CT || sealed[16..] != TAG {
272        return Err(crate::Error::SelfTestFailed("aes128-gcm"));
273    }
274    let opened = gcm.open(&NONCE, b"", &sealed)?;
275    if opened != PT {
276        return Err(crate::Error::SelfTestFailed("aes128-gcm"));
277    }
278    Ok(())
279}
280
281// ---------------------------------------------------------------------------
282// KAT:AES-128-CCM(加解密往返 + 标签篡改拒绝)
283// ---------------------------------------------------------------------------
284
285fn kat_aes128_ccm() -> Result<(), crate::Error> {
286    const KEY: [u8; 16] = [
287        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
288        0x4f,
289    ];
290    const NONCE: [u8; 13] = [
291        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
292    ];
293    let ccm = crate::ccm::Aes128Ccm::new(&KEY);
294    let pt = b"self-test ccm kat";
295    let sealed = ccm.seal(&NONCE, b"", pt)?;
296    let opened = ccm.open(&NONCE, b"", &sealed)?;
297    if opened != pt {
298        return Err(crate::Error::SelfTestFailed("aes128-ccm"));
299    }
300    let mut bad = sealed.clone();
301    let last = bad.len() - 1;
302    bad[last] ^= 1;
303    if ccm.open(&NONCE, b"", &bad).is_ok() {
304        return Err(crate::Error::SelfTestFailed("aes128-ccm"));
305    }
306    Ok(())
307}
308
309// ---------------------------------------------------------------------------
310// KAT:ECDSA P-256(RFC 6979 A.2.5 确定性签名逐字节 + 验证)
311// ---------------------------------------------------------------------------
312
313fn kat_ecdsa_p256() -> Result<(), crate::Error> {
314    use crate::sign::ecdsa;
315    const D_HEX: &str = "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721";
316    const SIG_HEX: &str = "\
3173046022100efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\
318022100f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8";
319    let d = hex(D_HEX);
320    let sk = ecdsa::p256::SigningKey::from_seed(d.as_slice().try_into().expect("fixed width"));
321    let sig = sk.sign(b"sample")?;
322    if hex(SIG_HEX) != sig {
323        return Err(crate::Error::SelfTestFailed("ecdsa-p256"));
324    }
325    let q = sk.public_key_sec1();
326    ecdsa::p256::VerifyKey::from_sec1_point(&q)?.verify(b"sample", &sig)?;
327    Ok(())
328}
329
330// ---------------------------------------------------------------------------
331// KAT:ML-KEM-768(NIST ACVP keyGen tcId 26 + encapDecap tcId 26,全值锚定;
332// keyGen 与 encapDecap 是不同案例,密钥材料不可混用)
333// ---------------------------------------------------------------------------
334
335fn kat_mlkem768() -> Result<(), crate::Error> {
336    use crate::mlkem;
337    // keyGen tcId 26:(d, z) → (ek, dk)
338    const D_HEX: &str = "e34a701c4c87582f42264ee422d3c684d97611f2523efe0c998af05056d693dc";
339    const Z_HEX: &str = "a85768f3486bd32a01bf9a8f21ea938e648eae4e5448c34c3eb88820b159eedd";
340    const EK_HEX: &str = "\n        6d14a071f7cc452558d5e71a7b087062ecb1386844588246126402b1fa163773\n        3cd5f60cc84bcb646a7892614d7c51b1c7f1a2799132f13427dc482158da2544\n        70a59e00a4e49686fdc077559367270c2153f11007592c9c4310cf8a12c6a871\n        3bd6bb51f3124f989ba0d54073cc242e0968780b875a869efb851586b9a868a3\n        84b9e6821b201b932c455369a739ec22569c977c212b381871813656af5b567e\n        f893b584624c863a259000f17b254b98b185097c50ebb68b244342e05d4de520\n        125b8e1033b1436093ace7ce8e71b458d525673363045a3b3eea9455428a3987\n        05a42327adb3774b7057f42b017ec0739a983f19e8214d09195fa24d2d571db7\n        3c19a6f8460e50830d415f627b88e94a7b153791a0c0c7e9484c74d53c714889\n        f0e321b6660a532a5bc0e557fbca35e29bc611200ed3c633077a4d873c5cc670\n        06b753bf6d6b7af6ca402ab618236c0affbc801f8222fbc36ce0984e2b18c944\n        bbcbef03b1e1361c1f44b0d734afb1566cff8744da8b9943d6b45a3c09030702\n        ca201ffe20cb7ec5b0d4149ee2c28e8b23374f471b57150d0ec9336261a2d5cb\n        84a3acacc4289473a4c0abc617c9abc178734434c82e1685588a5c2ea2678f6b\n        3c2228733130c466e5b86ef491153e48662247b875d201020b566b81b64d839a\n        b4633baa8ace202baab4496297f9807adbbb1e332c6f8022b2a18cfdd4a82530\n        b6d3f007c3353898d966cc2c21cb4244bd00443f209870acc42bc33068c724ec\n        17223619c1093cca6aeb29500664d1225036b4b81091906969481f1c723c140b\n        9d6c168f5b64bea69c5fd6385df7364b8723bcc85e038c7e464a900d68a21278\n        18994217aec8bdb39a970a9963de93688e2ac82abcc22fb9277ba22009e87838\n        1a38163901c7d4c85019538d35caae9c41af8c929ee20bb08ca619e72c2f2262\n        c1c9938572551ac02dc9268fbcc35d79011c3c090ad40a4f111c9be55c427eb7\n        96c1932d8673579af1b4c638b0944489012a2559a3b02481b01ac30ba8960f80\n        c0c2b3947d36a12c080498bee448716c973416c8242804a3da099ee137b0ba90\n        fe4a5c6a89200276a0cfb643ec2c56a2d708d7b4373e44c1502a763a600586e6\n        cda6273897d44448287dc2e602dc39200bf6166236559fd12a60892aeb153dd6\n        51bb469910b4b34669f91da8654d1eb72eb6e02800b3b0a7d0a48c836854d3a8\n        3e65569cb7230bb44f3f143a6dec5f2c39ab90f274f2088bd3d6a6fca0070273\n        bedc84777fb52e3c558b0ae06183d5a48d452f68e15207f861627aca14279630\n        f82ec3a0ca078633b600afa79743a600215be5637458ce2ce8aff5a08eb5017b\n        2c766577479f8dc6bf9f5cc75089932161b96cea406620aedb630407f7687ebb\n        b4814c7981637a48a90de68031e062a7af7612b4f5c7a6da86bd136529e64295\n        a5613ea73bd3d4448cb81f243135c0a660beb9c17e651def469a7d90a15d3481\n        090bcbf227012328941fa46f39c5006ad93d458aa6add655862b418c3094f551\n        460df2153a5810a7da74f0614c2588be49dc6f5e88154642bd1d376256332643\n        3507156a57c57694bdd26e7a246feb723aed67b04887c8e476b48cab59e5362f\n        26a9ef50c2bc80ba146226216fe62968a60d04e8c170d741c7a2b0e1abdac968";
341    // encapDecap tcId 26:同案例的 (ek, dk, m) → (c, ss)
342    const EK2_HEX: &str = "\n        89d2cb65f94dcbfc890efc7d0e5a7a38344d1641a3d0b024d50797a5f23c3a18\n        b3101a1269069f43a842bacc098a8821271c673db1beb33034e4d7774d16635c\n        7c2c3c2763453538bc1632e1851591a51642974e5928abb8e55fe55612f9b141\n        aff015545394b2092e590970ec29a7b7e7aa1fb4493bf7cb731906c2a5cb49e6\n        614859064e19b8fa26af51c44b5e7535bfdac072b646d3ea490d277f0d97ced4\n        7395fed91e8f2bce0e3ca122c2025f74067ab928a822b35653a74f06757629af\n        b1a1caf237100ea935e793c8f58a71b3d6ae2c8658b10150d4a38f572a0d49d2\n        8ae89451d338326fdb3b4350036c1081117740edb86b12081c5c1223dbb5660d\n        5b3cb3787d481849304c68be875466f14ee5495c2bd795ae412d09002d65b871\n        9b90cba3603ac4958ea03cc138c86f7851593125334701b677f82f4952a4c93b\n        5b4c134bb42a857fd15c650864a6aa94eb691c0b691be4684c1f5b7490467fc0\n        1b1d1fda4dda35c4ecc231bc73a6fef42c99d34eb82a4d014987b3e386910c62\n        679a118f3c5bd9f467e4162042424357db92ef484a4a1798c1257e870a30cb20\n        aaa0335d83314fe0aa7e63a862648041a72a6321523220b1ace9bb701b21ac12\n        53cb812c15575a9085eabeade73a4ae76e6a7b158a20586d78a5ac620a5c9abc\n        c9c043350a73656b0abe822da5e0ba76045fad75401d7a3b703791b7e9926171\n        0f86b72421d240a347638377205a152c794130a4e047742b888303bddc309116\n        764de7424cebea6db65348ac537e01a9cc56ea667d5aa87ac9aaa4317d262c10\n        143050b8d07a728ca633c13e468abcead372c77b8ecf3b986b98c1e55860b2b4\n        216766ad874c35ed7205068739230220b5a2317d102c598356f168acbe80608d\n        e4c9a710b8dd07078cd7c671058af1b0b8304a314f7b29be78a933c7b9294424\n        954a1bf8bc745de86198659e0e1225a910726074969c39a97c19240601a46e01\n        3dcdcb677a8cbd2c95a40629c256f24a328951df57502ab30772cc7e5b850027\n        c8551781ce4985bdacf6b865c104e8a4bc65c41694d456b7169e45ab3d7acabe\n        afe23ad6a7b94d1979a2f4c1cae7cd77d681d290b5d8e451bfdcccf5310b9d12\n        a88ec29b10255d5e17a192670aa9731c5ca67ec784c502781be8527d6fc003c6\n        701b3632284b40307a527c7620377feb0b73f722c9e3cd4dec64876b93ab5b7c\n        fc4a657f852b659282864384f442b22e8a21109387b8b47585fc680d0ba45c7a\n        8b1d7274bda57845d100d0f42a3b74628773351fd7ac305b2497639be90b3f4f\n        71a6aa3561eecc6a691bb5cb3914d8634ca1e1af543c049a8c6e868c51f0423b\n        d2d5ae09b79e57c27f3fe3ae2b26a441babfc6718ce8c05b4fe793b910b8fbcb\n        be7f1013242b40e0514d0bdc5c88bac594c794ce5122fbf34896819147b92838\n        1587963b0b90034aa07a10be176e01c80ad6a4b71b10af4241400a2a4cbbc059\n        61a15ec1474ed51a3cc6d35800679a462809caa3ab4f7094cd6610b4a700cba9\n        39e7eac93e38c99755908727619ed76a34e53c4fa25bfc97008206697dd145e5\n        b9188e5b014e941681e15fe3e132b8a3903474148ba28b987111c9bcb3989bbb\n        c671c581b44a492845f288e62196e471fed3c39c1bbddb0837d0d4706b0922c4";
343    const DK2_HEX: &str = "\n        b09125afb3cfb5295581373ab6885284d9706318280d223edc987fd14410dbe8\n        2e6ac89adfab70e67ca4b1c641ad037fd8c47870f159ec79cdcd52605b989049\n        9bb6dbd8347f342c61436b642c0ddf4617db06198b8285dce4c09d9775a2f41c\n        8cd18af8e75f57d4127df94d901ac83bacbd584cc50c43750f49b357f5935087\n        5c9b475480a8aaa168592ddb158614a639813566d205368c6c39f0413ca3230d\n        f60d44008282b682ac66b76c3c95f00b2a555035529c86ef3905b4a3968fea78\n        02b6c5eecb08e8f0c42d7ab7cd21a62fb136412a1840b52c99970ccf51892f73\n        497c3775be2189f7fc25e7c74d81fc217683292aa4866ddb04469855323a0810\n        f0893de5c7f94a9c0b5337db83c44891b2e694695b76575032bf51761682958b\n        d4f97be9a355b4a85bb6858b7e5a5ef653ab781056af9187d811c3a8936e5706\n        503db57062410bcc9421f1ab867a657856c411c4e025ecb3c387729ae8e112f3\n        30b988e22f47c35c280750d21b107687af7b329ef3cb5289f06fb7d44548391e\n        97ba6dd499b5907c54958413d92aa99d5646cf47a8f48cb70a07ad056b4eefe6\n        c8c46645f7028a32410558638c48e83ac1570160c3833bf64052f5b7df4364d3\n        e0b24e790aa7c98cee0441e6731d9de22d156c61e1c740397672ef54724f01b9\n        d49923aa321f86b98823f21360138392b90c69434635275f9bfbb9b8a99e8e1b\n        7f4ec25f75dbce33c13f750170bd6722efe496e7463e16aaa5867b869a96ad41\n        b22bd2556c924596fd778d79a102f6e46d8eb18fefac8db19993e5414ac81670\n        5286892492c8c9e852d6145dff0c10e4a6703a459e7e732a6dfa2766a622b062\n        2bfedb8f41c125f61b2ec264853b9ccc165979f6a263beb148905aac7618a70e\n        829e23f28696f92ef6fa07c102cdbdb1288ba5cff3a81abba15974535fe3106a\n        80068f14e98964572350a7112b1601c196710c096ccf164fbce1aabac9c5b953\n        5070e61ab8068d611ca765fabb6412607dab30c4fc6ad073731fdc4c48b88e26\n        7c47b439ad2560c30561815ceb1f52c896489944bbbab52b1b1d1680a1057964\n        dafa600c93a39a447ddbb0adf911afe3e823d8acc7cc04659f625f2c1837bb17\n        5282542cd22601f621581ab5a6c0384e087ccd32a5380b522fdd3a4202b5b41c\n        85caff2903b2dc2645703d9bc711fbb404c0c0376187ac588aaf5718522d2273\n        a9408dabcbc9701698d2da172aa6267a4c9693a24011c2265a2b6dc8e96304a9\n        8ddc5319a3140c399a08412c20f48537870bb84c32a094457895511ff7ec421d\n        e01a64b78534653f78327441b90cd115939dfaafa95b40d0a63d62d12eb5c909\n        6018cc83871e44e6cd0be26d16b7b5a209b8e6471d2954adf9fabd0153707c9c\n        aa2bcc38ded841c791a0eb597eeee2c518d926edb28ab53caa5b7746466931b0\n        ac9150688bf37049c1f82bcf648332434cd0a92fd2c958353a26cb65cb499057\n        109b2d688cc43c4b385da7c50868af1b8075e57088f5db12dfa493eacb6dc4ec\n        6e205baa2a89858ec2823c00553714cde47a96e36c7c198b3ec57ccf74d92cdd\n        b86aa0a8b8b5ca9d52bb60aba79f4f72b0125532ceb7a9077480d2bb60df51a9\n        89d2cb65f94dcbfc890efc7d0e5a7a38344d1641a3d0b024d50797a5f23c3a18\n        b3101a1269069f43a842bacc098a8821271c673db1beb33034e4d7774d16635c\n        7c2c3c2763453538bc1632e1851591a51642974e5928abb8e55fe55612f9b141\n        aff015545394b2092e590970ec29a7b7e7aa1fb4493bf7cb731906c2a5cb49e6\n        614859064e19b8fa26af51c44b5e7535bfdac072b646d3ea490d277f0d97ced4\n        7395fed91e8f2bce0e3ca122c2025f74067ab928a822b35653a74f06757629af\n        b1a1caf237100ea935e793c8f58a71b3d6ae2c8658b10150d4a38f572a0d49d2\n        8ae89451d338326fdb3b4350036c1081117740edb86b12081c5c1223dbb5660d\n        5b3cb3787d481849304c68be875466f14ee5495c2bd795ae412d09002d65b871\n        9b90cba3603ac4958ea03cc138c86f7851593125334701b677f82f4952a4c93b\n        5b4c134bb42a857fd15c650864a6aa94eb691c0b691be4684c1f5b7490467fc0\n        1b1d1fda4dda35c4ecc231bc73a6fef42c99d34eb82a4d014987b3e386910c62\n        679a118f3c5bd9f467e4162042424357db92ef484a4a1798c1257e870a30cb20\n        aaa0335d83314fe0aa7e63a862648041a72a6321523220b1ace9bb701b21ac12\n        53cb812c15575a9085eabeade73a4ae76e6a7b158a20586d78a5ac620a5c9abc\n        c9c043350a73656b0abe822da5e0ba76045fad75401d7a3b703791b7e9926171\n        0f86b72421d240a347638377205a152c794130a4e047742b888303bddc309116\n        764de7424cebea6db65348ac537e01a9cc56ea667d5aa87ac9aaa4317d262c10\n        143050b8d07a728ca633c13e468abcead372c77b8ecf3b986b98c1e55860b2b4\n        216766ad874c35ed7205068739230220b5a2317d102c598356f168acbe80608d\n        e4c9a710b8dd07078cd7c671058af1b0b8304a314f7b29be78a933c7b9294424\n        954a1bf8bc745de86198659e0e1225a910726074969c39a97c19240601a46e01\n        3dcdcb677a8cbd2c95a40629c256f24a328951df57502ab30772cc7e5b850027\n        c8551781ce4985bdacf6b865c104e8a4bc65c41694d456b7169e45ab3d7acabe\n        afe23ad6a7b94d1979a2f4c1cae7cd77d681d290b5d8e451bfdcccf5310b9d12\n        a88ec29b10255d5e17a192670aa9731c5ca67ec784c502781be8527d6fc003c6\n        701b3632284b40307a527c7620377feb0b73f722c9e3cd4dec64876b93ab5b7c\n        fc4a657f852b659282864384f442b22e8a21109387b8b47585fc680d0ba45c7a\n        8b1d7274bda57845d100d0f42a3b74628773351fd7ac305b2497639be90b3f4f\n        71a6aa3561eecc6a691bb5cb3914d8634ca1e1af543c049a8c6e868c51f0423b\n        d2d5ae09b79e57c27f3fe3ae2b26a441babfc6718ce8c05b4fe793b910b8fbcb\n        be7f1013242b40e0514d0bdc5c88bac594c794ce5122fbf34896819147b92838\n        1587963b0b90034aa07a10be176e01c80ad6a4b71b10af4241400a2a4cbbc059\n        61a15ec1474ed51a3cc6d35800679a462809caa3ab4f7094cd6610b4a700cba9\n        39e7eac93e38c99755908727619ed76a34e53c4fa25bfc97008206697dd145e5\n        b9188e5b014e941681e15fe3e132b8a3903474148ba28b987111c9bcb3989bbb\n        c671c581b44a492845f288e62196e471fed3c39c1bbddb0837d0d4706b0922c4\n        72e31df613da9a1dd33b5d2d8939684b89f7649e1c59b959ffbe972786c477f6\n        6177dbf3b059173fd06afcd90e80e862174fc57f97607bbff5b73d6360fb5c37";
344    const M_HEX: &str = "2ce74ad291133518fe60c7df5d251b9d82add48462ff505c6e547e949e6b6bf7";
345    const C_HEX: &str = "\n        56b42d593aab8e8773bd92d76eabddf3b1546f8326f57a7b773764b6c0dd3047\n        0f68dff82e0dca92509274ecfe83a954735fde6e14676daaa3680c30d524f4ef\n        a79ed6a1f9ed7e1c00560e8683538c3105ab931be0d2b249b38cb9b13af5ceaf\n        7887a59dba16688a7f28de0b14d19f391eb41832a56479416ccf94e997390ed7\n        878eeaff49328a70e0ab5fce6c63c09b35f4e45994de615b88bb722f70e87d2b\n        bd72ae71e1ee9008e459d8e743039a8ddeb874fce5301a2f8c0ee8c2fee7a4ee\n        68b5ed6a6d9ab74f98bb3ba0fe89e82bd5a525c5e8790f818ccc605877d46c8b\n        db5c337b025bb840ff471896e43bfa99d73dbe31805c27a43e57f0618b3ae522\n        a4644e0d4e4c1c548489431be558f3bfc50e16617e110dd7af9a6fd83e3fbb68\n        c304d15f6cb700d61d7aa915a6751ea3ba80223e654132a20999a43bf4085927\n        30b9a9499636c09fa729f9cb1f9d3442f47357a2b9cf15d3103b9bf396c23088\n        f118ede346b5c03891cfa5d517cef8471322e7e31087c4b036abad784bff72a9\n        b11fa198facbcb91f067feaf76fcfe5327c1070b3da6988400756760d2d1f060\n        298f1683d51e3616e98c51c9c03aa42f2e633651a47ad3cc2ab4a852ae0c4b04\n        b4e1c3dd944445a2b12b4f42a6435105c04122fc3587afe409a00b308d63c5dd\n        8163654504eedbb7b5329577c35fbeb3f463872cac28142b3c12a740ec6ea7ce\n        9ad78c6fc8fe1b4df5fc55c1667f31f2312da07799dc870a478608549fedafe0\n        21f1cf2984180364e90ad98d845652aa3cdd7a8eb09f5e51423fab42a7b7bb4d\n        514864be8d71297e9c3b17a993f0ae62e8ef52637bd1b885bd9b6ab727854d70\n        3d8dc478f96cb81fce4c60383ac01fcf0f971d4c8f352b7a82e218652f2c106c\n        a92ae686bacfcef5d327347a97a9b375d67341552bc2c538778e0f9801823ccd\n        fcd1eaaded55b18c9757e3f212b2889d3857db51f981d16185fd0f900853a750\n        05e3020a8b95b7d8f2f2631c70d78a957c7a62e1b3719070acd1fd480c25b838\n        47da027b6ebbc2eec2df22c87f9b46d5d7baf156b53cee929572b92c4784c4e8\n        29f3446a1ffe47f99decd0436029ddebd3ed8e87e5e73d123dbe8a4ddacf2abd\n        e87f33ae2b621c0ec5d5cad1259deec2aeff6088f04f27a20338b5762543e510\n        0899a4cbfb7b3ca456b3a19b83a4c432230c23e1c7f107c4cb112152f1c0f30d\n        a0bb33f4f11f47eea43872bafa84ae22256d708e0604dade4b2a4dde8cccf119\n        30e13553934ae3ece52f3d7ccc00287377879fe6b8ece7ef79423507c9da3395\n        59c20de1c51955999bae47401dc3cdfaa1b256d09c7db9fc8698bfcefa7302d5\n        6fbcde1fbaaa1c653454e6fd3d84e4f79a931c681cbb6cb462b10dae112bdfb7\n        f65c7fdf6e5fc594ec3a474a94bd97e6ec81f71c230bf70ca0f13ce3dffbd9ff\n        9804efd8f37a4d3629b43a8f55544ebc5ac0abd9a33d79699068346a0f1a3a96\n        e115a5d80be165b562d082984d5aacc3a2301981a6418f8ba7d7b0d7ca5875c6";
346    const K_HEX: &str = "2696d28e9c61c2a01ce9b1608dcb9d292785a0cd58efb7fe13b1de95f0db55b3";
347
348    // 1) 种子展开
349    let d: [u8; 32] = hex(D_HEX).try_into().expect("32 bytes");
350    let z: [u8; 32] = hex(Z_HEX).try_into().expect("32 bytes");
351    let (ek, _dk) = mlkem::keypair_from_seed(&d, &z);
352    if ek.as_bytes() != hex(EK_HEX).as_slice() {
353        return Err(crate::Error::SelfTestFailed("mlkem768"));
354    }
355    // 2) 封装(案例自带 ek)
356    let ek2 = mlkem::Mlkem768EncapsKey::from_bytes(hex(EK2_HEX).as_slice())?;
357    let m: [u8; 32] = hex(M_HEX).try_into().expect("32 bytes");
358    let (c, ss) = mlkem::encapsulate_with_seed(&ek2, &m)?;
359    if c.as_bytes() != hex(C_HEX).as_slice() || ss.expose_bytes() != hex(K_HEX).as_slice() {
360        return Err(crate::Error::SelfTestFailed("mlkem768"));
361    }
362    // 3) 解封装(案例自带 dk)
363    let dk2 = mlkem::Mlkem768DecapsKey::from_bytes(hex(DK2_HEX).as_slice())?;
364    let ss2 = mlkem::decapsulate(&dk2, &c);
365    if ss2.expose_bytes() != hex(K_HEX).as_slice() {
366        return Err(crate::Error::SelfTestFailed("mlkem768"));
367    }
368    Ok(())
369}
370
371fn kat_mlkem512() -> Result<(), crate::Error> {
372    use crate::mlkem::k512;
373    // NIST ACVP(2026-09 代):步骤 1 用 keyGen tcId 1,
374    // 步骤 2/3 用 encapDecap encapsulation tcId 1 的完整五元组(ek/dk/m/c/k 同案例)
375    const D_HEX: &str = "47b893474672ba92e4b12ee44fb32953af8e8503b5fb471d1614fb8a021a660a";
376    const Z_HEX: &str = "1f8cb39e9e30bc458a0dc5408884b1187fb217018df760fa57317703b844a0a9";
377    const EK_KEYGEN_HEX: &str = "
378        28266a088b3482439bca01afb7ca5c6136a979b5159985a9484b36b679a5f7b9
379        819eb63577891f7bb9cb98413ccc434adc79a16d6ab3076569ce6291c59b5d64
380        612a7fb0c15013200bc8bebb03a570174b5e4363aed86eb02a220d281fb5457f
381        0a549fc5051d49a6b2015259a2c3084f405e1769952260675586a58490405927
382        5a265234ef3abf88c171a80898fc783358bbc9803c8789027d917c9ebacbc568
383        cc18de84c85454b94249586c0c6e2b8a16fa789c51212dd1728ee9b8c6c40528
384        bf93826fa82368419623032af27b5694305816811d3ca85805100e9c1a9621e5
385        089e54cb47f5a8fea0b49ef81c6b5187f48924c7947d6b61697a4a8a18452ef8
386        03336ad4be503275bcacc03c181405f7b1dc9b47fb169eb37bbe27e29c763a4e
387        52b9a42520388cf09b8edbcdf41ccf6537190e6156c37cc1aac63c0f90ce78d0
388        b9b190c548d71b6f26cc8f585ea14004b5b30aaa100b2adc1263828833b24e46
389        163b41446f98c882092a39941867b80632e2097674a793935227db0b8577e03a
390        69c50a514c7473c892e3fba7c4316bdabc952a70644176687d4191323bad93d8
391        5a3ca250868c0747e6c44f6126c874afbec0bdd4503cb2c59a69816e7d410994
392        1467579a1ffe6a4f50fa379051729dab6e2f61432f15be67d667c7cc1054742b
393        2b953078a5cf88d9133087309d88c61da240d99c59137329907b47865321ecd5
394        564e987333b4cb607b0afca86769dc95b2f921357213fcb80c3b152918e9bab2
395        228c0a1b77897ac68ce55088165f87f397da9790873b62c5383c0ccc370f0267
396        cbe195651ccf336182c22ac3924b76c9e779b7a271d166b6d24b84242b7e73cc
397        723f764039f6c851744034c3304db0c091a5764fdc9d593556ff734b82a87ccb
398        c38ca99564d988bbd2d1bf071bb160722d365104fb27610651a8ed817f2742a6
399        b5a1273a61acaf4460b0ab1456a9922351400a1c7d95d856d6e3370622c9c416
400        4bc6b401435624a98b95caeb274f34ce92038d785068cdd8cf44c38d84acb2c4
401        66a2756c870ee78c26e738cc451002304eb8c90ab24b6463eb124d779f937a2e
402        3692611d2e34d57b36cc4b2cd3b31ff485c6684d408b972e0d5ca7d2224aae4e";
403    const EK_HEX: &str = "
404        17e5129b2029f3281987d6624725b64c51cf8dca3562372bacb7ae15fa9f2ff6
405        ab47659b7d305b61f55f571315ff69aa49e1388100319a650e86c59ba3024c3d
406        ec83d4aaab661c452f6cb6a8d2638c133c599045329ccc8f677d24683df1146e
407        e1c7318c3763a47acff81a03927b9bac5a49dc285c4ee204c4d72dbb1c97fe53
408        c622e621338669fcfac1e30a36f0a9769bbb2ba408787ee629cec383f29aaaab
409        d46e22133f339c08e29b82c4faaf0f676e1c2b04377975dc3a3b246488edd163
410        6c58abcd65b8198b6fa8a8475ef541daf34b5adb9db0589d8958b62a88930eeb
411        1c0c352b1e57bc7882c89efa318457813f2b55b96881e7f75c3dc97357681e54
412        553ae099095ac38b34c00199952602c481c0f1cf74b550ce7c4f6c5876aff076
413        dd921942db377e1749f0306f77443a2f058d785854c7e32b67f49bcb99a5e18a
414        ab607c649b892b4da7cc7af31557149f02b19460fb49e5051a7251ade0083cdc
415        1b0b0ca9633220c2b3c532fa2cbc0dc6cffcd4455e0005d06cafc727c778375c
416        b67ac461b3627a653c843baeaef866c7f67746262ff6d76536255c89045a172c
417        bfaa123d6eca3e56fc922f437a14f78d1adb54b3e54e8dd530450b8500e15a54
418        c97471cb26ab437394480282ba6b7a786c28857baf387725bb4342b4c3abe4cb
419        f90612b04c8bdc1599c2d4c66b80b1a1941440040a42aabe03e1ccb0b4190780
420        b6603bb69ec199b063826beaa5447c88427c6bd7a06e4164a490b955152979b8
421        f5bc9417598af01bcc371577189e1775165b9610b9f75aeea277018b1c2c0439
422        1115b6f353afeecbaa18aa4001db94fcdc5f46ea520e9836bc5a4576b58c59fc
423        3f75634946d8cc1b1081ce93aba8566304169332fa316d4ba675a086ad879379
424        63511e4a1845ea38689b564e11295672098cf01d5d4bab0338121128a682b048
425        e6aa31b83c65ff861e123010433a3c7607ae5e90c438067f637b44adf8798cd0
426        980ec83bdaeb4d427a8f8d88c519e52542f07734a645a2a5bd4d6521a4b64a96
427        a10f35002780169b35e35f01ca74fe6207b8b475ebc079647cc10aec3c29683e
428        071d87b82abbdd6d369e326e475325ed5ae7ed232b37f49388c06a740d421204";
429    const DK_HEX: &str = "
430        c9096f060a1c4c974253d2b64feb0e40d05ff5426fe9665252968c4948a2a363
431        a5b7f5523e5abd4e63393e9cab0148701529548cfc58901a4840517423f9a3e5
432        0327d1610bd291a2a051ac231144ac924b6845a735784daf24cacc9c17e11739
433        9b94967284900390ad35857979385a04929eb2094bc6444236da94d39ab74ea6
434        40470a068b69880da1846c6211b0222174d84f5f0649a7e266a58091bfd9b49b
435        9478f1801934a86293e8af643340541676f98a2d1b62a4835766fb6441172cbb
436        a0942b3665bebb1ca202ab8a7a0c655bc232e89a0027fc49f7ea49ff21564482
437        30602b70c6988e4fd182dc95338f1206d2fbc699fb12ac636ef6084735f3962e
438        e26d494423baa907d8d05d6076556f6279bb878b6466559d309a57c93843173d
439        ae1b281ea2a30a7c7325d8593579b07d574a73c51a8ae17092a80aa30c35bd6c
440        b76e122019ba2668551cb9640a07a3b5aa3c83a575ae78552928344dc4388433
441        3414c48a9bcaa45620c46334e508e7750b24cc696002995725159c334a909074
442        a1a3719d53c3ea2cb8df03c3953192162444f116453ba19b2ad380769851dd59
443        455fa0b4e1367da0bab063922846ea85ff119fb7e0447d464cbac53fbdfaa0ea
444        ba3e1be31b1243c6b1468369523c03e06ffdc714bf2cc171a8189cc3a8f4840d
445        b69860a6608b1b9c56a356cebc6c8fd01763e086566bc582ecb52db10a0c4ac9
446        7d94c25b5ab2cef0c42fcf26bd7a88044ea20b16041727573d156a8fb69a31b5
447        c015201733a0e84ded1757e3988dbcbc5571c8588286a418c149a49ca31b73cc
448        7321cbf83947395538152ac6b36b39daa538aea236db64827d660e574911fa09
449        4d4e9a279820be06d4bd4bd3492643964d432bd8dc5c0828b7f5993a50d25415
450        88a6b37022cce055814845cbac226807216b0839f449039651a43488400ba602
451        e6244b720629ddf3c9e88a894d524a0d60327f748a1be86a25a2bf41225fcfd9
452        848b917edb0b081e71bd50c09c0eb38286939879e23e942a6d4d649b4ca123f7
453        2c202f1240b9ec63c7410340c5c7c78baa5ba70b55172bc1c44abde4af86d380
454        17e5129b2029f3281987d6624725b64c51cf8dca3562372bacb7ae15fa9f2ff6
455        ab47659b7d305b61f55f571315ff69aa49e1388100319a650e86c59ba3024c3d
456        ec83d4aaab661c452f6cb6a8d2638c133c599045329ccc8f677d24683df1146e
457        e1c7318c3763a47acff81a03927b9bac5a49dc285c4ee204c4d72dbb1c97fe53
458        c622e621338669fcfac1e30a36f0a9769bbb2ba408787ee629cec383f29aaaab
459        d46e22133f339c08e29b82c4faaf0f676e1c2b04377975dc3a3b246488edd163
460        6c58abcd65b8198b6fa8a8475ef541daf34b5adb9db0589d8958b62a88930eeb
461        1c0c352b1e57bc7882c89efa318457813f2b55b96881e7f75c3dc97357681e54
462        553ae099095ac38b34c00199952602c481c0f1cf74b550ce7c4f6c5876aff076
463        dd921942db377e1749f0306f77443a2f058d785854c7e32b67f49bcb99a5e18a
464        ab607c649b892b4da7cc7af31557149f02b19460fb49e5051a7251ade0083cdc
465        1b0b0ca9633220c2b3c532fa2cbc0dc6cffcd4455e0005d06cafc727c778375c
466        b67ac461b3627a653c843baeaef866c7f67746262ff6d76536255c89045a172c
467        bfaa123d6eca3e56fc922f437a14f78d1adb54b3e54e8dd530450b8500e15a54
468        c97471cb26ab437394480282ba6b7a786c28857baf387725bb4342b4c3abe4cb
469        f90612b04c8bdc1599c2d4c66b80b1a1941440040a42aabe03e1ccb0b4190780
470        b6603bb69ec199b063826beaa5447c88427c6bd7a06e4164a490b955152979b8
471        f5bc9417598af01bcc371577189e1775165b9610b9f75aeea277018b1c2c0439
472        1115b6f353afeecbaa18aa4001db94fcdc5f46ea520e9836bc5a4576b58c59fc
473        3f75634946d8cc1b1081ce93aba8566304169332fa316d4ba675a086ad879379
474        63511e4a1845ea38689b564e11295672098cf01d5d4bab0338121128a682b048
475        e6aa31b83c65ff861e123010433a3c7607ae5e90c438067f637b44adf8798cd0
476        980ec83bdaeb4d427a8f8d88c519e52542f07734a645a2a5bd4d6521a4b64a96
477        a10f35002780169b35e35f01ca74fe6207b8b475ebc079647cc10aec3c29683e
478        071d87b82abbdd6d369e326e475325ed5ae7ed232b37f49388c06a740d421204
479        f31404c1c625039560adfe0bf427c4502b115fa8d02d78b4f2ef1a45078a1f80
480        52c2a1a06087a52fc79b8cf7f2fcd41d18f5a98190d84a0379870db22186ecb7";
481    const M_HEX: &str = "dcacfe4de1c115da106acd1eefeafdc7f0f4e5707453ee2d6b0d69d34cc0ef4a";
482    const C_HEX: &str = "
483        1c3204a5a2c031077459e24a179fc80f8833e19f36e7ac0d3071bfbd2d48fcf1
484        352b96efd0fa3195b44a27ec575b2794909e4089421e56409ad00cf472680f43
485        8e0a6d39e88fe6b938ef722c7b7f75f714264c8f22c528a63985c75d2412278b
486        137acd29003cad1711a2637c630164507b7d3c0acd1dd3ba6e689411df6d3eee
487        410ec8c93ef27bc82019c3943b85e645519bec1105d4738388c7a5452a67880d
488        e88d65c1626a55a4565b5c26b20bbfc33f2dcecf938149d8b58b19dfb5f451c3
489        a9fb5ab3dc486c435f5397b6e32416a9306d9869b91231adfcc9a4ad3d956ec4
490        9832c3ef2a6ed50638f6633d5a8fa7bb7b04ef45ff9b57d6ea7b771d57c3e5b9
491        bf96e03abd601bb46e5ce3e104233e7be642b082a610fcebea684c516aa39a05
492        1ddb87025ca849816e177c17c10115939b98b5d9f95323328cb5250c4e38b7e9
493        32481d20bcbc66b0becb3dc1aa196f5fe207b28f36344a3c00f2fbe179878d6c
494        7981467fc2df70d079088f09b4d2cb56d5dede593a77b31930e8f6138dfc7f9a
495        7295fb372c1a33713610b2be51f311e7ce6050ec276e5c3a50790ee4cb815110
496        52ab659dd54f4baf211eacdc2a4e987c7e2eb8b384f06813c2542bf765c3e6b1
497        42968bf3c66414d01a205964e743777040a6a9879c84681b6f6b3f2f2b0b0455
498        952a99a21cd609c1fd7be71ba6a1c9589445dc69cf1d4937763ac640df853fd3
499        e6dc9143467e473ff4b89975f24cca51506830c279ad8bf806611e836f405e8c
500        1fc36208e99acb764316bd7c42d2e248b1dc3d5c551ab77fc168296853129c31
501        f5722707791fd5f9aaabc4fd4c82a99970a93f274fd0d6252edcaedc076bf598
502        4e6e28056bb831ff289cb0f6ee87bc4816712da0f322a51765f5f230d0de72cb
503        b4714c9cfe56c1bd727dd7d3f7c1b5b83029c81a22b8fd430b137003b63f29b7
504        10a1a2c549f9ab0b5ac8a123935a1a3bf627690252aef65f20681e2e9a664321
505        cdf5fde1943c196ba2f04e42e35f62ce77198f01a747a14d57d7ed46259189a6
506        aa3363fc178a8cc5499f033f9b8d90d327aafab7de16831e8325d337ab3dcdf1";
507    const K_HEX: &str = "2d74374b55d29aa585e144a29ba4f0a96537a73b4f176c5527075f66e38e8858";
508
509    // 1) 种子展开(keyGen 案例)
510    let d: [u8; 32] = hex(D_HEX).try_into().expect("32 bytes");
511    let z: [u8; 32] = hex(Z_HEX).try_into().expect("32 bytes");
512    let (ek, _dk) = k512::keypair_from_seed(&d, &z);
513    if ek.as_bytes() != hex(EK_KEYGEN_HEX).as_slice() {
514        return Err(crate::Error::SelfTestFailed("mlkem512"));
515    }
516    // 2) 封装(encapDecap 案例自带 ek)
517    let ek2 = k512::EncapsKey::from_bytes(hex(EK_HEX).as_slice())?;
518    let m: [u8; 32] = hex(M_HEX).try_into().expect("32 bytes");
519    let (c, ss) = k512::encapsulate_with_seed(&ek2, &m)?;
520    if c.as_bytes() != hex(C_HEX).as_slice() || ss.expose_bytes() != hex(K_HEX).as_slice() {
521        return Err(crate::Error::SelfTestFailed("mlkem512"));
522    }
523    // 3) 解封装(同案例 dk)
524    let dk2 = k512::DecapsKey::from_bytes(hex(DK_HEX).as_slice())?;
525    let ss2 = k512::decapsulate(&dk2, &c);
526    if ss2.expose_bytes() != hex(K_HEX).as_slice() {
527        return Err(crate::Error::SelfTestFailed("mlkem512"));
528    }
529    Ok(())
530}
531
532fn kat_mlkem1024() -> Result<(), crate::Error> {
533    use crate::mlkem::k1024;
534    // NIST ACVP(2026-09 代):步骤 1 用 keyGen tcId 51,
535    // 步骤 2/3 用 encapDecap encapsulation tcId 51 的完整五元组(ek/dk/m/c/k 同案例)
536    const D_HEX: &str = "f3a706faf090c03db506863ab0b20bd8a1627956318e88c67eb875e8e7266009";
537    const Z_HEX: &str = "35d2bc43dd1cc879f765bf2a0c5e297889dde910e57e2bb0eae417b90ab7a275";
538    const EK_KEYGEN_HEX: &str = "
539        8d0923ca8a2da2b4146ec25321122b8a5aa8afe0c03415273008a46ee83031e9
540        8aaaa125abc75d3b30322560c197e75dd0e48a348099f7b2144d7b8a8660a4a9
541        7bcf19c0583bd9bb2123033cd7bb5a14b08b817831a673a28170f5f6443c0551
542        913a327cba18c3a053c4040250403b70ab9588832403aa0fc37665e04980fe16
543        02e7d2715d9cbc00515df432a4f5b32b3bc92ae3f31700166d498123e9457650
544        9b712b18491b1435ee7ab7aeb1ad30d72348c3cc083abe24a8b12097bf32f792
545        476288eecc3bf630adcdac6aca7950d9839501a448500742bae37f109203a809
546        b2b960a307e25347a32c3eab79288173a878789b296e9e8c1c28c5bb3ac47260
547        1c9765f7b77225a810c7b85370bef4a5b079d2015ada54236b8f33840675f9b2
548        eb427a1b5974cd5c61b24010886c5a7bda5bbed974af7217f3338ad719cb308a
549        8bcb1b6d6ed2a1643736c29095e8a8452a3a36c7bb5ae58cbfdc61529466a90f
550        454ed6895b0861083dd1371999b2f559a3a487cf59a074fb49215ea6a6be656f
551        9af17b121a2447cb7985590e9738842b899ba57ac311810ae2d9794f37483dd6
552        bccb64af6d56588ae94665961c025c3aa2861974c236bca4bd8ff5509f7ab774
553        593e7c5549e57c2f18d15c0515094ad9a0dfaa0601e524f8231156b627bb25a0
554        dae04dacd0a66c041cef400583fc13bae640291a39a5c5ca8bca1ad5c683cdd8
555        290891a76940817dd8c9f52678780548e37a05806600801426dbb950c3b2ba34
556        e24cc77864dd91b39f1408c716a69df63342854e50a245fb50977b9410ded2c9
557        3f86b1f9d5a78b87bf81e51ca620a7e8566b19ab700964a40e3266415228d432
558        156e5cbdf52364a90483a55c39b3fb16fc7465a3f8ac801b70b9fb28b583444b
559        a5c1a73722d417a9d6d9b7deb08bc6b330ff27cf61ab8831e27758c64af3b121
560        50cb7b33abc29858106d63686d8762459abf9413850ae53ed6313f76f83d0fb8
561        ab34374e7df693e4a1b3e5a8ad0ce820afe1cf401acde650a8101b0946022d52
562        178e19613c42b88b07cc04eaa81dfb28ac9dc076236b67219a30f8f945dd57bd
563        2f335c52d59372308d38993467db53da3382b74867b616481bd0091a2232c111
564        6dc88a589db9107224a681008c67c589186a6929549beef92253db02b0c8aa9f
565        9c875a670266c72bcbdb4f5625043703c1a0457395832e4c335180462ed2220c
566        59e7361903c107d85457f6cd82eb820d0855d97675c2e0151cdb73c2885ddb78
567        49d74541580124e890116a65bc068093b57914e20c937c60a3eb25576f1a976a
568        9583839b672144cd4a45c3477a45c29b4e0bc2bdbd206585c9b7a7741c8b6b57
569        93a92797a15ae7a5b73a74b2971463634ba52aa792af05530730b6d0a89a3461
570        56b733677932bd36593a7496130cc458dcc5ca987c21960604ec8a8c53960566
571        80cbf3f1aac4f401aa5029fb2150434bb4706c31a2d54e4297939fa7c9c6f857
572        00613ceb65c7f03ac56eb86e2d27ccc6dcb7b9394dcdb942ff222d86958a996c
573        0cb6a8a44f97a70441c95fa71250116eec20863c0b5a643458788ab001f8869d
574        909922f51ee547a1e889255b3a0599c65842e5ab8d73872f053bc62392ea5389
575        6d328102d460bf1609583c22c3b43780ec6dad0319eb4a5a65b4756c3cb40eaa
576        935183bf8bd46abe76ba46e199103a5313c3235f49c915e097bca804de680781
577        d8365731beac6789a9203fb8787c4c070e00a13a6722a66a28236db179825653
578        d33ccf898b72c6b8450d97afd3276bb13340519cbeda708d12a858f54c49f454
579        7195b7788a9150b2649e36aa394121926d568a488b16d3557b2a32af57d11fc3
580        373f80a28c0723273d362502e7c428ab44d3cbabf9ea585fd1bd0c9846556a1e
581        196b78cf951592984a0a8487a78c2317d7aca4118e1049750a0788f0d66ad9e4
582        8e34731130aba0b427360a856d96d80b3f028fdd3aba9035c10106ba1c0934be
583        d36c6d7c7434249654ea89fc22137f4ab903653b75fb25b6f01635e6cc7d39cf
584        1508690562826b49b6ffc59e0dd35022e541f8ba0d304aa5b4e20606907c4243
585        95666c54abc2b8fb009847c86317685000c231215c8c15945860f6a85ddb98a8
586        c3a527f2749d3c027e694e8f0b0f0fa454913aadb635aadd452f7128bf775256
587        9669a8b93290eb92e78f6adff23e89f57f3890753b51f12f3f3a8a654e677847";
588    const EK_HEX: &str = "
589        2191abb6d6beee29c5780758a970349879b61a028deea5404731292346c81eeb
590        1d17766afbcaa68c867d91132f34a494e28caa767241b50902f4825771865fc8
591        d633736248963a253dd52c1a07c7177cb6df74c43c3c74d7133da9dc915fe14b
592        5bc30b86153e7b86be04189f4ce8ceb6e5cb69808d35640ac50335b1633162e2
593        8260300646153be7b26a84c1c565956f8577c95cfa80b68978cd7a79e49c7e80
594        8b21cdb460ed8388c4859670333b69980b2f9a0f78d7a5b9d8554893a0b5c524
595        47b15318828989816f43406abfa4950607a5a57cba62f5c4340ac3d294191687
596        c7d2b60ef2d83f6bec15b1618129b78577447bd1e36a87142b9c552c8cd2304f
597        35b81b3bc5196c3840c0346d513ec8862ba14a172c051402f7398707a6133c8e
598        88e5b57f9b9eec18aed1404ea1c47be5169e5d558e262a231a75925d25c49965
599        0c8ac031be580cfb185a0e0362ce2194c53713a630cb59600c1942908ff5142b
600        02364b74b3f5474301ab2da4188dca640961142f28584ebe810683c046b59cc2
601        cc07025da63a5e8c5e99e24fbae183329886279c2d58d0b8cac1c7b3033f05e7
602        65c75414748687ff03c00b36709d41c5968528f178cd03b813b69054d3e20fb6
603        a04a713b1b169ab5f0369810fc5cd967a866b70a2d87514605794930be9b7b5d
604        49d08f5cb08d84fc5beecb994dd2a5fc10aeeb4cce1ed251fd4815a9d06eea6a
605        77f18c56acabb1f7c478dd52218b7bbf9770a51c3ac96402548a1bb22dd0c29f
606        422330057179ec2d91e1aee27bbcb1eb4671324265e28676090896e332535ba2
607        e9169c54113f19a45229c2448078ae08f7213f347244527ef726855ca054ab7a
608        1ed1a40a285a60d04c00ccfa8fd318cf182705ddb888df652669b3224cf54ed6
609        d00db5121331b40c7e35b1945925e5ac1503a999899317a2370020d818733303
610        27db7cc1d430e32c6570fccffeea90a871a4b04ca9a8065ab20a6459f1806221
611        a5482802f14a68893b111607c4e085a5a6410c027c1bf5fa33adeccea576211f
612        272cc942acacc879de398477780a26e7545ed693edd66ad24772b0c0c4b033a7
613        3ed94e1d02c384d1a288a78f5d8838b5ac0b37855dc59b987ec9b0f5c6868b03
614        19f3372024fc15b247144f925d3edc453fb8551ec1ca734a37a3972b8cc9b84a
615        217a4d0bc0d08c815359aba59a07eed998133587e062cf735a8521a18ac2059e
616        3f142f12ccc5ae024ca33b8e4a285be5a3ced460c2c3675b956294ef2c61c96a
617        88441665d56b2029688efe4540dc35abf5957bf9477f58277851719547510f22
618        d01a13d0047c5b88372a654c732aff395dbf825829a06e6ae6923296218ab9ad
619        33d0b07a0b6a3f3b3dd8428a1f82b72a921853d94c5d400494494f0d168148b5
620        392f386b16c6056d6b8d767b7f4d918a9384c855c8b4de8450eb6ccc2df18528
621        cba0a3552d5704683aa3aaf52751fd5a17801a76a58a75038533c1576e8668b5
622        d290ceb20cb1ff91317d9b418311c11e617d10b4b0e9055aef5cc6ebf8b91f02
623        44ed9854fde463e55112a238038750cd016735613b9c461996b90a3b4d4b0d8b
624        8825ec087fb138ab868a04cb3c9249b5b118f81ed06b99e6b521f206a32a4013
625        e876c6c80b36af39ac197a21b78b5f2713aa45c74335f32676c2ac2902710ec4
626        2af5984687a672e0466831c7b436300b30351795b22b3a939347a7b744831baf
627        e78c3bf09a8224a958568f75d71f822941dfd64d8969ada5b73c847272b59144
628        91bbce5f0c09ef501babd95190f93201ab413e833f7924b696d5c2b622722459
629        32a6d9c35e1670aa3bc57cd41b8d61a52aaa6d80bb774f922ecb41cd58469c4d
630        cac698f6730890237602064b148650c17e6edcb3713382fe755e46f3abe0c6c4
631        5184ae5c5c861e35a0e2bab2b4e8a4775584351170152512c7e99e96f285b916
632        605b53b91d6c501f51a93129483046cb2dfa16d4a540ed2b6ab18bbdfba2b201
633        baa20297ab1fe7133757c07dc041971aa4219157d77628f31aaf520650a2250a
634        dad1524e58268d21cc62e2469c8425e86359dbcaa066acb466e06947a1c144e5
635        3ee0dc930432c806c493b2bb25757b1e2c775a27a896ec25b58e8a78d4dac918
636        15a1f3d90635ea25ae13cefa77877dc0c2f0678ade307243b438c54aa69dda53
637        9b8a41c1b3c98c77fceb2c0bc2025a272d927943ea338cdf32a0dec8b187df5c";
638    const DK_HEX: &str = "
639        e4b509441c4df0443db6bc96f707be70d0b8af557ef228c78050699779b16550
640        2ba1a600230c849225552250b78c10c2a5e244b5364383d29bd8a75889da089d
641        c4485dc4289a559931c6cce9c8b95df482e5496ffde9b526f0055e241a19a7cb
642        15a2a85ea042cae75eaae29de68ca3cc1c1de7c1a0c187897f5a45c6a9225171
643        6d801c36f315b3738a489bc4ccaa2094b5f9af9524252fdb1f822b5ea1c63e66
644        96b0dbc1177c9124cdf9cdbd8529f78088d232693b67ad1fc08362c3c6b44213
645        e559a8a48562505565bc01cc2fa62acf736e3e5040b10b621b606b57598548ea
646        7f040203dc777580c2326dbb36dbc3befe89b977179d32001414d1100a70025b
647        652e8517ccd0b4c44012bb38d99599f27866a222f96b347483369a496627e47e
648        5f8a6f0aa899c7427822c25bce4205ccd22e1a00ae6b30cd190243d51519a4c5
649        1602568f883ba4bf8885998c0a0eabcb14e7a9d801376101a0e94282ca82492f
650        ba69d301920282b5aaba6940442bd7703855390f5db42d7f9794e7bb192e628d
651        119561a2597ae0e7301e7668a8a3aca6db1716465c48400fb1e841751235f641
652        77854295bc7a9c060c7f5e3b9da8eba23b9199bdb69f524b8be03b6096f2ab36
653        b377251888ac728657b66e5f54c71ae311e7a0528baac222da7c5b519d4c04ce
654        fba133423666663a9154640a6e767c14575d910a80a8a71f0936433be8ce46a5
655        a8e5a70f279bc3cee79665cc9835a3c295fc325285650bd0b1046669ab775a26
656        49259d4903bc2554bd741a29027b6c65a99a16c97f79accf2818b90800a5fc29
657        b401c1705892500b2899d1863390aa7b4884f2fc2593d437a1232777e6466f52
658        83c5b05d8b06bed8860f35129d92fc57e0e32f504611835b6083c6383ed99124
659        f282cac39d8fb17b5e079d25dc4756f30185653fcc408d4ec3c6845020a2073b
660        b2569113017ce9121bdf0a9b43d850b03510f5b10b5a92b072f6b26a38a21536
661        4d526a44176c0869c4895aa248f3d108e4dc5959ea2adf5a4ebf90b6cf05b9bc
662        8444d118547967957ff9a81e31912eb20158836d3b63c45a628aa1d35790c512
663        c0b7b0788b390bd687d5aa2dc6d63331a7152fcb36af850234d98d0f22304f50
664        a4767c9293a185fac14a11d124a9d4ce53972de8eb3f57e471d5a3414d752bac
665        832f12eca919d84d3832180ce6cf3b975da2e544958ca3a96c1e549067ff498e
666        accc3eb475c5fee3b85017c087663d0792324280c111d83105d67929aa64056b
667        ae2e5823bd7762750a051c24a466fb003e2b61e0b675f62c4764038fa2489983
668        f21602240edf812e0a557ac8e6a812b4108141937d9a053f052c218b69e3169e
669        4cea78602172f7000d6631b09a42b1efab6771d01ca58535ebb63d8924444419
670        a26ea3bb60e96abea8c203271fa41a008e889f141b2af3f4bef78228cc00632d
671        f9285a2624ba71118015ca2f0c4f2a641f753b257b90a26a016bcdc666fa065e
672        cf4257c0db1eee815e5a7285d944a1d9e18986a84f5980955960b79a957a893c
673        a206e2161e037498c896ed49659e32893e84c992cb3ea814a636d87a77f3479b
674        ab07bb9a6af00a71c49a22fad59a818b26513c27aaf389b1f5737176a46809a2
675        9337aec0c8569df87402a56327dcce27e5088959bdbc39c8d324adaef1060146
676        ad3085cd737007fd550ae78135b98301f0667164375a83ec2e28a47255f1c994
677        9cccbad619186825664aad07c35ea7241d29072b9bab1ffac5b4427c2b7c026b
678        3dcaaf765a67d0e1630dab279376439b98141dbb7e54f104a4a00a64d743b6d2
679        598e757fae50c9811c7d0c0864a144899bbc923a6722a49827a0c6aa3d62c558
680        133331537dda0cc2d2f8702d6282aa4220b0d31be6eb2bb5147bd2822f587945
681        b7444de151925a03c3e9969624155c21e205ec75cc5957a2268962a0f5861036
682        a55bc50e1aeaa0022664c761b530c9790b3591f7d587ce40ba48d43f390a89f3
683        660c98ca9c011ccd1dc1616469c008e2974fe128e802c96a9271aed831797c85
684        7c345427791d6f3625c2dc3931733675d37165ab4e50174587679cf3319386b9
685        7df1685aba1ab2caa00a1378cd577511ade21ec94236a9944e07e885dc12602e
686        762fa5b284d73c8f9cd8395da0c3b075bd9eec465b6a4ff6e125546a3903ea84
687        2191abb6d6beee29c5780758a970349879b61a028deea5404731292346c81eeb
688        1d17766afbcaa68c867d91132f34a494e28caa767241b50902f4825771865fc8
689        d633736248963a253dd52c1a07c7177cb6df74c43c3c74d7133da9dc915fe14b
690        5bc30b86153e7b86be04189f4ce8ceb6e5cb69808d35640ac50335b1633162e2
691        8260300646153be7b26a84c1c565956f8577c95cfa80b68978cd7a79e49c7e80
692        8b21cdb460ed8388c4859670333b69980b2f9a0f78d7a5b9d8554893a0b5c524
693        47b15318828989816f43406abfa4950607a5a57cba62f5c4340ac3d294191687
694        c7d2b60ef2d83f6bec15b1618129b78577447bd1e36a87142b9c552c8cd2304f
695        35b81b3bc5196c3840c0346d513ec8862ba14a172c051402f7398707a6133c8e
696        88e5b57f9b9eec18aed1404ea1c47be5169e5d558e262a231a75925d25c49965
697        0c8ac031be580cfb185a0e0362ce2194c53713a630cb59600c1942908ff5142b
698        02364b74b3f5474301ab2da4188dca640961142f28584ebe810683c046b59cc2
699        cc07025da63a5e8c5e99e24fbae183329886279c2d58d0b8cac1c7b3033f05e7
700        65c75414748687ff03c00b36709d41c5968528f178cd03b813b69054d3e20fb6
701        a04a713b1b169ab5f0369810fc5cd967a866b70a2d87514605794930be9b7b5d
702        49d08f5cb08d84fc5beecb994dd2a5fc10aeeb4cce1ed251fd4815a9d06eea6a
703        77f18c56acabb1f7c478dd52218b7bbf9770a51c3ac96402548a1bb22dd0c29f
704        422330057179ec2d91e1aee27bbcb1eb4671324265e28676090896e332535ba2
705        e9169c54113f19a45229c2448078ae08f7213f347244527ef726855ca054ab7a
706        1ed1a40a285a60d04c00ccfa8fd318cf182705ddb888df652669b3224cf54ed6
707        d00db5121331b40c7e35b1945925e5ac1503a999899317a2370020d818733303
708        27db7cc1d430e32c6570fccffeea90a871a4b04ca9a8065ab20a6459f1806221
709        a5482802f14a68893b111607c4e085a5a6410c027c1bf5fa33adeccea576211f
710        272cc942acacc879de398477780a26e7545ed693edd66ad24772b0c0c4b033a7
711        3ed94e1d02c384d1a288a78f5d8838b5ac0b37855dc59b987ec9b0f5c6868b03
712        19f3372024fc15b247144f925d3edc453fb8551ec1ca734a37a3972b8cc9b84a
713        217a4d0bc0d08c815359aba59a07eed998133587e062cf735a8521a18ac2059e
714        3f142f12ccc5ae024ca33b8e4a285be5a3ced460c2c3675b956294ef2c61c96a
715        88441665d56b2029688efe4540dc35abf5957bf9477f58277851719547510f22
716        d01a13d0047c5b88372a654c732aff395dbf825829a06e6ae6923296218ab9ad
717        33d0b07a0b6a3f3b3dd8428a1f82b72a921853d94c5d400494494f0d168148b5
718        392f386b16c6056d6b8d767b7f4d918a9384c855c8b4de8450eb6ccc2df18528
719        cba0a3552d5704683aa3aaf52751fd5a17801a76a58a75038533c1576e8668b5
720        d290ceb20cb1ff91317d9b418311c11e617d10b4b0e9055aef5cc6ebf8b91f02
721        44ed9854fde463e55112a238038750cd016735613b9c461996b90a3b4d4b0d8b
722        8825ec087fb138ab868a04cb3c9249b5b118f81ed06b99e6b521f206a32a4013
723        e876c6c80b36af39ac197a21b78b5f2713aa45c74335f32676c2ac2902710ec4
724        2af5984687a672e0466831c7b436300b30351795b22b3a939347a7b744831baf
725        e78c3bf09a8224a958568f75d71f822941dfd64d8969ada5b73c847272b59144
726        91bbce5f0c09ef501babd95190f93201ab413e833f7924b696d5c2b622722459
727        32a6d9c35e1670aa3bc57cd41b8d61a52aaa6d80bb774f922ecb41cd58469c4d
728        cac698f6730890237602064b148650c17e6edcb3713382fe755e46f3abe0c6c4
729        5184ae5c5c861e35a0e2bab2b4e8a4775584351170152512c7e99e96f285b916
730        605b53b91d6c501f51a93129483046cb2dfa16d4a540ed2b6ab18bbdfba2b201
731        baa20297ab1fe7133757c07dc041971aa4219157d77628f31aaf520650a2250a
732        dad1524e58268d21cc62e2469c8425e86359dbcaa066acb466e06947a1c144e5
733        3ee0dc930432c806c493b2bb25757b1e2c775a27a896ec25b58e8a78d4dac918
734        15a1f3d90635ea25ae13cefa77877dc0c2f0678ade307243b438c54aa69dda53
735        9b8a41c1b3c98c77fceb2c0bc2025a272d927943ea338cdf32a0dec8b187df5c
736        f84e555ebe890141df38cd7d478b75c658d3f5e9d8a114494478289a74a042fd
737        59434802516a1a73500c7ae6875f2df34a48054af2d4d19b60db6e7432acf2e7";
738    const M_HEX: &str = "2f1e2ca7bd72af847cac38cdefc4d345909d7517543edf32e2fc491ba05eb5c3";
739    const C_HEX: &str = "
740        d892e0948544d020ece56f00c6495b9bd1c469ac0f134002c864d10cf61c6c78
741        87890276a7546310ea077741d83428f22b60e2a40ed8bbf5d9227893bb0b7417
742        df4380323426ebc9744ef35a1bb6dab1181ff0b677e8c9b6574360994f96eb87
743        c3524e15e468283169d90d8a994bed0da9cd778ca239ca6c225390221fd408a3
744        eae541a032d714f3d078dd1ec722ca83fffc92a416f46fc1e8710c3f8e9cd452
745        c016f483985a5c1d951fe2b03c4ac2c9a0ba71f6ffa4b29ede76df69cad594d1
746        0618b94e783fddaf11cc801ed5036feb4a70d071079944b7d9f1ffbb98f123dc
747        34a52a40de57e079a1f8b180da9e6ecaa47a111f3c054dd9563d7e74b95ac65a
748        b453afe0bf0dcec5578fe6fbc2d9eb91932799ea64b2dfa95b2c9c6e931ffb0a
749        5a499ddc42c6d6b563d5a6c1f34dbcb36f57e6c10c69feadc70a3d01f5f3715f
750        6bea0d3e2e7ac2b04e01ca80a2e187bb7d9efa5ee257b9c3caea3947d785ca56
751        a76d0b6692a69418935dfc36fd9b26758f5e1043d5c2a6d5cc1556aed592bbcd
752        652f4024bf324910f8caa2415171f3ec8b1cc494f2b519cbd2b13d317672de60
753        f0a3d400e6079f85c3cdad0da5a17f2efaccec03d3c2e85b063ac3cb29f2fc48
754        50705b8d472e35dc12d06a7d021db302ca883dc7d4a57b7a9e1d023960d4ea5e
755        39c9d5bf328b8e4ac0afc3a6c990f598f373059ad28edbeb90412472c637877e
756        a82e7105caf467a15ce961695f2ff04c750e277e78c1ce9e490a5c45168936bc
757        f79261b95fb20afccdce790e0aedde204da4f6734700fc1ce05239f4ead0a496
758        fd8f733049ef9b6ecc54d5c635b56d6fcac7aba1ba32d707d888b820ce3e794c
759        2562798210d726c6c5db3d22a214f3f2a1f2477fcb77377af41d0f3f0ac635e7
760        bf0674cebb672ea93b9504efa8c5aafb7458e41a1d85c28841877ff2f444005c
761        d1c1e4227573e8151184786489c7cc39549f8a29b9c4d68be38f24081fe239c5
762        d0e14ad5e361720836a72a99ce88e47bf3e6a1a9eb57cdc3724cb4789fb88c13
763        5ca9ccd94ccc9bd73ee8a796bfea36e894ed27b0791e8ad23fcda5ae7c167d57
764        74aa00468d97bd0fd2b9bd78d01e1e39a66994a369212d95d7886be00f2b7a1f
765        187c85b873f33cb7d3e4c23993f7bc9ff7abbc6fd9d5c54f06e5f31b79da992b
766        c0f2aa79a36bd00483ca0add2dcb2be36c27dc913668f030128e82281e5e66e8
767        7c75dce3fc60431ce6a19c6c45e19a2d84430e945034b7da6c506ef009493af2
768        2f701088e01c20acb21972ff28d284d2a57a69d108161938f028011ba985e2b9
769        25532b52eed878646579183eb0ef3fb57be1e05c1bd2b68d0e4a9ce104e0ecdf
770        b30268e6c007812fd88498d97795447e7f88ce8a3bcd161b1e34dcf77413b778
771        f88a3ad66f247f7664d0f6af31ec1dadc00fec09d6ac63fc8a802c3752484b5b
772        43555381043fdd30ef5d7dcf4f5b68eb89f08a0fe5ecb65d6d9418ca5283ca39
773        8d3129cd3b44edf5e3568c953ce2b66a28b473cab0c43918a05c5a4358d13926
774        04495d06b395187caa6b36050b436b218485466cb6643eda7ac66aaeea758715
775        a22a8849066879cc966da7e0f7e843a1e234920aecf2a4fed2f78c69a6c103a5
776        a535d77976b1e40bb0d75d6e370212017734f2b0be9f3f87c5583634ea6a998d
777        8fe9ce5e5905f2a6abb9d635425d06731c1227eb634a3603d081cb1a7c2b0c16
778        85942efd6f65992dd39aa67cd954fa0310a0f5866ac6121e27e349d5c2adf37a
779        672c1dfb021a855000f0c0c29489926b997930b20df641120ef8605cfd9482ee
780        a9344918aec689f580a94508f318e63eadc3eb7486b9fcd7a92ba16f5e02cf78
781        ef73f528fae3a43d451c58261a82b3735e09d4f4679ce112803505006cbae6ea
782        f641a6f66b0c51ee90095735d87ad88c3d19cd7888248fcc3872939605f20597
783        6ef6c6aa52f8316e9def841594373fcd177f0e24d8975653a29f738fa8f0d457
784        f2d72b7c00a4b7a66fb080f705a8bd599314354e65842af598b8a2a40f6cb320
785        8762ac6cd467d06a2e987a7af72e355bea297da87bc9250ebe8d8f85cf292200
786        a21d93475ba46be78c17c1605c10b97659859f08f980114955e68361e180c980
787        15aa46a776070c4f2b328a903a70ad226742f0022279179fd2530390f190e300
788        8f7202c83a6a15866df848c8a150d12287451dec8ee7f04c1c3121e4bba687d7";
789    const K_HEX: &str = "5087e3b0c90bf601dd6501e071270eff8683621e9f5d67a7a668e50c4f460a75";
790
791    // 1) 种子展开(keyGen 案例)
792    let d: [u8; 32] = hex(D_HEX).try_into().expect("32 bytes");
793    let z: [u8; 32] = hex(Z_HEX).try_into().expect("32 bytes");
794    let (ek, _dk) = k1024::keypair_from_seed(&d, &z);
795    if ek.as_bytes() != hex(EK_KEYGEN_HEX).as_slice() {
796        return Err(crate::Error::SelfTestFailed("mlkem1024"));
797    }
798    // 2) 封装(encapDecap 案例自带 ek)
799    let ek2 = k1024::EncapsKey::from_bytes(hex(EK_HEX).as_slice())?;
800    let m: [u8; 32] = hex(M_HEX).try_into().expect("32 bytes");
801    let (c, ss) = k1024::encapsulate_with_seed(&ek2, &m)?;
802    if c.as_bytes() != hex(C_HEX).as_slice() || ss.expose_bytes() != hex(K_HEX).as_slice() {
803        return Err(crate::Error::SelfTestFailed("mlkem1024"));
804    }
805    // 3) 解封装(同案例 dk)
806    let dk2 = k1024::DecapsKey::from_bytes(hex(DK_HEX).as_slice())?;
807    let ss2 = k1024::decapsulate(&dk2, &c);
808    if ss2.expose_bytes() != hex(K_HEX).as_slice() {
809        return Err(crate::Error::SelfTestFailed("mlkem1024"));
810    }
811    Ok(())
812}
813
814// ---------------------------------------------------------------------------
815// KAT:RSA PKCS#1 v1.5(内建 KAT 密钥签名/验证;openssl 交叉锚定)
816// ---------------------------------------------------------------------------
817
818const RSA_KAT_PKCS8_HEX: &str = include_str!("kat_rsa_key.hex");
819const RSA_KAT_SIG_HEX: &str = include_str!("kat_rsa_sig.hex");
820
821fn hex(s: &str) -> Vec<u8> {
822    let clean: String = s.chars().filter(|c| !c.is_whitespace()).collect();
823    (0..clean.len())
824        .step_by(2)
825        .map(|i| u8::from_str_radix(&clean[i..i + 2], 16).expect("valid hex"))
826        .collect()
827}
828
829fn kat_rsa_pkcs1v15() -> Result<(), crate::Error> {
830    use crate::sign::rsa;
831    let sk = rsa::SigningKey::from_pkcs8_der(&hex(RSA_KAT_PKCS8_HEX))?;
832    let sig = sk.sign_pkcs1v15(256, b"sample")?;
833    if sig != hex(RSA_KAT_SIG_HEX) {
834        return Err(crate::Error::SelfTestFailed("rsa-pkcs1v15"));
835    }
836    let pk = rsa::VerifyKey::from_spki_der(&hex(RSA_KAT_PUB_SPKI))?;
837    pk.verify_pkcs1v15(256, b"sample", &sig)?;
838    Ok(())
839}
840
841const RSA_KAT_PUB_SPKI: &str = include_str!("kat_rsa_spki.hex");
842
843// ---------------------------------------------------------------------------
844// KAT:CTR-DRBG(CAVP [AES-256 no df] 流程:Instantiate → Reseed →
845// Generate → Generate,第二次输出逐字节比对)
846// ---------------------------------------------------------------------------
847
848fn kat_drbg() -> Result<(), crate::Error> {
849    const EI_HEX: &str = "e4bc23c5089a19d86f4119cb3fa08c0a4991e0a1def17e101e4c14d9c323460a7c2fb58e0b086c6c57b55f56cae25bad";
850    const EIR_HEX: &str = "fd85a836bba85019881e8c6bad23c9061adc75477659acaea8e4a01dfe07a1832dad1c136f59d70f8653a5dc118663d6";
851    const EXPECT_HEX: &str = "b2cb8905c05e5950ca31895096be29ea3d5a3b82b269495554eb80fe07de43e193b9e7c3ece73b80e062b1c1f68202fbb1c52a040ea2478864295282234aaada";
852    let mut d = CtrDrbg::new(&hex(EI_HEX), b"")?;
853    d.reseed(&hex(EIR_HEX), b"")?;
854    let mut discard = [0u8; 64];
855    d.generate(&mut discard)?;
856    let mut out = [0u8; 64];
857    d.generate(&mut out)?;
858    if out == hex(EXPECT_HEX)[..] {
859        Ok(())
860    } else {
861        Err(crate::Error::SelfTestFailed("ctr-drbg"))
862    }
863}
864
865use crate::drbg::CtrDrbg;
866
867#[cfg(test)]
868mod failure_injection {
869    use super::*;
870
871    #[test]
872    fn failed_state_gates_and_recovers() {
873        reset_for_tests();
874        // 正常自检通过
875        assert_eq!(run_power_on_self_tests(), SelfTestStatus::Passed);
876        // 注入失败:模块进入错误状态
877        force_fail_for_tests("injected");
878        assert_eq!(status(), SelfTestStatus::Failed("injected"));
879        assert_eq!(
880            ensure_passed(),
881            Err(crate::Error::SelfTestFailed("injected"))
882        );
883        // 复位后恢复
884        reset_for_tests();
885        assert_eq!(run_power_on_self_tests(), SelfTestStatus::Passed);
886    }
887}
888
889#[cfg(test)]
890mod kat_debug {
891    use super::*;
892
893    #[test]
894    fn debug_ecdsa_kat() {
895        let d = hex("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
896        let sk = crate::sign::ecdsa::p256::SigningKey::from_seed(d.as_slice().try_into().unwrap());
897        let sig = sk.sign(b"sample").unwrap();
898        println!(
899            "sig  = {}",
900            sig.iter().map(|b| format!("{b:02x}")).collect::<String>()
901        );
902        println!("kat  = {}", hex("3046022100efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716022100f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8").iter().map(|b| format!("{b:02x}")).collect::<String>());
903        println!(
904            "eq   = {}",
905            sig == hex(
906                "3046022100efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716022100f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8"
907            )
908        );
909        let q = sk.public_key_sec1();
910        let vk = crate::sign::ecdsa::p256::VerifyKey::from_sec1_point(&q).unwrap();
911        println!("verify = {:?}", vk.verify(b"sample", &sig));
912    }
913}