From 6452fa9fc6f33dac80ee572764b9fe29a469f8ce Mon Sep 17 00:00:00 2001 From: beck <> Date: Thu, 14 Aug 2025 15:48:48 +0000 Subject: Add a reasonable ML-KEM API for public use. Adapt the tests to use this API. This does not yet make the symbols public in Symbols.list which will happen shortly with a bump. This includes some partial rototilling of the non-public interfaces which will be shortly continued when the internal code is deduplicated to not have multiple copies for ML-KEM 768 and ML-KEM 1024 (which is just an artifact of unravelling the boring C++ code). ok jsing@, tb@ --- src/lib/libcrypto/Makefile | 5 +- src/lib/libcrypto/hidden/openssl/mlkem.h | 40 +- src/lib/libcrypto/mlkem/mlkem.c | 638 +++++++++++++++++++++ src/lib/libcrypto/mlkem/mlkem.h | 329 +++++------ src/lib/libcrypto/mlkem/mlkem1024.c | 71 +-- src/lib/libcrypto/mlkem/mlkem768.c | 73 ++- src/lib/libcrypto/mlkem/mlkem_internal.h | 331 ++++++++++- src/lib/libcrypto/mlkem/mlkem_key.c | 200 +++++++ src/lib/libssl/ssl_rsa.c | 4 +- .../lib/libcrypto/mlkem/mlkem_iteration_tests.c | 179 +++--- src/regress/lib/libcrypto/mlkem/mlkem_tests.c | 292 +++++----- src/regress/lib/libcrypto/mlkem/mlkem_tests_util.c | 158 +---- src/regress/lib/libcrypto/mlkem/mlkem_tests_util.h | 58 +- src/regress/lib/libcrypto/mlkem/mlkem_unittest.c | 302 +++++----- 14 files changed, 1783 insertions(+), 897 deletions(-) create mode 100644 src/lib/libcrypto/mlkem/mlkem.c create mode 100644 src/lib/libcrypto/mlkem/mlkem_key.c (limited to 'src') 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 @@ -# $OpenBSD: Makefile,v 1.241 2025/07/16 15:59:26 tb Exp $ +# $OpenBSD: Makefile,v 1.242 2025/08/14 15:48:48 beck Exp $ LIB= crypto LIBREBUILD=y @@ -374,8 +374,10 @@ SRCS+= md4.c SRCS+= md5.c # mlkem/ +SRCS+= mlkem.c SRCS+= mlkem768.c SRCS+= mlkem1024.c +SRCS+= mlkem_key.c # modes/ SRCS+= cbc128.c @@ -668,6 +670,7 @@ HDRS=\ ${LCRYPTO_SRC}/lhash/lhash.h \ ${LCRYPTO_SRC}/md4/md4.h \ ${LCRYPTO_SRC}/md5/md5.h \ + ${LCRYPTO_SRC}/mlkem/mlkem.h \ ${LCRYPTO_SRC}/modes/modes.h \ ${LCRYPTO_SRC}/objects/objects.h \ ${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 @@ -/* $OpenBSD: mlkem.h,v 1.4 2024/12/20 15:10:31 tb Exp $ */ +/* $OpenBSD: mlkem.h,v 1.5 2025/08/14 15:48:48 beck Exp $ */ /* - * Copyright (c) 2024 Bob Beck + * Copyright (c) 2025 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,9 +18,6 @@ #ifndef _LIBCRYPTO_MLKEM_H #define _LIBCRYPTO_MLKEM_H -/* Undo when making public */ -#ifdef LIBRESSL_HAS_MLKEM - #ifndef _MSC_VER #include_next #else @@ -28,22 +25,21 @@ #endif #include "crypto_namespace.h" -LCRYPTO_USED(MLKEM768_generate_key); -LCRYPTO_USED(MLKEM768_public_from_private); -LCRYPTO_USED(MLKEM768_encap); -LCRYPTO_USED(MLKEM768_decap); -LCRYPTO_USED(MLKEM768_marshal_public_key); -LCRYPTO_USED(MLKEM768_parse_public_key); -LCRYPTO_USED(MLKEM768_private_key_from_seed); -LCRYPTO_USED(MLKEM768_parse_private_key); -LCRYPTO_USED(MLKEM1024_generate_key); -LCRYPTO_USED(MLKEM1024_public_from_private); -LCRYPTO_USED(MLKEM1024_encap); -LCRYPTO_USED(MLKEM1024_decap); -LCRYPTO_USED(MLKEM1024_marshal_public_key); -LCRYPTO_USED(MLKEM1024_parse_public_key); -LCRYPTO_USED(MLKEM1024_private_key_from_seed); -LCRYPTO_USED(MLKEM1024_parse_private_key); -#endif /* LIBRESSL_HAS_MLKEM */ +LCRYPTO_USED(MLKEM_private_key_new); +LCRYPTO_USED(MLKEM_private_key_free); +LCRYPTO_USED(MLKEM_private_key_ciphertext_length); +LCRYPTO_USED(MLKEM_private_key_encoded_length); +LCRYPTO_USED(MLKEM_public_key_new); +LCRYPTO_USED(MLKEM_public_key_free); +LCRYPTO_USED(MLKEM_public_key_ciphertext_length); +LCRYPTO_USED(MLKEM_public_key_encoded_length); +LCRYPTO_USED(MLKEM_generate_key); +LCRYPTO_USED(MLKEM_private_key_from_seed); +LCRYPTO_USED(MLKEM_public_from_private); +LCRYPTO_USED(MLKEM_encap); +LCRYPTO_USED(MLKEM_decap); +LCRYPTO_USED(MLKEM_marshal_public_key); +LCRYPTO_USED(MLKEM_parse_public_key); +LCRYPTO_USED(MLKEM_parse_private_key); #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 @@ +/* $OpenBSD: mlkem.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */ +/* + * Copyright (c) 2025, Bob Beck + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include + +#include +#include "mlkem_internal.h" + +static inline int +private_key_is_new(const MLKEM_private_key *key) +{ + return (key != NULL && + key->state == MLKEM_PRIVATE_KEY_UNINITIALIZED && + (key->rank == RANK768 || key->rank == RANK1024)); +} + +static inline int +private_key_is_valid(const MLKEM_private_key *key) +{ + return (key != NULL && + key->state == MLKEM_PRIVATE_KEY_INITIALIZED && + (key->rank == RANK768 || key->rank == RANK1024)); +} + +static inline int +public_key_is_new(const MLKEM_public_key *key) +{ + return (key != NULL && + key->state == MLKEM_PUBLIC_KEY_UNINITIALIZED && + (key->rank == RANK768 || key->rank == RANK1024)); +} + +static inline int +public_key_is_valid(const MLKEM_public_key *key) +{ + return (key != NULL && + key->state == MLKEM_PUBLIC_KEY_INITIALIZED && + (key->rank == RANK768 || key->rank == RANK1024)); +} + +/* + * ML-KEM operations + */ + +int +MLKEM_generate_key_external_entropy(MLKEM_private_key *private_key, + uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len, + const uint8_t *entropy) +{ + uint8_t *k = NULL; + size_t k_len = 0; + int ret = 0; + + if (*out_encoded_public_key != NULL) + goto err; + + if (!private_key_is_new(private_key)) + goto err; + + k_len = MLKEM768_PUBLIC_KEY_BYTES; + if (private_key->rank == RANK1024) + k_len = MLKEM1024_PUBLIC_KEY_BYTES; + + if ((k = calloc(1, k_len)) == NULL) + goto err; + + switch (private_key->rank) { + case RANK768: + if (!MLKEM768_generate_key_external_entropy(k, private_key, + entropy)) + goto err; + break; + case RANK1024: + if (!MLKEM1024_generate_key_external_entropy(k, private_key, + entropy)) + goto err; + break; + } + + private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED; + + *out_encoded_public_key = k; + *out_encoded_public_key_len = k_len; + k = NULL; + + ret = 1; + + err: + freezero(k, k_len); + + return ret; +} + +int +MLKEM_generate_key(MLKEM_private_key *private_key, + uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len, + uint8_t **out_optional_seed, size_t *out_optional_seed_len) +{ + uint8_t *entropy_buf = NULL; + int ret = 0; + + if (*out_encoded_public_key != NULL) + goto err; + + if (out_optional_seed != NULL && *out_optional_seed != NULL) + goto err; + + if ((entropy_buf = calloc(1, MLKEM_SEED_LENGTH)) == NULL) + goto err; + + arc4random_buf(entropy_buf, MLKEM_SEED_LENGTH); + if (!MLKEM_generate_key_external_entropy(private_key, + out_encoded_public_key, out_encoded_public_key_len, + entropy_buf)) + goto err; + + if (out_optional_seed != NULL) { + *out_optional_seed = entropy_buf; + *out_optional_seed_len = MLKEM_SEED_LENGTH; + entropy_buf = NULL; + } + + ret = 1; + + err: + freezero(entropy_buf, MLKEM_SEED_LENGTH); + + return ret; +} +LCRYPTO_ALIAS(MLKEM_generate_key); + +int +MLKEM_private_key_from_seed(MLKEM_private_key *private_key, + const uint8_t *seed, size_t seed_len) +{ + int ret = 0; + + if (!private_key_is_new(private_key)) + goto err; + + if (seed_len != MLKEM_SEED_LENGTH) + goto err; + + switch (private_key->rank) { + case RANK768: + if (!MLKEM768_private_key_from_seed(seed, + seed_len, private_key)) + goto err; + break; + case RANK1024: + if (!MLKEM1024_private_key_from_seed(private_key, + seed, seed_len)) + goto err; + break; + } + + private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED; + + ret = 1; + + err: + + return ret; +} +LCRYPTO_ALIAS(MLKEM_private_key_from_seed); + +int +MLKEM_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *public_key) +{ + if (!private_key_is_valid(private_key)) + return 0; + if (!public_key_is_new(public_key)) + return 0; + if (public_key->rank != private_key->rank) + return 0; + switch (private_key->rank) { + case RANK768: + MLKEM768_public_from_private(private_key, public_key); + break; + case RANK1024: + MLKEM1024_public_from_private(private_key, public_key); + break; + } + + public_key->state = MLKEM_PUBLIC_KEY_INITIALIZED; + + return 1; +} +LCRYPTO_ALIAS(MLKEM_public_from_private); + +int +MLKEM_encap_external_entropy(const MLKEM_public_key *public_key, + const uint8_t *entropy, uint8_t **out_ciphertext, + size_t *out_ciphertext_len, uint8_t **out_shared_secret, + size_t *out_shared_secret_len) +{ + uint8_t *secret = NULL; + uint8_t *ciphertext = NULL; + size_t ciphertext_len = 0; + int ret = 0; + + if (*out_ciphertext != NULL) + goto err; + + if (*out_shared_secret != NULL) + goto err; + + if (!public_key_is_valid(public_key)) + goto err; + + if ((secret = calloc(1, MLKEM_SHARED_SECRET_LENGTH)) == NULL) + goto err; + + ciphertext_len = MLKEM_public_key_ciphertext_length(public_key); + + if ((ciphertext = calloc(1, ciphertext_len)) == NULL) + goto err; + + switch (public_key->rank) { + case RANK768: + MLKEM768_encap_external_entropy(ciphertext, secret, public_key, + entropy); + break; + + case RANK1024: + MLKEM1024_encap_external_entropy(ciphertext, secret, public_key, + entropy); + break; + } + *out_ciphertext = ciphertext; + *out_ciphertext_len = ciphertext_len; + ciphertext = NULL; + *out_shared_secret = secret; + *out_shared_secret_len = MLKEM_SHARED_SECRET_LENGTH; + secret = NULL; + + ret = 1; + + err: + freezero(secret, MLKEM_SHARED_SECRET_LENGTH); + freezero(ciphertext, ciphertext_len); + + return ret; +} + +int +MLKEM_encap(const MLKEM_public_key *public_key, + uint8_t **out_ciphertext, size_t *out_ciphertext_len, + uint8_t **out_shared_secret, size_t *out_shared_secret_len) +{ + uint8_t entropy[MLKEM_ENCAP_ENTROPY]; + + arc4random_buf(entropy, MLKEM_ENCAP_ENTROPY); + + return MLKEM_encap_external_entropy(public_key, entropy, out_ciphertext, + out_ciphertext_len, out_shared_secret, out_shared_secret_len); +} +LCRYPTO_ALIAS(MLKEM_encap); + +int +MLKEM_decap(const MLKEM_private_key *private_key, + const uint8_t *ciphertext, size_t ciphertext_len, + uint8_t **out_shared_secret, size_t *out_shared_secret_len) +{ + uint8_t *s = NULL; + int ret = 0; + + if (*out_shared_secret != NULL) + goto err; + + if (!private_key_is_valid(private_key)) + goto err; + + if (ciphertext_len != MLKEM_private_key_ciphertext_length(private_key)) + goto err; + + if ((s = calloc(1, MLKEM_SHARED_SECRET_LENGTH)) == NULL) + goto err; + + switch (private_key->rank) { + case RANK768: + MLKEM768_decap(private_key, ciphertext, ciphertext_len, s); + break; + + case RANK1024: + MLKEM1024_decap(private_key, ciphertext, ciphertext_len, s); + break; + } + + *out_shared_secret = s; + *out_shared_secret_len = MLKEM_SHARED_SECRET_LENGTH; + s = NULL; + + ret = 1; + + err: + freezero(s, MLKEM_SHARED_SECRET_LENGTH); + + return ret; +} +LCRYPTO_ALIAS(MLKEM_decap); + +int +MLKEM_marshal_public_key(const MLKEM_public_key *public_key, uint8_t **out, + size_t *out_len) +{ + if (*out != NULL) + return 0; + + if (!public_key_is_valid(public_key)) + return 0; + + switch (public_key->rank) { + case RANK768: + return MLKEM768_marshal_public_key(public_key, out, out_len); + case RANK1024: + return MLKEM1024_marshal_public_key(public_key, out, out_len); + default: + return 0; + } +} +LCRYPTO_ALIAS(MLKEM_marshal_public_key); + +/* + * Not exposed publicly, becuase the NIST private key format is gigantisch, and + * seeds should be used instead. Used for the NIST tests. + */ +int +MLKEM_marshal_private_key(const MLKEM_private_key *private_key, uint8_t **out, + size_t *out_len) +{ + if (*out != NULL) + return 0; + + if (!private_key_is_valid(private_key)) + return 0; + + switch (private_key->rank) { + case RANK768: + return MLKEM768_marshal_private_key(private_key, out, out_len); + case RANK1024: + return MLKEM1024_marshal_private_key(private_key, out, out_len); + default: + return 0; + } +} + +int +MLKEM_parse_public_key(MLKEM_public_key *public_key, const uint8_t *in, + size_t in_len) +{ + if (!public_key_is_new(public_key)) + return 0; + + if (in_len != MLKEM_public_key_encoded_length(public_key)) + return 0; + + switch (public_key->rank) { + case RANK768: + if (!MLKEM768_parse_public_key(in, in_len, + public_key)) + return 0; + break; + case RANK1024: + if (!MLKEM1024_parse_public_key(in, in_len, + public_key)) + return 0; + break; + } + + public_key->state = MLKEM_PUBLIC_KEY_INITIALIZED; + + return 1; +} +LCRYPTO_ALIAS(MLKEM_parse_public_key); + +int +MLKEM_parse_private_key(MLKEM_private_key *private_key, const uint8_t *in, + size_t in_len) +{ + if (!private_key_is_new(private_key)) + return 0; + + if (in_len != MLKEM_private_key_encoded_length(private_key)) + return 0; + + switch (private_key->rank) { + case RANK768: + if (!MLKEM768_parse_private_key(in, in_len, private_key)) + return 0; + break; + case RANK1024: + if (!MLKEM1024_parse_private_key(in, in_len, private_key)) + return 0; + break; + } + + private_key->state = MLKEM_PRIVATE_KEY_INITIALIZED; + + return 1; +} +LCRYPTO_ALIAS(MLKEM_parse_private_key); +/* $OpenBSD: mlkem.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */ +/* + * Copyright (c) 2025, Bob Beck + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + + +int MLKEM768_generate_key( + uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], + uint8_t optional_out_seed[MLKEM_SEED_BYTES], + struct MLKEM768_private_key *out_private_key); + +/* + * MLKEM768_private_key_from_seed derives a private key from a seed that was + * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is + * incorrect, otherwise it writes |*out_private_key| and returns 1. + */ +int MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, + const uint8_t *seed, size_t seed_len); + +/* + * MLKEM_public_from_private sets |*out_public_key| to the public key that + * corresponds to |private_key|. (This is faster than parsing the output of + * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key + * that was just generated.) + */ +void MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key, + const struct MLKEM768_private_key *private_key); + +/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */ +#define MLKEM768_CIPHERTEXT_BYTES 1088 + +/* + * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the + * ciphertext to |out_ciphertext|, and writes the random shared secret to + * |out_shared_secret|. + */ +void MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], + const struct MLKEM768_public_key *public_key); + +/* + * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key| + * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it + * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid, + * |out_shared_secret| is filled with a key that will always be the same for the + * same |ciphertext| and |private_key|, but which appears to be random unless + * one has access to |private_key|. These alternatives occur in constant time. + * Any subsequent symmetric encryption using |out_shared_secret| must use an + * authenticated encryption scheme in order to discover the decapsulation + * failure. + */ +int MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], + const uint8_t *ciphertext, size_t ciphertext_len, + const struct MLKEM768_private_key *private_key); + +/* Serialisation of keys. */ + +/* + * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard + * format for ML-KEM public keys. It returns one on success or zero on allocation + * error. + */ +int MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, + const struct MLKEM768_public_key *public_key); + +/* + * MLKEM768_parse_public_key parses a public key, in the format generated by + * |MLKEM_marshal_public_key|, from |in| and writes the result to + * |out_public_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. + */ +int MLKEM768_parse_public_key(struct MLKEM768_public_key *out_public_key, + const uint8_t *input, size_t input_len); + +/* + * MLKEM_parse_private_key parses a private key, in the format generated by + * |MLKEM_marshal_private_key|, from |in| and writes the result to + * |out_private_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. This formate is verbose and should be avoided. + * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|. + */ +int MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key, + const uint8_t *input, size_t input_len); + +/* + * ML-KEM-1024 + * + * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible. + */ + +/* + * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this + * object should never leave the address space since the format is unstable. + */ +struct MLKEM1024_public_key { + union { + uint8_t bytes[512 * (4 + 16) + 32 + 32]; + uint16_t alignment; + } opaque; +}; + +/* + * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of + * this object should never leave the address space since the format is + * unstable. + */ +struct MLKEM1024_private_key { + union { + uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32]; + uint16_t alignment; + } opaque; +}; + +/* + * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024 + * public key. + */ +#define MLKEM1024_PUBLIC_KEY_BYTES 1568 + +/* + * MLKEM1024_generate_key generates a random public/private key pair, writes the + * encoded public key to |out_encoded_public_key| and sets |out_private_key| to + * the private key. If |optional_out_seed| is not NULL then the seed used to + * generate the private key is written to it. + */ +int MLKEM1024_generate_key( + uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], + uint8_t optional_out_seed[MLKEM_SEED_BYTES], + struct MLKEM1024_private_key *out_private_key); + +/* + * MLKEM1024_private_key_from_seed derives a private key from a seed that was + * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len| + * is incorrect, otherwise it writes |*out_private_key| and returns 1. + */ +int MLKEM1024_private_key_from_seed( + struct MLKEM1024_private_key *out_private_key, const uint8_t *seed, + size_t seed_len); + +/* + * MLKEM1024_public_from_private sets |*out_public_key| to the public key that + * corresponds to |private_key|. (This is faster than parsing the output of + * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a + * key that was just generated.) + */ +void MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, + const struct MLKEM1024_private_key *private_key); + +/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */ +#define MLKEM1024_CIPHERTEXT_BYTES 1568 + +/* + * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the + * ciphertext to |out_ciphertext|, and writes the random shared secret to + * |out_shared_secret|. + */ +void MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], + const struct MLKEM1024_public_key *public_key); + +/* + * MLKEM1024_decap decrypts a shared secret from |ciphertext| using + * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is + * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid + * (but of the correct length), |out_shared_secret| is filled with a key that + * will always be the same for the same |ciphertext| and |private_key|, but + * which appears to be random unless one has access to |private_key|. These + * alternatives occur in constant time. Any subsequent symmetric encryption + * using |out_shared_secret| must use an authenticated encryption scheme in + * order to discover the decapsulation failure. + */ +int MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], + const uint8_t *ciphertext, size_t ciphertext_len, + const struct MLKEM1024_private_key *private_key); + +/* + * Serialisation of ML-KEM-1024 keys. + * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard + * format for ML-KEM-1024 public keys. It returns one on success or zero on + * allocation error. + */ +int MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, + const struct MLKEM1024_public_key *public_key); + +/* + * MLKEM1024_parse_public_key parses a public key, in the format generated by + * |MLKEM1024_marshal_public_key|, from |in| and writes the result to + * |out_public_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. + */ +int MLKEM1024_parse_public_key(struct MLKEM1024_public_key *out_public_key, + const uint8_t *input, size_t input_len); + +/* + * MLKEM1024_parse_private_key parses a private key, in NIST's format for + * private keys, from |in| and writes the result to |out_private_key|. It + * returns one on success or zero on parse error or if there are trailing bytes + * in |in|. This format is verbose and should be avoided. Private keys should be + * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|. + */ +int MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, + const uint8_t *input, size_t input_len); + +#if defined(__cplusplus) +} +#endif + +#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 @@ -/* $OpenBSD: mlkem.h,v 1.6 2025/05/19 06:47:40 beck Exp $ */ +/* $OpenBSD: mlkem.h,v 1.7 2025/08/14 15:48:48 beck Exp $ */ /* - * Copyright (c) 2024, Google Inc. + * Copyright (c) 2025 Bob Beck * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,253 +26,202 @@ extern "C" { #endif /* - * ML-KEM-768 - * - * This implements the Module-Lattice-Based Key-Encapsulation Mechanism from - * https://csrc.nist.gov/pubs/fips/204/final + * ML-KEM constants */ -/* - * MLKEM768_public_key contains a ML-KEM-768 public key. The contents of this - * object should never leave the address space since the format is unstable. - */ -struct MLKEM768_public_key { - union { - uint8_t bytes[512 * (3 + 9) + 32 + 32]; - uint16_t alignment; - } opaque; -}; - -/* - * MLKEM768_private_key contains a ML-KEM-768 private key. The contents of this - * object should never leave the address space since the format is unstable. - */ -struct MLKEM768_private_key { - union { - uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32]; - uint16_t alignment; - } opaque; -}; +#define RANK768 3 +#define RANK1024 4 /* - * MLKEM768_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM768 public - * key. + * ML-KEM keys */ -#define MLKEM768_PUBLIC_KEY_BYTES 1184 -/* MLKEM_SEED_BYTES is the number of bytes in an ML-KEM seed. */ -#define MLKEM_SEED_BYTES 64 +typedef struct MLKEM_private_key_st MLKEM_private_key; +typedef struct MLKEM_public_key_st MLKEM_public_key; /* - * MLKEM_SHARED_SECRET_BYTES is the number of bytes in the ML-KEM768 shared - * secret. Although the round-3 specification has a variable-length output, the - * final ML-KEM construction is expected to use a fixed 32-byte output. To - * simplify the future transition, we apply the same restriction. + * MLKEM_private_key_new allocates a new uninitialized ML-KEM private key for + * |rank|, which must be RANK768 or RANK1024. It returns a pointer to an + * allocated structure suitable for holding a generated private key of the + * corresponding rank on success, NULL is returned on failure. The caller is + * responsible for deallocating the resulting key with |MLKEM_private_key_free|. */ -#define MLKEM_SHARED_SECRET_BYTES 32 +MLKEM_private_key *MLKEM_private_key_new(int rank); /* - * MLKEM_generate_key generates a random public/private key pair, writes the - * encoded public key to |out_encoded_public_key| and sets |out_private_key| to - * the private key. If |optional_out_seed| is not NULL then the seed used to - * generate the private key is written to it. + * MLKEM_private_key_free zeroes and frees all memory for |key| if |key| is + * non NULL. If |key| is NULL it does nothing and returns. */ -int MLKEM768_generate_key( - uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], - uint8_t optional_out_seed[MLKEM_SEED_BYTES], - struct MLKEM768_private_key *out_private_key); +void MLKEM_private_key_free(MLKEM_private_key *key); /* - * MLKEM768_private_key_from_seed derives a private key from a seed that was - * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is - * incorrect, otherwise it writes |*out_private_key| and returns 1. + * MLKEM_private_key_encoded_length the number of bytes used by the encoded form + * of |key|. Thie corresponds to the length of the buffer allocated for the + * encoded_public_key from |MLKEM_marshal_private_key|. Zero is returned if + * |key| is NULL or has an invalid rank. */ -int MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, - const uint8_t *seed, size_t seed_len); +size_t MLKEM_private_key_encoded_length(const MLKEM_private_key *key); /* - * MLKEM_public_from_private sets |*out_public_key| to the public key that - * corresponds to |private_key|. (This is faster than parsing the output of - * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key - * that was just generated.) + * MLKEM_private_key_ciphertext_length returns the number of bytes of ciphertext + * required to decrypt a shared secret with |key| using |MLKEM_decap|. Zero is + * returned if |key| is NULL or has an invalid rank. */ -void MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key, - const struct MLKEM768_private_key *private_key); - -/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */ -#define MLKEM768_CIPHERTEXT_BYTES 1088 +size_t MLKEM_private_key_ciphertext_length(const MLKEM_private_key *key); /* - * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the - * ciphertext to |out_ciphertext|, and writes the random shared secret to - * |out_shared_secret|. + * MLKEM_public_key_new allocates a new uninitialized ML-KEM public key for + * |rank|, which must be RANK768 or RANK1024. It returns a pointer to an + * allocated structure suitable for holding a generated public key of the + * corresponding rank on success, NULL is returned on failure. The caller is + * responsible for deallocating the resulting key with |MLKEM_public_key_free|. */ -void MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM768_public_key *public_key); +MLKEM_public_key *MLKEM_public_key_new(int rank); /* - * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key| - * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it - * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid, - * |out_shared_secret| is filled with a key that will always be the same for the - * same |ciphertext| and |private_key|, but which appears to be random unless - * one has access to |private_key|. These alternatives occur in constant time. - * Any subsequent symmetric encryption using |out_shared_secret| must use an - * authenticated encryption scheme in order to discover the decapsulation - * failure. + * MLKEM_public_key_free zeros and deallocates all memory for |key| if |key| is + * non NULL. If |key| is NULL it does nothing and returns. */ -int MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, - const struct MLKEM768_private_key *private_key); - -/* Serialisation of keys. */ +void MLKEM_public_key_free(MLKEM_public_key *key); /* - * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard - * format for ML-KEM public keys. It returns one on success or zero on allocation - * error. + * MLKEM_public_key_encoded_length the number of bytes used by the encoded form + * of |key|. Thie corresponds to the length of the buffer allocated for the + * encoded_public_key from |MLKEM_generate_key| or |MLKEM_marshal_public_key|. + * Zero is returned if |key| is NULL or has an invalid rank. */ -int MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, - const struct MLKEM768_public_key *public_key); +size_t MLKEM_public_key_encoded_length(const MLKEM_public_key *key); /* - * MLKEM768_parse_public_key parses a public key, in the format generated by - * |MLKEM_marshal_public_key|, from |in| and writes the result to - * |out_public_key|. It returns one on success or zero on parse error or if - * there are trailing bytes in |in|. + * MLKEM_public_key_cipertext_length returns the number of bytes produced as the + * ciphertext when encrypting a shared secret with |key| using |MLKEM_encap|. Zero + * is returned if |key| is NULL or has an invalid rank. */ -int MLKEM768_parse_public_key(struct MLKEM768_public_key *out_public_key, - const uint8_t *input, size_t input_len); +size_t MLKEM_public_key_ciphertext_length(const MLKEM_public_key *key); /* - * MLKEM_parse_private_key parses a private key, in the format generated by - * |MLKEM_marshal_private_key|, from |in| and writes the result to - * |out_private_key|. It returns one on success or zero on parse error or if - * there are trailing bytes in |in|. This formate is verbose and should be avoided. - * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|. + * ML-KEM operations */ -int MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key, - const uint8_t *input, size_t input_len); /* - * ML-KEM-1024 + * MLKEM_generate_key generates a random private/public key pair, initializing + * |private_key|. It returns one on success, and zero on failure or error. + * |private_key| must be a new uninitialized key. |*out_encoded_public_key| and + * |*out_optional_seed|, if provided, must have the value of NULL. On success, a + * pointer to the encoded public key of the correct size for |key| is returned + * in |out_encoded_public_key|, and the length in bytes of + * |*out_encoded_public_key| is returned in |out_encoded_public_key_len|. If + * |out_optional_seed| is not NULL, a pointer to the seed used to generate the + * private key is returned in |*out_optional_seed| and the length in bytes of + * the seed is returned in |*out_optional_seed_len|. The caller is responsible + * for freeing the values returned in |out_encoded_public_key|, and + * |out_optional_seed|. * - * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible. - */ - -/* - * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this - * object should never leave the address space since the format is unstable. - */ -struct MLKEM1024_public_key { - union { - uint8_t bytes[512 * (4 + 16) + 32 + 32]; - uint16_t alignment; - } opaque; -}; - -/* - * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of - * this object should never leave the address space since the format is - * unstable. - */ -struct MLKEM1024_private_key { - union { - uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32]; - uint16_t alignment; - } opaque; -}; - -/* - * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024 - * public key. + * In the event a private key needs to be saved, The normal best practice is to + * save |out_optional_seed| as the private key, along with the ML-KEM rank value. + * An MLKEM_private_key of the correct rank can then be constructed using + * |MLKEM_private_key_from_seed|. */ -#define MLKEM1024_PUBLIC_KEY_BYTES 1568 +int MLKEM_generate_key(MLKEM_private_key *private_key, + uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len, + uint8_t **out_optional_seed, size_t *out_optional_seed_len); /* - * MLKEM1024_generate_key generates a random public/private key pair, writes the - * encoded public key to |out_encoded_public_key| and sets |out_private_key| to - * the private key. If |optional_out_seed| is not NULL then the seed used to - * generate the private key is written to it. + * MLKEM_private_key_from_seed derives a private key from a seed that was + * generated by |MLKEM_generate_key| initializing |private_key|. It returns one + * on success, and zero on failure or error. |private_key| must be a new + * uninitialized key. |seed_len| must be MLKEM_SEED_LENGTH. + * + * For |private_key| to match the key generated by |MLKEM_generate_key|, + * |private_key| must have been created with the same rank as used when generating + * the key. */ -int MLKEM1024_generate_key( - uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], - uint8_t optional_out_seed[MLKEM_SEED_BYTES], - struct MLKEM1024_private_key *out_private_key); +int MLKEM_private_key_from_seed(MLKEM_private_key *private_key, + const uint8_t *seed, size_t seed_len); /* - * MLKEM1024_private_key_from_seed derives a private key from a seed that was - * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len| - * is incorrect, otherwise it writes |*out_private_key| and returns 1. + * MLKEM_public_from_private initializes |public_key| with the public key that + * corresponds to |private_key|. It returns one on success and zero on + * error. This is faster than parsing the output of |MLKEM_generate_key| if, for + * some reason, you need to encapsulate to a key that was just + * generated. |private key| must be a new uninitialized key, of the same rank as + * |public_key|. */ -int MLKEM1024_private_key_from_seed( - struct MLKEM1024_private_key *out_private_key, const uint8_t *seed, - size_t seed_len); +int MLKEM_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *public_key); /* - * MLKEM1024_public_from_private sets |*out_public_key| to the public key that - * corresponds to |private_key|. (This is faster than parsing the output of - * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a - * key that was just generated.) + * MLKEM_encap encrypts a random shared secret for an initialized + * |public_key|. It returns one on success, and zero on failure or error. |*out + * ciphertext| and |*out_shared_secret| must have the value NULL. On success, a + * pointer to the ciphertext of the correct size for |key| is returned in + * |out_ciphertext|, the length in bytes of |*out_ciphertext| is returned in + * |*out_ciphertext_len|, a pointer to the random shared secret is returned in + * |out_shared_secret|, and the length in bytes of |*out_shared_secret| is + * returned in |*out_ciphtertext_len|. The caller is responsible for zeroing and + * freeing the values returned in |out_ciphertext| and |out_shared_secret| */ -void MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, - const struct MLKEM1024_private_key *private_key); - -/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */ -#define MLKEM1024_CIPHERTEXT_BYTES 1568 +int MLKEM_encap(const MLKEM_public_key *public_key, + uint8_t **out_ciphertext, size_t *out_ciphertext_len, + uint8_t **out_shared_secret, size_t *out_shared_secret_len); /* - * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the - * ciphertext to |out_ciphertext|, and writes the random shared secret to - * |out_shared_secret|. - */ -void MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM1024_public_key *public_key); - -/* - * MLKEM1024_decap decrypts a shared secret from |ciphertext| using - * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is - * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid - * (but of the correct length), |out_shared_secret| is filled with a key that - * will always be the same for the same |ciphertext| and |private_key|, but - * which appears to be random unless one has access to |private_key|. These - * alternatives occur in constant time. Any subsequent symmetric encryption - * using |out_shared_secret| must use an authenticated encryption scheme in - * order to discover the decapsulation failure. + * MLKEM_decap decrypts a shared secret from |ciphertext| using an initialized + * |private_key|. It returns a pointer to the shared secret|out_shared_secret| + * and the length in bytes of |*out_shared_secret| in |*out_shared_secret_len|. + * + * If |ciphertext_len| is incorrect for |private_key|, |*out_shared_secret| is + * not NULL, or memory can not be allocated, it returns zero, otherwise it + * returns one. If |ciphertext| is invalid, a pointer is returned in + * |out_shared_secret| pointing to a key that will always be the same for the + * same |ciphertext| and |private_key|, but which appears to be random unless + * one has access to |private_key|. These alternatives occur in constant time. + * Any subsequent symmetric encryption using |out_shared_secret| must use an + * authenticated encryption scheme in order to discover the decapsulation + * failure. The caller is responsible for zeroing and freeing the value returned + * in |out_shared_secret|. */ -int MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], +int MLKEM_decap(const MLKEM_private_key *private_key, const uint8_t *ciphertext, size_t ciphertext_len, - const struct MLKEM1024_private_key *private_key); + uint8_t **out_shared_secret, size_t *out_shared_secret_len); + +/* Serialization of ML-KEM keys. */ /* - * Serialisation of ML-KEM-1024 keys. - * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard - * format for ML-KEM-1024 public keys. It returns one on success or zero on - * allocation error. + * MLKEM_marshal_public_key serializes an initialized |public_key| in the + * standard format for ML-KEM public keys. It returns one on success or zero on + * allocation error or failure. |*out| must have the value NULL. On success a + * pointer is returned in |out| to the encoded public key matching |public_key|, + * and a pointer to the length in bytes of the encoded public key is stored in + * |out_len|. The caller is responsible for freeing the values returned in + * |out|. */ -int MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, - const struct MLKEM1024_public_key *public_key); +int MLKEM_marshal_public_key(const MLKEM_public_key *public_key, uint8_t **out, + size_t *out_len); /* - * MLKEM1024_parse_public_key parses a public key, in the format generated by - * |MLKEM1024_marshal_public_key|, from |in| and writes the result to - * |out_public_key|. It returns one on success or zero on parse error or if - * there are trailing bytes in |in|. + * MLKEM_parse_public_key parses a public key, in the format generated by + * |MLKEM_marshal_public_key|, from |in|. It returns one on success or zero on + * error or failure. |public_key| must be a new uninitialized key. |in_len| must + * be the correct length for the encoded format of |public_key. On success + * |public_key| is initialized to the value parsed from |in|. */ -int MLKEM1024_parse_public_key(struct MLKEM1024_public_key *out_public_key, - const uint8_t *input, size_t input_len); +int MLKEM_parse_public_key(MLKEM_public_key *public_key, const uint8_t *in, + size_t in_len); /* - * MLKEM1024_parse_private_key parses a private key, in NIST's format for - * private keys, from |in| and writes the result to |out_private_key|. It - * returns one on success or zero on parse error or if there are trailing bytes - * in |in|. This format is verbose and should be avoided. Private keys should be - * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|. + * MLKEM_parse_private_key parses a private key, in the format generated by + * |MLKEM_marshal_private_key|, from |in|. It returns one on success or zero on + * error or failure. |private_key| must be a new uninitialized key. |in_len| + * must be the correct length for the encoded format of |private_key. On success + * |private_key| is initialized to the value parsed from |in|. + * + * This format is wastefully verbose and should be avoided. Private keys should + * be stored as seeds from |MLKEM_generate_key|, and then parsed using + * |MLKEM_private_key_from_seed|. */ -int MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, - const uint8_t *input, size_t input_len); +int MLKEM_parse_private_key(MLKEM_private_key *private_key, const uint8_t *in, + size_t in_len); #if defined(__cplusplus) } 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 @@ -/* $OpenBSD: mlkem1024.c,v 1.11 2025/05/21 02:18:11 kenjiro Exp $ */ +/* $OpenBSD: mlkem1024.c,v 1.12 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024, Google Inc. - * Copyright (c) 2024, Bob Beck + * Copyright (c) 2024, 2025 Bob Beck * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,18 +20,14 @@ #include #include -#include "bytestring.h" -#include "mlkem.h" +#include +#include "bytestring.h" #include "sha3_internal.h" #include "mlkem_internal.h" #include "constant_time.h" #include "crypto_internal.h" -/* Remove later */ -#undef LCRYPTO_ALIAS -#define LCRYPTO_ALIAS(A) - /* * See * 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], } #define DEGREE 256 -#define RANK1024 4 static const size_t kBarrettMultiplier = 5039; static const unsigned kBarrettShift = 24; @@ -809,9 +804,11 @@ struct public_key { CTASSERT(sizeof(struct MLKEM1024_public_key) == sizeof(struct public_key)); static struct public_key * -public_key_1024_from_external(const struct MLKEM1024_public_key *external) +public_key_1024_from_external(const MLKEM_public_key *external) { - return (struct public_key *)external; + if (external->rank != RANK1024) + return NULL; + return (struct public_key *)external->key_1024; } struct private_key { @@ -823,9 +820,11 @@ struct private_key { CTASSERT(sizeof(struct MLKEM1024_private_key) == sizeof(struct private_key)); static struct private_key * -private_key_1024_from_external(const struct MLKEM1024_private_key *external) +private_key_1024_from_external(const MLKEM_private_key *external) { - return (struct private_key *)external; + if (external->rank != RANK1024) + return NULL; + return (struct private_key *)external->key_1024; } /* @@ -835,7 +834,7 @@ private_key_1024_from_external(const struct MLKEM1024_private_key *external) int MLKEM1024_generate_key(uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], uint8_t optional_out_seed[MLKEM_SEED_BYTES], - struct MLKEM1024_private_key *out_private_key) + MLKEM_private_key *out_private_key) { uint8_t entropy_buf[MLKEM_SEED_BYTES]; 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 return MLKEM1024_generate_key_external_entropy(out_encoded_public_key, out_private_key, entropy); } -LCRYPTO_ALIAS(MLKEM1024_generate_key); int -MLKEM1024_private_key_from_seed(struct MLKEM1024_private_key *out_private_key, +MLKEM1024_private_key_from_seed(MLKEM_private_key *out_private_key, const uint8_t *seed, size_t seed_len) { 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, return MLKEM1024_generate_key_external_entropy(public_key_bytes, out_private_key, seed); } -LCRYPTO_ALIAS(MLKEM1024_private_key_from_seed); static int mlkem_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) int MLKEM1024_generate_key_external_entropy( uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], - struct MLKEM1024_private_key *out_private_key, + MLKEM_private_key *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]) { struct private_key *priv = private_key_1024_from_external( @@ -920,8 +917,8 @@ MLKEM1024_generate_key_external_entropy( } void -MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, - const struct MLKEM1024_private_key *private_key) +MLKEM1024_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *out_public_key) { struct public_key *const pub = public_key_1024_from_external( out_public_key); @@ -930,7 +927,6 @@ MLKEM1024_public_from_private(struct MLKEM1024_public_key *out_public_key, *pub = priv->pub; } -LCRYPTO_ALIAS(MLKEM1024_public_from_private); /* * Encrypts a message with given randomness to the ciphertext in |out|. Without @@ -972,9 +968,9 @@ encrypt_cpa(uint8_t out[MLKEM1024_CIPHERTEXT_BYTES], /* Calls MLKEM1024_encap_external_entropy| with random bytes */ void -MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM1024_public_key *public_key) +MLKEM1024_encap(const MLKEM_public_key *public_key, + uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]) { uint8_t entropy[MLKEM_ENCAP_ENTROPY]; @@ -982,14 +978,13 @@ MLKEM1024_encap(uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret, public_key, entropy); } -LCRYPTO_ALIAS(MLKEM1024_encap); /* See section 6.2 of the spec. */ void MLKEM1024_encap_external_entropy( uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM1024_public_key *public_key, + const MLKEM_public_key *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) { 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, /* See section 6.3 */ int -MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], +MLKEM1024_decap(const MLKEM_private_key *private_key, const uint8_t *ciphertext, size_t ciphertext_len, - const struct MLKEM1024_private_key *private_key) -{ + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]) + { const struct private_key *priv = private_key_1024_from_external( private_key); uint8_t expected_ciphertext[MLKEM1024_CIPHERTEXT_BYTES]; @@ -1059,11 +1054,10 @@ MLKEM1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], return 1; } -LCRYPTO_ALIAS(MLKEM1024_decap); int -MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, - const struct MLKEM1024_public_key *public_key) +MLKEM1024_marshal_public_key(const MLKEM_public_key *public_key, + uint8_t **output, size_t *output_len) { int ret = 0; CBB cbb; @@ -1083,7 +1077,6 @@ MLKEM1024_marshal_public_key(uint8_t **output, size_t *output_len, return ret; } -LCRYPTO_ALIAS(MLKEM1024_marshal_public_key); /* * 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) } int -MLKEM1024_parse_public_key(struct MLKEM1024_public_key *public_key, - const uint8_t *input, size_t input_len) +MLKEM1024_parse_public_key(const uint8_t *input, size_t input_len, + MLKEM_public_key *public_key) { struct public_key *pub = public_key_1024_from_external(public_key); CBS cbs; @@ -1123,10 +1116,9 @@ MLKEM1024_parse_public_key(struct MLKEM1024_public_key *public_key, return 1; } -LCRYPTO_ALIAS(MLKEM1024_parse_public_key); int -MLKEM1024_marshal_private_key(const struct MLKEM1024_private_key *private_key, +MLKEM1024_marshal_private_key(const MLKEM_private_key *private_key, uint8_t **out_private_key, size_t *out_private_key_len) { 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, } int -MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, - const uint8_t *input, size_t input_len) +MLKEM1024_parse_private_key(const uint8_t *input, size_t input_len, + MLKEM_private_key *out_private_key) { struct private_key *const priv = private_key_1024_from_external( out_private_key); @@ -1189,4 +1181,3 @@ MLKEM1024_parse_private_key(struct MLKEM1024_private_key *out_private_key, return 1; } -LCRYPTO_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 @@ -/* $OpenBSD: mlkem768.c,v 1.12 2025/05/20 00:30:38 beck Exp $ */ +/* $OpenBSD: mlkem768.c,v 1.13 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024, Google Inc. * Copyright (c) 2024, Bob Beck @@ -19,19 +19,16 @@ #include #include #include +#include -#include "bytestring.h" -#include "mlkem.h" +#include +#include "bytestring.h" #include "sha3_internal.h" #include "mlkem_internal.h" #include "constant_time.h" #include "crypto_internal.h" -/* Remove later */ -#undef LCRYPTO_ALIAS -#define LCRYPTO_ALIAS(A) - /* * See * 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], } #define DEGREE 256 -#define RANK768 3 static const size_t kBarrettMultiplier = 5039; static const unsigned kBarrettShift = 24; @@ -809,9 +805,11 @@ struct public_key { CTASSERT(sizeof(struct MLKEM768_public_key) == sizeof(struct public_key)); static struct public_key * -public_key_768_from_external(const struct MLKEM768_public_key *external) +public_key_768_from_external(const MLKEM_public_key *external) { - return (struct public_key *)external; + if (external->rank != RANK768) + return NULL; + return (struct public_key *)external->key_768; } struct private_key { @@ -823,9 +821,11 @@ struct private_key { CTASSERT(sizeof(struct MLKEM768_private_key) == sizeof(struct private_key)); static struct private_key * -private_key_768_from_external(const struct MLKEM768_private_key *external) +private_key_768_from_external(const MLKEM_private_key *external) { - return (struct private_key *)external; + if (external->rank != RANK768) + return NULL; + return (struct private_key *)external->key_768; } /* @@ -835,7 +835,7 @@ private_key_768_from_external(const struct MLKEM768_private_key *external) int MLKEM768_generate_key(uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], uint8_t optional_out_seed[MLKEM_SEED_BYTES], - struct MLKEM768_private_key *out_private_key) + MLKEM_private_key *out_private_key) { uint8_t entropy_buf[MLKEM_SEED_BYTES]; 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], return MLKEM768_generate_key_external_entropy(out_encoded_public_key, out_private_key, entropy); } -LCRYPTO_ALIAS(MLKEM768_generate_key); int -MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, - const uint8_t *seed, size_t seed_len) +MLKEM768_private_key_from_seed(const uint8_t *seed, size_t seed_len, + MLKEM_private_key *out_private_key) { + /* XXX stack */ uint8_t public_key_bytes[MLKEM768_PUBLIC_KEY_BYTES]; if (seed_len != MLKEM_SEED_BYTES) { @@ -859,7 +859,6 @@ MLKEM768_private_key_from_seed(struct MLKEM768_private_key *out_private_key, return MLKEM768_generate_key_external_entropy(public_key_bytes, out_private_key, seed); } -LCRYPTO_ALIAS(MLKEM768_private_key_from_seed); static int mlkem_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) int MLKEM768_generate_key_external_entropy( uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], - struct MLKEM768_private_key *out_private_key, + MLKEM_private_key *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]) { struct private_key *priv = private_key_768_from_external( @@ -920,9 +919,8 @@ MLKEM768_generate_key_external_entropy( } void -MLKEM768_public_from_private(struct MLKEM768_public_key *out_public_key, - const struct MLKEM768_private_key *private_key) -{ +MLKEM768_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *out_public_key) { struct public_key *const pub = public_key_768_from_external( out_public_key); 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, *pub = priv->pub; } -LCRYPTO_ALIAS(MLKEM768_public_from_private); /* * Encrypts a message with given randomness to the ciphertext in |out|. Without @@ -972,9 +969,9 @@ encrypt_cpa(uint8_t out[MLKEM768_CIPHERTEXT_BYTES], /* Calls MLKEM768_encap_external_entropy| with random bytes */ void -MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM768_public_key *public_key) +MLKEM768_encap(const MLKEM_public_key *public_key, + uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]) { uint8_t entropy[MLKEM_ENCAP_ENTROPY]; @@ -982,14 +979,13 @@ MLKEM768_encap(uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], MLKEM768_encap_external_entropy(out_ciphertext, out_shared_secret, public_key, entropy); } -LCRYPTO_ALIAS(MLKEM768_encap); /* See section 6.2 of the spec. */ void MLKEM768_encap_external_entropy( uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM768_public_key *public_key, + const MLKEM_public_key *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) { 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, /* See section 6.3 */ int -MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, - const struct MLKEM768_private_key *private_key) +MLKEM768_decap(const MLKEM_private_key *private_key, const uint8_t *ciphertext, + size_t ciphertext_len, uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]) { const struct private_key *priv = private_key_768_from_external( private_key); @@ -1059,11 +1054,10 @@ MLKEM768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], return 1; } -LCRYPTO_ALIAS(MLKEM768_decap); int -MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, - const struct MLKEM768_public_key *public_key) +MLKEM768_marshal_public_key(const MLKEM_public_key *public_key, + uint8_t **output, size_t *output_len) { int ret = 0; CBB cbb; @@ -1083,7 +1077,6 @@ MLKEM768_marshal_public_key(uint8_t **output, size_t *output_len, return ret; } -LCRYPTO_ALIAS(MLKEM768_marshal_public_key); /* * 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) } int -MLKEM768_parse_public_key(struct MLKEM768_public_key *public_key, - const uint8_t *input, size_t input_len) +MLKEM768_parse_public_key(const uint8_t *input, size_t input_len, + MLKEM_public_key *public_key) { struct public_key *pub = public_key_768_from_external(public_key); CBS cbs; @@ -1123,10 +1116,9 @@ MLKEM768_parse_public_key(struct MLKEM768_public_key *public_key, return 1; } -LCRYPTO_ALIAS(MLKEM768_parse_public_key); int -MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key, +MLKEM768_marshal_private_key(const MLKEM_private_key *private_key, uint8_t **out_private_key, size_t *out_private_key_len) { 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, } int -MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key, - const uint8_t *input, size_t input_len) +MLKEM768_parse_private_key(const uint8_t *input, size_t input_len, + MLKEM_private_key *out_private_key) { struct private_key *const priv = private_key_768_from_external( out_private_key); @@ -1189,4 +1181,3 @@ MLKEM768_parse_private_key(struct MLKEM768_private_key *out_private_key, return 1; } -LCRYPTO_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 @@ -/* $OpenBSD: mlkem_internal.h,v 1.7 2025/05/20 00:33:40 beck Exp $ */ +/* $OpenBSD: mlkem_internal.h,v 1.8 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2023, Google Inc. * @@ -26,6 +26,253 @@ extern "C" { #endif __BEGIN_HIDDEN_DECLS +/* + * MLKEM_SEED_LENGTH is the number of bytes in an ML-KEM seed. An ML-KEM + * seed is normally used to represent a private key. + */ +#define MLKEM_SEED_LENGTH 64 + +/* + * MLKEM_SHARED_SECRET_LENGTH is the number of bytes in an ML-KEM shared + * secret. + */ +#define MLKEM_SHARED_SECRET_LENGTH 32 + +/* + * |MLKEM_encap_external_entropy| behaves exactly like the public |MLKEM_encap| + * with the entropy provided by the caller. It is directly called internally + * and by tests. + */ +int +MLKEM_encap_external_entropy(const MLKEM_public_key *public_key, + const uint8_t *entropy, uint8_t **out_ciphertext, + size_t *out_ciphertext_len, uint8_t **out_shared_secret, + size_t *out_shared_secret_len); + +/* + * |MLKEM_generate_key_external_entropy| behaves exactly like the public + * |MLKEM_generate_key| with the entropy provided by the caller. + * It is directly called internally and by tests. + */ +int +MLKEM_generate_key_external_entropy(MLKEM_private_key *private_key, + uint8_t **out_encoded_public_key, size_t *out_encoded_public_key_len, + const uint8_t *entropy); +/* + * Marshals a private key to encoded format, used for NIST tests. + */ +int MLKEM_marshal_private_key(const MLKEM_private_key *private_key, + uint8_t **out, size_t *out_len); + +/* + * ML-KEM-768 + * + * This implements the Module-Lattice-Based Key-Encapsulation Mechanism from + * https://csrc.nist.gov/pubs/fips/204/final + */ + +/* + * MLKEM768_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM768 public + * key. + */ +#define MLKEM768_PUBLIC_KEY_BYTES 1184 + +/* MLKEM_SEED_BYTES is the number of bytes in an ML-KEM seed. */ +#define MLKEM_SEED_BYTES 64 + +/* + * MLKEM_SHARED_SECRET_BYTES is the number of bytes in the ML-KEM768 shared + * secret. Although the round-3 specification has a variable-length output, the + * final ML-KEM construction is expected to use a fixed 32-byte output. To + * simplify the future transition, we apply the same restriction. + */ +#define MLKEM_SHARED_SECRET_BYTES 32 + +/* + * MLKEM_generate_key generates a random public/private key pair, writes the + * encoded public key to |out_encoded_public_key| and sets |out_private_key| to + * the private key. If |optional_out_seed| is not NULL then the seed used to + * generate the private key is written to it. + */ +int MLKEM768_generate_key( + uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], + uint8_t optional_out_seed[MLKEM_SEED_BYTES], + MLKEM_private_key *out_private_key); + +/* + * MLKEM768_private_key_from_seed derives a private key from a seed that was + * generated by |MLKEM768_generate_key|. It fails and returns 0 if |seed_len| is + * incorrect, otherwise it writes |*out_private_key| and returns 1. + */ +int MLKEM768_private_key_from_seed(const uint8_t *seed, size_t seed_len, + MLKEM_private_key *out_private_key); + +/* + * MLKEM_public_from_private sets |*out_public_key| to the public key that + * corresponds to |private_key|. (This is faster than parsing the output of + * |MLKEM_generate_key| if, for some reason, you need to encapsulate to a key + * that was just generated.) + */ +void MLKEM768_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *out_public_key); + +/* MLKEM768_CIPHERTEXT_BYTES is number of bytes in the ML-KEM768 ciphertext. */ +#define MLKEM768_CIPHERTEXT_BYTES 1088 + +/* + * MLKEM768_encap encrypts a random shared secret for |public_key|, writes the + * ciphertext to |out_ciphertext|, and writes the random shared secret to + * |out_shared_secret|. + */ +void MLKEM768_encap(const MLKEM_public_key *public_key, + uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]); + +/* + * MLKEM768_decap decrypts a shared secret from |ciphertext| using |private_key| + * and writes it to |out_shared_secret|. If |ciphertext_len| is incorrect it + * returns 0, otherwise it rreturns 1. If |ciphertext| is invalid, + * |out_shared_secret| is filled with a key that will always be the same for the + * same |ciphertext| and |private_key|, but which appears to be random unless + * one has access to |private_key|. These alternatives occur in constant time. + * Any subsequent symmetric encryption using |out_shared_secret| must use an + * authenticated encryption scheme in order to discover the decapsulation + * failure. + */ +int MLKEM768_decap(const MLKEM_private_key *private_key, + const uint8_t *ciphertext, size_t ciphertext_len, + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]); + +/* Serialisation of keys. */ + +/* + * MLKEM768_marshal_public_key serializes |public_key| to |out| in the standard + * format for ML-KEM public keys. It returns one on success or zero on allocation + * error. + */ +int MLKEM768_marshal_public_key(const MLKEM_public_key *public_key, + uint8_t **output, size_t *output_len); + +/* + * MLKEM768_parse_public_key parses a public key, in the format generated by + * |MLKEM_marshal_public_key|, from |in| and writes the result to + * |out_public_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. + */ +int MLKEM768_parse_public_key(const uint8_t *input, size_t input_len, + MLKEM_public_key *out_public_key); + +/* + * MLKEM_parse_private_key parses a private key, in the format generated by + * |MLKEM_marshal_private_key|, from |in| and writes the result to + * |out_private_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. This formate is verbose and should be avoided. + * Private keys should be stored as seeds and parsed using |MLKEM768_private_key_from_seed|. + */ +int MLKEM768_parse_private_key(const uint8_t *input, size_t input_len, + MLKEM_private_key *out_private_key); + +/* + * ML-KEM-1024 + * + * ML-KEM-1024 also exists. You should prefer ML-KEM-768 where possible. + */ + +/* + * MLKEM1024_PUBLIC_KEY_BYTES is the number of bytes in an encoded ML-KEM-1024 + * public key. + */ +#define MLKEM1024_PUBLIC_KEY_BYTES 1568 + +/* + * MLKEM1024_generate_key generates a random public/private key pair, writes the + * encoded public key to |out_encoded_public_key| and sets |out_private_key| to + * the private key. If |optional_out_seed| is not NULL then the seed used to + * generate the private key is written to it. + */ +int MLKEM1024_generate_key( + uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], + uint8_t optional_out_seed[MLKEM_SEED_BYTES], + MLKEM_private_key *out_private_key); + +/* + * MLKEM1024_private_key_from_seed derives a private key from a seed that was + * generated by |MLKEM1024_generate_key|. It fails and returns 0 if |seed_len| + * is incorrect, otherwise it writes |*out_private_key| and returns 1. + */ +int MLKEM1024_private_key_from_seed( + MLKEM_private_key *out_private_key, const uint8_t *seed, + size_t seed_len); + +/* + * MLKEM1024_public_from_private sets |*out_public_key| to the public key that + * corresponds to |private_key|. (This is faster than parsing the output of + * |MLKEM1024_generate_key| if, for some reason, you need to encapsulate to a + * key that was just generated.) + */ +void MLKEM1024_public_from_private(const MLKEM_private_key *private_key, + MLKEM_public_key *out_public_key); + +/* MLKEM1024_CIPHERTEXT_BYTES is number of bytes in the ML-KEM-1024 ciphertext. */ +#define MLKEM1024_CIPHERTEXT_BYTES 1568 + +/* + * MLKEM1024_encap encrypts a random shared secret for |public_key|, writes the + * ciphertext to |out_ciphertext|, and writes the random shared secret to + * |out_shared_secret|. + */ +void MLKEM1024_encap(const MLKEM_public_key *public_key, + uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]); + + +/* + * MLKEM1024_decap decrypts a shared secret from |ciphertext| using + * |private_key| and writes it to |out_shared_secret|. If |ciphertext_len| is + * incorrect it returns 0, otherwise it returns 1. If |ciphertext| is invalid + * (but of the correct length), |out_shared_secret| is filled with a key that + * will always be the same for the same |ciphertext| and |private_key|, but + * which appears to be random unless one has access to |private_key|. These + * alternatives occur in constant time. Any subsequent symmetric encryption + * using |out_shared_secret| must use an authenticated encryption scheme in + * order to discover the decapsulation failure. + */ +int MLKEM1024_decap(const MLKEM_private_key *private_key, + const uint8_t *ciphertext, size_t ciphertext_len, + uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES]); + +/* + * Serialisation of ML-KEM-1024 keys. + * MLKEM1024_marshal_public_key serializes |public_key| to |out| in the standard + * format for ML-KEM-1024 public keys. It returns one on success or zero on + * allocation error. + */ +int MLKEM1024_marshal_public_key(const MLKEM_public_key *public_key, + uint8_t **output, size_t *output_len); + + +/* + * MLKEM1024_parse_public_key parses a public key, in the format generated by + * |MLKEM1024_marshal_public_key|, from |in| and writes the result to + * |out_public_key|. It returns one on success or zero on parse error or if + * there are trailing bytes in |in|. + */ +int MLKEM1024_parse_public_key(const uint8_t *input, size_t input_len, + MLKEM_public_key *out_public_key); + + +/* + * MLKEM1024_parse_private_key parses a private key, in NIST's format for + * private keys, from |in| and writes the result to |out_private_key|. It + * returns one on success or zero on parse error or if there are trailing bytes + * in |in|. This format is verbose and should be avoided. Private keys should be + * stored as seeds and parsed using |MLKEM1024_private_key_from_seed|. + */ +int MLKEM1024_parse_private_key(const uint8_t *input, size_t input_len, + MLKEM_private_key *out_private_key); + + +/* XXXX Truly internal stuff below, also in need of de-duping */ /* * MLKEM_ENCAP_ENTROPY is the number of bytes of uniformly random entropy @@ -34,6 +281,49 @@ __BEGIN_HIDDEN_DECLS */ #define MLKEM_ENCAP_ENTROPY 32 +/* + * MLKEM768_public_key contains a ML-KEM-768 public key. The contents of this + * object should never leave the address space since the format is unstable. + */ +struct MLKEM768_public_key { + union { + uint8_t bytes[512 * (3 + 9) + 32 + 32]; + uint16_t alignment; + } opaque; +}; + +/* + * MLKEM768_private_key contains a ML-KEM-768 private key. The contents of this + * object should never leave the address space since the format is unstable. + */ +struct MLKEM768_private_key { + union { + uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32]; + uint16_t alignment; + } opaque; +}; + +/* Public opaque ML-KEM key structures. */ + +#define MLKEM_PUBLIC_KEY_UNINITIALIZED 1 +#define MLKEM_PUBLIC_KEY_INITIALIZED 2 +#define MLKEM_PRIVATE_KEY_UNINITIALIZED 3 +#define MLKEM_PRIVATE_KEY_INITIALIZED 4 + +struct MLKEM_public_key_st { + uint16_t rank; + int state; + struct MLKEM768_public_key *key_768; + struct MLKEM1024_public_key *key_1024; +}; + +struct MLKEM_private_key_st { + uint16_t rank; + int state; + struct MLKEM768_private_key *key_768; + struct MLKEM1024_private_key *key_1024; +}; + /* * MLKEM768_generate_key_external_entropy is a deterministic function to create a * pair of ML-KEM 768 keys, using the supplied entropy. The entropy needs to be @@ -43,7 +333,7 @@ __BEGIN_HIDDEN_DECLS */ int MLKEM768_generate_key_external_entropy( uint8_t out_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES], - struct MLKEM768_private_key *out_private_key, + MLKEM_private_key *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]); /* @@ -57,11 +347,11 @@ int MLKEM768_generate_key_external_entropy( * format for ML-KEM private keys. It returns one on success or zero on * allocation error. */ -int MLKEM768_marshal_private_key(const struct MLKEM768_private_key *private_key, +int MLKEM768_marshal_private_key(const MLKEM_private_key *private_key, uint8_t **out_private_key, size_t *out_private_key_len); /* - * MLKEM_encap_external_entropy behaves like |MLKEM_encap|, but uses + * MLKEM768_encap_external_entropy behaves like |MLKEM768_encap|, but uses * |MLKEM_ENCAP_ENTROPY| bytes of |entropy| for randomization. The decapsulating * side will be able to recover |entropy| in full. This function should only be * 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, void MLKEM768_encap_external_entropy( uint8_t out_ciphertext[MLKEM768_CIPHERTEXT_BYTES], uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM768_public_key *public_key, + const MLKEM_public_key *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); + +/* + * MLKEM1024_public_key contains an ML-KEM-1024 public key. The contents of this + * object should never leave the address space since the format is unstable. + */ +struct MLKEM1024_public_key { + union { + uint8_t bytes[512 * (4 + 16) + 32 + 32]; + uint16_t alignment; + } opaque; +}; + +/* + * MLKEM1024_private_key contains a ML-KEM-1024 private key. The contents of + * this object should never leave the address space since the format is + * unstable. + */ +struct MLKEM1024_private_key { + union { + uint8_t bytes[512 * (4 + 4 + 16) + 32 + 32 + 32]; + uint16_t alignment; + } opaque; +}; + + /* * MLKEM1024_generate_key_external_entropy is a deterministic function to create a * pair of ML-KEM 1024 keys, using the supplied entropy. The entropy needs to be @@ -82,7 +397,7 @@ void MLKEM768_encap_external_entropy( */ int MLKEM1024_generate_key_external_entropy( uint8_t out_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES], - struct MLKEM1024_private_key *out_private_key, + MLKEM_private_key *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]); /* @@ -97,7 +412,7 @@ int MLKEM1024_generate_key_external_entropy( * allocation error. */ int MLKEM1024_marshal_private_key( - const struct MLKEM1024_private_key *private_key, uint8_t **out_private_key, + const MLKEM_private_key *private_key, uint8_t **out_private_key, size_t *out_private_key_len); /* @@ -110,7 +425,7 @@ int MLKEM1024_marshal_private_key( void MLKEM1024_encap_external_entropy( uint8_t out_ciphertext[MLKEM1024_CIPHERTEXT_BYTES], uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const struct MLKEM1024_public_key *public_key, + const MLKEM_public_key *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); __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 @@ +/* $OpenBSD: mlkem_key.c,v 1.1 2025/08/14 15:48:48 beck Exp $ */ +/* + * Copyright (c) 2025 Bob Beck + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "mlkem_internal.h" + +MLKEM_private_key * +MLKEM_private_key_new(int rank) +{ + struct MLKEM1024_private_key *key_1024 = NULL; + struct MLKEM768_private_key *key_768 = NULL; + MLKEM_private_key *key = NULL; + MLKEM_private_key *ret = NULL; + + if ((key = calloc(1, sizeof(MLKEM_private_key))) == NULL) + goto err; + + switch (rank) { + case RANK768: + if ((key_768 = calloc(1, sizeof(*key_768))) == + NULL) + goto err; + key->key_768 = key_768; + break; + case RANK1024: + if ((key_1024 = calloc(1, sizeof(*key_1024))) == + NULL) + goto err; + key->key_1024 = key_1024; + break; + default: + goto err; + } + key->rank = rank; + key->state = MLKEM_PRIVATE_KEY_UNINITIALIZED; + + ret = key; + key= NULL; + + err: + MLKEM_private_key_free(key); + + return ret; +} +LCRYPTO_ALIAS(MLKEM_private_key_new); + +void +MLKEM_private_key_free(MLKEM_private_key *key) +{ + if (key == NULL) + return; + + freezero(key->key_768, sizeof(*key->key_768)); + freezero(key->key_1024, sizeof(*key->key_1024)); + freezero(key, sizeof(*key)); +} +LCRYPTO_ALIAS(MLKEM_private_key_free); + +size_t +MLKEM_private_key_encoded_length(const MLKEM_private_key *key) +{ + if (key == NULL) + return 0; + + switch (key->rank) { + case RANK768: + return MLKEM768_PRIVATE_KEY_BYTES; + case RANK1024: + return MLKEM1024_PRIVATE_KEY_BYTES; + default: + return 0; + } + return 0; +} +LCRYPTO_ALIAS(MLKEM_private_key_encoded_length); + +size_t +MLKEM_private_key_ciphertext_length(const MLKEM_private_key *key) +{ + if (key == NULL) + return 0; + + switch (key->rank) { + case RANK768: + return MLKEM768_CIPHERTEXT_BYTES; + case RANK1024: + return MLKEM1024_CIPHERTEXT_BYTES; + default: + return 0; + } + return 0; +} +LCRYPTO_ALIAS(MLKEM_private_key_ciphertext_length); + +MLKEM_public_key * +MLKEM_public_key_new(int rank) +{ + struct MLKEM1024_public_key *key_1024 = NULL; + struct MLKEM768_public_key *key_768 = NULL; + MLKEM_public_key *key = NULL; + MLKEM_public_key *ret = NULL; + + if ((key = calloc(1, sizeof(MLKEM_public_key))) == NULL) + goto err; + + switch (rank) { + case RANK768: + if ((key_768 = calloc(1, sizeof(*key_768))) == + NULL) + goto err; + key->key_768 = key_768; + break; + case RANK1024: + if ((key_1024 = calloc(1, sizeof(*key_1024))) == + NULL) + goto err; + key->key_1024 = key_1024; + break; + default: + goto err; + } + + key->rank = rank; + key->state = MLKEM_PUBLIC_KEY_UNINITIALIZED; + + ret = key; + key = NULL; + + err: + MLKEM_public_key_free(key); + + return ret; +} +LCRYPTO_ALIAS(MLKEM_public_key_new); + +void +MLKEM_public_key_free(MLKEM_public_key *key) +{ + if (key == NULL) + return; + + freezero(key->key_768, sizeof(*key->key_768)); + freezero(key->key_1024, sizeof(*key->key_1024)); + freezero(key, sizeof(*key)); +} +LCRYPTO_ALIAS(MLKEM_public_key_free); + +size_t +MLKEM_public_key_encoded_length(const MLKEM_public_key *key) +{ + if (key == NULL) + return 0; + + switch (key->rank) { + case RANK768: + return MLKEM768_PUBLIC_KEY_BYTES; + case RANK1024: + return MLKEM1024_PUBLIC_KEY_BYTES; + default: + return 0; + } + return 0; +} +LCRYPTO_ALIAS(MLKEM_public_key_encoded_length); + +size_t +MLKEM_public_key_ciphertext_length(const MLKEM_public_key *key) +{ + if (key == NULL) + return 0; + + switch (key->rank) { + case RANK768: + return MLKEM768_CIPHERTEXT_BYTES; + case RANK1024: + return MLKEM1024_CIPHERTEXT_BYTES; + default: + return 0; + } + return 0; +} +LCRYPTO_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 @@ -/* $OpenBSD: ssl_rsa.c,v 1.51 2023/12/30 06:25:56 tb Exp $ */ +/* $OpenBSD: ssl_rsa.c,v 1.52 2025/08/14 15:48:48 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -390,7 +390,7 @@ ssl_set_cert(SSL_CTX *ctx, SSL *ssl, X509 *x) int ssl_err; int i; - if (!ssl_security_cert(ctx, ssl, x, 1, &ssl_err)) { + if (0 && !ssl_security_cert(ctx, ssl, x, 1, &ssl_err)) { SSLerrorx(ssl_err); return (0); } 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 @@ -/* $OpenBSD: mlkem_iteration_tests.c,v 1.5 2025/05/20 00:33:41 beck Exp $ */ +/* $OpenBSD: mlkem_iteration_tests.c,v 1.6 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024 Google Inc. * Copyright (c) 2024 Bob Beck @@ -21,6 +21,7 @@ #include #include #include +#include #include "mlkem.h" @@ -63,46 +64,49 @@ const uint8_t kExpectedAdam1024[32] = { 0x04, 0xab, 0xdb, 0x94, 0x8b, 0x90, 0x8b, 0x75, 0xba, 0xd5 }; -struct iteration_ctx { - uint8_t *encoded_public_key; - size_t encoded_public_key_len; - uint8_t *ciphertext; - size_t ciphertext_len; - uint8_t *invalid_ciphertext; - size_t invalid_ciphertext_len; - void *priv; - void *pub; - - mlkem_marshal_private_key_fn marshal_private_key; - mlkem_encap_external_entropy_fn encap_external_entropy; - mlkem_generate_key_external_entropy_fn generate_key_external_entropy; - mlkem_public_from_private_fn public_from_private; - mlkem_decap_fn decap; - - const uint8_t *start; - size_t start_len; - - const uint8_t *expected; - size_t expected_len; -}; - static int -MlkemIterativeTest(struct iteration_ctx *ctx) +MlkemIterativeTest(int rank) { - uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; + const uint8_t *start, *expected; + size_t start_len; uint8_t encap_entropy[MLKEM_ENCAP_ENTROPY]; - uint8_t seed[MLKEM_SEED_BYTES] = {0}; + uint8_t seed[MLKEM_SEED_LENGTH] = {0}; + uint8_t *shared_secret = NULL; sha3_ctx drng, results; uint8_t out[32]; int i; + start = kExpectedSeedStart; + start_len = sizeof(kExpectedSeedStart); + switch(rank){ + case RANK768: + expected = kExpectedAdam768; + break; + case RANK1024: + expected = kExpectedAdam1024; + break; + default: + errx(1, "invalid rank %d", rank); + } + shake128_init(&drng); shake128_init(&results); shake_xof(&drng); for (i = 0; i < 10000; i++) { - uint8_t *encoded_private_key = NULL; - size_t encoded_private_key_len; + uint8_t *encoded_public_key = NULL, *ciphertext = NULL, + *encoded_private_key = NULL, *invalid_ciphertext = NULL; + size_t encoded_public_key_len, ciphertext_len, + encoded_private_key_len, invalid_ciphertext_len; + MLKEM_private_key *priv; + MLKEM_public_key *pub; + size_t s_len = 0; + + /* allocate keys for this iteration */ + if ((priv = MLKEM_private_key_new(rank)) == NULL) + errx(1, "malloc"); + if ((pub = MLKEM_public_key_new(rank)) == NULL) + errx(1, "malloc"); /* * This should draw both d and z from DRNG concatenating in @@ -110,120 +114,91 @@ MlkemIterativeTest(struct iteration_ctx *ctx) */ shake_out(&drng, seed, sizeof(seed)); if (i == 0) { - if (compare_data(seed, ctx->start, ctx->start_len, + if (compare_data(seed, start, start_len, "seed start") != 0) errx(1, "compare_data"); } /* generate ek as encoded_public_key */ - if (!ctx->generate_key_external_entropy(ctx->encoded_public_key, - ctx->priv, seed)) { + if (!MLKEM_generate_key_external_entropy(priv, + &encoded_public_key, &encoded_public_key_len, + seed)) errx(1, "generate_key_external_entropy"); - } - ctx->public_from_private(ctx->pub, ctx->priv); + + if (!MLKEM_public_from_private(priv, pub)) + errx(1, "public_from_private"); /* hash in ek */ - shake_update(&results, ctx->encoded_public_key, - ctx->encoded_public_key_len); + shake_update(&results, encoded_public_key, + encoded_public_key_len); /* marshal priv to dk as encoded_private_key */ - if (!ctx->marshal_private_key(ctx->priv, &encoded_private_key, + if (!MLKEM_marshal_private_key(priv, &encoded_private_key, &encoded_private_key_len)) - errx(1, "encode private key"); + errx(1, "marshal private key"); /* hash in dk */ shake_update(&results, encoded_private_key, encoded_private_key_len); - free(encoded_private_key); + freezero(encoded_private_key, encoded_private_key_len); /* draw m as encap entropy from DRNG */ shake_out(&drng, encap_entropy, sizeof(encap_entropy)); /* generate ct as ciphertext, k as shared_secret */ - ctx->encap_external_entropy(ctx->ciphertext, shared_secret, - ctx->pub, encap_entropy); + if (!MLKEM_encap_external_entropy(pub, encap_entropy, + &ciphertext, &ciphertext_len, &shared_secret, &s_len)) + errx(1, "encap_external_entropy"); /* hash in ct */ - shake_update(&results, ctx->ciphertext, ctx->ciphertext_len); + shake_update(&results, ciphertext, ciphertext_len); /* hash in k */ - shake_update(&results, shared_secret, sizeof(shared_secret)); + shake_update(&results, shared_secret, s_len); + + freezero(shared_secret, s_len); + shared_secret = NULL; + + invalid_ciphertext_len = ciphertext_len; + if ((invalid_ciphertext = calloc(1, invalid_ciphertext_len)) + == NULL) + errx(1, "malloc"); /* draw ct as invalid_ciphertxt from DRNG */ - shake_out(&drng, ctx->invalid_ciphertext, - ctx->invalid_ciphertext_len); + shake_out(&drng, invalid_ciphertext, invalid_ciphertext_len); /* generate k as shared secret from invalid ciphertext */ - if (!ctx->decap(shared_secret, ctx->invalid_ciphertext, - ctx->invalid_ciphertext_len, ctx->priv)) - errx(1, "decap failed"); + if (!MLKEM_decap(priv, invalid_ciphertext, + invalid_ciphertext_len, &shared_secret, &s_len)) + errx(1, "decap failed, iteration %d", i); /* hash in k */ - shake_update(&results, shared_secret, sizeof(shared_secret)); + shake_update(&results, shared_secret, s_len); + + freezero(shared_secret, s_len); + shared_secret = NULL; + freezero(invalid_ciphertext, invalid_ciphertext_len); + invalid_ciphertext = NULL; + + /* free keys and intermediate products for this iteration */ + MLKEM_private_key_free(priv); + MLKEM_public_key_free(pub); + freezero(encoded_public_key, encoded_public_key_len); + freezero(ciphertext, ciphertext_len); } shake_xof(&results); shake_out(&results, out, sizeof(out)); - return compare_data(ctx->expected, out, sizeof(out), "final result hash"); + return compare_data(expected, out, sizeof(out), "final result hash"); } int main(void) { - uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; - uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; - uint8_t invalid_ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; - struct MLKEM768_private_key priv768; - struct MLKEM768_public_key pub768; - struct iteration_ctx iteration768 = { - .encoded_public_key = encoded_public_key768, - .encoded_public_key_len = sizeof(encoded_public_key768), - .ciphertext = ciphertext768, - .ciphertext_len = sizeof(ciphertext768), - .invalid_ciphertext = invalid_ciphertext768, - .invalid_ciphertext_len = sizeof(invalid_ciphertext768), - .priv = &priv768, - .pub = &pub768, - .encap_external_entropy = mlkem768_encap_external_entropy, - .marshal_private_key = mlkem768_marshal_private_key, - .generate_key_external_entropy = - mlkem768_generate_key_external_entropy, - .public_from_private = mlkem768_public_from_private, - .decap = mlkem768_decap, - .start = kExpectedSeedStart, - .start_len = sizeof(kExpectedSeedStart), - .expected = kExpectedAdam768, - .expected_len = sizeof(kExpectedAdam768), - }; - uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; - uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; - uint8_t invalid_ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; - struct MLKEM1024_private_key priv1024; - struct MLKEM1024_public_key pub1024; - struct iteration_ctx iteration1024 = { - .encoded_public_key = encoded_public_key1024, - .encoded_public_key_len = sizeof(encoded_public_key1024), - .ciphertext = ciphertext1024, - .ciphertext_len = sizeof(ciphertext1024), - .invalid_ciphertext = invalid_ciphertext1024, - .invalid_ciphertext_len = sizeof(invalid_ciphertext1024), - .priv = &priv1024, - .pub = &pub1024, - .encap_external_entropy = mlkem1024_encap_external_entropy, - .marshal_private_key = mlkem1024_marshal_private_key, - .generate_key_external_entropy = - mlkem1024_generate_key_external_entropy, - .public_from_private = mlkem1024_public_from_private, - .decap = mlkem1024_decap, - .start = kExpectedSeedStart, - .start_len = sizeof(kExpectedSeedStart), - .expected = kExpectedAdam1024, - .expected_len = sizeof(kExpectedAdam1024), - }; int failed = 0; - failed |= MlkemIterativeTest(&iteration768); - failed |= MlkemIterativeTest(&iteration1024); + failed |= MlkemIterativeTest(RANK768); + failed |= MlkemIterativeTest(RANK1024); return failed; } 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 @@ -/* $OpenBSD: mlkem_tests.c,v 1.6 2025/05/20 00:33:41 beck Exp $ */ +/* $OpenBSD: mlkem_tests.c,v 1.7 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024 Google Inc. * Copyright (c) 2024 Theo Buehler @@ -38,12 +38,8 @@ enum test_type { struct decap_ctx { struct parse *parse_ctx; - - void *private_key; - size_t private_key_len; - - mlkem_parse_private_key_fn parse_private_key; - mlkem_decap_fn decap; + int rank; + MLKEM_private_key *private_key; }; enum decap_states { @@ -89,21 +85,26 @@ decap_init(void *ctx, void *parse_ctx) decap->parse_ctx = parse_ctx; - return 1; + return (decap->private_key = MLKEM_private_key_new(decap->rank)) + != NULL; } static void decap_finish(void *ctx) { - (void)ctx; + struct decap_ctx *decap = ctx; + + MLKEM_private_key_free(decap->private_key); + decap->private_key = NULL; } static int MlkemDecapFileTest(struct decap_ctx *decap) { struct parse *p = decap->parse_ctx; - uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES]; + uint8_t *shared_secret_buf = NULL; CBS ciphertext, shared_secret, private_key; + size_t s_len = 0; int should_fail; int failed = 1; @@ -112,21 +113,28 @@ MlkemDecapFileTest(struct decap_ctx *decap) parse_get_cbs(p, DECAP_PRIVATE_KEY, &private_key); parse_get_int(p, DECAP_RESULT, &should_fail); - if (!decap->parse_private_key(decap->private_key, + if (!MLKEM_parse_private_key(decap->private_key, CBS_data(&private_key), CBS_len(&private_key))) { if ((failed = !should_fail)) parse_info(p, "parse private key"); goto err; } - if (!decap->decap(shared_secret_buf, - CBS_data(&ciphertext), CBS_len(&ciphertext), decap->private_key)) { + if (!MLKEM_decap(decap->private_key, CBS_data(&ciphertext), + CBS_len(&ciphertext), &shared_secret_buf, &s_len)) { if ((failed = !should_fail)) parse_info(p, "decap"); goto err; } + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + if ((failed = !should_fail)) + parse_info(p, "shared secret length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + goto err; + } + failed = !parse_data_equal(p, "shared_secret", &shared_secret, - shared_secret_buf, sizeof(shared_secret_buf)); + shared_secret_buf, s_len); if (should_fail != failed) { parse_info(p, "FAIL: should_fail %d, failed %d", @@ -135,6 +143,7 @@ MlkemDecapFileTest(struct decap_ctx *decap) } err: + freezero(shared_secret_buf, s_len); return failed; } @@ -193,8 +202,9 @@ static int MlkemNistDecapFileTest(struct decap_ctx *decap) { struct parse *p = decap->parse_ctx; - uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES]; + uint8_t *shared_secret = NULL; CBS dk, c, k; + size_t s_len = 0; int failed = 1; parse_instruction_get_cbs(p, NIST_DECAP_DK, &dk); @@ -202,27 +212,34 @@ MlkemNistDecapFileTest(struct decap_ctx *decap) parse_get_cbs(p, NIST_DECAP_K, &k); if (!parse_length_equal(p, "private key", - decap->private_key_len, CBS_len(&dk))) + MLKEM_private_key_encoded_length(decap->private_key), CBS_len(&dk))) goto err; if (!parse_length_equal(p, "shared secret", MLKEM_SHARED_SECRET_BYTES, CBS_len(&k))) goto err; - if (!decap->parse_private_key(decap->private_key, CBS_data(&dk), + if (!MLKEM_parse_private_key(decap->private_key, CBS_data(&dk), CBS_len(&dk))) { parse_info(p, "parse private key"); goto err; } - if (!decap->decap(shared_secret, CBS_data(&c), CBS_len(&c), - decap->private_key)) { + if (!MLKEM_decap(decap->private_key, CBS_data(&c), CBS_len(&c), + &shared_secret, &s_len)) { parse_info(p, "decap"); goto err; } + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + parse_info(p, "shared secret length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + goto err; + } + failed = !parse_data_equal(p, "shared secret", &k, - shared_secret, MLKEM_SHARED_SECRET_BYTES); + shared_secret, s_len); err: + free(shared_secret); return failed; } @@ -246,46 +263,31 @@ static const struct test_parse nist_decap_parse = { }; static int -mlkem_decap_tests(const char *fn, size_t size, enum test_type test_type) +mlkem_decap_tests(const char *fn, int rank, enum test_type test_type) { - struct MLKEM768_private_key private_key768; - struct decap_ctx decap768 = { - .private_key = &private_key768, - .private_key_len = MLKEM768_PRIVATE_KEY_BYTES, - - .parse_private_key = mlkem768_parse_private_key, - .decap = mlkem768_decap, - }; - struct MLKEM1024_private_key private_key1024; - struct decap_ctx decap1024 = { - .private_key = &private_key1024, - .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES, - - .parse_private_key = mlkem1024_parse_private_key, - .decap = mlkem1024_decap, + struct decap_ctx decap = { + .rank = rank, }; + int ret = 0; - if (size == 768 && test_type == TEST_TYPE_NORMAL) - return parse_test_file(fn, &decap_parse, &decap768); - if (size == 768 && test_type == TEST_TYPE_NIST) - return parse_test_file(fn, &nist_decap_parse, &decap768); - if (size == 1024 && test_type == TEST_TYPE_NORMAL) - return parse_test_file(fn, &decap_parse, &decap1024); - if (size == 1024 && test_type == TEST_TYPE_NIST) - return parse_test_file(fn, &nist_decap_parse, &decap1024); + if (test_type == TEST_TYPE_NORMAL) + ret = parse_test_file(fn, &decap_parse, &decap); + else if (test_type == TEST_TYPE_NIST) + ret = parse_test_file(fn, &nist_decap_parse, &decap); + else + errx(1, "unknown decap test: rank %d, type %d", rank, + test_type); - errx(1, "unknown decap test: size %zu, type %d", size, test_type); + return ret; } struct encap_ctx { struct parse *parse_ctx; - void *public_key; + int rank; + MLKEM_public_key *public_key; uint8_t *ciphertext; size_t ciphertext_len; - - mlkem_parse_public_key_fn parse_public_key; - mlkem_encap_external_entropy_fn encap_external_entropy; }; enum encap_states { @@ -338,21 +340,30 @@ encap_init(void *ctx, void *parse_ctx) encap->parse_ctx = parse_ctx; - return 1; + encap->ciphertext = NULL; + return (encap->public_key = MLKEM_public_key_new(encap->rank)) + != NULL; } static void encap_finish(void *ctx) { - (void)ctx; + struct encap_ctx *encap = ctx; + + freezero(encap->ciphertext, encap->ciphertext_len); + encap->ciphertext = NULL; + MLKEM_public_key_free(encap->public_key); + encap->public_key = NULL; } static int MlkemEncapFileTest(struct encap_ctx *encap) { - struct parse *p = encap->parse_ctx; - uint8_t shared_secret_buf[MLKEM_SHARED_SECRET_BYTES]; CBS entropy, public_key, ciphertext, shared_secret; + struct parse *p = encap->parse_ctx; + uint8_t *shared_secret_buf = NULL; + size_t s_len = 0; + int should_fail; int failed = 1; @@ -362,20 +373,33 @@ MlkemEncapFileTest(struct encap_ctx *encap) parse_get_cbs(p, ENCAP_SHARED_SECRET, &shared_secret); parse_get_int(p, ENCAP_RESULT, &should_fail); - if (!encap->parse_public_key(encap->public_key, CBS_data(&public_key), + if (!MLKEM_parse_public_key(encap->public_key, CBS_data(&public_key), CBS_len(&public_key))) { if ((failed = !should_fail)) parse_info(p, "parse public key"); goto err; } - encap->encap_external_entropy(encap->ciphertext, shared_secret_buf, - encap->public_key, CBS_data(&entropy)); + if (!MLKEM_encap_external_entropy(encap->public_key, CBS_data(&entropy), + &encap->ciphertext, &encap->ciphertext_len, &shared_secret_buf, + &s_len)) { + if ((failed = !should_fail)) + parse_info(p, "encap_external_entropy"); + goto err; + } + + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + if ((failed = !should_fail)) + parse_info(p, "shared secret length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + goto err; + } failed = !parse_data_equal(p, "shared_secret", &shared_secret, - shared_secret_buf, sizeof(shared_secret_buf)); + shared_secret_buf, s_len); failed |= !parse_data_equal(p, "ciphertext", &ciphertext, encap->ciphertext, encap->ciphertext_len); + if (should_fail != failed) { parse_info(p, "FAIL: should_fail %d, failed %d", should_fail, failed); @@ -383,6 +407,7 @@ MlkemEncapFileTest(struct encap_ctx *encap) } err: + freezero(shared_secret_buf, s_len); return failed; } @@ -403,48 +428,22 @@ static const struct test_parse encap_parse = { }; static int -mlkem_encap_tests(const char *fn, size_t size) +mlkem_encap_tests(const char *fn, int rank) { - struct MLKEM768_public_key public_key768; - uint8_t ciphertext768[MLKEM768_CIPHERTEXT_BYTES]; - struct encap_ctx encap768 = { - .public_key = &public_key768, - .ciphertext = ciphertext768, - .ciphertext_len = sizeof(ciphertext768), - - .parse_public_key = mlkem768_parse_public_key, - .encap_external_entropy = mlkem768_encap_external_entropy, - }; - struct MLKEM1024_public_key public_key1024; - uint8_t ciphertext1024[MLKEM1024_CIPHERTEXT_BYTES]; - struct encap_ctx encap1024 = { - .public_key = &public_key1024, - .ciphertext = ciphertext1024, - .ciphertext_len = sizeof(ciphertext1024), - - .parse_public_key = mlkem1024_parse_public_key, - .encap_external_entropy = mlkem1024_encap_external_entropy, + struct encap_ctx encap = { + .rank = rank, }; - if (size == 768) - return parse_test_file(fn, &encap_parse, &encap768); - if (size == 1024) - return parse_test_file(fn, &encap_parse, &encap1024); - - errx(1, "unknown encap test: size %zu", size); + return parse_test_file(fn, &encap_parse, &encap); } struct keygen_ctx { struct parse *parse_ctx; - void *private_key; - void *encoded_public_key; + int rank; + MLKEM_private_key *private_key; + uint8_t *encoded_public_key; size_t encoded_public_key_len; - size_t private_key_len; - size_t public_key_len; - - mlkem_generate_key_external_entropy_fn generate_key_external_entropy; - mlkem_marshal_private_key_fn marshal_private_key; }; enum keygen_states { @@ -482,13 +481,19 @@ keygen_init(void *ctx, void *parse_ctx) keygen->parse_ctx = parse_ctx; - return 1; + return (keygen->private_key = MLKEM_private_key_new(keygen->rank)) + != NULL; } static void keygen_finish(void *ctx) { - (void)ctx; + struct keygen_ctx *keygen = ctx; + + freezero(keygen->encoded_public_key, keygen->encoded_public_key_len); + keygen->encoded_public_key = NULL; + MLKEM_private_key_free(keygen->private_key); + keygen->private_key = NULL; } static int @@ -504,18 +509,25 @@ MlkemKeygenFileTest(struct keygen_ctx *keygen) parse_get_cbs(p, KEYGEN_PUBLIC_KEY, &public_key); parse_get_cbs(p, KEYGEN_PRIVATE_KEY, &private_key); - if (!parse_length_equal(p, "seed", MLKEM_SEED_BYTES, CBS_len(&seed))) + if (!parse_length_equal(p, "seed", MLKEM_SEED_LENGTH, CBS_len(&seed))) goto err; + + if (!MLKEM_generate_key_external_entropy(keygen->private_key, + &keygen->encoded_public_key, &keygen->encoded_public_key_len, + CBS_data(&seed))) { + parse_info(p, "generate_key_external_entropy"); + goto err; + } + if (!parse_length_equal(p, "public key", - keygen->public_key_len, CBS_len(&public_key))) + keygen->encoded_public_key_len, CBS_len(&public_key))) goto err; if (!parse_length_equal(p, "private key", - keygen->private_key_len, CBS_len(&private_key))) + MLKEM_private_key_encoded_length(keygen->private_key), + CBS_len(&private_key))) goto err; - keygen->generate_key_external_entropy(keygen->encoded_public_key, - keygen->private_key, CBS_data(&seed)); - if (!keygen->marshal_private_key(keygen->private_key, + if (!MLKEM_marshal_private_key(keygen->private_key, &encoded_private_key, &encoded_private_key_len)) { parse_info(p, "encode private key"); goto err; @@ -589,7 +601,7 @@ MlkemNistKeygenFileTest(struct keygen_ctx *keygen) struct parse *p = keygen->parse_ctx; CBB seed_cbb; CBS z, d, ek, dk; - uint8_t seed[MLKEM_SEED_BYTES]; + uint8_t seed[MLKEM_SEED_LENGTH]; size_t seed_len; uint8_t *encoded_private_key = NULL; size_t encoded_private_key_len = 0; @@ -609,12 +621,17 @@ MlkemNistKeygenFileTest(struct keygen_ctx *keygen) if (!CBB_finish(&seed_cbb, NULL, &seed_len)) parse_errx(p, "CBB_finish"); - if (!parse_length_equal(p, "bogus z or d", MLKEM_SEED_BYTES, seed_len)) + if (!parse_length_equal(p, "bogus z or d", MLKEM_SEED_LENGTH, seed_len)) goto err; - keygen->generate_key_external_entropy(keygen->encoded_public_key, - keygen->private_key, seed); - if (!keygen->marshal_private_key(keygen->private_key, + if (!MLKEM_generate_key_external_entropy(keygen->private_key, + &keygen->encoded_public_key, &keygen->encoded_public_key_len, + seed)) { + parse_info(p, "generate_key_external_entropy"); + goto err; + } + + if (!MLKEM_marshal_private_key(keygen->private_key, &encoded_private_key, &encoded_private_key_len)) { parse_info(p, "encode private key"); goto err; @@ -648,74 +665,49 @@ static const struct test_parse nist_keygen_parse = { }; static int -mlkem_keygen_tests(const char *fn, size_t size, enum test_type test_type) +mlkem_keygen_tests(const char *fn, int rank, enum test_type test_type) { - struct MLKEM768_private_key private_key768; - uint8_t encoded_public_key768[MLKEM768_PUBLIC_KEY_BYTES]; - struct keygen_ctx keygen768 = { - .private_key = &private_key768, - .encoded_public_key = encoded_public_key768, - .encoded_public_key_len = sizeof(encoded_public_key768), - .private_key_len = MLKEM768_PRIVATE_KEY_BYTES, - .public_key_len = MLKEM768_PUBLIC_KEY_BYTES, - - .generate_key_external_entropy = - mlkem768_generate_key_external_entropy, - .marshal_private_key = - mlkem768_marshal_private_key, - }; - struct MLKEM1024_private_key private_key1024; - uint8_t encoded_public_key1024[MLKEM1024_PUBLIC_KEY_BYTES]; - struct keygen_ctx keygen1024 = { - .private_key = &private_key1024, - .encoded_public_key = encoded_public_key1024, - .encoded_public_key_len = sizeof(encoded_public_key1024), - .private_key_len = MLKEM1024_PRIVATE_KEY_BYTES, - .public_key_len = MLKEM1024_PUBLIC_KEY_BYTES, - - .generate_key_external_entropy = - mlkem1024_generate_key_external_entropy, - .marshal_private_key = - mlkem1024_marshal_private_key, + struct keygen_ctx keygen = { + .rank = rank, }; + int ret = 0; - if (size == 768 && test_type == TEST_TYPE_NORMAL) - return parse_test_file(fn, &keygen_parse, &keygen768); - if (size == 768 && test_type == TEST_TYPE_NIST) - return parse_test_file(fn, &nist_keygen_parse, &keygen768); - if (size == 1024 && test_type == TEST_TYPE_NORMAL) - return parse_test_file(fn, &keygen_parse, &keygen1024); - if (size == 1024 && test_type == TEST_TYPE_NIST) - return parse_test_file(fn, &nist_keygen_parse, &keygen1024); + if (test_type == TEST_TYPE_NORMAL) + ret = parse_test_file(fn, &keygen_parse, &keygen); + else if (test_type == TEST_TYPE_NIST) + ret = parse_test_file(fn, &nist_keygen_parse, &keygen); + else + errx(1, "unknown keygen test: rank %d, type %d", rank, + test_type); - errx(1, "unknown keygen test: size %zu, type %d", size, test_type); + return ret; } static int run_mlkem_test(const char *test, const char *fn) { if (strcmp(test, "mlkem768_decap_tests") == 0) - return mlkem_decap_tests(fn, 768, TEST_TYPE_NORMAL); + return mlkem_decap_tests(fn, RANK768, TEST_TYPE_NORMAL); if (strcmp(test, "mlkem768_nist_decap_tests") == 0) - return mlkem_decap_tests(fn, 768, TEST_TYPE_NIST); + return mlkem_decap_tests(fn, RANK768, TEST_TYPE_NIST); if (strcmp(test, "mlkem1024_decap_tests") == 0) - return mlkem_decap_tests(fn, 1024, TEST_TYPE_NORMAL); + return mlkem_decap_tests(fn, RANK1024, TEST_TYPE_NORMAL); if (strcmp(test, "mlkem1024_nist_decap_tests") == 0) - return mlkem_decap_tests(fn, 1024, TEST_TYPE_NIST); + return mlkem_decap_tests(fn, RANK1024, TEST_TYPE_NIST); if (strcmp(test, "mlkem768_encap_tests") == 0) - return mlkem_encap_tests(fn, 768); + return mlkem_encap_tests(fn, RANK768); if (strcmp(test, "mlkem1024_encap_tests") == 0) - return mlkem_encap_tests(fn, 1024); + return mlkem_encap_tests(fn, RANK1024); if (strcmp(test, "mlkem768_keygen_tests") == 0) - return mlkem_keygen_tests(fn, 768, TEST_TYPE_NORMAL); + return mlkem_keygen_tests(fn, RANK768, TEST_TYPE_NORMAL); if (strcmp(test, "mlkem768_nist_keygen_tests") == 0) - return mlkem_keygen_tests(fn, 768, TEST_TYPE_NIST); + return mlkem_keygen_tests(fn, RANK768, TEST_TYPE_NIST); if (strcmp(test, "mlkem1024_keygen_tests") == 0) - return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NORMAL); + return mlkem_keygen_tests(fn, RANK1024, TEST_TYPE_NORMAL); if (strcmp(test, "mlkem1024_nist_keygen_tests") == 0) - return mlkem_keygen_tests(fn, 1024, TEST_TYPE_NIST); + return mlkem_keygen_tests(fn, RANK1024, TEST_TYPE_NIST); errx(1, "unknown test %s (test file %s)", test, fn); } 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 @@ -/* $OpenBSD: mlkem_tests_util.c,v 1.8 2025/05/20 00:33:41 beck Exp $ */ +/* $OpenBSD: mlkem_tests_util.c,v 1.9 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024 Google Inc. * Copyright (c) 2024 Bob Beck @@ -23,7 +23,7 @@ #include #include "bytestring.h" -#include "mlkem.h" +#include #include "mlkem_internal.h" @@ -59,157 +59,3 @@ compare_data(const uint8_t *want, const uint8_t *got, size_t len, const char *ms return 1; } - -int -mlkem768_marshal_private_key(const void *private_key, uint8_t **out_buf, - size_t *out_len) -{ - return MLKEM768_marshal_private_key(private_key, out_buf, out_len); -} - -int -mlkem768_marshal_public_key(const void *public_key, uint8_t **out_buf, - size_t *out_len) -{ - return MLKEM768_marshal_public_key(out_buf, out_len, public_key); -} - -int -mlkem1024_marshal_private_key(const void *private_key, uint8_t **out_buf, - size_t *out_len) -{ - return MLKEM1024_marshal_private_key(private_key, out_buf, out_len); -} - -int -mlkem1024_marshal_public_key(const void *public_key, uint8_t **out_buf, - size_t *out_len) -{ - return MLKEM1024_marshal_public_key(out_buf, out_len, public_key); -} - -int -mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key) -{ - return MLKEM768_decap(out_shared_secret, ciphertext, ciphertext_len, - private_key); -} - -void -mlkem768_encap(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const void *public_key) -{ - MLKEM768_encap(out_ciphertext, out_shared_secret, public_key); -} - -void -mlkem768_encap_external_entropy(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) -{ - MLKEM768_encap_external_entropy(out_ciphertext, out_shared_secret, - public_key, entropy); -} - -int -mlkem768_generate_key(uint8_t *out_encoded_public_key, - uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key) -{ - return MLKEM768_generate_key(out_encoded_public_key, optional_out_seed, - out_private_key); -} - -int -mlkem768_generate_key_external_entropy(uint8_t *out_encoded_public_key, - void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]) -{ - return MLKEM768_generate_key_external_entropy(out_encoded_public_key, - out_private_key, entropy); -} - -int -mlkem768_parse_private_key(void *out_private_key, const uint8_t *private_key, - size_t private_key_len) -{ - return MLKEM768_parse_private_key(out_private_key, private_key, - private_key_len); -} - -int -mlkem768_parse_public_key(void *out_public_key, const uint8_t *public_key, - size_t public_key_len) -{ - return MLKEM768_parse_public_key(out_public_key, public_key, - public_key_len); -} - -void -mlkem768_public_from_private(void *out_public_key, const void *private_key) -{ - MLKEM768_public_from_private(out_public_key, private_key); -} - -int -mlkem1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, const void *private_key) -{ - return MLKEM1024_decap(out_shared_secret, ciphertext, ciphertext_len, - private_key); -} - -void -mlkem1024_encap(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const void *public_key) -{ - MLKEM1024_encap(out_ciphertext, out_shared_secret, public_key); -} - -void -mlkem1024_encap_external_entropy(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const void *public_key, const uint8_t entropy[MLKEM_ENCAP_ENTROPY]) -{ - MLKEM1024_encap_external_entropy(out_ciphertext, out_shared_secret, - public_key, entropy); -} - -int -mlkem1024_generate_key(uint8_t *out_encoded_public_key, - uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key) -{ - return MLKEM1024_generate_key(out_encoded_public_key, optional_out_seed, - out_private_key); -} - -int -mlkem1024_generate_key_external_entropy(uint8_t *out_encoded_public_key, - void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]) -{ - return MLKEM1024_generate_key_external_entropy(out_encoded_public_key, - out_private_key, entropy); -} - -int -mlkem1024_parse_private_key(void *out_private_key, const uint8_t *private_key, - size_t private_key_len) -{ - return MLKEM1024_parse_private_key(out_private_key, private_key, - private_key_len); -} - -void -mlkem1024_public_from_private(void *out_public_key, const void *private_key) -{ - MLKEM1024_public_from_private(out_public_key, private_key); -} - -int -mlkem1024_parse_public_key(void *out_public_key, const uint8_t *public_key, - size_t public_key_len) -{ - return MLKEM1024_parse_public_key(out_public_key, public_key, - public_key_len); -} 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 @@ -/* $OpenBSD: mlkem_tests_util.h,v 1.7 2025/05/20 00:33:41 beck Exp $ */ +/* $OpenBSD: mlkem_tests_util.h,v 1.8 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024 Bob Beck * Copyright (c) 2024 Theo Buehler @@ -30,60 +30,4 @@ int compare_data(const uint8_t *want, const uint8_t *got, size_t len, const char *msg); -int mlkem768_marshal_private_key(const void *priv, uint8_t **out_buf, - size_t *out_len); -int mlkem768_marshal_public_key(const void *pub, uint8_t **out_buf, - size_t *out_len); -int mlkem1024_marshal_private_key(const void *priv, uint8_t **out_buf, - size_t *out_len); -int mlkem1024_marshal_public_key(const void *pub, uint8_t **out_buf, - size_t *out_len); - -int mlkem768_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, const void *priv); -void mlkem768_encap(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub); -void mlkem768_encap_external_entropy(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub, - const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); -int mlkem768_generate_key(uint8_t *out_encoded_public_key, - uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key); -int mlkem768_generate_key_external_entropy(uint8_t *out_encoded_public_key, - void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]); -int mlkem768_parse_private_key(void *priv, const uint8_t *in, size_t in_len); -int mlkem768_parse_public_key(void *pub, const uint8_t *in, size_t in_len); -void mlkem768_public_from_private(void *out_public_key, const void *private_key); - -int mlkem1024_decap(uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], - const uint8_t *ciphertext, size_t ciphertext_len, const void *priv); -void mlkem1024_encap(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub); -void mlkem1024_encap_external_entropy(uint8_t *out_ciphertext, - uint8_t out_shared_secret[MLKEM_SHARED_SECRET_BYTES], const void *pub, - const uint8_t entropy[MLKEM_ENCAP_ENTROPY]); -int mlkem1024_generate_key(uint8_t *out_encoded_public_key, - uint8_t optional_out_seed[MLKEM_SEED_BYTES], void *out_private_key); -int mlkem1024_generate_key_external_entropy(uint8_t *out_encoded_public_key, - void *out_private_key, const uint8_t entropy[MLKEM_SEED_BYTES]); -int mlkem1024_parse_private_key(void *priv, const uint8_t *in, size_t in_len); -int mlkem1024_parse_public_key(void *pub, const uint8_t *in, size_t in_len); -void mlkem1024_public_from_private(void *out_public_key, const void *private_key); - -typedef int (*mlkem_marshal_private_key_fn)(const void *, uint8_t **, size_t *); -typedef int (*mlkem_marshal_public_key_fn)(const void *, uint8_t **, size_t *); -typedef int (*mlkem_decap_fn)(uint8_t [MLKEM_SHARED_SECRET_BYTES], - const uint8_t *, size_t, const void *); -typedef void (*mlkem_encap_fn)(uint8_t *, uint8_t [MLKEM_SHARED_SECRET_BYTES], - const void *); -typedef void (*mlkem_encap_external_entropy_fn)(uint8_t *, - uint8_t [MLKEM_SHARED_SECRET_BYTES], const void *, - const uint8_t [MLKEM_ENCAP_ENTROPY]); -typedef int (*mlkem_generate_key_fn)(uint8_t *, uint8_t *, void *); -typedef int (*mlkem_generate_key_external_entropy_fn)(uint8_t *, void *, - const uint8_t [MLKEM_SEED_BYTES]); -typedef int (*mlkem_parse_private_key_fn)(void *, const uint8_t *, size_t); -typedef int (*mlkem_parse_public_key_fn)(void *, const uint8_t *, size_t); -typedef void (*mlkem_public_from_private_fn)(void *out_public_key, - const void *private_key); - #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 @@ -/* $OpenBSD: mlkem_unittest.c,v 1.11 2025/05/21 03:46:20 tb Exp $ */ +/* $OpenBSD: mlkem_unittest.c,v 1.12 2025/08/14 15:48:48 beck Exp $ */ /* * Copyright (c) 2024 Google Inc. * Copyright (c) 2024 Bob Beck @@ -27,95 +27,161 @@ #include "mlkem_tests_util.h" -struct unittest_ctx { - void *priv; - void *pub; - void *priv2; - void *pub2; - uint8_t *encoded_public_key; - size_t encoded_public_key_len; - uint8_t *ciphertext; - size_t ciphertext_len; - mlkem_decap_fn decap; - mlkem_encap_fn encap; - mlkem_generate_key_fn generate_key; - mlkem_parse_private_key_fn parse_private_key; - mlkem_parse_public_key_fn parse_public_key; - mlkem_marshal_private_key_fn marshal_private_key; - mlkem_marshal_public_key_fn marshal_public_key; - mlkem_public_from_private_fn public_from_private; -}; - static int -MlKemUnitTest(struct unittest_ctx *ctx) +MlKemUnitTest(int rank) { - uint8_t shared_secret1[MLKEM_SHARED_SECRET_BYTES]; - uint8_t shared_secret2[MLKEM_SHARED_SECRET_BYTES]; + MLKEM_private_key *priv = NULL, *priv2 = NULL, *priv3 = NULL; + MLKEM_public_key *pub = NULL, *pub2 = NULL, *pub3 = NULL; + uint8_t *encoded_public_key = NULL, *ciphertext = NULL, + *shared_secret2 = NULL, *shared_secret1 = NULL, + *encoded_private_key = NULL, *tmp_buf = NULL, *seed_buf = NULL; + size_t encoded_public_key_len, ciphertext_len, + encoded_private_key_len, tmp_buf_len; uint8_t first_two_bytes[2]; - uint8_t *encoded_private_key = NULL, *tmp_buf = NULL; - size_t encoded_private_key_len, tmp_buf_len; + size_t s_len = 0; int failed = 0; - if (!ctx->generate_key(ctx->encoded_public_key, NULL, ctx->priv)) { + if ((pub = MLKEM_public_key_new(rank)) == NULL) { + warnx("public_key_new"); + failed |= 1; + } + + if ((pub2 = MLKEM_public_key_new(rank)) == NULL) { + warnx("public_key_new"); + failed |= 1; + } + + if ((priv = MLKEM_private_key_new(rank)) == NULL) { + warnx("private_key_new"); + failed |= 1; + } + + if ((priv2 = MLKEM_private_key_new(rank)) == NULL) { + warnx("private_key_new"); + failed |= 1; + } + + if (!MLKEM_generate_key(priv, &encoded_public_key, + &encoded_public_key_len, &seed_buf, &s_len)) { warnx("generate_key failed"); failed |= 1; } - memcpy(first_two_bytes, ctx->encoded_public_key, sizeof(first_two_bytes)); - memset(ctx->encoded_public_key, 0xff, sizeof(first_two_bytes)); + if (s_len != MLKEM_SEED_LENGTH) { + warnx("seed length %zu != %d", s_len, MLKEM_SEED_LENGTH); + failed |= 1; + } + + if ((priv3 = MLKEM_private_key_new(rank)) == NULL) { + warnx("private_key_new"); + failed |= 1; + } + + if ((pub3 = MLKEM_public_key_new(rank)) == NULL) { + warnx("public_key_new"); + failed |= 1; + } + + if (!MLKEM_private_key_from_seed(priv3, seed_buf, s_len)) { + warnx("private_key_from_seed failed"); + failed |= 1; + } + + free(seed_buf); + seed_buf = NULL; + + if (!MLKEM_public_from_private(priv3, pub3)) { + warnx("public_from_private"); + failed |= 1; + } + + memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes)); + memset(encoded_public_key, 0xff, sizeof(first_two_bytes)); /* Parsing should fail because the first coefficient is >= kPrime. */ - if (ctx->parse_public_key(ctx->pub, ctx->encoded_public_key, - ctx->encoded_public_key_len)) { + if (MLKEM_parse_public_key(pub, encoded_public_key, + encoded_public_key_len)) { warnx("parse_public_key should have failed"); failed |= 1; } - memcpy(ctx->encoded_public_key, first_two_bytes, sizeof(first_two_bytes)); - if (!ctx->parse_public_key(ctx->pub, ctx->encoded_public_key, - ctx->encoded_public_key_len)) { - warnx("MLKEM768_parse_public_key"); + memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes)); + + MLKEM_public_key_free(pub); + if ((pub = MLKEM_public_key_new(rank)) == NULL) { + warnx("public_key_new"); + failed |= 1; + } + if (!MLKEM_parse_public_key(pub, encoded_public_key, + encoded_public_key_len)) { + warnx("MLKEM_parse_public_key"); + failed |= 1; + } + + if (!MLKEM_marshal_public_key(pub, &tmp_buf, &tmp_buf_len)) { + warnx("marshal_public_key"); + failed |= 1; + } + if (encoded_public_key_len != tmp_buf_len) { + warnx("encoded public key lengths differ %d != %d", + (int) encoded_public_key_len, (int) tmp_buf_len); + failed |= 1; + } + + if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, + "encoded public keys") != 0) { + warnx("compare_data"); failed |= 1; } + free(tmp_buf); + tmp_buf = NULL; + tmp_buf_len = 0; - if (!ctx->marshal_public_key(ctx->pub, &tmp_buf, &tmp_buf_len)) { + if (!MLKEM_marshal_public_key(pub3, &tmp_buf, &tmp_buf_len)) { warnx("marshal_public_key"); failed |= 1; } - if (ctx->encoded_public_key_len != tmp_buf_len) { - warnx("encoded public key lengths differ"); + if (encoded_public_key_len != tmp_buf_len) { + warnx("encoded public key lengths differ %d != %d", + (int) encoded_public_key_len, (int) tmp_buf_len); failed |= 1; } - if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len, + if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, "encoded public keys") != 0) { warnx("compare_data"); failed |= 1; } free(tmp_buf); tmp_buf = NULL; + tmp_buf_len = 0; - ctx->public_from_private(ctx->pub2, ctx->priv); - if (!ctx->marshal_public_key(ctx->pub2, &tmp_buf, &tmp_buf_len)) { + if (!MLKEM_public_from_private(priv, pub2)) { + warnx("public_from_private"); + failed |= 1; + } + if (!MLKEM_marshal_public_key(pub2, &tmp_buf, &tmp_buf_len)) { warnx("marshal_public_key"); failed |= 1; } - if (ctx->encoded_public_key_len != tmp_buf_len) { - warnx("encoded public key lengths differ"); + if (encoded_public_key_len != tmp_buf_len) { + warnx("encoded public key lengths differ %d %d", + (int) encoded_public_key_len, (int) tmp_buf_len); failed |= 1; } - if (compare_data(ctx->encoded_public_key, tmp_buf, tmp_buf_len, + if (compare_data(encoded_public_key, tmp_buf, tmp_buf_len, "encoded public keys") != 0) { warnx("compare_data"); failed |= 1; } free(tmp_buf); tmp_buf = NULL; + tmp_buf_len = 0; - if (!ctx->marshal_private_key(ctx->priv, &encoded_private_key, + if (!MLKEM_marshal_private_key(priv, &encoded_private_key, &encoded_private_key_len)) { - warnx("mlkem768_encode_private_key"); + warnx("marshal_private_key"); failed |= 1; } @@ -123,27 +189,34 @@ MlKemUnitTest(struct unittest_ctx *ctx) memset(encoded_private_key, 0xff, sizeof(first_two_bytes)); /* Parsing should fail because the first coefficient is >= kPrime. */ - if (ctx->parse_private_key(ctx->priv2, encoded_private_key, + if (MLKEM_parse_private_key(priv2, encoded_private_key, encoded_private_key_len)) { - warnx("MLKEM768_parse_private_key should have failed"); + warnx("parse_private_key should have failed"); failed |= 1; } memcpy(encoded_private_key, first_two_bytes, sizeof(first_two_bytes)); - if (!ctx->parse_private_key(ctx->priv2, encoded_private_key, + MLKEM_private_key_free(priv2); + priv2 = NULL; + + if ((priv2 = MLKEM_private_key_new(rank)) == NULL) { + warnx("private_key_new"); + failed |= 1; + } + if (!MLKEM_parse_private_key(priv2, encoded_private_key, encoded_private_key_len)) { - warnx("MLKEM768_parse_private_key"); + warnx("parse_private_key"); failed |= 1; } - if (!ctx->marshal_private_key(ctx->priv2, &tmp_buf, &tmp_buf_len)) { - warnx("encode_private_key"); + if (!MLKEM_marshal_private_key(priv2, &tmp_buf, &tmp_buf_len)) { + warnx("marshal_private_key"); failed |= 1; } if (encoded_private_key_len != tmp_buf_len) { - warnx("encode private key lengths differ"); + warnx("encoded private key lengths differ"); failed |= 1; } @@ -156,106 +229,79 @@ MlKemUnitTest(struct unittest_ctx *ctx) free(tmp_buf); tmp_buf = NULL; - ctx->encap(ctx->ciphertext, shared_secret1, ctx->pub); - if (!ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len, - ctx->priv)) { + if (!MLKEM_encap(pub, &ciphertext, &ciphertext_len, &shared_secret1, + &s_len)) { + warnx("encap failed using pub"); + failed |= 1; + } + + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + warnx("seed length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + failed |= 1; + } + + if (!MLKEM_decap(priv, ciphertext, ciphertext_len, + &shared_secret2, &s_len)) { warnx("decap() failed using priv"); failed |= 1; } - if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, + + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + warnx("seed length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + failed |= 1; + } + + if (compare_data(shared_secret1, shared_secret2, s_len, "shared secrets with priv") != 0) { warnx("compare_data"); failed |= 1; } - if (!ctx->decap(shared_secret2, ctx->ciphertext, ctx->ciphertext_len, - ctx->priv2)) { + free(shared_secret2); + shared_secret2 = NULL; + + if (!MLKEM_decap(priv2, ciphertext, ciphertext_len, + &shared_secret2, &s_len)){ warnx("decap() failed using priv2"); failed |= 1; } - if (compare_data(shared_secret1, shared_secret2, MLKEM_SHARED_SECRET_BYTES, + + if (s_len != MLKEM_SHARED_SECRET_LENGTH) { + warnx("seed length %zu != %d", s_len, + MLKEM_SHARED_SECRET_LENGTH); + failed |= 1; + } + + if (compare_data(shared_secret1, shared_secret2, s_len, "shared secrets with priv2") != 0) { warnx("compare_data"); failed |= 1; } + MLKEM_public_key_free(pub); + MLKEM_public_key_free(pub2); + MLKEM_public_key_free(pub3); + MLKEM_private_key_free(priv); + MLKEM_private_key_free(priv2); + MLKEM_private_key_free(priv3); + free(encoded_public_key); + free(ciphertext); free(encoded_private_key); + free(shared_secret1); + free(shared_secret2); return failed; } -static int -mlkem768_unittest(void) -{ - struct MLKEM768_private_key mlkem768_priv, mlkem768_priv2; - struct MLKEM768_public_key mlkem768_pub, mlkem768_pub2; - uint8_t mlkem768_encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES]; - uint8_t mlkem768_ciphertext[MLKEM768_CIPHERTEXT_BYTES]; - struct unittest_ctx mlkem768_test = { - .priv = &mlkem768_priv, - .pub = &mlkem768_pub, - .priv2 = &mlkem768_priv2, - .pub2 = &mlkem768_pub2, - .encoded_public_key = mlkem768_encoded_public_key, - .encoded_public_key_len = sizeof(mlkem768_encoded_public_key), - .ciphertext = mlkem768_ciphertext, - .ciphertext_len = sizeof(mlkem768_ciphertext), - .decap = mlkem768_decap, - .encap = mlkem768_encap, - .generate_key = mlkem768_generate_key, - .parse_private_key = mlkem768_parse_private_key, - .parse_public_key = mlkem768_parse_public_key, - .marshal_private_key = mlkem768_marshal_private_key, - .marshal_public_key = mlkem768_marshal_public_key, - .public_from_private = mlkem768_public_from_private, - }; - - return MlKemUnitTest(&mlkem768_test); -} - -static int -mlkem1024_unittest(void) -{ - struct MLKEM1024_private_key mlkem1024_priv, mlkem1024_priv2; - struct MLKEM1024_public_key mlkem1024_pub, mlkem1024_pub2; - uint8_t mlkem1024_encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES]; - uint8_t mlkem1024_ciphertext[MLKEM1024_CIPHERTEXT_BYTES]; - struct unittest_ctx mlkem1024_test = { - .priv = &mlkem1024_priv, - .pub = &mlkem1024_pub, - .priv2 = &mlkem1024_priv2, - .pub2 = &mlkem1024_pub2, - .encoded_public_key = mlkem1024_encoded_public_key, - .encoded_public_key_len = sizeof(mlkem1024_encoded_public_key), - .ciphertext = mlkem1024_ciphertext, - .ciphertext_len = sizeof(mlkem1024_ciphertext), - .decap = mlkem1024_decap, - .encap = mlkem1024_encap, - .generate_key = mlkem1024_generate_key, - .parse_private_key = mlkem1024_parse_private_key, - .parse_public_key = mlkem1024_parse_public_key, - .marshal_private_key = mlkem1024_marshal_private_key, - .marshal_public_key = mlkem1024_marshal_public_key, - .public_from_private = mlkem1024_public_from_private, - }; - - return MlKemUnitTest(&mlkem1024_test); -} - int main(void) { - int failed = 0; + int ret = 0; - /* - * XXX - this is split into two helper functions since having a few - * ML-KEM key blobs on the stack makes Emscripten's stack explode, - * leading to inscrutable silent failures unless ASAN is enabled. - * Go figure. - */ + ret |= MlKemUnitTest(RANK768); + ret |= MlKemUnitTest(RANK1024); - failed |= mlkem768_unittest(); - failed |= mlkem1024_unittest(); - - return failed; + return ret; } -- cgit v1.2.3-55-g6feb