diff options
Diffstat (limited to 'src/regress/lib/libcrypto/mlkem/mlkem_tests.c')
-rw-r--r-- | src/regress/lib/libcrypto/mlkem/mlkem_tests.c | 728 |
1 files changed, 728 insertions, 0 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_tests.c new file mode 100644 index 0000000000..716f38d144 --- /dev/null +++ b/src/regress/lib/libcrypto/mlkem/mlkem_tests.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* $OpenBSD: mlkem_tests.c,v 1.1 2024/12/26 00:04:24 tb Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2024 Google Inc. | ||
4 | * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> | ||
5 | * Copyright (c) 2024 Bob Beck <beck@obtuse.com> | ||
6 | * | ||
7 | * Permission to use, copy, modify, and/or distribute this software for any | ||
8 | * purpose with or without fee is hereby granted, provided that the above | ||
9 | * copyright notice and this permission notice appear in all copies. | ||
10 | * | ||
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
14 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
16 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
17 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
18 | */ | ||
19 | |||
20 | #include <err.h> | ||
21 | #include <stdint.h> | ||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | |||
26 | #include "bytestring.h" | ||
27 | #include "mlkem.h" | ||
28 | |||
29 | #include "mlkem_internal.h" | ||
30 | |||
31 | #include "mlkem_tests_util.h" | ||
32 | #include "parse_test_file.h" | ||
33 | |||
34 | enum test_type { | ||
35 | TEST_TYPE_NORMAL, | ||
36 | TEST_TYPE_NIST, | ||
37 | }; | ||
38 | |||
39 | struct decap_ctx { | ||
40 | struct parse *parse_ctx; | ||
41 | |||
42 | void *private_key; | ||
43 | size_t private_key_len; | ||
44 | |||
45 | mlkem_parse_private_key_fn parse_private_key; | ||
46 | mlkem_decap_fn decap; | ||
47 | }; | ||
48 | |||
49 | enum decap_states { | ||
50 | DECAP_PRIVATE_KEY, | ||
51 | DECAP_CIPHERTEXT, | ||
52 | DECAP_RESULT, | ||
53 | DECAP_SHARED_SECRET, | ||
54 | N_DECAP_STATES, | ||
55 | }; | ||
56 | |||
57 | static const struct line_spec decap_state_machine[] = { | ||
58 | [DECAP_PRIVATE_KEY] = { | ||
59 | .state = DECAP_PRIVATE_KEY, | ||
60 | .type = LINE_HEX, | ||
61 | .name = "private key", | ||
62 | .label = "private_key", | ||
63 | }, | ||
64 | [DECAP_CIPHERTEXT] = { | ||
65 | .state = DECAP_CIPHERTEXT, | ||
66 | .type = LINE_HEX, | ||
67 | .name = "cipher text", | ||
68 | .label = "ciphertext", | ||
69 | }, | ||
70 | [DECAP_RESULT] = { | ||
71 | .state = DECAP_RESULT, | ||
72 | .type = LINE_STRING_MATCH, | ||
73 | .name = "result", | ||
74 | .label = "result", | ||
75 | .match = "fail", | ||
76 | }, | ||
77 | [DECAP_SHARED_SECRET] = { | ||
78 | .state = DECAP_SHARED_SECRET, | ||
79 | .type = LINE_HEX, | ||
80 | .name = "shared secret", | ||
81 | .label = "shared_secret", | ||
82 | }, | ||
83 | }; | ||
84 | |||
85 | static int | ||
86 | decap_init(void *ctx, void *parse_ctx) | ||
87 | { | ||
88 | struct decap_ctx *decap = ctx; | ||
89 | |||
90 | decap->parse_ctx = parse_ctx; | ||
91 | |||
92 | return 1; | ||
93 | } | ||
94 | |||
95 | static void | ||
96 | decap_finish(void *ctx) | ||
97 | { | ||
98 | (void)ctx; | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | MlkemDecapFileTest(struct decap_ctx *decap) | ||
103 | { | ||
104 | struct parse *p = decap->parse_ctx; | ||
105 | uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES]; | ||
106 | CBS ciphertext, shared_secret, private_key; | ||
107 | int should_fail; | ||
108 | int failed = 1; | ||
109 | |||
110 | parse_get_cbs(p, DECAP_CIPHERTEXT, &ciphertext); | ||
111 | parse_get_cbs(p, DECAP_SHARED_SECRET, &shared_secret); | ||
112 | parse_get_cbs(p, DECAP_PRIVATE_KEY, &private_key); | ||
113 | parse_get_int(p, DECAP_RESULT, &should_fail); | ||
114 | |||
115 | if (!decap->parse_private_key(decap->private_key, &private_key)) { | ||
116 | if ((failed = !should_fail)) | ||
117 | parse_info(p, "parse private key"); | ||
118 | goto err; | ||
119 | } | ||
120 | if (!decap->decap(shared_secret_buf, | ||
121 | CBS_data(&ciphertext), CBS_len(&ciphertext), decap->private_key)) { | ||
122 | if ((failed = !should_fail)) | ||
123 | parse_info(p, decap"); | ||
124 | goto err; | ||
125 | } | ||
126 | |||
127 | failed = !parse_data_equal(p, "shared_secret", &shared_secret, | ||
128 | shared_secret_buf, sizeof(shared_secret_buf)); | ||
129 | |||
130 | if (should_fail != failed) { | ||
131 | parse_info(p, "FAIL: should_fail %d, failed %d", | ||
132 | should_fail, failed); | ||
133 | failed = 1; | ||
134 | } | ||
135 | |||
136 | err: | ||
137 | return failed; | ||
138 | } | ||
139 | |||
140 | static int | ||
141 | decap_run_test_case(void *ctx) | ||
142 | { | ||
143 | return MlkemDecapFileTest(ctx); | ||
144 | } | ||
145 | |||
146 | static const struct test_parse decap_parse = { | ||
147 | .states = decap_state_machine, | ||
148 | .num_states = N_DECAP_STATES, | ||
149 | |||
150 | .init = decap_init, | ||
151 | .finish = decap_finish, | ||
152 | |||
153 | .run_test_case = decap_run_test_case, | ||
154 | }; | ||
155 | |||
156 | enum nist_decap_instructions { | ||
157 | NIST_DECAP_DK, | ||
158 | N_NIST_DECAP_INSTRUCTIONS, | ||
159 | }; | ||
160 | |||
161 | static const struct line_spec nist_decap_instruction_state_machine[] = { | ||
162 | [NIST_DECAP_DK] = { | ||
163 | .state = NIST_DECAP_DK, | ||
164 | .type = LINE_HEX, | ||
165 | .name = "private key (instruction [dk])", | ||
166 | .label = "dk", | ||
167 | }, | ||
168 | }; | ||
169 | |||
170 | enum nist_decap_states { | ||
171 | NIST_DECAP_C, | ||
172 | NIST_DECAP_K, | ||
173 | N_NIST_DECAP_STATES, | ||
174 | }; | ||
175 | |||
176 | static const struct line_spec nist_decap_state_machine[] = { | ||
177 | [NIST_DECAP_C] = { | ||
178 | .state = NIST_DECAP_C, | ||
179 | .type = LINE_HEX, | ||
180 | .name = "ciphertext (c)", | ||
181 | .label = "c", | ||
182 | }, | ||
183 | [NIST_DECAP_K] = { | ||
184 | .state = NIST_DECAP_K, | ||
185 | .type = LINE_HEX, | ||
186 | .name = "shared secret (k)", | ||
187 | .label = "k", | ||
188 | }, | ||
189 | }; | ||
190 | |||
191 | static int | ||
192 | MlkemNistDecapFileTest(struct decap_ctx *decap) | ||
193 | { | ||
194 | struct parse *p = decap->parse_ctx; | ||
195 | uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; | ||
196 | CBS dk, c, k; | ||
197 | int failed = 1; | ||
198 | |||
199 | parse_instruction_get_cbs(p, NIST_DECAP_DK, &dk); | ||
200 | parse_get_cbs(p, NIST_DECAP_C, &c); | ||
201 | parse_get_cbs(p, NIST_DECAP_K, &k); | ||
202 | |||
203 | if (!parse_length_equal(p, "private key", | ||
204 | decap->private_key_len, CBS_len(&dk))) | ||
205 | goto err; | ||
206 | if (!parse_length_equal(p, "shared secret", | ||
207 | MLKEM_SHARED_SECRET_BYTES, CBS_len(&k))) | ||
208 | goto err; | ||
209 | |||
210 | if (!decap->parse_private_key(decap->private_key, &dk)) { | ||
211 | parse_info(p, "parse private key"); | ||
212 | goto err; | ||
213 | } | ||
214 | if (!decap->decap(shared_secret, CBS_data(&c), CBS_len(&c), | ||
215 | decap->private_key)) { | ||
216 | parse_info(p, "decap"); | ||
217 | goto err; | ||
218 | } | ||
219 | |||
220 | failed = !parse_data_equal(p, "shared secret", &k, | ||
221 | shared_secret, MLKEM_SHARED_SECRET_BYTES); | ||
222 | |||
223 | err: | ||
224 | return failed; | ||
225 | } | ||
226 | |||
227 | static int | ||
228 | nist_decap_run_test_case(void *ctx) | ||
229 | { | ||
230 | return MlkemNistDecapFileTest(ctx); | ||
231 | } | ||
232 | |||
233 | static const struct test_parse nist_decap_parse = { | ||
234 | .instructions = nist_decap_instruction_state_machine, | ||
235 | .num_instructions = N_NIST_DECAP_INSTRUCTIONS, | ||
236 | |||
237 | .states = nist_decap_state_machine, | ||
238 | .num_states = N_NIST_DECAP_STATES, | ||
239 | |||
240 | .init = decap_init, | ||
241 | .finish = decap_finish, | ||
242 | |||
243 | .run_test_case = nist_decap_run_test_case, | ||
244 | }; | ||
245 | |||
246 | static int | ||
247 | mlkem_decap_tests(const char *fn, size_t size, enum test_type test_type) | ||
248 | { | ||
249 | struct MLKEM768_private_key private_key768; | ||
250 | struct decap_ctx decap768 = { | ||
251 | .private_key = &private_key768, | ||
252 | .private_key_len = MLKEM768_PRIVATE_KEY_BYTES, | ||
253 | |||
254 | .parse_private_key = mlkem768_parse_private_key, | ||
255 | .decap = mlkem768_decap, | ||
256 | }; | ||
257 | struct MLKEM1024_private_key private_key1024; | ||
258 | struct decap_ctx decap1024 = { | ||
259 | .private_key = &private_key1024, | ||
260 | .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES, | ||
261 | |||
262 | .parse_private_key = mlkem1024_parse_private_key, | ||
263 | .decap = mlkem1024_decap, | ||
264 | }; | ||
265 | |||
266 | if (size == 768 && test_type == TEST_TYPE_NORMAL) | ||
267 | return parse_test_file(fn, &decap_parse, &decap768); | ||
268 | if (size == 768 && test_type == TEST_TYPE_NIST) | ||
269 | return parse_test_file(fn, &nist_decap_parse, &decap768); | ||
270 | if (size == 1024 && test_type == TEST_TYPE_NORMAL) | ||
271 | return parse_test_file(fn, &decap_parse, &decap1024); | ||
272 | if (size == 1024 && test_type == TEST_TYPE_NIST) | ||
273 | return parse_test_file(fn, &nist_decap_parse, &decap1024); | ||
274 | |||
275 | errx(1, "unknown decap test: size %zu, type %d", size, test_type); | ||
276 | } | ||
277 | |||
278 | struct encap_ctx { | ||
279 | struct parse *parse_ctx; | ||
280 | |||
281 | void *public_key; | ||
282 | uint8_t *ciphertext; | ||
283 | size_t ciphertext_len; | ||
284 | |||
285 | mlkem_parse_public_key_fn parse_public_key; | ||
286 | mlkem_encap_external_entropy_fn encap_external_entropy; | ||
287 | }; | ||
288 | |||
289 | enum encap_states { | ||
290 | ENCAP_ENTROPY, | ||
291 | ENCAP_PUBLIC_KEY, | ||
292 | ENCAP_RESULT, | ||
293 | ENCAP_CIPHERTEXT, | ||
294 | ENCAP_SHARED_SECRET, | ||
295 | N_ENCAP_STATES, | ||
296 | }; | ||
297 | |||
298 | static const struct line_spec encap_state_machine[] = { | ||
299 | [ENCAP_ENTROPY] = { | ||
300 | .state = ENCAP_ENTROPY, | ||
301 | .type = LINE_HEX, | ||
302 | .name = "entropy", | ||
303 | .label = "entropy", | ||
304 | }, | ||
305 | [ENCAP_PUBLIC_KEY] = { | ||
306 | .state = ENCAP_PUBLIC_KEY, | ||
307 | .type = LINE_HEX, | ||
308 | .name = "public key", | ||
309 | .label = "public_key", | ||
310 | }, | ||
311 | [ENCAP_RESULT] = { | ||
312 | .state = ENCAP_RESULT, | ||
313 | .type = LINE_STRING_MATCH, | ||
314 | .name = "result", | ||
315 | .label = "result", | ||
316 | .match = "fail", | ||
317 | }, | ||
318 | [ENCAP_CIPHERTEXT] = { | ||
319 | .state = ENCAP_CIPHERTEXT, | ||
320 | .type = LINE_HEX, | ||
321 | .name = "ciphertext", | ||
322 | .label = "ciphertext", | ||
323 | }, | ||
324 | [ENCAP_SHARED_SECRET] = { | ||
325 | .state = ENCAP_SHARED_SECRET, | ||
326 | .type = LINE_HEX, | ||
327 | .name = "shared secret", | ||
328 | .label = "shared_secret", | ||
329 | }, | ||
330 | }; | ||
331 | |||
332 | static int | ||
333 | encap_init(void *ctx, void *parse_ctx) | ||
334 | { | ||
335 | struct encap_ctx *encap = ctx; | ||
336 | |||
337 | encap->parse_ctx = parse_ctx; | ||
338 | |||
339 | return 1; | ||
340 | } | ||
341 | |||
342 | static void | ||
343 | encap_finish(void *ctx) | ||
344 | { | ||
345 | (void)ctx; | ||
346 | } | ||
347 | |||
348 | static int | ||
349 | MlkemEncapFileTest(struct encap_ctx *encap) | ||
350 | { | ||
351 | struct parse *p = encap->parse_ctx; | ||
352 | uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES]; | ||
353 | CBS entropy, public_key, ciphertext, shared_secret; | ||
354 | int should_fail; | ||
355 | int failed = 1; | ||
356 | |||
357 | parse_get_cbs(p, ENCAP_ENTROPY, &entropy); | ||
358 | parse_get_cbs(p, ENCAP_PUBLIC_KEY, &public_key); | ||
359 | parse_get_cbs(p, ENCAP_CIPHERTEXT, &ciphertext); | ||
360 | parse_get_cbs(p, ENCAP_SHARED_SECRET, &shared_secret); | ||
361 | parse_get_int(p, ENCAP_RESULT, &should_fail); | ||
362 | |||
363 | if (!encap->parse_public_key(encap->public_key, &public_key)) { | ||
364 | if ((failed = !should_fail)) | ||
365 | parse_info(p, "parse public key"); | ||
366 | goto err; | ||
367 | } | ||
368 | encap->encap_external_entropy(encap->ciphertext, shared_secret_buf, | ||
369 | encap->public_key, CBS_data(&entropy)); | ||
370 | |||
371 | failed = !parse_data_equal(p, "shared_secret", &shared_secret, | ||
372 | shared_secret_buf, sizeof(shared_secret_buf)); | ||
373 | failed |= !parse_data_equal(p, "ciphertext", &ciphertext, | ||
374 | encap->ciphertext, encap->ciphertext_len); | ||
375 | |||
376 | if (should_fail != failed) { | ||
377 | parse_info(p, "FAIL: should_fail %d, failed %d", | ||
378 | should_fail, failed); | ||
379 | failed = 1; | ||
380 | } | ||
381 | |||
382 | err: | ||
383 | return failed; | ||
384 | } | ||
385 | |||
386 | static int | ||
387 | encap_run_test_case(void *ctx) | ||
388 | { | ||
389 | return MlkemEncapFileTest(ctx); | ||
390 | } | ||
391 | |||
392 | static const struct test_parse encap_parse = { | ||
393 | .states = encap_state_machine, | ||
394 | .num_states = N_ENCAP_STATES, | ||
395 | |||
396 | .init = encap_init, | ||
397 | .finish = encap_finish, | ||
398 | |||
399 | .run_test_case = encap_run_test_case, | ||
400 | }; | ||
401 | |||
402 | static int | ||
403 | mlkem_encap_tests(const char *fn, size_t size) | ||
404 | { | ||
405 | struct MLKEM768_public_key public_key768; | ||
406 | uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; | ||
407 | struct encap_ctx encap768 = { | ||
408 | .public_key = &public_key768, | ||
409 | .ciphertext = ciphertext768, | ||
410 | .ciphertext_len = sizeof(ciphertext768), | ||
411 | |||
412 | .parse_public_key = mlkem768_parse_public_key, | ||
413 | .encap_external_entropy = mlkem768_encap_external_entropy, | ||
414 | }; | ||
415 | struct MLKEM1024_public_key public_key1024; | ||
416 | uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; | ||
417 | struct encap_ctx encap1024 = { | ||
418 | .public_key = &public_key1024, | ||
419 | .ciphertext = ciphertext1024, | ||
420 | .ciphertext_len = sizeof(ciphertext1024), | ||
421 | |||
422 | .parse_public_key = mlkem1024_parse_public_key, | ||
423 | .encap_external_entropy = mlkem1024_encap_external_entropy, | ||
424 | }; | ||
425 | |||
426 | if (size == 768) | ||
427 | return parse_test_file(fn, &encap_parse, &encap768); | ||
428 | if (size == 1024) | ||
429 | return parse_test_file(fn, &encap_parse, &encap1024); | ||
430 | |||
431 | errx(1, "unknown encap test: size %zu", size); | ||
432 | } | ||
433 | |||
434 | struct keygen_ctx { | ||
435 | struct parse *parse_ctx; | ||
436 | |||
437 | void *private_key; | ||
438 | void *encoded_public_key; | ||
439 | size_t encoded_public_key_len; | ||
440 | size_t private_key_len; | ||
441 | size_t public_key_len; | ||
442 | |||
443 | mlkem_generate_key_external_entropy_fn generate_key_external_entropy; | ||
444 | mlkem_encode_private_key_fn encode_private_key; | ||
445 | }; | ||
446 | |||
447 | enum keygen_states { | ||
448 | KEYGEN_SEED, | ||
449 | KEYGEN_PUBLIC_KEY, | ||
450 | KEYGEN_PRIVATE_KEY, | ||
451 | N_KEYGEN_STATES, | ||
452 | }; | ||
453 | |||
454 | static const struct line_spec keygen_state_machine[] = { | ||
455 | [KEYGEN_SEED] = { | ||
456 | .state = KEYGEN_SEED, | ||
457 | .type = LINE_HEX, | ||
458 | .name = "seed", | ||
459 | .label = "seed", | ||
460 | }, | ||
461 | [KEYGEN_PUBLIC_KEY] = { | ||
462 | .state = KEYGEN_PUBLIC_KEY, | ||
463 | .type = LINE_HEX, | ||
464 | .name = "public key", | ||
465 | .label = "public_key", | ||
466 | }, | ||
467 | [KEYGEN_PRIVATE_KEY] = { | ||
468 | .state = KEYGEN_PRIVATE_KEY, | ||
469 | .type = LINE_HEX, | ||
470 | .name = "private key", | ||
471 | .label = "private_key", | ||
472 | }, | ||
473 | }; | ||
474 | |||
475 | static int | ||
476 | keygen_init(void *ctx, void *parse_ctx) | ||
477 | { | ||
478 | struct keygen_ctx *keygen = ctx; | ||
479 | |||
480 | keygen->parse_ctx = parse_ctx; | ||
481 | |||
482 | return 1; | ||
483 | } | ||
484 | |||
485 | static void | ||
486 | keygen_finish(void *ctx) | ||
487 | { | ||
488 | (void)ctx; | ||
489 | } | ||
490 | |||
491 | static int | ||
492 | MlkemKeygenFileTest(struct keygen_ctx *keygen) | ||
493 | { | ||
494 | struct parse *p = keygen->parse_ctx; | ||
495 | CBS seed, public_key, private_key; | ||
496 | uint8_t *encoded_private_key = NULL; | ||
497 | size_t encoded_private_key_len = 0; | ||
498 | int failed = 1; | ||
499 | |||
500 | parse_get_cbs(p, KEYGEN_SEED, &seed); | ||
501 | parse_get_cbs(p, KEYGEN_PUBLIC_KEY, &public_key); | ||
502 | parse_get_cbs(p, KEYGEN_PRIVATE_KEY, &private_key); | ||
503 | |||
504 | if (!parse_length_equal(p, "seed", MLKEM_SEED_BYTES, CBS_len(&seed))) | ||
505 | goto err; | ||
506 | if (!parse_length_equal(p, "public key", | ||
507 | keygen->public_key_len, CBS_len(&public_key))) | ||
508 | goto err; | ||
509 | if (!parse_length_equal(p, "private key", | ||
510 | keygen->private_key_len, CBS_len(&private_key))) | ||
511 | goto err; | ||
512 | |||
513 | keygen->generate_key_external_entropy(keygen->encoded_public_key, | ||
514 | keygen->private_key, CBS_data(&seed)); | ||
515 | if (!keygen->encode_private_key(keygen->private_key, | ||
516 | &encoded_private_key, &encoded_private_key_len)) { | ||
517 | parse_info(p, "encode private key"); | ||
518 | goto err; | ||
519 | } | ||
520 | |||
521 | failed = !parse_data_equal(p, "private key", &private_key, | ||
522 | encoded_private_key, encoded_private_key_len); | ||
523 | failed |= !parse_data_equal(p, "public key", &public_key, | ||
524 | keygen->encoded_public_key, keygen->encoded_public_key_len); | ||
525 | |||
526 | err: | ||
527 | freezero(encoded_private_key, encoded_private_key_len); | ||
528 | |||
529 | return failed; | ||
530 | } | ||
531 | |||
532 | static int | ||
533 | keygen_run_test_case(void *ctx) | ||
534 | { | ||
535 | return MlkemKeygenFileTest(ctx); | ||
536 | } | ||
537 | |||
538 | static const struct test_parse keygen_parse = { | ||
539 | .states = keygen_state_machine, | ||
540 | .num_states = N_KEYGEN_STATES, | ||
541 | |||
542 | .init = keygen_init, | ||
543 | .finish = keygen_finish, | ||
544 | |||
545 | .run_test_case = keygen_run_test_case, | ||
546 | }; | ||
547 | |||
548 | enum nist_keygen_states { | ||
549 | NIST_KEYGEN_Z, | ||
550 | NIST_KEYGEN_D, | ||
551 | NIST_KEYGEN_EK, | ||
552 | NIST_KEYGEN_DK, | ||
553 | N_NIST_KEYGEN_STATES, | ||
554 | }; | ||
555 | |||
556 | static const struct line_spec nist_keygen_state_machine[] = { | ||
557 | [NIST_KEYGEN_Z] = { | ||
558 | .state = NIST_KEYGEN_Z, | ||
559 | .type = LINE_HEX, | ||
560 | .name = "seed (z)", | ||
561 | .label = "z", | ||
562 | }, | ||
563 | [NIST_KEYGEN_D] = { | ||
564 | .state = NIST_KEYGEN_D, | ||
565 | .type = LINE_HEX, | ||
566 | .name = "seed (d)", | ||
567 | .label = "d", | ||
568 | }, | ||
569 | [NIST_KEYGEN_EK] = { | ||
570 | .state = NIST_KEYGEN_EK, | ||
571 | .type = LINE_HEX, | ||
572 | .name = "public key (ek)", | ||
573 | .label = "ek", | ||
574 | }, | ||
575 | [NIST_KEYGEN_DK] = { | ||
576 | .state = NIST_KEYGEN_DK, | ||
577 | .type = LINE_HEX, | ||
578 | .name = "private key (dk)", | ||
579 | .label = "dk", | ||
580 | }, | ||
581 | }; | ||
582 | |||
583 | static int | ||
584 | MlkemNistKeygenFileTest(struct keygen_ctx *keygen) | ||
585 | { | ||
586 | struct parse *p = keygen->parse_ctx; | ||
587 | CBB seed_cbb; | ||
588 | CBS z, d, ek, dk; | ||
589 | uint8_t seed[MLKEM_SEED_BYTES]; | ||
590 | size_t seed_len; | ||
591 | uint8_t *encoded_private_key = NULL; | ||
592 | size_t encoded_private_key_len = 0; | ||
593 | int failed = 1; | ||
594 | |||
595 | parse_get_cbs(p, NIST_KEYGEN_Z, &z); | ||
596 | parse_get_cbs(p, NIST_KEYGEN_D, &d); | ||
597 | parse_get_cbs(p, NIST_KEYGEN_EK, &ek); | ||
598 | parse_get_cbs(p, NIST_KEYGEN_DK, &dk); | ||
599 | |||
600 | if (!CBB_init_fixed(&seed_cbb, seed, sizeof(seed))) | ||
601 | parse_errx(p, "CBB_init_fixed"); | ||
602 | if (!CBB_add_bytes(&seed_cbb, CBS_data(&d), CBS_len(&d))) | ||
603 | parse_errx(p, "CBB_add_bytes"); | ||
604 | if (!CBB_add_bytes(&seed_cbb, CBS_data(&z), CBS_len(&z))) | ||
605 | parse_errx(p, "CBB_add_bytes"); | ||
606 | if (!CBB_finish(&seed_cbb, NULL, &seed_len)) | ||
607 | parse_errx(p, "CBB_finish"); | ||
608 | |||
609 | if (!parse_length_equal(p, "bogus z or d", MLKEM_SEED_BYTES, seed_len)) | ||
610 | goto err; | ||
611 | |||
612 | keygen->generate_key_external_entropy(keygen->encoded_public_key, | ||
613 | keygen->private_key, seed); | ||
614 | if (!keygen->encode_private_key(keygen->private_key, | ||
615 | &encoded_private_key, &encoded_private_key_len)) { | ||
616 | parse_info(p, "encode private key"); | ||
617 | goto err; | ||
618 | } | ||
619 | |||
620 | failed = !parse_data_equal(p, "public key", &ek, | ||
621 | keygen->encoded_public_key, keygen->encoded_public_key_len); | ||
622 | failed |= !parse_data_equal(p, "private key", &dk, | ||
623 | encoded_private_key, encoded_private_key_len); | ||
624 | |||
625 | err: | ||
626 | freezero(encoded_private_key, encoded_private_key_len); | ||
627 | |||
628 | return failed; | ||
629 | } | ||
630 | |||
631 | static int | ||
632 | nist_keygen_run_test_case(void *ctx) | ||
633 | { | ||
634 | return MlkemNistKeygenFileTest(ctx); | ||
635 | } | ||
636 | |||
637 | static const struct test_parse nist_keygen_parse = { | ||
638 | .states = nist_keygen_state_machine, | ||
639 | .num_states = N_NIST_KEYGEN_STATES, | ||
640 | |||
641 | .init = keygen_init, | ||
642 | .finish = keygen_finish, | ||
643 | |||
644 | .run_test_case = nist_keygen_run_test_case, | ||
645 | }; | ||
646 | |||
647 | static int | ||
648 | mlkem_keygen_tests(const char *fn, size_t size, enum test_type test_type) | ||
649 | { | ||
650 | struct MLKEM768_private_key private_key768; | ||
651 | uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; | ||
652 | struct keygen_ctx keygen768 = { | ||
653 | .private_key = &private_key768, | ||
654 | .encoded_public_key = encoded_public_key768, | ||
655 | .encoded_public_key_len = sizeof(encoded_public_key768), | ||
656 | .private_key_len = MLKEM768_PRIVATE_KEY_BYTES, | ||
657 | .public_key_len = MLKEM768_PUBLIC_KEY_BYTES, | ||
658 | .generate_key_external_entropy = | ||
659 | mlkem768_generate_key_external_entropy, | ||
660 | .encode_private_key = | ||
661 | mlkem768_encode_private_key, | ||
662 | }; | ||
663 | struct MLKEM1024_private_key private_key1024; | ||
664 | uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; | ||
665 | struct keygen_ctx keygen1024 = { | ||
666 | .private_key = &private_key1024, | ||
667 | .encoded_public_key = encoded_public_key1024, | ||
668 | .encoded_public_key_len = sizeof(encoded_public_key1024), | ||
669 | .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES, | ||
670 | .public_key_len = MLKEM1024_PUBLIC_KEY_BYTES, | ||
671 | |||
672 | .generate_key_external_entropy = | ||
673 | mlkem1024_generate_key_external_entropy, | ||
674 | .encode_private_key = | ||
675 | mlkem1024_encode_private_key, | ||
676 | }; | ||
677 | |||
678 | if (size == 768 && test_type == TEST_TYPE_NORMAL) | ||
679 | return parse_test_file(fn, &keygen_parse, &keygen768); | ||
680 | if (size == 768 && test_type == TEST_TYPE_NIST) | ||
681 | return parse_test_file(fn, &nist_keygen_parse, &keygen768); | ||
682 | if (size == 1024 && test_type == TEST_TYPE_NORMAL) | ||
683 | return parse_test_file(fn, &keygen_parse, &keygen1024); | ||
684 | if (size == 1024 && test_type == TEST_TYPE_NIST) | ||
685 | return parse_test_file(fn, &nist_keygen_parse, &keygen1024); | ||
686 | |||
687 | errx(1, "unknown keygen test: size %zu, type %d", size, test_type); | ||
688 | } | ||
689 | |||
690 | static int | ||
691 | run_mlkem_test(const char *test, const char *fn) | ||
692 | { | ||
693 | if (strcmp(test, "mlkem768_decap_tests") == 0) | ||
694 | return mlkem_decap_tests(fn, 768, TEST_TYPE_NORMAL); | ||
695 | if (strcmp(test, "mlkem768_nist_decap_tests") == 0) | ||
696 | return mlkem_decap_tests(fn, 768, TEST_TYPE_NIST); | ||
697 | if (strcmp(test, "mlkem1024_decap_tests") == 0) | ||
698 | return mlkem_decap_tests(fn, 1024, TEST_TYPE_NORMAL); | ||
699 | if (strcmp(test, "mlkem1024_nist_decap_tests") == 0) | ||
700 | return mlkem_decap_tests(fn, 1024, TEST_TYPE_NIST); | ||
701 | |||
702 | if (strcmp(test, "mlkem768_encap_tests") == 0) | ||
703 | return mlkem_encap_tests(fn, 768); | ||
704 | if (strcmp(test, "mlkem1024_encap_tests") == 0) | ||
705 | return mlkem_encap_tests(fn, 1024); | ||
706 | |||
707 | if (strcmp(test, "mlkem768_keygen_tests") == 0) | ||
708 | return mlkem_keygen_tests(fn, 768, TEST_TYPE_NORMAL); | ||
709 | if (strcmp(test, "mlkem768_nist_keygen_tests") == 0) | ||
710 | return mlkem_keygen_tests(fn, 768, TEST_TYPE_NIST); | ||
711 | if (strcmp(test, "mlkem1024_keygen_tests") == 0) | ||
712 | return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NORMAL); | ||
713 | if (strcmp(test, "mlkem1024_nist_keygen_tests") == 0) | ||
714 | return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NIST); | ||
715 | |||
716 | errx(1, "unknown test %s (test file %s)", test, fn); | ||
717 | } | ||
718 | |||
719 | int | ||
720 | main(int argc, const char *argv[]) | ||
721 | { | ||
722 | if (argc != 3) { | ||
723 | fprintf(stderr, "usage: mlkem_test test testfile.txt\n"); | ||
724 | exit(1); | ||
725 | } | ||
726 | |||
727 | return run_mlkem_test(argv[1], argv[2]); | ||
728 | } | ||