1 /* Copyright 2024 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <cstdint>
16 #include <vector>
17
18 #include <string.h>
19
20 #include <gtest/gtest.h>
21
22 #include <openssl/base.h>
23 #include <openssl/bytestring.h>
24 #include <openssl/mem.h>
25 #include <openssl/mlkem.h>
26
27 #include "../fipsmodule/bcm_interface.h"
28 #include "../fipsmodule/keccak/internal.h"
29 #include "../internal.h"
30 #include "../test/file_test.h"
31 #include "../test/test_util.h"
32
33
34 namespace {
35
36 template <typename T>
Marshal(int (* marshal_func)(CBB *,const T *),const T * t)37 std::vector<uint8_t> Marshal(int (*marshal_func)(CBB *, const T *),
38 const T *t) {
39 bssl::ScopedCBB cbb;
40 uint8_t *encoded;
41 size_t encoded_len;
42 if (!CBB_init(cbb.get(), 1) || //
43 !marshal_func(cbb.get(), t) || //
44 !CBB_finish(cbb.get(), &encoded, &encoded_len)) {
45 abort();
46 }
47
48 std::vector<uint8_t> ret(encoded, encoded + encoded_len);
49 OPENSSL_free(encoded);
50 return ret;
51 }
52
53 // These functions wrap the methods that are only in the BCM interface. They
54 // take care of casting the key types from the public keys to the BCM types.
55 // That saves casting noise in the template functions.
56
wrapper_768_marshal_private_key(CBB * out,const struct MLKEM768_private_key * private_key)57 int wrapper_768_marshal_private_key(
58 CBB *out, const struct MLKEM768_private_key *private_key) {
59 return bcm_success(BCM_mlkem768_marshal_private_key(
60 out, reinterpret_cast<const BCM_mlkem768_private_key *>(private_key)));
61 }
62
wrapper_1024_marshal_private_key(CBB * out,const struct MLKEM1024_private_key * private_key)63 int wrapper_1024_marshal_private_key(
64 CBB *out, const struct MLKEM1024_private_key *private_key) {
65 return bcm_success(BCM_mlkem1024_marshal_private_key(
66 out, reinterpret_cast<const BCM_mlkem1024_private_key *>(private_key)));
67 }
68
wrapper_768_generate_key_external_seed(uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],struct MLKEM768_private_key * out_private_key,const uint8_t seed[MLKEM_SEED_BYTES])69 void wrapper_768_generate_key_external_seed(
70 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
71 struct MLKEM768_private_key *out_private_key,
72 const uint8_t seed[MLKEM_SEED_BYTES]) {
73 BCM_mlkem768_generate_key_external_seed(
74 out_encoded_public_key,
75 reinterpret_cast<BCM_mlkem768_private_key *>(out_private_key), seed);
76 }
77
wrapper_1024_generate_key_external_seed(uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],struct MLKEM1024_private_key * out_private_key,const uint8_t seed[MLKEM_SEED_BYTES])78 void wrapper_1024_generate_key_external_seed(
79 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
80 struct MLKEM1024_private_key *out_private_key,
81 const uint8_t seed[MLKEM_SEED_BYTES]) {
82 BCM_mlkem1024_generate_key_external_seed(
83 out_encoded_public_key,
84 reinterpret_cast<BCM_mlkem1024_private_key *>(out_private_key), seed);
85 }
86
wrapper_768_encap_external_entropy(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],const struct MLKEM768_public_key * public_key,const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY])87 void wrapper_768_encap_external_entropy(
88 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
89 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
90 const struct MLKEM768_public_key *public_key,
91 const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) {
92 BCM_mlkem768_encap_external_entropy(
93 out_ciphertext, out_shared_secret,
94 reinterpret_cast<const BCM_mlkem768_public_key *>(public_key), entropy);
95 }
96
wrapper_1024_encap_external_entropy(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],const struct MLKEM1024_public_key * public_key,const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY])97 void wrapper_1024_encap_external_entropy(
98 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
99 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
100 const struct MLKEM1024_public_key *public_key,
101 const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) {
102 BCM_mlkem1024_encap_external_entropy(
103 out_ciphertext, out_shared_secret,
104 reinterpret_cast<const BCM_mlkem1024_public_key *>(public_key), entropy);
105 }
106
wrapper_768_parse_private_key(struct MLKEM768_private_key * out_private_key,CBS * in)107 int wrapper_768_parse_private_key(struct MLKEM768_private_key *out_private_key,
108 CBS *in) {
109 return bcm_success(BCM_mlkem768_parse_private_key(
110 reinterpret_cast<BCM_mlkem768_private_key *>(out_private_key), in));
111 }
112
wrapper_1024_parse_private_key(struct MLKEM1024_private_key * out_private_key,CBS * in)113 int wrapper_1024_parse_private_key(
114 struct MLKEM1024_private_key *out_private_key, CBS *in) {
115 return bcm_success(BCM_mlkem1024_parse_private_key(
116 reinterpret_cast<BCM_mlkem1024_private_key *>(out_private_key), in));
117 }
118
119 template <typename PUBLIC_KEY, size_t PUBLIC_KEY_BYTES, typename PRIVATE_KEY,
120 size_t PRIVATE_KEY_BYTES,
121 void (*GENERATE)(uint8_t *, uint8_t *, PRIVATE_KEY *),
122 int (*FROM_SEED)(PRIVATE_KEY *, const uint8_t *, size_t),
123 void (*PUBLIC_FROM_PRIVATE)(PUBLIC_KEY *, const PRIVATE_KEY *),
124 int (*PARSE_PUBLIC)(PUBLIC_KEY *, CBS *),
125 int (*MARSHAL_PUBLIC)(CBB *, const PUBLIC_KEY *),
126 int (*PARSE_PRIVATE)(PRIVATE_KEY *, CBS *),
127 int (*MARSHAL_PRIVATE)(CBB *, const PRIVATE_KEY *),
128 size_t CIPHERTEXT_BYTES,
129 void (*ENCAP)(uint8_t *, uint8_t *, const PUBLIC_KEY *),
130 int (*DECAP)(uint8_t *, const uint8_t *, size_t, const PRIVATE_KEY *)>
BasicTest()131 void BasicTest() {
132 // This function makes several ML-KEM keys, which runs up against stack
133 // limits. Heap-allocate them instead.
134
135 uint8_t encoded_public_key[PUBLIC_KEY_BYTES];
136 uint8_t seed[MLKEM_SEED_BYTES];
137 auto priv = std::make_unique<PRIVATE_KEY>();
138 GENERATE(encoded_public_key, seed, priv.get());
139
140 {
141 auto priv2 = std::make_unique<PRIVATE_KEY>();
142 ASSERT_TRUE(FROM_SEED(priv2.get(), seed, sizeof(seed)));
143 EXPECT_EQ(Bytes(Declassified(Marshal(MARSHAL_PRIVATE, priv.get()))),
144 Bytes(Declassified(Marshal(MARSHAL_PRIVATE, priv2.get()))));
145 }
146
147 uint8_t first_two_bytes[2];
148 OPENSSL_memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes));
149 OPENSSL_memset(encoded_public_key, 0xff, sizeof(first_two_bytes));
150 CBS encoded_public_key_cbs;
151 CBS_init(&encoded_public_key_cbs, encoded_public_key,
152 sizeof(encoded_public_key));
153 auto pub = std::make_unique<PUBLIC_KEY>();
154 // Parsing should fail because the first coefficient is >= kPrime;
155 ASSERT_FALSE(PARSE_PUBLIC(pub.get(), &encoded_public_key_cbs));
156
157 OPENSSL_memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
158 CBS_init(&encoded_public_key_cbs, encoded_public_key,
159 sizeof(encoded_public_key));
160 ASSERT_TRUE(PARSE_PUBLIC(pub.get(), &encoded_public_key_cbs));
161 EXPECT_EQ(CBS_len(&encoded_public_key_cbs), 0u);
162
163 EXPECT_EQ(Bytes(encoded_public_key),
164 Bytes(Marshal(MARSHAL_PUBLIC, pub.get())));
165
166 auto pub2 = std::make_unique<PUBLIC_KEY>();
167 PUBLIC_FROM_PRIVATE(pub2.get(), priv.get());
168 EXPECT_EQ(Bytes(encoded_public_key),
169 Bytes(Marshal(MARSHAL_PUBLIC, pub2.get())));
170
171 std::vector<uint8_t> encoded_private_key(
172 Marshal(MARSHAL_PRIVATE, priv.get()));
173 EXPECT_EQ(encoded_private_key.size(), size_t{PRIVATE_KEY_BYTES});
174
175 OPENSSL_memcpy(first_two_bytes, encoded_private_key.data(),
176 sizeof(first_two_bytes));
177 OPENSSL_memset(encoded_private_key.data(), 0xff, sizeof(first_two_bytes));
178 CBS cbs;
179 CBS_init(&cbs, encoded_private_key.data(), encoded_private_key.size());
180 auto priv2 = std::make_unique<PRIVATE_KEY>();
181 // Parsing should fail because the first coefficient is >= kPrime.
182 ASSERT_FALSE(PARSE_PRIVATE(priv2.get(), &cbs));
183
184 OPENSSL_memcpy(encoded_private_key.data(), first_two_bytes,
185 sizeof(first_two_bytes));
186 CBS_init(&cbs, encoded_private_key.data(), encoded_private_key.size());
187 ASSERT_TRUE(PARSE_PRIVATE(priv2.get(), &cbs));
188 EXPECT_EQ(Bytes(Declassified(encoded_private_key)),
189 Bytes(Declassified(Marshal(MARSHAL_PRIVATE, priv2.get()))));
190
191 uint8_t ciphertext[CIPHERTEXT_BYTES];
192 uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES];
193 uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES];
194 ENCAP(ciphertext, shared_secret1, pub.get());
195 ASSERT_TRUE(
196 DECAP(shared_secret2, ciphertext, sizeof(ciphertext), priv.get()));
197 EXPECT_EQ(Bytes(Declassified(shared_secret1)),
198 Bytes(Declassified(shared_secret2)));
199 ASSERT_TRUE(
200 DECAP(shared_secret2, ciphertext, sizeof(ciphertext), priv2.get()));
201 EXPECT_EQ(Bytes(Declassified(shared_secret1)),
202 Bytes(Declassified(shared_secret2)));
203 }
204
TEST(MLKEMTest,Basic768)205 TEST(MLKEMTest, Basic768) {
206 BasicTest<MLKEM768_public_key, MLKEM768_PUBLIC_KEY_BYTES,
207 MLKEM768_private_key, BCM_MLKEM768_PRIVATE_KEY_BYTES,
208 MLKEM768_generate_key, MLKEM768_private_key_from_seed,
209 MLKEM768_public_from_private, MLKEM768_parse_public_key,
210 MLKEM768_marshal_public_key, wrapper_768_parse_private_key,
211 wrapper_768_marshal_private_key, MLKEM768_CIPHERTEXT_BYTES,
212 MLKEM768_encap, MLKEM768_decap>();
213 }
214
TEST(MLKEMTest,Basic1024)215 TEST(MLKEMTest, Basic1024) {
216 BasicTest<MLKEM1024_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
217 MLKEM1024_private_key, BCM_MLKEM1024_PRIVATE_KEY_BYTES,
218 MLKEM1024_generate_key, MLKEM1024_private_key_from_seed,
219 MLKEM1024_public_from_private, MLKEM1024_parse_public_key,
220 MLKEM1024_marshal_public_key, wrapper_1024_parse_private_key,
221 wrapper_1024_marshal_private_key, MLKEM1024_CIPHERTEXT_BYTES,
222 MLKEM1024_encap, MLKEM1024_decap>();
223 }
224
225 template <typename PUBLIC_KEY, size_t PUBLIC_KEY_BYTES, typename PRIVATE_KEY,
226 int (*MARSHAL_PRIVATE)(CBB *, const PRIVATE_KEY *),
227 void (*GENERATE)(uint8_t *, PRIVATE_KEY *, const uint8_t *)>
MLKEMKeyGenFileTest(FileTest * t)228 void MLKEMKeyGenFileTest(FileTest *t) {
229 std::vector<uint8_t> expected_pub_key_bytes, seed, expected_priv_key_bytes;
230 ASSERT_TRUE(t->GetBytes(&seed, "seed"));
231 CONSTTIME_SECRET(seed.data(), seed.size());
232 ASSERT_TRUE(t->GetBytes(&expected_pub_key_bytes, "public_key"));
233 ASSERT_TRUE(t->GetBytes(&expected_priv_key_bytes, "private_key"));
234
235 ASSERT_EQ(seed.size(), size_t{MLKEM_SEED_BYTES});
236
237 std::vector<uint8_t> pub_key_bytes(PUBLIC_KEY_BYTES);
238 auto priv = std::make_unique<PRIVATE_KEY>();
239 GENERATE(pub_key_bytes.data(), priv.get(), seed.data());
240 const std::vector<uint8_t> priv_key_bytes(
241 Marshal(MARSHAL_PRIVATE, priv.get()));
242
243 EXPECT_EQ(Bytes(pub_key_bytes), Bytes(expected_pub_key_bytes));
244 EXPECT_EQ(Bytes(Declassified(priv_key_bytes)),
245 Bytes(expected_priv_key_bytes));
246 }
247
TEST(MLKEMTest,KeyGen768TestVectors)248 TEST(MLKEMTest, KeyGen768TestVectors) {
249 FileTestGTest(
250 "crypto/mlkem/mlkem768_keygen_tests.txt",
251 MLKEMKeyGenFileTest<MLKEM768_public_key, MLKEM768_PUBLIC_KEY_BYTES,
252 MLKEM768_private_key, wrapper_768_marshal_private_key,
253 wrapper_768_generate_key_external_seed>);
254 }
255
TEST(MLKEMTest,KeyGen1024TestVectors)256 TEST(MLKEMTest, KeyGen1024TestVectors) {
257 FileTestGTest(
258 "crypto/mlkem/mlkem1024_keygen_tests.txt",
259 MLKEMKeyGenFileTest<MLKEM1024_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
260 MLKEM1024_private_key,
261 wrapper_1024_marshal_private_key,
262 wrapper_1024_generate_key_external_seed>);
263 }
264
265 template <typename PUBLIC_KEY, size_t PUBLIC_KEY_BYTES, typename PRIVATE_KEY,
266 int (*MARSHAL_PRIVATE)(CBB *, const PRIVATE_KEY *),
267 void (*GENERATE)(uint8_t *, PRIVATE_KEY *, const uint8_t *)>
MLKEMNistKeyGenFileTest(FileTest * t)268 void MLKEMNistKeyGenFileTest(FileTest *t) {
269 std::vector<uint8_t> expected_pub_key_bytes, z, d, expected_priv_key_bytes;
270 ASSERT_TRUE(t->GetBytes(&z, "z"));
271 ASSERT_TRUE(t->GetBytes(&d, "d"));
272 ASSERT_TRUE(t->GetBytes(&expected_pub_key_bytes, "ek"));
273 ASSERT_TRUE(t->GetBytes(&expected_priv_key_bytes, "dk"));
274
275 ASSERT_EQ(z.size(), size_t{MLKEM_SEED_BYTES} / 2);
276 ASSERT_EQ(d.size(), size_t{MLKEM_SEED_BYTES} / 2);
277
278 uint8_t seed[MLKEM_SEED_BYTES];
279 OPENSSL_memcpy(&seed[0], d.data(), d.size());
280 OPENSSL_memcpy(&seed[MLKEM_SEED_BYTES / 2], z.data(), z.size());
281 std::vector<uint8_t> pub_key_bytes(PUBLIC_KEY_BYTES);
282 auto priv = std::make_unique<PRIVATE_KEY>();
283 GENERATE(pub_key_bytes.data(), priv.get(), seed);
284 const std::vector<uint8_t> priv_key_bytes(
285 Marshal(MARSHAL_PRIVATE, priv.get()));
286
287 EXPECT_EQ(Bytes(pub_key_bytes), Bytes(expected_pub_key_bytes));
288 EXPECT_EQ(Bytes(priv_key_bytes), Bytes(expected_priv_key_bytes));
289 }
290
TEST(MLKEMTest,NISTKeyGen768TestVectors)291 TEST(MLKEMTest, NISTKeyGen768TestVectors) {
292 FileTestGTest(
293 "crypto/mlkem/mlkem768_nist_keygen_tests.txt",
294 MLKEMNistKeyGenFileTest<MLKEM768_public_key, MLKEM768_PUBLIC_KEY_BYTES,
295 MLKEM768_private_key,
296 wrapper_768_marshal_private_key,
297 wrapper_768_generate_key_external_seed>);
298 }
299
TEST(MLKEMTest,NISTKeyGen1024TestVectors)300 TEST(MLKEMTest, NISTKeyGen1024TestVectors) {
301 FileTestGTest(
302 "crypto/mlkem/mlkem1024_nist_keygen_tests.txt",
303 MLKEMNistKeyGenFileTest<MLKEM1024_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
304 MLKEM1024_private_key,
305 wrapper_1024_marshal_private_key,
306 wrapper_1024_generate_key_external_seed>);
307 }
308
309 template <typename PUBLIC_KEY, size_t PUBLIC_KEY_BYTES,
310 int (*PARSE_PUBLIC)(PUBLIC_KEY *, CBS *), size_t CIPHERTEXT_BYTES,
311 void (*ENCAP)(uint8_t *, uint8_t *, const PUBLIC_KEY *,
312 const uint8_t *)>
MLKEMEncapFileTest(FileTest * t)313 void MLKEMEncapFileTest(FileTest *t) {
314 std::vector<uint8_t> pub_key_bytes, entropy, expected_ciphertext,
315 expected_shared_secret;
316 ASSERT_TRUE(t->GetBytes(&entropy, "entropy"));
317 CONSTTIME_SECRET(entropy.data(), entropy.size());
318 ASSERT_TRUE(t->GetBytes(&pub_key_bytes, "public_key"));
319 ASSERT_TRUE(t->GetBytes(&expected_ciphertext, "ciphertext"));
320 ASSERT_TRUE(t->GetBytes(&expected_shared_secret, "shared_secret"));
321 std::string result;
322 ASSERT_TRUE(t->GetAttribute(&result, "result"));
323
324 PUBLIC_KEY pub_key;
325 CBS pub_key_cbs;
326 CBS_init(&pub_key_cbs, pub_key_bytes.data(), pub_key_bytes.size());
327 const int parse_ok = PARSE_PUBLIC(&pub_key, &pub_key_cbs);
328 ASSERT_EQ(parse_ok, result == "pass");
329 if (!parse_ok) {
330 return;
331 }
332
333 uint8_t ciphertext[CIPHERTEXT_BYTES];
334 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
335 ENCAP(ciphertext, shared_secret, &pub_key, entropy.data());
336
337 ASSERT_EQ(Bytes(expected_ciphertext), Bytes(ciphertext));
338 ASSERT_EQ(Bytes(expected_shared_secret), Bytes(Declassified(shared_secret)));
339 }
340
TEST(MLKEMTest,Encap768TestVectors)341 TEST(MLKEMTest, Encap768TestVectors) {
342 FileTestGTest(
343 "crypto/mlkem/mlkem768_encap_tests.txt",
344 MLKEMEncapFileTest<MLKEM768_public_key, MLKEM768_PUBLIC_KEY_BYTES,
345 MLKEM768_parse_public_key, MLKEM768_CIPHERTEXT_BYTES,
346 wrapper_768_encap_external_entropy>);
347 }
348
TEST(MLKEMTest,Encap1024TestVectors)349 TEST(MLKEMTest, Encap1024TestVectors) {
350 FileTestGTest(
351 "crypto/mlkem/mlkem1024_encap_tests.txt",
352 MLKEMEncapFileTest<MLKEM1024_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
353 MLKEM1024_parse_public_key, MLKEM1024_CIPHERTEXT_BYTES,
354 wrapper_1024_encap_external_entropy>);
355 }
356
357 template <typename PRIVATE_KEY, size_t PRIVATE_KEY_BYTES,
358 int (*PARSE_PRIVATE)(PRIVATE_KEY *, CBS *), size_t CIPHERTEXT_BYTES,
359 int (*DECAP)(uint8_t *, const uint8_t *, size_t, const PRIVATE_KEY *)>
MLKEMDecapFileTest(FileTest * t)360 void MLKEMDecapFileTest(FileTest *t) {
361 std::vector<uint8_t> priv_key_bytes, ciphertext, expected_shared_secret;
362 ASSERT_TRUE(t->GetBytes(&priv_key_bytes, "private_key"));
363 ASSERT_TRUE(t->GetBytes(&ciphertext, "ciphertext"));
364 ASSERT_TRUE(t->GetBytes(&expected_shared_secret, "shared_secret"));
365 std::string result;
366 ASSERT_TRUE(t->GetAttribute(&result, "result"));
367
368 PRIVATE_KEY priv_key;
369 CBS priv_key_cbs;
370 CBS_init(&priv_key_cbs, priv_key_bytes.data(), priv_key_bytes.size());
371 const int parse_ok = PARSE_PRIVATE(&priv_key, &priv_key_cbs);
372 if (!parse_ok) {
373 ASSERT_NE(result, "pass");
374 return;
375 }
376
377 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
378 const int decap_ok =
379 DECAP(shared_secret, ciphertext.data(), ciphertext.size(), &priv_key);
380 if (!decap_ok) {
381 ASSERT_NE(result, "pass");
382 return;
383 }
384
385 ASSERT_EQ(Bytes(expected_shared_secret), Bytes(shared_secret));
386 }
387
TEST(MLKEMTest,Decap768TestVectors)388 TEST(MLKEMTest, Decap768TestVectors) {
389 FileTestGTest(
390 "crypto/mlkem/mlkem768_decap_tests.txt",
391 MLKEMDecapFileTest<MLKEM768_private_key, BCM_MLKEM768_PRIVATE_KEY_BYTES,
392 wrapper_768_parse_private_key,
393 MLKEM768_CIPHERTEXT_BYTES, MLKEM768_decap>);
394 }
395
TEST(MLKEMTest,Decap1024TestVectors)396 TEST(MLKEMTest, Decap1024TestVectors) {
397 FileTestGTest(
398 "crypto/mlkem/mlkem1024_decap_tests.txt",
399 MLKEMDecapFileTest<MLKEM1024_private_key, BCM_MLKEM1024_PRIVATE_KEY_BYTES,
400 wrapper_1024_parse_private_key,
401 MLKEM1024_CIPHERTEXT_BYTES, MLKEM1024_decap>);
402 }
403
404 template <typename PRIVATE_KEY, int (*PARSE_PRIVATE)(PRIVATE_KEY *, CBS *),
405 int (*DECAP)(uint8_t *, const uint8_t *, size_t, const PRIVATE_KEY *)>
MLKEMNistDecapFileTest(FileTest * t)406 void MLKEMNistDecapFileTest(FileTest *t) {
407 std::vector<uint8_t> ciphertext, expected_shared_secret, private_key_bytes;
408 ASSERT_TRUE(t->GetBytes(&ciphertext, "c"));
409 ASSERT_TRUE(t->GetBytes(&expected_shared_secret, "k"));
410 ASSERT_TRUE(t->GetInstructionBytes(&private_key_bytes, "dk"));
411
412 PRIVATE_KEY priv;
413 CBS private_key_cbs;
414 CBS_init(&private_key_cbs, private_key_bytes.data(),
415 private_key_bytes.size());
416 ASSERT_TRUE(PARSE_PRIVATE(&priv, &private_key_cbs));
417
418 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
419 ASSERT_TRUE(
420 DECAP(shared_secret, ciphertext.data(), ciphertext.size(), &priv));
421
422 ASSERT_EQ(Bytes(shared_secret), Bytes(expected_shared_secret));
423 }
424
TEST(MLKEMTest,NistDecap768TestVectors)425 TEST(MLKEMTest, NistDecap768TestVectors) {
426 FileTestGTest(
427 "crypto/mlkem/mlkem768_nist_decap_tests.txt",
428 MLKEMNistDecapFileTest<MLKEM768_private_key,
429 wrapper_768_parse_private_key, MLKEM768_decap>);
430 }
431
TEST(MLKEMTest,NistDecap1024TestVectors)432 TEST(MLKEMTest, NistDecap1024TestVectors) {
433 FileTestGTest(
434 "crypto/mlkem/mlkem1024_nist_decap_tests.txt",
435 MLKEMNistDecapFileTest<MLKEM1024_private_key,
436 wrapper_1024_parse_private_key, MLKEM1024_decap>);
437 }
438
439 template <
440 typename PUBLIC_KEY, size_t PUBLIC_KEY_BYTES, typename PRIVATE_KEY,
441 size_t PRIVATE_KEY_BYTES,
442 void (*GENERATE)(uint8_t *, PRIVATE_KEY *, const uint8_t *),
443 void (*TO_PUBLIC)(PUBLIC_KEY *, const PRIVATE_KEY *),
444 int (*MARSHAL_PRIVATE)(CBB *, const PRIVATE_KEY *), size_t CIPHERTEXT_BYTES,
445 void (*ENCAP)(uint8_t *, uint8_t *, const PUBLIC_KEY *, const uint8_t *),
446 int (*DECAP)(uint8_t *, const uint8_t *, size_t, const PRIVATE_KEY *)>
IteratedTest(uint8_t out[32])447 void IteratedTest(uint8_t out[32]) {
448 BORINGSSL_keccak_st generate_st;
449 BORINGSSL_keccak_init(&generate_st, boringssl_shake128);
450 BORINGSSL_keccak_st results_st;
451 BORINGSSL_keccak_init(&results_st, boringssl_shake128);
452
453 auto priv = std::make_unique<PRIVATE_KEY>();
454 auto pub = std::make_unique<PUBLIC_KEY>();
455 for (int i = 0; i < 10000; i++) {
456 uint8_t seed[MLKEM_SEED_BYTES];
457 BORINGSSL_keccak_squeeze(&generate_st, seed, sizeof(seed));
458 uint8_t encoded_pub[PUBLIC_KEY_BYTES];
459 GENERATE(encoded_pub, priv.get(), seed);
460 TO_PUBLIC(pub.get(), priv.get());
461
462 BORINGSSL_keccak_absorb(&results_st, encoded_pub, sizeof(encoded_pub));
463 const std::vector<uint8_t> encoded_priv(
464 Marshal(MARSHAL_PRIVATE, priv.get()));
465 BORINGSSL_keccak_absorb(&results_st, encoded_priv.data(),
466 encoded_priv.size());
467
468 uint8_t encap_entropy[BCM_MLKEM_ENCAP_ENTROPY];
469 BORINGSSL_keccak_squeeze(&generate_st, encap_entropy,
470 sizeof(encap_entropy));
471 uint8_t ciphertext[CIPHERTEXT_BYTES];
472 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
473 ENCAP(ciphertext, shared_secret, pub.get(), encap_entropy);
474
475 BORINGSSL_keccak_absorb(&results_st, ciphertext, sizeof(ciphertext));
476 BORINGSSL_keccak_absorb(&results_st, shared_secret, sizeof(shared_secret));
477
478 uint8_t invalid_ciphertext[CIPHERTEXT_BYTES];
479 BORINGSSL_keccak_squeeze(&generate_st, invalid_ciphertext,
480 sizeof(invalid_ciphertext));
481 ASSERT_TRUE(DECAP(shared_secret, invalid_ciphertext,
482 sizeof(invalid_ciphertext), priv.get()));
483
484 BORINGSSL_keccak_absorb(&results_st, shared_secret, sizeof(shared_secret));
485 }
486
487 BORINGSSL_keccak_squeeze(&results_st, out, 32);
488 }
489
TEST(MLKEMTest,Iterate768)490 TEST(MLKEMTest, Iterate768) {
491 // The structure of this test is taken from
492 // https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
493 // but the final value has been updated to reflect the change from Kyber to
494 // ML-KEM.
495 uint8_t result[32];
496 IteratedTest<MLKEM768_public_key, MLKEM768_PUBLIC_KEY_BYTES,
497 MLKEM768_private_key, BCM_MLKEM768_PRIVATE_KEY_BYTES,
498 wrapper_768_generate_key_external_seed,
499 MLKEM768_public_from_private, wrapper_768_marshal_private_key,
500 MLKEM768_CIPHERTEXT_BYTES, wrapper_768_encap_external_entropy,
501 MLKEM768_decap>(result);
502
503 const uint8_t kExpected[32] = {
504 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
505 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
506 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1};
507 EXPECT_EQ(Bytes(result), Bytes(kExpected));
508 }
509
510
TEST(MLKEMTest,Iterate1024)511 TEST(MLKEMTest, Iterate1024) {
512 // The structure of this test is taken from
513 // https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
514 // but the final value has been updated to reflect the change from Kyber to
515 // ML-KEM.
516 uint8_t result[32];
517 IteratedTest<MLKEM1024_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
518 MLKEM1024_private_key, BCM_MLKEM1024_PRIVATE_KEY_BYTES,
519 wrapper_1024_generate_key_external_seed,
520 MLKEM1024_public_from_private, wrapper_1024_marshal_private_key,
521 MLKEM1024_CIPHERTEXT_BYTES, wrapper_1024_encap_external_entropy,
522 MLKEM1024_decap>(result);
523
524 const uint8_t kExpected[32] = {
525 0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
526 0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
527 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5};
528 EXPECT_EQ(Bytes(result), Bytes(kExpected));
529 }
530
TEST(MLKEMTest,Self)531 TEST(MLKEMTest, Self) { ASSERT_TRUE(boringssl_self_test_mlkem()); }
532
TEST(MLKEMTest,PWCT)533 TEST(MLKEMTest, PWCT) {
534 auto pub768 = std::make_unique<uint8_t[]>(BCM_MLKEM768_PUBLIC_KEY_BYTES);
535 auto priv768 = std::make_unique<BCM_mlkem768_private_key>();
536 ASSERT_EQ(
537 BCM_mlkem768_generate_key_fips(pub768.get(), nullptr, priv768.get()),
538 bcm_status::approved);
539
540 auto pub1024 = std::make_unique<uint8_t[]>(BCM_MLKEM1024_PUBLIC_KEY_BYTES);
541 auto priv1024 = std::make_unique<BCM_mlkem1024_private_key>();
542 ASSERT_EQ(
543 BCM_mlkem1024_generate_key_fips(pub1024.get(), nullptr, priv1024.get()),
544 bcm_status::approved);
545 }
546
547 } // namespace
548