From 11026b06dca0fc886d20e79eff0162b42fec3f8f Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sat, 29 Aug 2026 12:19:59 +0200 Subject: lib: Add support for NIST curve hybrid KEMs OpenSSL 3.5 supports SecP256r1MLKEM768 and SecP384r1MLKEM1024 pairing ML-KEM with a NIST curve. These were not supported yet by O7s. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/lib/crypt.c | 58 ++++++++++++++++++++++------------------- src/lib/tests/kex_test_ml_kem.c | 10 ++++--- 2 files changed, 37 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/lib/crypt.c b/src/lib/crypt.c index 35007ef3..8ca7084f 100644 --- a/src/lib/crypt.c +++ b/src/lib/crypt.c @@ -69,39 +69,43 @@ const uint16_t crypt_supported_nids[] = { }; static const struct nid_map kex_nid_map[] = { - {NID_X9_62_prime256v1, "prime256v1"}, - {NID_secp384r1, "secp384r1"}, - {NID_secp521r1, "secp521r1"}, - {NID_X25519, "X25519"}, - {NID_X448, "X448"}, - {NID_ffdhe2048, "ffdhe2048"}, - {NID_ffdhe3072, "ffdhe3072"}, - {NID_ffdhe4096, "ffdhe4096"}, - {NID_MLKEM512, "ML-KEM-512"}, - {NID_MLKEM768, "ML-KEM-768"}, - {NID_MLKEM1024, "ML-KEM-1024"}, - {NID_X25519MLKEM768, "X25519MLKEM768"}, - {NID_X448MLKEM1024, "X448MLKEM1024"}, - {NID_undef, NULL} + {NID_X9_62_prime256v1, "prime256v1"}, + {NID_secp384r1, "secp384r1"}, + {NID_secp521r1, "secp521r1"}, + {NID_X25519, "X25519"}, + {NID_X448, "X448"}, + {NID_ffdhe2048, "ffdhe2048"}, + {NID_ffdhe3072, "ffdhe3072"}, + {NID_ffdhe4096, "ffdhe4096"}, + {NID_MLKEM512, "ML-KEM-512"}, + {NID_MLKEM768, "ML-KEM-768"}, + {NID_MLKEM1024, "ML-KEM-1024"}, + {NID_X25519MLKEM768, "X25519MLKEM768"}, + {NID_X448MLKEM1024, "X448MLKEM1024"}, + {NID_SecP256r1MLKEM768, "SecP256r1MLKEM768"}, + {NID_SecP384r1MLKEM1024, "SecP384r1MLKEM1024"}, + {NID_undef, NULL} }; /* Ordered in strength preference, lowest first (NIST SP 800-57 levels) */ const uint16_t kex_supported_nids[] = { #ifdef HAVE_OPENSSL - NID_ffdhe2048, /* FFDHE-2048, ~112-bit */ - NID_X9_62_prime256v1, /* ECDH P-256, 128-bit */ - NID_X25519, /* ECDH X25519, 128-bit */ - NID_ffdhe3072, /* FFDHE-3072, ~128-bit */ - NID_ffdhe4096, /* FFDHE-4096, ~152-bit */ - NID_secp384r1, /* ECDH P-384, 192-bit */ - NID_X448, /* ECDH X448, 224-bit */ - NID_secp521r1, /* ECDH P-521, 256-bit */ + NID_ffdhe2048, /* FFDHE-2048, ~112-bit */ + NID_X9_62_prime256v1, /* ECDH P-256, 128-bit */ + NID_X25519, /* ECDH X25519, 128-bit */ + NID_ffdhe3072, /* FFDHE-3072, ~128-bit */ + NID_ffdhe4096, /* FFDHE-4096, ~152-bit */ + NID_secp384r1, /* ECDH P-384, 192-bit */ + NID_X448, /* ECDH X448, 224-bit */ + NID_secp521r1, /* ECDH P-521, 256-bit */ #ifdef HAVE_ML - NID_MLKEM512, /* ML-KEM-512, PQC L1 (~AES-128) */ - NID_MLKEM768, /* ML-KEM-768, PQC L3 (~AES-192) */ - NID_MLKEM1024, /* ML-KEM-1024, PQC L5 (~AES-256) */ - NID_X25519MLKEM768, /* X25519 + ML-KEM-768, PQC L3 */ - NID_X448MLKEM1024, /* X448 + ML-KEM-1024, PQC L5 */ + NID_MLKEM512, /* ML-KEM-512, PQC L1 (~AES-128) */ + NID_MLKEM768, /* ML-KEM-768, PQC L3 (~AES-192) */ + NID_MLKEM1024, /* ML-KEM-1024, PQC L5 (~AES-256) */ + NID_SecP256r1MLKEM768, /* P-256 + ML-KEM-768, PQC L3 */ + NID_X25519MLKEM768, /* X25519 + ML-KEM-768, PQC L3 */ + NID_SecP384r1MLKEM1024, /* P-384 + ML-KEM-1024, PQC L5 */ + NID_X448MLKEM1024, /* X448 + ML-KEM-1024, PQC L5 */ #endif #endif NID_undef diff --git a/src/lib/tests/kex_test_ml_kem.c b/src/lib/tests/kex_test_ml_kem.c index 7521c5d5..3059946c 100644 --- a/src/lib/tests/kex_test_ml_kem.c +++ b/src/lib/tests/kex_test_ml_kem.c @@ -267,6 +267,7 @@ static int test_kex_kem_corrupted_ciphertext(const char * algo) uint8_t s1[SYMMKEYSZ]; uint8_t s2[SYMMKEYSZ]; int kdf; + int ret; TEST_START("(%s)", algo); @@ -301,14 +302,15 @@ static int test_kex_kem_corrupted_ciphertext(const char * algo) buf2[0] ^= 0xFF; buf2[ct_len - 1] ^= 0xFF; - /* ML-KEM uses implicit rejection */ - if (kex_kem_decap(pkp, ct, kdf, s2) < 0) { + /* EC hybrids may reject the corrupted point explicitly */ + ret = kex_kem_decap(pkp, ct, kdf, s2); + if (strstr(algo, "SecP") == NULL && ret < 0) { printf("Decapsulation failed unexpectedly.\n"); goto fail_pkp; } - /* The shared secrets should NOT match with corrupted CT */ - if (memcmp(s1, s2, SYMMKEYSZ) == 0) { + /* Corrupted CT must never yield the original secret */ + if (ret == 0 && memcmp(s1, s2, SYMMKEYSZ) == 0) { printf("Corrupted ciphertext produced same secret.\n"); goto fail_pkp; } -- cgit v1.2.3