summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/regress/lib/libcrypto/mlkem/Makefile43
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_decap_tests.c195
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_encap_tests.c210
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_iteration_test.c149
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_keygen_tests.c190
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_nist_decap_tests.c193
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem1024_nist_keygen_tests.c197
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_decap_tests.c195
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_encap_tests.c210
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c149
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_keygen_tests.c190
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_nist_decap_tests.c193
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem768_nist_keygen_tests.c197
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c230
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests.c728
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c214
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h80
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_unittest.c283
-rw-r--r--src/regress/lib/libcrypto/mlkem/parse_test_file.c752
-rw-r--r--src/regress/lib/libcrypto/mlkem/parse_test_file.h83
20 files changed, 2110 insertions, 2571 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/Makefile b/src/regress/lib/libcrypto/mlkem/Makefile
index cb24605bd7..9c0c24f432 100644
--- a/src/regress/lib/libcrypto/mlkem/Makefile
+++ b/src/regress/lib/libcrypto/mlkem/Makefile
@@ -1,28 +1,31 @@
1# $OpenBSD: Makefile,v 1.7 2024/12/20 01:53:46 tb Exp $ 1# $OpenBSD: Makefile,v 1.8 2024/12/26 00:04:24 tb Exp $
2 2
3PROGS += mlkem768_decap_tests 3REGRESS_SLOW_TARGETS += run-regress-mlkem_iteration_tests
4PROGS += mlkem768_encap_tests 4
5PROGS += mlkem768_iteration_test 5PROGS += mlkem_tests
6PROGS += mlkem768_keygen_tests
7PROGS += mlkem768_nist_decap_tests
8PROGS += mlkem768_nist_keygen_tests
9PROGS += mlkem1024_decap_tests
10PROGS += mlkem1024_encap_tests
11PROGS += mlkem1024_iteration_test
12PROGS += mlkem1024_keygen_tests
13PROGS += mlkem1024_nist_decap_tests
14PROGS += mlkem1024_nist_keygen_tests
15PROGS += mlkem_unittest 6PROGS += mlkem_unittest
7PROGS += mlkem_iteration_tests
16 8
17# Link test programs with mlkem_tests_util.c and use custom target 9FILE_TEST += mlkem768_decap_tests
18.for p in ${PROGS} 10FILE_TEST += mlkem768_encap_tests
19SRCS_$p += $p.c mlkem_tests_util.c 11FILE_TEST += mlkem768_keygen_tests
12FILE_TEST += mlkem768_nist_decap_tests
13FILE_TEST += mlkem768_nist_keygen_tests
14FILE_TEST += mlkem1024_decap_tests
15FILE_TEST += mlkem1024_encap_tests
16FILE_TEST += mlkem1024_keygen_tests
17FILE_TEST += mlkem1024_nist_decap_tests
18FILE_TEST += mlkem1024_nist_keygen_tests
20 19
21REGRESS_TARGETS += run-$p 20run-regress-mlkem_tests: mlkem_tests
22run-$p: $p 21.for f in ${FILE_TEST}
23 ./$p ${.CURDIR}/$p.txt 22 ./mlkem_tests $f ${.CURDIR}/$f.txt
24.endfor 23.endfor
25 24
25SRCS_mlkem_tests = mlkem_tests.c mlkem_tests_util.c parse_test_file.c
26SRCS_mlkem_iteration_tests = mlkem_iteration_tests.c mlkem_tests_util.c
27SRCS_mlkem_unittest = mlkem_unittest.c mlkem_tests_util.c
28
26LDADD = ${CRYPTO_INT} 29LDADD = ${CRYPTO_INT}
27DPADD = ${LIBCRYPTO} 30DPADD = ${LIBCRYPTO}
28 31
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_decap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_decap_tests.c
deleted file mode 100644
index 7d76a625b0..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_decap_tests.c
+++ /dev/null
@@ -1,195 +0,0 @@
1/* $OpenBSD: mlkem1024_decap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_tests_util.h"
30
31static int
32MlkemDecapFileTest(CBB *ciphertext_cbb, CBB *shared_secret_cbb,
33 CBB *private_key_cbb, int should_fail, size_t line)
34{
35 struct MLKEM1024_private_key priv;
36 uint8_t *ciphertext = NULL, *shared_secret = NULL, *private_key = NULL;
37 size_t ciphertext_len = 0, shared_secret_len = 0, private_key_len = 0;
38 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES];
39 CBS private_key_cbs;
40 int failed = 1;
41
42 if (!CBB_finish(ciphertext_cbb, &ciphertext, &ciphertext_len))
43 goto err;
44 if (!CBB_finish(shared_secret_cbb, &shared_secret, &shared_secret_len))
45 goto err;
46 if (!CBB_finish(private_key_cbb, &private_key, &private_key_len))
47 goto err;
48
49 CBS_init(&private_key_cbs, private_key, private_key_len);
50
51 if (!MLKEM1024_parse_private_key(&priv, &private_key_cbs)) {
52 if ((failed = !should_fail))
53 warnx("#%zu: parse_private_key", line);
54 goto err;
55 }
56 if (!MLKEM1024_decap(shared_secret_buf, ciphertext, ciphertext_len,
57 &priv)) {
58 if ((failed = !should_fail))
59 warnx("#%zu: decap", line);
60 goto err;
61 }
62
63 failed = compare_data(shared_secret, shared_secret_buf,
64 MLKEM_SHARED_SECRET_BYTES, line, "shared_secret");
65
66 if (should_fail != failed) {
67 warnx("FAIL: #%zu: should_fail %d, failed %d",
68 line, should_fail, failed);
69 failed = 1;
70 }
71
72 err:
73 CBB_cleanup(ciphertext_cbb);
74 CBB_cleanup(shared_secret_cbb);
75 CBB_cleanup(private_key_cbb);
76 freezero(ciphertext, ciphertext_len);
77 freezero(shared_secret, shared_secret_len);
78 freezero(private_key, private_key_len);
79
80 return failed;
81}
82
83#define S_START 0
84#define S_COMMENT 1
85#define S_PRIVATE_KEY 2
86#define S_CIPHERTEXT 3
87#define S_RESULT 4
88#define S_SHARED_SECRET 5
89
90#define S2S(x) case x: return #x
91
92static const char *
93state2str(int state)
94{
95 switch (state) {
96 S2S(S_START);
97 S2S(S_COMMENT);
98 S2S(S_PRIVATE_KEY);
99 S2S(S_CIPHERTEXT);
100 S2S(S_RESULT);
101 S2S(S_SHARED_SECRET);
102 default:
103 errx(1, "unknown state %d", state);
104 }
105}
106
107int
108main(int argc, char **argv)
109{
110 CBB ciphertext = { 0 }, shared_secret = { 0 }, private_key = { 0 };
111 int should_fail = 0;
112 const char *test;
113 size_t line = 0;
114 char *buf = NULL;
115 size_t buflen = 0;
116 ssize_t len;
117 FILE *fp;
118 int state;
119 int failed = 0;
120
121 if (argc < 2)
122 errx(1, "%s: missing test file", argv[0]);
123
124 test = argv[1];
125
126 if ((fp = fopen(test, "r")) == NULL)
127 err(1, "can't open test file");
128
129 state = S_COMMENT;
130 line = 0;
131
132 while ((len = getline(&buf, &buflen, fp)) != -1) {
133 const char *msg = state2str(state);
134 CBS cbs;
135 uint8_t u8;
136
137 line++;
138 CBS_init(&cbs, buf, len);
139
140 if (!CBS_get_last_u8(&cbs, &u8))
141 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
142 assert(u8 == '\n');
143
144 switch (state) {
145 case S_START:
146 state = S_COMMENT;
147 break;
148 case S_COMMENT:
149 if (!CBS_get_u8(&cbs, &u8))
150 errx(1, "#%zu %s: CBB_get_u8", line, msg);
151 assert(u8 == '#');
152 if (!CBS_skip(&cbs, CBS_len(&cbs)))
153 errx(1, "#%zu %s: CBB_skip", line, msg);
154 state = S_PRIVATE_KEY;
155 break;
156 case S_PRIVATE_KEY:
157 if (!get_string_cbs(&cbs, "private_key: ", line, msg))
158 errx(1, "#%zu %s: get_string_cbs", line, msg);
159 hex_decode_cbs(&cbs, &private_key, line, msg);
160 state = S_CIPHERTEXT;
161 break;
162 case S_CIPHERTEXT:
163 if (!get_string_cbs(&cbs, "ciphertext: ", line, msg))
164 errx(1, "#%zu %s: get_string_cbs", line, msg);
165 hex_decode_cbs(&cbs, &ciphertext, line, msg);
166 state = S_RESULT;
167 break;
168 case S_RESULT:
169 if (!get_string_cbs(&cbs, "result: ", line, msg))
170 errx(1, "#%zu %s: get_string_cbs", line, msg);
171 should_fail = get_string_cbs(&cbs, "fail", line, msg);
172 state = S_SHARED_SECRET;
173 break;
174 case S_SHARED_SECRET:
175 if (!get_string_cbs(&cbs, "shared_secret: ", line, msg))
176 errx(1, "#%zu %s: get_string_cbs", line, msg);
177 hex_decode_cbs(&cbs, &shared_secret, line, msg);
178
179 failed |= MlkemDecapFileTest(&ciphertext, &shared_secret,
180 &private_key, should_fail, line);
181
182 state = S_START;
183 break;
184 }
185 if (CBS_len(&cbs) > 0)
186 errx(1, "#%zu %s: CBS_len", line, msg);
187 }
188 free(buf);
189
190 if (ferror(fp))
191 err(1, NULL);
192 fclose(fp);
193
194 return failed;
195}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_encap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_encap_tests.c
deleted file mode 100644
index 6459566ff0..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_encap_tests.c
+++ /dev/null
@@ -1,210 +0,0 @@
1/* $OpenBSD: mlkem1024_encap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemEncapFileTest(CBB *entropy_cbb, CBB *pubkey_cbb, CBB *ciphertext_cbb,
34 CBB *shared_secret_cbb, int should_fail, size_t line)
35{
36 struct MLKEM1024_public_key pub;
37 uint8_t *entropy = NULL, *public_key = NULL, *ciphertext = NULL;
38 uint8_t *shared_secret = NULL;
39 size_t entropy_len = 0, public_key_len = 0, ciphertext_len = 0;
40 size_t shared_secret_len = 0;
41 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES];
42 uint8_t ciphertext_buf[MLKEM1024_CIPHERTEXT_BYTES];
43 CBS public_key_cbs;
44 int failed = 1;
45
46 if (!CBB_finish(entropy_cbb, &entropy, &entropy_len))
47 goto err;
48 if (!CBB_finish(pubkey_cbb, &public_key, &public_key_len))
49 goto err;
50 if (!CBB_finish(ciphertext_cbb, &ciphertext, &ciphertext_len))
51 goto err;
52 if (!CBB_finish(shared_secret_cbb, &shared_secret, &shared_secret_len))
53 goto err;
54
55 CBS_init(&public_key_cbs, public_key, public_key_len);
56
57 if (!MLKEM1024_parse_public_key(&pub, &public_key_cbs)) {
58 if ((failed = !should_fail))
59 warnx("#%zu: parse_public_key", line);
60 goto err;
61 }
62 MLKEM1024_encap_external_entropy(ciphertext_buf, shared_secret_buf,
63 &pub, entropy);
64
65 failed = compare_data(shared_secret, shared_secret_buf,
66 MLKEM_SHARED_SECRET_BYTES, line, "shared_secret");
67 failed |= compare_data(ciphertext, ciphertext_buf,
68 MLKEM1024_CIPHERTEXT_BYTES, line, "ciphertext");
69
70 if (should_fail != failed) {
71 warnx("FAIL: #%zu: should_fail %d, failed %d",
72 line, should_fail, failed);
73 failed = 1;
74 }
75
76 err:
77 CBB_cleanup(entropy_cbb);
78 CBB_cleanup(pubkey_cbb);
79 CBB_cleanup(ciphertext_cbb);
80 CBB_cleanup(shared_secret_cbb);
81 freezero(entropy, entropy_len);
82 freezero(public_key, public_key_len);
83 freezero(ciphertext, ciphertext_len);
84 freezero(shared_secret, shared_secret_len);
85
86 return failed;
87}
88
89#define S_START 0
90#define S_COMMENT 1
91#define S_ENTROPY 2
92#define S_PUBLIC_KEY 3
93#define S_RESULT 4
94#define S_CIPHERTEXT 5
95#define S_SHARED_SECRET 6
96
97#define S2S(x) case x: return #x
98
99static const char *
100state2str(int state)
101{
102 switch (state) {
103 S2S(S_START);
104 S2S(S_COMMENT);
105 S2S(S_ENTROPY);
106 S2S(S_PUBLIC_KEY);
107 S2S(S_RESULT);
108 S2S(S_CIPHERTEXT);
109 S2S(S_SHARED_SECRET);
110 default:
111 errx(1, "unknown state %d", state);
112 }
113}
114
115int
116main(int argc, char **argv)
117{
118 CBB entropy = { 0 }, public_key = { 0 }, ciphertext = { 0 }, shared_secret = { 0 };
119 int should_fail = 0;
120 const char *test;
121 size_t line;
122 char *buf = NULL;
123 size_t buflen = 0;
124 ssize_t len;
125 FILE *fp;
126 int state;
127 int failed = 0;
128
129 if (argc < 2)
130 errx(1, "%s: missing test file", argv[0]);
131
132 test = argv[1];
133 line = 0;
134
135 if ((fp = fopen(test, "r")) == NULL)
136 err(1, "can't open test file");
137
138 state = S_COMMENT;
139 line = 0;
140
141 while ((len = getline(&buf, &buflen, fp)) != -1) {
142 const char *msg = state2str(state);
143 CBS cbs;
144 uint8_t u8;
145
146 line++;
147 CBS_init(&cbs, buf, len);
148
149 if (!CBS_get_last_u8(&cbs, &u8))
150 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
151 assert(u8 == '\n');
152
153 switch (state) {
154 case S_START:
155 state = S_COMMENT;
156 break;
157 case S_COMMENT:
158 if (!CBS_get_u8(&cbs, &u8))
159 errx(1, "#%zu %s: CBB_get_u8", line, msg);
160 assert(u8 == '#');
161 if (!CBS_skip(&cbs, CBS_len(&cbs)))
162 errx(1, "#%zu %s: CBB_skip", line, msg);
163 state = S_ENTROPY;
164 break;
165 case S_ENTROPY:
166 if (!get_string_cbs(&cbs, "entropy: ", line, msg))
167 errx(1, "#%zu %s: get_string_cbs", line, msg);
168 hex_decode_cbs(&cbs, &entropy, line, msg);
169 state = S_PUBLIC_KEY;
170 break;
171 case S_PUBLIC_KEY:
172 if (!get_string_cbs(&cbs, "public_key = ", line, msg))
173 errx(1, "#%zu %s: get_string_cbs", line, msg);
174 hex_decode_cbs(&cbs, &public_key, line, msg);
175 state = S_RESULT;
176 break;
177 case S_RESULT:
178 if (!get_string_cbs(&cbs, "result: ", line, msg))
179 errx(1, "#%zu %s: get_string_cbs", line, msg);
180 should_fail = get_string_cbs(&cbs, "fail", line, msg);
181 state = S_CIPHERTEXT;
182 break;
183 case S_CIPHERTEXT:
184 if (!get_string_cbs(&cbs, "ciphertext: ", line, msg))
185 errx(1, "#%zu %s: get_string_cbs", line, msg);
186 hex_decode_cbs(&cbs, &ciphertext, line, msg);
187 state = S_SHARED_SECRET;
188 break;
189 case S_SHARED_SECRET:
190 if (!get_string_cbs(&cbs, "shared_secret: ", line, msg))
191 errx(1, "#%zu %s: get_string_cbs", line, msg);
192 hex_decode_cbs(&cbs, &shared_secret, line, msg);
193
194 failed |= MlkemEncapFileTest(&entropy, &public_key,
195 &ciphertext, &shared_secret, should_fail, line);
196
197 state = S_START;
198 break;
199 }
200 if (CBS_len(&cbs) > 0)
201 errx(1, "#%zu %s: CBS_len", line, msg);
202 }
203 free(buf);
204
205 if (ferror(fp))
206 err(1, NULL);
207 fclose(fp);
208
209 return failed;
210}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_iteration_test.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_iteration_test.c
deleted file mode 100644
index e6a4d4f906..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_iteration_test.c
+++ /dev/null
@@ -1,149 +0,0 @@
1/* $OpenBSD: mlkem1024_iteration_test.c,v 1.3 2024/12/20 00:07:12 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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
25#include "mlkem.h"
26
27#include "mlkem_internal.h"
28#include "mlkem_tests_util.h"
29#include "sha3_internal.h"
30
31/*
32 * The structure of this test is taken from
33 * https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
34 * but the final value has been updated to reflect the change from Kyber to
35 * ML-KEM.
36 *
37 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
38 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
39 */
40
41static int
42MlkemIterativeTest(void)
43{
44 /* https://github.com/C2SP/CCTV/tree/main/ML-KEM */
45 /*
46 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
47 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
48 */
49 const uint8_t kExpectedSeedStart[16] = {
50 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
51 0x50, 0x76, 0x05, 0x85, 0x3e
52 };
53
54 /*
55 * Filippo says:
56 * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543
57 * but Boring believes this:
58 */
59 const uint8_t kExpectedAdam[32] = {
60 0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
61 0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
62 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
63 };
64 uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
65 uint8_t invalid_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
66 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
67 uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
68 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
69 uint8_t seed[MLKEM_SEED_BYTES] = {0};
70 struct MLKEM1024_private_key priv;
71 struct MLKEM1024_public_key pub;
72 sha3_ctx drng, results;
73 uint8_t out[32];
74 int i;
75
76 shake128_init(&drng);
77 shake128_init(&results);
78
79 shake_xof(&drng);
80 for (i = 0; i < 10000; i++) {
81 uint8_t *encoded_private_key = NULL;
82 size_t encoded_private_key_len;
83
84 /*
85 * This should draw both d and z from DRNG concatenating in
86 * seed.
87 */
88 shake_out(&drng, seed, sizeof(seed));
89 if (i == 0) {
90 if (compare_data(seed, kExpectedSeedStart,
91 sizeof(kExpectedSeedStart), 0, "seed start") != 0)
92 errx(1, "compare_data");
93 }
94
95 /* generate ek as encoded_public_key */
96 MLKEM1024_generate_key_external_entropy(encoded_public_key,
97 &priv, seed);
98 MLKEM1024_public_from_private(&pub, &priv);
99
100 /* hash in ek */
101 shake_update(&results, encoded_public_key,
102 sizeof(encoded_public_key));
103
104 /* marshal priv to dk as encoded_private_key */
105 if (!mlkem1024_encode_private_key(&priv, &encoded_private_key,
106 &encoded_private_key_len))
107 errx(1, "mlkem1024_encode_private_key");
108
109 /* hash in dk */
110 shake_update(&results, encoded_private_key,
111 encoded_private_key_len);
112
113 free(encoded_private_key);
114
115 /* draw m as encap entropy from DRNG */
116 shake_out(&drng, encap_entropy, sizeof(encap_entropy));
117
118 /* generate ct as ciphertext, k as shared_secret */
119 MLKEM1024_encap_external_entropy(ciphertext, shared_secret,
120 &pub, encap_entropy);
121
122 /* hash in ct */
123 shake_update(&results, ciphertext, sizeof(ciphertext));
124 /* hash in k */
125 shake_update(&results, shared_secret, sizeof(shared_secret));
126
127 /* draw ct as invalid_ciphertxt from DRNG */
128 shake_out(&drng, invalid_ciphertext,
129 sizeof(invalid_ciphertext));
130
131 /* generte k as shared secret from invalid ciphertext */
132 if (!MLKEM1024_decap(shared_secret, invalid_ciphertext,
133 sizeof(invalid_ciphertext), &priv))
134 errx(1, "decap failed");
135
136 /* hash in k */
137 shake_update(&results, shared_secret, sizeof(shared_secret));
138 }
139 shake_xof(&results);
140 shake_out(&results, out, sizeof(out));
141
142 return compare_data(kExpectedAdam, out, sizeof(out), i, "final result hash");
143}
144
145int
146main(int argc, char **argv)
147{
148 return MlkemIterativeTest();
149}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_keygen_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_keygen_tests.c
deleted file mode 100644
index 442475bc82..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_keygen_tests.c
+++ /dev/null
@@ -1,190 +0,0 @@
1/* $OpenBSD: mlkem1024_keygen_tests.c,v 1.6 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemKeygenFileTest(CBB *seed_cbb, CBB *public_key_cbb, CBB *private_key_cbb,
34 size_t line)
35{
36 struct MLKEM1024_private_key priv;
37 uint8_t *seed = NULL, *public_key = NULL, *private_key = NULL;
38 size_t seed_len = 0, public_key_len = 0, private_key_len = 0;
39 uint8_t *encoded_private_key = NULL;
40 uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
41 size_t len;
42 int failed = 1;
43
44 if (!CBB_finish(seed_cbb, &seed, &seed_len))
45 goto err;
46 if (!compare_length(MLKEM_SEED_BYTES, seed_len, line, "seed length"))
47 goto err;
48 if (!CBB_finish(public_key_cbb, &public_key, &public_key_len))
49 goto err;
50 if (!compare_length(MLKEM1024_PUBLIC_KEY_BYTES, public_key_len, line,
51 "public key length"))
52 goto err;
53 if (!CBB_finish(private_key_cbb, &private_key, &private_key_len))
54 goto err;
55 if (!compare_length(MLKEM1024_PUBLIC_KEY_BYTES, public_key_len, line,
56 "public key length"))
57 goto err;
58
59 MLKEM1024_generate_key_external_entropy(encoded_public_key, &priv,
60 seed);
61 if (!mlkem1024_encode_private_key(&priv, &encoded_private_key, &len)) {
62 warnx("#%zu: encoded_private_key", line);
63 goto err;
64 }
65
66 if (!compare_length(MLKEM1024_PRIVATE_KEY_BYTES, len, line,
67 "private key length"))
68 goto err;
69
70 failed = compare_data(private_key, encoded_private_key,
71 MLKEM1024_PRIVATE_KEY_BYTES, line, "private key");
72 failed |= compare_data(public_key, encoded_public_key,
73 MLKEM1024_PUBLIC_KEY_BYTES, line, "public key");
74
75 err:
76 CBB_cleanup(seed_cbb);
77 CBB_cleanup(public_key_cbb);
78 CBB_cleanup(private_key_cbb);
79 freezero(seed, seed_len);
80 freezero(public_key, public_key_len);
81 freezero(private_key, private_key_len);
82 free(encoded_private_key);
83
84 return failed;
85}
86
87#define S_START 0
88#define S_COMMENT 1
89#define S_SEED 2
90#define S_PUBLIC_KEY 3
91#define S_PRIVATE_KEY 4
92
93#define S2S(x) case x: return #x
94
95static const char *
96state2str(int state)
97{
98 switch (state) {
99 S2S(S_START);
100 S2S(S_COMMENT);
101 S2S(S_SEED);
102 S2S(S_PUBLIC_KEY);
103 S2S(S_PRIVATE_KEY);
104 default:
105 errx(1, "unknown state %d", state);
106 }
107}
108
109int
110main(int argc, char **argv)
111{
112 CBB seed = { 0 }, public_key = { 0 }, private_key = { 0 };
113 const char *test;
114 size_t line = 0;
115 char *buf = NULL;
116 size_t buflen = 0;
117 ssize_t len;
118 FILE *fp;
119 int state;
120 int failed = 0;
121
122 if (argc < 2)
123 errx(1, "%s: missing test file", argv[0]);
124
125 test = argv[1];
126
127 if ((fp = fopen(test, "r")) == NULL)
128 err(1, "can't open test file");
129
130 state = S_COMMENT;
131 line = 0;
132
133 while ((len = getline(&buf, &buflen, fp)) != -1) {
134 const char *msg = state2str(state);
135 CBS cbs;
136 uint8_t u8;
137
138 line++;
139 CBS_init(&cbs, buf, len);
140
141 if (!CBS_get_last_u8(&cbs, &u8))
142 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
143 assert(u8 == '\n');
144
145 switch (state) {
146 case S_START:
147 state = S_COMMENT;
148 break;
149 case S_COMMENT:
150 if (!CBS_get_u8(&cbs, &u8))
151 errx(1, "#%zu %s: CBB_get_u8", line, msg);
152 assert(u8 == '#');
153 if (!CBS_skip(&cbs, CBS_len(&cbs)))
154 errx(1, "#%zu %s: CBB_skip", line, msg);
155 state = S_SEED;
156 break;
157 case S_SEED:
158 if (!get_string_cbs(&cbs, "seed: ", line, msg))
159 errx(1, "#%zu %s: get_string_cbs", line, msg);
160 hex_decode_cbs(&cbs, &seed, line, msg);
161 state = S_PUBLIC_KEY;
162 break;
163 case S_PUBLIC_KEY:
164 if (!get_string_cbs(&cbs, "public_key: ", line, msg))
165 errx(1, "#%zu %s: get_string_cbs", line, msg);
166 hex_decode_cbs(&cbs, &public_key, line, msg);
167 state = S_PRIVATE_KEY;
168 break;
169 case S_PRIVATE_KEY:
170 if (!get_string_cbs(&cbs, "private_key: ", line, msg))
171 errx(1, "#%zu %s: get_string_cbs", line, msg);
172 hex_decode_cbs(&cbs, &private_key, line, msg);
173
174 failed |= MlkemKeygenFileTest(&seed, &public_key,
175 &private_key, line);
176
177 state = S_START;
178 break;
179 }
180 if (CBS_len(&cbs) > 0)
181 errx(1, "#%zu %s: CBS_len", line, msg);
182 }
183 free(buf);
184
185 if (ferror(fp))
186 err(1, NULL);
187 fclose(fp);
188
189 return failed;
190}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_decap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_decap_tests.c
deleted file mode 100644
index 929a4b21b1..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_decap_tests.c
+++ /dev/null
@@ -1,193 +0,0 @@
1/* $OpenBSD: mlkem1024_nist_decap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemNistDecapFileTest(CBB *c_cbb, CBB *k_cbb, CBS *dk, size_t line)
34{
35 uint8_t *c = NULL, *k = NULL;
36 size_t c_len = 0, k_len = 0;
37 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
38 struct MLKEM1024_private_key priv;
39 int failed = 1;
40
41 if (!CBB_finish(c_cbb, &c, &c_len))
42 goto err;
43 if (!CBB_finish(k_cbb, &k, &k_len))
44 goto err;
45
46 if (!compare_length(MLKEM1024_PRIVATE_KEY_BYTES, CBS_len(dk), line,
47 "private key len bogus"))
48 goto err;
49 if (!compare_length(MLKEM_SHARED_SECRET_BYTES, k_len, line,
50 "shared secret len bogus"))
51 goto err;
52
53 if (!MLKEM1024_parse_private_key(&priv, dk)) {
54 warnx("#%zu MLKEM1024_parse_private_key", line);
55 goto err;
56 }
57 if (!MLKEM1024_decap(shared_secret, c, c_len, &priv)) {
58 warnx("#%zu MLKEM1024_decap", line);
59 goto err;
60 }
61
62 failed = compare_data(shared_secret, k, k_len, line, "shared_secret");
63
64 err:
65 CBB_cleanup(c_cbb);
66 CBB_cleanup(k_cbb);
67 freezero(c, c_len);
68 freezero(k, k_len);
69
70 return failed;
71}
72
73#define S_START 0
74#define S_C 1
75#define S_K 2
76#define S_EMPTY 3
77
78#define S2S(x) case x: return #x
79
80static const char *
81state2str(int state)
82{
83 switch (state) {
84 S2S(S_START);
85 S2S(S_C);
86 S2S(S_K);
87 S2S(S_EMPTY);
88 default:
89 errx(1, "unknown state %d", state);
90 }
91}
92
93int
94main(int argc, char **argv)
95{
96 CBB dk_cbb = { 0 }, c = { 0 }, k = { 0 };
97 CBS instr;
98 uint8_t *dk = NULL;
99 size_t dk_len = 0;
100 uint8_t bracket, newline;
101 const char *test;
102 size_t line;
103 char *buf = NULL;
104 size_t buflen = 0;
105 ssize_t len;
106 FILE *fp;
107 int state;
108 int failed = 0;
109
110 if (argc < 2)
111 errx(1, "%s: missing test file", argv[0]);
112
113 test = argv[1];
114
115 if ((fp = fopen(test, "r")) == NULL)
116 err(1, "can't open test file");
117
118 if ((len = getline(&buf, &buflen, fp)) == -1)
119 err(1, "failed to read instruction line");
120
121 /*
122 * The private key is enclosed in brackets in an "instruction line".
123 */
124 line = 1;
125 CBS_init(&instr, buf, len);
126 if (!CBS_get_u8(&instr, &bracket))
127 err(1, "failed to parse instruction line '['");
128 assert(bracket == '[');
129 if (!CBS_get_last_u8(&instr, &newline))
130 errx(1, "failed to parse instruction line '\\n'");
131 assert(newline == '\n');
132 if (!CBS_get_last_u8(&instr, &bracket))
133 errx(1, "failed to parse instruction line ']'");
134 assert(bracket == ']');
135 if (!get_string_cbs(&instr, "dk: ", line, "private key"))
136 errx(1, "failed to read instruction line 'dk: '");
137 hex_decode_cbs(&instr, &dk_cbb, line, "private key");
138 assert(CBS_len(&instr) == 0);
139
140 if (!CBB_finish(&dk_cbb, &dk, &dk_len))
141 errx(1, "CBB finish instruction line");
142
143 state = S_START;
144
145 while ((len = getline(&buf, &buflen, fp)) != -1) {
146 const char *msg = state2str(state);
147 CBS cbs, dk_cbs;
148 uint8_t u8;
149
150 line++;
151 CBS_init(&cbs, buf, len);
152
153 if (!CBS_get_last_u8(&cbs, &u8))
154 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
155 assert(u8 == '\n');
156
157 switch (state) {
158 case S_START:
159 state = S_C;
160 break;
161 case S_C:
162 if (!get_string_cbs(&cbs, "c: ", line, msg))
163 errx(1, "#%zu %s: get_string_cbs", line, msg);
164 hex_decode_cbs(&cbs, &c, line, msg);
165 state = S_K;
166 break;
167 case S_K:
168 if (!get_string_cbs(&cbs, "k: ", line, msg))
169 errx(1, "#%zu %s: get_string_cbs", line, msg);
170 hex_decode_cbs(&cbs, &k, line, msg);
171 state = S_EMPTY;
172 break;
173 case S_EMPTY:
174 CBS_init(&dk_cbs, dk, dk_len);
175
176 failed |= MlkemNistDecapFileTest(&c, &k, &dk_cbs, line);
177
178 state = S_C;
179 break;
180 }
181 if (CBS_len(&cbs) > 0)
182 errx(1, "#%zu %s: CBS_len", line, msg);
183 }
184 free(buf);
185
186 if (ferror(fp))
187 err(1, NULL);
188 fclose(fp);
189
190 freezero(dk, dk_len);
191
192 return failed;
193}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_keygen_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_keygen_tests.c
deleted file mode 100644
index 5ef5ff0caa..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem1024_nist_keygen_tests.c
+++ /dev/null
@@ -1,197 +0,0 @@
1/* $OpenBSD: mlkem1024_nist_keygen_tests.c,v 1.5 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemNistKeygenFileTest(CBB *z_cbb, CBB *d_cbb, CBB *ek_cbb, CBB *dk_cbb,
34 size_t line)
35{
36 CBB seed_cbb;
37 uint8_t *z = NULL, *d = NULL, *ek = NULL, *dk = NULL;
38 size_t z_len = 0, d_len = 0, ek_len = 0, dk_len = 0;
39 uint8_t seed[MLKEM_SEED_BYTES];
40 struct MLKEM1024_private_key priv;
41 uint8_t *encoded_private_key = NULL;
42 uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
43 size_t len;
44 int failed = 1;
45
46 if (!CBB_init_fixed(&seed_cbb, seed, sizeof(seed)))
47 goto err;
48
49 if (!CBB_finish(z_cbb, &z, &z_len))
50 goto err;
51 if (!CBB_finish(d_cbb, &d, &d_len))
52 goto err;
53 if (!CBB_finish(ek_cbb, &ek, &ek_len))
54 goto err;
55 if (!CBB_finish(dk_cbb, &dk, &dk_len))
56 goto err;
57
58 if (!CBB_add_bytes(&seed_cbb, d, d_len))
59 goto err;
60 if (!CBB_add_bytes(&seed_cbb, z, z_len))
61 goto err;
62 if (!CBB_finish(&seed_cbb, NULL, &len))
63 goto err;
64
65 if (!compare_length(MLKEM_SEED_BYTES, len, line, "z or d length bogus"))
66 goto err;
67
68 MLKEM1024_generate_key_external_entropy(encoded_public_key, &priv, seed);
69
70 if (!mlkem1024_encode_private_key(&priv, &encoded_private_key, &len)) {
71 warnx("#%zu mlkem1024_encode_private_key", line);
72 goto err;
73 }
74
75 if (!compare_length(MLKEM1024_PRIVATE_KEY_BYTES, len, line,
76 "private key length"))
77 goto err;
78
79 failed = compare_data(ek, encoded_public_key, MLKEM1024_PUBLIC_KEY_BYTES,
80 line, "public key");
81 failed |= compare_data(dk, encoded_private_key, MLKEM1024_PRIVATE_KEY_BYTES,
82 line, "private key");
83
84 err:
85 CBB_cleanup(&seed_cbb);
86 CBB_cleanup(z_cbb);
87 CBB_cleanup(d_cbb);
88 CBB_cleanup(ek_cbb);
89 CBB_cleanup(dk_cbb);
90 freezero(z, z_len);
91 freezero(d, d_len);
92 freezero(ek, ek_len);
93 freezero(dk, dk_len);
94 free(encoded_private_key);
95
96 return failed;
97}
98
99#define S_START 0
100#define S_Z 1
101#define S_D 2
102#define S_EK 3
103#define S_DK 4
104
105#define S2S(x) case x: return #x
106
107static const char *
108state2str(int state)
109{
110 switch (state) {
111 S2S(S_START);
112 S2S(S_Z);
113 S2S(S_D);
114 S2S(S_EK);
115 S2S(S_DK);
116 default:
117 errx(1, "unknown state %d", state);
118 }
119}
120
121int
122main(int argc, char **argv)
123{
124 CBB z = { 0 }, d = { 0 }, ek = { 0 }, dk = { 0 };
125 const char *test;
126 size_t line = 0;
127 char *buf = NULL;
128 size_t buflen = 0;
129 ssize_t len;
130 FILE *fp;
131 int state;
132 int failed = 0;
133
134 if (argc < 2)
135 errx(1, "%s: missing test file", argv[0]);
136
137 test = argv[1];
138
139 if ((fp = fopen(test, "r")) == NULL)
140 err(1, "can't open test file");
141
142 state = S_Z;
143 line = 0;
144
145 while ((len = getline(&buf, &buflen, fp)) != -1) {
146 const char *msg = state2str(state);
147 CBS cbs;
148 uint8_t u8;
149
150 line++;
151 CBS_init(&cbs, buf, len);
152
153 if (!CBS_get_last_u8(&cbs, &u8))
154 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
155 assert(u8 == '\n');
156
157 switch (state) {
158 case S_START:
159 state = S_Z;
160 break;
161 case S_Z:
162 if (!get_string_cbs(&cbs, "z: ", line, msg))
163 errx(1, "#%zu %s: get_string_cbs", line, msg);
164 hex_decode_cbs(&cbs, &z, line, msg);
165 state = S_D;
166 break;
167 case S_D:
168 if (!get_string_cbs(&cbs, "d: ", line, msg))
169 errx(1, "#%zu %s: get_string_cbs", line, msg);
170 hex_decode_cbs(&cbs, &d, line, msg);
171 state = S_EK;
172 break;
173 case S_EK:
174 if (!get_string_cbs(&cbs, "ek: ", line, msg))
175 errx(1, "#%zu %s: get_string_cbs", line, msg);
176 hex_decode_cbs(&cbs, &ek, line, msg);
177 state = S_DK;
178 break;
179 case S_DK:
180 if (!get_string_cbs(&cbs, "dk: ", line, msg))
181 errx(1, "#%zu %s: get_string_cbs", line, msg);
182 hex_decode_cbs(&cbs, &dk, line, msg);
183
184 failed |= MlkemNistKeygenFileTest(&z, &d, &ek, &dk, line);
185
186 state = S_START;
187 break;
188 }
189 }
190 free(buf);
191
192 if (ferror(fp))
193 err(1, NULL);
194 fclose(fp);
195
196 return failed;
197}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_decap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem768_decap_tests.c
deleted file mode 100644
index a58f005644..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_decap_tests.c
+++ /dev/null
@@ -1,195 +0,0 @@
1/* $OpenBSD: mlkem768_decap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_tests_util.h"
30
31static int
32MlkemDecapFileTest(CBB *ciphertext_cbb, CBB *shared_secret_cbb,
33 CBB *private_key_cbb, int should_fail, size_t line)
34{
35 struct MLKEM768_private_key priv;
36 uint8_t *ciphertext = NULL, *shared_secret = NULL, *private_key = NULL;
37 size_t ciphertext_len = 0, shared_secret_len = 0, private_key_len = 0;
38 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES];
39 CBS private_key_cbs;
40 int failed = 1;
41
42 if (!CBB_finish(ciphertext_cbb, &ciphertext, &ciphertext_len))
43 goto err;
44 if (!CBB_finish(shared_secret_cbb, &shared_secret, &shared_secret_len))
45 goto err;
46 if (!CBB_finish(private_key_cbb, &private_key, &private_key_len))
47 goto err;
48
49 CBS_init(&private_key_cbs, private_key, private_key_len);
50
51 if (!MLKEM768_parse_private_key(&priv, &private_key_cbs)) {
52 if ((failed = !should_fail))
53 warnx("#%zu: parse_private_key", line);
54 goto err;
55 }
56 if (!MLKEM768_decap(shared_secret_buf, ciphertext, ciphertext_len,
57 &priv)) {
58 if ((failed = !should_fail))
59 warnx("#%zu: decap", line);
60 goto err;
61 }
62
63 failed = compare_data(shared_secret, shared_secret_buf,
64 MLKEM_SHARED_SECRET_BYTES, line, "shared_secret");
65
66 if (should_fail != failed) {
67 warnx("FAIL: #%zu: should_fail %d, failed %d",
68 line, should_fail, failed);
69 failed = 1;
70 }
71
72 err:
73 CBB_cleanup(ciphertext_cbb);
74 CBB_cleanup(shared_secret_cbb);
75 CBB_cleanup(private_key_cbb);
76 freezero(ciphertext, ciphertext_len);
77 freezero(shared_secret, shared_secret_len);
78 freezero(private_key, private_key_len);
79
80 return failed;
81}
82
83#define S_START 0
84#define S_COMMENT 1
85#define S_PRIVATE_KEY 2
86#define S_CIPHERTEXT 3
87#define S_RESULT 4
88#define S_SHARED_SECRET 5
89
90#define S2S(x) case x: return #x
91
92static const char *
93state2str(int state)
94{
95 switch (state) {
96 S2S(S_START);
97 S2S(S_COMMENT);
98 S2S(S_PRIVATE_KEY);
99 S2S(S_CIPHERTEXT);
100 S2S(S_RESULT);
101 S2S(S_SHARED_SECRET);
102 default:
103 errx(1, "unknown state %d", state);
104 }
105}
106
107int
108main(int argc, char **argv)
109{
110 CBB ciphertext = { 0 }, shared_secret = { 0 }, private_key = { 0 };
111 int should_fail = 0;
112 const char *test;
113 size_t line = 0;
114 char *buf = NULL;
115 size_t buflen = 0;
116 ssize_t len;
117 FILE *fp;
118 int state;
119 int failed = 0;
120
121 if (argc < 2)
122 errx(1, "%s: missing test file", argv[0]);
123
124 test = argv[1];
125
126 if ((fp = fopen(test, "r")) == NULL)
127 err(1, "can't open test file");
128
129 state = S_COMMENT;
130 line = 0;
131
132 while ((len = getline(&buf, &buflen, fp)) != -1) {
133 const char *msg = state2str(state);
134 CBS cbs;
135 uint8_t u8;
136
137 line++;
138 CBS_init(&cbs, buf, len);
139
140 if (!CBS_get_last_u8(&cbs, &u8))
141 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
142 assert(u8 == '\n');
143
144 switch (state) {
145 case S_START:
146 state = S_COMMENT;
147 break;
148 case S_COMMENT:
149 if (!CBS_get_u8(&cbs, &u8))
150 errx(1, "#%zu %s: CBB_get_u8", line, msg);
151 assert(u8 == '#');
152 if (!CBS_skip(&cbs, CBS_len(&cbs)))
153 errx(1, "#%zu %s: CBB_skip", line, msg);
154 state = S_PRIVATE_KEY;
155 break;
156 case S_PRIVATE_KEY:
157 if (!get_string_cbs(&cbs, "private_key: ", line, msg))
158 errx(1, "#%zu %s: get_string_cbs", line, msg);
159 hex_decode_cbs(&cbs, &private_key, line, msg);
160 state = S_CIPHERTEXT;
161 break;
162 case S_CIPHERTEXT:
163 if (!get_string_cbs(&cbs, "ciphertext: ", line, msg))
164 errx(1, "#%zu %s: get_string_cbs", line, msg);
165 hex_decode_cbs(&cbs, &ciphertext, line, msg);
166 state = S_RESULT;
167 break;
168 case S_RESULT:
169 if (!get_string_cbs(&cbs, "result: ", line, msg))
170 errx(1, "#%zu %s: get_string_cbs", line, msg);
171 should_fail = get_string_cbs(&cbs, "fail", line, msg);
172 state = S_SHARED_SECRET;
173 break;
174 case S_SHARED_SECRET:
175 if (!get_string_cbs(&cbs, "shared_secret: ", line, msg))
176 errx(1, "#%zu %s: get_string_cbs", line, msg);
177 hex_decode_cbs(&cbs, &shared_secret, line, msg);
178
179 failed |= MlkemDecapFileTest(&ciphertext, &shared_secret,
180 &private_key, should_fail, line);
181
182 state = S_START;
183 break;
184 }
185 if (CBS_len(&cbs) > 0)
186 errx(1, "#%zu %s: CBS_len", line, msg);
187 }
188 free(buf);
189
190 if (ferror(fp))
191 err(1, NULL);
192 fclose(fp);
193
194 return failed;
195}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_encap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem768_encap_tests.c
deleted file mode 100644
index e2123f8de5..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_encap_tests.c
+++ /dev/null
@@ -1,210 +0,0 @@
1/* $OpenBSD: mlkem768_encap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemEncapFileTest(CBB *entropy_cbb, CBB *pubkey_cbb, CBB *ciphertext_cbb,
34 CBB *shared_secret_cbb, int should_fail, size_t line)
35{
36 struct MLKEM768_public_key pub;
37 uint8_t *entropy = NULL, *public_key = NULL, *ciphertext = NULL;
38 uint8_t *shared_secret = NULL;
39 size_t entropy_len = 0, public_key_len = 0, ciphertext_len = 0;
40 size_t shared_secret_len = 0;
41 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES];
42 uint8_t ciphertext_buf[MLKEM768_CIPHERTEXT_BYTES];
43 CBS public_key_cbs;
44 int failed = 1;
45
46 if (!CBB_finish(entropy_cbb, &entropy, &entropy_len))
47 goto err;
48 if (!CBB_finish(pubkey_cbb, &public_key, &public_key_len))
49 goto err;
50 if (!CBB_finish(ciphertext_cbb, &ciphertext, &ciphertext_len))
51 goto err;
52 if (!CBB_finish(shared_secret_cbb, &shared_secret, &shared_secret_len))
53 goto err;
54
55 CBS_init(&public_key_cbs, public_key, public_key_len);
56
57 if (!MLKEM768_parse_public_key(&pub, &public_key_cbs)) {
58 if ((failed = !should_fail))
59 warnx("#%zu: parse_public_key", line);
60 goto err;
61 }
62 MLKEM768_encap_external_entropy(ciphertext_buf, shared_secret_buf,
63 &pub, entropy);
64
65 failed = compare_data(shared_secret, shared_secret_buf,
66 MLKEM_SHARED_SECRET_BYTES, line, "shared_secret");
67 failed |= compare_data(ciphertext, ciphertext_buf,
68 MLKEM768_CIPHERTEXT_BYTES, line, "ciphertext");
69
70 if (should_fail != failed) {
71 warnx("FAIL: #%zu: should_fail %d, failed %d",
72 line, should_fail, failed);
73 failed = 1;
74 }
75
76 err:
77 CBB_cleanup(entropy_cbb);
78 CBB_cleanup(pubkey_cbb);
79 CBB_cleanup(ciphertext_cbb);
80 CBB_cleanup(shared_secret_cbb);
81 freezero(entropy, entropy_len);
82 freezero(public_key, public_key_len);
83 freezero(ciphertext, ciphertext_len);
84 freezero(shared_secret, shared_secret_len);
85
86 return failed;
87}
88
89#define S_START 0
90#define S_COMMENT 1
91#define S_ENTROPY 2
92#define S_PUBLIC_KEY 3
93#define S_RESULT 4
94#define S_CIPHERTEXT 5
95#define S_SHARED_SECRET 6
96
97#define S2S(x) case x: return #x
98
99static const char *
100state2str(int state)
101{
102 switch (state) {
103 S2S(S_START);
104 S2S(S_COMMENT);
105 S2S(S_ENTROPY);
106 S2S(S_PUBLIC_KEY);
107 S2S(S_RESULT);
108 S2S(S_CIPHERTEXT);
109 S2S(S_SHARED_SECRET);
110 default:
111 errx(1, "unknown state %d", state);
112 }
113}
114
115int
116main(int argc, char **argv)
117{
118 CBB entropy = { 0 }, public_key = { 0 }, ciphertext = { 0 }, shared_secret = { 0 };
119 int should_fail = 0;
120 const char *test;
121 size_t line;
122 char *buf = NULL;
123 size_t buflen = 0;
124 ssize_t len;
125 FILE *fp;
126 int state;
127 int failed = 0;
128
129 if (argc < 2)
130 errx(1, "%s: missing test file", argv[0]);
131
132 test = argv[1];
133 line = 0;
134
135 if ((fp = fopen(test, "r")) == NULL)
136 err(1, "can't open test file");
137
138 state = S_COMMENT;
139 line = 0;
140
141 while ((len = getline(&buf, &buflen, fp)) != -1) {
142 const char *msg = state2str(state);
143 CBS cbs;
144 uint8_t u8;
145
146 line++;
147 CBS_init(&cbs, buf, len);
148
149 if (!CBS_get_last_u8(&cbs, &u8))
150 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
151 assert(u8 == '\n');
152
153 switch (state) {
154 case S_START:
155 state = S_COMMENT;
156 break;
157 case S_COMMENT:
158 if (!CBS_get_u8(&cbs, &u8))
159 errx(1, "#%zu %s: CBB_get_u8", line, msg);
160 assert(u8 == '#');
161 if (!CBS_skip(&cbs, CBS_len(&cbs)))
162 errx(1, "#%zu %s: CBB_skip", line, msg);
163 state = S_ENTROPY;
164 break;
165 case S_ENTROPY:
166 if (!get_string_cbs(&cbs, "entropy: ", line, msg))
167 errx(1, "#%zu %s: get_string_cbs", line, msg);
168 hex_decode_cbs(&cbs, &entropy, line, msg);
169 state = S_PUBLIC_KEY;
170 break;
171 case S_PUBLIC_KEY:
172 if (!get_string_cbs(&cbs, "public_key = ", line, msg))
173 errx(1, "#%zu %s: get_string_cbs", line, msg);
174 hex_decode_cbs(&cbs, &public_key, line, msg);
175 state = S_RESULT;
176 break;
177 case S_RESULT:
178 if (!get_string_cbs(&cbs, "result: ", line, msg))
179 errx(1, "#%zu %s: get_string_cbs", line, msg);
180 should_fail = get_string_cbs(&cbs, "fail", line, msg);
181 state = S_CIPHERTEXT;
182 break;
183 case S_CIPHERTEXT:
184 if (!get_string_cbs(&cbs, "ciphertext: ", line, msg))
185 errx(1, "#%zu %s: get_string_cbs", line, msg);
186 hex_decode_cbs(&cbs, &ciphertext, line, msg);
187 state = S_SHARED_SECRET;
188 break;
189 case S_SHARED_SECRET:
190 if (!get_string_cbs(&cbs, "shared_secret: ", line, msg))
191 errx(1, "#%zu %s: get_string_cbs", line, msg);
192 hex_decode_cbs(&cbs, &shared_secret, line, msg);
193
194 failed |= MlkemEncapFileTest(&entropy, &public_key,
195 &ciphertext, &shared_secret, should_fail, line);
196
197 state = S_START;
198 break;
199 }
200 if (CBS_len(&cbs) > 0)
201 errx(1, "#%zu %s: CBS_len", line, msg);
202 }
203 free(buf);
204
205 if (ferror(fp))
206 err(1, NULL);
207 fclose(fp);
208
209 return failed;
210}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c b/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
deleted file mode 100644
index 9517980d7b..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_iteration_test.c
+++ /dev/null
@@ -1,149 +0,0 @@
1/* $OpenBSD: mlkem768_iteration_test.c,v 1.3 2024/12/20 00:07:12 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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
25#include "mlkem.h"
26
27#include "mlkem_internal.h"
28#include "mlkem_tests_util.h"
29#include "sha3_internal.h"
30
31/*
32 * The structure of this test is taken from
33 * https://github.com/C2SP/CCTV/blob/main/ML-KEM/README.md?ref=words.filippo.io#accumulated-pq-crystals-vectors
34 * but the final value has been updated to reflect the change from Kyber to
35 * ML-KEM.
36 *
37 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
38 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
39 */
40
41static int
42MlkemIterativeTest(void)
43{
44 /* https://github.com/C2SP/CCTV/tree/main/ML-KEM */
45 /*
46 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
47 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
48 */
49 const uint8_t kExpectedSeedStart[16] = {
50 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
51 0x50, 0x76, 0x05, 0x85, 0x3e
52 };
53
54 /*
55 * Filippo says:
56 * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3
57 * but Boring believes this:
58 */
59 const uint8_t kExpectedAdam[32] = {
60 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
61 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
62 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1
63 };
64 uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
65 uint8_t invalid_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
66 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
67 uint8_t ciphertext[MLKEM768_CIPHERTEXT_BYTES];
68 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
69 uint8_t seed[MLKEM_SEED_BYTES] = {0};
70 struct MLKEM768_private_key priv;
71 struct MLKEM768_public_key pub;
72 sha3_ctx drng, results;
73 uint8_t out[32];
74 int i;
75
76 shake128_init(&drng);
77 shake128_init(&results);
78
79 shake_xof(&drng);
80 for (i = 0; i < 10000; i++) {
81 uint8_t *encoded_private_key = NULL;
82 size_t encoded_private_key_len;
83
84 /*
85 * This should draw both d and z from DRNG concatenating in
86 * seed.
87 */
88 shake_out(&drng, seed, sizeof(seed));
89 if (i == 0) {
90 if (compare_data(seed, kExpectedSeedStart,
91 sizeof(kExpectedSeedStart), 0, "seed start") != 0)
92 errx(1, "compare_data");
93 }
94
95 /* generate ek as encoded_public_key */
96 MLKEM768_generate_key_external_entropy(encoded_public_key,
97 &priv, seed);
98 MLKEM768_public_from_private(&pub, &priv);
99
100 /* hash in ek */
101 shake_update(&results, encoded_public_key,
102 sizeof(encoded_public_key));
103
104 /* marshal priv to dk as encoded_private_key */
105 if (!mlkem768_encode_private_key(&priv, &encoded_private_key,
106 &encoded_private_key_len))
107 errx(1, "mlkem768_encode_private_key");
108
109 /* hash in dk */
110 shake_update(&results, encoded_private_key,
111 encoded_private_key_len);
112
113 free(encoded_private_key);
114
115 /* draw m as encap entropy from DRNG */
116 shake_out(&drng, encap_entropy, sizeof(encap_entropy));
117
118 /* generate ct as ciphertext, k as shared_secret */
119 MLKEM768_encap_external_entropy(ciphertext, shared_secret,
120 &pub, encap_entropy);
121
122 /* hash in ct */
123 shake_update(&results, ciphertext, sizeof(ciphertext));
124 /* hash in k */
125 shake_update(&results, shared_secret, sizeof(shared_secret));
126
127 /* draw ct as invalid_ciphertxt from DRNG */
128 shake_out(&drng, invalid_ciphertext,
129 sizeof(invalid_ciphertext));
130
131 /* generte k as shared secret from invalid ciphertext */
132 if (!MLKEM768_decap(shared_secret, invalid_ciphertext,
133 sizeof(invalid_ciphertext), &priv))
134 errx(1, "decap failed");
135
136 /* hash in k */
137 shake_update(&results, shared_secret, sizeof(shared_secret));
138 }
139 shake_xof(&results);
140 shake_out(&results, out, sizeof(out));
141
142 return compare_data(kExpectedAdam, out, sizeof(out), i, "final result hash");
143}
144
145int
146main(int argc, char **argv)
147{
148 return MlkemIterativeTest();
149}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_keygen_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem768_keygen_tests.c
deleted file mode 100644
index 3f8012c4e6..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_keygen_tests.c
+++ /dev/null
@@ -1,190 +0,0 @@
1/* $OpenBSD: mlkem768_keygen_tests.c,v 1.6 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemKeygenFileTest(CBB *seed_cbb, CBB *public_key_cbb, CBB *private_key_cbb,
34 size_t line)
35{
36 struct MLKEM768_private_key priv;
37 uint8_t *seed = NULL, *public_key = NULL, *private_key = NULL;
38 size_t seed_len = 0, public_key_len = 0, private_key_len = 0;
39 uint8_t *encoded_private_key = NULL;
40 uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
41 size_t len;
42 int failed = 1;
43
44 if (!CBB_finish(seed_cbb, &seed, &seed_len))
45 goto err;
46 if (!compare_length(MLKEM_SEED_BYTES, seed_len, line, "seed length"))
47 goto err;
48 if (!CBB_finish(public_key_cbb, &public_key, &public_key_len))
49 goto err;
50 if (!compare_length(MLKEM768_PUBLIC_KEY_BYTES, public_key_len, line,
51 "public key length"))
52 goto err;
53 if (!CBB_finish(private_key_cbb, &private_key, &private_key_len))
54 goto err;
55 if (!compare_length(MLKEM768_PUBLIC_KEY_BYTES, public_key_len, line,
56 "public key length"))
57 goto err;
58
59 MLKEM768_generate_key_external_entropy(encoded_public_key, &priv,
60 seed);
61 if (!mlkem768_encode_private_key(&priv, &encoded_private_key, &len)) {
62 warnx("#%zu: encoded_private_key", line);
63 goto err;
64 }
65
66 if (!compare_length(MLKEM768_PRIVATE_KEY_BYTES, len, line,
67 "private key length"))
68 goto err;
69
70 failed = compare_data(private_key, encoded_private_key,
71 MLKEM768_PRIVATE_KEY_BYTES, line, "private key");
72 failed |= compare_data(public_key, encoded_public_key,
73 MLKEM768_PUBLIC_KEY_BYTES, line, "public key");
74
75 err:
76 CBB_cleanup(seed_cbb);
77 CBB_cleanup(public_key_cbb);
78 CBB_cleanup(private_key_cbb);
79 freezero(seed, seed_len);
80 freezero(public_key, public_key_len);
81 freezero(private_key, private_key_len);
82 free(encoded_private_key);
83
84 return failed;
85}
86
87#define S_START 0
88#define S_COMMENT 1
89#define S_SEED 2
90#define S_PUBLIC_KEY 3
91#define S_PRIVATE_KEY 4
92
93#define S2S(x) case x: return #x
94
95static const char *
96state2str(int state)
97{
98 switch (state) {
99 S2S(S_START);
100 S2S(S_COMMENT);
101 S2S(S_SEED);
102 S2S(S_PUBLIC_KEY);
103 S2S(S_PRIVATE_KEY);
104 default:
105 errx(1, "unknown state %d", state);
106 }
107}
108
109int
110main(int argc, char **argv)
111{
112 CBB seed = { 0 }, public_key = { 0 }, private_key = { 0 };
113 const char *test;
114 size_t line = 0;
115 char *buf = NULL;
116 size_t buflen = 0;
117 ssize_t len;
118 FILE *fp;
119 int state;
120 int failed = 0;
121
122 if (argc < 2)
123 errx(1, "%s: missing test file", argv[0]);
124
125 test = argv[1];
126
127 if ((fp = fopen(test, "r")) == NULL)
128 err(1, "can't open test file");
129
130 state = S_COMMENT;
131 line = 0;
132
133 while ((len = getline(&buf, &buflen, fp)) != -1) {
134 const char *msg = state2str(state);
135 CBS cbs;
136 uint8_t u8;
137
138 line++;
139 CBS_init(&cbs, buf, len);
140
141 if (!CBS_get_last_u8(&cbs, &u8))
142 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
143 assert(u8 == '\n');
144
145 switch (state) {
146 case S_START:
147 state = S_COMMENT;
148 break;
149 case S_COMMENT:
150 if (!CBS_get_u8(&cbs, &u8))
151 errx(1, "#%zu %s: CBB_get_u8", line, msg);
152 assert(u8 == '#');
153 if (!CBS_skip(&cbs, CBS_len(&cbs)))
154 errx(1, "#%zu %s: CBB_skip", line, msg);
155 state = S_SEED;
156 break;
157 case S_SEED:
158 if (!get_string_cbs(&cbs, "seed: ", line, msg))
159 errx(1, "#%zu %s: get_string_cbs", line, msg);
160 hex_decode_cbs(&cbs, &seed, line, msg);
161 state = S_PUBLIC_KEY;
162 break;
163 case S_PUBLIC_KEY:
164 if (!get_string_cbs(&cbs, "public_key: ", line, msg))
165 errx(1, "#%zu %s: get_string_cbs", line, msg);
166 hex_decode_cbs(&cbs, &public_key, line, msg);
167 state = S_PRIVATE_KEY;
168 break;
169 case S_PRIVATE_KEY:
170 if (!get_string_cbs(&cbs, "private_key: ", line, msg))
171 errx(1, "#%zu %s: get_string_cbs", line, msg);
172 hex_decode_cbs(&cbs, &private_key, line, msg);
173
174 failed |= MlkemKeygenFileTest(&seed, &public_key,
175 &private_key, line);
176
177 state = S_START;
178 break;
179 }
180 if (CBS_len(&cbs) > 0)
181 errx(1, "#%zu %s: CBS_len", line, msg);
182 }
183 free(buf);
184
185 if (ferror(fp))
186 err(1, NULL);
187 fclose(fp);
188
189 return failed;
190}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_nist_decap_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem768_nist_decap_tests.c
deleted file mode 100644
index 9de8958c39..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_nist_decap_tests.c
+++ /dev/null
@@ -1,193 +0,0 @@
1/* $OpenBSD: mlkem768_nist_decap_tests.c,v 1.4 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemNistDecapFileTest(CBB *c_cbb, CBB *k_cbb, CBS *dk, size_t line)
34{
35 uint8_t *c = NULL, *k = NULL;
36 size_t c_len = 0, k_len = 0;
37 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
38 struct MLKEM768_private_key priv;
39 int failed = 1;
40
41 if (!CBB_finish(c_cbb, &c, &c_len))
42 goto err;
43 if (!CBB_finish(k_cbb, &k, &k_len))
44 goto err;
45
46 if (!compare_length(MLKEM768_PRIVATE_KEY_BYTES, CBS_len(dk), line,
47 "private key len bogus"))
48 goto err;
49 if (!compare_length(MLKEM_SHARED_SECRET_BYTES, k_len, line,
50 "shared secret len bogus"))
51 goto err;
52
53 if (!MLKEM768_parse_private_key(&priv, dk)) {
54 warnx("#%zu MLKEM768_parse_private_key", line);
55 goto err;
56 }
57 if (!MLKEM768_decap(shared_secret, c, c_len, &priv)) {
58 warnx("#%zu MLKEM768_decap", line);
59 goto err;
60 }
61
62 failed = compare_data(shared_secret, k, k_len, line, "shared_secret");
63
64 err:
65 CBB_cleanup(c_cbb);
66 CBB_cleanup(k_cbb);
67 freezero(c, c_len);
68 freezero(k, k_len);
69
70 return failed;
71}
72
73#define S_START 0
74#define S_C 1
75#define S_K 2
76#define S_EMPTY 3
77
78#define S2S(x) case x: return #x
79
80static const char *
81state2str(int state)
82{
83 switch (state) {
84 S2S(S_START);
85 S2S(S_C);
86 S2S(S_K);
87 S2S(S_EMPTY);
88 default:
89 errx(1, "unknown state %d", state);
90 }
91}
92
93int
94main(int argc, char **argv)
95{
96 CBB dk_cbb = { 0 }, c = { 0 }, k = { 0 };
97 CBS instr;
98 uint8_t *dk = NULL;
99 size_t dk_len = 0;
100 uint8_t bracket, newline;
101 const char *test;
102 size_t line;
103 char *buf = NULL;
104 size_t buflen = 0;
105 ssize_t len;
106 FILE *fp;
107 int state;
108 int failed = 0;
109
110 if (argc < 2)
111 errx(1, "%s: missing test file", argv[0]);
112
113 test = argv[1];
114
115 if ((fp = fopen(test, "r")) == NULL)
116 err(1, "can't open test file");
117
118 if ((len = getline(&buf, &buflen, fp)) == -1)
119 err(1, "failed to read instruction line");
120
121 /*
122 * The private key is enclosed in brackets in an "instruction line".
123 */
124 line = 1;
125 CBS_init(&instr, buf, len);
126 if (!CBS_get_u8(&instr, &bracket))
127 err(1, "failed to parse instruction line '['");
128 assert(bracket == '[');
129 if (!CBS_get_last_u8(&instr, &newline))
130 errx(1, "failed to parse instruction line '\\n'");
131 assert(newline == '\n');
132 if (!CBS_get_last_u8(&instr, &bracket))
133 errx(1, "failed to parse instruction line ']'");
134 assert(bracket == ']');
135 if (!get_string_cbs(&instr, "dk: ", line, "private key"))
136 errx(1, "failed to read instruction line 'dk: '");
137 hex_decode_cbs(&instr, &dk_cbb, line, "private key");
138 assert(CBS_len(&instr) == 0);
139
140 if (!CBB_finish(&dk_cbb, &dk, &dk_len))
141 errx(1, "CBB finish instruction line");
142
143 state = S_START;
144
145 while ((len = getline(&buf, &buflen, fp)) != -1) {
146 const char *msg = state2str(state);
147 CBS cbs, dk_cbs;
148 uint8_t u8;
149
150 line++;
151 CBS_init(&cbs, buf, len);
152
153 if (!CBS_get_last_u8(&cbs, &u8))
154 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
155 assert(u8 == '\n');
156
157 switch (state) {
158 case S_START:
159 state = S_C;
160 break;
161 case S_C:
162 if (!get_string_cbs(&cbs, "c: ", line, msg))
163 errx(1, "#%zu %s: get_string_cbs", line, msg);
164 hex_decode_cbs(&cbs, &c, line, msg);
165 state = S_K;
166 break;
167 case S_K:
168 if (!get_string_cbs(&cbs, "k: ", line, msg))
169 errx(1, "#%zu %s: get_string_cbs", line, msg);
170 hex_decode_cbs(&cbs, &k, line, msg);
171 state = S_EMPTY;
172 break;
173 case S_EMPTY:
174 CBS_init(&dk_cbs, dk, dk_len);
175
176 failed |= MlkemNistDecapFileTest(&c, &k, &dk_cbs, line);
177
178 state = S_C;
179 break;
180 }
181 if (CBS_len(&cbs) > 0)
182 errx(1, "#%zu %s: CBS_len", line, msg);
183 }
184 free(buf);
185
186 if (ferror(fp))
187 err(1, NULL);
188 fclose(fp);
189
190 freezero(dk, dk_len);
191
192 return failed;
193}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem768_nist_keygen_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem768_nist_keygen_tests.c
deleted file mode 100644
index ae0e018e69..0000000000
--- a/src/regress/lib/libcrypto/mlkem/mlkem768_nist_keygen_tests.c
+++ /dev/null
@@ -1,197 +0,0 @@
1/* $OpenBSD: mlkem768_nist_keygen_tests.c,v 1.5 2024/12/20 00:32:15 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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 <assert.h>
21#include <err.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <stdlib.h>
25
26#include "bytestring.h"
27#include "mlkem.h"
28
29#include "mlkem_internal.h"
30#include "mlkem_tests_util.h"
31
32static int
33MlkemNistKeygenFileTest(CBB *z_cbb, CBB *d_cbb, CBB *ek_cbb, CBB *dk_cbb,
34 size_t line)
35{
36 CBB seed_cbb;
37 uint8_t *z = NULL, *d = NULL, *ek = NULL, *dk = NULL;
38 size_t z_len = 0, d_len = 0, ek_len = 0, dk_len = 0;
39 uint8_t seed[MLKEM_SEED_BYTES];
40 struct MLKEM768_private_key priv;
41 uint8_t *encoded_private_key = NULL;
42 uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
43 size_t len;
44 int failed = 1;
45
46 if (!CBB_init_fixed(&seed_cbb, seed, sizeof(seed)))
47 goto err;
48
49 if (!CBB_finish(z_cbb, &z, &z_len))
50 goto err;
51 if (!CBB_finish(d_cbb, &d, &d_len))
52 goto err;
53 if (!CBB_finish(ek_cbb, &ek, &ek_len))
54 goto err;
55 if (!CBB_finish(dk_cbb, &dk, &dk_len))
56 goto err;
57
58 if (!CBB_add_bytes(&seed_cbb, d, d_len))
59 goto err;
60 if (!CBB_add_bytes(&seed_cbb, z, z_len))
61 goto err;
62 if (!CBB_finish(&seed_cbb, NULL, &len))
63 goto err;
64
65 if (!compare_length(MLKEM_SEED_BYTES, len, line, "z or d length bogus"))
66 goto err;
67
68 MLKEM768_generate_key_external_entropy(encoded_public_key, &priv, seed);
69
70 if (!mlkem768_encode_private_key(&priv, &encoded_private_key, &len)) {
71 warnx("#%zu mlkem768_encode_private_key", line);
72 goto err;
73 }
74
75 if (!compare_length(MLKEM768_PRIVATE_KEY_BYTES, len, line,
76 "private key length"))
77 goto err;
78
79 failed = compare_data(ek, encoded_public_key, MLKEM768_PUBLIC_KEY_BYTES,
80 line, "public key");
81 failed |= compare_data(dk, encoded_private_key, MLKEM768_PRIVATE_KEY_BYTES,
82 line, "private key");
83
84 err:
85 CBB_cleanup(&seed_cbb);
86 CBB_cleanup(z_cbb);
87 CBB_cleanup(d_cbb);
88 CBB_cleanup(ek_cbb);
89 CBB_cleanup(dk_cbb);
90 freezero(z, z_len);
91 freezero(d, d_len);
92 freezero(ek, ek_len);
93 freezero(dk, dk_len);
94 free(encoded_private_key);
95
96 return failed;
97}
98
99#define S_START 0
100#define S_Z 1
101#define S_D 2
102#define S_EK 3
103#define S_DK 4
104
105#define S2S(x) case x: return #x
106
107static const char *
108state2str(int state)
109{
110 switch (state) {
111 S2S(S_START);
112 S2S(S_Z);
113 S2S(S_D);
114 S2S(S_EK);
115 S2S(S_DK);
116 default:
117 errx(1, "unknown state %d", state);
118 }
119}
120
121int
122main(int argc, char **argv)
123{
124 CBB z = { 0 }, d = { 0 }, ek = { 0 }, dk = { 0 };
125 const char *test;
126 size_t line = 0;
127 char *buf = NULL;
128 size_t buflen = 0;
129 ssize_t len;
130 FILE *fp;
131 int state;
132 int failed = 0;
133
134 if (argc < 2)
135 errx(1, "%s: missing test file", argv[0]);
136
137 test = argv[1];
138
139 if ((fp = fopen(test, "r")) == NULL)
140 err(1, "can't open test file");
141
142 state = S_Z;
143 line = 0;
144
145 while ((len = getline(&buf, &buflen, fp)) != -1) {
146 const char *msg = state2str(state);
147 CBS cbs;
148 uint8_t u8;
149
150 line++;
151 CBS_init(&cbs, buf, len);
152
153 if (!CBS_get_last_u8(&cbs, &u8))
154 errx(1, "#%zu %s: CBB_get_last_u8", line, msg);
155 assert(u8 == '\n');
156
157 switch (state) {
158 case S_START:
159 state = S_Z;
160 break;
161 case S_Z:
162 if (!get_string_cbs(&cbs, "z: ", line, msg))
163 errx(1, "#%zu %s: get_string_cbs", line, msg);
164 hex_decode_cbs(&cbs, &z, line, msg);
165 state = S_D;
166 break;
167 case S_D:
168 if (!get_string_cbs(&cbs, "d: ", line, msg))
169 errx(1, "#%zu %s: get_string_cbs", line, msg);
170 hex_decode_cbs(&cbs, &d, line, msg);
171 state = S_EK;
172 break;
173 case S_EK:
174 if (!get_string_cbs(&cbs, "ek: ", line, msg))
175 errx(1, "#%zu %s: get_string_cbs", line, msg);
176 hex_decode_cbs(&cbs, &ek, line, msg);
177 state = S_DK;
178 break;
179 case S_DK:
180 if (!get_string_cbs(&cbs, "dk: ", line, msg))
181 errx(1, "#%zu %s: get_string_cbs", line, msg);
182 hex_decode_cbs(&cbs, &dk, line, msg);
183
184 failed |= MlkemNistKeygenFileTest(&z, &d, &ek, &dk, line);
185
186 state = S_START;
187 break;
188 }
189 }
190 free(buf);
191
192 if (ferror(fp))
193 err(1, NULL);
194 fclose(fp);
195
196 return failed;
197}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c
new file mode 100644
index 0000000000..1ecda95bb9
--- /dev/null
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c
@@ -0,0 +1,230 @@
1/* $OpenBSD: mlkem_iteration_tests.c,v 1.1 2024/12/26 00:04:24 tb Exp $ */
2/*
3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
5 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
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
25#include "mlkem.h"
26
27#include "mlkem_internal.h"
28#include "mlkem_tests_util.h"
29#include "sha3_internal.h"
30
31/*
32 * Based on https://c2sp.org/CCTV/ML-KEM
33 *
34 * The final value has been updated to reflect the change from Kyber to ML-KEM.
35 *
36 * The deterministic RNG is a single SHAKE-128 instance with an empty input.
37 * (The RNG stream starts with 7f9c2ba4e88f827d616045507605853e.)
38 */
39const uint8_t kExpectedSeedStart[16] = {
40 0x7f, 0x9c, 0x2b, 0xa4, 0xe8, 0x8f, 0x82, 0x7d, 0x61, 0x60, 0x45,
41 0x50, 0x76, 0x05, 0x85, 0x3e
42};
43
44/*
45 * Filippo says:
46 * ML-KEM-768: f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3
47 * but Boring believes this:
48 */
49const uint8_t kExpectedAdam768[32] = {
50 0xf9, 0x59, 0xd1, 0x8d, 0x3d, 0x11, 0x80, 0x12, 0x14, 0x33, 0xbf,
51 0x0e, 0x05, 0xf1, 0x1e, 0x79, 0x08, 0xcf, 0x9d, 0x03, 0xed, 0xc1,
52 0x50, 0xb2, 0xb0, 0x7c, 0xb9, 0x0b, 0xef, 0x5b, 0xc1, 0xc1
53};
54
55/*
56 * Filippo says:
57 * ML-KEM-1024: 47ac888fe61544efc0518f46094b4f8a600965fc89822acb06dc7169d24f3543
58 * but Boring believes this:
59 */
60const uint8_t kExpectedAdam1024[32] = {
61 0xe3, 0xbf, 0x82, 0xb0, 0x13, 0x30, 0x7b, 0x2e, 0x9d, 0x47, 0xdd,
62 0xe7, 0x91, 0xff, 0x6d, 0xfc, 0x82, 0xe6, 0x94, 0xe6, 0x38, 0x24,
63 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
64};
65
66typedef void (*mlkem_public_from_private_fn)(void *out_public_key,
67 const void *private_key);
68
69struct iteration_ctx {
70 uint8_t *encoded_public_key;
71 size_t encoded_public_key_len;
72 uint8_t *ciphertext;
73 size_t ciphertext_len;
74 uint8_t *invalid_ciphertext;
75 size_t invalid_ciphertext_len;
76 void *priv;
77 void *pub;
78
79 mlkem_encode_private_key_fn encode_private_key;
80 mlkem_encap_external_entropy_fn encap_external_entropy;
81 mlkem_generate_key_external_entropy_fn generate_key_external_entropy;
82 mlkem_public_from_private_fn public_from_private;
83 mlkem_decap_fn decap;
84
85 const uint8_t *start;
86 size_t start_len;
87
88 const uint8_t *expected;
89 size_t expected_len;
90};
91
92static int
93MlkemIterativeTest(struct iteration_ctx *ctx)
94{
95 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
96 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
97 uint8_t seed[MLKEM_SEED_BYTES] = {0};
98 sha3_ctx drng, results;
99 uint8_t out[32];
100 int i;
101
102 shake128_init(&drng);
103 shake128_init(&results);
104
105 shake_xof(&drng);
106 for (i = 0; i < 10000; i++) {
107 uint8_t *encoded_private_key = NULL;
108 size_t encoded_private_key_len;
109
110 /*
111 * This should draw both d and z from DRNG concatenating in
112 * seed.
113 */
114 shake_out(&drng, seed, sizeof(seed));
115 if (i == 0) {
116 if (compare_data(seed, ctx->start, ctx->start_len,
117 "seed start") != 0)
118 errx(1, "compare_data");
119 }
120
121 /* generate ek as encoded_public_key */
122 ctx->generate_key_external_entropy(ctx->encoded_public_key,
123 ctx->priv, seed);
124 ctx->public_from_private(ctx->pub, ctx->priv);
125
126 /* hash in ek */
127 shake_update(&results, ctx->encoded_public_key,
128 ctx->encoded_public_key_len);
129
130 /* marshal priv to dk as encoded_private_key */
131 if (!ctx->encode_private_key(ctx->priv, &encoded_private_key,
132 &encoded_private_key_len))
133 errx(1, "encode private key");
134
135 /* hash in dk */
136 shake_update(&results, encoded_private_key,
137 encoded_private_key_len);
138
139 free(encoded_private_key);
140
141 /* draw m as encap entropy from DRNG */
142 shake_out(&drng, encap_entropy, sizeof(encap_entropy));
143
144 /* generate ct as ciphertext, k as shared_secret */
145 ctx->encap_external_entropy(ctx->ciphertext, shared_secret,
146 ctx->pub, encap_entropy);
147
148 /* hash in ct */
149 shake_update(&results, ctx->ciphertext, ctx->ciphertext_len);
150 /* hash in k */
151 shake_update(&results, shared_secret, sizeof(shared_secret));
152
153 /* draw ct as invalid_ciphertxt from DRNG */
154 shake_out(&drng, ctx->invalid_ciphertext,
155 ctx->invalid_ciphertext_len);
156
157 /* generate k as shared secret from invalid ciphertext */
158 if (!ctx->decap(shared_secret, ctx->invalid_ciphertext,
159 ctx->invalid_ciphertext_len, ctx->priv))
160 errx(1, "decap failed");
161
162 /* hash in k */
163 shake_update(&results, shared_secret, sizeof(shared_secret));
164 }
165 shake_xof(&results);
166 shake_out(&results, out, sizeof(out));
167
168 return compare_data(ctx->expected, out, sizeof(out), "final result hash");
169}
170
171int
172main(void)
173{
174 uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES];
175 uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
176 uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
177 struct MLKEM768_private_key priv768;
178 struct MLKEM768_public_key pub768;
179 struct iteration_ctx iteration768 = {
180 .encoded_public_key = encoded_public_key768,
181 .encoded_public_key_len = sizeof(encoded_public_key768),
182 .ciphertext = ciphertext768,
183 .ciphertext_len = sizeof(ciphertext768),
184 .invalid_ciphertext = invalid_ciphertext768,
185 .invalid_ciphertext_len = sizeof(invalid_ciphertext768),
186 .priv = &priv768,
187 .pub = &pub768,
188 .encap_external_entropy = mlkem768_encap_external_entropy,
189 .encode_private_key = mlkem768_encode_private_key,
190 .generate_key_external_entropy =
191 mlkem768_generate_key_external_entropy,
192 .public_from_private = mlkem768_public_from_private,
193 .decap = mlkem768_decap,
194 .start = kExpectedSeedStart,
195 .start_len = sizeof(kExpectedSeedStart),
196 .expected = kExpectedAdam768,
197 .expected_len = sizeof(kExpectedAdam768),
198 };
199 uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES];
200 uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
201 uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
202 struct MLKEM1024_private_key priv1024;
203 struct MLKEM1024_public_key pub1024;
204 struct iteration_ctx iteration1024 = {
205 .encoded_public_key = encoded_public_key1024,
206 .encoded_public_key_len = sizeof(encoded_public_key1024),
207 .ciphertext = ciphertext1024,
208 .ciphertext_len = sizeof(ciphertext1024),
209 .invalid_ciphertext = invalid_ciphertext1024,
210 .invalid_ciphertext_len = sizeof(invalid_ciphertext1024),
211 .priv = &priv1024,
212 .pub = &pub1024,
213 .encap_external_entropy = mlkem1024_encap_external_entropy,
214 .encode_private_key = mlkem1024_encode_private_key,
215 .generate_key_external_entropy =
216 mlkem1024_generate_key_external_entropy,
217 .public_from_private = mlkem1024_public_from_private,
218 .decap = mlkem1024_decap,
219 .start = kExpectedSeedStart,
220 .start_len = sizeof(kExpectedSeedStart),
221 .expected = kExpectedAdam1024,
222 .expected_len = sizeof(kExpectedAdam1024),
223 };
224 int failed = 0;
225
226 failed |= MlkemIterativeTest(&iteration768);
227 failed |= MlkemIterativeTest(&iteration1024);
228
229 return failed;
230}
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
34enum test_type {
35 TEST_TYPE_NORMAL,
36 TEST_TYPE_NIST,
37};
38
39struct 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
49enum decap_states {
50 DECAP_PRIVATE_KEY,
51 DECAP_CIPHERTEXT,
52 DECAP_RESULT,
53 DECAP_SHARED_SECRET,
54 N_DECAP_STATES,
55};
56
57static 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
85static int
86decap_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
95static void
96decap_finish(void *ctx)
97{
98 (void)ctx;
99}
100
101static int
102MlkemDecapFileTest(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
140static int
141decap_run_test_case(void *ctx)
142{
143 return MlkemDecapFileTest(ctx);
144}
145
146static 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
156enum nist_decap_instructions {
157 NIST_DECAP_DK,
158 N_NIST_DECAP_INSTRUCTIONS,
159};
160
161static 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
170enum nist_decap_states {
171 NIST_DECAP_C,
172 NIST_DECAP_K,
173 N_NIST_DECAP_STATES,
174};
175
176static 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
191static int
192MlkemNistDecapFileTest(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
227static int
228nist_decap_run_test_case(void *ctx)
229{
230 return MlkemNistDecapFileTest(ctx);
231}
232
233static 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
246static int
247mlkem_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
278struct 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
289enum 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
298static 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
332static int
333encap_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
342static void
343encap_finish(void *ctx)
344{
345 (void)ctx;
346}
347
348static int
349MlkemEncapFileTest(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
386static int
387encap_run_test_case(void *ctx)
388{
389 return MlkemEncapFileTest(ctx);
390}
391
392static 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
402static int
403mlkem_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
434struct 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
447enum keygen_states {
448 KEYGEN_SEED,
449 KEYGEN_PUBLIC_KEY,
450 KEYGEN_PRIVATE_KEY,
451 N_KEYGEN_STATES,
452};
453
454static 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
475static int
476keygen_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
485static void
486keygen_finish(void *ctx)
487{
488 (void)ctx;
489}
490
491static int
492MlkemKeygenFileTest(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
532static int
533keygen_run_test_case(void *ctx)
534{
535 return MlkemKeygenFileTest(ctx);
536}
537
538static 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
548enum 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
556static 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
583static int
584MlkemNistKeygenFileTest(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
631static int
632nist_keygen_run_test_case(void *ctx)
633{
634 return MlkemNistKeygenFileTest(ctx);
635}
636
637static 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
647static int
648mlkem_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
690static int
691run_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
719int
720main(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}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c
index 6287849e3b..1bb2ed3a8b 100644
--- a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_tests_util.c,v 1.4 2024/12/20 15:47:26 tb Exp $ */ 1/* $OpenBSD: mlkem_tests_util.c,v 1.5 2024/12/26 00:04:24 tb Exp $ */
2/* 2/*
3 * Copyright (c) 2024 Google Inc. 3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
@@ -45,13 +45,12 @@ hexdump(const uint8_t *buf, size_t len, const uint8_t *compare)
45} 45}
46 46
47int 47int
48compare_data(const uint8_t *want, const uint8_t *got, size_t len, size_t line, 48compare_data(const uint8_t *want, const uint8_t *got, size_t len, const char *msg)
49 const char *msg)
50{ 49{
51 if (memcmp(want, got, len) == 0) 50 if (memcmp(want, got, len) == 0)
52 return 0; 51 return 0;
53 52
54 warnx("FAIL: #%zu - %s differs", line, msg); 53 warnx("FAIL: %s differs", msg);
55 fprintf(stderr, "want:\n"); 54 fprintf(stderr, "want:\n");
56 hexdump(want, len, got); 55 hexdump(want, len, got);
57 fprintf(stderr, "got:\n"); 56 fprintf(stderr, "got:\n");
@@ -62,80 +61,15 @@ compare_data(const uint8_t *want, const uint8_t *got, size_t len, size_t line,
62} 61}
63 62
64int 63int
65compare_length(size_t want, size_t got, size_t line, const char *msg) 64mlkem768_encode_private_key(const void *private_key, uint8_t **out_buf,
66{ 65 size_t *out_len)
67 if (want == got)
68 return 1;
69
70 warnx("#%zu: %s: want %zu, got %zu", line, msg, want, got);
71 return 0;
72}
73
74static int
75hex_get_nibble_cbs(CBS *cbs, uint8_t *out_nibble)
76{
77 uint8_t c;
78
79 if (!CBS_get_u8(cbs, &c))
80 return 0;
81
82 if (c >= '0' && c <= '9') {
83 *out_nibble = c - '0';
84 return 1;
85 }
86 if (c >= 'a' && c <= 'f') {
87 *out_nibble = c - 'a' + 10;
88 return 1;
89 }
90 if (c >= 'A' && c <= 'F') {
91 *out_nibble = c - 'A' + 10;
92 return 1;
93 }
94
95 return 0;
96}
97
98void
99hex_decode_cbs(CBS *cbs, CBB *cbb, size_t line, const char *msg)
100{
101 if (!CBB_init(cbb, 0))
102 errx(1, "#%zu %s: %s CBB_init", line, msg, __func__);
103
104 while (CBS_len(cbs) > 0) {
105 uint8_t hi, lo;
106
107 if (!hex_get_nibble_cbs(cbs, &hi))
108 errx(1, "#%zu %s: %s nibble", line, msg, __func__);
109 if (!hex_get_nibble_cbs(cbs, &lo))
110 errx(1, "#%zu %s: %s nibble", line, msg, __func__);
111
112 if (!CBB_add_u8(cbb, hi << 4 | lo))
113 errx(1, "#%zu %s: %s CBB_add_u8", line, msg, __func__);
114 }
115}
116
117int
118get_string_cbs(CBS *cbs_in, const char *str, size_t line, const char *msg)
119{
120 CBS cbs;
121 size_t len = strlen(str);
122
123 if (!CBS_get_bytes(cbs_in, &cbs, len))
124 errx(1, "#%zu %s: %s CBB_get_bytes", line, msg, __func__);
125
126 return CBS_mem_equal(&cbs, str, len);
127}
128
129int
130mlkem768_encode_private_key(const struct MLKEM768_private_key *priv,
131 uint8_t **out_buf, size_t *out_len)
132{ 66{
133 CBB cbb; 67 CBB cbb;
134 int ret = 0; 68 int ret = 0;
135 69
136 if (!CBB_init(&cbb, MLKEM768_PUBLIC_KEY_BYTES)) 70 if (!CBB_init(&cbb, MLKEM768_PUBLIC_KEY_BYTES))
137 goto err; 71 goto err;
138 if (!MLKEM768_marshal_private_key(&cbb, priv)) 72 if (!MLKEM768_marshal_private_key(&cbb, private_key))
139 goto err; 73 goto err;
140 if (!CBB_finish(&cbb, out_buf, out_len)) 74 if (!CBB_finish(&cbb, out_buf, out_len))
141 goto err; 75 goto err;
@@ -149,15 +83,15 @@ mlkem768_encode_private_key(const struct MLKEM768_private_key *priv,
149} 83}
150 84
151int 85int
152mlkem768_encode_public_key(const struct MLKEM768_public_key *pub, 86mlkem768_encode_public_key(const void *public_key, uint8_t **out_buf,
153 uint8_t **out_buf, size_t *out_len) 87 size_t *out_len)
154{ 88{
155 CBB cbb; 89 CBB cbb;
156 int ret = 0; 90 int ret = 0;
157 91
158 if (!CBB_init(&cbb, MLKEM768_PUBLIC_KEY_BYTES)) 92 if (!CBB_init(&cbb, MLKEM768_PUBLIC_KEY_BYTES))
159 goto err; 93 goto err;
160 if (!MLKEM768_marshal_public_key(&cbb, pub)) 94 if (!MLKEM768_marshal_public_key(&cbb, public_key))
161 goto err; 95 goto err;
162 if (!CBB_finish(&cbb, out_buf, out_len)) 96 if (!CBB_finish(&cbb, out_buf, out_len))
163 goto err; 97 goto err;
@@ -171,15 +105,15 @@ mlkem768_encode_public_key(const struct MLKEM768_public_key *pub,
171} 105}
172 106
173int 107int
174mlkem1024_encode_private_key(const struct MLKEM1024_private_key *priv, 108mlkem1024_encode_private_key(const void *private_key, uint8_t **out_buf,
175 uint8_t **out_buf, size_t *out_len) 109 size_t *out_len)
176{ 110{
177 CBB cbb; 111 CBB cbb;
178 int ret = 0; 112 int ret = 0;
179 113
180 if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES)) 114 if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
181 goto err; 115 goto err;
182 if (!MLKEM1024_marshal_private_key(&cbb, priv)) 116 if (!MLKEM1024_marshal_private_key(&cbb, private_key))
183 goto err; 117 goto err;
184 if (!CBB_finish(&cbb, out_buf, out_len)) 118 if (!CBB_finish(&cbb, out_buf, out_len))
185 goto err; 119 goto err;
@@ -193,15 +127,15 @@ mlkem1024_encode_private_key(const struct MLKEM1024_private_key *priv,
193} 127}
194 128
195int 129int
196mlkem1024_encode_public_key(const struct MLKEM1024_public_key *pub, 130mlkem1024_encode_public_key(const void *public_key, uint8_t **out_buf,
197 uint8_t **out_buf, size_t *out_len) 131 size_t *out_len)
198{ 132{
199 CBB cbb; 133 CBB cbb;
200 int ret = 0; 134 int ret = 0;
201 135
202 if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES)) 136 if (!CBB_init(&cbb, MLKEM1024_PUBLIC_KEY_BYTES))
203 goto err; 137 goto err;
204 if (!MLKEM1024_marshal_public_key(&cbb, pub)) 138 if (!MLKEM1024_marshal_public_key(&cbb, public_key))
205 goto err; 139 goto err;
206 if (!CBB_finish(&cbb, out_buf, out_len)) 140 if (!CBB_finish(&cbb, out_buf, out_len))
207 goto err; 141 goto err;
@@ -213,3 +147,121 @@ mlkem1024_encode_public_key(const struct MLKEM1024_public_key *pub,
213 147
214 return ret; 148 return ret;
215} 149}
150
151int
152mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
153 const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key)
154{
155 return MLKEM768_decap(out_shared_secret, ciphertext, ciphertext_len,
156 private_key);
157}
158
159void
160mlkem768_encap(uint8_t *out_ciphertext,
161 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
162 const void *public_key)
163{
164 MLKEM768_encap(out_ciphertext, out_shared_secret, public_key);
165}
166
167void
168mlkem768_encap_external_entropy(uint8_t *out_ciphertext,
169 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
170 const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
171{
172 MLKEM768_encap_external_entropy(out_ciphertext, out_shared_secret,
173 public_key, entropy);
174}
175
176void
177mlkem768_generate_key(uint8_t *out_encoded_public_key,
178 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key)
179{
180 MLKEM768_generate_key(out_encoded_public_key, optional_out_seed,
181 out_private_key);
182}
183
184void
185mlkem768_generate_key_external_entropy(uint8_t *out_encoded_public_key,
186 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES])
187{
188 MLKEM768_generate_key_external_entropy(out_encoded_public_key,
189 out_private_key, entropy);
190}
191
192int
193mlkem768_parse_private_key(void *out_private_key, CBS *private_key_cbs)
194{
195 return MLKEM768_parse_private_key(out_private_key, private_key_cbs);
196}
197
198int
199mlkem768_parse_public_key(void *out_public_key, CBS *public_key_cbs)
200{
201 return MLKEM768_parse_public_key(out_public_key, public_key_cbs);
202}
203
204void
205mlkem768_public_from_private(void *out_public_key, const void *private_key)
206{
207 MLKEM768_public_from_private(out_public_key, private_key);
208}
209
210int
211mlkem1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
212 const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key)
213{
214 return MLKEM1024_decap(out_shared_secret, ciphertext, ciphertext_len,
215 private_key);
216}
217
218void
219mlkem1024_encap(uint8_t *out_ciphertext,
220 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
221 const void *public_key)
222{
223 MLKEM1024_encap(out_ciphertext, out_shared_secret, public_key);
224}
225
226void
227mlkem1024_encap_external_entropy(uint8_t *out_ciphertext,
228 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
229 const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
230{
231 MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret,
232 public_key, entropy);
233}
234
235void
236mlkem1024_generate_key(uint8_t *out_encoded_public_key,
237 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key)
238{
239 MLKEM1024_generate_key(out_encoded_public_key, optional_out_seed,
240 out_private_key);
241}
242
243void
244mlkem1024_generate_key_external_entropy(uint8_t *out_encoded_public_key,
245 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES])
246{
247 MLKEM1024_generate_key_external_entropy(out_encoded_public_key,
248 out_private_key, entropy);
249}
250
251int
252mlkem1024_parse_private_key(void *out_private_key, CBS *private_key_cbs)
253{
254 return MLKEM1024_parse_private_key(out_private_key, private_key_cbs);
255}
256
257void
258mlkem1024_public_from_private(void *out_public_key, const void *private_key)
259{
260 MLKEM1024_public_from_private(out_public_key, private_key);
261}
262
263int
264mlkem1024_parse_public_key(void *out_public_key, CBS *public_key_cbs)
265{
266 return MLKEM1024_parse_public_key(out_public_key, public_key_cbs);
267}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h
index cbb0f83f8c..7fbe6f76a9 100644
--- a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_tests_util.h,v 1.3 2024/12/20 00:07:12 tb Exp $ */ 1/* $OpenBSD: mlkem_tests_util.h,v 1.4 2024/12/26 00:04:24 tb Exp $ */
2/* 2/*
3 * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 3 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> 4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
@@ -24,26 +24,66 @@
24 24
25#include "bytestring.h" 25#include "bytestring.h"
26 26
27struct MLKEM1024_private_key; 27#include "mlkem.h"
28struct MLKEM1024_public_key; 28#include "mlkem_internal.h"
29struct MLKEM768_private_key;
30struct MLKEM768_public_key;
31 29
32/* XXX - return values of the two compare functions are inconsistent */
33int compare_data(const uint8_t *want, const uint8_t *got, size_t len, 30int compare_data(const uint8_t *want, const uint8_t *got, size_t len,
34 size_t line, const char *msg); 31 const char *msg);
35int compare_length(size_t want, size_t got, size_t line, const char *msg); 32
36 33int mlkem768_encode_private_key(const void *priv, uint8_t **out_buf,
37void hex_decode_cbs(CBS *cbs, CBB *cbb, size_t line, const char *msg); 34 size_t *out_len);
38int get_string_cbs(CBS *cbs, const char *str, size_t line, const char *msg); 35int mlkem768_encode_public_key(const void *pub, uint8_t **out_buf,
39 36 size_t *out_len);
40int mlkem768_encode_private_key(const struct MLKEM768_private_key *priv, 37int mlkem1024_encode_private_key(const void *priv, uint8_t **out_buf,
41 uint8_t **out_buf, size_t *out_len); 38 size_t *out_len);
42int mlkem768_encode_public_key(const struct MLKEM768_public_key *pub, 39int mlkem1024_encode_public_key(const void *pub, uint8_t **out_buf,
43 uint8_t **out_buf, size_t *out_len); 40 size_t *out_len);
44int mlkem1024_encode_private_key(const struct MLKEM1024_private_key *priv, 41
45 uint8_t **out_buf, size_t *out_len); 42int mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
46int mlkem1024_encode_public_key(const struct MLKEM1024_public_key *pub, 43 const uint8_t *ciphertext, size_t ciphertext_len, const void *priv);
47 uint8_t **out_buf, size_t *out_len); 44void mlkem768_encap(uint8_t *out_ciphertext,
45 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub);
46void mlkem768_encap_external_entropy(uint8_t *out_ciphertext,
47 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub,
48 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
49void mlkem768_generate_key(uint8_t *out_encoded_public_key,
50 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key);
51void mlkem768_generate_key_external_entropy(uint8_t *out_encoded_public_key,
52 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]);
53int mlkem768_parse_private_key(void *priv, CBS *private_key_cbs);
54int mlkem768_parse_public_key(void *pub, CBS *in);
55void mlkem768_public_from_private(void *out_public_key, const void *private_key);
56
57int mlkem1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
58 const uint8_t *ciphertext, size_t ciphertext_len, const void *priv);
59void mlkem1024_encap(uint8_t *out_ciphertext,
60 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub);
61void mlkem1024_encap_external_entropy(uint8_t *out_ciphertext,
62 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub,
63 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
64void mlkem1024_generate_key(uint8_t *out_encoded_public_key,
65 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key);
66void mlkem1024_generate_key_external_entropy(uint8_t *out_encoded_public_key,
67 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]);
68int mlkem1024_parse_private_key(void *priv, CBS *private_key_cbs);
69int mlkem1024_parse_public_key(void *pub, CBS *in);
70void mlkem1024_public_from_private(void *out_public_key, const void *private_key);
71
72typedef int (*mlkem_encode_private_key_fn)(const void *, uint8_t **, size_t *);
73typedef int (*mlkem_encode_public_key_fn)(const void *, uint8_t **, size_t *);
74typedef int (*mlkem_decap_fn)(uint8_t [MLKEM_SHARED_SECRET_BYTES],
75 const uint8_t *, size_t, const void *);
76typedef void (*mlkem_encap_fn)(uint8_t *, uint8_t [MLKEM_SHARED_SECRET_BYTES],
77 const void *);
78typedef void (*mlkem_encap_external_entropy_fn)(uint8_t *,
79 uint8_t [MLKEM_SHARED_SECRET_BYTES], const void *,
80 const uint8_t [MLKEM_ENCAP_ENTROPY]);
81typedef void (*mlkem_generate_key_fn)(uint8_t *, uint8_t *, void *);
82typedef void (*mlkem_generate_key_external_entropy_fn)(uint8_t *, void *,
83 const uint8_t [MLKEM_SEED_BYTES]);
84typedef int (*mlkem_parse_private_key_fn)(void *, CBS *);
85typedef int (*mlkem_parse_public_key_fn)(void *, CBS *);
86typedef void (*mlkem_public_from_private_fn)(void *out_public_key,
87 const void *private_key);
48 88
49#endif /* MLKEM_TEST_UTIL_H */ 89#endif /* MLKEM_TEST_UTIL_H */
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c b/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
index 18bf128bea..3f7e2886b9 100644
--- a/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_unittest.c,v 1.4 2024/12/20 00:07:12 tb Exp $ */ 1/* $OpenBSD: mlkem_unittest.c,v 1.5 2024/12/26 00:04:24 tb Exp $ */
2/* 2/*
3 * Copyright (c) 2024 Google Inc. 3 * Copyright (c) 2024 Google Inc.
4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 4 * Copyright (c) 2024 Bob Beck <beck@obtuse.com>
@@ -27,13 +27,28 @@
27 27
28#include "mlkem_tests_util.h" 28#include "mlkem_tests_util.h"
29 29
30struct unittest_ctx {
31 void *priv;
32 void *pub;
33 void *priv2;
34 void *pub2;
35 uint8_t *encoded_public_key;
36 size_t encoded_public_key_len;
37 uint8_t *ciphertext;
38 size_t ciphertext_len;
39 mlkem_decap_fn decap;
40 mlkem_encap_fn encap;
41 mlkem_generate_key_fn generate_key;
42 mlkem_parse_private_key_fn parse_private_key;
43 mlkem_parse_public_key_fn parse_public_key;
44 mlkem_encode_private_key_fn encode_private_key;
45 mlkem_encode_public_key_fn encode_public_key;
46 mlkem_public_from_private_fn public_from_private;
47};
48
30static int 49static int
31MlKem768UnitTest(void) 50MlKemUnitTest(struct unittest_ctx *ctx)
32{ 51{
33 struct MLKEM768_private_key priv = { 0 }, priv2 = { 0 };
34 struct MLKEM768_public_key pub = { 0 }, pub2 = { 0 };
35 uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
36 uint8_t ciphertext[MLKEM768_CIPHERTEXT_BYTES];
37 uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES]; 52 uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES];
38 uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES]; 53 uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES];
39 uint8_t first_two_bytes[2]; 54 uint8_t first_two_bytes[2];
@@ -42,22 +57,22 @@ MlKem768UnitTest(void)
42 CBS cbs; 57 CBS cbs;
43 int failed = 0; 58 int failed = 0;
44 59
45 MLKEM768_generate_key(encoded_public_key, NULL, &priv); 60 ctx->generate_key(ctx->encoded_public_key, NULL, ctx->priv);
46 61
47 memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes)); 62 memcpy(first_two_bytes, ctx->encoded_public_key, sizeof(first_two_bytes));
48 memset(encoded_public_key, 0xff, sizeof(first_two_bytes)); 63 memset(ctx->encoded_public_key, 0xff, sizeof(first_two_bytes));
49 64
50 CBS_init(&cbs, encoded_public_key, sizeof(encoded_public_key)); 65 CBS_init(&cbs, ctx->encoded_public_key, ctx->encoded_public_key_len);
51 66
52 /* Parsing should fail because the first coefficient is >= kPrime. */ 67 /* Parsing should fail because the first coefficient is >= kPrime. */
53 if (MLKEM768_parse_public_key(&pub, &cbs)) { 68 if (ctx->parse_public_key(ctx->pub, &cbs)) {
54 warnx("MLKEM768_parse_public_key should have failed"); 69 warnx("parse_public_key should have failed");
55 failed |= 1; 70 failed |= 1;
56 } 71 }
57 72
58 memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes)); 73 memcpy(ctx->encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
59 CBS_init(&cbs, encoded_public_key, sizeof(encoded_public_key)); 74 CBS_init(&cbs, ctx->encoded_public_key, ctx->encoded_public_key_len);
60 if (!MLKEM768_parse_public_key(&pub, &cbs)) { 75 if (!ctx->parse_public_key(ctx->pub, &cbs)) {
61 warnx("MLKEM768_parse_public_key"); 76 warnx("MLKEM768_parse_public_key");
62 failed |= 1; 77 failed |= 1;
63 } 78 }
@@ -67,16 +82,16 @@ MlKem768UnitTest(void)
67 failed |= 1; 82 failed |= 1;
68 } 83 }
69 84
70 if (!mlkem768_encode_public_key(&pub, &tmp_buf, &tmp_buf_len)) { 85 if (!ctx->encode_public_key(ctx->pub, &tmp_buf, &tmp_buf_len)) {
71 warnx("encode_public_key"); 86 warnx("encode_public_key");
72 failed |= 1; 87 failed |= 1;
73 } 88 }
74 if (sizeof(encoded_public_key) != tmp_buf_len) { 89 if (ctx->encoded_public_key_len != tmp_buf_len) {
75 warnx("mlkem768 encoded public key lengths differ"); 90 warnx("encoded public key lengths differ");
76 failed |= 1; 91 failed |= 1;
77 } 92 }
78 93
79 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, 768, 94 if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len,
80 "encoded public keys") != 0) { 95 "encoded public keys") != 0) {
81 warnx("compare_data"); 96 warnx("compare_data");
82 failed |= 1; 97 failed |= 1;
@@ -84,17 +99,17 @@ MlKem768UnitTest(void)
84 free(tmp_buf); 99 free(tmp_buf);
85 tmp_buf = NULL; 100 tmp_buf = NULL;
86 101
87 MLKEM768_public_from_private(&pub2, &priv); 102 ctx->public_from_private(ctx->pub2, ctx->priv);
88 if (!mlkem768_encode_public_key(&pub2, &tmp_buf, &tmp_buf_len)) { 103 if (!ctx->encode_public_key(ctx->pub2, &tmp_buf, &tmp_buf_len)) {
89 warnx("mlkem768_encode_public_key"); 104 warnx("encode_public_key");
90 failed |= 1; 105 failed |= 1;
91 } 106 }
92 if (sizeof(encoded_public_key) != tmp_buf_len) { 107 if (ctx->encoded_public_key_len != tmp_buf_len) {
93 warnx("mlkem768 encoded public key lengths differ"); 108 warnx("encoded public key lengths differ");
94 failed |= 1; 109 failed |= 1;
95 } 110 }
96 111
97 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, 768, 112 if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len,
98 "encoded public keys") != 0) { 113 "encoded public keys") != 0) {
99 warnx("compare_data"); 114 warnx("compare_data");
100 failed |= 1; 115 failed |= 1;
@@ -102,7 +117,7 @@ MlKem768UnitTest(void)
102 free(tmp_buf); 117 free(tmp_buf);
103 tmp_buf = NULL; 118 tmp_buf = NULL;
104 119
105 if (!mlkem768_encode_private_key(&priv, &encoded_private_key, 120 if (!ctx->encode_private_key(ctx->priv, &encoded_private_key,
106 &encoded_private_key_len)) { 121 &encoded_private_key_len)) {
107 warnx("mlkem768_encode_private_key"); 122 warnx("mlkem768_encode_private_key");
108 failed |= 1; 123 failed |= 1;
@@ -113,7 +128,7 @@ MlKem768UnitTest(void)
113 CBS_init(&cbs, encoded_private_key, encoded_private_key_len); 128 CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
114 129
115 /* Parsing should fail because the first coefficient is >= kPrime. */ 130 /* Parsing should fail because the first coefficient is >= kPrime. */
116 if (MLKEM768_parse_private_key(&priv2, &cbs)) { 131 if (ctx->parse_private_key(ctx->priv2, &cbs)) {
117 warnx("MLKEM768_parse_private_key should have failed"); 132 warnx("MLKEM768_parse_private_key should have failed");
118 failed |= 1; 133 failed |= 1;
119 } 134 }
@@ -121,162 +136,22 @@ MlKem768UnitTest(void)
121 memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes)); 136 memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes));
122 CBS_init(&cbs, encoded_private_key, encoded_private_key_len); 137 CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
123 138
124 if (!MLKEM768_parse_private_key(&priv2, &cbs)) { 139 if (!ctx->parse_private_key(ctx->priv2, &cbs)) {
125 warnx("MLKEM768_parse_private_key"); 140 warnx("MLKEM768_parse_private_key");
126 failed |= 1; 141 failed |= 1;
127 } 142 }
128 143
129 if (!mlkem768_encode_private_key(&priv2, &tmp_buf, &tmp_buf_len)) { 144 if (!ctx->encode_private_key(ctx->priv2, &tmp_buf, &tmp_buf_len)) {
130 warnx("mlkem768_encode_private_key"); 145 warnx("encode_private_key");
131 failed |= 1;
132 }
133
134 if (encoded_private_key_len != tmp_buf_len) {
135 warnx("mlkem768 encode private key lengths differ");
136 failed |= 1;
137 }
138
139 if (compare_data(encoded_private_key, tmp_buf, tmp_buf_len, 768,
140 "encoded private key") != 0) {
141 warnx("compare_data");
142 failed |= 1;
143 }
144
145 free(tmp_buf);
146 tmp_buf = NULL;
147
148 MLKEM768_encap(ciphertext, shared_secret1, &pub);
149 MLKEM768_decap(shared_secret2, ciphertext, MLKEM768_CIPHERTEXT_BYTES,
150 &priv);
151 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
152 768, "shared secrets with priv") != 0) {
153 warnx("compare_data");
154 failed |= 1;
155 }
156
157 MLKEM768_decap(shared_secret2, ciphertext, MLKEM768_CIPHERTEXT_BYTES,
158 &priv2);
159 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
160 768, "shared secrets with priv2") != 0) {
161 warnx("compare_data");
162 failed |= 1;
163 }
164
165 free(encoded_private_key);
166
167 return failed;
168}
169
170static int
171MlKem1024UnitTest(void)
172{
173 struct MLKEM1024_private_key priv = { 0 }, priv2 = { 0 };
174 struct MLKEM1024_public_key pub = { 0 }, pub2 = { 0 };
175 uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
176 uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
177 uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES];
178 uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES];
179 uint8_t first_two_bytes[2];
180 uint8_t *encoded_private_key = NULL, *tmp_buf = NULL;
181 size_t encoded_private_key_len, tmp_buf_len;
182 CBS cbs;
183 int failed = 0;
184
185 MLKEM1024_generate_key(encoded_public_key, NULL, &priv);
186
187 memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes));
188 memset(encoded_public_key, 0xff, sizeof(first_two_bytes));
189
190 CBS_init(&cbs, encoded_public_key, sizeof(encoded_public_key));
191
192 /* Parsing should fail because the first coefficient is >= kPrime. */
193 if (MLKEM1024_parse_public_key(&pub, &cbs)) {
194 warnx("MLKEM1024_parse_public_key should have failed");
195 failed |= 1;
196 }
197
198 memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
199 CBS_init(&cbs, encoded_public_key, sizeof(encoded_public_key));
200 if (!MLKEM1024_parse_public_key(&pub, &cbs)) {
201 warnx("MLKEM1024_parse_public_key");
202 failed |= 1;
203 }
204
205 if (CBS_len(&cbs) != 0u) {
206 warnx("CBS_len must be 0");
207 failed |= 1;
208 }
209
210 if (!mlkem1024_encode_public_key(&pub, &tmp_buf, &tmp_buf_len)) {
211 warnx("encode_public_key");
212 failed |= 1;
213 }
214 if (sizeof(encoded_public_key) != tmp_buf_len) {
215 warnx("mlkem1024 encoded public key lengths differ");
216 failed |= 1;
217 }
218
219 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, 1024,
220 "encoded public keys") != 0) {
221 warnx("compare_data");
222 failed |= 1;
223 }
224 free(tmp_buf);
225 tmp_buf = NULL;
226
227 MLKEM1024_public_from_private(&pub2, &priv);
228 if (!mlkem1024_encode_public_key(&pub2, &tmp_buf, &tmp_buf_len)) {
229 warnx("mlkem1024_encode_public_key");
230 failed |= 1;
231 }
232 if (sizeof(encoded_public_key) != tmp_buf_len) {
233 warnx("mlkem1024 encoded public key lengths differ");
234 failed |= 1;
235 }
236
237 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, 1024,
238 "encoded public keys") != 0) {
239 warnx("compare_data");
240 failed |= 1;
241 }
242 free(tmp_buf);
243 tmp_buf = NULL;
244
245 if (!mlkem1024_encode_private_key(&priv, &encoded_private_key,
246 &encoded_private_key_len)) {
247 warnx("mlkem1024_encode_private_key");
248 failed |= 1;
249 }
250
251 memcpy(first_two_bytes, encoded_private_key, sizeof(first_two_bytes));
252 memset(encoded_private_key, 0xff, sizeof(first_two_bytes));
253 CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
254
255 /* Parsing should fail because the first coefficient is >= kPrime. */
256 if (MLKEM1024_parse_private_key(&priv2, &cbs)) {
257 warnx("MLKEM1024_parse_private_key should have failed");
258 failed |= 1;
259 }
260
261 memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes));
262 CBS_init(&cbs, encoded_private_key, encoded_private_key_len);
263
264 if (!MLKEM1024_parse_private_key(&priv2, &cbs)) {
265 warnx("MLKEM1024_parse_private_key");
266 failed |= 1;
267 }
268
269 if (!mlkem1024_encode_private_key(&priv2, &tmp_buf, &tmp_buf_len)) {
270 warnx("mlkem1024_encode_private_key");
271 failed |= 1; 146 failed |= 1;
272 } 147 }
273 148
274 if (encoded_private_key_len != tmp_buf_len) { 149 if (encoded_private_key_len != tmp_buf_len) {
275 warnx("mlkem1024 encode private key lengths differ"); 150 warnx("encode private key lengths differ");
276 failed |= 1; 151 failed |= 1;
277 } 152 }
278 153
279 if (compare_data(encoded_private_key, tmp_buf, tmp_buf_len, 1024, 154 if (compare_data(encoded_private_key, tmp_buf, tmp_buf_len,
280 "encoded private key") != 0) { 155 "encoded private key") != 0) {
281 warnx("compare_data"); 156 warnx("compare_data");
282 failed |= 1; 157 failed |= 1;
@@ -285,19 +160,19 @@ MlKem1024UnitTest(void)
285 free(tmp_buf); 160 free(tmp_buf);
286 tmp_buf = NULL; 161 tmp_buf = NULL;
287 162
288 MLKEM1024_encap(ciphertext, shared_secret1, &pub); 163 ctx->encap(ctx->ciphertext, shared_secret1, ctx->pub);
289 MLKEM1024_decap(shared_secret2, ciphertext, MLKEM1024_CIPHERTEXT_BYTES, 164 ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len,
290 &priv); 165 ctx->priv);
291 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, 166 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
292 1024, "shared secrets with priv") != 0) { 167 "shared secrets with priv") != 0) {
293 warnx("compare_data"); 168 warnx("compare_data");
294 failed |= 1; 169 failed |= 1;
295 } 170 }
296 171
297 MLKEM1024_decap(shared_secret2, ciphertext, MLKEM1024_CIPHERTEXT_BYTES, 172 ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len,
298 &priv2); 173 ctx->priv2);
299 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, 174 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES,
300 1024, "shared secrets with priv2") != 0) { 175 "shared secrets with priv2") != 0) {
301 warnx("compare_data"); 176 warnx("compare_data");
302 failed |= 1; 177 failed |= 1;
303 } 178 }
@@ -308,12 +183,56 @@ MlKem1024UnitTest(void)
308} 183}
309 184
310int 185int
311main(int argc, char **argv) 186main(void)
312{ 187{
188 struct MLKEM768_private_key mlkem768_priv, mlkem768_priv2;
189 struct MLKEM768_public_key mlkem768_pub, mlkem768_pub2;
190 uint8_t mlkem768_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
191 uint8_t mlkem768_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
192 struct unittest_ctx mlkem768_test = {
193 .priv = &mlkem768_priv,
194 .pub = &mlkem768_pub,
195 .priv2 = &mlkem768_priv2,
196 .pub2 = &mlkem768_pub2,
197 .encoded_public_key = mlkem768_encoded_public_key,
198 .encoded_public_key_len = sizeof(mlkem768_encoded_public_key),
199 .ciphertext = mlkem768_ciphertext,
200 .ciphertext_len = sizeof(mlkem768_ciphertext),
201 .decap = mlkem768_decap,
202 .encap = mlkem768_encap,
203 .generate_key = mlkem768_generate_key,
204 .parse_private_key = mlkem768_parse_private_key,
205 .parse_public_key = mlkem768_parse_public_key,
206 .encode_private_key = mlkem768_encode_private_key,
207 .encode_public_key = mlkem768_encode_public_key,
208 .public_from_private = mlkem768_public_from_private,
209 };
210 struct MLKEM1024_private_key mlkem1024_priv, mlkem1024_priv2;
211 struct MLKEM1024_public_key mlkem1024_pub, mlkem1024_pub2;
212 uint8_t mlkem1024_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
213 uint8_t mlkem1024_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
214 struct unittest_ctx mlkem1024_test = {
215 .priv = &mlkem1024_priv,
216 .pub = &mlkem1024_pub,
217 .priv2 = &mlkem1024_priv2,
218 .pub2 = &mlkem1024_pub2,
219 .encoded_public_key = mlkem1024_encoded_public_key,
220 .encoded_public_key_len = sizeof(mlkem1024_encoded_public_key),
221 .ciphertext = mlkem1024_ciphertext,
222 .ciphertext_len = sizeof(mlkem1024_ciphertext),
223 .decap = mlkem1024_decap,
224 .encap = mlkem1024_encap,
225 .generate_key = mlkem1024_generate_key,
226 .parse_private_key = mlkem1024_parse_private_key,
227 .parse_public_key = mlkem1024_parse_public_key,
228 .encode_private_key = mlkem1024_encode_private_key,
229 .encode_public_key = mlkem1024_encode_public_key,
230 .public_from_private = mlkem1024_public_from_private,
231 };
313 int failed = 0; 232 int failed = 0;
314 233
315 failed |= MlKem768UnitTest(); 234 failed |= MlKemUnitTest(&mlkem768_test);
316 failed |= MlKem1024UnitTest(); 235 failed |= MlKemUnitTest(&mlkem1024_test);
317 236
318 return failed; 237 return failed;
319} 238}
diff --git a/src/regress/lib/libcrypto/mlkem/parse_test_file.c b/src/regress/lib/libcrypto/mlkem/parse_test_file.c
new file mode 100644
index 0000000000..d79fa3dfd5
--- /dev/null
+++ b/src/regress/lib/libcrypto/mlkem/parse_test_file.c
@@ -0,0 +1,752 @@
1/* $OpenBSD: parse_test_file.c,v 1.1 2024/12/26 00:04:24 tb Exp $ */
2
3/*
4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <assert.h>
22#include <err.h>
23#include <stdarg.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include "bytestring.h"
30
31#include "parse_test_file.h"
32
33struct line_data {
34 uint8_t *data;
35 size_t data_len;
36 CBS cbs;
37 int val;
38};
39
40static struct line_data *
41line_data_new(void)
42{
43 return calloc(1, sizeof(struct line_data));
44}
45
46static void
47line_data_clear(struct line_data *ld)
48{
49 freezero(ld->data, ld->data_len);
50 explicit_bzero(ld, sizeof(*ld));
51}
52
53static void
54line_data_free(struct line_data *ld)
55{
56 if (ld == NULL)
57 return;
58 line_data_clear(ld);
59 free(ld);
60}
61
62static void
63line_data_get_int(struct line_data *ld, int *out)
64{
65 *out = ld->val;
66}
67
68static void
69line_data_get_cbs(struct line_data *ld, CBS *out)
70{
71 CBS_dup(&ld->cbs, out);
72}
73
74static void
75line_data_set_int(struct line_data *ld, int val)
76{
77 ld->val = val;
78}
79
80static int
81line_data_set_from_cbb(struct line_data *ld, CBB *cbb)
82{
83 if (!CBB_finish(cbb, &ld->data, &ld->data_len))
84 return 0;
85
86 CBS_init(&ld->cbs, ld->data, ld->data_len);
87
88 return 1;
89}
90
91struct parse_state {
92 size_t line;
93 size_t test;
94
95 size_t max;
96 size_t cur;
97 struct line_data **data;
98
99 size_t instruction_max;
100 size_t instruction_cur;
101 struct line_data **instruction_data;
102
103 int running_test_case;
104};
105
106static void
107parse_state_init(struct parse_state *ps, size_t max, size_t instruction_max)
108{
109 size_t i;
110
111 assert(max > 0);
112
113 memset(ps, 0, sizeof(*ps));
114 ps->test = 1;
115
116 ps->max = max;
117 if ((ps->data = calloc(max, sizeof(*ps->data))) == NULL)
118 err(1, NULL);
119 for (i = 0; i < max; i++) {
120 if ((ps->data[i] = line_data_new()) == NULL)
121 err(1, NULL);
122 }
123
124 if ((ps->instruction_max = instruction_max) > 0) {
125 if ((ps->instruction_data = calloc(instruction_max,
126 sizeof(*ps->instruction_data))) == NULL)
127 err(1, NULL);
128 for (i = 0; i < instruction_max; i++)
129 if ((ps->instruction_data[i] = line_data_new()) == NULL)
130 err(1, NULL);
131 }
132}
133
134static void
135parse_state_finish(struct parse_state *ps)
136{
137 size_t i;
138
139 for (i = 0; i < ps->max; i++)
140 line_data_free(ps->data[i]);
141 free(ps->data);
142
143 for (i = 0; i < ps->instruction_max; i++)
144 line_data_free(ps->instruction_data[i]);
145 free(ps->instruction_data);
146}
147
148static void
149parse_state_new_line(struct parse_state *ps)
150{
151 ps->line++;
152}
153
154static void
155parse_instruction_advance(struct parse_state *ps)
156{
157 assert(ps->instruction_cur < ps->instruction_max);
158 ps->instruction_cur++;
159}
160
161static void
162parse_state_advance(struct parse_state *ps)
163{
164 assert(ps->cur < ps->max);
165
166 ps->cur++;
167 if ((ps->cur %= ps->max) == 0)
168 ps->test++;
169}
170
171struct parse {
172 struct parse_state state;
173 CBS cbs;
174 char *buf;
175 size_t buf_max;
176 const struct test_parse *tctx;
177 void *ctx;
178
179 const char *fn;
180 FILE *fp;
181};
182
183static int
184parse_instructions_parsed(struct parse *p)
185{
186 return p->state.instruction_max == p->state.instruction_cur;
187}
188
189static void
190parse_advance(struct parse *p)
191{
192 if (!parse_instructions_parsed(p)) {
193 parse_instruction_advance(&p->state);
194 return;
195 }
196 parse_state_advance(&p->state);
197}
198
199static size_t
200parse_max(struct parse *p)
201{
202 return p->state.max;
203}
204
205static size_t
206parse_instruction_max(struct parse *p)
207{
208 return p->state.instruction_max;
209}
210
211static size_t
212parse_cur(struct parse *p)
213{
214 if (!parse_instructions_parsed(p)) {
215 assert(p->state.instruction_cur < p->state.instruction_max);
216 return p->state.instruction_cur;
217 }
218
219 assert(p->state.cur < parse_max(p));
220 return p->state.cur;
221}
222
223static size_t
224parse_must_run_test_case(struct parse *p)
225{
226 return parse_instructions_parsed(p) && parse_max(p) - parse_cur(p) == 1;
227}
228
229static const struct line_spec *
230parse_states(struct parse *p)
231{
232 if (!parse_instructions_parsed(p))
233 return p->tctx->instructions;
234 return p->tctx->states;
235}
236
237static const struct line_spec *
238parse_instruction_states(struct parse *p)
239{
240 return p->tctx->instructions;
241}
242
243static const struct line_spec *
244parse_state(struct parse *p)
245{
246 return &parse_states(p)[parse_cur(p)];
247}
248
249static size_t
250line(struct parse *p)
251{
252 return p->state.line;
253}
254
255static size_t
256test(struct parse *p)
257{
258 return p->state.test;
259}
260
261static const char *
262name(struct parse *p)
263{
264 if (p->state.running_test_case)
265 return "running test case";
266 return parse_state(p)->name;
267}
268
269static const char *
270label(struct parse *p)
271{
272 return parse_state(p)->label;
273}
274
275static const char *
276match(struct parse *p)
277{
278 return parse_state(p)->match;
279}
280
281static enum line
282parse_line_type(struct parse *p)
283{
284 return parse_state(p)->type;
285}
286
287static void
288parse_vinfo(struct parse *p, const char *fmt, va_list ap)
289{
290 fprintf(stderr, "%s:%zu test #%zu (%s): ",
291 p->fn, line(p), test(p), name(p));
292 vfprintf(stderr, fmt, ap);
293 fprintf(stderr, "\n");
294}
295
296void
297parse_info(struct parse *p, const char *fmt, ...)
298{
299 va_list ap;
300
301 va_start(ap, fmt);
302 parse_vinfo(p, fmt, ap);
303 va_end(ap);
304}
305
306void
307parse_errx(struct parse *p, const char *fmt, ...)
308{
309 va_list ap;
310
311 va_start(ap, fmt);
312 parse_vinfo(p, fmt, ap);
313 va_end(ap);
314
315 exit(1);
316}
317
318int
319parse_length_equal(struct parse *p, const char *descr, size_t want, size_t got)
320{
321 if (want == got)
322 return 1;
323
324 parse_info(p, "%s length: want %zu, got %zu", descr, want, got);
325 return 0;
326}
327
328static void
329hexdump(const uint8_t *buf, size_t len, const uint8_t *compare)
330{
331 const char *mark = "", *newline;
332 size_t i;
333
334 for (i = 1; i <= len; i++) {
335 if (compare != NULL)
336 mark = (buf[i - 1] != compare[i - 1]) ? "*" : " ";
337 newline = i % 8 ? "" : "\n";
338 fprintf(stderr, " %s0x%02x,%s", mark, buf[i - 1], newline);
339 }
340 if ((len % 8) != 0)
341 fprintf(stderr, "\n");
342}
343
344int
345parse_data_equal(struct parse *p, const char *descr, CBS *want,
346 const uint8_t *got, size_t got_len)
347{
348 if (!parse_length_equal(p, descr, CBS_len(want), got_len))
349 return 0;
350 if (CBS_mem_equal(want, got, got_len))
351 return 1;
352
353 parse_info(p, "%s differs", descr);
354 fprintf(stderr, "want:\n");
355 hexdump(CBS_data(want), CBS_len(want), got);
356 fprintf(stderr, "got:\n");
357 hexdump(got, got_len, CBS_data(want));
358 fprintf(stderr, "\n");
359
360 return 0;
361}
362
363static void
364parse_line_data_clear(struct parse *p)
365{
366 size_t i;
367
368 for (i = 0; i < parse_max(p); i++)
369 line_data_clear(p->state.data[i]);
370}
371
372static struct line_data **
373parse_state_data(struct parse *p)
374{
375 if (!parse_instructions_parsed(p))
376 return p->state.instruction_data;
377 return p->state.data;
378}
379
380static void
381parse_state_set_int(struct parse *p, int val)
382{
383 if (parse_line_type(p) != LINE_STRING_MATCH)
384 parse_errx(p, "%s: want %d, got %d", __func__,
385 LINE_STRING_MATCH, parse_line_type(p));
386 line_data_set_int(parse_state_data(p)[parse_cur(p)], val);
387}
388
389static void
390parse_state_set_from_cbb(struct parse *p, CBB *cbb)
391{
392 if (parse_line_type(p) != LINE_HEX)
393 parse_errx(p, "%s: want %d, got %d", __func__,
394 LINE_STRING_MATCH, parse_line_type(p));
395 if (!line_data_set_from_cbb(parse_state_data(p)[parse_cur(p)], cbb))
396 parse_errx(p, "line_data_set_from_cbb");
397}
398
399int
400parse_get_int(struct parse *p, size_t idx, int *out)
401{
402 assert(parse_must_run_test_case(p));
403 assert(idx < parse_max(p));
404 assert(parse_states(p)[idx].type == LINE_STRING_MATCH);
405
406 line_data_get_int(p->state.data[idx], out);
407
408 return 1;
409}
410
411int
412parse_get_cbs(struct parse *p, size_t idx, CBS *out)
413{
414 assert(parse_must_run_test_case(p));
415 assert(idx < parse_max(p));
416 assert(parse_states(p)[idx].type == LINE_HEX);
417
418 line_data_get_cbs(p->state.data[idx], out);
419
420 return 1;
421}
422
423int
424parse_instruction_get_int(struct parse *p, size_t idx, int *out)
425{
426 assert(parse_must_run_test_case(p));
427 assert(idx < parse_instruction_max(p));
428 assert(parse_instruction_states(p)[idx].type == LINE_STRING_MATCH);
429
430 line_data_get_int(p->state.instruction_data[idx], out);
431
432 return 1;
433}
434
435int
436parse_instruction_get_cbs(struct parse *p, size_t idx, CBS *out)
437{
438 assert(parse_must_run_test_case(p));
439 assert(idx < parse_instruction_max(p));
440 assert(parse_instruction_states(p)[idx].type == LINE_HEX);
441
442 line_data_get_cbs(p->state.instruction_data[idx], out);
443
444 return 1;
445}
446
447static int
448CBS_peek_bytes(CBS *cbs, CBS *out, size_t len)
449{
450 CBS dup;
451
452 CBS_dup(cbs, &dup);
453 return CBS_get_bytes(&dup, out, len);
454}
455
456static int
457parse_peek_string_cbs(struct parse *p, const char *str)
458{
459 CBS cbs;
460 size_t len = strlen(str);
461
462 if (!CBS_peek_bytes(&p->cbs, &cbs, len))
463 parse_errx(p, "CBS_peek_data");
464
465 return CBS_mem_equal(&cbs, (const uint8_t *)str, len);
466}
467
468static int
469parse_get_string_cbs(struct parse *p, const char *str)
470{
471 CBS cbs;
472 size_t len = strlen(str);
473
474 if (!CBS_get_bytes(&p->cbs, &cbs, len))
475 parse_errx(p, "CBS_get_bytes");
476
477 return CBS_mem_equal(&cbs, (const uint8_t *)str, len);
478}
479
480static int
481parse_get_string_end_cbs(struct parse *p, const char *str)
482{
483 CBS cbs;
484 int equal = 1;
485
486 CBS_init(&cbs, (const uint8_t *)str, strlen(str));
487
488 if (CBS_len(&p->cbs) < CBS_len(&cbs))
489 parse_errx(p, "line too short to match %s", str);
490
491 while (CBS_len(&cbs) > 0) {
492 uint8_t want, got;
493
494 if (!CBS_get_last_u8(&cbs, &want))
495 parse_errx(p, "CBS_get_last_u8");
496 if (!CBS_get_last_u8(&p->cbs, &got))
497 parse_errx(p, "CBS_get_last_u8");
498 if (want != got)
499 equal = 0;
500 }
501
502 return equal;
503}
504
505static void
506parse_check_label_matches(struct parse *p)
507{
508 const char *sep = ": ";
509
510 if (!parse_get_string_cbs(p, label(p)))
511 parse_errx(p, "label mismatch %s", label(p));
512
513 /* Now we expect either ": " or " = ". */
514 if (!parse_peek_string_cbs(p, sep))
515 sep = " = ";
516 if (!parse_get_string_cbs(p, sep))
517 parse_errx(p, "error getting \"%s\"", sep);
518}
519
520static int
521parse_empty_or_comment_line(struct parse *p)
522{
523 if (CBS_len(&p->cbs) == 0) {
524 return 1;
525 }
526 if (parse_peek_string_cbs(p, "#")) {
527 if (!CBS_skip(&p->cbs, CBS_len(&p->cbs)))
528 parse_errx(p, "CBS_skip");
529 return 1;
530 }
531 return 0;
532}
533
534static void
535parse_string_match_line(struct parse *p)
536{
537 int string_matches;
538
539 parse_check_label_matches(p);
540
541 string_matches = parse_get_string_cbs(p, match(p));
542 parse_state_set_int(p, string_matches);
543
544 if (!string_matches) {
545 if (!CBS_skip(&p->cbs, CBS_len(&p->cbs)))
546 parse_errx(p, "CBS_skip");
547 }
548}
549
550static int
551parse_get_hex_nibble_cbs(CBS *cbs, uint8_t *out_nibble)
552{
553 uint8_t c;
554
555 if (!CBS_get_u8(cbs, &c))
556 return 0;
557
558 if (c >= '0' && c <= '9') {
559 *out_nibble = c - '0';
560 return 1;
561 }
562 if (c >= 'a' && c <= 'f') {
563 *out_nibble = c - 'a' + 10;
564 return 1;
565 }
566 if (c >= 'A' && c <= 'F') {
567 *out_nibble = c - 'A' + 10;
568 return 1;
569 }
570
571 return 0;
572}
573
574static void
575parse_hex_line(struct parse *p)
576{
577 CBB cbb;
578
579 parse_check_label_matches(p);
580
581 if (!CBB_init(&cbb, 0))
582 parse_errx(p, "CBB_init");
583
584 while (CBS_len(&p->cbs) > 0) {
585 uint8_t hi, lo;
586
587 if (!parse_get_hex_nibble_cbs(&p->cbs, &hi))
588 parse_errx(p, "parse_get_hex_nibble_cbs");
589 if (!parse_get_hex_nibble_cbs(&p->cbs, &lo))
590 parse_errx(p, "parse_get_hex_nibble_cbs");
591
592 if (!CBB_add_u8(&cbb, hi << 4 | lo))
593 parse_errx(p, "CBB_add_u8");
594 }
595
596 parse_state_set_from_cbb(p, &cbb);
597}
598
599static void
600parse_maybe_prepare_instruction_line(struct parse *p)
601{
602 if (parse_instructions_parsed(p))
603 return;
604
605 /* Should not happen due to parse_empty_or_comment_line(). */
606 if (CBS_len(&p->cbs) == 0)
607 parse_errx(p, "empty instruction line");
608
609 if (!parse_peek_string_cbs(p, "["))
610 parse_errx(p, "expected instruction line");
611 if (!parse_get_string_cbs(p, "["))
612 parse_errx(p, "expected start of instruction line");
613 if (!parse_get_string_end_cbs(p, "]"))
614 parse_errx(p, "expected end of instruction line");
615}
616
617static void
618parse_check_line_consumed(struct parse *p)
619{
620 if (CBS_len(&p->cbs) > 0)
621 parse_errx(p, "%zu unprocessed bytes", CBS_len(&p->cbs));
622}
623
624static int
625parse_run_test_case(struct parse *p)
626{
627 const struct test_parse *tctx = p->tctx;
628
629 p->state.running_test_case = 1;
630 return tctx->run_test_case(p->ctx);
631}
632
633static void
634parse_reinit(struct parse *p)
635{
636 const struct test_parse *tctx = p->tctx;
637
638 p->state.running_test_case = 0;
639 parse_line_data_clear(p);
640 tctx->finish(p->ctx);
641 tctx->init(p->ctx, p);
642}
643
644static int
645parse_maybe_run_test_case(struct parse *p)
646{
647 int failed = 0;
648
649 if (parse_must_run_test_case(p)) {
650 failed |= parse_run_test_case(p);
651 parse_reinit(p);
652 }
653
654 parse_advance(p);
655
656 return failed;
657}
658
659static int
660parse_process_line(struct parse *p)
661{
662 if (parse_empty_or_comment_line(p))
663 return 0;
664
665 parse_maybe_prepare_instruction_line(p);
666
667 switch (parse_line_type(p)) {
668 case LINE_STRING_MATCH:
669 parse_string_match_line(p);
670 break;
671 case LINE_HEX:
672 parse_hex_line(p);
673 break;
674 default:
675 parse_errx(p, "unknown line type %d", parse_line_type(p));
676 }
677 parse_check_line_consumed(p);
678
679 return parse_maybe_run_test_case(p);
680}
681
682static void
683parse_init(struct parse *p, const char *fn, const struct test_parse *tctx,
684 void *ctx)
685{
686 FILE *fp;
687
688 memset(p, 0, sizeof(*p));
689
690 if ((fp = fopen(fn, "r")) == NULL)
691 err(1, "error opening %s", fn);
692
693 /* Poor man's basename since POSIX basename is stupid. */
694 if ((p->fn = strrchr(fn, '/')) != NULL)
695 p->fn++;
696 else
697 p->fn = fn;
698
699 p->fp = fp;
700 parse_state_init(&p->state, tctx->num_states, tctx->num_instructions);
701 p->tctx = tctx;
702 p->ctx = ctx;
703 tctx->init(ctx, p);
704}
705
706static int
707parse_next_line(struct parse *p)
708{
709 ssize_t len;
710 uint8_t u8;
711
712 if ((len = getline(&p->buf, &p->buf_max, p->fp)) == -1)
713 return 0;
714
715 CBS_init(&p->cbs, (const uint8_t *)p->buf, len);
716 parse_state_new_line(&p->state);
717
718 if (!CBS_get_last_u8(&p->cbs, &u8))
719 parse_errx(p, "CBS_get_last_u8");
720
721 assert(u8 == '\n');
722
723 return 1;
724}
725
726static void
727parse_finish(struct parse *p)
728{
729 parse_state_finish(&p->state);
730
731 free(p->buf);
732
733 if (ferror(p->fp))
734 err(1, "%s", p->fn);
735 fclose(p->fp);
736}
737
738int
739parse_test_file(const char *fn, const struct test_parse *tctx, void *ctx)
740{
741 struct parse p;
742 int failed = 0;
743
744 parse_init(&p, fn, tctx, ctx);
745
746 while (parse_next_line(&p))
747 failed |= parse_process_line(&p);
748
749 parse_finish(&p);
750
751 return failed;
752}
diff --git a/src/regress/lib/libcrypto/mlkem/parse_test_file.h b/src/regress/lib/libcrypto/mlkem/parse_test_file.h
new file mode 100644
index 0000000000..772b7f6232
--- /dev/null
+++ b/src/regress/lib/libcrypto/mlkem/parse_test_file.h
@@ -0,0 +1,83 @@
1/* $OpenBSD: parse_test_file.h,v 1.1 2024/12/26 00:04:24 tb Exp $ */
2
3/*
4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#ifndef PARSE_TEST_FILE_H
20#define PARSE_TEST_FILE_H
21
22#include <stdint.h>
23#include <stdio.h>
24
25#include "bytestring.h"
26
27#if defined(__cplusplus)
28extern "C" {
29#endif
30
31struct parse;
32
33enum line {
34 LINE_STRING_MATCH, /* Checks if string after label matches. */
35 LINE_HEX, /* Parses hex into cbb from type2cbb. */
36};
37
38struct line_spec {
39 int state;
40 enum line type;
41 const char *name;
42 const char *label; /* followed by ": " or " = " */
43 const char *match; /* only for LINE_STRING_MATCH */
44};
45
46struct test_parse {
47 const struct line_spec *states;
48 size_t num_states;
49
50 const struct line_spec *instructions;
51 size_t num_instructions;
52
53 int (*init)(void *ctx, void *parse_ctx);
54 void (*finish)(void *ctx);
55
56 int (*run_test_case)(void *ctx);
57};
58
59int parse_test_file(const char *fn, const struct test_parse *lctx, void *ctx);
60
61int parse_get_int(struct parse *p, size_t idx, int *out);
62int parse_get_cbs(struct parse *p, size_t idx, CBS *out);
63
64int parse_instruction_get_int(struct parse *p, size_t idx, int *out);
65int parse_instruction_get_cbs(struct parse *p, size_t idx, CBS *out);
66
67int parse_length_equal(struct parse *p, const char *descr, size_t want, size_t got);
68int parse_data_equal(struct parse *p, const char *descr, CBS *want,
69 const uint8_t *got, size_t len);
70
71void parse_info(struct parse *ctx, const char *fmt, ...)
72 __attribute__((__format__ (printf, 2, 3)))
73 __attribute__((__nonnull__ (2)));
74void parse_errx(struct parse *ctx, const char *fmt, ...)
75 __attribute__((__format__ (printf, 2, 3)))
76 __attribute__((__nonnull__ (2)))
77 __attribute__((__noreturn__));
78
79#ifdef __cplusplus
80}
81#endif
82
83#endif /* PARSE_TEST_FILE_H */