diff options
author | tb <> | 2024-12-26 00:04:24 +0000 |
---|---|---|
committer | tb <> | 2024-12-26 00:04:24 +0000 |
commit | 31d1b04da9af806cdb66a2b49ed6490e67479eef (patch) | |
tree | f187d226245651988501e2fb8891081ff9eea9f2 /src/regress/lib/libcrypto/mlkem/mlkem_tests.c | |
parent | fe8b80dbfd7a71d866da84cfdab5d2ce23feac28 (diff) | |
download | openbsd-31d1b04da9af806cdb66a2b49ed6490e67479eef.tar.gz openbsd-31d1b04da9af806cdb66a2b49ed6490e67479eef.tar.bz2 openbsd-31d1b04da9af806cdb66a2b49ed6490e67479eef.zip |
Overhaul ML-KEM regress once more
Implement a file parser that drives a state machine to extract the test data
from the .txt files and manages the parsed data. Comments and empty lines are
ignored. The code currently assumes that instruction lines are at the start
of the file (which isn't generally true) and only supports two line types for
now. This is good enough for all the ML-KEM tests but should be easy enough
to extend.
Once all data for a test case is parsed in the expected order, a test handler
is called which can retrieve the test data via a simple API and throw warnings
and errors with information on the test case line number, etc.
Merge the tests into three programs: one parsing the .txt files and running
the corresponding test cases, a unit test and the iteration tests. Deduplicate
the actual test code and let the caller pass in an object containing the
API functions, private keys and arrays that need to be different between the
768 version and the 1024 version. This way we don't have two sets of half a
dozen .c files differing only in 3 or 4 occurrences of 768 and 1024.
All this will also make it a lot easier to hook these tests into portable.
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 | } | ||