From d85326a119c34789055c388fcd18bb0161fbfd21 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sat, 14 Feb 2026 14:33:50 +0100 Subject: irmd: Add strength-based crypto negotiation Each side's configured cipher, KDF, and KEX algorithm now represents a minimum security floor ("at least this strong"). Cipher and KDF use strongest-wins: the server compares ranks and selects the stronger of client vs server config. The negotiated values are sent in the response header. The client verifies the server's response meets its own minimum, which prevents downgrade attacks on the wire. KEX uses a minimum-floor check: the server extracts the client's algorithm from its public key and rejects if it ranks below the server's configured algorithm. A server configured with ML-KEM will reject all classical algorithms. Special case: for client-encap KEM, the client has already derived its key using its KDF, so the server must use the same KDF and can only reject if it is too weak. The supported_nids arrays are ordered weakest to strongest and serve as the single source of truth for ranking. Cipher ranking (weakest to strongest): aes-128-ctr, aes-192-ctr, aes-256-ctr, aes-128-gcm, aes-192-gcm, aes-256-gcm, chacha20-poly1305 KDF ranking (weakest to strongest): blake2s256, sha256, sha3-256, sha384, sha3-384, blake2b512, sha512, sha3-512 KEX ranking (weakest to strongest): ffdhe2048, prime256v1, X25519, ffdhe3072, secp384r1, ffdhe4096, X448, secp521r1, ML-KEM-512, ML-KEM-768, ML-KEM-1024, X25519MLKEM768, X448MLKEM1024 Negotiation outcomes: strong srv cipher + weak cli cipher -> use strongest weak srv cipher + strong cli cipher -> use strongest srv encryption + cli none -> server rejects srv none + cli encryption -> use client's strong srv KEX + weak cli KEX -> server rejects weak srv KEX + strong cli KEX -> succeeds wire tamper to weaker cipher -> client rejects Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- enc.conf.in | 4 +- include/ouroboros/crypt.h | 6 + src/irmd/oap/cli.c | 29 ++++- src/irmd/oap/internal.h | 15 --- src/irmd/oap/srv.c | 111 ++++++++++++------ src/irmd/oap/tests/oap_test.c | 262 +++++++++++++++++++++++++++++++++++++++--- src/lib/crypt.c | 70 +++++++++-- src/lib/crypt/openssl.c | 2 +- 8 files changed, 417 insertions(+), 82 deletions(-) diff --git a/enc.conf.in b/enc.conf.in index 64502fbb..8f91d717 100644 --- a/enc.conf.in +++ b/enc.conf.in @@ -73,8 +73,8 @@ # sha3-256 SHA3-256 # sha3-384 SHA3-384 # sha3-512 SHA3-512 -# blake2b512 BLAKE2b-512 (requires OpenSSL 1.1.0+) -# blake2s256 BLAKE2s-256 (requires OpenSSL 1.1.0+) +# blake2b512 BLAKE2b-512 +# blake2s256 BLAKE2s-256 # # KEM Mode (kem_mode=): # --------------------- diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h index 5b39fb5f..1f2640e8 100644 --- a/include/ouroboros/crypt.h +++ b/include/ouroboros/crypt.h @@ -276,6 +276,12 @@ const char * crypt_nid_to_str(uint16_t nid); uint16_t crypt_str_to_nid(const char * cipher); +int crypt_cipher_rank(int nid); + +int crypt_kdf_rank(int nid); + +int crypt_kex_rank(int nid); + int md_validate_nid(int nid); const char * md_nid_to_str(uint16_t nid); diff --git a/src/irmd/oap/cli.c b/src/irmd/oap/cli.c index ea2a25d1..9472e331 100644 --- a/src/irmd/oap/cli.c +++ b/src/irmd/oap/cli.c @@ -439,10 +439,16 @@ static int do_client_kex_complete(struct oap_cli_ctx * s, { struct sec_config * kcfg = &s->kcfg; uint8_t * id = s->id.data; + int cipher_nid; + int kdf_nid; if (!IS_KEX_ALGO_SET(kcfg)) return 0; + /* Save client's configured minimums */ + cipher_nid = kcfg->c.nid; + kdf_nid = kcfg->k.nid; + /* Accept server's cipher choice */ if (peer_hdr->cipher_str == NULL) { log_err_id(id, "Server did not provide cipher."); @@ -456,7 +462,28 @@ static int do_client_kex_complete(struct oap_cli_ctx * s, return -ENOTSUP; } - log_dbg_id(id, "Accepted server cipher %s.", peer_hdr->cipher_str); + /* Verify server cipher >= client's minimum */ + if (crypt_cipher_rank(kcfg->c.nid) < crypt_cipher_rank(cipher_nid)) { + log_err_id(id, "Server cipher %s too weak.", + peer_hdr->cipher_str); + return -ECRYPT; + } + + log_dbg_id(id, "Accepted server cipher %s.", + peer_hdr->cipher_str); + + /* Accept server's KDF for non-client-encap modes */ + if (kcfg->x.mode != KEM_MODE_CLIENT_ENCAP + && peer_hdr->kdf_nid != NID_undef) { + if (crypt_kdf_rank(peer_hdr->kdf_nid) + < crypt_kdf_rank(kdf_nid)) { + log_err_id(id, "Server KDF too weak."); + return -ECRYPT; + } + SET_KEX_KDF_NID(kcfg, peer_hdr->kdf_nid); + log_dbg_id(id, "Accepted server KDF %s.", + md_nid_to_str(kcfg->k.nid)); + } /* Derive shared secret */ if (IS_KEM_ALGORITHM(kcfg->x.str)) diff --git a/src/irmd/oap/internal.h b/src/irmd/oap/internal.h index 8363e3a2..f118cc33 100644 --- a/src/irmd/oap/internal.h +++ b/src/irmd/oap/internal.h @@ -34,24 +34,15 @@ #include #include -/* - * Authentication functions (auth.c) - */ int oap_check_hdr(const struct oap_hdr * hdr); int oap_auth_peer(char * name, const struct oap_hdr * local_hdr, const struct oap_hdr * peer_hdr); -/* - * Key exchange functions (kex.c) - */ int oap_negotiate_cipher(const struct oap_hdr * peer_hdr, struct sec_config * kcfg); -/* - * Credential loading (oap.c) - shared between client and server - */ #ifndef OAP_TEST_MODE int load_credentials(const char * name, const struct name_sec_paths * paths, @@ -63,9 +54,6 @@ int load_kex_config(const char * name, struct sec_config * cfg); #endif -/* - * Server functions (srv.c) - */ #ifndef OAP_TEST_MODE int load_srv_credentials(const struct name_info * info, void ** pkp, @@ -94,9 +82,6 @@ int do_server_kex(const struct name_info * info, buffer_t * kex, struct crypt_sk * sk); -/* - * Client functions (cli.c) - */ #ifndef OAP_TEST_MODE int load_cli_credentials(const struct name_info * info, void ** pkp, diff --git a/src/irmd/oap/srv.c b/src/irmd/oap/srv.c index c5a4453f..93270c48 100644 --- a/src/irmd/oap/srv.c +++ b/src/irmd/oap/srv.c @@ -134,43 +134,70 @@ static int get_algo_from_peer_key(const struct oap_hdr * peer_hdr, return 0; } -static int negotiate_kex(const struct oap_hdr * peer_hdr, - struct sec_config * kcfg) +static int negotiate_cipher(const struct oap_hdr * peer_hdr, + struct sec_config * kcfg) { uint8_t * id = peer_hdr->id.data; + int cli_nid; + int cli_rank; + int srv_rank; + + /* Cipher: select the strongest of client and server */ + cli_nid = peer_hdr->cipher_str != NULL + ? (int) crypt_str_to_nid(peer_hdr->cipher_str) + : NID_undef; + + if (cli_nid != NID_undef + && crypt_cipher_rank(cli_nid) < 0) { + log_err_id(id, "Unsupported cipher '%s'.", + peer_hdr->cipher_str); + return -ENOTSUP; + } - if (kcfg->c.nid == NID_undef) { - if (peer_hdr->cipher_str != NULL) { - SET_KEX_CIPHER(kcfg, peer_hdr->cipher_str); - if (kcfg->c.nid == NID_undef) { - log_err_id(id, "Unsupported cipher '%s'.", - peer_hdr->cipher_str); - return -ENOTSUP; - } - log_dbg_id(id, "Peer requested cipher %s.", - peer_hdr->cipher_str); - } else { - log_err_id(id, "Encryption requested, no cipher."); - return -ECRYPT; - } + cli_rank = crypt_cipher_rank(cli_nid); + srv_rank = crypt_cipher_rank(kcfg->c.nid); + + if (cli_rank > srv_rank) { + SET_KEX_CIPHER_NID(kcfg, cli_nid); + log_dbg_id(id, "Selected client cipher %s.", + kcfg->c.str); + } else if (srv_rank > 0) { + log_dbg_id(id, "Selected server cipher %s.", + kcfg->c.str); } else { - log_dbg_id(id, "Using local cipher %s.", kcfg->c.str); + log_err_id(id, "Encryption requested, no cipher."); + return -ECRYPT; } - /* Negotiate KDF - server overrides client if configured */ - if (kcfg->k.nid != NID_undef) { - log_dbg_id(id, "Using local KDF %s.", - md_nid_to_str(kcfg->k.nid)); - } else if (peer_hdr->kdf_nid != NID_undef) { - if (md_validate_nid(peer_hdr->kdf_nid) == 0) { - kcfg->k.nid = peer_hdr->kdf_nid; - log_dbg_id(id, "Using peer KDF %s.", - md_nid_to_str(peer_hdr->kdf_nid)); - } else { - log_err_id(id, "Unsupported KDF NID %d.", - peer_hdr->kdf_nid); - return -ENOTSUP; + /* KDF: select the strongest of client and server */ + if (peer_hdr->kdf_nid != NID_undef + && crypt_kdf_rank(peer_hdr->kdf_nid) < 0) { + log_err_id(id, "Unsupported KDF NID %d.", + peer_hdr->kdf_nid); + return -ENOTSUP; + } + + cli_rank = crypt_kdf_rank(peer_hdr->kdf_nid); + srv_rank = crypt_kdf_rank(kcfg->k.nid); + + /* + * For client-encap KEM, the KDF is baked into + * the ciphertext. The server must use the client's + * KDF and can only verify the minimum. + */ + if (OAP_KEX_ROLE(peer_hdr) == KEM_MODE_CLIENT_ENCAP) { + if (srv_rank > cli_rank) { + log_err_id(id, "Client KDF too weak."); + return -ECRYPT; } + SET_KEX_KDF_NID(kcfg, peer_hdr->kdf_nid); + } else if (cli_rank > srv_rank) { + SET_KEX_KDF_NID(kcfg, peer_hdr->kdf_nid); + log_dbg_id(id, "Selected client KDF %s.", + md_nid_to_str(kcfg->k.nid)); + } else if (srv_rank > 0) { + log_dbg_id(id, "Selected server KDF %s.", + md_nid_to_str(kcfg->k.nid)); } if (IS_KEX_ALGO_SET(kcfg)) @@ -305,6 +332,7 @@ int do_server_kex(const struct name_info * info, struct crypt_sk * sk) { char algo_buf[KEX_ALGO_BUFSZ]; + int srv_kex_nid; uint8_t * id; id = peer_hdr->id.data; @@ -318,15 +346,26 @@ int do_server_kex(const struct name_info * info, return 0; } - if (negotiate_kex(peer_hdr, kcfg) < 0) + if (negotiate_cipher(peer_hdr, kcfg) < 0) return -ECRYPT; + /* Save server's configured KEX before overwriting */ + srv_kex_nid = kcfg->x.nid; + if (OAP_KEX_ROLE(peer_hdr) != KEM_MODE_CLIENT_ENCAP) { /* Server encapsulation or DHE: extract algo from DER PK */ if (get_algo_from_peer_key(peer_hdr, algo_buf) < 0) return -ECRYPT; SET_KEX_ALGO(kcfg, algo_buf); + + /* Reject if client KEX is weaker than server's */ + if (crypt_kex_rank(kcfg->x.nid) + < crypt_kex_rank(srv_kex_nid)) { + log_err_id(id, "Client KEX %s too weak.", + kcfg->x.str); + return -ECRYPT; + } } /* Dispatch based on algorithm type */ @@ -368,13 +407,11 @@ int oap_srv_process(const struct name_info * info, log_dbg("Processing OAP request for %s.", info->name); - /* Load server credentials */ if (load_srv_credentials(info, &pkp, &crt) < 0) { log_err("Failed to load security keys for %s.", info->name); goto fail_cred; } - /* Load KEX config */ if (load_srv_kex_config(info, &kcfg) < 0) { log_err("Failed to load KEX config for %s.", info->name); goto fail_kex; @@ -392,13 +429,11 @@ int oap_srv_process(const struct name_info * info, id = peer_hdr.id.data; /* Logging */ - /* Check for replay */ if (oap_check_hdr(&peer_hdr) < 0) { log_err_id(id, "OAP header failed replay check."); goto fail_auth; } - /* Authenticate client before processing KEX data */ oap_hdr_init(&local_hdr, peer_hdr.id, kex_buf, *data, NID_undef); if (oap_auth_peer(cli_name, &local_hdr, &peer_hdr) < 0) { @@ -409,11 +444,15 @@ int oap_srv_process(const struct name_info * info, if (do_server_kex(info, &peer_hdr, &kcfg, &local_hdr.kex, sk) < 0) goto fail_kex; + /* Update cipher NID after negotiation */ + sk->nid = kcfg.c.nid; + /* Build response header with hash of client request */ local_hdr.nid = sk->nid; /* Use client's md_nid, defaulting to SHA-384 for PQC */ - req_md_nid = peer_hdr.md_nid != NID_undef ? peer_hdr.md_nid : NID_sha384; + req_md_nid = peer_hdr.md_nid != NID_undef ? + peer_hdr.md_nid : NID_sha384; /* Compute request hash using client's md_nid */ hash_ret = md_digest(req_md_nid, req_buf, hash_buf); diff --git a/src/irmd/oap/tests/oap_test.c b/src/irmd/oap/tests/oap_test.c index 70f0a248..fc78ed9a 100644 --- a/src/irmd/oap/tests/oap_test.c +++ b/src/irmd/oap/tests/oap_test.c @@ -536,7 +536,7 @@ static int test_oap_roundtrip_all(void) return ret; } -/* Cipher negotiation - client should accept server's chosen cipher */ +/* Cipher negotiation - strongest cipher and KDF are selected */ static int test_oap_cipher_mismatch(void) { struct oap_test_ctx ctx; @@ -545,17 +545,17 @@ static int test_oap_cipher_mismatch(void) memset(&test_cfg, 0, sizeof(test_cfg)); - /* Server: ChaCha20-Poly1305, SHA3-256, SHA-384 */ + /* Server: AES-128-GCM, SHA-256 */ test_cfg.srv.kex = NID_X25519; - test_cfg.srv.cipher = NID_chacha20_poly1305; - test_cfg.srv.kdf = NID_sha3_256; - test_cfg.srv.md = NID_sha384; + test_cfg.srv.cipher = NID_aes_128_gcm; + test_cfg.srv.kdf = NID_sha256; + test_cfg.srv.md = NID_sha256; test_cfg.srv.auth = AUTH; - /* Client: AES-256-GCM, SHA-256, SHA-256 */ + /* Client: AES-256-GCM, SHA-512 */ test_cfg.cli.kex = NID_X25519; test_cfg.cli.cipher = NID_aes_256_gcm; - test_cfg.cli.kdf = NID_sha256; + test_cfg.cli.kdf = NID_sha512; test_cfg.cli.md = NID_sha256; test_cfg.cli.auth = NO_AUTH; @@ -577,17 +577,17 @@ static int test_oap_cipher_mismatch(void) goto fail_cleanup; } - /* Verify: both should have the server's chosen cipher and KDF */ - if (ctx.srv.nid != test_cfg.srv.cipher) { + /* Verify: both should have the strongest cipher */ + if (ctx.srv.nid != NID_aes_256_gcm) { printf("Server cipher mismatch: expected %s, got %s\n", - crypt_nid_to_str(test_cfg.srv.cipher), + crypt_nid_to_str(NID_aes_256_gcm), crypt_nid_to_str(ctx.srv.nid)); goto fail_cleanup; } - if (ctx.cli.nid != test_cfg.srv.cipher) { + if (ctx.cli.nid != NID_aes_256_gcm) { printf("Client cipher mismatch: expected %s, got %s\n", - crypt_nid_to_str(test_cfg.srv.cipher), + crypt_nid_to_str(NID_aes_256_gcm), crypt_nid_to_str(ctx.cli.nid)); goto fail_cleanup; } @@ -598,9 +598,9 @@ static int test_oap_cipher_mismatch(void) /* KDF NID at offset 26: ID(16) + ts(8) + cipher(2) */ resp_kdf_nid = ntoh16(*(uint16_t *)(ctx.resp_hdr.data + 26)); - if (resp_kdf_nid != test_cfg.srv.kdf) { + if (resp_kdf_nid != NID_sha512) { printf("Response KDF mismatch: expected %s, got %s\n", - md_nid_to_str(test_cfg.srv.kdf), + md_nid_to_str(NID_sha512), md_nid_to_str(resp_kdf_nid)); goto fail_cleanup; } @@ -618,6 +618,228 @@ static int test_oap_cipher_mismatch(void) return TEST_RC_FAIL; } +/* Server encryption, client none: server rejects (no KEX data) */ +static int test_oap_srv_enc_cli_none(void) +{ + struct oap_test_ctx ctx; + + TEST_START(); + + memset(&test_cfg, 0, sizeof(test_cfg)); + + /* Server: encryption configured */ + test_cfg.srv.kex = NID_X25519; + test_cfg.srv.cipher = NID_aes_256_gcm; + test_cfg.srv.kdf = NID_sha256; + test_cfg.srv.md = NID_sha256; + test_cfg.srv.auth = AUTH; + + /* Client: no encryption */ + test_cfg.cli.md = NID_sha256; + test_cfg.cli.auth = NO_AUTH; + + if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0) + goto fail; + + if (oap_cli_prepare_ctx(&ctx) < 0) { + printf("Client prepare failed.\n"); + goto fail_cleanup; + } + + /* Server should reject: KEX required but client sent none */ + if (oap_srv_process_ctx(&ctx) == 0) { + printf("Server should have rejected.\n"); + goto fail_cleanup; + } + + oap_test_teardown(&ctx); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + + fail_cleanup: + oap_test_teardown(&ctx); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +/* Client encryption, server none: use client settings */ +static int test_oap_cli_enc_srv_none(void) +{ + struct oap_test_ctx ctx; + + TEST_START(); + + memset(&test_cfg, 0, sizeof(test_cfg)); + + /* Server: no encryption configured */ + test_cfg.srv.md = NID_sha256; + test_cfg.srv.auth = AUTH; + + /* Client: encryption configured */ + test_cfg.cli.kex = NID_X25519; + test_cfg.cli.cipher = NID_aes_256_gcm; + test_cfg.cli.kdf = NID_sha256; + test_cfg.cli.md = NID_sha256; + test_cfg.cli.auth = NO_AUTH; + + if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0) + goto fail; + + if (oap_cli_prepare_ctx(&ctx) < 0) { + printf("Client prepare failed.\n"); + goto fail_cleanup; + } + + if (oap_srv_process_ctx(&ctx) < 0) { + printf("Server process failed.\n"); + goto fail_cleanup; + } + + if (oap_cli_complete_ctx(&ctx) < 0) { + printf("Client complete failed.\n"); + goto fail_cleanup; + } + + if (memcmp(ctx.cli.key, ctx.srv.key, SYMMKEYSZ) != 0) { + printf("Key mismatch.\n"); + goto fail_cleanup; + } + + if (ctx.cli.nid != NID_aes_256_gcm) { + printf("Expected %s, got %s.\n", + crypt_nid_to_str(NID_aes_256_gcm), + crypt_nid_to_str(ctx.cli.nid)); + goto fail_cleanup; + } + + if (ctx.srv.nid != NID_aes_256_gcm) { + printf("Expected %s, got %s.\n", + crypt_nid_to_str(NID_aes_256_gcm), + crypt_nid_to_str(ctx.srv.nid)); + goto fail_cleanup; + } + + oap_test_teardown(&ctx); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + + fail_cleanup: + oap_test_teardown(&ctx); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +/* Client rejects server response with downgraded cipher */ +static int test_oap_cli_rejects_downgrade(void) +{ + struct oap_test_ctx ctx; + uint16_t weak; + + TEST_START(); + + memset(&test_cfg, 0, sizeof(test_cfg)); + + test_cfg.srv.kex = NID_X25519; + test_cfg.srv.cipher = NID_aes_256_gcm; + test_cfg.srv.kdf = NID_sha256; + test_cfg.srv.md = NID_sha256; + test_cfg.srv.auth = AUTH; + + test_cfg.cli.kex = NID_X25519; + test_cfg.cli.cipher = NID_aes_256_gcm; + test_cfg.cli.kdf = NID_sha256; + test_cfg.cli.md = NID_sha256; + test_cfg.cli.auth = NO_AUTH; + + if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0) + goto fail; + + if (oap_cli_prepare_ctx(&ctx) < 0) { + printf("Client prepare failed.\n"); + goto fail_cleanup; + } + + if (oap_srv_process_ctx(&ctx) < 0) { + printf("Server process failed.\n"); + goto fail_cleanup; + } + + /* Tamper: replace cipher NID with weaker one */ + weak = hton16(NID_aes_128_ctr); + memcpy(ctx.resp_hdr.data + OAP_CIPHER_NID_OFFSET, + &weak, sizeof(weak)); + + /* Client should reject the downgraded cipher */ + if (oap_cli_complete_ctx(&ctx) == 0) { + printf("Client accepted downgrade.\n"); + goto fail_cleanup; + } + + oap_test_teardown(&ctx); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + + fail_cleanup: + oap_test_teardown(&ctx); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +/* Server rejects client with weaker KEX algorithm */ +static int test_oap_srv_rejects_weak_kex(void) +{ + struct oap_test_ctx ctx; + + TEST_START(); + + memset(&test_cfg, 0, sizeof(test_cfg)); + + /* Server: secp521r1 (strong) */ + test_cfg.srv.kex = NID_secp521r1; + test_cfg.srv.cipher = NID_aes_256_gcm; + test_cfg.srv.kdf = NID_sha256; + test_cfg.srv.md = NID_sha256; + test_cfg.srv.auth = AUTH; + + /* Client: ffdhe2048 (weakest) */ + test_cfg.cli.kex = NID_ffdhe2048; + test_cfg.cli.cipher = NID_aes_256_gcm; + test_cfg.cli.kdf = NID_sha256; + test_cfg.cli.md = NID_sha256; + test_cfg.cli.auth = NO_AUTH; + + if (oap_test_setup(&ctx, root_ca_crt_ec, im_ca_crt_ec) < 0) + goto fail; + + if (oap_cli_prepare_ctx(&ctx) < 0) { + printf("Client prepare failed.\n"); + goto fail_cleanup; + } + + /* Server should reject: client KEX too weak */ + if (oap_srv_process_ctx(&ctx) == 0) { + printf("Server should reject weak KEX.\n"); + goto fail_cleanup; + } + + oap_test_teardown(&ctx); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + + fail_cleanup: + oap_test_teardown(&ctx); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + /* Test roundtrip with different signature digest algorithms */ static int test_oap_roundtrip_md(int md) { @@ -682,8 +904,8 @@ static int test_oap_roundtrip_md_all(void) int ret = 0; int i; - /* Test with default (0) */ - ret |= test_oap_roundtrip_md(0); + /* Test with default */ + ret |= test_oap_roundtrip_md(NID_undef); /* Test with all supported digest NIDs */ for (i = 0; md_supported_nids[i] != NID_undef; i++) @@ -919,6 +1141,10 @@ int oap_test(int argc, ret |= test_oap_unsupported_nid(); ret |= test_oap_cipher_mismatch(); + ret |= test_oap_srv_enc_cli_none(); + ret |= test_oap_cli_enc_srv_none(); + ret |= test_oap_cli_rejects_downgrade(); + ret |= test_oap_srv_rejects_weak_kex(); ret |= test_oap_outdated_packet(); ret |= test_oap_future_packet(); @@ -940,6 +1166,10 @@ int oap_test(int argc, (void) test_oap_nid_without_kex; (void) test_oap_unsupported_nid; (void) test_oap_cipher_mismatch; + (void) test_oap_srv_enc_cli_none; + (void) test_oap_cli_enc_srv_none; + (void) test_oap_cli_rejects_downgrade; + (void) test_oap_srv_rejects_weak_kex; (void) test_oap_outdated_packet; (void) test_oap_future_packet; (void) test_oap_replay_packet; diff --git a/src/lib/crypt.c b/src/lib/crypt.c index 8c29cbb3..92da803d 100644 --- a/src/lib/crypt.c +++ b/src/lib/crypt.c @@ -56,15 +56,16 @@ static const struct nid_map cipher_nid_map[] = { {NID_undef, NULL} }; +/* Ordered in strength preference, lowest first */ const uint16_t crypt_supported_nids[] = { #ifdef HAVE_OPENSSL + NID_aes_128_ctr, + NID_aes_192_ctr, + NID_aes_256_ctr, NID_aes_128_gcm, NID_aes_192_gcm, NID_aes_256_gcm, NID_chacha20_poly1305, - NID_aes_128_ctr, - NID_aes_192_ctr, - NID_aes_256_ctr, #endif NID_undef }; @@ -86,16 +87,17 @@ static const struct nid_map kex_nid_map[] = { {NID_undef, NULL} }; +/* Ordered in strength preference, lowest first */ const uint16_t kex_supported_nids[] = { #ifdef HAVE_OPENSSL + NID_ffdhe2048, NID_X9_62_prime256v1, - NID_secp384r1, - NID_secp521r1, NID_X25519, - NID_X448, - NID_ffdhe2048, NID_ffdhe3072, + NID_secp384r1, NID_ffdhe4096, + NID_X448, + NID_secp521r1, #ifdef HAVE_OPENSSL_PQC NID_MLKEM512, NID_MLKEM768, @@ -119,16 +121,17 @@ static const struct nid_map md_nid_map[] = { {NID_undef, NULL} }; +/* Ordered in strength preference, lowest first */ const uint16_t md_supported_nids[] = { #ifdef HAVE_OPENSSL + NID_blake2s256, NID_sha256, - NID_sha384, - NID_sha512, NID_sha3_256, + NID_sha384, NID_sha3_384, - NID_sha3_512, NID_blake2b512, - NID_blake2s256, + NID_sha512, + NID_sha3_512, #endif NID_undef }; @@ -544,6 +547,51 @@ int md_validate_nid(int nid) return -ENOTSUP; } +int crypt_cipher_rank(int nid) +{ + int i; + + if (nid == NID_undef) + return 0; + + for (i = 0; crypt_supported_nids[i] != NID_undef; i++) { + if ((int) crypt_supported_nids[i] == nid) + return i + 1; + } + + return -1; +} + +int crypt_kdf_rank(int nid) +{ + int i; + + if (nid == NID_undef) + return 0; + + for (i = 0; md_supported_nids[i] != NID_undef; i++) { + if ((int) md_supported_nids[i] == nid) + return i + 1; + } + + return -1; +} + +int crypt_kex_rank(int nid) +{ + int i; + + if (nid == NID_undef) + return 0; + + for (i = 0; kex_supported_nids[i] != NID_undef; i++) { + if ((int) kex_supported_nids[i] == nid) + return i + 1; + } + + return -1; +} + /* Hash length now returned by md_digest() */ int crypt_encrypt(struct crypt_ctx * ctx, diff --git a/src/lib/crypt/openssl.c b/src/lib/crypt/openssl.c index 232aa6c9..5d95f408 100644 --- a/src/lib/crypt/openssl.c +++ b/src/lib/crypt/openssl.c @@ -974,7 +974,7 @@ int openssl_encrypt(struct ossl_crypt_ctx * ctx, if (random_buffer(iv, ctx->ivsz) < 0) goto fail_encrypt; - /* Set IV bit 7 to current key phase (bit KEY_ROTATION_BIT of counter) */ + /* Set IV bit 7 to current key phase (KEY_ROTATION_BIT of counter) */ if (ctx->rot.cntr & ctx->rot.mask) iv[0] |= 0x80; else -- cgit v1.2.3