Skip to main content

ic_fips/
selftest.rs

1//! Known-answer tests for every implemented algorithm.
2//!
3//! FIPS 140-3 requires a *cryptographic algorithm self-test* (CAST) for each
4//! approved security function, run before that function is first used, plus a
5//! pre-operational software integrity test. This module provides both.
6//!
7//! Each algorithm implements [`ic_core::traits::SelfTest`], so the table below
8//! is a list of function pointers rather than a re-implementation of each
9//! vector — the test that runs at startup is the same code path the unit tests
10//! exercise.
11
12use ic_core::traits::SelfTest;
13use ic_core::Result;
14
15/// The result of one known-answer test.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct TestOutcome {
18    /// The ontology identifier of the algorithm under test.
19    pub algorithm: &'static str,
20    /// Whether its known-answer test passed.
21    pub passed: bool,
22}
23
24/// The outcome of a full self-test run.
25#[derive(Debug, Clone)]
26pub struct SelfTestReport {
27    /// How many tests passed.
28    pub passed: usize,
29    /// How many failed.
30    pub failed: usize,
31    /// Per-algorithm results, in table order.
32    pub outcomes: [TestOutcome; TEST_COUNT],
33}
34
35impl SelfTestReport {
36    /// The algorithms whose tests failed.
37    pub fn failures(&self) -> impl Iterator<Item = &TestOutcome> {
38        self.outcomes.iter().filter(|o| !o.passed)
39    }
40
41    /// Whether every test passed.
42    pub fn all_passed(&self) -> bool {
43        self.failed == 0
44    }
45}
46
47/// One entry in the CAST table.
48type Cast = (&'static str, fn() -> Result<()>);
49
50/// Every algorithm with a known-answer test, in a fixed order.
51///
52/// The order is stable so that a failure report is comparable between runs.
53static CASTS: &[Cast] = &[
54    // Hashes
55    ("sha2-224", ic_hash::Sha224::self_test),
56    ("sha2-256", ic_hash::Sha256::self_test),
57    ("sha2-384", ic_hash::Sha384::self_test),
58    ("sha2-512", ic_hash::Sha512::self_test),
59    ("sha2-512-224", ic_hash::Sha512_224::self_test),
60    ("sha2-512-256", ic_hash::Sha512_256::self_test),
61    ("sha3-224", ic_hash::Sha3_224::self_test),
62    ("sha3-256", ic_hash::Sha3_256::self_test),
63    ("sha3-384", ic_hash::Sha3_384::self_test),
64    ("sha3-512", ic_hash::Sha3_512::self_test),
65    ("shake128", ic_hash::Shake128::self_test),
66    ("shake256", ic_hash::Shake256::self_test),
67    ("cshake128", ic_hash::CShake128::self_test),
68    ("cshake256", ic_hash::CShake256::self_test),
69    ("tuplehash128", ic_hash::TupleHash128::self_test),
70    ("tuplehash256", ic_hash::TupleHash256::self_test),
71    ("parallelhash128", ic_hash::ParallelHash128::self_test),
72    ("parallelhash256", ic_hash::ParallelHash256::self_test),
73    // MACs
74    ("hmac-sha2-256", ic_mac::HmacSha256::self_test),
75    ("hmac-sha2-384", ic_mac::HmacSha384::self_test),
76    ("hmac-sha2-512", ic_mac::HmacSha512::self_test),
77    ("hmac-sha2-512-256", ic_mac::HmacSha512_256::self_test),
78    ("hmac-sha3-256", ic_mac::HmacSha3_256::self_test),
79    ("hmac-sha3-512", ic_mac::HmacSha3_512::self_test),
80    ("cmac-aes-128", ic_mac::CmacAes128::self_test),
81    ("cmac-aes-192", ic_mac::CmacAes192::self_test),
82    ("cmac-aes-256", ic_mac::CmacAes256::self_test),
83    ("kmac128", ic_mac::Kmac128::self_test),
84    ("kmac256", ic_mac::Kmac256::self_test),
85    ("poly1305", ic_cipher::Poly1305::self_test),
86    ("blake2b", blake2b_self_test),
87    // Block ciphers and AEADs
88    ("aes-128", ic_cipher::Aes128::self_test),
89    ("aes-192", ic_cipher::Aes192::self_test),
90    ("aes-256", ic_cipher::Aes256::self_test),
91    ("aes-128-gcm", ic_cipher::Aes128Gcm::self_test),
92    ("aes-192-gcm", ic_cipher::Aes192Gcm::self_test),
93    ("aes-256-gcm", ic_cipher::Aes256Gcm::self_test),
94    ("chacha20-poly1305", ic_cipher::ChaCha20Poly1305::self_test),
95    ("aes-128-gcm-siv", ic_cipher::Aes128GcmSiv::self_test),
96    ("aes-256-gcm-siv", ic_cipher::Aes256GcmSiv::self_test),
97    ("aes-128-kw", ic_cipher::Aes128Kw::self_test),
98    ("aes-256-kw", ic_cipher::Aes256Kw::self_test),
99    ("aes-192-kwp", ic_cipher::Aes192Kwp::self_test),
100    ("aes-256-kwp", ic_cipher::Aes256Kwp::self_test),
101    // KDFs
102    (
103        "hkdf-sha2-256",
104        ic_kdf::Hkdf::<ic_mac::HmacSha256>::self_test,
105    ),
106    ("argon2id", argon2id_self_test),
107    ("pbkdf2-hmac-sha2-256", pbkdf2_hmac_sha2_256_self_test),
108    (
109        "sp800-108-counter-hmac-sha2-256",
110        sp800_108_counter_hmac_sha2_256_self_test,
111    ),
112    // Post-quantum
113    ("ml-kem-512", ml_kem_512_self_test),
114    ("ml-kem-768", ml_kem_768_self_test),
115    ("ml-kem-1024", ml_kem_1024_self_test),
116    ("ml-dsa-44", ml_dsa_44_self_test),
117    ("ml-dsa-65", ml_dsa_65_self_test),
118    ("ml-dsa-87", ml_dsa_87_self_test),
119    // DRBGs
120    ("hmac-drbg-sha2-256", ic_drbg::HmacDrbgSha256::self_test),
121    ("ctr-drbg-aes-256", ic_drbg::CtrDrbg::self_test),
122    // Elliptic curve
123    ("x25519", ic_ec::X25519::self_test),
124    ("ed25519", ic_ec::Ed25519::self_test),
125    ("ecdh-p256", ic_ec::p256::EcdhP256::self_test),
126    ("ecdsa-p256-sha256", ic_ec::p256::EcdsaP256Sha256::self_test),
127    ("ecdh-p384", ic_ec::p384::EcdhP384::self_test),
128    ("ecdsa-p384-sha384", ic_ec::p384::EcdsaP384Sha384::self_test),
129    ("ecdh-p521", ic_ec::p521::EcdhP521::self_test),
130    ("ecdsa-p521-sha512", ic_ec::p521::EcdsaP521Sha512::self_test),
131    // Constructions
132    ("hpke-x25519-sha256", ic_hpke::Hpke::self_test),
133    ("hpke-p384-sha384", ic_hpke::p384::HpkeP384::self_test),
134    ("shamir-gf256", ic_cipher::shamir::Shamir::self_test),
135    // RSA
136    //
137    // Six 2048-bit private-key operations, which dominate the runtime of this
138    // suite. They stay in because every ontology entry marked Available has to
139    // have a CAST; an algorithm offered without one is exactly the gap this
140    // table exists to close.
141    ("rsa-pkcs1-sha256", ic_rsa::Pkcs1Sha256::self_test),
142    ("rsa-pkcs1-sha384", ic_rsa::Pkcs1Sha384::self_test),
143    ("rsa-pkcs1-sha512", ic_rsa::Pkcs1Sha512::self_test),
144    ("rsa-pss-sha256", ic_rsa::PssSha256::self_test),
145    ("rsa-pss-sha384", ic_rsa::PssSha384::self_test),
146    ("rsa-pss-sha512", ic_rsa::PssSha512::self_test),
147];
148
149/// PBKDF2-HMAC-SHA256 known-answer test.
150///
151/// RFC 7914 section 11 publishes PBKDF2-HMAC-SHA256 vectors, but at one
152/// iteration, which `pbkdf2` refuses as below SP 800-132's floor of 1000. So
153/// this case runs at 1000 iterations, and the expected value is
154/// `hashlib.pbkdf2_hmac`'s, OpenSSL's implementation rather than this one's;
155/// the RFC vector itself passes against `hashlib`. Recorded in docs/FIPS.md.
156fn pbkdf2_hmac_sha2_256_self_test() -> Result<()> {
157    let mut got = [0u8; 32];
158    ic_kdf::pbkdf2::<ic_mac::HmacSha256>(
159        b"IronCrypto self-test password",
160        b"sixteen-byte-salt",
161        1000,
162        &mut got,
163    )?;
164    let mut want = [0u8; 32];
165    ic_core::codec::hex_decode(
166        b"c995fdfb115da8f22db24f086faac0b9e75c0233c524559639483cece2aa182a",
167        &mut want,
168    )?;
169    ic_core::ensure!(
170        ic_core::ct::verify(&want, &got),
171        SelfTestFailed,
172        "pbkdf2-hmac-sha2-256"
173    );
174    Ok(())
175}
176
177/// SP 800-108 counter-mode KDF with HMAC-SHA256, known-answer test.
178///
179/// No published vector for this input encoding was available offline, so the
180/// expected value comes from an independent transcription of the counter-mode
181/// construction -- `HMAC(key, [i]_32 || label || 0x00 || context || [L]_32)`
182/// per block -- in Python's `hmac`, sharing nothing with `ic_kdf`. Two blocks,
183/// so the counter's increment is covered. Recorded in docs/FIPS.md.
184fn sp800_108_counter_hmac_sha2_256_self_test() -> Result<()> {
185    let mut key = [0u8; 32];
186    for (i, b) in key.iter_mut().enumerate() {
187        *b = i as u8;
188    }
189    let mut got = [0u8; 48];
190    ic_kdf::kbkdf_counter::<ic_mac::HmacSha256>(&key, b"IronCrypto", b"self-test", &mut got)?;
191    let mut want = [0u8; 48];
192    ic_core::codec::hex_decode(
193        b"9388d4be5f62e976766497a16a3dd6edca95efa5009a812d59a30ffff509a043255c98dbcf5c2e360079f967c384e327",
194        &mut want,
195    )?;
196    ic_core::ensure!(
197        ic_core::ct::verify(&want, &got),
198        SelfTestFailed,
199        "sp800-108-counter-hmac-sha2-256"
200    );
201    Ok(())
202}
203
204/// BLAKE2b known-answer test: RFC 7693 Appendix A.
205///
206/// BLAKE2b has a variable output length and so does not fit the fixed-size
207/// `Digest`/`SelfTest` pair; its CAST is spelled out here instead.
208fn blake2b_self_test() -> Result<()> {
209    let mut got = [0u8; 64];
210    ic_hash::Blake2b::hash(b"abc", &mut got)?;
211    let mut want = [0u8; 64];
212    ic_core::codec::hex_decode(
213        b"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
214        &mut want,
215    )?;
216    ic_core::ensure!(ic_core::ct::verify(&want, &got), SelfTestFailed, "blake2b");
217    Ok(())
218}
219
220/// ML-KEM-768 known-answer test: ACVP ML-KEM-keyGen-FIPS203, tcId 26.
221///
222/// Key generation is deterministic in `(d, z)`, so this one case exercises the
223/// sampler, the NTT and the whole encapsulation-key encoding together. Only the
224/// encapsulation key is compared: the decapsulation key comes out of the same
225/// computation and contains this verbatim, so checking both would double the
226/// size of this file and catch nothing more.
227///
228/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
229/// testvectors/ml-kem-768-keygen.json, where the full set of 25 is checked.
230fn ml_kem_768_self_test() -> Result<()> {
231    const D: &[u8] = b"\
232         E582B7D75E6C80B05AE392A1FC9F7153B12390FD99930368CC67A768BAEBC8A0";
233    const Z: &[u8] = b"\
234         1CDACB8740C0B87C4A379575F187B367CBFA3B300BF591B109F79816E9CBE8F0";
235    const EK: &[u8] = b"\
236         28C793778741B80B02B4339F2AA4347255B099F17264E1B8CC0A2C7C2A1A79F7997B907FD0496C6E6C8AD7714F5F\
237         339D75F11F625591A869BE1175AE47F05FD4313468232BA6957D7807B824F445AC99A0D568AB1AD54DCA8249D148\
238         2E61275F52248C77F61A4248753188CD1794CD0A465EC0DC4B025985C461B74E76286E4C37E77405695CC9FD0654\
239         374B427A20343AEC0FF1A187768273BFC4905472A1DA387F14559D6CE87313F6A5B6138434539F9A13684055B177\
240         E543F8B40F432ABD7CC49989A50A9084C660913F45A8593B17499BC4CF936C2BC1851421CB986808A0EF30AFE97A\
241         AB5B8B8EB3F0B3506A95B91563A0E57DB7231044987EF141BDAB3537C316AD16F17805A81F29329879A94E96157E\
242         4B7447F7D59603B21BD896CC47B7CD4E232322EB9C5D2215696BCFFCA3A04EFCC4C5D9CC39AC9A6E8700D38C244B\
243         0169E7FA1FE81B4B10365E74E6A1F7F756D11ACDC84043F81006D62995376C22535958FEB53F78117EE0F61C4C86\
244         2640D06DC57A2B8BE62A41A642AF3BC63F6BAC98BBBBFF70570F37B8F8D9572F2735657A6C98F96CAF57A8498687\
245         20B2640B8BB2732237A1F984C18872D10289CE43C952C9257E06529AEB76AFD127B17596FD25C5216C9CABD9B18E\
246         FC50E87BBB04568BB7D5C4E9288C006483AF5912E19108573700BD10CD77224B80659EA75AA74270B33AC4008B73\
247         8BFEE271E78658C8742FF13C96AD0781A03C7576CA26DD58B52980BA58C0505E446AFA140CDCEA0490DB1F9B1881\
248         5D4314B2459CACC562441C91F4084E5426C88E632CF7482E79907911D06473260835D7B85E7856A829AEA0381707\
249         B939CE86882CC09C4448C6AE94A9C303107C5667EEFB8DF7763CC21189A3C590C40AA51F491503A7935EC08F4FC3\
250         00CBE607ED8C9100C29FBF45584B13C8D780069337AEC76C36CEB70373E2AB6E7B934B466F53FB32EAF040055496\
251         B8540E23A2A277E534468608D5EC0F8D38CEA5BBB806C1BF4F164F6AC826FE733F95461E29DCC11200C0AADA1B83\
252         32023EAB329718CE25CC0A09555903F3578BBC863B1752CA94365DA556DF54C3B7E05CBB7115FBC1B6C57A172C31\
253         B9906560C8FB54F3C563A2256CC073243B8179B4A28D60E086CF51082EE429272996F0AABE03BA0EAFD3C8E7D954\
254         BD0933E2F60ED0C32CEDE7B820A28E48F3CA3C40913CCCAE2337ABFC59843F08C9863325D65A4E9E15C1F46172B1\
255         18B2B5EB0F1D5158A00134F27B085488C3A0621FE4E5678698250FB74EE5152E3E35A66544A05D279EA99131FBC1\
256         5165060B90F88EEB7B20892A4DE4CB1683495BD7DA037966B47CC040F1764C5DEB06B5499D4267391CEBBB47F734\
257         D8539E39528436A1858182854BF20B1F93279AFB706464C65CCC5AE099B37CC03556C26ABF4C3F8B9BA3A9367072\
258         11A49A59B268F5284F7970C77612719450377417428C4BA47C9CA115CF95304C4759C5D8859B44985C06A6C92468\
259         9237BA320D610960D61C53E85431789E67A40113F167FF93429C264F6CABC95448C903437D39A6577BE0CF001285\
260         2AA476351A9046A110A1A625A3D74C910B78BCE9CFCA735E4F91B8A4C57DBE489E849446098AACF73070AEE638FC\
261         C8896473D3C159D3AFB4B687B40DFBF371A9C2644B605187B71A14BC4C8678FE8247";
262
263    let mut d = [0u8; 32];
264    let mut z = [0u8; 32];
265    ic_core::codec::hex_decode(D, &mut d)?;
266    ic_core::codec::hex_decode(Z, &mut z)?;
267
268    let mut ek = [0u8; ic_mlkem::kem::ENCAPS_KEY_LEN];
269    let mut dk = [0u8; ic_mlkem::kem::DECAPS_KEY_LEN];
270    ic_mlkem::MlKem768::keygen_deterministic(&d, &z, &mut ek, &mut dk);
271
272    let mut want = [0u8; ic_mlkem::kem::ENCAPS_KEY_LEN];
273    ic_core::codec::hex_decode(EK, &mut want)?;
274    ic_core::ensure!(
275        ic_core::ct::verify(&want, &ek),
276        SelfTestFailed,
277        "ml-kem-768"
278    );
279    Ok(())
280}
281
282/// ML-KEM-512 known-answer test: ACVP ML-KEM-keyGen-FIPS203, tcId 1.
283///
284/// The same construction as [`ml_kem_768_self_test`], for the parameter set
285/// that shares ML-KEM-768's code: the parameters are what differ, and key
286/// generation is where they all take effect.
287///
288/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
289/// testvectors/ml-kem-512-keygen.json, where the full set of 25 is checked.
290fn ml_kem_512_self_test() -> Result<()> {
291    const D: &[u8] = b"\
292         47B893474672BA92E4B12EE44FB32953AF8E8503B5FB471D1614FB8A021A660A";
293    const Z: &[u8] = b"\
294         1F8CB39E9E30BC458A0DC5408884B1187FB217018DF760FA57317703B844A0A9";
295    const EK: &[u8] = b"\
296         28266A088B3482439BCA01AFB7CA5C6136A979B5159985A9484B36B679A5F7B9819EB63577891F7BB9CB98413C\
297         CC434ADC79A16D6AB3076569CE6291C59B5D64612A7FB0C15013200BC8BEBB03A570174B5E4363AED86EB02A22\
298         0D281FB5457F0A549FC5051D49A6B2015259A2C3084F405E1769952260675586A584904059275A265234EF3ABF\
299         88C171A80898FC783358BBC9803C8789027D917C9EBACBC568CC18DE84C85454B94249586C0C6E2B8A16FA789C\
300         51212DD1728EE9B8C6C40528BF93826FA82368419623032AF27B5694305816811D3CA85805100E9C1A9621E508\
301         9E54CB47F5A8FEA0B49EF81C6B5187F48924C7947D6B61697A4A8A18452EF803336AD4BE503275BCACC03C1814\
302         05F7B1DC9B47FB169EB37BBE27E29C763A4E52B9A42520388CF09B8EDBCDF41CCF6537190E6156C37CC1AAC63C\
303         0F90CE78D0B9B190C548D71B6F26CC8F585EA14004B5B30AAA100B2ADC1263828833B24E46163B41446F98C882\
304         092A39941867B80632E2097674A793935227DB0B8577E03A69C50A514C7473C892E3FBA7C4316BDABC952A7064\
305         4176687D4191323BAD93D85A3CA250868C0747E6C44F6126C874AFBEC0BDD4503CB2C59A69816E7D4109941467\
306         579A1FFE6A4F50FA379051729DAB6E2F61432F15BE67D667C7CC1054742B2B953078A5CF88D9133087309D88C6\
307         1DA240D99C59137329907B47865321ECD5564E987333B4CB607B0AFCA86769DC95B2F921357213FCB80C3B1529\
308         18E9BAB2228C0A1B77897AC68CE55088165F87F397DA9790873B62C5383C0CCC370F0267CBE195651CCF336182\
309         C22AC3924B76C9E779B7A271D166B6D24B84242B7E73CC723F764039F6C851744034C3304DB0C091A5764FDC9D\
310         593556FF734B82A87CCBC38CA99564D988BBD2D1BF071BB160722D365104FB27610651A8ED817F2742A6B5A127\
311         3A61ACAF4460B0AB1456A9922351400A1C7D95D856D6E3370622C9C4164BC6B401435624A98B95CAEB274F34CE\
312         92038D785068CDD8CF44C38D84ACB2C466A2756C870EE78C26E738CC451002304EB8C90AB24B6463EB124D779F\
313         937A2E3692611D2E34D57B36CC4B2CD3B31FF485C6684D408B972E0D5CA7D2224AAE4E";
314
315    let mut d = [0u8; 32];
316    let mut z = [0u8; 32];
317    ic_core::codec::hex_decode(D, &mut d)?;
318    ic_core::codec::hex_decode(Z, &mut z)?;
319
320    let mut ek = [0u8; ic_mlkem::kem512::ENCAPS_KEY_LEN];
321    let mut dk = [0u8; ic_mlkem::kem512::DECAPS_KEY_LEN];
322    ic_mlkem::MlKem512::keygen_deterministic(&d, &z, &mut ek, &mut dk);
323
324    let mut want = [0u8; ic_mlkem::kem512::ENCAPS_KEY_LEN];
325    ic_core::codec::hex_decode(EK, &mut want)?;
326    ic_core::ensure!(
327        ic_core::ct::verify(&want, &ek),
328        SelfTestFailed,
329        "ml-kem-512"
330    );
331    Ok(())
332}
333
334/// ML-KEM-1024 known-answer test: ACVP ML-KEM-keyGen-FIPS203, tcId 51.
335///
336/// The same construction as [`ml_kem_768_self_test`], for the parameter set
337/// that shares ML-KEM-768's code: the parameters are what differ, and key
338/// generation is where they all take effect.
339///
340/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
341/// testvectors/ml-kem-1024-keygen.json, where the full set of 25 is checked.
342fn ml_kem_1024_self_test() -> Result<()> {
343    const D: &[u8] = b"\
344         F3A706FAF090C03DB506863AB0B20BD8A1627956318E88C67EB875E8E7266009";
345    const Z: &[u8] = b"\
346         35D2BC43DD1CC879F765BF2A0C5E297889DDE910E57E2BB0EAE417B90AB7A275";
347    const EK: &[u8] = b"\
348         8D0923CA8A2DA2B4146EC25321122B8A5AA8AFE0C03415273008A46EE83031E98AAAA125ABC75D3B30322560C1\
349         97E75DD0E48A348099F7B2144D7B8A8660A4A97BCF19C0583BD9BB2123033CD7BB5A14B08B817831A673A28170\
350         F5F6443C0551913A327CBA18C3A053C4040250403B70AB9588832403AA0FC37665E04980FE1602E7D2715D9CBC\
351         00515DF432A4F5B32B3BC92AE3F31700166D498123E94576509B712B18491B1435EE7AB7AEB1AD30D72348C3CC\
352         083ABE24A8B12097BF32F792476288EECC3BF630ADCDAC6ACA7950D9839501A448500742BAE37F109203A809B2\
353         B960A307E25347A32C3EAB79288173A878789B296E9E8C1C28C5BB3AC472601C9765F7B77225A810C7B85370BE\
354         F4A5B079D2015ADA54236B8F33840675F9B2EB427A1B5974CD5C61B24010886C5A7BDA5BBED974AF7217F3338A\
355         D719CB308A8BCB1B6D6ED2A1643736C29095E8A8452A3A36C7BB5AE58CBFDC61529466A90F454ED6895B086108\
356         3DD1371999B2F559A3A487CF59A074FB49215EA6A6BE656F9AF17B121A2447CB7985590E9738842B899BA57AC3\
357         11810AE2D9794F37483DD6BCCB64AF6D56588AE94665961C025C3AA2861974C236BCA4BD8FF5509F7AB774593E\
358         7C5549E57C2F18D15C0515094AD9A0DFAA0601E524F8231156B627BB25A0DAE04DACD0A66C041CEF400583FC13\
359         BAE640291A39A5C5CA8BCA1AD5C683CDD8290891A76940817DD8C9F52678780548E37A05806600801426DBB950\
360         C3B2BA34E24CC77864DD91B39F1408C716A69DF63342854E50A245FB50977B9410DED2C93F86B1F9D5A78B87BF\
361         81E51CA620A7E8566B19AB700964A40E3266415228D432156E5CBDF52364A90483A55C39B3FB16FC7465A3F8AC\
362         801B70B9FB28B583444BA5C1A73722D417A9D6D9B7DEB08BC6B330FF27CF61AB8831E27758C64AF3B12150CB7B\
363         33ABC29858106D63686D8762459ABF9413850AE53ED6313F76F83D0FB8AB34374E7DF693E4A1B3E5A8AD0CE820\
364         AFE1CF401ACDE650A8101B0946022D52178E19613C42B88B07CC04EAA81DFB28AC9DC076236B67219A30F8F945\
365         DD57BD2F335C52D59372308D38993467DB53DA3382B74867B616481BD0091A2232C1116DC88A589DB9107224A6\
366         81008C67C589186A6929549BEEF92253DB02B0C8AA9F9C875A670266C72BCBDB4F5625043703C1A0457395832E\
367         4C335180462ED2220C59E7361903C107D85457F6CD82EB820D0855D97675C2E0151CDB73C2885DDB7849D74541\
368         580124E890116A65BC068093B57914E20C937C60A3EB25576F1A976A9583839B672144CD4A45C3477A45C29B4E\
369         0BC2BDBD206585C9B7A7741C8B6B5793A92797A15AE7A5B73A74B2971463634BA52AA792AF05530730B6D0A89A\
370         346156B733677932BD36593A7496130CC458DCC5CA987C21960604EC8A8C5396056680CBF3F1AAC4F401AA5029\
371         FB2150434BB4706C31A2D54E4297939FA7C9C6F85700613CEB65C7F03AC56EB86E2D27CCC6DCB7B9394DCDB942\
372         FF222D86958A996C0CB6A8A44F97A70441C95FA71250116EEC20863C0B5A643458788AB001F8869D909922F51E\
373         E547A1E889255B3A0599C65842E5AB8D73872F053BC62392EA53896D328102D460BF1609583C22C3B43780EC6D\
374         AD0319EB4A5A65B4756C3CB40EAA935183BF8BD46ABE76BA46E199103A5313C3235F49C915E097BCA804DE6807\
375         81D8365731BEAC6789A9203FB8787C4C070E00A13A6722A66A28236DB179825653D33CCF898B72C6B8450D97AF\
376         D3276BB13340519CBEDA708D12A858F54C49F4547195B7788A9150B2649E36AA394121926D568A488B16D3557B\
377         2A32AF57D11FC3373F80A28C0723273D362502E7C428AB44D3CBABF9EA585FD1BD0C9846556A1E196B78CF9515\
378         92984A0A8487A78C2317D7ACA4118E1049750A0788F0D66AD9E48E34731130ABA0B427360A856D96D80B3F028F\
379         DD3ABA9035C10106BA1C0934BED36C6D7C7434249654EA89FC22137F4AB903653B75FB25B6F01635E6CC7D39CF\
380         1508690562826B49B6FFC59E0DD35022E541F8BA0D304AA5B4E20606907C424395666C54ABC2B8FB009847C863\
381         17685000C231215C8C15945860F6A85DDB98A8C3A527F2749D3C027E694E8F0B0F0FA454913AADB635AADD452F\
382         7128BF7752569669A8B93290EB92E78F6ADFF23E89F57F3890753B51F12F3F3A8A654E677847";
383
384    let mut d = [0u8; 32];
385    let mut z = [0u8; 32];
386    ic_core::codec::hex_decode(D, &mut d)?;
387    ic_core::codec::hex_decode(Z, &mut z)?;
388
389    let mut ek = [0u8; ic_mlkem::kem1024::ENCAPS_KEY_LEN];
390    let mut dk = [0u8; ic_mlkem::kem1024::DECAPS_KEY_LEN];
391    ic_mlkem::MlKem1024::keygen_deterministic(&d, &z, &mut ek, &mut dk);
392
393    let mut want = [0u8; ic_mlkem::kem1024::ENCAPS_KEY_LEN];
394    ic_core::codec::hex_decode(EK, &mut want)?;
395    ic_core::ensure!(
396        ic_core::ct::verify(&want, &ek),
397        SelfTestFailed,
398        "ml-kem-1024"
399    );
400    Ok(())
401}
402
403/// ML-DSA-44 known-answer test: ACVP ML-DSA-keyGen-FIPS204, tcId 1.
404///
405/// The same construction as [`ml_dsa_65_self_test`], for the parameter set
406/// that shares ML-DSA-65's code: key generation is deterministic in the seed
407/// and pins every parameter-dependent layer at once.
408///
409/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
410/// testvectors/ml-dsa-44-keygen.json, where the full set of 25 is checked.
411fn ml_dsa_44_self_test() -> Result<()> {
412    const SEED: &[u8] = b"\
413         7194B13C95231010AFD2C909992BD2003BA6F437C3886BDBE3F6B867A14BA161";
414    const PK: &[u8] = b"\
415         0B89806F0EEC39F2891116152ED4319D4260DFB8AC0710765BD497E6E1DE17783CF81E435A412EABEF5DB3AF5D\
416         15867BBB4C60F8CF98BA31BAD6D41A5F8EB0C11B632C3F19D844A223C353BD182883DCF13B5C97823D0C0E6902\
417         DB25AD8D344A37F59F4AFACA5BC8874792DA1E6A3EAE742AB7034B20A4AB75A93BCA4B68002DD242CED348920B\
418         7E5ABF645A0E2E79617BCB3EE7BA972B3E718D3EFFC59B1869814BA3F526927477B12BF25CBAD8B04B09905FDA\
419         D3820715A8B9A905DE1CD65EFF6B0B0886305EFB6CFEEC9E90B5EF9A5AAEC45C753298E8DF9B017CE0FEC9B743\
420         1B20775CE8CB11F1F42D1D9FE936D0803196E71ADDC26CC430CC3B69760C7CCAFAB7651E21BAA28F92BBFF1C4A\
421         6EEF156D6F08F80B5E3B6FC943E6E984378B90888D09A6EA38B0BA86A3446211452E076DC9F65620014205D527\
422         1C7A44FEC3CC5375EB246AFFC11B26CAFB8B96CEE3A68E31642E3D69B9130795F25ED818EBB211CD8BE648ADB5\
423         C8A120C8186017727FBCAB31C7425C08FE9195DE6BDBADA5778D727EE5CDE0674FACB7AB81786357B529C71DDB\
424         24DF770E8E95E5F3112BD297B352CB91B08ED1097A98E87BD7CE4235B8DD42292CD4C59D87C1F0FF00734AA22D\
425         7CAE4361ADC47742C897601048526702538828BA3C3A959990C0E99463FD22417E147FF2DAA74C0C8D3A06E970\
426         3A2E160590086DB8011A3D9CEC5AE6348706F87CB2379632CE56E660A0BA1B30E3846C5B5C6C0339DD993E543A\
427         5322AF5A11FC7040A2DF23A0B43E882D7A0FF4431A723BBB918AFF7F14BC045CBE94BCAB27AE3109147B588665\
428         EF486006562B1297016EFDE787B46237060EE431E0F011166F916AA0789A7647103B7400A1CBCF0E22BD7B6DD2\
429         BB3EC51EC98F0EC6A5BAA4CDC83F993D302F8FA849F2046B78AA32F0B3751885ECB941799E250E6546DCA5C20C\
430         24845190F239EDC20DDA77353D555DE61509CA6D3C6DC3195BBC6F1703CB03EAD5E7FCBCF5D196E9AB71522408\
431         E11D6337C74F9A31EB22AD084A19132BF72E7076A9743ED070ABA78789791824E050CD27694C2648263D120081\
432         1FA1B81A00B8FC09CB7A338795E54F6598D7753395F05C60E6EBA9630912B7AA8CAAB3017565DEF72C7929F4E7\
433         736C2B8043FEB448801E2DED704E834294B69F6A109C0968214FDC5C3FF0D1B1555D617E16DF61829231962C59\
434         B22A10FE400F8B8CB2A3F19FB4B2E8D087F22687506E7F0D061857D1C1789C7F55B899FF4B322982D64BD0AA75\
435         1D5BEE320B135C7F5DDCD5E6245B57DD22F44042F2BA6DE942365A59FD0C6B0F20C07B71277C6EE7DD9D225032\
436         605AED1D3CF8242EB85C33A0AFC3AB42764088D8F4A80FAF804CD84360B2055181E58A0B5AD4C367ABC6679820\
437         45AD0FD7E048AF8C326D5DB60233302B107E515B15B0F90E5F348C54192B559B4C0A86CDF0719387EA3FF6B1D6\
438         0B324A98963C56927E2B8DD5A39AC792AEB85EBDBD8DC34B395C2B4DEF4D853AC21A7660348EA8C96C943DE0BA\
439         FF3AA6849179E5EF2BAA1731C81C605BEC3860FC4A6A08CC9F75BDE9533511780FF1E0B01D34C0DC3EB80A7E2F\
440         52A7A4B815DDA98EA775DFE0C5B3D419B05934DDA05A9616C0978CC99CC8D7B68227BD846419D765956C3D7AA8\
441         11CE60AF22DF322FEF0DCE38C4278E0237F1D29EF139E201C8ECB4D36E79910D06C5CA4CAA8C2886B96DE6EDD4\
442         0D2499E30EB942F22BEBF6ED5C8E37DF9557E74D67DC467BAAFA68F1CE37C8BD9B3A4F9DE71670128125AA16AC\
443         A7232239575E1C6819C820AD16832F23647DD53C5740A8552F86901AA4F883EFD5A3EFD7C3BF458C5122712D44\
444         BE43306C9B8264";
445
446    let mut seed = [0u8; 32];
447    ic_core::codec::hex_decode(SEED, &mut seed)?;
448
449    let mut pk = [0u8; ic_mldsa::sign44::PUBLIC_KEY_LEN];
450    let mut sk = [0u8; ic_mldsa::sign44::SECRET_KEY_LEN];
451    ic_core::ensure!(
452        ic_mldsa::sign44::keygen(&seed, &mut pk, &mut sk),
453        SelfTestFailed,
454        "ml-dsa-44 pairwise consistency"
455    );
456
457    let mut want = [0u8; ic_mldsa::sign44::PUBLIC_KEY_LEN];
458    ic_core::codec::hex_decode(PK, &mut want)?;
459    ic_core::ensure!(ic_core::ct::verify(&want, &pk), SelfTestFailed, "ml-dsa-44");
460    Ok(())
461}
462
463/// ML-DSA-87 known-answer test: ACVP ML-DSA-keyGen-FIPS204, tcId 51.
464///
465/// The same construction as [`ml_dsa_65_self_test`], for the parameter set
466/// that shares ML-DSA-65's code: key generation is deterministic in the seed
467/// and pins every parameter-dependent layer at once.
468///
469/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
470/// testvectors/ml-dsa-87-keygen.json, where the full set of 25 is checked.
471fn ml_dsa_87_self_test() -> Result<()> {
472    const SEED: &[u8] = b"\
473         A16F5B0796703E2D1A0140A35CBF36EFABE70E752BA59B6A9A0E9C4B05302F73";
474    const PK: &[u8] = b"\
475         A5787E8044248F3F85AAC54E9469FC98F1B1138CC127B120F9946C80B96E3D89CCFE38C995645D4B6A559EACB2\
476         AFB81621D765C6E42E73031D44CBE74D322C7B16249576EB4C500253538D1A2C6B408E681B93B9014E3147DFBE\
477         CF9D9858F7E8635F8598BA6847127D216A888FFB1636CC761616A0389C39A4245695DDE0C86CCF8A3BC5A50EA6\
478         FDBCC0A34457D4DEFE35F775C5993685AEF2237C31912A619FF804AFE8AC3418C13502820AEE5249D6E577EE0B\
479         2A5E3E8DA2DD30F514A076B50556D7581BA1F9B2E4671756A63065C20EBDA6EE2C33C9D97AB14C5F2204FC5359\
480         ADB2BDAA3AAB7AE1DACFF18A67D801BCB8F054BBC444F0FD0001A7908EBDCDF2B84F3C026EEC1282498D31AC33\
481         AE6A309ACAB17B70DC9F0EFBE52648D1AD2A4CD5964DC619B66CDC9D35EDA7A3DC21C729B9929024DB8B852DFD\
482         F102A086845702FD249EE43B54D2D033ABA95110C4F0A66EEAEE78F3C41D5D792A37D1D2299252A5498A44CA35\
483         4F6F37FDC2F3B72B1A8378A5BA8B4997556E5A6F4125AD946BD4FC402C4320B27111BC204B8B5448F43F7E77A8\
484         166A48137D85584BEFE2D9C85CCCD9BBF3F8A4E05930180AFDD697DA82ED9F1069150FFC76578C941CDBCC5BD2\
485         ECB7D6ABF9E68327DF51C26C8B42CB1DD8BDA98A82C4C6BA6A991E651BDFF68F0A62D5DBFEA020D4303E0C53D4\
486         74CB11D553C5BEE1156917D72AC2A6CCC4EC1C775D41EE660E2485A45AF5A7CA083DDDCC3EE4FFFC5E77BA97CC\
487         4473303C77D8B6FDB35E2A20627BEBBE327DD2D1AD1F880CA8EAE1E0067A9093929E5AB18D406A518EDD8C1E0F\
488         0AB07736F55FCECF4A3B2ADCE1CE3E080B58DDF85A2262D0803A7E5B4E485E642BA533E2EC7A43F9E8DB20F752\
489         92ADAC704395469408C15641A9C28B83E8C1C799FAE0652F51369978F4C089FE15DD78C5F560CD28F5F75FE0A3\
490         9A60A61AEF6C7D802141E9809E7ACA68A38BE9BDF5312258704F8B11AF4262220CB55641FE95DF83EC9F786D6E\
491         69202C91EC4CAA4E38A21C3CC609C28B8F65552FE8334850858BBB30B837D874867EAD330A1F5B4D7F6BEC4748\
492         BE54A782D5B21A192BC00A7F2240FBD900460785D1971A94C587493D21CDA249676EDAC6C865147E269488B9CA\
493         78FAEFDC778FF60E79735DD5539879182424B054FAE8A9E153BFFD6957C533AEDC105E43AD7626312C8D229327\
494         D3E72AA9644BF3E2C9A08ABF807CF3A472C7DAF0AC4290A9E8F88D07AE8FEDB8A4B218C3EBBDBE53882781F2DE\
495         034B17FEFE69302B4975CF43E03645DC53BD355AF988B3A3D2431BF9D9A865750051EED7BAEFD1BAD4939C7EC2\
496         93509A5851A145F79DCBEEFD195571AC2172AC6036812B4DC8040D186B8984CF9DC24F8F765166C6B2E8389DD2\
497         4ED63CEE951442861669BDA0622BD90B041DC477C01A0D95D547E07A892CE1F26275ABC6F97702E03B776E2CA7\
498         1E3D0EBB88D1ADF591122E6F0EC95A6CFBB976AE64BD0C7F074CB6E78E644DEE8200E2D626435907B9134000DB\
499         C3B50271F5A8F254D2CF02DA039D458E80FA13A33567AA9B374B47B799D0FF1A14CA92A50EFA192EA1641FE29A\
500         380E11386528B7248D6DE6AA90E1C9744713B768264B72A13CCAA94F3E19ADA5129E0D8155EC1B267E2CDC7F0E\
501         7A08F203D9A18F8C8D18529709EF746A21D5FEC36EB547A2FD4490092AD0F06C55EE0A1CB104E2C3C3CF2BCBCB\
502         FFEAEC744A13E37310962CE40E072E7ECAE223943A03002077D3D96C7B4C3FB1E2C9EE9C5222A63252F26372BC\
503         2BD94507CE5728CEB7A0AA33527E0662B4561D1BD255806450772EABB7ED40F787A2E3C664FA6BC3BE9BCD84FC\
504         7B42F16F94CC56CFB67297E475177E19E51010CFB74BA996BA1C3D793EA010C99901E2223B375E364BA193772B\
505         329D5D05AED959EBB924B698F0657AF43EEE8E0D54D15C93511209AAA9E10251BF81AD8B467D8FAEE2A440782C\
506         372F55A62CFEF408803E4B3BD8EFB94061EAB525BD31957779B89EB1C75AB97F278B3EAF99A05686E04873D7A6\
507         85868D1C0F4510ADAB0B267FE4D3CB70C35B295AFC671B60C6575B60EEB99756E7B204A24D7095DF277BE18668\
508         E0F1AA5621D16F204575F76C3B13385BAF61AFF7F36D56111FD88FF093BEC4FFC26BA63720660B5BD209A9C14C\
509         0AC6B4E08B38FF580C18E39A22A9AC36912892537B0FD42800445DBEF1A03D2D8624C1B125519927C282EEFAC4\
510         AFC16128B07456FBC99D34C783AE08EBB0C46915971429FE64E448F1678BF76C7C20F91AD80E213E39AD89962A\
511         EA44E06EAB351C9B3A5859D5A884DE0CD767260CB60A96F508C62B731047CB1F3CA6AE28742771F4EC3BE45DF5\
512         00F0132B6E947D2468AAB9410BC1F581328471B3E53E8E29A0794F4EA8BDF9FDB3922CECBD2AAAF75A2AF4C7CE\
513         BA03382332D39DFB770559789708930E4C77966C7364E2A4D762C122BE3FCBD37276EA6194071BB7C18E262752\
514         4F3AB2BE7C0C6E59F62D1F075E1951DD3352F6CA9762F243F6691FE6A9DCE4278B178F688E433B990272000F23\
515         C92F74A1CFF1429BDF1C3FE1B9AA1C0E7A58ABFFBB54D3F38ED93B11FB12233CFF48C812A227747849012F7BB8\
516         5529D87459E11DCA9F7A9C3242054DB6BB93B48A47D69BA791B2A5D694A8776B22B8881BB2D192AB0A45AEC71A\
517         7171F844E3B1C4149D118EFA899E88D96A296D5EB811AF923AFD1A92E0C71464B90C8E5EC24954FE8C9BFB518A\
518         308B3D301D8D6C62E5AD40F63BCE24699AF0BBD2E48FD9AA7C3A1C597731700E3D86E16AD36A67BC031BF381B8\
519         2CF40B1B235F51727913BBC311A186FDC23EE267739740CC57C36BE05F9331EA40279C34C44FB8FDFCC7E9CDA5\
520         4C394CEF9C17F8529A56E3BCCBA327CABD07F5F9C567B70DFBD9D7139E39B7E1A493482321B11C881BA9D44CBA\
521         BEA64F1AF18C18B3B5F98FF4A95FD907E112AED857B033BB7E0FF9466CF5FC691CCC1142BFB8CE81BAD7DB5544\
522         09B79E23D0A41063857E4AA05583525E69EDAA017F6A7FA96C25A38E7A7D01E22F96ADBB683B4EB8A487202426\
523         D68D297B30BE0A803D4FCA1E037DB3BD63A2F06EFD68BD4D7BEAD8338D36AC1425163B3C739B86AC9D967ED54D\
524         E24CA58BC129CFF73585B667DC33F32BC4A4D9E106076849BC2574F0E5AB2C0B5CBDBCA59BB644BC86F3339B23\
525         B3B12959FC7887F7291136343E54F4A56C344DC5BE641DBCE62AED4D4BB958C4D54D7196DB4ACB8A1F43F88CDB\
526         B758546F44C59293501E0569F00B97FC244507C248031D07EE4DBB6A97270B772791E758E582989D93124AB82F\
527         15A9DCA469C1A0E98CC75FF683430036F16B4E94E94DECA06EBE7FE2B8E9C0A690AAD7A91877799FCF24CE2758\
528         44A63E68F1A5C799BC83C7F5384CD9322E20B619D39D0031482FFED8224F6C522C8533CB29C04AF154D920D747\
529         D81616EA219E358385AA9FDB8E94A7EE5B53F2CC31B3A7BAC787E54AB9536FC42A3E369043C6F5C11D0F7D4528\
530         52C3FB3F1845942186044385FB9E482962B1DAEBD2B3DF125D1A61843F71A272E3A1D97AAB97DE5831C56D16A6\
531         A6620F8C6CD0F4F1E41AFE6895BA3664D23A03EAB3531126E87335F4BCBBF2E39C47B5A58BA15066F79717D829\
532         6667553ECD0991F14D42F8934D753929F146E4B58D00A3E0139D66";
533
534    let mut seed = [0u8; 32];
535    ic_core::codec::hex_decode(SEED, &mut seed)?;
536
537    let mut pk = [0u8; ic_mldsa::sign87::PUBLIC_KEY_LEN];
538    let mut sk = [0u8; ic_mldsa::sign87::SECRET_KEY_LEN];
539    ic_core::ensure!(
540        ic_mldsa::sign87::keygen(&seed, &mut pk, &mut sk),
541        SelfTestFailed,
542        "ml-dsa-87 pairwise consistency"
543    );
544
545    let mut want = [0u8; ic_mldsa::sign87::PUBLIC_KEY_LEN];
546    ic_core::codec::hex_decode(PK, &mut want)?;
547    ic_core::ensure!(ic_core::ct::verify(&want, &pk), SelfTestFailed, "ml-dsa-87");
548    Ok(())
549}
550
551/// ML-DSA-65 known-answer test: ACVP ML-DSA-keyGen-FIPS204, tcId 26.
552///
553/// As with ML-KEM, key generation is deterministic in the seed and pins the
554/// samplers, the NTT, `Power2Round` and the key encoding in one case. Only the
555/// verification key is compared, for the same reason.
556///
557/// `keygen` also runs its own pairwise consistency check and returns false if
558/// that fails, so this asserts on the return value as well as on the bytes.
559///
560/// Taken from usnistgov/ACVP-Server at 975de31eb83d. The same case is in
561/// testvectors/ml-dsa-65-keygen.json, where the full set of 25 is checked.
562fn ml_dsa_65_self_test() -> Result<()> {
563    const SEED: &[u8] = b"\
564         A991FD42B071D49C48AE3E75C647459E0DAAD1E1BA356A04801912D3294BCFF8";
565    const PK: &[u8] = b"\
566         36DB0B5DCE98BD190CB139E80B71B49C7D7040B71C5A1F3412C46BDE939192B1B57CCB88AC2714C1240CB0EB62C6\
567         89E031AEA3D9F3EB3ED7BFA45931D288DCAE3413199B31A7032560DCE8A61E195D13A1440615C2F3AA7DD28C5B1B\
568         742BFA400052186721F13D3DF9DCFAEE348B10D66913C7148913E085E1A4A03C659398DADC6A8E0E0C1A7F9F44D3\
569         0436DB90FD65A6AB8F36137338255653BAAE8DA21526A333426DBD9F76CCE0F43212643E854D772018B35CE726BC\
570         AAA5AB0651BAF8C122E13929BB35B6E4963DF2595FDC7237CDAA7234BF776B07F353CCDBA12AD3E025138E3492D7\
571         F8E929DB55DC23E23075F66D57A10492E6A10AE7B758ACC2291CA18BA1CA07A5B574AB6D8AAC18B9524990AD2F11\
572         0225B7D82F696300A660A166AD35B3C57ECBAB77117C79656FA8AE2A19A7DEFB2AFD2AF54683D043BE0F933B8EAE\
573         0D591448ED55D00068CD9FE10B067FCFAAC53AEDB1E9B667E36C4E30231F85C7AA0A474AF2FA4776226F4479555E\
574         155528D78B98183CBDF7FAE4E7301140F163EB71E991D15FAD4A0D2F25A5A62FA2E9BCC823CC2927662E40C53821\
575         3DED9E2DF508E911E4924E507A50861FBB050EDBBF56D937206F8FBC6F4CEAD4CD10D06B73AADCD4AA39703A7A2B\
576         FFAE68B7BAA47341B699DA9F3B167D4D90EFEE0A07EE3529A3B5E8648B9CB07EE973E1D8DCCF1D16E95092C4A018\
577         4CCB4902D6086D9F444ACA5FA45F43CA91B351E82585989FFCBD6D2C3471D6B8593AA46F29D0DD9B44E8AA4D8F9A\
578         0BC886BB7982C56AAB11E23BFDBD8BC674732FADACACAE25FA416B2D0CC7743827293336507DA4B14C1F0AA2E929\
579         AF975466DADC89A016F33A0CCA2D5C08114CF04B02358805A772536432C44DBE9886130D2D3A0FFA0E175875A220\
580         7686F5E562B879EB2957573AB706B942468C20CC69BC566D29D9F151F3CFAE71CC97CE4A30722D4679FC1C089B50\
581         09935931EE60AAC5496B0FD5F24E514C0E20FA1DCC7729184A50FC85FAD1D2F32F715FBF55666E49F5A19761F2DD\
582         1AA5D1A33C7916EB6A794981C0334176ABC493EF30D9EAEAAD42E705989DCFCDEB578529A700BD14076A348A2062\
583         D6483CC63CD7F55136587AAC0E531A06EB2DE74E61CFCBBCE18F2ADA5A741F683BF101F71432EE659DD1508E0C8F\
584         B2400E0CCBE435DA3466D543D3EF5BA369E125C0B84D855EFE6D4A22FF929A7A7A984E448D23871E09B88A0BC3F3\
585         B7DA55DD2EDFB5A6DAA102819FF50CD4DCCD0A95D2F27354668065D4A56C31FB18B92B2A8DB2C6453BAA9333AEA6\
586         EABB1BD6411D584DD5900262057A707F81CC5137DBDD9AC1079BB98DA78A8E4BD1B2E0546C2B3D956FCC280D37D8\
587         55E31F1E4315B387A742280F057F3219EAE512884AE7EC4D2E3A72265B1D0163FBCBF616B2E289B0EAF9C63437D5\
588         0B7B50CE408F5B4562F2ABF510C19F5E8A0ACE264DB6E0F2A69A7D0B4A5E62A2B964F08C8FFE9C295F5773BDD7FA\
589         B054A13822D428FAE28AE5E4ADC9D9F6E4DFFC457A3E49F0BCF62B32961C4667B60960452AFD917FDD00D954FA30\
590         C8533E5629F90AF85948DC1BAD889F91832DDF9B738254C9E7939726C37AB4557C2CE363C1391816C467537B471E\
591         5985E8084C277B62BE514922D352E20689EABB3EB91C343F36E77B152D5E85AFD088F4D02E7024B248A7420F58C7\
592         EBBEB480CAE39B56164F5ACD37A4F56B3DB6E1CC6B7C8C96CD3C44A69D9AC99175257BAB7FD83C5B574B5C9702C0\
593         FD13A5B176C60F82D2DFFF50C2AF25D96E0F8D27EC818D499E479B9642AED4A4A0E6AF5F14CC5E1299EABAE055EF\
594         3C763D1E350E2D76E92CEE47A4233368466A298AFB4CA108A325D2A4F8B79F21EE7349C1C186ECD7897F9886CF27\
595         EC01B05388870484867F84BAE2C016D04A3762241907C4DE207798DD125A2CEBB6C2982F779E04117BDD65CD7FF0\
596         361A59D3EC05F6D903B6D15554BEEFB6D40D96A0D4B37AE76C69C1B9592088B7DB878F95ABEDCB5FD5423ED93DF1\
597         B27D01A4DC9F4438E7C55F35B0AEB7395B08E1ECBF15CB2B61D043C0454AEAEF2D487093FA0D7DE3FC6CAF084B6A\
598         0F15A5CB05D9340D4E6763983DC45B7828539C77A60D5E081A03FE29949D916392B6D989B4C8C047E3635A76BA88\
599         AC18A7A18CCFF5C7E06B02A43D2DFF169FA449739E382BD020E0963C14A9ACAE6B6561C722D2BDA183F33EE6A904\
600         DF0207F5B098E56335CC063F9640C4997F593218D502F6B382354C73979E93C4B1B2471965FFA5A0DC8EE8ECA9F5\
601         697E7EF08DC0EEFD9AD75CD4122194B450201DFD73CDA46A7B2478DF66129FBB9C75B774213F9615BD990F8D0750\
602         1FED440FD25D6CB912B8ECFA678D887A4EE28677E6E0491D49EFC7A3B34B9815C5C22983DA280D0AFDE2324F5281\
603         BC8B796DDACFD82723BBD9AA34B0C96075B36848591E47B80086897846FA76D092BC8BC6200837BFB5545039F860\
604         2B7EA49F63C0C3B8317EEEB7612F8E818DDE09E43C7DA76FD2FF6847906A45DA3D993E8EAED9FB3B1E579D8CC690\
605         0C89522AAEB0B4A80DA1E66AB8DFD62DFE4E4D77A3A77E5BD669207C70AA8537DD6D80A647B0420D79531A745605\
606         2C3C989F0F08DE3D343C40067680B39ECE95A17AAC8A622D1D5D95B38CA0F11D94E5B0A7634EEF4055517ACE79F0\
607         DF1D7C172E0246ABB2AB6B135EE1A38A3B84F86FD7C3CAF178CD4446D0B554256AD45C657E1192070ABA7DF480F4\
608         89EBDF9753A79CCBC6AA893913C5F1271F1C6035";
609
610    let mut seed = [0u8; 32];
611    ic_core::codec::hex_decode(SEED, &mut seed)?;
612
613    let mut pk = [0u8; ic_mldsa::sign::PUBLIC_KEY_LEN];
614    let mut sk = [0u8; ic_mldsa::sign::SECRET_KEY_LEN];
615    ic_core::ensure!(
616        ic_mldsa::sign::keygen(&seed, &mut pk, &mut sk),
617        SelfTestFailed,
618        "ml-dsa-65 pairwise consistency"
619    );
620
621    let mut want = [0u8; ic_mldsa::sign::PUBLIC_KEY_LEN];
622    ic_core::codec::hex_decode(PK, &mut want)?;
623    ic_core::ensure!(ic_core::ct::verify(&want, &pk), SelfTestFailed, "ml-dsa-65");
624    Ok(())
625}
626
627/// Argon2id known-answer test: RFC 9106 section 5.3.
628fn argon2id_self_test() -> Result<()> {
629    use ic_kdf::argon2::{argon2_full, Argon2Params, Variant};
630
631    let params = Argon2Params {
632        memory_kib: 32,
633        passes: 3,
634        lanes: 4,
635    };
636    let mut got = [0u8; 32];
637    argon2_full(
638        Variant::Argon2id,
639        &params,
640        &[0x01u8; 32],
641        &[0x02u8; 16],
642        &[0x03u8; 8],
643        &[0x04u8; 12],
644        &mut got,
645    )?;
646    let mut want = [0u8; 32];
647    ic_core::codec::hex_decode(
648        b"0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659",
649        &mut want,
650    )?;
651    ic_core::ensure!(ic_core::ct::verify(&want, &got), SelfTestFailed, "argon2id");
652    Ok(())
653}
654
655/// The number of known-answer tests in the suite.
656pub const TEST_COUNT: usize = 73;
657
658/// Run every known-answer test and summarize the results.
659///
660/// This never panics and never short-circuits: a failing algorithm must not
661/// hide the status of the ones after it, because the report is what an operator
662/// uses to decide whether the build is salvageable.
663pub fn run_all_self_tests() -> SelfTestReport {
664    let mut outcomes = [TestOutcome {
665        algorithm: "",
666        passed: false,
667    }; TEST_COUNT];
668    let mut passed = 0;
669    let mut failed = 0;
670
671    for (i, (name, test)) in CASTS.iter().enumerate() {
672        let ok = test().is_ok();
673        outcomes[i] = TestOutcome {
674            algorithm: name,
675            passed: ok,
676        };
677        if ok {
678            passed += 1;
679        } else {
680            failed += 1;
681        }
682    }
683
684    SelfTestReport {
685        passed,
686        failed,
687        outcomes,
688    }
689}
690
691/// Run the known-answer test for a single algorithm.
692///
693/// Returns [`ic_core::ErrorKind::Unsupported`] when the identifier has no
694/// registered test.
695pub fn run_self_test(algorithm_id: &str) -> Result<()> {
696    match CASTS.iter().find(|(name, _)| *name == algorithm_id) {
697        Some((_, test)) => test(),
698        None => Err(ic_core::err!(
699            Unsupported,
700            "no known-answer test for this algorithm"
701        )),
702    }
703}
704
705/// The identifiers of every algorithm with a known-answer test.
706pub fn tested_algorithms() -> impl Iterator<Item = &'static str> {
707    CASTS.iter().map(|(name, _)| *name)
708}
709
710/// The pre-operational software integrity test.
711///
712/// # What this checks, and what it does not
713///
714/// A conforming integrity test computes an approved MAC or signature over the
715/// module's *executable image* and compares it against a value embedded at
716/// build time. Doing that requires a post-link step that patches the digest
717/// into the binary, which is a property of the build system rather than the
718/// source, and is listed as a pre-validation task in `FIPS.md`.
719///
720/// What runs here is the weaker check available to a pure-source library: an
721/// HMAC over the self-test vector table, which detects a corrupted or partially
722/// linked constant pool. It is a real check — flip a byte in any embedded
723/// vector and it fails — but it is not an image integrity test, and this
724/// documentation says so rather than letting the function's name imply more
725/// than it delivers.
726pub fn integrity_check() -> Result<()> {
727    use ic_core::traits::Mac;
728
729    let mut mac = ic_mac::HmacSha256::new(INTEGRITY_KEY)?;
730    for (name, _) in CASTS {
731        mac.update(name.as_bytes());
732        mac.update(&[0]);
733    }
734    let tag = mac.finalize();
735
736    let mut expected = [0u8; 32];
737    ic_core::codec::hex_decode(INTEGRITY_TAG.as_bytes(), &mut expected)?;
738    ic_core::ensure!(
739        ic_core::ct::verify(&expected, tag.as_ref()),
740        SelfTestFailed,
741        "module integrity check failed"
742    );
743    Ok(())
744}
745
746/// The domain separator for the integrity tag.
747///
748/// Named rather than inlined so the operational path and the test that
749/// regenerates the tag cannot drift apart, which would leave the constant
750/// correct for a key nothing actually uses.
751const INTEGRITY_KEY: &[u8] = b"IronCrypto/integrity/v1";
752
753/// The expected integrity tag over the CAST table.
754const INTEGRITY_TAG: &str = "2f52a38b5fe89927788c2820acdb50f496ddc67e7c9729c9dd334d25018156ea";
755
756#[cfg(test)]
757mod tests {
758    use super::*;
759
760    #[test]
761    fn table_length_matches_the_declared_count() {
762        assert_eq!(
763            CASTS.len(),
764            TEST_COUNT,
765            "update TEST_COUNT when adding a CAST"
766        );
767    }
768
769    #[test]
770    fn every_test_passes() {
771        let report = run_all_self_tests();
772        let names: std::vec::Vec<_> = report.failures().map(|o| o.algorithm).collect();
773        assert_eq!(report.failed, 0, "failing self-tests: {names:?}");
774        assert_eq!(report.passed, TEST_COUNT);
775        assert!(report.all_passed());
776    }
777
778    #[test]
779    fn every_available_ontology_entry_has_a_cast() {
780        for e in ic_ontology::all()
781            .iter()
782            .filter(|e| e.status == ic_ontology::ImplStatus::Available)
783        {
784            // Modes are exercised through the AEAD and block-cipher tests, and
785            // the generic KDFs and the DRBG through their SHA-256
786            // instantiations, each of which has its own CAST. PBKDF2 and
787            // SP 800-108 were on this list with that reason and no SHA-256
788            // CAST behind it, so two approved KDFs ran untested; both have
789            // one now.
790            let exempt = matches!(
791                e.id,
792                "aes-cbc"
793                    | "aes-ctr"
794                    | "hkdf-sha2-384"
795                    | "hkdf-sha2-512"
796                    | "pbkdf2-hmac-sha2-512"
797                    | "hmac-drbg-sha2-512"
798            );
799            if exempt {
800                continue;
801            }
802            assert!(
803                tested_algorithms().any(|t| t == e.id),
804                "{} is available but has no known-answer test",
805                e.id
806            );
807        }
808    }
809
810    /// Each CAST must call the algorithm it is filed under.
811    ///
812    /// The two coverage tests either side of this one check that the table and
813    /// the ontology list the same algorithms. Neither looks at the function.
814    /// `("sha2-224", Sha256::self_test)` passes both, and then SHA-224 can be
815    /// broken in any way at all while the report says it passed -- which is
816    /// precisely what a self-test exists to prevent, so it is worth one more
817    /// test to rule out.
818    ///
819    /// The ontology records each entry's Rust path for its own reasons, which
820    /// makes it a usable second opinion on what a CAST for that entry should
821    /// call. The table is read from the source text because a function pointer
822    /// does not carry its own name at runtime.
823    #[test]
824    fn each_cast_calls_the_algorithm_it_is_filed_under() {
825        // Compiled in, so this cannot be defeated by running from another
826        // directory, and cannot go looking at a stale copy on disk.
827        const SOURCE: &str = include_str!("selftest.rs");
828
829        let table = SOURCE
830            .split_once("static CASTS: &[Cast] = &[")
831            .expect("the CAST table is not where this expects it")
832            .1
833            .split_once("\n];")
834            .expect("the CAST table does not end")
835            .0;
836
837        let mut by_path = 0;
838        let mut by_local_fn = 0;
839
840        // Flattened first: rustfmt wraps a row whose path is long, and
841        // `hkdf-sha2-256` is spread over four lines. Reading line by line
842        // silently skipped it.
843        let flat: String = table
844            .lines()
845            .map(|l| l.split("//").next().unwrap_or("").trim())
846            .collect::<Vec<_>>()
847            .join(" ");
848
849        for row in flat.split("),") {
850            let Some((id, rest)) = row.split_once('"').and_then(|(_, r)| r.split_once('"')) else {
851                continue;
852            };
853            let function = rest
854                .trim()
855                .trim_start_matches(',')
856                .trim()
857                .trim_end_matches(')')
858                .trim_end_matches(',')
859                .trim();
860
861            if function.is_empty() {
862                continue;
863            }
864            let entry =
865                ic_ontology::get(id).unwrap_or_else(|| panic!("{id} has no ontology entry"));
866
867            if let Some(ty) = function.strip_suffix("::self_test") {
868                // The usual form: the trait method on the type itself. The
869                // ontology's path for the entry must be that same type, or the
870                // table is testing something else under this name.
871                assert_eq!(
872                    ty, entry.rust_path,
873                    "the CAST for {id} calls {ty}, but the ontology says {id} is \
874                     {}",
875                    entry.rust_path
876                );
877                by_path += 1;
878            } else {
879                // A hand-written vector, for the algorithms whose check does not
880                // fit the trait. The name is the only thing tying it to the
881                // entry, so it has to match.
882                let expected = format!("{}_self_test", id.replace('-', "_"));
883                assert_eq!(
884                    function, expected,
885                    "the CAST for {id} calls {function}, which does not name {id}"
886                );
887                by_local_fn += 1;
888            }
889        }
890
891        // Floors: the parse above is the kind that quietly matches nothing if
892        // the table is reformatted, and then this passes having checked none of
893        // it. Both forms must also still be present, or the branch that is gone
894        // is no longer being tested.
895        assert_eq!(
896            by_path + by_local_fn,
897            CASTS.len(),
898            "the source table and the compiled one are different lengths, so the \
899             parse missed rows"
900        );
901        assert!(
902            by_path > 50,
903            "only {by_path} casts checked against a rust path"
904        );
905        assert!(by_local_fn > 0, "no hand-written casts found");
906    }
907
908    #[test]
909    fn every_cast_names_a_real_ontology_entry() {
910        for name in tested_algorithms() {
911            assert!(
912                ic_ontology::get(name).is_some(),
913                "{name} has a CAST but no ontology entry"
914            );
915        }
916    }
917
918    #[test]
919    fn single_algorithm_tests_are_addressable() {
920        run_self_test("sha2-256").unwrap();
921        run_self_test("aes-256-gcm").unwrap();
922        assert_eq!(
923            run_self_test("no-such-algorithm").unwrap_err().kind(),
924            ic_core::ErrorKind::Unsupported
925        );
926    }
927
928    #[test]
929    fn integrity_check_passes() {
930        integrity_check().unwrap();
931    }
932
933    /// The expected tag must match the one the table actually produces, and the
934    /// failure must say what to paste.
935    ///
936    /// [`integrity_check`] compares in constant time and reports only that the
937    /// check failed, which is right for an operational path and useless for
938    /// maintenance: adding a CAST changes the tag, and before this test the
939    /// only guidance was a bare "module integrity check failed". Anyone hitting
940    /// it had to reverse-engineer where the constant came from. Now the
941    /// recomputed value is printed, so the fix is a copy and paste.
942    #[test]
943    fn the_integrity_tag_matches_the_table() {
944        use ic_core::traits::Mac;
945
946        let mut mac = ic_mac::HmacSha256::new(INTEGRITY_KEY).unwrap();
947        for (name, _) in CASTS {
948            mac.update(name.as_bytes());
949            mac.update(&[0]);
950        }
951        let tag = mac.finalize();
952        let computed: String = tag.as_ref().iter().map(|b| format!("{b:02x}")).collect();
953
954        assert_eq!(
955            computed, INTEGRITY_TAG,
956            "the CAST table changed. Set INTEGRITY_TAG to {computed:?}"
957        );
958    }
959}