summaryrefslogtreecommitdiff
path: root/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
diff options
context:
space:
mode:
authorbeck <>2025-08-14 15:48:48 +0000
committerbeck <>2025-08-14 15:48:48 +0000
commit6452fa9fc6f33dac80ee572764b9fe29a469f8ce (patch)
tree0956ae670e4f193442bcf99d2b1fb70a43a6b5b5 /src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
parent9bef27f78e41e8026f1d588e4e36e385061f3deb (diff)
downloadopenbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.tar.gz
openbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.tar.bz2
openbsd-6452fa9fc6f33dac80ee572764b9fe29a469f8ce.zip
Add a reasonable ML-KEM API for public use.
Adapt the tests to use this API. This does not yet make the symbols public in Symbols.list which will happen shortly with a bump. This includes some partial rototilling of the non-public interfaces which will be shortly continued when the internal code is deduplicated to not have multiple copies for ML-KEM 768 and ML-KEM 1024 (which is just an artifact of unravelling the boring C++ code). ok jsing@, tb@
Diffstat (limited to 'src/regress/lib/libcrypto/mlkem/mlkem_unittest.c')
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_unittest.c302
1 files changed, 174 insertions, 128 deletions
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c b/src/regress/lib/libcrypto/mlkem/mlkem_unittest.c
index adb1c47d8e..417d40555f 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.11 2025/05/21 03:46:20 tb Exp $ */ 1/* $OpenBSD: mlkem_unittest.c,v 1.12 2025/08/14 15:48:48 beck 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,95 +27,161 @@
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_marshal_private_key_fn marshal_private_key;
45 mlkem_marshal_public_key_fn marshal_public_key;
46 mlkem_public_from_private_fn public_from_private;
47};
48
49static int 30static int
50MlKemUnitTest(struct unittest_ctx *ctx) 31MlKemUnitTest(int rank)
51{ 32{
52 uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES]; 33 MLKEM_private_key *priv = NULL, *priv2 = NULL, *priv3 = NULL;
53 uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES]; 34 MLKEM_public_key *pub = NULL, *pub2 = NULL, *pub3 = NULL;
35 uint8_t *encoded_public_key = NULL, *ciphertext = NULL,
36 *shared_secret2 = NULL, *shared_secret1 = NULL,
37 *encoded_private_key = NULL, *tmp_buf = NULL, *seed_buf = NULL;
38 size_t encoded_public_key_len, ciphertext_len,
39 encoded_private_key_len, tmp_buf_len;
54 uint8_t first_two_bytes[2]; 40 uint8_t first_two_bytes[2];
55 uint8_t *encoded_private_key = NULL, *tmp_buf = NULL; 41 size_t s_len = 0;
56 size_t encoded_private_key_len, tmp_buf_len;
57 int failed = 0; 42 int failed = 0;
58 43
59 if (!ctx->generate_key(ctx->encoded_public_key, NULL, ctx->priv)) { 44 if ((pub = MLKEM_public_key_new(rank)) == NULL) {
45 warnx("public_key_new");
46 failed |= 1;
47 }
48
49 if ((pub2 = MLKEM_public_key_new(rank)) == NULL) {
50 warnx("public_key_new");
51 failed |= 1;
52 }
53
54 if ((priv = MLKEM_private_key_new(rank)) == NULL) {
55 warnx("private_key_new");
56 failed |= 1;
57 }
58
59 if ((priv2 = MLKEM_private_key_new(rank)) == NULL) {
60 warnx("private_key_new");
61 failed |= 1;
62 }
63
64 if (!MLKEM_generate_key(priv, &encoded_public_key,
65 &encoded_public_key_len, &seed_buf, &s_len)) {
60 warnx("generate_key failed"); 66 warnx("generate_key failed");
61 failed |= 1; 67 failed |= 1;
62 } 68 }
63 69
64 memcpy(first_two_bytes, ctx->encoded_public_key, sizeof(first_two_bytes)); 70 if (s_len != MLKEM_SEED_LENGTH) {
65 memset(ctx->encoded_public_key, 0xff, sizeof(first_two_bytes)); 71 warnx("seed length %zu != %d", s_len, MLKEM_SEED_LENGTH);
72 failed |= 1;
73 }
74
75 if ((priv3 = MLKEM_private_key_new(rank)) == NULL) {
76 warnx("private_key_new");
77 failed |= 1;
78 }
79
80 if ((pub3 = MLKEM_public_key_new(rank)) == NULL) {
81 warnx("public_key_new");
82 failed |= 1;
83 }
84
85 if (!MLKEM_private_key_from_seed(priv3, seed_buf, s_len)) {
86 warnx("private_key_from_seed failed");
87 failed |= 1;
88 }
89
90 free(seed_buf);
91 seed_buf = NULL;
92
93 if (!MLKEM_public_from_private(priv3, pub3)) {
94 warnx("public_from_private");
95 failed |= 1;
96 }
97
98 memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes));
99 memset(encoded_public_key, 0xff, sizeof(first_two_bytes));
66 100
67 /* Parsing should fail because the first coefficient is >= kPrime. */ 101 /* Parsing should fail because the first coefficient is >= kPrime. */
68 if (ctx->parse_public_key(ctx->pub, ctx->encoded_public_key, 102 if (MLKEM_parse_public_key(pub, encoded_public_key,
69 ctx->encoded_public_key_len)) { 103 encoded_public_key_len)) {
70 warnx("parse_public_key should have failed"); 104 warnx("parse_public_key should have failed");
71 failed |= 1; 105 failed |= 1;
72 } 106 }
73 107
74 memcpy(ctx->encoded_public_key, first_two_bytes, sizeof(first_two_bytes)); 108 memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes));
75 if (!ctx->parse_public_key(ctx->pub, ctx->encoded_public_key, 109
76 ctx->encoded_public_key_len)) { 110 MLKEM_public_key_free(pub);
77 warnx("MLKEM768_parse_public_key"); 111 if ((pub = MLKEM_public_key_new(rank)) == NULL) {
112 warnx("public_key_new");
113 failed |= 1;
114 }
115 if (!MLKEM_parse_public_key(pub, encoded_public_key,
116 encoded_public_key_len)) {
117 warnx("MLKEM_parse_public_key");
118 failed |= 1;
119 }
120
121 if (!MLKEM_marshal_public_key(pub, &tmp_buf, &tmp_buf_len)) {
122 warnx("marshal_public_key");
123 failed |= 1;
124 }
125 if (encoded_public_key_len != tmp_buf_len) {
126 warnx("encoded public key lengths differ %d != %d",
127 (int) encoded_public_key_len, (int) tmp_buf_len);
128 failed |= 1;
129 }
130
131 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len,
132 "encoded public keys") != 0) {
133 warnx("compare_data");
78 failed |= 1; 134 failed |= 1;
79 } 135 }
136 free(tmp_buf);
137 tmp_buf = NULL;
138 tmp_buf_len = 0;
80 139
81 if (!ctx->marshal_public_key(ctx->pub, &tmp_buf, &tmp_buf_len)) { 140 if (!MLKEM_marshal_public_key(pub3, &tmp_buf, &tmp_buf_len)) {
82 warnx("marshal_public_key"); 141 warnx("marshal_public_key");
83 failed |= 1; 142 failed |= 1;
84 } 143 }
85 if (ctx->encoded_public_key_len != tmp_buf_len) { 144 if (encoded_public_key_len != tmp_buf_len) {
86 warnx("encoded public key lengths differ"); 145 warnx("encoded public key lengths differ %d != %d",
146 (int) encoded_public_key_len, (int) tmp_buf_len);
87 failed |= 1; 147 failed |= 1;
88 } 148 }
89 149
90 if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len, 150 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len,
91 "encoded public keys") != 0) { 151 "encoded public keys") != 0) {
92 warnx("compare_data"); 152 warnx("compare_data");
93 failed |= 1; 153 failed |= 1;
94 } 154 }
95 free(tmp_buf); 155 free(tmp_buf);
96 tmp_buf = NULL; 156 tmp_buf = NULL;
157 tmp_buf_len = 0;
97 158
98 ctx->public_from_private(ctx->pub2, ctx->priv); 159 if (!MLKEM_public_from_private(priv, pub2)) {
99 if (!ctx->marshal_public_key(ctx->pub2, &tmp_buf, &tmp_buf_len)) { 160 warnx("public_from_private");
161 failed |= 1;
162 }
163 if (!MLKEM_marshal_public_key(pub2, &tmp_buf, &tmp_buf_len)) {
100 warnx("marshal_public_key"); 164 warnx("marshal_public_key");
101 failed |= 1; 165 failed |= 1;
102 } 166 }
103 if (ctx->encoded_public_key_len != tmp_buf_len) { 167 if (encoded_public_key_len != tmp_buf_len) {
104 warnx("encoded public key lengths differ"); 168 warnx("encoded public key lengths differ %d %d",
169 (int) encoded_public_key_len, (int) tmp_buf_len);
105 failed |= 1; 170 failed |= 1;
106 } 171 }
107 172
108 if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len, 173 if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len,
109 "encoded public keys") != 0) { 174 "encoded public keys") != 0) {
110 warnx("compare_data"); 175 warnx("compare_data");
111 failed |= 1; 176 failed |= 1;
112 } 177 }
113 free(tmp_buf); 178 free(tmp_buf);
114 tmp_buf = NULL; 179 tmp_buf = NULL;
180 tmp_buf_len = 0;
115 181
116 if (!ctx->marshal_private_key(ctx->priv, &encoded_private_key, 182 if (!MLKEM_marshal_private_key(priv, &encoded_private_key,
117 &encoded_private_key_len)) { 183 &encoded_private_key_len)) {
118 warnx("mlkem768_encode_private_key"); 184 warnx("marshal_private_key");
119 failed |= 1; 185 failed |= 1;
120 } 186 }
121 187
@@ -123,27 +189,34 @@ MlKemUnitTest(struct unittest_ctx *ctx)
123 memset(encoded_private_key, 0xff, sizeof(first_two_bytes)); 189 memset(encoded_private_key, 0xff, sizeof(first_two_bytes));
124 190
125 /* Parsing should fail because the first coefficient is >= kPrime. */ 191 /* Parsing should fail because the first coefficient is >= kPrime. */
126 if (ctx->parse_private_key(ctx->priv2, encoded_private_key, 192 if (MLKEM_parse_private_key(priv2, encoded_private_key,
127 encoded_private_key_len)) { 193 encoded_private_key_len)) {
128 warnx("MLKEM768_parse_private_key should have failed"); 194 warnx("parse_private_key should have failed");
129 failed |= 1; 195 failed |= 1;
130 } 196 }
131 197
132 memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes)); 198 memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes));
133 199
134 if (!ctx->parse_private_key(ctx->priv2, encoded_private_key, 200 MLKEM_private_key_free(priv2);
201 priv2 = NULL;
202
203 if ((priv2 = MLKEM_private_key_new(rank)) == NULL) {
204 warnx("private_key_new");
205 failed |= 1;
206 }
207 if (!MLKEM_parse_private_key(priv2, encoded_private_key,
135 encoded_private_key_len)) { 208 encoded_private_key_len)) {
136 warnx("MLKEM768_parse_private_key"); 209 warnx("parse_private_key");
137 failed |= 1; 210 failed |= 1;
138 } 211 }
139 212
140 if (!ctx->marshal_private_key(ctx->priv2, &tmp_buf, &tmp_buf_len)) { 213 if (!MLKEM_marshal_private_key(priv2, &tmp_buf, &tmp_buf_len)) {
141 warnx("encode_private_key"); 214 warnx("marshal_private_key");
142 failed |= 1; 215 failed |= 1;
143 } 216 }
144 217
145 if (encoded_private_key_len != tmp_buf_len) { 218 if (encoded_private_key_len != tmp_buf_len) {
146 warnx("encode private key lengths differ"); 219 warnx("encoded private key lengths differ");
147 failed |= 1; 220 failed |= 1;
148 } 221 }
149 222
@@ -156,106 +229,79 @@ MlKemUnitTest(struct unittest_ctx *ctx)
156 free(tmp_buf); 229 free(tmp_buf);
157 tmp_buf = NULL; 230 tmp_buf = NULL;
158 231
159 ctx->encap(ctx->ciphertext, shared_secret1, ctx->pub); 232 if (!MLKEM_encap(pub, &ciphertext, &ciphertext_len, &shared_secret1,
160 if (!ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len, 233 &s_len)) {
161 ctx->priv)) { 234 warnx("encap failed using pub");
235 failed |= 1;
236 }
237
238 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
239 warnx("seed length %zu != %d", s_len,
240 MLKEM_SHARED_SECRET_LENGTH);
241 failed |= 1;
242 }
243
244 if (!MLKEM_decap(priv, ciphertext, ciphertext_len,
245 &shared_secret2, &s_len)) {
162 warnx("decap() failed using priv"); 246 warnx("decap() failed using priv");
163 failed |= 1; 247 failed |= 1;
164 } 248 }
165 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, 249
250 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
251 warnx("seed length %zu != %d", s_len,
252 MLKEM_SHARED_SECRET_LENGTH);
253 failed |= 1;
254 }
255
256 if (compare_data(shared_secret1, shared_secret2, s_len,
166 "shared secrets with priv") != 0) { 257 "shared secrets with priv") != 0) {
167 warnx("compare_data"); 258 warnx("compare_data");
168 failed |= 1; 259 failed |= 1;
169 } 260 }
170 261
171 if (!ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len, 262 free(shared_secret2);
172 ctx->priv2)) { 263 shared_secret2 = NULL;
264
265 if (!MLKEM_decap(priv2, ciphertext, ciphertext_len,
266 &shared_secret2, &s_len)){
173 warnx("decap() failed using priv2"); 267 warnx("decap() failed using priv2");
174 failed |= 1; 268 failed |= 1;
175 } 269 }
176 if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, 270
271 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
272 warnx("seed length %zu != %d", s_len,
273 MLKEM_SHARED_SECRET_LENGTH);
274 failed |= 1;
275 }
276
277 if (compare_data(shared_secret1, shared_secret2, s_len,
177 "shared secrets with priv2") != 0) { 278 "shared secrets with priv2") != 0) {
178 warnx("compare_data"); 279 warnx("compare_data");
179 failed |= 1; 280 failed |= 1;
180 } 281 }
181 282
283 MLKEM_public_key_free(pub);
284 MLKEM_public_key_free(pub2);
285 MLKEM_public_key_free(pub3);
286 MLKEM_private_key_free(priv);
287 MLKEM_private_key_free(priv2);
288 MLKEM_private_key_free(priv3);
289 free(encoded_public_key);
290 free(ciphertext);
182 free(encoded_private_key); 291 free(encoded_private_key);
292 free(shared_secret1);
293 free(shared_secret2);
183 294
184 return failed; 295 return failed;
185} 296}
186 297
187static int
188mlkem768_unittest(void)
189{
190 struct MLKEM768_private_key mlkem768_priv, mlkem768_priv2;
191 struct MLKEM768_public_key mlkem768_pub, mlkem768_pub2;
192 uint8_t mlkem768_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
193 uint8_t mlkem768_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
194 struct unittest_ctx mlkem768_test = {
195 .priv = &mlkem768_priv,
196 .pub = &mlkem768_pub,
197 .priv2 = &mlkem768_priv2,
198 .pub2 = &mlkem768_pub2,
199 .encoded_public_key = mlkem768_encoded_public_key,
200 .encoded_public_key_len = sizeof(mlkem768_encoded_public_key),
201 .ciphertext = mlkem768_ciphertext,
202 .ciphertext_len = sizeof(mlkem768_ciphertext),
203 .decap = mlkem768_decap,
204 .encap = mlkem768_encap,
205 .generate_key = mlkem768_generate_key,
206 .parse_private_key = mlkem768_parse_private_key,
207 .parse_public_key = mlkem768_parse_public_key,
208 .marshal_private_key = mlkem768_marshal_private_key,
209 .marshal_public_key = mlkem768_marshal_public_key,
210 .public_from_private = mlkem768_public_from_private,
211 };
212
213 return MlKemUnitTest(&mlkem768_test);
214}
215
216static int
217mlkem1024_unittest(void)
218{
219 struct MLKEM1024_private_key mlkem1024_priv, mlkem1024_priv2;
220 struct MLKEM1024_public_key mlkem1024_pub, mlkem1024_pub2;
221 uint8_t mlkem1024_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
222 uint8_t mlkem1024_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
223 struct unittest_ctx mlkem1024_test = {
224 .priv = &mlkem1024_priv,
225 .pub = &mlkem1024_pub,
226 .priv2 = &mlkem1024_priv2,
227 .pub2 = &mlkem1024_pub2,
228 .encoded_public_key = mlkem1024_encoded_public_key,
229 .encoded_public_key_len = sizeof(mlkem1024_encoded_public_key),
230 .ciphertext = mlkem1024_ciphertext,
231 .ciphertext_len = sizeof(mlkem1024_ciphertext),
232 .decap = mlkem1024_decap,
233 .encap = mlkem1024_encap,
234 .generate_key = mlkem1024_generate_key,
235 .parse_private_key = mlkem1024_parse_private_key,
236 .parse_public_key = mlkem1024_parse_public_key,
237 .marshal_private_key = mlkem1024_marshal_private_key,
238 .marshal_public_key = mlkem1024_marshal_public_key,
239 .public_from_private = mlkem1024_public_from_private,
240 };
241
242 return MlKemUnitTest(&mlkem1024_test);
243}
244
245int 298int
246main(void) 299main(void)
247{ 300{
248 int failed = 0; 301 int ret = 0;
249 302
250 /* 303 ret |= MlKemUnitTest(RANK768);
251 * XXX - this is split into two helper functions since having a few 304 ret |= MlKemUnitTest(RANK1024);
252 * ML-KEM key blobs on the stack makes Emscripten's stack explode,
253 * leading to inscrutable silent failures unless ASAN is enabled.
254 * Go figure.
255 */
256 305
257 failed |= mlkem768_unittest(); 306 return ret;
258 failed |= mlkem1024_unittest();
259
260 return failed;
261} 307}