summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/mlkem/mlkem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libcrypto/mlkem/mlkem.c')
-rw-r--r--src/lib/libcrypto/mlkem/mlkem.c638
1 files changed, 638 insertions, 0 deletions
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 */