diff options
| author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2026-08-29 12:19:59 +0200 |
|---|---|---|
| committer | Sander Vrijders <sander@ouroboros.rocks> | 2026-08-31 08:31:46 +0200 |
| commit | 11026b06dca0fc886d20e79eff0162b42fec3f8f (patch) | |
| tree | 86962f65634a4ebe20822cf8c4ef659f5d0005a4 | |
| parent | 0719ed46d29b0e57cb9128f5396ff894b7456766 (diff) | |
| download | ouroboros-11026b06dca0fc886d20e79eff0162b42fec3f8f.tar.gz ouroboros-11026b06dca0fc886d20e79eff0162b42fec3f8f.zip | |
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 <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
| -rw-r--r-- | include/ouroboros/crypt.h | 4 | ||||
| -rw-r--r-- | sec.conf.in | 6 | ||||
| -rw-r--r-- | src/lib/crypt.c | 58 | ||||
| -rw-r--r-- | src/lib/tests/kex_test_ml_kem.c | 10 |
4 files changed, 44 insertions, 34 deletions
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h index dd0c2853..b567bf8d 100644 --- a/include/ouroboros/crypt.h +++ b/include/ouroboros/crypt.h @@ -69,8 +69,10 @@ /* Hybrid KEM NIDs: project-assigned range, OpenSSL defines none */ #define NID_X25519MLKEM768 2053 #define NID_X448MLKEM1024 2054 +#define NID_SecP256r1MLKEM768 2055 +#define NID_SecP384r1MLKEM1024 2056 #define NID_HYBRID_KEM_MIN NID_X25519MLKEM768 -#define NID_HYBRID_KEM_MAX NID_X448MLKEM1024 +#define NID_HYBRID_KEM_MAX NID_SecP384r1MLKEM1024 /* KDF NIDs (match OpenSSL values) */ #define NID_hkdf 1036 diff --git a/sec.conf.in b/sec.conf.in index 4115c850..efd07878 100644 --- a/sec.conf.in +++ b/sec.conf.in @@ -46,8 +46,10 @@ # ML-KEM-1024 CRYSTALS-Kyber-1024 # # Hybrid KEMs: -# X25519MLKEM768 X25519 + ML-KEM-768 -# X448MLKEM1024 X448 + ML-KEM-1024 +# X25519MLKEM768 X25519 + ML-KEM-768 +# X448MLKEM1024 X448 + ML-KEM-1024 +# SecP256r1MLKEM768 NIST P-256 + ML-KEM-768 +# SecP384r1MLKEM1024 NIST P-384 + ML-KEM-1024 # # Supported cipher algorithms (cipher=): # -------------------------------------- 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; } |
