From 6252aab88ae0616e112d7e59a4013e35ac7c42d4 Mon Sep 17 00:00:00 2001 From: Jon Simons Date: Thu, 24 Aug 2017 18:14:38 +0200 Subject: ecdh: enable ecdh_sha2_nistp{384,521} kex methods Summary: Based on Dirkjan's original patch series here: * https://www.libssh.org/archive/libssh/2015-08/0000029.html Here the changes are adapted for the current master branch, and expanded to include libgcrypt support. Co-Authored-By: Dirkjan Bussink Signed-off-by: Jon Simons Reviewed-by: Andreas Schneider Test Plan: * Ran pkd tests for libcrypto and libgcrypt builds. * Ran client torture_algorithms.c tests for libcrypto and libgcrypt builds. * Tested across multiple libgcrypts ("1.6.3" and "1.7.6-beta"). Reviewers: aris, asn Tags: #libssh Differential Revision: https://bugs.libssh.org/D7 --- src/client.c | 2 ++ src/dh.c | 26 +++++++++++++++++++++++- src/ecdh_crypto.c | 33 +++++++++++++++++++++++++++++-- src/ecdh_gcrypt.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++------ src/kex.c | 6 +++++- src/packet_cb.c | 2 ++ src/server.c | 2 ++ src/session.c | 4 ++++ 8 files changed, 124 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client.c b/src/client.c index 3b120bbc..11a00229 100644 --- a/src/client.c +++ b/src/client.c @@ -260,6 +260,8 @@ static int dh_handshake(ssh_session session) { break; #ifdef HAVE_ECDH case SSH_KEX_ECDH_SHA2_NISTP256: + case SSH_KEX_ECDH_SHA2_NISTP384: + case SSH_KEX_ECDH_SHA2_NISTP521: rc = ssh_client_ecdh_init(session); break; #endif diff --git a/src/dh.c b/src/dh.c index c54bb9f1..0339be02 100644 --- a/src/dh.c +++ b/src/dh.c @@ -608,7 +608,9 @@ int ssh_make_sessionid(ssh_session session) { } #ifdef HAVE_ECDH - } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { + } else if ((session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256) || + (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP384) || + (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP521)) { if (session->next_crypto->ecdh_client_pubkey == NULL || session->next_crypto->ecdh_server_pubkey == NULL) { SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing"); @@ -670,6 +672,28 @@ int ssh_make_sessionid(ssh_session session) { sha256(ssh_buffer_get(buf), ssh_buffer_get_len(buf), session->next_crypto->secret_hash); break; + case SSH_KEX_ECDH_SHA2_NISTP384: + session->next_crypto->digest_len = SHA384_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA384; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if (session->next_crypto->secret_hash == NULL) { + ssh_set_error_oom(session); + goto error; + } + sha384(ssh_buffer_get(buf), ssh_buffer_get_len(buf), + session->next_crypto->secret_hash); + break; + case SSH_KEX_ECDH_SHA2_NISTP521: + session->next_crypto->digest_len = SHA512_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA512; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if (session->next_crypto->secret_hash == NULL) { + ssh_set_error_oom(session); + goto error; + } + sha512(ssh_buffer_get(buf), ssh_buffer_get_len(buf), + session->next_crypto->secret_hash); + break; } /* During the first kex, secret hash and session ID are equal. However, after * a key re-exchange, a new secret hash is calculated. This hash will not replace diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c index e774e560..041e6c0e 100644 --- a/src/ecdh_crypto.c +++ b/src/ecdh_crypto.c @@ -35,6 +35,20 @@ #define NISTP384 NID_secp384r1 #define NISTP521 NID_secp521r1 +/** @internal + * @brief Map the given key exchange enum value to its curve name. + */ +static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) { + if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { + return NISTP256; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) { + return NISTP384; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) { + return NISTP521; + } + return SSH_ERROR; +} + /** @internal * @brief Starts ecdh-sha2-nistp256 key exchange */ @@ -43,6 +57,7 @@ int ssh_client_ecdh_init(ssh_session session){ const EC_GROUP *group; const EC_POINT *pubkey; ssh_string client_pubkey; + int curve; int len; int rc; bignum_CTX ctx = BN_CTX_new(); @@ -53,7 +68,13 @@ int ssh_client_ecdh_init(ssh_session session){ return SSH_ERROR; } - key = EC_KEY_new_by_curve_name(NISTP256); + curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type); + if (curve == SSH_ERROR) { + BN_CTX_free(ctx); + return SSH_ERROR; + } + + key = EC_KEY_new_by_curve_name(curve); if (key == NULL) { BN_CTX_free(ctx); return SSH_ERROR; @@ -185,6 +206,7 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ /* SSH host keys (rsa,dsa,ecdsa) */ ssh_key privkey; ssh_string sig_blob = NULL; + int curve; int len; int rc; @@ -199,7 +221,14 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ /* Build server's keypair */ ctx = BN_CTX_new(); - ecdh_key = EC_KEY_new_by_curve_name(NISTP256); + + curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type); + if (curve == SSH_ERROR) { + BN_CTX_free(ctx); + return SSH_ERROR; + } + + ecdh_key = EC_KEY_new_by_curve_name(curve); if (ecdh_key == NULL) { ssh_set_error_oom(session); BN_CTX_free(ctx); diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c index 08d45c9a..f7430800 100644 --- a/src/ecdh_gcrypt.c +++ b/src/ecdh_gcrypt.c @@ -34,7 +34,21 @@ #include /** @internal - * @brief Starts ecdh-sha2-nistp256 key exchange + * @brief Map the given key exchange enum value to its curve name. + */ +static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) { + if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { + return "NIST P-256"; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) { + return "NIST P-384"; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) { + return "NIST P-521"; + } + return NULL; +} + +/** @internal + * @brief Starts ecdh-sha2-nistp{256,384,521} key exchange. */ int ssh_client_ecdh_init(ssh_session session) { @@ -43,6 +57,13 @@ int ssh_client_ecdh_init(ssh_session session) ssh_string client_pubkey = NULL; gcry_sexp_t param = NULL; gcry_sexp_t key = NULL; + const char *curve = NULL; + + curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type); + if (curve == NULL) { + rc = SSH_ERROR; + goto out; + } rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); if (rc < 0) { @@ -53,7 +74,7 @@ int ssh_client_ecdh_init(ssh_session session) err = gcry_sexp_build(¶m, NULL, "(genkey(ecdh(curve %s)))", - "NIST P-256"); + curve); if (err) { rc = SSH_ERROR; goto out; @@ -105,12 +126,20 @@ int ecdh_build_k(ssh_session session) gcry_mpi_t s = NULL; gcry_mpi_point_t point; #else + size_t k_len = 0; + enum ssh_key_exchange_e kex_type = session->next_crypto->kex_type; ssh_string s; #endif ssh_string pubkey_raw; gcry_sexp_t pubkey = NULL; ssh_string privkey = NULL; int rc = SSH_ERROR; + const char *curve = NULL; + + curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type); + if (curve == NULL) { + goto out; + } pubkey_raw = session->server ? session->next_crypto->ecdh_client_pubkey @@ -119,7 +148,7 @@ int ecdh_build_k(ssh_session session) err = gcry_sexp_build(&pubkey, NULL, "(key-data(public-key(ecdh(curve %s)(q %b))))", - "NIST P-256", + curve, ssh_string_len(pubkey_raw), ssh_string_data(pubkey_raw)); if (err) { @@ -173,7 +202,19 @@ int ecdh_build_k(ssh_session session) goto out; } - if (ssh_string_len(s) != 65) { + if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { + k_len = 65; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) { + k_len = 97; + } else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) { + k_len = 133; + } else { + ssh_string_burn(s); + ssh_string_free(s); + goto out; + } + + if (ssh_string_len(s) != k_length) { ssh_string_burn(s); ssh_string_free(s); goto out; @@ -182,7 +223,7 @@ int ecdh_build_k(ssh_session session) err = gcry_mpi_scan(&session->next_crypto->k, GCRYMPI_FMT_USG, (const char *)ssh_string_data(s) + 1, - 32, + k_length / 2, NULL); ssh_string_burn(s); ssh_string_free(s); @@ -228,6 +269,12 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet) { ssh_key privkey; ssh_string sig_blob = NULL; int rc = SSH_ERROR; + const char *curve = NULL; + + curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type); + if (curve == NULL) { + goto out; + } /* Extract the client pubkey from the init packet */ q_c_string = ssh_buffer_get_ssh_string(packet); @@ -239,7 +286,7 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet) { /* Build server's keypair */ err = gcry_sexp_build(¶m, NULL, "(genkey(ecdh(curve %s)))", - "NIST P-256"); + curve); if (err) { goto out; } diff --git a/src/kex.c b/src/kex.c index f34728c7..2e7702b8 100644 --- a/src/kex.c +++ b/src/kex.c @@ -78,7 +78,7 @@ #endif #ifdef HAVE_ECDH -#define ECDH "ecdh-sha2-nistp256," +#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521," #define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss" #else #define HOSTKEYS "ssh-ed25519,ssh-rsa,ssh-dss" @@ -589,6 +589,10 @@ int ssh_kex_select_methods (ssh_session session){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp384") == 0){ + session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP384; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp521") == 0){ + session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP521; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){ session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; } diff --git a/src/packet_cb.c b/src/packet_cb.c index 472e8a73..106c5d9b 100644 --- a/src/packet_cb.c +++ b/src/packet_cb.c @@ -110,6 +110,8 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ break; #ifdef HAVE_ECDH case SSH_KEX_ECDH_SHA2_NISTP256: + case SSH_KEX_ECDH_SHA2_NISTP384: + case SSH_KEX_ECDH_SHA2_NISTP521: rc = ssh_client_ecdh_reply(session, packet); break; #endif diff --git a/src/server.c b/src/server.c index 3c1ee74c..25fdd0c5 100644 --- a/src/server.c +++ b/src/server.c @@ -198,6 +198,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ break; #ifdef HAVE_ECDH case SSH_KEX_ECDH_SHA2_NISTP256: + case SSH_KEX_ECDH_SHA2_NISTP384: + case SSH_KEX_ECDH_SHA2_NISTP521: rc = ssh_server_ecdh_init(session, packet); break; #endif diff --git a/src/session.c b/src/session.c index 01773c52..60c72b92 100644 --- a/src/session.c +++ b/src/session.c @@ -357,6 +357,10 @@ const char* ssh_get_kex_algo(ssh_session session) { return "diffie-hellman-group14-sha1"; case SSH_KEX_ECDH_SHA2_NISTP256: return "ecdh-sha2-nistp256"; + case SSH_KEX_ECDH_SHA2_NISTP384: + return "ecdh-sha2-nistp384"; + case SSH_KEX_ECDH_SHA2_NISTP521: + return "ecdh-sha2-nistp521"; case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: return "curve25519-sha256@libssh.org"; default: -- cgit v1.2.3