summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-08-29 12:19:59 +0200
committerSander Vrijders <sander@ouroboros.rocks>2026-08-31 08:31:46 +0200
commit11026b06dca0fc886d20e79eff0162b42fec3f8f (patch)
tree86962f65634a4ebe20822cf8c4ef659f5d0005a4 /src/lib
parent0719ed46d29b0e57cb9128f5396ff894b7456766 (diff)
downloadouroboros-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>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/crypt.c58
-rw-r--r--src/lib/tests/kex_test_ml_kem.c10
2 files changed, 37 insertions, 31 deletions
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;
}