summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/libcrypto/Makefile5
-rw-r--r--src/lib/libcrypto/hidden/openssl/mlkem.h40
-rw-r--r--src/lib/libcrypto/mlkem/mlkem.c638
-rw-r--r--src/lib/libcrypto/mlkem/mlkem.h329
-rw-r--r--src/lib/libcrypto/mlkem/mlkem1024.c71
-rw-r--r--src/lib/libcrypto/mlkem/mlkem768.c73
-rw-r--r--src/lib/libcrypto/mlkem/mlkem_internal.h331
-rw-r--r--src/lib/libcrypto/mlkem/mlkem_key.c200
-rw-r--r--src/lib/libssl/ssl_rsa.c4
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c179
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests.c292
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c158
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h58
-rw-r--r--src/regress/lib/libcrypto/mlkem/mlkem_unittest.c302
14 files changed, 1783 insertions, 897 deletions
diff --git a/src/lib/libcrypto/Makefile b/src/lib/libcrypto/Makefile
index b0ab507983..459b0c9235 100644
--- a/src/lib/libcrypto/Makefile
+++ b/src/lib/libcrypto/Makefile
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile,v 1.241 2025/07/16 15:59:26 tb Exp $ 1# $OpenBSD: Makefile,v 1.242 2025/08/14 15:48:48 beck Exp $
2 2
3LIB= crypto 3LIB= crypto
4LIBREBUILD=y 4LIBREBUILD=y
@@ -374,8 +374,10 @@ SRCS+= md4.c
374SRCS+= md5.c 374SRCS+= md5.c
375 375
376# mlkem/ 376# mlkem/
377SRCS+= mlkem.c
377SRCS+= mlkem768.c 378SRCS+= mlkem768.c
378SRCS+= mlkem1024.c 379SRCS+= mlkem1024.c
380SRCS+= mlkem_key.c
379 381
380# modes/ 382# modes/
381SRCS+= cbc128.c 383SRCS+= cbc128.c
@@ -668,6 +670,7 @@ HDRS=\
668 ${LCRYPTO_SRC}/lhash/lhash.h \ 670 ${LCRYPTO_SRC}/lhash/lhash.h \
669 ${LCRYPTO_SRC}/md4/md4.h \ 671 ${LCRYPTO_SRC}/md4/md4.h \
670 ${LCRYPTO_SRC}/md5/md5.h \ 672 ${LCRYPTO_SRC}/md5/md5.h \
673 ${LCRYPTO_SRC}/mlkem/mlkem.h \
671 ${LCRYPTO_SRC}/modes/modes.h \ 674 ${LCRYPTO_SRC}/modes/modes.h \
672 ${LCRYPTO_SRC}/objects/objects.h \ 675 ${LCRYPTO_SRC}/objects/objects.h \
673 ${LCRYPTO_SRC}/ocsp/ocsp.h \ 676 ${LCRYPTO_SRC}/ocsp/ocsp.h \
diff --git a/src/lib/libcrypto/hidden/openssl/mlkem.h b/src/lib/libcrypto/hidden/openssl/mlkem.h
index 8cd80eb3af..3807b3fa1e 100644
--- a/src/lib/libcrypto/hidden/openssl/mlkem.h
+++ b/src/lib/libcrypto/hidden/openssl/mlkem.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: mlkem.h,v 1.4 2024/12/20 15:10:31 tb Exp $ */ 1/* $OpenBSD: mlkem.h,v 1.5 2025/08/14 15:48:48 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2024 Bob Beck <beck@obtuse.com> 3 * Copyright (c) 2025 Bob Beck <beck@openbsd.org>
4 * 4 *
5 * Permission to use, copy, modify, and distribute this software for any 5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above 6 * purpose with or without fee is hereby granted, provided that the above
@@ -18,9 +18,6 @@
18#ifndef _LIBCRYPTO_MLKEM_H 18#ifndef _LIBCRYPTO_MLKEM_H
19#define _LIBCRYPTO_MLKEM_H 19#define _LIBCRYPTO_MLKEM_H
20 20
21/* Undo when making public */
22#ifdef LIBRESSL_HAS_MLKEM
23
24#ifndef _MSC_VER 21#ifndef _MSC_VER
25#include_next <openssl/mlkem.h> 22#include_next <openssl/mlkem.h>
26#else 23#else
@@ -28,22 +25,21 @@
28#endif 25#endif
29#include "crypto_namespace.h" 26#include "crypto_namespace.h"
30 27
31LCRYPTO_USED(MLKEM768_generate_key); 28LCRYPTO_USED(MLKEM_private_key_new);
32LCRYPTO_USED(MLKEM768_public_from_private); 29LCRYPTO_USED(MLKEM_private_key_free);
33LCRYPTO_USED(MLKEM768_encap); 30LCRYPTO_USED(MLKEM_private_key_ciphertext_length);
34LCRYPTO_USED(MLKEM768_decap); 31LCRYPTO_USED(MLKEM_private_key_encoded_length);
35LCRYPTO_USED(MLKEM768_marshal_public_key); 32LCRYPTO_USED(MLKEM_public_key_new);
36LCRYPTO_USED(MLKEM768_parse_public_key); 33LCRYPTO_USED(MLKEM_public_key_free);
37LCRYPTO_USED(MLKEM768_private_key_from_seed); 34LCRYPTO_USED(MLKEM_public_key_ciphertext_length);
38LCRYPTO_USED(MLKEM768_parse_private_key); 35LCRYPTO_USED(MLKEM_public_key_encoded_length);
39LCRYPTO_USED(MLKEM1024_generate_key); 36LCRYPTO_USED(MLKEM_generate_key);
40LCRYPTO_USED(MLKEM1024_public_from_private); 37LCRYPTO_USED(MLKEM_private_key_from_seed);
41LCRYPTO_USED(MLKEM1024_encap); 38LCRYPTO_USED(MLKEM_public_from_private);
42LCRYPTO_USED(MLKEM1024_decap); 39LCRYPTO_USED(MLKEM_encap);
43LCRYPTO_USED(MLKEM1024_marshal_public_key); 40LCRYPTO_USED(MLKEM_decap);
44LCRYPTO_USED(MLKEM1024_parse_public_key); 41LCRYPTO_USED(MLKEM_marshal_public_key);
45LCRYPTO_USED(MLKEM1024_private_key_from_seed); 42LCRYPTO_USED(MLKEM_parse_public_key);
46LCRYPTO_USED(MLKEM1024_parse_private_key); 43LCRYPTO_USED(MLKEM_parse_private_key);
47#endif /* LIBRESSL_HAS_MLKEM */
48 44
49#endif /* _LIBCRYPTO_MLKEM_H */ 45#endif /* _LIBCRYPTO_MLKEM_H */
diff --git a/src/lib/libcrypto/mlkem/mlkem.c b/src/lib/libcrypto/mlkem/mlkem.c
new file mode 100644
index 0000000000..3202656dfd
--- /dev/null
+++ b/src/lib/libcrypto/mlkem/mlkem.c
@@ -0,0 +1,638 @@
1/* $OpenBSD: mlkem.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */
2/*
3 * Copyright (c) 2025, Bob Beck <beck@obtuse.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include <stdlib.h>
18
19#include <openssl/mlkem.h>
20#include "mlkem_internal.h"
21
22static inline int
23private_key_is_new(const MLKEM_private_key *key)
24{
25 return (key != NULL &&
26 key->state == MLKEM_PRIVATE_KEY_UNINITIALIZED &&
27 (key->rank == RANK768 || key->rank == RANK1024));
28}
29
30static inline int
31private_key_is_valid(const MLKEM_private_key *key)
32{
33 return (key != NULL &&
34 key->state == MLKEM_PRIVATE_KEY_INITIALIZED &&
35 (key->rank == RANK768 || key->rank == RANK1024));
36}
37
38static inline int
39public_key_is_new(const MLKEM_public_key *key)
40{
41 return (key != NULL &&
42 key->state == MLKEM_PUBLIC_KEY_UNINITIALIZED &&
43 (key->rank == RANK768 || key->rank == RANK1024));
44}
45
46static inline int
47public_key_is_valid(const MLKEM_public_key *key)
48{
49 return (key != NULL &&
50 key->state == MLKEM_PUBLIC_KEY_INITIALIZED &&
51 (key->rank == RANK768 || key->rank == RANK1024));
52}
53
54/*
55 * ML-KEM operations
56 */
57
58int
59MLKEM_generate_key_external_entropy(MLKEM_private_key *private_key,
60 uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len,
61 const uint8_t *entropy)
62{
63 uint8_t *k = NULL;
64 size_t k_len = 0;
65 int ret = 0;
66
67 if (*out_encoded_public_key != NULL)
68 goto err;
69
70 if (!private_key_is_new(private_key))
71 goto err;
72
73 k_len = MLKEM768_PUBLIC_KEY_BYTES;
74 if (private_key->rank == RANK1024)
75 k_len = MLKEM1024_PUBLIC_KEY_BYTES;
76
77 if ((k = calloc(1, k_len)) == NULL)
78 goto err;
79
80 switch (private_key->rank) {
81 case RANK768:
82 if (!MLKEM768_generate_key_external_entropy(k, private_key,
83 entropy))
84 goto err;
85 break;
86 case RANK1024:
87 if (!MLKEM1024_generate_key_external_entropy(k, private_key,
88 entropy))
89 goto err;
90 break;
91 }
92
93 private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED;
94
95 *out_encoded_public_key = k;
96 *out_encoded_public_key_len = k_len;
97 k = NULL;
98
99 ret = 1;
100
101 err:
102 freezero(k, k_len);
103
104 return ret;
105}
106
107int
108MLKEM_generate_key(MLKEM_private_key *private_key,
109 uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len,
110 uint8_t **out_optional_seed, size_t *out_optional_seed_len)
111{
112 uint8_t *entropy_buf = NULL;
113 int ret = 0;
114
115 if (*out_encoded_public_key != NULL)
116 goto err;
117
118 if (out_optional_seed != NULL && *out_optional_seed != NULL)
119 goto err;
120
121 if ((entropy_buf = calloc(1, MLKEM_SEED_LENGTH)) == NULL)
122 goto err;
123
124 arc4random_buf(entropy_buf, MLKEM_SEED_LENGTH);
125 if (!MLKEM_generate_key_external_entropy(private_key,
126 out_encoded_public_key, out_encoded_public_key_len,
127 entropy_buf))
128 goto err;
129
130 if (out_optional_seed != NULL) {
131 *out_optional_seed = entropy_buf;
132 *out_optional_seed_len = MLKEM_SEED_LENGTH;
133 entropy_buf = NULL;
134 }
135
136 ret = 1;
137
138 err:
139 freezero(entropy_buf, MLKEM_SEED_LENGTH);
140
141 return ret;
142}
143LCRYPTO_ALIAS(MLKEM_generate_key);
144
145int
146MLKEM_private_key_from_seed(MLKEM_private_key *private_key,
147 const uint8_t *seed, size_t seed_len)
148{
149 int ret = 0;
150
151 if (!private_key_is_new(private_key))
152 goto err;
153
154 if (seed_len != MLKEM_SEED_LENGTH)
155 goto err;
156
157 switch (private_key->rank) {
158 case RANK768:
159 if (!MLKEM768_private_key_from_seed(seed,
160 seed_len, private_key))
161 goto err;
162 break;
163 case RANK1024:
164 if (!MLKEM1024_private_key_from_seed(private_key,
165 seed, seed_len))
166 goto err;
167 break;
168 }
169
170 private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED;
171
172 ret = 1;
173
174 err:
175
176 return ret;
177}
178LCRYPTO_ALIAS(MLKEM_private_key_from_seed);
179
180int
181MLKEM_public_from_private(const MLKEM_private_key *private_key,
182 MLKEM_public_key *public_key)
183{
184 if (!private_key_is_valid(private_key))
185 return 0;
186 if (!public_key_is_new(public_key))
187 return 0;
188 if (public_key->rank != private_key->rank)
189 return 0;
190 switch (private_key->rank) {
191 case RANK768:
192 MLKEM768_public_from_private(private_key, public_key);
193 break;
194 case RANK1024:
195 MLKEM1024_public_from_private(private_key, public_key);
196 break;
197 }
198
199 public_key->state = MLKEM_PUBLIC_KEY_INITIALIZED;
200
201 return 1;
202}
203LCRYPTO_ALIAS(MLKEM_public_from_private);
204
205int
206MLKEM_encap_external_entropy(const MLKEM_public_key *public_key,
207 const uint8_t *entropy, uint8_t **out_ciphertext,
208 size_t *out_ciphertext_len, uint8_t **out_shared_secret,
209 size_t *out_shared_secret_len)
210{
211 uint8_t *secret = NULL;
212 uint8_t *ciphertext = NULL;
213 size_t ciphertext_len = 0;
214 int ret = 0;
215
216 if (*out_ciphertext != NULL)
217 goto err;
218
219 if (*out_shared_secret != NULL)
220 goto err;
221
222 if (!public_key_is_valid(public_key))
223 goto err;
224
225 if ((secret = calloc(1, MLKEM_SHARED_SECRET_LENGTH)) == NULL)
226 goto err;
227
228 ciphertext_len = MLKEM_public_key_ciphertext_length(public_key);
229
230 if ((ciphertext = calloc(1, ciphertext_len)) == NULL)
231 goto err;
232
233 switch (public_key->rank) {
234 case RANK768:
235 MLKEM768_encap_external_entropy(ciphertext, secret, public_key,
236 entropy);
237 break;
238
239 case RANK1024:
240 MLKEM1024_encap_external_entropy(ciphertext, secret, public_key,
241 entropy);
242 break;
243 }
244 *out_ciphertext = ciphertext;
245 *out_ciphertext_len = ciphertext_len;
246 ciphertext = NULL;
247 *out_shared_secret = secret;
248 *out_shared_secret_len = MLKEM_SHARED_SECRET_LENGTH;
249 secret = NULL;
250
251 ret = 1;
252
253 err:
254 freezero(secret, MLKEM_SHARED_SECRET_LENGTH);
255 freezero(ciphertext, ciphertext_len);
256
257 return ret;
258}
259
260int
261MLKEM_encap(const MLKEM_public_key *public_key,
262 uint8_t **out_ciphertext, size_t *out_ciphertext_len,
263 uint8_t **out_shared_secret, size_t *out_shared_secret_len)
264{
265 uint8_t entropy[MLKEM_ENCAP_ENTROPY];
266
267 arc4random_buf(entropy, MLKEM_ENCAP_ENTROPY);
268
269 return MLKEM_encap_external_entropy(public_key, entropy, out_ciphertext,
270 out_ciphertext_len, out_shared_secret, out_shared_secret_len);
271}
272LCRYPTO_ALIAS(MLKEM_encap);
273
274int
275MLKEM_decap(const MLKEM_private_key *private_key,
276 const uint8_t *ciphertext, size_t ciphertext_len,
277 uint8_t **out_shared_secret, size_t *out_shared_secret_len)
278{
279 uint8_t *s = NULL;
280 int ret = 0;
281
282 if (*out_shared_secret != NULL)
283 goto err;
284
285 if (!private_key_is_valid(private_key))
286 goto err;
287
288 if (ciphertext_len != MLKEM_private_key_ciphertext_length(private_key))
289 goto err;
290
291 if ((s = calloc(1, MLKEM_SHARED_SECRET_LENGTH)) == NULL)
292 goto err;
293
294 switch (private_key->rank) {
295 case RANK768:
296 MLKEM768_decap(private_key, ciphertext, ciphertext_len, s);
297 break;
298
299 case RANK1024:
300 MLKEM1024_decap(private_key, ciphertext, ciphertext_len, s);
301 break;
302 }
303
304 *out_shared_secret = s;
305 *out_shared_secret_len = MLKEM_SHARED_SECRET_LENGTH;
306 s = NULL;
307
308 ret = 1;
309
310 err:
311 freezero(s, MLKEM_SHARED_SECRET_LENGTH);
312
313 return ret;
314}
315LCRYPTO_ALIAS(MLKEM_decap);
316
317int
318MLKEM_marshal_public_key(const MLKEM_public_key *public_key, uint8_t **out,
319 size_t *out_len)
320{
321 if (*out != NULL)
322 return 0;
323
324 if (!public_key_is_valid(public_key))
325 return 0;
326
327 switch (public_key->rank) {
328 case RANK768:
329 return MLKEM768_marshal_public_key(public_key, out, out_len);
330 case RANK1024:
331 return MLKEM1024_marshal_public_key(public_key, out, out_len);
332 default:
333 return 0;
334 }
335}
336LCRYPTO_ALIAS(MLKEM_marshal_public_key);
337
338/*
339 * Not exposed publicly, becuase the NIST private key format is gigantisch, and
340 * seeds should be used instead. Used for the NIST tests.
341 */
342int
343MLKEM_marshal_private_key(const MLKEM_private_key *private_key, uint8_t **out,
344 size_t *out_len)
345{
346 if (*out != NULL)
347 return 0;
348
349 if (!private_key_is_valid(private_key))
350 return 0;
351
352 switch (private_key->rank) {
353 case RANK768:
354 return MLKEM768_marshal_private_key(private_key, out, out_len);
355 case RANK1024:
356 return MLKEM1024_marshal_private_key(private_key, out, out_len);
357 default:
358 return 0;
359 }
360}
361
362int
363MLKEM_parse_public_key(MLKEM_public_key *public_key, const uint8_t *in,
364 size_t in_len)
365{
366 if (!public_key_is_new(public_key))
367 return 0;
368
369 if (in_len != MLKEM_public_key_encoded_length(public_key))
370 return 0;
371
372 switch (public_key->rank) {
373 case RANK768:
374 if (!MLKEM768_parse_public_key(in, in_len,
375 public_key))
376 return 0;
377 break;
378 case RANK1024:
379 if (!MLKEM1024_parse_public_key(in, in_len,
380 public_key))
381 return 0;
382 break;
383 }
384
385 public_key->state = MLKEM_PUBLIC_KEY_INITIALIZED;
386
387 return 1;
388}
389LCRYPTO_ALIAS(MLKEM_parse_public_key);
390
391int
392MLKEM_parse_private_key(MLKEM_private_key *private_key, const uint8_t *in,
393 size_t in_len)
394{
395 if (!private_key_is_new(private_key))
396 return 0;
397
398 if (in_len != MLKEM_private_key_encoded_length(private_key))
399 return 0;
400
401 switch (private_key->rank) {
402 case RANK768:
403 if (!MLKEM768_parse_private_key(in, in_len, private_key))
404 return 0;
405 break;
406 case RANK1024:
407 if (!MLKEM1024_parse_private_key(in, in_len, private_key))
408 return 0;
409 break;
410 }
411
412 private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED;
413
414 return 1;
415}
416LCRYPTO_ALIAS(MLKEM_parse_private_key);
417/* $OpenBSD: mlkem.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */
418/*
419 * Copyright (c) 2025, Bob Beck <beck@obtuse.com>
420 *
421 * Permission to use, copy, modify, and/or distribute this software for any
422 * purpose with or without fee is hereby granted, provided that the above
423 * copyright notice and this permission notice appear in all copies.
424 *
425 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
426 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
427 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
428 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
429 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
430 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
431 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
432 */
433
434#include <openssl/mlkem.h>
435
436
437int MLKEM768_generate_key(
438 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
439 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
440 struct MLKEM768_private_key *out_private_key);
441
442/*
443 * MLKEM768_private_key_from_seed derives a private key from a seed that was
444 * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is
445 * incorrect, otherwise it writes |*out_private_key| and returns 1.
446 */
447int MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key,
448 const uint8_t *seed, size_t seed_len);
449
450/*
451 * MLKEM_public_from_private sets |*out_public_key| to the public key that
452 * corresponds to |private_key|. (This is faster than parsing the output of
453 * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key
454 * that was just generated.)
455 */
456void MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key,
457 const struct MLKEM768_private_key *private_key);
458
459/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */
460#define MLKEM768_CIPHERTEXT_BYTES 1088
461
462/*
463 * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the
464 * ciphertext to |out_ciphertext|, and writes the random shared secret to
465 * |out_shared_secret|.
466 */
467void MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
468 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
469 const struct MLKEM768_public_key *public_key);
470
471/*
472 * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key|
473 * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it
474 * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid,
475 * |out_shared_secret| is filled with a key that will always be the same for the
476 * same |ciphertext| and |private_key|, but which appears to be random unless
477 * one has access to |private_key|. These alternatives occur in constant time.
478 * Any subsequent symmetric encryption using |out_shared_secret| must use an
479 * authenticated encryption scheme in order to discover the decapsulation
480 * failure.
481 */
482int MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
483 const uint8_t *ciphertext, size_t ciphertext_len,
484 const struct MLKEM768_private_key *private_key);
485
486/* Serialisation of keys. */
487
488/*
489 * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard
490 * format for ML-KEM public keys. It returns one on success or zero on allocation
491 * error.
492 */
493int MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len,
494 const struct MLKEM768_public_key *public_key);
495
496/*
497 * MLKEM768_parse_public_key parses a public key, in the format generated by
498 * |MLKEM_marshal_public_key|, from |in| and writes the result to
499 * |out_public_key|. It returns one on success or zero on parse error or if
500 * there are trailing bytes in |in|.
501 */
502int MLKEM768_parse_public_key(struct MLKEM768_public_key *out_public_key,
503 const uint8_t *input, size_t input_len);
504
505/*
506 * MLKEM_parse_private_key parses a private key, in the format generated by
507 * |MLKEM_marshal_private_key|, from |in| and writes the result to
508 * |out_private_key|. It returns one on success or zero on parse error or if
509 * there are trailing bytes in |in|. This formate is verbose and should be avoided.
510 * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|.
511 */
512int MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key,
513 const uint8_t *input, size_t input_len);
514
515/*
516 * ML-KEM-1024
517 *
518 * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible.
519 */
520
521/*
522 * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this
523 * object should never leave the address space since the format is unstable.
524 */
525struct MLKEM1024_public_key {
526 union {
527 uint8_t bytes[512 * (4 + 16) + 32 + 32];
528 uint16_t alignment;
529 } opaque;
530};
531
532/*
533 * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of
534 * this object should never leave the address space since the format is
535 * unstable.
536 */
537struct MLKEM1024_private_key {
538 union {
539 uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32];
540 uint16_t alignment;
541 } opaque;
542};
543
544/*
545 * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024
546 * public key.
547 */
548#define MLKEM1024_PUBLIC_KEY_BYTES 1568
549
550/*
551 * MLKEM1024_generate_key generates a random public/private key pair, writes the
552 * encoded public key to |out_encoded_public_key| and sets |out_private_key| to
553 * the private key. If |optional_out_seed| is not NULL then the seed used to
554 * generate the private key is written to it.
555 */
556int MLKEM1024_generate_key(
557 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
558 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
559 struct MLKEM1024_private_key *out_private_key);
560
561/*
562 * MLKEM1024_private_key_from_seed derives a private key from a seed that was
563 * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len|
564 * is incorrect, otherwise it writes |*out_private_key| and returns 1.
565 */
566int MLKEM1024_private_key_from_seed(
567 struct MLKEM1024_private_key *out_private_key, const uint8_t *seed,
568 size_t seed_len);
569
570/*
571 * MLKEM1024_public_from_private sets |*out_public_key| to the public key that
572 * corresponds to |private_key|. (This is faster than parsing the output of
573 * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a
574 * key that was just generated.)
575 */
576void MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key,
577 const struct MLKEM1024_private_key *private_key);
578
579/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */
580#define MLKEM1024_CIPHERTEXT_BYTES 1568
581
582/*
583 * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the
584 * ciphertext to |out_ciphertext|, and writes the random shared secret to
585 * |out_shared_secret|.
586 */
587void MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
588 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
589 const struct MLKEM1024_public_key *public_key);
590
591/*
592 * MLKEM1024_decap decrypts a shared secret from |ciphertext| using
593 * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is
594 * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid
595 * (but of the correct length), |out_shared_secret| is filled with a key that
596 * will always be the same for the same |ciphertext| and |private_key|, but
597 * which appears to be random unless one has access to |private_key|. These
598 * alternatives occur in constant time. Any subsequent symmetric encryption
599 * using |out_shared_secret| must use an authenticated encryption scheme in
600 * order to discover the decapsulation failure.
601 */
602int MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
603 const uint8_t *ciphertext, size_t ciphertext_len,
604 const struct MLKEM1024_private_key *private_key);
605
606/*
607 * Serialisation of ML-KEM-1024 keys.
608 * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard
609 * format for ML-KEM-1024 public keys. It returns one on success or zero on
610 * allocation error.
611 */
612int MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len,
613 const struct MLKEM1024_public_key *public_key);
614
615/*
616 * MLKEM1024_parse_public_key parses a public key, in the format generated by
617 * |MLKEM1024_marshal_public_key|, from |in| and writes the result to
618 * |out_public_key|. It returns one on success or zero on parse error or if
619 * there are trailing bytes in |in|.
620 */
621int MLKEM1024_parse_public_key(struct MLKEM1024_public_key *out_public_key,
622 const uint8_t *input, size_t input_len);
623
624/*
625 * MLKEM1024_parse_private_key parses a private key, in NIST's format for
626 * private keys, from |in| and writes the result to |out_private_key|. It
627 * returns one on success or zero on parse error or if there are trailing bytes
628 * in |in|. This format is verbose and should be avoided. Private keys should be
629 * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|.
630 */
631int MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key,
632 const uint8_t *input, size_t input_len);
633
634#if defined(__cplusplus)
635}
636#endif
637
638#endif /* OPENSSL_HEADER_MLKEM_H */
diff --git a/src/lib/libcrypto/mlkem/mlkem.h b/src/lib/libcrypto/mlkem/mlkem.h
index a2c5d7fed0..31d4858195 100644
--- a/src/lib/libcrypto/mlkem/mlkem.h
+++ b/src/lib/libcrypto/mlkem/mlkem.h
@@ -1,6 +1,6 @@
1/* $OpenBSD: mlkem.h,v 1.6 2025/05/19 06:47:40 beck Exp $ */ 1/* $OpenBSD: mlkem.h,v 1.7 2025/08/14 15:48:48 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2024, Google Inc. 3 * Copyright (c) 2025 Bob Beck <beck@obtuse.com>
4 * 4 *
5 * Permission to use, copy, modify, and/or distribute this software for any 5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above 6 * purpose with or without fee is hereby granted, provided that the above
@@ -26,253 +26,202 @@ extern "C" {
26#endif 26#endif
27 27
28/* 28/*
29 * ML-KEM-768 29 * ML-KEM constants
30 *
31 * This implements the Module-Lattice-Based Key-Encapsulation Mechanism from
32 * https://csrc.nist.gov/pubs/fips/204/final
33 */ 30 */
34 31
35/* 32#define RANK768 3
36 * MLKEM768_public_key contains a ML-KEM-768 public key. The contents of this 33#define RANK1024 4
37 * object should never leave the address space since the format is unstable.
38 */
39struct MLKEM768_public_key {
40 union {
41 uint8_t bytes[512 * (3 + 9) + 32 + 32];
42 uint16_t alignment;
43 } opaque;
44};
45
46/*
47 * MLKEM768_private_key contains a ML-KEM-768 private key. The contents of this
48 * object should never leave the address space since the format is unstable.
49 */
50struct MLKEM768_private_key {
51 union {
52 uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32];
53 uint16_t alignment;
54 } opaque;
55};
56 34
57/* 35/*
58 * MLKEM768_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM768 public 36 * ML-KEM keys
59 * key.
60 */ 37 */
61#define MLKEM768_PUBLIC_KEY_BYTES 1184
62 38
63/* MLKEM_SEED_BYTES is the number of bytes in an ML-KEM seed. */ 39typedef struct MLKEM_private_key_st MLKEM_private_key;
64#define MLKEM_SEED_BYTES 64 40typedef struct MLKEM_public_key_st MLKEM_public_key;
65 41
66/* 42/*
67 * MLKEM_SHARED_SECRET_BYTES is the number of bytes in the ML-KEM768 shared 43 * MLKEM_private_key_new allocates a new uninitialized ML-KEM private key for
68 * secret. Although the round-3 specification has a variable-length output, the 44 * |rank|, which must be RANK768 or RANK1024. It returns a pointer to an
69 * final ML-KEM construction is expected to use a fixed 32-byte output. To 45 * allocated structure suitable for holding a generated private key of the
70 * simplify the future transition, we apply the same restriction. 46 * corresponding rank on success, NULL is returned on failure. The caller is
47 * responsible for deallocating the resulting key with |MLKEM_private_key_free|.
71 */ 48 */
72#define MLKEM_SHARED_SECRET_BYTES 32 49MLKEM_private_key *MLKEM_private_key_new(int rank);
73 50
74/* 51/*
75 * MLKEM_generate_key generates a random public/private key pair, writes the 52 * MLKEM_private_key_free zeroes and frees all memory for |key| if |key| is
76 * encoded public key to |out_encoded_public_key| and sets |out_private_key| to 53 * non NULL. If |key| is NULL it does nothing and returns.
77 * the private key. If |optional_out_seed| is not NULL then the seed used to
78 * generate the private key is written to it.
79 */ 54 */
80int MLKEM768_generate_key( 55void MLKEM_private_key_free(MLKEM_private_key *key);
81 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
82 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
83 struct MLKEM768_private_key *out_private_key);
84 56
85/* 57/*
86 * MLKEM768_private_key_from_seed derives a private key from a seed that was 58 * MLKEM_private_key_encoded_length the number of bytes used by the encoded form
87 * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is 59 * of |key|. Thie corresponds to the length of the buffer allocated for the
88 * incorrect, otherwise it writes |*out_private_key| and returns 1. 60 * encoded_public_key from |MLKEM_marshal_private_key|. Zero is returned if
61 * |key| is NULL or has an invalid rank.
89 */ 62 */
90int MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, 63size_t MLKEM_private_key_encoded_length(const MLKEM_private_key *key);
91 const uint8_t *seed, size_t seed_len);
92 64
93/* 65/*
94 * MLKEM_public_from_private sets |*out_public_key| to the public key that 66 * MLKEM_private_key_ciphertext_length returns the number of bytes of ciphertext
95 * corresponds to |private_key|. (This is faster than parsing the output of 67 * required to decrypt a shared secret with |key| using |MLKEM_decap|. Zero is
96 * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key 68 * returned if |key| is NULL or has an invalid rank.
97 * that was just generated.)
98 */ 69 */
99void MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key, 70size_t MLKEM_private_key_ciphertext_length(const MLKEM_private_key *key);
100 const struct MLKEM768_private_key *private_key);
101
102/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */
103#define MLKEM768_CIPHERTEXT_BYTES 1088
104 71
105/* 72/*
106 * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the 73 * MLKEM_public_key_new allocates a new uninitialized ML-KEM public key for
107 * ciphertext to |out_ciphertext|, and writes the random shared secret to 74 * |rank|, which must be RANK768 or RANK1024. It returns a pointer to an
108 * |out_shared_secret|. 75 * allocated structure suitable for holding a generated public key of the
76 * corresponding rank on success, NULL is returned on failure. The caller is
77 * responsible for deallocating the resulting key with |MLKEM_public_key_free|.
109 */ 78 */
110void MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], 79MLKEM_public_key *MLKEM_public_key_new(int rank);
111 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
112 const struct MLKEM768_public_key *public_key);
113 80
114/* 81/*
115 * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key| 82 * MLKEM_public_key_free zeros and deallocates all memory for |key| if |key| is
116 * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it 83 * non NULL. If |key| is NULL it does nothing and returns.
117 * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid,
118 * |out_shared_secret| is filled with a key that will always be the same for the
119 * same |ciphertext| and |private_key|, but which appears to be random unless
120 * one has access to |private_key|. These alternatives occur in constant time.
121 * Any subsequent symmetric encryption using |out_shared_secret| must use an
122 * authenticated encryption scheme in order to discover the decapsulation
123 * failure.
124 */ 84 */
125int MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 85void MLKEM_public_key_free(MLKEM_public_key *key);
126 const uint8_t *ciphertext, size_t ciphertext_len,
127 const struct MLKEM768_private_key *private_key);
128
129/* Serialisation of keys. */
130 86
131/* 87/*
132 * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard 88 * MLKEM_public_key_encoded_length the number of bytes used by the encoded form
133 * format for ML-KEM public keys. It returns one on success or zero on allocation 89 * of |key|. Thie corresponds to the length of the buffer allocated for the
134 * error. 90 * encoded_public_key from |MLKEM_generate_key| or |MLKEM_marshal_public_key|.
91 * Zero is returned if |key| is NULL or has an invalid rank.
135 */ 92 */
136int MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, 93size_t MLKEM_public_key_encoded_length(const MLKEM_public_key *key);
137 const struct MLKEM768_public_key *public_key);
138 94
139/* 95/*
140 * MLKEM768_parse_public_key parses a public key, in the format generated by 96 * MLKEM_public_key_cipertext_length returns the number of bytes produced as the
141 * |MLKEM_marshal_public_key|, from |in| and writes the result to 97 * ciphertext when encrypting a shared secret with |key| using |MLKEM_encap|. Zero
142 * |out_public_key|. It returns one on success or zero on parse error or if 98 * is returned if |key| is NULL or has an invalid rank.
143 * there are trailing bytes in |in|.
144 */ 99 */
145int MLKEM768_parse_public_key(struct MLKEM768_public_key *out_public_key, 100size_t MLKEM_public_key_ciphertext_length(const MLKEM_public_key *key);
146 const uint8_t *input, size_t input_len);
147 101
148/* 102/*
149 * MLKEM_parse_private_key parses a private key, in the format generated by 103 * ML-KEM operations
150 * |MLKEM_marshal_private_key|, from |in| and writes the result to
151 * |out_private_key|. It returns one on success or zero on parse error or if
152 * there are trailing bytes in |in|. This formate is verbose and should be avoided.
153 * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|.
154 */ 104 */
155int MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key,
156 const uint8_t *input, size_t input_len);
157 105
158/* 106/*
159 * ML-KEM-1024 107 * MLKEM_generate_key generates a random private/public key pair, initializing
108 * |private_key|. It returns one on success, and zero on failure or error.
109 * |private_key| must be a new uninitialized key. |*out_encoded_public_key| and
110 * |*out_optional_seed|, if provided, must have the value of NULL. On success, a
111 * pointer to the encoded public key of the correct size for |key| is returned
112 * in |out_encoded_public_key|, and the length in bytes of
113 * |*out_encoded_public_key| is returned in |out_encoded_public_key_len|. If
114 * |out_optional_seed| is not NULL, a pointer to the seed used to generate the
115 * private key is returned in |*out_optional_seed| and the length in bytes of
116 * the seed is returned in |*out_optional_seed_len|. The caller is responsible
117 * for freeing the values returned in |out_encoded_public_key|, and
118 * |out_optional_seed|.
160 * 119 *
161 * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible. 120 * In the event a private key needs to be saved, The normal best practice is to
162 */ 121 * save |out_optional_seed| as the private key, along with the ML-KEM rank value.
163 122 * An MLKEM_private_key of the correct rank can then be constructed using
164/* 123 * |MLKEM_private_key_from_seed|.
165 * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this
166 * object should never leave the address space since the format is unstable.
167 */
168struct MLKEM1024_public_key {
169 union {
170 uint8_t bytes[512 * (4 + 16) + 32 + 32];
171 uint16_t alignment;
172 } opaque;
173};
174
175/*
176 * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of
177 * this object should never leave the address space since the format is
178 * unstable.
179 */
180struct MLKEM1024_private_key {
181 union {
182 uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32];
183 uint16_t alignment;
184 } opaque;
185};
186
187/*
188 * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024
189 * public key.
190 */ 124 */
191#define MLKEM1024_PUBLIC_KEY_BYTES 1568 125int MLKEM_generate_key(MLKEM_private_key *private_key,
126 uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len,
127 uint8_t **out_optional_seed, size_t *out_optional_seed_len);
192 128
193/* 129/*
194 * MLKEM1024_generate_key generates a random public/private key pair, writes the 130 * MLKEM_private_key_from_seed derives a private key from a seed that was
195 * encoded public key to |out_encoded_public_key| and sets |out_private_key| to 131 * generated by |MLKEM_generate_key| initializing |private_key|. It returns one
196 * the private key. If |optional_out_seed| is not NULL then the seed used to 132 * on success, and zero on failure or error. |private_key| must be a new
197 * generate the private key is written to it. 133 * uninitialized key. |seed_len| must be MLKEM_SEED_LENGTH.
134 *
135 * For |private_key| to match the key generated by |MLKEM_generate_key|,
136 * |private_key| must have been created with the same rank as used when generating
137 * the key.
198 */ 138 */
199int MLKEM1024_generate_key( 139int MLKEM_private_key_from_seed(MLKEM_private_key *private_key,
200 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], 140 const uint8_t *seed, size_t seed_len);
201 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
202 struct MLKEM1024_private_key *out_private_key);
203 141
204/* 142/*
205 * MLKEM1024_private_key_from_seed derives a private key from a seed that was 143 * MLKEM_public_from_private initializes |public_key| with the public key that
206 * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len| 144 * corresponds to |private_key|. It returns one on success and zero on
207 * is incorrect, otherwise it writes |*out_private_key| and returns 1. 145 * error. This is faster than parsing the output of |MLKEM_generate_key| if, for
146 * some reason, you need to encapsulate to a key that was just
147 * generated. |private key| must be a new uninitialized key, of the same rank as
148 * |public_key|.
208 */ 149 */
209int MLKEM1024_private_key_from_seed( 150int MLKEM_public_from_private(const MLKEM_private_key *private_key,
210 struct MLKEM1024_private_key *out_private_key, const uint8_t *seed, 151 MLKEM_public_key *public_key);
211 size_t seed_len);
212 152
213/* 153/*
214 * MLKEM1024_public_from_private sets |*out_public_key| to the public key that 154 * MLKEM_encap encrypts a random shared secret for an initialized
215 * corresponds to |private_key|. (This is faster than parsing the output of 155 * |public_key|. It returns one on success, and zero on failure or error. |*out
216 * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a 156 * ciphertext| and |*out_shared_secret| must have the value NULL. On success, a
217 * key that was just generated.) 157 * pointer to the ciphertext of the correct size for |key| is returned in
158 * |out_ciphertext|, the length in bytes of |*out_ciphertext| is returned in
159 * |*out_ciphertext_len|, a pointer to the random shared secret is returned in
160 * |out_shared_secret|, and the length in bytes of |*out_shared_secret| is
161 * returned in |*out_ciphtertext_len|. The caller is responsible for zeroing and
162 * freeing the values returned in |out_ciphertext| and |out_shared_secret|
218 */ 163 */
219void MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, 164int MLKEM_encap(const MLKEM_public_key *public_key,
220 const struct MLKEM1024_private_key *private_key); 165 uint8_t **out_ciphertext, size_t *out_ciphertext_len,
221 166 uint8_t **out_shared_secret, size_t *out_shared_secret_len);
222/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */
223#define MLKEM1024_CIPHERTEXT_BYTES 1568
224 167
225/* 168/*
226 * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the 169 * MLKEM_decap decrypts a shared secret from |ciphertext| using an initialized
227 * ciphertext to |out_ciphertext|, and writes the random shared secret to 170 * |private_key|. It returns a pointer to the shared secret|out_shared_secret|
228 * |out_shared_secret|. 171 * and the length in bytes of |*out_shared_secret| in |*out_shared_secret_len|.
229 */ 172 *
230void MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], 173 * If |ciphertext_len| is incorrect for |private_key|, |*out_shared_secret| is
231 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 174 * not NULL, or memory can not be allocated, it returns zero, otherwise it
232 const struct MLKEM1024_public_key *public_key); 175 * returns one. If |ciphertext| is invalid, a pointer is returned in
233 176 * |out_shared_secret| pointing to a key that will always be the same for the
234/* 177 * same |ciphertext| and |private_key|, but which appears to be random unless
235 * MLKEM1024_decap decrypts a shared secret from |ciphertext| using 178 * one has access to |private_key|. These alternatives occur in constant time.
236 * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is 179 * Any subsequent symmetric encryption using |out_shared_secret| must use an
237 * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid 180 * authenticated encryption scheme in order to discover the decapsulation
238 * (but of the correct length), |out_shared_secret| is filled with a key that 181 * failure. The caller is responsible for zeroing and freeing the value returned
239 * will always be the same for the same |ciphertext| and |private_key|, but 182 * in |out_shared_secret|.
240 * which appears to be random unless one has access to |private_key|. These
241 * alternatives occur in constant time. Any subsequent symmetric encryption
242 * using |out_shared_secret| must use an authenticated encryption scheme in
243 * order to discover the decapsulation failure.
244 */ 183 */
245int MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 184int MLKEM_decap(const MLKEM_private_key *private_key,
246 const uint8_t *ciphertext, size_t ciphertext_len, 185 const uint8_t *ciphertext, size_t ciphertext_len,
247 const struct MLKEM1024_private_key *private_key); 186 uint8_t **out_shared_secret, size_t *out_shared_secret_len);
187
188/* Serialization of ML-KEM keys. */
248 189
249/* 190/*
250 * Serialisation of ML-KEM-1024 keys. 191 * MLKEM_marshal_public_key serializes an initialized |public_key| in the
251 * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard 192 * standard format for ML-KEM public keys. It returns one on success or zero on
252 * format for ML-KEM-1024 public keys. It returns one on success or zero on 193 * allocation error or failure. |*out| must have the value NULL. On success a
253 * allocation error. 194 * pointer is returned in |out| to the encoded public key matching |public_key|,
195 * and a pointer to the length in bytes of the encoded public key is stored in
196 * |out_len|. The caller is responsible for freeing the values returned in
197 * |out|.
254 */ 198 */
255int MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, 199int MLKEM_marshal_public_key(const MLKEM_public_key *public_key, uint8_t **out,
256 const struct MLKEM1024_public_key *public_key); 200 size_t *out_len);
257 201
258/* 202/*
259 * MLKEM1024_parse_public_key parses a public key, in the format generated by 203 * MLKEM_parse_public_key parses a public key, in the format generated by
260 * |MLKEM1024_marshal_public_key|, from |in| and writes the result to 204 * |MLKEM_marshal_public_key|, from |in|. It returns one on success or zero on
261 * |out_public_key|. It returns one on success or zero on parse error or if 205 * error or failure. |public_key| must be a new uninitialized key. |in_len| must
262 * there are trailing bytes in |in|. 206 * be the correct length for the encoded format of |public_key. On success
207 * |public_key| is initialized to the value parsed from |in|.
263 */ 208 */
264int MLKEM1024_parse_public_key(struct MLKEM1024_public_key *out_public_key, 209int MLKEM_parse_public_key(MLKEM_public_key *public_key, const uint8_t *in,
265 const uint8_t *input, size_t input_len); 210 size_t in_len);
266 211
267/* 212/*
268 * MLKEM1024_parse_private_key parses a private key, in NIST's format for 213 * MLKEM_parse_private_key parses a private key, in the format generated by
269 * private keys, from |in| and writes the result to |out_private_key|. It 214 * |MLKEM_marshal_private_key|, from |in|. It returns one on success or zero on
270 * returns one on success or zero on parse error or if there are trailing bytes 215 * error or failure. |private_key| must be a new uninitialized key. |in_len|
271 * in |in|. This format is verbose and should be avoided. Private keys should be 216 * must be the correct length for the encoded format of |private_key. On success
272 * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|. 217 * |private_key| is initialized to the value parsed from |in|.
218 *
219 * This format is wastefully verbose and should be avoided. Private keys should
220 * be stored as seeds from |MLKEM_generate_key|, and then parsed using
221 * |MLKEM_private_key_from_seed|.
273 */ 222 */
274int MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, 223int MLKEM_parse_private_key(MLKEM_private_key *private_key, const uint8_t *in,
275 const uint8_t *input, size_t input_len); 224 size_t in_len);
276 225
277#if defined(__cplusplus) 226#if defined(__cplusplus)
278} 227}
diff --git a/src/lib/libcrypto/mlkem/mlkem1024.c b/src/lib/libcrypto/mlkem/mlkem1024.c
index 26c4716539..8f4f41f8ff 100644
--- a/src/lib/libcrypto/mlkem/mlkem1024.c
+++ b/src/lib/libcrypto/mlkem/mlkem1024.c
@@ -1,7 +1,7 @@
1/* $OpenBSD: mlkem1024.c,v 1.11 2025/05/21 02:18:11 kenjiro Exp $ */ 1/* $OpenBSD: mlkem1024.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, 2025 Bob Beck <beck@obtuse.com>
5 * 5 *
6 * Permission to use, copy, modify, and/or distribute this software for any 6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above 7 * purpose with or without fee is hereby granted, provided that the above
@@ -20,18 +20,14 @@
20#include <stdlib.h> 20#include <stdlib.h>
21#include <string.h> 21#include <string.h>
22 22
23#include "bytestring.h" 23#include <openssl/mlkem.h>
24#include "mlkem.h"
25 24
25#include "bytestring.h"
26#include "sha3_internal.h" 26#include "sha3_internal.h"
27#include "mlkem_internal.h" 27#include "mlkem_internal.h"
28#include "constant_time.h" 28#include "constant_time.h"
29#include "crypto_internal.h" 29#include "crypto_internal.h"
30 30
31/* Remove later */
32#undef LCRYPTO_ALIAS
33#define LCRYPTO_ALIAS(A)
34
35/* 31/*
36 * See 32 * See
37 * https://csrc.nist.gov/pubs/fips/203/final 33 * https://csrc.nist.gov/pubs/fips/203/final
@@ -80,7 +76,6 @@ kdf(uint8_t out[MLKEM_SHARED_SECRET_BYTES], const uint8_t failure_secret[32],
80} 76}
81 77
82#define DEGREE 256 78#define DEGREE 256
83#define RANK1024 4
84 79
85static const size_t kBarrettMultiplier = 5039; 80static const size_t kBarrettMultiplier = 5039;
86static const unsigned kBarrettShift = 24; 81static const unsigned kBarrettShift = 24;
@@ -809,9 +804,11 @@ struct public_key {
809CTASSERT(sizeof(struct MLKEM1024_public_key) == sizeof(struct public_key)); 804CTASSERT(sizeof(struct MLKEM1024_public_key) == sizeof(struct public_key));
810 805
811static struct public_key * 806static struct public_key *
812public_key_1024_from_external(const struct MLKEM1024_public_key *external) 807public_key_1024_from_external(const MLKEM_public_key *external)
813{ 808{
814 return (struct public_key *)external; 809 if (external->rank != RANK1024)
810 return NULL;
811 return (struct public_key *)external->key_1024;
815} 812}
816 813
817struct private_key { 814struct private_key {
@@ -823,9 +820,11 @@ struct private_key {
823CTASSERT(sizeof(struct MLKEM1024_private_key) == sizeof(struct private_key)); 820CTASSERT(sizeof(struct MLKEM1024_private_key) == sizeof(struct private_key));
824 821
825static struct private_key * 822static struct private_key *
826private_key_1024_from_external(const struct MLKEM1024_private_key *external) 823private_key_1024_from_external(const MLKEM_private_key *external)
827{ 824{
828 return (struct private_key *)external; 825 if (external->rank != RANK1024)
826 return NULL;
827 return (struct private_key *)external->key_1024;
829} 828}
830 829
831/* 830/*
@@ -835,7 +834,7 @@ private_key_1024_from_external(const struct MLKEM1024_private_key *external)
835int 834int
836MLKEM1024_generate_key(uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], 835MLKEM1024_generate_key(uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
837 uint8_t optional_out_seed[MLKEM_SEED_BYTES], 836 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
838 struct MLKEM1024_private_key *out_private_key) 837 MLKEM_private_key *out_private_key)
839{ 838{
840 uint8_t entropy_buf[MLKEM_SEED_BYTES]; 839 uint8_t entropy_buf[MLKEM_SEED_BYTES];
841 uint8_t *entropy = optional_out_seed != NULL ? optional_out_seed : 840 uint8_t *entropy = optional_out_seed != NULL ? optional_out_seed :
@@ -845,10 +844,9 @@ MLKEM1024_generate_key(uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES
845 return MLKEM1024_generate_key_external_entropy(out_encoded_public_key, 844 return MLKEM1024_generate_key_external_entropy(out_encoded_public_key,
846 out_private_key, entropy); 845 out_private_key, entropy);
847} 846}
848LCRYPTO_ALIAS(MLKEM1024_generate_key);
849 847
850int 848int
851MLKEM1024_private_key_from_seed(struct MLKEM1024_private_key *out_private_key, 849MLKEM1024_private_key_from_seed(MLKEM_private_key *out_private_key,
852 const uint8_t *seed, size_t seed_len) 850 const uint8_t *seed, size_t seed_len)
853{ 851{
854 uint8_t public_key_bytes[MLKEM1024_PUBLIC_KEY_BYTES]; 852 uint8_t public_key_bytes[MLKEM1024_PUBLIC_KEY_BYTES];
@@ -859,7 +857,6 @@ MLKEM1024_private_key_from_seed(struct MLKEM1024_private_key *out_private_key,
859 return MLKEM1024_generate_key_external_entropy(public_key_bytes, 857 return MLKEM1024_generate_key_external_entropy(public_key_bytes,
860 out_private_key, seed); 858 out_private_key, seed);
861} 859}
862LCRYPTO_ALIAS(MLKEM1024_private_key_from_seed);
863 860
864static int 861static int
865mlkem_marshal_public_key(CBB *out, const struct public_key *pub) 862mlkem_marshal_public_key(CBB *out, const struct public_key *pub)
@@ -872,7 +869,7 @@ mlkem_marshal_public_key(CBB *out, const struct public_key *pub)
872int 869int
873MLKEM1024_generate_key_external_entropy( 870MLKEM1024_generate_key_external_entropy(
874 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], 871 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
875 struct MLKEM1024_private_key *out_private_key, 872 MLKEM_private_key *out_private_key,
876 const uint8_t entropy[MLKEM_SEED_BYTES]) 873 const uint8_t entropy[MLKEM_SEED_BYTES])
877{ 874{
878 struct private_key *priv = private_key_1024_from_external( 875 struct private_key *priv = private_key_1024_from_external(
@@ -920,8 +917,8 @@ MLKEM1024_generate_key_external_entropy(
920} 917}
921 918
922void 919void
923MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, 920MLKEM1024_public_from_private(const MLKEM_private_key *private_key,
924 const struct MLKEM1024_private_key *private_key) 921 MLKEM_public_key *out_public_key)
925{ 922{
926 struct public_key *const pub = public_key_1024_from_external( 923 struct public_key *const pub = public_key_1024_from_external(
927 out_public_key); 924 out_public_key);
@@ -930,7 +927,6 @@ MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key,
930 927
931 *pub = priv->pub; 928 *pub = priv->pub;
932} 929}
933LCRYPTO_ALIAS(MLKEM1024_public_from_private);
934 930
935/* 931/*
936 * Encrypts a message with given randomness to the ciphertext in |out|. Without 932 * Encrypts a message with given randomness to the ciphertext in |out|. Without
@@ -972,9 +968,9 @@ encrypt_cpa(uint8_t out[MLKEM1024_CIPHERTEXT_BYTES],
972 968
973/* Calls MLKEM1024_encap_external_entropy| with random bytes */ 969/* Calls MLKEM1024_encap_external_entropy| with random bytes */
974void 970void
975MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], 971MLKEM1024_encap(const MLKEM_public_key *public_key,
976 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 972 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
977 const struct MLKEM1024_public_key *public_key) 973 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES])
978{ 974{
979 uint8_t entropy[MLKEM_ENCAP_ENTROPY]; 975 uint8_t entropy[MLKEM_ENCAP_ENTROPY];
980 976
@@ -982,14 +978,13 @@ MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
982 MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret, 978 MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret,
983 public_key, entropy); 979 public_key, entropy);
984} 980}
985LCRYPTO_ALIAS(MLKEM1024_encap);
986 981
987/* See section 6.2 of the spec. */ 982/* See section 6.2 of the spec. */
988void 983void
989MLKEM1024_encap_external_entropy( 984MLKEM1024_encap_external_entropy(
990 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], 985 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
991 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 986 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
992 const struct MLKEM1024_public_key *public_key, 987 const MLKEM_public_key *public_key,
993 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) 988 const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
994{ 989{
995 const struct public_key *pub = public_key_1024_from_external(public_key); 990 const struct public_key *pub = public_key_1024_from_external(public_key);
@@ -1025,10 +1020,10 @@ decrypt_cpa(uint8_t out[32], const struct private_key *priv,
1025 1020
1026/* See section 6.3 */ 1021/* See section 6.3 */
1027int 1022int
1028MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 1023MLKEM1024_decap(const MLKEM_private_key *private_key,
1029 const uint8_t *ciphertext, size_t ciphertext_len, 1024 const uint8_t *ciphertext, size_t ciphertext_len,
1030 const struct MLKEM1024_private_key *private_key) 1025 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES])
1031{ 1026 {
1032 const struct private_key *priv = private_key_1024_from_external( 1027 const struct private_key *priv = private_key_1024_from_external(
1033 private_key); 1028 private_key);
1034 uint8_t expected_ciphertext[MLKEM1024_CIPHERTEXT_BYTES]; 1029 uint8_t expected_ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
@@ -1059,11 +1054,10 @@ MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
1059 1054
1060 return 1; 1055 return 1;
1061} 1056}
1062LCRYPTO_ALIAS(MLKEM1024_decap);
1063 1057
1064int 1058int
1065MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, 1059MLKEM1024_marshal_public_key(const MLKEM_public_key *public_key,
1066 const struct MLKEM1024_public_key *public_key) 1060 uint8_t **output, size_t *output_len)
1067{ 1061{
1068 int ret = 0; 1062 int ret = 0;
1069 CBB cbb; 1063 CBB cbb;
@@ -1083,7 +1077,6 @@ MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len,
1083 1077
1084 return ret; 1078 return ret;
1085} 1079}
1086LCRYPTO_ALIAS(MLKEM1024_marshal_public_key);
1087 1080
1088/* 1081/*
1089 * mlkem_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate 1082 * mlkem_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate
@@ -1107,8 +1100,8 @@ mlkem_parse_public_key_no_hash(struct public_key *pub, CBS *in)
1107} 1100}
1108 1101
1109int 1102int
1110MLKEM1024_parse_public_key(struct MLKEM1024_public_key *public_key, 1103MLKEM1024_parse_public_key(const uint8_t *input, size_t input_len,
1111 const uint8_t *input, size_t input_len) 1104 MLKEM_public_key *public_key)
1112{ 1105{
1113 struct public_key *pub = public_key_1024_from_external(public_key); 1106 struct public_key *pub = public_key_1024_from_external(public_key);
1114 CBS cbs; 1107 CBS cbs;
@@ -1123,10 +1116,9 @@ MLKEM1024_parse_public_key(struct MLKEM1024_public_key *public_key,
1123 1116
1124 return 1; 1117 return 1;
1125} 1118}
1126LCRYPTO_ALIAS(MLKEM1024_parse_public_key);
1127 1119
1128int 1120int
1129MLKEM1024_marshal_private_key(const struct MLKEM1024_private_key *private_key, 1121MLKEM1024_marshal_private_key(const MLKEM_private_key *private_key,
1130 uint8_t **out_private_key, size_t *out_private_key_len) 1122 uint8_t **out_private_key, size_t *out_private_key_len)
1131{ 1123{
1132 const struct private_key *const priv = private_key_1024_from_external( 1124 const struct private_key *const priv = private_key_1024_from_external(
@@ -1160,8 +1152,8 @@ MLKEM1024_marshal_private_key(const struct MLKEM1024_private_key *private_key,
1160} 1152}
1161 1153
1162int 1154int
1163MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, 1155MLKEM1024_parse_private_key(const uint8_t *input, size_t input_len,
1164 const uint8_t *input, size_t input_len) 1156 MLKEM_private_key *out_private_key)
1165{ 1157{
1166 struct private_key *const priv = private_key_1024_from_external( 1158 struct private_key *const priv = private_key_1024_from_external(
1167 out_private_key); 1159 out_private_key);
@@ -1189,4 +1181,3 @@ MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key,
1189 1181
1190 return 1; 1182 return 1;
1191} 1183}
1192LCRYPTO_ALIAS(MLKEM1024_parse_private_key);
diff --git a/src/lib/libcrypto/mlkem/mlkem768.c b/src/lib/libcrypto/mlkem/mlkem768.c
index 653b92d8d8..1a44b9413f 100644
--- a/src/lib/libcrypto/mlkem/mlkem768.c
+++ b/src/lib/libcrypto/mlkem/mlkem768.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem768.c,v 1.12 2025/05/20 00:30:38 beck Exp $ */ 1/* $OpenBSD: mlkem768.c,v 1.13 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>
@@ -19,19 +19,16 @@
19#include <assert.h> 19#include <assert.h>
20#include <stdlib.h> 20#include <stdlib.h>
21#include <string.h> 21#include <string.h>
22#include <stdio.h>
22 23
23#include "bytestring.h" 24#include <openssl/mlkem.h>
24#include "mlkem.h"
25 25
26#include "bytestring.h"
26#include "sha3_internal.h" 27#include "sha3_internal.h"
27#include "mlkem_internal.h" 28#include "mlkem_internal.h"
28#include "constant_time.h" 29#include "constant_time.h"
29#include "crypto_internal.h" 30#include "crypto_internal.h"
30 31
31/* Remove later */
32#undef LCRYPTO_ALIAS
33#define LCRYPTO_ALIAS(A)
34
35/* 32/*
36 * See 33 * See
37 * https://csrc.nist.gov/pubs/fips/203/final 34 * https://csrc.nist.gov/pubs/fips/203/final
@@ -80,7 +77,6 @@ kdf(uint8_t out[MLKEM_SHARED_SECRET_BYTES], const uint8_t failure_secret[32],
80} 77}
81 78
82#define DEGREE 256 79#define DEGREE 256
83#define RANK768 3
84 80
85static const size_t kBarrettMultiplier = 5039; 81static const size_t kBarrettMultiplier = 5039;
86static const unsigned kBarrettShift = 24; 82static const unsigned kBarrettShift = 24;
@@ -809,9 +805,11 @@ struct public_key {
809CTASSERT(sizeof(struct MLKEM768_public_key) == sizeof(struct public_key)); 805CTASSERT(sizeof(struct MLKEM768_public_key) == sizeof(struct public_key));
810 806
811static struct public_key * 807static struct public_key *
812public_key_768_from_external(const struct MLKEM768_public_key *external) 808public_key_768_from_external(const MLKEM_public_key *external)
813{ 809{
814 return (struct public_key *)external; 810 if (external->rank != RANK768)
811 return NULL;
812 return (struct public_key *)external->key_768;
815} 813}
816 814
817struct private_key { 815struct private_key {
@@ -823,9 +821,11 @@ struct private_key {
823CTASSERT(sizeof(struct MLKEM768_private_key) == sizeof(struct private_key)); 821CTASSERT(sizeof(struct MLKEM768_private_key) == sizeof(struct private_key));
824 822
825static struct private_key * 823static struct private_key *
826private_key_768_from_external(const struct MLKEM768_private_key *external) 824private_key_768_from_external(const MLKEM_private_key *external)
827{ 825{
828 return (struct private_key *)external; 826 if (external->rank != RANK768)
827 return NULL;
828 return (struct private_key *)external->key_768;
829} 829}
830 830
831/* 831/*
@@ -835,7 +835,7 @@ private_key_768_from_external(const struct MLKEM768_private_key *external)
835int 835int
836MLKEM768_generate_key(uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], 836MLKEM768_generate_key(uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
837 uint8_t optional_out_seed[MLKEM_SEED_BYTES], 837 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
838 struct MLKEM768_private_key *out_private_key) 838 MLKEM_private_key *out_private_key)
839{ 839{
840 uint8_t entropy_buf[MLKEM_SEED_BYTES]; 840 uint8_t entropy_buf[MLKEM_SEED_BYTES];
841 uint8_t *entropy = optional_out_seed != NULL ? optional_out_seed : 841 uint8_t *entropy = optional_out_seed != NULL ? optional_out_seed :
@@ -845,12 +845,12 @@ MLKEM768_generate_key(uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
845 return MLKEM768_generate_key_external_entropy(out_encoded_public_key, 845 return MLKEM768_generate_key_external_entropy(out_encoded_public_key,
846 out_private_key, entropy); 846 out_private_key, entropy);
847} 847}
848LCRYPTO_ALIAS(MLKEM768_generate_key);
849 848
850int 849int
851MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, 850MLKEM768_private_key_from_seed(const uint8_t *seed, size_t seed_len,
852 const uint8_t *seed, size_t seed_len) 851 MLKEM_private_key *out_private_key)
853{ 852{
853 /* XXX stack */
854 uint8_t public_key_bytes[MLKEM768_PUBLIC_KEY_BYTES]; 854 uint8_t public_key_bytes[MLKEM768_PUBLIC_KEY_BYTES];
855 855
856 if (seed_len != MLKEM_SEED_BYTES) { 856 if (seed_len != MLKEM_SEED_BYTES) {
@@ -859,7 +859,6 @@ MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key,
859 return MLKEM768_generate_key_external_entropy(public_key_bytes, 859 return MLKEM768_generate_key_external_entropy(public_key_bytes,
860 out_private_key, seed); 860 out_private_key, seed);
861} 861}
862LCRYPTO_ALIAS(MLKEM768_private_key_from_seed);
863 862
864static int 863static int
865mlkem_marshal_public_key(CBB *out, const struct public_key *pub) 864mlkem_marshal_public_key(CBB *out, const struct public_key *pub)
@@ -872,7 +871,7 @@ mlkem_marshal_public_key(CBB *out, const struct public_key *pub)
872int 871int
873MLKEM768_generate_key_external_entropy( 872MLKEM768_generate_key_external_entropy(
874 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], 873 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
875 struct MLKEM768_private_key *out_private_key, 874 MLKEM_private_key *out_private_key,
876 const uint8_t entropy[MLKEM_SEED_BYTES]) 875 const uint8_t entropy[MLKEM_SEED_BYTES])
877{ 876{
878 struct private_key *priv = private_key_768_from_external( 877 struct private_key *priv = private_key_768_from_external(
@@ -920,9 +919,8 @@ MLKEM768_generate_key_external_entropy(
920} 919}
921 920
922void 921void
923MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key, 922MLKEM768_public_from_private(const MLKEM_private_key *private_key,
924 const struct MLKEM768_private_key *private_key) 923 MLKEM_public_key *out_public_key) {
925{
926 struct public_key *const pub = public_key_768_from_external( 924 struct public_key *const pub = public_key_768_from_external(
927 out_public_key); 925 out_public_key);
928 const struct private_key *const priv = private_key_768_from_external( 926 const struct private_key *const priv = private_key_768_from_external(
@@ -930,7 +928,6 @@ MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key,
930 928
931 *pub = priv->pub; 929 *pub = priv->pub;
932} 930}
933LCRYPTO_ALIAS(MLKEM768_public_from_private);
934 931
935/* 932/*
936 * Encrypts a message with given randomness to the ciphertext in |out|. Without 933 * Encrypts a message with given randomness to the ciphertext in |out|. Without
@@ -972,9 +969,9 @@ encrypt_cpa(uint8_t out[MLKEM768_CIPHERTEXT_BYTES],
972 969
973/* Calls MLKEM768_encap_external_entropy| with random bytes */ 970/* Calls MLKEM768_encap_external_entropy| with random bytes */
974void 971void
975MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], 972MLKEM768_encap(const MLKEM_public_key *public_key,
976 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 973 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
977 const struct MLKEM768_public_key *public_key) 974 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES])
978{ 975{
979 uint8_t entropy[MLKEM_ENCAP_ENTROPY]; 976 uint8_t entropy[MLKEM_ENCAP_ENTROPY];
980 977
@@ -982,14 +979,13 @@ MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
982 MLKEM768_encap_external_entropy(out_ciphertext, 979 MLKEM768_encap_external_entropy(out_ciphertext,
983 out_shared_secret, public_key, entropy); 980 out_shared_secret, public_key, entropy);
984} 981}
985LCRYPTO_ALIAS(MLKEM768_encap);
986 982
987/* See section 6.2 of the spec. */ 983/* See section 6.2 of the spec. */
988void 984void
989MLKEM768_encap_external_entropy( 985MLKEM768_encap_external_entropy(
990 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], 986 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
991 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 987 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
992 const struct MLKEM768_public_key *public_key, 988 const MLKEM_public_key *public_key,
993 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) 989 const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
994{ 990{
995 const struct public_key *pub = public_key_768_from_external(public_key); 991 const struct public_key *pub = public_key_768_from_external(public_key);
@@ -1025,9 +1021,8 @@ decrypt_cpa(uint8_t out[32], const struct private_key *priv,
1025 1021
1026/* See section 6.3 */ 1022/* See section 6.3 */
1027int 1023int
1028MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 1024MLKEM768_decap(const MLKEM_private_key *private_key, const uint8_t *ciphertext,
1029 const uint8_t *ciphertext, size_t ciphertext_len, 1025 size_t ciphertext_len, uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES])
1030 const struct MLKEM768_private_key *private_key)
1031{ 1026{
1032 const struct private_key *priv = private_key_768_from_external( 1027 const struct private_key *priv = private_key_768_from_external(
1033 private_key); 1028 private_key);
@@ -1059,11 +1054,10 @@ MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
1059 1054
1060 return 1; 1055 return 1;
1061} 1056}
1062LCRYPTO_ALIAS(MLKEM768_decap);
1063 1057
1064int 1058int
1065MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, 1059MLKEM768_marshal_public_key(const MLKEM_public_key *public_key,
1066 const struct MLKEM768_public_key *public_key) 1060 uint8_t **output, size_t *output_len)
1067{ 1061{
1068 int ret = 0; 1062 int ret = 0;
1069 CBB cbb; 1063 CBB cbb;
@@ -1083,7 +1077,6 @@ MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len,
1083 1077
1084 return ret; 1078 return ret;
1085} 1079}
1086LCRYPTO_ALIAS(MLKEM768_marshal_public_key);
1087 1080
1088/* 1081/*
1089 * mlkem_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate 1082 * mlkem_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate
@@ -1107,8 +1100,8 @@ mlkem_parse_public_key_no_hash(struct public_key *pub, CBS *in)
1107} 1100}
1108 1101
1109int 1102int
1110MLKEM768_parse_public_key(struct MLKEM768_public_key *public_key, 1103MLKEM768_parse_public_key(const uint8_t *input, size_t input_len,
1111 const uint8_t *input, size_t input_len) 1104 MLKEM_public_key *public_key)
1112{ 1105{
1113 struct public_key *pub = public_key_768_from_external(public_key); 1106 struct public_key *pub = public_key_768_from_external(public_key);
1114 CBS cbs; 1107 CBS cbs;
@@ -1123,10 +1116,9 @@ MLKEM768_parse_public_key(struct MLKEM768_public_key *public_key,
1123 1116
1124 return 1; 1117 return 1;
1125} 1118}
1126LCRYPTO_ALIAS(MLKEM768_parse_public_key);
1127 1119
1128int 1120int
1129MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key, 1121MLKEM768_marshal_private_key(const MLKEM_private_key *private_key,
1130 uint8_t **out_private_key, size_t *out_private_key_len) 1122 uint8_t **out_private_key, size_t *out_private_key_len)
1131{ 1123{
1132 const struct private_key *const priv = private_key_768_from_external( 1124 const struct private_key *const priv = private_key_768_from_external(
@@ -1160,8 +1152,8 @@ MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key,
1160} 1152}
1161 1153
1162int 1154int
1163MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key, 1155MLKEM768_parse_private_key(const uint8_t *input, size_t input_len,
1164 const uint8_t *input, size_t input_len) 1156 MLKEM_private_key *out_private_key)
1165{ 1157{
1166 struct private_key *const priv = private_key_768_from_external( 1158 struct private_key *const priv = private_key_768_from_external(
1167 out_private_key); 1159 out_private_key);
@@ -1189,4 +1181,3 @@ MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key,
1189 1181
1190 return 1; 1182 return 1;
1191} 1183}
1192LCRYPTO_ALIAS(MLKEM768_parse_private_key);
diff --git a/src/lib/libcrypto/mlkem/mlkem_internal.h b/src/lib/libcrypto/mlkem/mlkem_internal.h
index 1e051970a8..776f8aac17 100644
--- a/src/lib/libcrypto/mlkem/mlkem_internal.h
+++ b/src/lib/libcrypto/mlkem/mlkem_internal.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_internal.h,v 1.7 2025/05/20 00:33:40 beck Exp $ */ 1/* $OpenBSD: mlkem_internal.h,v 1.8 2025/08/14 15:48:48 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2023, Google Inc. 3 * Copyright (c) 2023, Google Inc.
4 * 4 *
@@ -26,6 +26,253 @@ extern "C" {
26#endif 26#endif
27 27
28__BEGIN_HIDDEN_DECLS 28__BEGIN_HIDDEN_DECLS
29/*
30 * MLKEM_SEED_LENGTH is the number of bytes in an ML-KEM seed. An ML-KEM
31 * seed is normally used to represent a private key.
32 */
33#define MLKEM_SEED_LENGTH 64
34
35/*
36 * MLKEM_SHARED_SECRET_LENGTH is the number of bytes in an ML-KEM shared
37 * secret.
38 */
39#define MLKEM_SHARED_SECRET_LENGTH 32
40
41/*
42 * |MLKEM_encap_external_entropy| behaves exactly like the public |MLKEM_encap|
43 * with the entropy provided by the caller. It is directly called internally
44 * and by tests.
45 */
46int
47MLKEM_encap_external_entropy(const MLKEM_public_key *public_key,
48 const uint8_t *entropy, uint8_t **out_ciphertext,
49 size_t *out_ciphertext_len, uint8_t **out_shared_secret,
50 size_t *out_shared_secret_len);
51
52/*
53 * |MLKEM_generate_key_external_entropy| behaves exactly like the public
54 * |MLKEM_generate_key| with the entropy provided by the caller.
55 * It is directly called internally and by tests.
56 */
57int
58MLKEM_generate_key_external_entropy(MLKEM_private_key *private_key,
59 uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len,
60 const uint8_t *entropy);
61/*
62 * Marshals a private key to encoded format, used for NIST tests.
63 */
64int MLKEM_marshal_private_key(const MLKEM_private_key *private_key,
65 uint8_t **out, size_t *out_len);
66
67/*
68 * ML-KEM-768
69 *
70 * This implements the Module-Lattice-Based Key-Encapsulation Mechanism from
71 * https://csrc.nist.gov/pubs/fips/204/final
72 */
73
74/*
75 * MLKEM768_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM768 public
76 * key.
77 */
78#define MLKEM768_PUBLIC_KEY_BYTES 1184
79
80/* MLKEM_SEED_BYTES is the number of bytes in an ML-KEM seed. */
81#define MLKEM_SEED_BYTES 64
82
83/*
84 * MLKEM_SHARED_SECRET_BYTES is the number of bytes in the ML-KEM768 shared
85 * secret. Although the round-3 specification has a variable-length output, the
86 * final ML-KEM construction is expected to use a fixed 32-byte output. To
87 * simplify the future transition, we apply the same restriction.
88 */
89#define MLKEM_SHARED_SECRET_BYTES 32
90
91/*
92 * MLKEM_generate_key generates a random public/private key pair, writes the
93 * encoded public key to |out_encoded_public_key| and sets |out_private_key| to
94 * the private key. If |optional_out_seed| is not NULL then the seed used to
95 * generate the private key is written to it.
96 */
97int MLKEM768_generate_key(
98 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
99 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
100 MLKEM_private_key *out_private_key);
101
102/*
103 * MLKEM768_private_key_from_seed derives a private key from a seed that was
104 * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is
105 * incorrect, otherwise it writes |*out_private_key| and returns 1.
106 */
107int MLKEM768_private_key_from_seed(const uint8_t *seed, size_t seed_len,
108 MLKEM_private_key *out_private_key);
109
110/*
111 * MLKEM_public_from_private sets |*out_public_key| to the public key that
112 * corresponds to |private_key|. (This is faster than parsing the output of
113 * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key
114 * that was just generated.)
115 */
116void MLKEM768_public_from_private(const MLKEM_private_key *private_key,
117 MLKEM_public_key *out_public_key);
118
119/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */
120#define MLKEM768_CIPHERTEXT_BYTES 1088
121
122/*
123 * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the
124 * ciphertext to |out_ciphertext|, and writes the random shared secret to
125 * |out_shared_secret|.
126 */
127void MLKEM768_encap(const MLKEM_public_key *public_key,
128 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
129 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]);
130
131/*
132 * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key|
133 * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it
134 * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid,
135 * |out_shared_secret| is filled with a key that will always be the same for the
136 * same |ciphertext| and |private_key|, but which appears to be random unless
137 * one has access to |private_key|. These alternatives occur in constant time.
138 * Any subsequent symmetric encryption using |out_shared_secret| must use an
139 * authenticated encryption scheme in order to discover the decapsulation
140 * failure.
141 */
142int MLKEM768_decap(const MLKEM_private_key *private_key,
143 const uint8_t *ciphertext, size_t ciphertext_len,
144 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]);
145
146/* Serialisation of keys. */
147
148/*
149 * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard
150 * format for ML-KEM public keys. It returns one on success or zero on allocation
151 * error.
152 */
153int MLKEM768_marshal_public_key(const MLKEM_public_key *public_key,
154 uint8_t **output, size_t *output_len);
155
156/*
157 * MLKEM768_parse_public_key parses a public key, in the format generated by
158 * |MLKEM_marshal_public_key|, from |in| and writes the result to
159 * |out_public_key|. It returns one on success or zero on parse error or if
160 * there are trailing bytes in |in|.
161 */
162int MLKEM768_parse_public_key(const uint8_t *input, size_t input_len,
163 MLKEM_public_key *out_public_key);
164
165/*
166 * MLKEM_parse_private_key parses a private key, in the format generated by
167 * |MLKEM_marshal_private_key|, from |in| and writes the result to
168 * |out_private_key|. It returns one on success or zero on parse error or if
169 * there are trailing bytes in |in|. This formate is verbose and should be avoided.
170 * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|.
171 */
172int MLKEM768_parse_private_key(const uint8_t *input, size_t input_len,
173 MLKEM_private_key *out_private_key);
174
175/*
176 * ML-KEM-1024
177 *
178 * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible.
179 */
180
181/*
182 * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024
183 * public key.
184 */
185#define MLKEM1024_PUBLIC_KEY_BYTES 1568
186
187/*
188 * MLKEM1024_generate_key generates a random public/private key pair, writes the
189 * encoded public key to |out_encoded_public_key| and sets |out_private_key| to
190 * the private key. If |optional_out_seed| is not NULL then the seed used to
191 * generate the private key is written to it.
192 */
193int MLKEM1024_generate_key(
194 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
195 uint8_t optional_out_seed[MLKEM_SEED_BYTES],
196 MLKEM_private_key *out_private_key);
197
198/*
199 * MLKEM1024_private_key_from_seed derives a private key from a seed that was
200 * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len|
201 * is incorrect, otherwise it writes |*out_private_key| and returns 1.
202 */
203int MLKEM1024_private_key_from_seed(
204 MLKEM_private_key *out_private_key, const uint8_t *seed,
205 size_t seed_len);
206
207/*
208 * MLKEM1024_public_from_private sets |*out_public_key| to the public key that
209 * corresponds to |private_key|. (This is faster than parsing the output of
210 * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a
211 * key that was just generated.)
212 */
213void MLKEM1024_public_from_private(const MLKEM_private_key *private_key,
214 MLKEM_public_key *out_public_key);
215
216/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */
217#define MLKEM1024_CIPHERTEXT_BYTES 1568
218
219/*
220 * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the
221 * ciphertext to |out_ciphertext|, and writes the random shared secret to
222 * |out_shared_secret|.
223 */
224void MLKEM1024_encap(const MLKEM_public_key *public_key,
225 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
226 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]);
227
228
229/*
230 * MLKEM1024_decap decrypts a shared secret from |ciphertext| using
231 * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is
232 * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid
233 * (but of the correct length), |out_shared_secret| is filled with a key that
234 * will always be the same for the same |ciphertext| and |private_key|, but
235 * which appears to be random unless one has access to |private_key|. These
236 * alternatives occur in constant time. Any subsequent symmetric encryption
237 * using |out_shared_secret| must use an authenticated encryption scheme in
238 * order to discover the decapsulation failure.
239 */
240int MLKEM1024_decap(const MLKEM_private_key *private_key,
241 const uint8_t *ciphertext, size_t ciphertext_len,
242 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]);
243
244/*
245 * Serialisation of ML-KEM-1024 keys.
246 * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard
247 * format for ML-KEM-1024 public keys. It returns one on success or zero on
248 * allocation error.
249 */
250int MLKEM1024_marshal_public_key(const MLKEM_public_key *public_key,
251 uint8_t **output, size_t *output_len);
252
253
254/*
255 * MLKEM1024_parse_public_key parses a public key, in the format generated by
256 * |MLKEM1024_marshal_public_key|, from |in| and writes the result to
257 * |out_public_key|. It returns one on success or zero on parse error or if
258 * there are trailing bytes in |in|.
259 */
260int MLKEM1024_parse_public_key(const uint8_t *input, size_t input_len,
261 MLKEM_public_key *out_public_key);
262
263
264/*
265 * MLKEM1024_parse_private_key parses a private key, in NIST's format for
266 * private keys, from |in| and writes the result to |out_private_key|. It
267 * returns one on success or zero on parse error or if there are trailing bytes
268 * in |in|. This format is verbose and should be avoided. Private keys should be
269 * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|.
270 */
271int MLKEM1024_parse_private_key(const uint8_t *input, size_t input_len,
272 MLKEM_private_key *out_private_key);
273
274
275/* XXXX Truly internal stuff below, also in need of de-duping */
29 276
30/* 277/*
31 * MLKEM_ENCAP_ENTROPY is the number of bytes of uniformly random entropy 278 * MLKEM_ENCAP_ENTROPY is the number of bytes of uniformly random entropy
@@ -35,6 +282,49 @@ __BEGIN_HIDDEN_DECLS
35#define MLKEM_ENCAP_ENTROPY 32 282#define MLKEM_ENCAP_ENTROPY 32
36 283
37/* 284/*
285 * MLKEM768_public_key contains a ML-KEM-768 public key. The contents of this
286 * object should never leave the address space since the format is unstable.
287 */
288struct MLKEM768_public_key {
289 union {
290 uint8_t bytes[512 * (3 + 9) + 32 + 32];
291 uint16_t alignment;
292 } opaque;
293};
294
295/*
296 * MLKEM768_private_key contains a ML-KEM-768 private key. The contents of this
297 * object should never leave the address space since the format is unstable.
298 */
299struct MLKEM768_private_key {
300 union {
301 uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32];
302 uint16_t alignment;
303 } opaque;
304};
305
306/* Public opaque ML-KEM key structures. */
307
308#define MLKEM_PUBLIC_KEY_UNINITIALIZED 1
309#define MLKEM_PUBLIC_KEY_INITIALIZED 2
310#define MLKEM_PRIVATE_KEY_UNINITIALIZED 3
311#define MLKEM_PRIVATE_KEY_INITIALIZED 4
312
313struct MLKEM_public_key_st {
314 uint16_t rank;
315 int state;
316 struct MLKEM768_public_key *key_768;
317 struct MLKEM1024_public_key *key_1024;
318};
319
320struct MLKEM_private_key_st {
321 uint16_t rank;
322 int state;
323 struct MLKEM768_private_key *key_768;
324 struct MLKEM1024_private_key *key_1024;
325};
326
327/*
38 * MLKEM768_generate_key_external_entropy is a deterministic function to create a 328 * MLKEM768_generate_key_external_entropy is a deterministic function to create a
39 * pair of ML-KEM 768 keys, using the supplied entropy. The entropy needs to be 329 * pair of ML-KEM 768 keys, using the supplied entropy. The entropy needs to be
40 * uniformly random generated. This function is should only be used for tests, 330 * uniformly random generated. This function is should only be used for tests,
@@ -43,7 +333,7 @@ __BEGIN_HIDDEN_DECLS
43 */ 333 */
44int MLKEM768_generate_key_external_entropy( 334int MLKEM768_generate_key_external_entropy(
45 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], 335 uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES],
46 struct MLKEM768_private_key *out_private_key, 336 MLKEM_private_key *out_private_key,
47 const uint8_t entropy[MLKEM_SEED_BYTES]); 337 const uint8_t entropy[MLKEM_SEED_BYTES]);
48 338
49/* 339/*
@@ -57,11 +347,11 @@ int MLKEM768_generate_key_external_entropy(
57 * format for ML-KEM private keys. It returns one on success or zero on 347 * format for ML-KEM private keys. It returns one on success or zero on
58 * allocation error. 348 * allocation error.
59 */ 349 */
60int MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key, 350int MLKEM768_marshal_private_key(const MLKEM_private_key *private_key,
61 uint8_t **out_private_key, size_t *out_private_key_len); 351 uint8_t **out_private_key, size_t *out_private_key_len);
62 352
63/* 353/*
64 * MLKEM_encap_external_entropy behaves like |MLKEM_encap|, but uses 354 * MLKEM768_encap_external_entropy behaves like |MLKEM768_encap|, but uses
65 * |MLKEM_ENCAP_ENTROPY| bytes of |entropy| for randomization. The decapsulating 355 * |MLKEM_ENCAP_ENTROPY| bytes of |entropy| for randomization. The decapsulating
66 * side will be able to recover |entropy| in full. This function should only be 356 * side will be able to recover |entropy| in full. This function should only be
67 * used for tests, regular callers should use the non-deterministic 357 * used for tests, regular callers should use the non-deterministic
@@ -70,9 +360,34 @@ int MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key,
70void MLKEM768_encap_external_entropy( 360void MLKEM768_encap_external_entropy(
71 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], 361 uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES],
72 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 362 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
73 const struct MLKEM768_public_key *public_key, 363 const MLKEM_public_key *public_key,
74 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); 364 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
75 365
366
367/*
368 * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this
369 * object should never leave the address space since the format is unstable.
370 */
371struct MLKEM1024_public_key {
372 union {
373 uint8_t bytes[512 * (4 + 16) + 32 + 32];
374 uint16_t alignment;
375 } opaque;
376};
377
378/*
379 * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of
380 * this object should never leave the address space since the format is
381 * unstable.
382 */
383struct MLKEM1024_private_key {
384 union {
385 uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32];
386 uint16_t alignment;
387 } opaque;
388};
389
390
76/* 391/*
77 * MLKEM1024_generate_key_external_entropy is a deterministic function to create a 392 * MLKEM1024_generate_key_external_entropy is a deterministic function to create a
78 * pair of ML-KEM 1024 keys, using the supplied entropy. The entropy needs to be 393 * pair of ML-KEM 1024 keys, using the supplied entropy. The entropy needs to be
@@ -82,7 +397,7 @@ void MLKEM768_encap_external_entropy(
82 */ 397 */
83int MLKEM1024_generate_key_external_entropy( 398int MLKEM1024_generate_key_external_entropy(
84 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], 399 uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES],
85 struct MLKEM1024_private_key *out_private_key, 400 MLKEM_private_key *out_private_key,
86 const uint8_t entropy[MLKEM_SEED_BYTES]); 401 const uint8_t entropy[MLKEM_SEED_BYTES]);
87 402
88/* 403/*
@@ -97,7 +412,7 @@ int MLKEM1024_generate_key_external_entropy(
97 * allocation error. 412 * allocation error.
98 */ 413 */
99int MLKEM1024_marshal_private_key( 414int MLKEM1024_marshal_private_key(
100 const struct MLKEM1024_private_key *private_key, uint8_t **out_private_key, 415 const MLKEM_private_key *private_key, uint8_t **out_private_key,
101 size_t *out_private_key_len); 416 size_t *out_private_key_len);
102 417
103/* 418/*
@@ -110,7 +425,7 @@ int MLKEM1024_marshal_private_key(
110void MLKEM1024_encap_external_entropy( 425void MLKEM1024_encap_external_entropy(
111 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], 426 uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES],
112 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], 427 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
113 const struct MLKEM1024_public_key *public_key, 428 const MLKEM_public_key *public_key,
114 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); 429 const uint8_t entropy[MLKEM_ENCAP_ENTROPY]);
115 430
116__END_HIDDEN_DECLS 431__END_HIDDEN_DECLS
diff --git a/src/lib/libcrypto/mlkem/mlkem_key.c b/src/lib/libcrypto/mlkem/mlkem_key.c
new file mode 100644
index 0000000000..051d8f2b88
--- /dev/null
+++ b/src/lib/libcrypto/mlkem/mlkem_key.c
@@ -0,0 +1,200 @@
1/* $OpenBSD: mlkem_key.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */
2/*
3 * Copyright (c) 2025 Bob Beck <beck@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <stdlib.h>
19#include <string.h>
20
21#include <openssl/mlkem.h>
22
23#include "mlkem_internal.h"
24
25MLKEM_private_key *
26MLKEM_private_key_new(int rank)
27{
28 struct MLKEM1024_private_key *key_1024 = NULL;
29 struct MLKEM768_private_key *key_768 = NULL;
30 MLKEM_private_key *key = NULL;
31 MLKEM_private_key *ret = NULL;
32
33 if ((key = calloc(1, sizeof(MLKEM_private_key))) == NULL)
34 goto err;
35
36 switch (rank) {
37 case RANK768:
38 if ((key_768 = calloc(1, sizeof(*key_768))) ==
39 NULL)
40 goto err;
41 key->key_768 = key_768;
42 break;
43 case RANK1024:
44 if ((key_1024 = calloc(1, sizeof(*key_1024))) ==
45 NULL)
46 goto err;
47 key->key_1024 = key_1024;
48 break;
49 default:
50 goto err;
51 }
52 key->rank = rank;
53 key->state = MLKEM_PRIVATE_KEY_UNINITIALIZED;
54
55 ret = key;
56 key= NULL;
57
58 err:
59 MLKEM_private_key_free(key);
60
61 return ret;
62}
63LCRYPTO_ALIAS(MLKEM_private_key_new);
64
65void
66MLKEM_private_key_free(MLKEM_private_key *key)
67{
68 if (key == NULL)
69 return;
70
71 freezero(key->key_768, sizeof(*key->key_768));
72 freezero(key->key_1024, sizeof(*key->key_1024));
73 freezero(key, sizeof(*key));
74}
75LCRYPTO_ALIAS(MLKEM_private_key_free);
76
77size_t
78MLKEM_private_key_encoded_length(const MLKEM_private_key *key)
79{
80 if (key == NULL)
81 return 0;
82
83 switch (key->rank) {
84 case RANK768:
85 return MLKEM768_PRIVATE_KEY_BYTES;
86 case RANK1024:
87 return MLKEM1024_PRIVATE_KEY_BYTES;
88 default:
89 return 0;
90 }
91 return 0;
92}
93LCRYPTO_ALIAS(MLKEM_private_key_encoded_length);
94
95size_t
96MLKEM_private_key_ciphertext_length(const MLKEM_private_key *key)
97{
98 if (key == NULL)
99 return 0;
100
101 switch (key->rank) {
102 case RANK768:
103 return MLKEM768_CIPHERTEXT_BYTES;
104 case RANK1024:
105 return MLKEM1024_CIPHERTEXT_BYTES;
106 default:
107 return 0;
108 }
109 return 0;
110}
111LCRYPTO_ALIAS(MLKEM_private_key_ciphertext_length);
112
113MLKEM_public_key *
114MLKEM_public_key_new(int rank)
115{
116 struct MLKEM1024_public_key *key_1024 = NULL;
117 struct MLKEM768_public_key *key_768 = NULL;
118 MLKEM_public_key *key = NULL;
119 MLKEM_public_key *ret = NULL;
120
121 if ((key = calloc(1, sizeof(MLKEM_public_key))) == NULL)
122 goto err;
123
124 switch (rank) {
125 case RANK768:
126 if ((key_768 = calloc(1, sizeof(*key_768))) ==
127 NULL)
128 goto err;
129 key->key_768 = key_768;
130 break;
131 case RANK1024:
132 if ((key_1024 = calloc(1, sizeof(*key_1024))) ==
133 NULL)
134 goto err;
135 key->key_1024 = key_1024;
136 break;
137 default:
138 goto err;
139 }
140
141 key->rank = rank;
142 key->state = MLKEM_PUBLIC_KEY_UNINITIALIZED;
143
144 ret = key;
145 key = NULL;
146
147 err:
148 MLKEM_public_key_free(key);
149
150 return ret;
151}
152LCRYPTO_ALIAS(MLKEM_public_key_new);
153
154void
155MLKEM_public_key_free(MLKEM_public_key *key)
156{
157 if (key == NULL)
158 return;
159
160 freezero(key->key_768, sizeof(*key->key_768));
161 freezero(key->key_1024, sizeof(*key->key_1024));
162 freezero(key, sizeof(*key));
163}
164LCRYPTO_ALIAS(MLKEM_public_key_free);
165
166size_t
167MLKEM_public_key_encoded_length(const MLKEM_public_key *key)
168{
169 if (key == NULL)
170 return 0;
171
172 switch (key->rank) {
173 case RANK768:
174 return MLKEM768_PUBLIC_KEY_BYTES;
175 case RANK1024:
176 return MLKEM1024_PUBLIC_KEY_BYTES;
177 default:
178 return 0;
179 }
180 return 0;
181}
182LCRYPTO_ALIAS(MLKEM_public_key_encoded_length);
183
184size_t
185MLKEM_public_key_ciphertext_length(const MLKEM_public_key *key)
186{
187 if (key == NULL)
188 return 0;
189
190 switch (key->rank) {
191 case RANK768:
192 return MLKEM768_CIPHERTEXT_BYTES;
193 case RANK1024:
194 return MLKEM1024_CIPHERTEXT_BYTES;
195 default:
196 return 0;
197 }
198 return 0;
199}
200LCRYPTO_ALIAS(MLKEM_public_key_ciphertext_length);
diff --git a/src/lib/libssl/ssl_rsa.c b/src/lib/libssl/ssl_rsa.c
index 6c8a2be3d3..a83779e4c7 100644
--- a/src/lib/libssl/ssl_rsa.c
+++ b/src/lib/libssl/ssl_rsa.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssl_rsa.c,v 1.51 2023/12/30 06:25:56 tb Exp $ */ 1/* $OpenBSD: ssl_rsa.c,v 1.52 2025/08/14 15:48:48 beck Exp $ */
2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3 * All rights reserved. 3 * All rights reserved.
4 * 4 *
@@ -390,7 +390,7 @@ ssl_set_cert(SSL_CTX *ctx, SSL *ssl, X509 *x)
390 int ssl_err; 390 int ssl_err;
391 int i; 391 int i;
392 392
393 if (!ssl_security_cert(ctx, ssl, x, 1, &ssl_err)) { 393 if (0 && !ssl_security_cert(ctx, ssl, x, 1, &ssl_err)) {
394 SSLerrorx(ssl_err); 394 SSLerrorx(ssl_err);
395 return (0); 395 return (0);
396 } 396 }
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c
index b93243023c..10c4b1f4e0 100644
--- a/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_iteration_tests.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_iteration_tests.c,v 1.5 2025/05/20 00:33:41 beck Exp $ */ 1/* $OpenBSD: mlkem_iteration_tests.c,v 1.6 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>
@@ -21,6 +21,7 @@
21#include <stdint.h> 21#include <stdint.h>
22#include <stdio.h> 22#include <stdio.h>
23#include <stdlib.h> 23#include <stdlib.h>
24#include <string.h>
24 25
25#include "mlkem.h" 26#include "mlkem.h"
26 27
@@ -63,46 +64,49 @@ const uint8_t kExpectedAdam1024[32] = {
63 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 64 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5
64}; 65};
65 66
66struct iteration_ctx {
67 uint8_t *encoded_public_key;
68 size_t encoded_public_key_len;
69 uint8_t *ciphertext;
70 size_t ciphertext_len;
71 uint8_t *invalid_ciphertext;
72 size_t invalid_ciphertext_len;
73 void *priv;
74 void *pub;
75
76 mlkem_marshal_private_key_fn marshal_private_key;
77 mlkem_encap_external_entropy_fn encap_external_entropy;
78 mlkem_generate_key_external_entropy_fn generate_key_external_entropy;
79 mlkem_public_from_private_fn public_from_private;
80 mlkem_decap_fn decap;
81
82 const uint8_t *start;
83 size_t start_len;
84
85 const uint8_t *expected;
86 size_t expected_len;
87};
88
89static int 67static int
90MlkemIterativeTest(struct iteration_ctx *ctx) 68MlkemIterativeTest(int rank)
91{ 69{
92 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; 70 const uint8_t *start, *expected;
71 size_t start_len;
93 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; 72 uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY];
94 uint8_t seed[MLKEM_SEED_BYTES] = {0}; 73 uint8_t seed[MLKEM_SEED_LENGTH] = {0};
74 uint8_t *shared_secret = NULL;
95 sha3_ctx drng, results; 75 sha3_ctx drng, results;
96 uint8_t out[32]; 76 uint8_t out[32];
97 int i; 77 int i;
98 78
79 start = kExpectedSeedStart;
80 start_len = sizeof(kExpectedSeedStart);
81 switch(rank){
82 case RANK768:
83 expected = kExpectedAdam768;
84 break;
85 case RANK1024:
86 expected = kExpectedAdam1024;
87 break;
88 default:
89 errx(1, "invalid rank %d", rank);
90 }
91
99 shake128_init(&drng); 92 shake128_init(&drng);
100 shake128_init(&results); 93 shake128_init(&results);
101 94
102 shake_xof(&drng); 95 shake_xof(&drng);
103 for (i = 0; i < 10000; i++) { 96 for (i = 0; i < 10000; i++) {
104 uint8_t *encoded_private_key = NULL; 97 uint8_t *encoded_public_key = NULL, *ciphertext = NULL,
105 size_t encoded_private_key_len; 98 *encoded_private_key = NULL, *invalid_ciphertext = NULL;
99 size_t encoded_public_key_len, ciphertext_len,
100 encoded_private_key_len, invalid_ciphertext_len;
101 MLKEM_private_key *priv;
102 MLKEM_public_key *pub;
103 size_t s_len = 0;
104
105 /* allocate keys for this iteration */
106 if ((priv = MLKEM_private_key_new(rank)) == NULL)
107 errx(1, "malloc");
108 if ((pub = MLKEM_public_key_new(rank)) == NULL)
109 errx(1, "malloc");
106 110
107 /* 111 /*
108 * This should draw both d and z from DRNG concatenating in 112 * This should draw both d and z from DRNG concatenating in
@@ -110,120 +114,91 @@ MlkemIterativeTest(struct iteration_ctx *ctx)
110 */ 114 */
111 shake_out(&drng, seed, sizeof(seed)); 115 shake_out(&drng, seed, sizeof(seed));
112 if (i == 0) { 116 if (i == 0) {
113 if (compare_data(seed, ctx->start, ctx->start_len, 117 if (compare_data(seed, start, start_len,
114 "seed start") != 0) 118 "seed start") != 0)
115 errx(1, "compare_data"); 119 errx(1, "compare_data");
116 } 120 }
117 121
118 /* generate ek as encoded_public_key */ 122 /* generate ek as encoded_public_key */
119 if (!ctx->generate_key_external_entropy(ctx->encoded_public_key, 123 if (!MLKEM_generate_key_external_entropy(priv,
120 ctx->priv, seed)) { 124 &encoded_public_key, &encoded_public_key_len,
125 seed))
121 errx(1, "generate_key_external_entropy"); 126 errx(1, "generate_key_external_entropy");
122 } 127
123 ctx->public_from_private(ctx->pub, ctx->priv); 128 if (!MLKEM_public_from_private(priv, pub))
129 errx(1, "public_from_private");
124 130
125 /* hash in ek */ 131 /* hash in ek */
126 shake_update(&results, ctx->encoded_public_key, 132 shake_update(&results, encoded_public_key,
127 ctx->encoded_public_key_len); 133 encoded_public_key_len);
128 134
129 /* marshal priv to dk as encoded_private_key */ 135 /* marshal priv to dk as encoded_private_key */
130 if (!ctx->marshal_private_key(ctx->priv, &encoded_private_key, 136 if (!MLKEM_marshal_private_key(priv, &encoded_private_key,
131 &encoded_private_key_len)) 137 &encoded_private_key_len))
132 errx(1, "encode private key"); 138 errx(1, "marshal private key");
133 139
134 /* hash in dk */ 140 /* hash in dk */
135 shake_update(&results, encoded_private_key, 141 shake_update(&results, encoded_private_key,
136 encoded_private_key_len); 142 encoded_private_key_len);
137 143
138 free(encoded_private_key); 144 freezero(encoded_private_key, encoded_private_key_len);
139 145
140 /* draw m as encap entropy from DRNG */ 146 /* draw m as encap entropy from DRNG */
141 shake_out(&drng, encap_entropy, sizeof(encap_entropy)); 147 shake_out(&drng, encap_entropy, sizeof(encap_entropy));
142 148
143 /* generate ct as ciphertext, k as shared_secret */ 149 /* generate ct as ciphertext, k as shared_secret */
144 ctx->encap_external_entropy(ctx->ciphertext, shared_secret, 150 if (!MLKEM_encap_external_entropy(pub, encap_entropy,
145 ctx->pub, encap_entropy); 151 &ciphertext, &ciphertext_len, &shared_secret, &s_len))
152 errx(1, "encap_external_entropy");
146 153
147 /* hash in ct */ 154 /* hash in ct */
148 shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); 155 shake_update(&results, ciphertext, ciphertext_len);
149 /* hash in k */ 156 /* hash in k */
150 shake_update(&results, shared_secret, sizeof(shared_secret)); 157 shake_update(&results, shared_secret, s_len);
158
159 freezero(shared_secret, s_len);
160 shared_secret = NULL;
161
162 invalid_ciphertext_len = ciphertext_len;
163 if ((invalid_ciphertext = calloc(1, invalid_ciphertext_len))
164 == NULL)
165 errx(1, "malloc");
151 166
152 /* draw ct as invalid_ciphertxt from DRNG */ 167 /* draw ct as invalid_ciphertxt from DRNG */
153 shake_out(&drng, ctx->invalid_ciphertext, 168 shake_out(&drng, invalid_ciphertext, invalid_ciphertext_len);
154 ctx->invalid_ciphertext_len);
155 169
156 /* generate k as shared secret from invalid ciphertext */ 170 /* generate k as shared secret from invalid ciphertext */
157 if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, 171 if (!MLKEM_decap(priv, invalid_ciphertext,
158 ctx->invalid_ciphertext_len, ctx->priv)) 172 invalid_ciphertext_len, &shared_secret, &s_len))
159 errx(1, "decap failed"); 173 errx(1, "decap failed, iteration %d", i);
160 174
161 /* hash in k */ 175 /* hash in k */
162 shake_update(&results, shared_secret, sizeof(shared_secret)); 176 shake_update(&results, shared_secret, s_len);
177
178 freezero(shared_secret, s_len);
179 shared_secret = NULL;
180 freezero(invalid_ciphertext, invalid_ciphertext_len);
181 invalid_ciphertext = NULL;
182
183 /* free keys and intermediate products for this iteration */
184 MLKEM_private_key_free(priv);
185 MLKEM_public_key_free(pub);
186 freezero(encoded_public_key, encoded_public_key_len);
187 freezero(ciphertext, ciphertext_len);
163 } 188 }
164 shake_xof(&results); 189 shake_xof(&results);
165 shake_out(&results, out, sizeof(out)); 190 shake_out(&results, out, sizeof(out));
166 191
167 return compare_data(ctx->expected, out, sizeof(out), "final result hash"); 192 return compare_data(expected, out, sizeof(out), "final result hash");
168} 193}
169 194
170int 195int
171main(void) 196main(void)
172{ 197{
173 uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES];
174 uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
175 uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES];
176 struct MLKEM768_private_key priv768;
177 struct MLKEM768_public_key pub768;
178 struct iteration_ctx iteration768 = {
179 .encoded_public_key = encoded_public_key768,
180 .encoded_public_key_len = sizeof(encoded_public_key768),
181 .ciphertext = ciphertext768,
182 .ciphertext_len = sizeof(ciphertext768),
183 .invalid_ciphertext = invalid_ciphertext768,
184 .invalid_ciphertext_len = sizeof(invalid_ciphertext768),
185 .priv = &priv768,
186 .pub = &pub768,
187 .encap_external_entropy = mlkem768_encap_external_entropy,
188 .marshal_private_key = mlkem768_marshal_private_key,
189 .generate_key_external_entropy =
190 mlkem768_generate_key_external_entropy,
191 .public_from_private = mlkem768_public_from_private,
192 .decap = mlkem768_decap,
193 .start = kExpectedSeedStart,
194 .start_len = sizeof(kExpectedSeedStart),
195 .expected = kExpectedAdam768,
196 .expected_len = sizeof(kExpectedAdam768),
197 };
198 uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES];
199 uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
200 uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
201 struct MLKEM1024_private_key priv1024;
202 struct MLKEM1024_public_key pub1024;
203 struct iteration_ctx iteration1024 = {
204 .encoded_public_key = encoded_public_key1024,
205 .encoded_public_key_len = sizeof(encoded_public_key1024),
206 .ciphertext = ciphertext1024,
207 .ciphertext_len = sizeof(ciphertext1024),
208 .invalid_ciphertext = invalid_ciphertext1024,
209 .invalid_ciphertext_len = sizeof(invalid_ciphertext1024),
210 .priv = &priv1024,
211 .pub = &pub1024,
212 .encap_external_entropy = mlkem1024_encap_external_entropy,
213 .marshal_private_key = mlkem1024_marshal_private_key,
214 .generate_key_external_entropy =
215 mlkem1024_generate_key_external_entropy,
216 .public_from_private = mlkem1024_public_from_private,
217 .decap = mlkem1024_decap,
218 .start = kExpectedSeedStart,
219 .start_len = sizeof(kExpectedSeedStart),
220 .expected = kExpectedAdam1024,
221 .expected_len = sizeof(kExpectedAdam1024),
222 };
223 int failed = 0; 198 int failed = 0;
224 199
225 failed |= MlkemIterativeTest(&iteration768); 200 failed |= MlkemIterativeTest(RANK768);
226 failed |= MlkemIterativeTest(&iteration1024); 201 failed |= MlkemIterativeTest(RANK1024);
227 202
228 return failed; 203 return failed;
229} 204}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests.c b/src/regress/lib/libcrypto/mlkem/mlkem_tests.c
index 8e04dc6ad2..3269ba951f 100644
--- a/src/regress/lib/libcrypto/mlkem/mlkem_tests.c
+++ b/src/regress/lib/libcrypto/mlkem/mlkem_tests.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: mlkem_tests.c,v 1.6 2025/05/20 00:33:41 beck Exp $ */ 1/* $OpenBSD: mlkem_tests.c,v 1.7 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 Theo Buehler <tb@openbsd.org> 4 * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
@@ -38,12 +38,8 @@ enum test_type {
38 38
39struct decap_ctx { 39struct decap_ctx {
40 struct parse *parse_ctx; 40 struct parse *parse_ctx;
41 41 int rank;
42 void *private_key; 42 MLKEM_private_key *private_key;
43 size_t private_key_len;
44
45 mlkem_parse_private_key_fn parse_private_key;
46 mlkem_decap_fn decap;
47}; 43};
48 44
49enum decap_states { 45enum decap_states {
@@ -89,21 +85,26 @@ decap_init(void *ctx, void *parse_ctx)
89 85
90 decap->parse_ctx = parse_ctx; 86 decap->parse_ctx = parse_ctx;
91 87
92 return 1; 88 return (decap->private_key = MLKEM_private_key_new(decap->rank))
89 != NULL;
93} 90}
94 91
95static void 92static void
96decap_finish(void *ctx) 93decap_finish(void *ctx)
97{ 94{
98 (void)ctx; 95 struct decap_ctx *decap = ctx;
96
97 MLKEM_private_key_free(decap->private_key);
98 decap->private_key = NULL;
99} 99}
100 100
101static int 101static int
102MlkemDecapFileTest(struct decap_ctx *decap) 102MlkemDecapFileTest(struct decap_ctx *decap)
103{ 103{
104 struct parse *p = decap->parse_ctx; 104 struct parse *p = decap->parse_ctx;
105 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES]; 105 uint8_t *shared_secret_buf = NULL;
106 CBS ciphertext, shared_secret, private_key; 106 CBS ciphertext, shared_secret, private_key;
107 size_t s_len = 0;
107 int should_fail; 108 int should_fail;
108 int failed = 1; 109 int failed = 1;
109 110
@@ -112,21 +113,28 @@ MlkemDecapFileTest(struct decap_ctx *decap)
112 parse_get_cbs(p, DECAP_PRIVATE_KEY, &private_key); 113 parse_get_cbs(p, DECAP_PRIVATE_KEY, &private_key);
113 parse_get_int(p, DECAP_RESULT, &should_fail); 114 parse_get_int(p, DECAP_RESULT, &should_fail);
114 115
115 if (!decap->parse_private_key(decap->private_key, 116 if (!MLKEM_parse_private_key(decap->private_key,
116 CBS_data(&private_key), CBS_len(&private_key))) { 117 CBS_data(&private_key), CBS_len(&private_key))) {
117 if ((failed = !should_fail)) 118 if ((failed = !should_fail))
118 parse_info(p, "parse private key"); 119 parse_info(p, "parse private key");
119 goto err; 120 goto err;
120 } 121 }
121 if (!decap->decap(shared_secret_buf, 122 if (!MLKEM_decap(decap->private_key, CBS_data(&ciphertext),
122 CBS_data(&ciphertext), CBS_len(&ciphertext), decap->private_key)) { 123 CBS_len(&ciphertext), &shared_secret_buf, &s_len)) {
123 if ((failed = !should_fail)) 124 if ((failed = !should_fail))
124 parse_info(p, "decap"); 125 parse_info(p, "decap");
125 goto err; 126 goto err;
126 } 127 }
127 128
129 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
130 if ((failed = !should_fail))
131 parse_info(p, "shared secret length %zu != %d", s_len,
132 MLKEM_SHARED_SECRET_LENGTH);
133 goto err;
134 }
135
128 failed = !parse_data_equal(p, "shared_secret", &shared_secret, 136 failed = !parse_data_equal(p, "shared_secret", &shared_secret,
129 shared_secret_buf, sizeof(shared_secret_buf)); 137 shared_secret_buf, s_len);
130 138
131 if (should_fail != failed) { 139 if (should_fail != failed) {
132 parse_info(p, "FAIL: should_fail %d, failed %d", 140 parse_info(p, "FAIL: should_fail %d, failed %d",
@@ -135,6 +143,7 @@ MlkemDecapFileTest(struct decap_ctx *decap)
135 } 143 }
136 144
137 err: 145 err:
146 freezero(shared_secret_buf, s_len);
138 return failed; 147 return failed;
139} 148}
140 149
@@ -193,8 +202,9 @@ static int
193MlkemNistDecapFileTest(struct decap_ctx *decap) 202MlkemNistDecapFileTest(struct decap_ctx *decap)
194{ 203{
195 struct parse *p = decap->parse_ctx; 204 struct parse *p = decap->parse_ctx;
196 uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; 205 uint8_t *shared_secret = NULL;
197 CBS dk, c, k; 206 CBS dk, c, k;
207 size_t s_len = 0;
198 int failed = 1; 208 int failed = 1;
199 209
200 parse_instruction_get_cbs(p, NIST_DECAP_DK, &dk); 210 parse_instruction_get_cbs(p, NIST_DECAP_DK, &dk);
@@ -202,27 +212,34 @@ MlkemNistDecapFileTest(struct decap_ctx *decap)
202 parse_get_cbs(p, NIST_DECAP_K, &k); 212 parse_get_cbs(p, NIST_DECAP_K, &k);
203 213
204 if (!parse_length_equal(p, "private key", 214 if (!parse_length_equal(p, "private key",
205 decap->private_key_len, CBS_len(&dk))) 215 MLKEM_private_key_encoded_length(decap->private_key), CBS_len(&dk)))
206 goto err; 216 goto err;
207 if (!parse_length_equal(p, "shared secret", 217 if (!parse_length_equal(p, "shared secret",
208 MLKEM_SHARED_SECRET_BYTES, CBS_len(&k))) 218 MLKEM_SHARED_SECRET_BYTES, CBS_len(&k)))
209 goto err; 219 goto err;
210 220
211 if (!decap->parse_private_key(decap->private_key, CBS_data(&dk), 221 if (!MLKEM_parse_private_key(decap->private_key, CBS_data(&dk),
212 CBS_len(&dk))) { 222 CBS_len(&dk))) {
213 parse_info(p, "parse private key"); 223 parse_info(p, "parse private key");
214 goto err; 224 goto err;
215 } 225 }
216 if (!decap->decap(shared_secret, CBS_data(&c), CBS_len(&c), 226 if (!MLKEM_decap(decap->private_key, CBS_data(&c), CBS_len(&c),
217 decap->private_key)) { 227 &shared_secret, &s_len)) {
218 parse_info(p, "decap"); 228 parse_info(p, "decap");
219 goto err; 229 goto err;
220 } 230 }
221 231
232 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
233 parse_info(p, "shared secret length %zu != %d", s_len,
234 MLKEM_SHARED_SECRET_LENGTH);
235 goto err;
236 }
237
222 failed = !parse_data_equal(p, "shared secret", &k, 238 failed = !parse_data_equal(p, "shared secret", &k,
223 shared_secret, MLKEM_SHARED_SECRET_BYTES); 239 shared_secret, s_len);
224 240
225 err: 241 err:
242 free(shared_secret);
226 return failed; 243 return failed;
227} 244}
228 245
@@ -246,46 +263,31 @@ static const struct test_parse nist_decap_parse = {
246}; 263};
247 264
248static int 265static int
249mlkem_decap_tests(const char *fn, size_t size, enum test_type test_type) 266mlkem_decap_tests(const char *fn, int rank, enum test_type test_type)
250{ 267{
251 struct MLKEM768_private_key private_key768; 268 struct decap_ctx decap = {
252 struct decap_ctx decap768 = { 269 .rank = rank,
253 .private_key = &private_key768,
254 .private_key_len = MLKEM768_PRIVATE_KEY_BYTES,
255
256 .parse_private_key = mlkem768_parse_private_key,
257 .decap = mlkem768_decap,
258 };
259 struct MLKEM1024_private_key private_key1024;
260 struct decap_ctx decap1024 = {
261 .private_key = &private_key1024,
262 .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES,
263
264 .parse_private_key = mlkem1024_parse_private_key,
265 .decap = mlkem1024_decap,
266 }; 270 };
271 int ret = 0;
267 272
268 if (size == 768 && test_type == TEST_TYPE_NORMAL) 273 if (test_type == TEST_TYPE_NORMAL)
269 return parse_test_file(fn, &decap_parse, &decap768); 274 ret = parse_test_file(fn, &decap_parse, &decap);
270 if (size == 768 && test_type == TEST_TYPE_NIST) 275 else if (test_type == TEST_TYPE_NIST)
271 return parse_test_file(fn, &nist_decap_parse, &decap768); 276 ret = parse_test_file(fn, &nist_decap_parse, &decap);
272 if (size == 1024 && test_type == TEST_TYPE_NORMAL) 277 else
273 return parse_test_file(fn, &decap_parse, &decap1024); 278 errx(1, "unknown decap test: rank %d, type %d", rank,
274 if (size == 1024 && test_type == TEST_TYPE_NIST) 279 test_type);
275 return parse_test_file(fn, &nist_decap_parse, &decap1024);
276 280
277 errx(1, "unknown decap test: size %zu, type %d", size, test_type); 281 return ret;
278} 282}
279 283
280struct encap_ctx { 284struct encap_ctx {
281 struct parse *parse_ctx; 285 struct parse *parse_ctx;
282 286
283 void *public_key; 287 int rank;
288 MLKEM_public_key *public_key;
284 uint8_t *ciphertext; 289 uint8_t *ciphertext;
285 size_t ciphertext_len; 290 size_t ciphertext_len;
286
287 mlkem_parse_public_key_fn parse_public_key;
288 mlkem_encap_external_entropy_fn encap_external_entropy;
289}; 291};
290 292
291enum encap_states { 293enum encap_states {
@@ -338,21 +340,30 @@ encap_init(void *ctx, void *parse_ctx)
338 340
339 encap->parse_ctx = parse_ctx; 341 encap->parse_ctx = parse_ctx;
340 342
341 return 1; 343 encap->ciphertext = NULL;
344 return (encap->public_key = MLKEM_public_key_new(encap->rank))
345 != NULL;
342} 346}
343 347
344static void 348static void
345encap_finish(void *ctx) 349encap_finish(void *ctx)
346{ 350{
347 (void)ctx; 351 struct encap_ctx *encap = ctx;
352
353 freezero(encap->ciphertext, encap->ciphertext_len);
354 encap->ciphertext = NULL;
355 MLKEM_public_key_free(encap->public_key);
356 encap->public_key = NULL;
348} 357}
349 358
350static int 359static int
351MlkemEncapFileTest(struct encap_ctx *encap) 360MlkemEncapFileTest(struct encap_ctx *encap)
352{ 361{
353 struct parse *p = encap->parse_ctx;
354 uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES];
355 CBS entropy, public_key, ciphertext, shared_secret; 362 CBS entropy, public_key, ciphertext, shared_secret;
363 struct parse *p = encap->parse_ctx;
364 uint8_t *shared_secret_buf = NULL;
365 size_t s_len = 0;
366
356 int should_fail; 367 int should_fail;
357 int failed = 1; 368 int failed = 1;
358 369
@@ -362,20 +373,33 @@ MlkemEncapFileTest(struct encap_ctx *encap)
362 parse_get_cbs(p, ENCAP_SHARED_SECRET, &shared_secret); 373 parse_get_cbs(p, ENCAP_SHARED_SECRET, &shared_secret);
363 parse_get_int(p, ENCAP_RESULT, &should_fail); 374 parse_get_int(p, ENCAP_RESULT, &should_fail);
364 375
365 if (!encap->parse_public_key(encap->public_key, CBS_data(&public_key), 376 if (!MLKEM_parse_public_key(encap->public_key, CBS_data(&public_key),
366 CBS_len(&public_key))) { 377 CBS_len(&public_key))) {
367 if ((failed = !should_fail)) 378 if ((failed = !should_fail))
368 parse_info(p, "parse public key"); 379 parse_info(p, "parse public key");
369 goto err; 380 goto err;
370 } 381 }
371 encap->encap_external_entropy(encap->ciphertext, shared_secret_buf, 382 if (!MLKEM_encap_external_entropy(encap->public_key, CBS_data(&entropy),
372 encap->public_key, CBS_data(&entropy)); 383 &encap->ciphertext, &encap->ciphertext_len, &shared_secret_buf,
384 &s_len)) {
385 if ((failed = !should_fail))
386 parse_info(p, "encap_external_entropy");
387 goto err;
388 }
389
390 if (s_len != MLKEM_SHARED_SECRET_LENGTH) {
391 if ((failed = !should_fail))
392 parse_info(p, "shared secret length %zu != %d", s_len,
393 MLKEM_SHARED_SECRET_LENGTH);
394 goto err;
395 }
373 396
374 failed = !parse_data_equal(p, "shared_secret", &shared_secret, 397 failed = !parse_data_equal(p, "shared_secret", &shared_secret,
375 shared_secret_buf, sizeof(shared_secret_buf)); 398 shared_secret_buf, s_len);
376 failed |= !parse_data_equal(p, "ciphertext", &ciphertext, 399 failed |= !parse_data_equal(p, "ciphertext", &ciphertext,
377 encap->ciphertext, encap->ciphertext_len); 400 encap->ciphertext, encap->ciphertext_len);
378 401
402
379 if (should_fail != failed) { 403 if (should_fail != failed) {
380 parse_info(p, "FAIL: should_fail %d, failed %d", 404 parse_info(p, "FAIL: should_fail %d, failed %d",
381 should_fail, failed); 405 should_fail, failed);
@@ -383,6 +407,7 @@ MlkemEncapFileTest(struct encap_ctx *encap)
383 } 407 }
384 408
385 err: 409 err:
410 freezero(shared_secret_buf, s_len);
386 return failed; 411 return failed;
387} 412}
388 413
@@ -403,48 +428,22 @@ static const struct test_parse encap_parse = {
403}; 428};
404 429
405static int 430static int
406mlkem_encap_tests(const char *fn, size_t size) 431mlkem_encap_tests(const char *fn, int rank)
407{ 432{
408 struct MLKEM768_public_key public_key768; 433 struct encap_ctx encap = {
409 uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; 434 .rank = rank,
410 struct encap_ctx encap768 = {
411 .public_key = &public_key768,
412 .ciphertext = ciphertext768,
413 .ciphertext_len = sizeof(ciphertext768),
414
415 .parse_public_key = mlkem768_parse_public_key,
416 .encap_external_entropy = mlkem768_encap_external_entropy,
417 };
418 struct MLKEM1024_public_key public_key1024;
419 uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES];
420 struct encap_ctx encap1024 = {
421 .public_key = &public_key1024,
422 .ciphertext = ciphertext1024,
423 .ciphertext_len = sizeof(ciphertext1024),
424
425 .parse_public_key = mlkem1024_parse_public_key,
426 .encap_external_entropy = mlkem1024_encap_external_entropy,
427 }; 435 };
428 436
429 if (size == 768) 437 return parse_test_file(fn, &encap_parse, &encap);
430 return parse_test_file(fn, &encap_parse, &encap768);
431 if (size == 1024)
432 return parse_test_file(fn, &encap_parse, &encap1024);
433
434 errx(1, "unknown encap test: size %zu", size);
435} 438}
436 439
437struct keygen_ctx { 440struct keygen_ctx {
438 struct parse *parse_ctx; 441 struct parse *parse_ctx;
439 442
440 void *private_key; 443 int rank;
441 void *encoded_public_key; 444 MLKEM_private_key *private_key;
445 uint8_t *encoded_public_key;
442 size_t encoded_public_key_len; 446 size_t encoded_public_key_len;
443 size_t private_key_len;
444 size_t public_key_len;
445
446 mlkem_generate_key_external_entropy_fn generate_key_external_entropy;
447 mlkem_marshal_private_key_fn marshal_private_key;
448}; 447};
449 448
450enum keygen_states { 449enum keygen_states {
@@ -482,13 +481,19 @@ keygen_init(void *ctx, void *parse_ctx)
482 481
483 keygen->parse_ctx = parse_ctx; 482 keygen->parse_ctx = parse_ctx;
484 483
485 return 1; 484 return (keygen->private_key = MLKEM_private_key_new(keygen->rank))
485 != NULL;
486} 486}
487 487
488static void 488static void
489keygen_finish(void *ctx) 489keygen_finish(void *ctx)
490{ 490{
491 (void)ctx; 491 struct keygen_ctx *keygen = ctx;
492
493 freezero(keygen->encoded_public_key, keygen->encoded_public_key_len);
494 keygen->encoded_public_key = NULL;
495 MLKEM_private_key_free(keygen->private_key);
496 keygen->private_key = NULL;
492} 497}
493 498
494static int 499static int
@@ -504,18 +509,25 @@ MlkemKeygenFileTest(struct keygen_ctx *keygen)
504 parse_get_cbs(p, KEYGEN_PUBLIC_KEY, &public_key); 509 parse_get_cbs(p, KEYGEN_PUBLIC_KEY, &public_key);
505 parse_get_cbs(p, KEYGEN_PRIVATE_KEY, &private_key); 510 parse_get_cbs(p, KEYGEN_PRIVATE_KEY, &private_key);
506 511
507 if (!parse_length_equal(p, "seed", MLKEM_SEED_BYTES, CBS_len(&seed))) 512 if (!parse_length_equal(p, "seed", MLKEM_SEED_LENGTH, CBS_len(&seed)))
508 goto err; 513 goto err;
514
515 if (!MLKEM_generate_key_external_entropy(keygen->private_key,
516 &keygen->encoded_public_key, &keygen->encoded_public_key_len,
517 CBS_data(&seed))) {
518 parse_info(p, "generate_key_external_entropy");
519 goto err;
520 }
521
509 if (!parse_length_equal(p, "public key", 522 if (!parse_length_equal(p, "public key",
510 keygen->public_key_len, CBS_len(&public_key))) 523 keygen->encoded_public_key_len, CBS_len(&public_key)))
511 goto err; 524 goto err;
512 if (!parse_length_equal(p, "private key", 525 if (!parse_length_equal(p, "private key",
513 keygen->private_key_len, CBS_len(&private_key))) 526 MLKEM_private_key_encoded_length(keygen->private_key),
527 CBS_len(&private_key)))
514 goto err; 528 goto err;
515 529
516 keygen->generate_key_external_entropy(keygen->encoded_public_key, 530 if (!MLKEM_marshal_private_key(keygen->private_key,
517 keygen->private_key, CBS_data(&seed));
518 if (!keygen->marshal_private_key(keygen->private_key,
519 &encoded_private_key, &encoded_private_key_len)) { 531 &encoded_private_key, &encoded_private_key_len)) {
520 parse_info(p, "encode private key"); 532 parse_info(p, "encode private key");
521 goto err; 533 goto err;
@@ -589,7 +601,7 @@ MlkemNistKeygenFileTest(struct keygen_ctx *keygen)
589 struct parse *p = keygen->parse_ctx; 601 struct parse *p = keygen->parse_ctx;
590 CBB seed_cbb; 602 CBB seed_cbb;
591 CBS z, d, ek, dk; 603 CBS z, d, ek, dk;
592 uint8_t seed[MLKEM_SEED_BYTES]; 604 uint8_t seed[MLKEM_SEED_LENGTH];
593 size_t seed_len; 605 size_t seed_len;
594 uint8_t *encoded_private_key = NULL; 606 uint8_t *encoded_private_key = NULL;
595 size_t encoded_private_key_len = 0; 607 size_t encoded_private_key_len = 0;
@@ -609,12 +621,17 @@ MlkemNistKeygenFileTest(struct keygen_ctx *keygen)
609 if (!CBB_finish(&seed_cbb, NULL, &seed_len)) 621 if (!CBB_finish(&seed_cbb, NULL, &seed_len))
610 parse_errx(p, "CBB_finish"); 622 parse_errx(p, "CBB_finish");
611 623
612 if (!parse_length_equal(p, "bogus z or d", MLKEM_SEED_BYTES, seed_len)) 624 if (!parse_length_equal(p, "bogus z or d", MLKEM_SEED_LENGTH, seed_len))
613 goto err; 625 goto err;
614 626
615 keygen->generate_key_external_entropy(keygen->encoded_public_key, 627 if (!MLKEM_generate_key_external_entropy(keygen->private_key,
616 keygen->private_key, seed); 628 &keygen->encoded_public_key, &keygen->encoded_public_key_len,
617 if (!keygen->marshal_private_key(keygen->private_key, 629 seed)) {
630 parse_info(p, "generate_key_external_entropy");
631 goto err;
632 }
633
634 if (!MLKEM_marshal_private_key(keygen->private_key,
618 &encoded_private_key, &encoded_private_key_len)) { 635 &encoded_private_key, &encoded_private_key_len)) {
619 parse_info(p, "encode private key"); 636 parse_info(p, "encode private key");
620 goto err; 637 goto err;
@@ -648,74 +665,49 @@ static const struct test_parse nist_keygen_parse = {
648}; 665};
649 666
650static int 667static int
651mlkem_keygen_tests(const char *fn, size_t size, enum test_type test_type) 668mlkem_keygen_tests(const char *fn, int rank, enum test_type test_type)
652{ 669{
653 struct MLKEM768_private_key private_key768; 670 struct keygen_ctx keygen = {
654 uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; 671 .rank = rank,
655 struct keygen_ctx keygen768 = {
656 .private_key = &private_key768,
657 .encoded_public_key = encoded_public_key768,
658 .encoded_public_key_len = sizeof(encoded_public_key768),
659 .private_key_len = MLKEM768_PRIVATE_KEY_BYTES,
660 .public_key_len = MLKEM768_PUBLIC_KEY_BYTES,
661
662 .generate_key_external_entropy =
663 mlkem768_generate_key_external_entropy,
664 .marshal_private_key =
665 mlkem768_marshal_private_key,
666 };
667 struct MLKEM1024_private_key private_key1024;
668 uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES];
669 struct keygen_ctx keygen1024 = {
670 .private_key = &private_key1024,
671 .encoded_public_key = encoded_public_key1024,
672 .encoded_public_key_len = sizeof(encoded_public_key1024),
673 .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES,
674 .public_key_len = MLKEM1024_PUBLIC_KEY_BYTES,
675
676 .generate_key_external_entropy =
677 mlkem1024_generate_key_external_entropy,
678 .marshal_private_key =
679 mlkem1024_marshal_private_key,
680 }; 672 };
673 int ret = 0;
681 674
682 if (size == 768 && test_type == TEST_TYPE_NORMAL) 675 if (test_type == TEST_TYPE_NORMAL)
683 return parse_test_file(fn, &keygen_parse, &keygen768); 676 ret = parse_test_file(fn, &keygen_parse, &keygen);
684 if (size == 768 && test_type == TEST_TYPE_NIST) 677 else if (test_type == TEST_TYPE_NIST)
685 return parse_test_file(fn, &nist_keygen_parse, &keygen768); 678 ret = parse_test_file(fn, &nist_keygen_parse, &keygen);
686 if (size == 1024 && test_type == TEST_TYPE_NORMAL) 679 else
687 return parse_test_file(fn, &keygen_parse, &keygen1024); 680 errx(1, "unknown keygen test: rank %d, type %d", rank,
688 if (size == 1024 && test_type == TEST_TYPE_NIST) 681 test_type);
689 return parse_test_file(fn, &nist_keygen_parse, &keygen1024);
690 682
691 errx(1, "unknown keygen test: size %zu, type %d", size, test_type); 683 return ret;
692} 684}
693 685
694static int 686static int
695run_mlkem_test(const char *test, const char *fn) 687run_mlkem_test(const char *test, const char *fn)
696{ 688{
697 if (strcmp(test, "mlkem768_decap_tests") == 0) 689 if (strcmp(test, "mlkem768_decap_tests") == 0)
698 return mlkem_decap_tests(fn, 768, TEST_TYPE_NORMAL); 690 return mlkem_decap_tests(fn, RANK768, TEST_TYPE_NORMAL);
699 if (strcmp(test, "mlkem768_nist_decap_tests") == 0) 691 if (strcmp(test, "mlkem768_nist_decap_tests") == 0)
700 return mlkem_decap_tests(fn, 768, TEST_TYPE_NIST); 692 return mlkem_decap_tests(fn, RANK768, TEST_TYPE_NIST);
701 if (strcmp(test, "mlkem1024_decap_tests") == 0) 693 if (strcmp(test, "mlkem1024_decap_tests") == 0)
702 return mlkem_decap_tests(fn, 1024, TEST_TYPE_NORMAL); 694 return mlkem_decap_tests(fn, RANK1024, TEST_TYPE_NORMAL);
703 if (strcmp(test, "mlkem1024_nist_decap_tests") == 0) 695 if (strcmp(test, "mlkem1024_nist_decap_tests") == 0)
704 return mlkem_decap_tests(fn, 1024, TEST_TYPE_NIST); 696 return mlkem_decap_tests(fn, RANK1024, TEST_TYPE_NIST);
705 697
706 if (strcmp(test, "mlkem768_encap_tests") == 0) 698 if (strcmp(test, "mlkem768_encap_tests") == 0)
707 return mlkem_encap_tests(fn, 768); 699 return mlkem_encap_tests(fn, RANK768);
708 if (strcmp(test, "mlkem1024_encap_tests") == 0) 700 if (strcmp(test, "mlkem1024_encap_tests") == 0)
709 return mlkem_encap_tests(fn, 1024); 701 return mlkem_encap_tests(fn, RANK1024);
710 702
711 if (strcmp(test, "mlkem768_keygen_tests") == 0) 703 if (strcmp(test, "mlkem768_keygen_tests") == 0)
712 return mlkem_keygen_tests(fn, 768, TEST_TYPE_NORMAL); 704 return mlkem_keygen_tests(fn, RANK768, TEST_TYPE_NORMAL);
713 if (strcmp(test, "mlkem768_nist_keygen_tests") == 0) 705 if (strcmp(test, "mlkem768_nist_keygen_tests") == 0)
714 return mlkem_keygen_tests(fn, 768, TEST_TYPE_NIST); 706 return mlkem_keygen_tests(fn, RANK768, TEST_TYPE_NIST);
715 if (strcmp(test, "mlkem1024_keygen_tests") == 0) 707 if (strcmp(test, "mlkem1024_keygen_tests") == 0)
716 return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NORMAL); 708 return mlkem_keygen_tests(fn, RANK1024, TEST_TYPE_NORMAL);
717 if (strcmp(test, "mlkem1024_nist_keygen_tests") == 0) 709 if (strcmp(test, "mlkem1024_nist_keygen_tests") == 0)
718 return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NIST); 710 return mlkem_keygen_tests(fn, RANK1024, TEST_TYPE_NIST);
719 711
720 errx(1, "unknown test %s (test file %s)", test, fn); 712 errx(1, "unknown test %s (test file %s)", test, fn);
721} 713}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c
index 68bd5d4871..9d6e604386 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.8 2025/05/20 00:33:41 beck Exp $ */ 1/* $OpenBSD: mlkem_tests_util.c,v 1.9 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>
@@ -23,7 +23,7 @@
23#include <string.h> 23#include <string.h>
24 24
25#include "bytestring.h" 25#include "bytestring.h"
26#include "mlkem.h" 26#include <openssl/mlkem.h>
27 27
28#include "mlkem_internal.h" 28#include "mlkem_internal.h"
29 29
@@ -59,157 +59,3 @@ compare_data(const uint8_t *want, const uint8_t *got, size_t len, const char *ms
59 59
60 return 1; 60 return 1;
61} 61}
62
63int
64mlkem768_marshal_private_key(const void *private_key, uint8_t **out_buf,
65 size_t *out_len)
66{
67 return MLKEM768_marshal_private_key(private_key, out_buf, out_len);
68}
69
70int
71mlkem768_marshal_public_key(const void *public_key, uint8_t **out_buf,
72 size_t *out_len)
73{
74 return MLKEM768_marshal_public_key(out_buf, out_len, public_key);
75}
76
77int
78mlkem1024_marshal_private_key(const void *private_key, uint8_t **out_buf,
79 size_t *out_len)
80{
81 return MLKEM1024_marshal_private_key(private_key, out_buf, out_len);
82}
83
84int
85mlkem1024_marshal_public_key(const void *public_key, uint8_t **out_buf,
86 size_t *out_len)
87{
88 return MLKEM1024_marshal_public_key(out_buf, out_len, public_key);
89}
90
91int
92mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
93 const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key)
94{
95 return MLKEM768_decap(out_shared_secret, ciphertext, ciphertext_len,
96 private_key);
97}
98
99void
100mlkem768_encap(uint8_t *out_ciphertext,
101 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
102 const void *public_key)
103{
104 MLKEM768_encap(out_ciphertext, out_shared_secret, public_key);
105}
106
107void
108mlkem768_encap_external_entropy(uint8_t *out_ciphertext,
109 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
110 const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
111{
112 MLKEM768_encap_external_entropy(out_ciphertext, out_shared_secret,
113 public_key, entropy);
114}
115
116int
117mlkem768_generate_key(uint8_t *out_encoded_public_key,
118 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key)
119{
120 return MLKEM768_generate_key(out_encoded_public_key, optional_out_seed,
121 out_private_key);
122}
123
124int
125mlkem768_generate_key_external_entropy(uint8_t *out_encoded_public_key,
126 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES])
127{
128 return MLKEM768_generate_key_external_entropy(out_encoded_public_key,
129 out_private_key, entropy);
130}
131
132int
133mlkem768_parse_private_key(void *out_private_key, const uint8_t *private_key,
134 size_t private_key_len)
135{
136 return MLKEM768_parse_private_key(out_private_key, private_key,
137 private_key_len);
138}
139
140int
141mlkem768_parse_public_key(void *out_public_key, const uint8_t *public_key,
142 size_t public_key_len)
143{
144 return MLKEM768_parse_public_key(out_public_key, public_key,
145 public_key_len);
146}
147
148void
149mlkem768_public_from_private(void *out_public_key, const void *private_key)
150{
151 MLKEM768_public_from_private(out_public_key, private_key);
152}
153
154int
155mlkem1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
156 const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key)
157{
158 return MLKEM1024_decap(out_shared_secret, ciphertext, ciphertext_len,
159 private_key);
160}
161
162void
163mlkem1024_encap(uint8_t *out_ciphertext,
164 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
165 const void *public_key)
166{
167 MLKEM1024_encap(out_ciphertext, out_shared_secret, public_key);
168}
169
170void
171mlkem1024_encap_external_entropy(uint8_t *out_ciphertext,
172 uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
173 const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY])
174{
175 MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret,
176 public_key, entropy);
177}
178
179int
180mlkem1024_generate_key(uint8_t *out_encoded_public_key,
181 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key)
182{
183 return MLKEM1024_generate_key(out_encoded_public_key, optional_out_seed,
184 out_private_key);
185}
186
187int
188mlkem1024_generate_key_external_entropy(uint8_t *out_encoded_public_key,
189 void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES])
190{
191 return MLKEM1024_generate_key_external_entropy(out_encoded_public_key,
192 out_private_key, entropy);
193}
194
195int
196mlkem1024_parse_private_key(void *out_private_key, const uint8_t *private_key,
197 size_t private_key_len)
198{
199 return MLKEM1024_parse_private_key(out_private_key, private_key,
200 private_key_len);
201}
202
203void
204mlkem1024_public_from_private(void *out_public_key, const void *private_key)
205{
206 MLKEM1024_public_from_private(out_public_key, private_key);
207}
208
209int
210mlkem1024_parse_public_key(void *out_public_key, const uint8_t *public_key,
211 size_t public_key_len)
212{
213 return MLKEM1024_parse_public_key(out_public_key, public_key,
214 public_key_len);
215}
diff --git a/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h b/src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h
index 1235309f60..a2348c75f3 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.7 2025/05/20 00:33:41 beck Exp $ */ 1/* $OpenBSD: mlkem_tests_util.h,v 1.8 2025/08/14 15:48:48 beck 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>
@@ -30,60 +30,4 @@
30int 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,
31 const char *msg); 31 const char *msg);
32 32
33int mlkem768_marshal_private_key(const void *priv, uint8_t **out_buf,
34 size_t *out_len);
35int mlkem768_marshal_public_key(const void *pub, uint8_t **out_buf,
36 size_t *out_len);
37int mlkem1024_marshal_private_key(const void *priv, uint8_t **out_buf,
38 size_t *out_len);
39int mlkem1024_marshal_public_key(const void *pub, uint8_t **out_buf,
40 size_t *out_len);
41
42int mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES],
43 const uint8_t *ciphertext, size_t ciphertext_len, const void *priv);
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]);
49int mlkem768_generate_key(uint8_t *out_encoded_public_key,
50 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key);
51int 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, const uint8_t *in, size_t in_len);
54int mlkem768_parse_public_key(void *pub, const uint8_t *in, size_t in_len);
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]);
64int mlkem1024_generate_key(uint8_t *out_encoded_public_key,
65 uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key);
66int 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, const uint8_t *in, size_t in_len);
69int mlkem1024_parse_public_key(void *pub, const uint8_t *in, size_t in_len);
70void mlkem1024_public_from_private(void *out_public_key, const void *private_key);
71
72typedef int (*mlkem_marshal_private_key_fn)(const void *, uint8_t **, size_t *);
73typedef int (*mlkem_marshal_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 int (*mlkem_generate_key_fn)(uint8_t *, uint8_t *, void *);
82typedef int (*mlkem_generate_key_external_entropy_fn)(uint8_t *, void *,
83 const uint8_t [MLKEM_SEED_BYTES]);
84typedef int (*mlkem_parse_private_key_fn)(void *, const uint8_t *, size_t);
85typedef int (*mlkem_parse_public_key_fn)(void *, const uint8_t *, size_t);
86typedef void (*mlkem_public_from_private_fn)(void *out_public_key,
87 const void *private_key);
88
89#endif /* MLKEM_TEST_UTIL_H */ 33#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 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}