1use super::mlkem::{
2 ML_KEM_1024, MlKemError, SHARED_SECRET_SIZE, crypto_kem_dec, crypto_kem_enc_derand, crypto_kem_keypair_derand,
3 indcpa_secret_key_bytes,
4};
5
6pub const PUBLIC_KEY_SIZE_1024: usize = 1568;
7pub const SECRET_KEY_SIZE_1024: usize = 3168;
8pub const CIPHERTEXT_SIZE_1024: usize = 1568;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
23#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
24pub struct SecretKey1024 {
25 bytes: [u8; SECRET_KEY_SIZE_1024],
26}
27
28#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct PublicKey1024 {
33 bytes: [u8; PUBLIC_KEY_SIZE_1024],
34}
35
36#[inline]
42#[cfg(feature = "random")]
43pub fn generate_keypair_1024() -> (SecretKey1024, PublicKey1024) {
44 SecretKey1024::generate()
45}
46
47impl SecretKey1024 {
48 pub fn from_bytes(bytes: &[u8; SECRET_KEY_SIZE_1024]) -> Self {
49 Self {
50 bytes: *bytes,
51 }
52 }
53
54 pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE_1024] {
55 self.bytes
56 }
57
58 #[cfg(feature = "random")]
59 pub fn generate() -> (Self, PublicKey1024) {
60 let coins: [u8; 64] = crate::random::random_bytes();
61 Self::generate_derand(&coins)
62 }
63
64 fn generate_derand(coins: &[u8; 64]) -> (Self, PublicKey1024) {
65 let (sk_bytes, pk_bytes) =
66 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, coins);
67 (
68 Self {
69 bytes: sk_bytes,
70 },
71 PublicKey1024 {
72 bytes: pk_bytes,
73 },
74 )
75 }
76
77 pub fn decapsulate(&self, ciphertext: &[u8; CIPHERTEXT_SIZE_1024]) -> Result<[u8; SHARED_SECRET_SIZE], MlKemError> {
78 crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &self.bytes, ciphertext)
79 }
80
81 pub fn public_key(&self) -> PublicKey1024 {
82 let offset = indcpa_secret_key_bytes::<4>();
83 let mut pk_bytes = [0u8; PUBLIC_KEY_SIZE_1024];
84 pk_bytes.copy_from_slice(&self.bytes[offset..offset + PUBLIC_KEY_SIZE_1024]);
85 PublicKey1024 {
86 bytes: pk_bytes,
87 }
88 }
89}
90
91impl From<&[u8; SECRET_KEY_SIZE_1024]> for SecretKey1024 {
92 fn from(bytes: &[u8; SECRET_KEY_SIZE_1024]) -> Self {
93 Self::from_bytes(bytes)
94 }
95}
96
97impl TryFrom<&[u8]> for SecretKey1024 {
98 type Error = MlKemError;
99
100 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
101 Ok(Self::from_bytes(bytes.try_into().map_err(|_| MlKemError::InvalidKey)?))
102 }
103}
104
105impl PublicKey1024 {
106 pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_SIZE_1024]) -> Self {
107 Self {
108 bytes: *bytes,
109 }
110 }
111
112 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE_1024] {
113 self.bytes
114 }
115
116 #[cfg(feature = "random")]
117 pub fn encapsulate(&self) -> ([u8; CIPHERTEXT_SIZE_1024], [u8; SHARED_SECRET_SIZE]) {
118 let coins: [u8; 32] = crate::random::random_bytes();
119 self.encapsulate_derand(&coins)
120 }
121
122 fn encapsulate_derand(&self, coins: &[u8; 32]) -> ([u8; CIPHERTEXT_SIZE_1024], [u8; SHARED_SECRET_SIZE]) {
123 crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &self.bytes, coins)
124 }
125}
126
127impl From<&[u8; PUBLIC_KEY_SIZE_1024]> for PublicKey1024 {
128 fn from(bytes: &[u8; PUBLIC_KEY_SIZE_1024]) -> Self {
129 Self::from_bytes(bytes)
130 }
131}
132
133impl TryFrom<&[u8]> for PublicKey1024 {
134 type Error = MlKemError;
135
136 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
137 Ok(Self::from_bytes(bytes.try_into().map_err(|_| MlKemError::InvalidKey)?))
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::{
144 super::mlkem::{
145 ML_KEM_1024, crypto_kem_dec, crypto_kem_enc_derand, crypto_kem_keypair_derand, decode_hex_array,
146 sha3_256_hex,
147 },
148 *,
149 };
150
151 #[test]
152 fn ml_kem_1024_round_trip() {
153 let (private_key, public_key) = generate_keypair_1024();
154 let (ciphertext, encapsulated_secret) = public_key.encapsulate();
155 let decapsulated_secret = private_key.decapsulate(&ciphertext).unwrap();
156
157 assert_eq!(encapsulated_secret, decapsulated_secret);
158 }
159
160 #[test]
161 fn ml_kem_1024_deterministic_derand_vectors_are_stable() {
162 let key_coins = [3u8; 64];
163 let enc_coins = [5u8; 32];
164 let (secret_key, public_key) =
165 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &key_coins);
166 let (ciphertext, shared_secret) = crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(
167 &ML_KEM_1024,
168 &public_key,
169 &enc_coins,
170 );
171 let decapsulated =
172 crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &secret_key, &ciphertext)
173 .unwrap();
174
175 assert_eq!(shared_secret, decapsulated);
176 assert_eq!(
177 hex::encode(&public_key[..32]),
178 "2dd29da8b193397a4336c02382aab3bcfbac25f0cd71c888af379e1e75149a79"
179 );
180 assert_eq!(
181 hex::encode(&ciphertext[..32]),
182 "5f12f173ef59a45f910d3a225913f3297b2277636a72401a273648015cccf079"
183 );
184 assert_eq!(
185 hex::encode(shared_secret),
186 "8bf157178aa556b55f95686ba9b5afe13a6b75c848f1ddd9a334d50287bec24e"
187 );
188 }
189
190 #[test]
191 fn ml_kem_1024_cctv_accumulated_10k() {
192 use crate::{Xof, sha3::Shake128};
193
194 let mut rng = Shake128::new();
195 rng.absorb(&[]);
196
197 let mut acc = Shake128::new();
198
199 for _ in 0..10_000u32 {
200 let mut d = [0u8; 32];
201 let mut z = [0u8; 32];
202 let mut m = [0u8; 32];
203 let mut ct_random = [0u8; CIPHERTEXT_SIZE_1024];
204
205 rng.squeeze(&mut d);
206 rng.squeeze(&mut z);
207 rng.squeeze(&mut m);
208 rng.squeeze(&mut ct_random);
209
210 let mut coins = [0u8; 64];
211 coins[..32].copy_from_slice(&d);
212 coins[32..].copy_from_slice(&z);
213
214 let (dk, ek) =
215 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &coins);
216 let (ct, k_encaps) =
217 crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &ek, &m);
218
219 let k_decaps =
220 crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &dk, &ct).unwrap();
221 assert_eq!(k_encaps, k_decaps);
222
223 let k_decaps_random =
224 crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &dk, &ct_random).unwrap();
225
226 acc.absorb(&ek);
227 acc.absorb(&dk);
228 acc.absorb(&ct);
229 acc.absorb(&k_encaps);
230 acc.absorb(&k_decaps_random);
231 }
232
233 let mut hash = [0u8; 32];
234 acc.squeeze(&mut hash);
235 assert_eq!(
236 hex::encode(hash),
237 "e3bf82b013307b2e9d47dde791ff6dfc82e694e6382404abdb948b908b75bad5",
238 "ML-KEM-1024 CCTV accumulated hash mismatch"
239 );
240 }
241
242 #[test]
243 fn ml_kem_1024_cctv_intermediate_vector() {
244 let d: [u8; 32] = decode_hex_array("2a62c39ef4fc499f2d132716f480bb7521a49558ae84ee80d9352e66daf1e3a8");
245 let z: [u8; 32] = decode_hex_array("5f574ef7f013d4336801fed022178c3ed91d0b6d51325315fc1dcabf4770a2ea");
246 let m: [u8; 32] = decode_hex_array("e07d685ed308e609c9c7842026e35732f6ffc6e2fee10f0afd348f2b42a8acb4");
247
248 let mut coins = [0u8; 64];
249 coins[..32].copy_from_slice(&d);
250 coins[32..].copy_from_slice(&z);
251
252 let (dk, ek) = crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &coins);
253 let (ct, k) = crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &ek, &m);
254
255 assert_eq!(
256 sha3_256_hex(&ek),
257 "3b308d1344ed70366b84d790acb705b86cd3dfd471fff171969aaa338f26dca5"
258 );
259 assert_eq!(
260 sha3_256_hex(&dk),
261 "aa63a9e0c035ada6635e7938b71856b24917ff9b3ebca1a4d205a83b502a415a"
262 );
263 assert_eq!(
264 sha3_256_hex(&ct),
265 "8caba02733421f12a7ba9a2bcbe4de7c9853156a0637df5a7a0f9127c81da943"
266 );
267 assert_eq!(
268 hex::encode(k),
269 "d53825c3ff666bb2881215dbec04a8bdce9099b2a3680938c2f199b54d505953"
270 );
271 }
272
273 #[test]
274 fn ml_kem_1024_decapsulation_rejects_tampered_ciphertext() {
275 let (private_key, public_key) = generate_keypair_1024();
276 let (mut ciphertext, encapsulated_secret) = public_key.encapsulate();
277
278 ciphertext[0] ^= 0x80;
279
280 let decapsulated_secret = private_key.decapsulate(&ciphertext).unwrap();
281
282 assert_ne!(encapsulated_secret, decapsulated_secret);
283 }
284
285 #[test]
286 fn ml_kem_1024_decapsulation_with_wrong_key_rejects() {
287 let (_, alice_pk) = generate_keypair_1024();
288 let (bob_sk, _bob_pk) = generate_keypair_1024();
289 let (ct, _alice_ss) = alice_pk.encapsulate();
290
291 let wrong_ss = bob_sk.decapsulate(&ct).unwrap();
292 assert_ne!(_alice_ss, wrong_ss);
293 }
294
295 #[test]
296 fn ml_kem_1024_round_trip_many() {
297 for _ in 0..100 {
298 let (sk, pk) = generate_keypair_1024();
299 let (ct, ss_enc) = pk.encapsulate();
300 let ss_dec = sk.decapsulate(&ct).unwrap();
301 assert_eq!(ss_enc, ss_dec);
302 }
303 }
304
305 #[test]
306 fn ml_kem_1024_all_zero_ciphertext_does_not_panic() {
307 let (sk, _pk) = generate_keypair_1024();
308 let ct = [0u8; CIPHERTEXT_SIZE_1024];
309 let _result = sk.decapsulate(&ct);
310 }
311
312 #[test]
313 fn ml_kem_1024_all_ones_ciphertext_does_not_panic() {
314 let (sk, _pk) = generate_keypair_1024();
315 let ct = [0xffu8; CIPHERTEXT_SIZE_1024];
316 let _result = sk.decapsulate(&ct);
317 }
318
319 #[test]
320 fn ml_kem_1024_derand_keygen_is_deterministic() {
321 let coins = [3u8; 64];
322 let (sk1, pk1) =
323 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &coins);
324 let (sk2, pk2) =
325 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &coins);
326 assert_eq!(sk1, sk2);
327 assert_eq!(pk1, pk2);
328 }
329
330 #[test]
331 fn ml_kem_1024_key_sizes_are_correct() {
332 let (sk, pk) = generate_keypair_1024();
333 let sk_bytes = sk.to_bytes();
334 let pk_bytes = pk.to_bytes();
335 assert_eq!(sk_bytes.len(), SECRET_KEY_SIZE_1024);
336 assert_eq!(pk_bytes.len(), PUBLIC_KEY_SIZE_1024);
337 let (ct, _) = pk.encapsulate();
338 assert_eq!(ct.len(), CIPHERTEXT_SIZE_1024);
339 }
340
341 #[test]
342 fn ml_kem_1024_encaps_is_deterministic_with_same_coins() {
343 let enc_coins = [5u8; 32];
344 let key_coins = [3u8; 64];
345 let (_sk, pk) =
346 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &key_coins);
347 let (ct1, ss1) =
348 crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &pk, &enc_coins);
349 let (ct2, ss2) =
350 crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &pk, &enc_coins);
351 assert_eq!(ct1, ct2);
352 assert_eq!(ss1, ss2);
353 }
354
355 #[test]
356 fn ml_kem_1024_decapsulation_with_wrong_key_is_deterministic() {
357 let (_, pk_a) = generate_keypair_1024();
358 let (sk_b, _pk_b) = generate_keypair_1024();
359 let (ct, _) = pk_a.encapsulate();
360
361 let ss1 = sk_b.decapsulate(&ct).unwrap();
362 let ss2 = sk_b.decapsulate(&ct).unwrap();
363 assert_eq!(ss1, ss2, "implicit rejection must be deterministic");
364 }
365
366 #[test]
367 fn ml_kem_1024_wycheproof_keygen() {
368 let data: serde_json::Value = serde_json::from_str(include_str!(
369 "../../testdata/wycheproof/testvectors_v1/mlkem_1024_keygen_seed_test.json"
370 ))
371 .unwrap();
372 let mut tested = 0u64;
373 for group in data["testGroups"].as_array().unwrap() {
374 if group["parameterSet"].as_str() != Some("ML-KEM-1024") {
375 continue;
376 }
377 for test in group["tests"].as_array().unwrap() {
378 let seed_hex = test["seed"].as_str().unwrap();
379 let expected_ek_hex = test["ek"].as_str().unwrap();
380 let expected_dk_hex = test["dk"].as_str().unwrap();
381 let result = test["result"].as_str().unwrap();
382
383 let seed = hex::decode_array::<64>(seed_hex.as_bytes()).unwrap();
384
385 let (dk, ek) =
386 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &seed);
387
388 let ek_hex = hex::encode(ek);
389 let dk_hex = hex::encode(dk);
390
391 if result == "valid" {
392 assert_eq!(
393 ek_hex, expected_ek_hex,
394 "wycheproof keygen KAT tcId={} ek mismatch",
395 test["tcId"]
396 );
397 assert_eq!(
398 dk_hex, expected_dk_hex,
399 "wycheproof keygen KAT tcId={} dk mismatch",
400 test["tcId"]
401 );
402 }
403 tested += 1;
404 }
405 }
406 assert!(tested > 0, "no ML-KEM-1024 keygen tests were run");
407 }
408
409 fn wycheproof_kem_skip_invalid_lengths(seed_hex: &str, c_hex: &str, ct_size: usize) -> bool {
410 seed_hex.len() != 128 || c_hex.len() != ct_size * 2
411 }
412
413 #[test]
414 fn ml_kem_1024_wycheproof_kem() {
415 let data: serde_json::Value =
416 serde_json::from_str(include_str!("../../testdata/wycheproof/testvectors_v1/mlkem_1024_test.json"))
417 .unwrap();
418 let mut tested = 0u64;
419 for group in data["testGroups"].as_array().unwrap() {
420 if group["parameterSet"].as_str() != Some("ML-KEM-1024") {
421 continue;
422 }
423 for test in group["tests"].as_array().unwrap() {
424 let seed_hex = test["seed"].as_str().unwrap();
425 let c_hex = test["c"].as_str().unwrap();
426 let expected_k_hex = test["K"].as_str().unwrap();
427 let result = test["result"].as_str().unwrap();
428
429 if wycheproof_kem_skip_invalid_lengths(seed_hex, c_hex, CIPHERTEXT_SIZE_1024) {
430 tested += 1;
431 continue;
432 }
433
434 let seed = hex::decode_array::<64>(seed_hex.as_bytes()).unwrap();
435
436 let (dk, ek) =
437 crypto_kem_keypair_derand::<4, SECRET_KEY_SIZE_1024, PUBLIC_KEY_SIZE_1024>(&ML_KEM_1024, &seed);
438
439 if let Some(expected_ek_hex) = test.get("ek").and_then(|v| v.as_str()) {
440 let ek_hex = hex::encode(ek);
441 assert_eq!(ek_hex, expected_ek_hex, "wycheproof KEM KAT tcId={} ek mismatch", test["tcId"]);
442 }
443
444 let c = decode_hex_array::<CIPHERTEXT_SIZE_1024>(c_hex);
445 let shared_secret =
446 crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &dk, &c);
447
448 if result == "valid" {
449 let k = shared_secret.unwrap();
450 let k_hex = hex::encode(k);
451 assert_eq!(k_hex, expected_k_hex, "wycheproof KEM KAT tcId={} K mismatch", test["tcId"]);
452 } else {
453 assert!(
454 shared_secret.is_ok(),
455 "wycheproof KEM KAT tcId={} unexpected error",
456 test["tcId"]
457 );
458 }
459 tested += 1;
460 }
461 }
462 assert!(tested > 0, "no ML-KEM-1024 KEM tests were run");
463 }
464
465 #[test]
466 fn ml_kem_1024_wycheproof_encaps() {
467 let data: serde_json::Value = serde_json::from_str(include_str!(
468 "../../testdata/wycheproof/testvectors_v1/mlkem_1024_encaps_test.json"
469 ))
470 .unwrap();
471 let mut tested = 0u64;
472 for group in data["testGroups"].as_array().unwrap() {
473 if group["parameterSet"].as_str() != Some("ML-KEM-1024") {
474 continue;
475 }
476 for test in group["tests"].as_array().unwrap() {
477 let ek_hex = test["ek"].as_str().unwrap();
478 let m_hex = test["m"].as_str().unwrap();
479 let expected_c_hex = test["c"].as_str().unwrap();
480 let expected_k_hex = test["K"].as_str().unwrap();
481 let result = test["result"].as_str().unwrap();
482
483 if ek_hex.len() != PUBLIC_KEY_SIZE_1024 * 2 {
484 tested += 1;
485 continue;
486 }
487
488 let ek = decode_hex_array::<PUBLIC_KEY_SIZE_1024>(ek_hex);
489
490 if result == "valid" {
491 let m = decode_hex_array::<32>(m_hex);
492 let (c, k) =
493 crypto_kem_enc_derand::<4, PUBLIC_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &ek, &m);
494 let c_hex_out = hex::encode(c);
495 let k_hex_out = hex::encode(k);
496 assert_eq!(
497 c_hex_out, expected_c_hex,
498 "wycheproof encaps KAT tcId={} c mismatch",
499 test["tcId"]
500 );
501 assert_eq!(
502 k_hex_out, expected_k_hex,
503 "wycheproof encaps KAT tcId={} K mismatch",
504 test["tcId"]
505 );
506 }
507 tested += 1;
508 }
509 }
510 assert!(tested > 0, "no ML-KEM-1024 encaps tests were run");
511 }
512
513 #[test]
514 fn ml_kem_1024_wycheproof_decaps_validation() {
515 let data: serde_json::Value = serde_json::from_str(include_str!(
516 "../../testdata/wycheproof/testvectors_v1/mlkem_1024_semi_expanded_decaps_test.json"
517 ))
518 .unwrap();
519 let mut tested = 0u64;
520 for group in data["testGroups"].as_array().unwrap() {
521 if group["parameterSet"].as_str() != Some("ML-KEM-1024") {
522 continue;
523 }
524 for test in group["tests"].as_array().unwrap() {
525 let flags: Vec<&str> = test["flags"]
526 .as_array()
527 .map(|a| a.iter().filter_map(|v| v.as_str()).collect())
528 .unwrap_or_default();
529 let dk_hex = test["dk"].as_str().unwrap();
530 let c_hex = test["c"].as_str().unwrap();
531
532 if flags.contains(&"IncorrectDecapsulationKeyLength") || flags.contains(&"IncorrectCiphertextLength") {
533 tested += 1;
534 continue;
535 }
536
537 let dk = decode_hex_array::<SECRET_KEY_SIZE_1024>(dk_hex);
538 let c = decode_hex_array::<CIPHERTEXT_SIZE_1024>(c_hex);
539
540 let result = crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &dk, &c);
541
542 assert!(result.is_ok(), "wycheproof decaps tcId={} panicked", test["tcId"]);
543 tested += 1;
544 }
545 }
546 assert!(tested > 0, "no ML-KEM-1024 decaps validation tests were run");
547 }
548
549 #[test]
550 fn ml_kem_1024_cross_implementation_pqcrypto() {
551 let data: serde_json::Value =
554 serde_json::from_str(include_str!("../../testdata/mlkem/pqcrypto_1024_vectors.json")).unwrap();
555 let vectors = data.as_array().unwrap();
556 assert!(vectors.len() >= 5, "not enough cross-impl vectors");
557
558 for (i, vector) in vectors.iter().enumerate() {
559 let sk_hex = vector["sk"].as_str().unwrap();
560 let ct_hex = vector["ct"].as_str().unwrap();
561 let expected_ss_hex = vector["ss"].as_str().unwrap();
562
563 let sk = decode_hex_array::<SECRET_KEY_SIZE_1024>(sk_hex);
564 let ct = decode_hex_array::<CIPHERTEXT_SIZE_1024>(ct_hex);
565
566 let ss = crypto_kem_dec::<4, SECRET_KEY_SIZE_1024, CIPHERTEXT_SIZE_1024>(&ML_KEM_1024, &sk, &ct).unwrap();
567 assert_eq!(
568 hex::encode(ss),
569 expected_ss_hex,
570 "cross-impl pqcrypto vector {i} decapsulation mismatch"
571 );
572 }
573 }
574}