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