From 7551857d0862a3c170fd84459b3bb1ef0e06b31e Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sun, 17 Mar 2019 15:45:24 -0400 Subject: [PATCH] dh: Move DH key handling into a separate file. In preparation for adding crypto-libraries specific backends. Signed-off-by: Simo Sorce Reviewed-by: Anderson Toshiyuki Sasaki Reviewed-by: Andreas Schneider --- include/libssh/dh.h | 38 +++-- src/CMakeLists.txt | 3 + src/dh.c | 387 +++----------------------------------------- src/dh_key.c | 378 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+), 380 deletions(-) create mode 100644 src/dh_key.c diff --git a/include/libssh/dh.h b/include/libssh/dh.h index fbfce85d..0d6720ee 100644 --- a/include/libssh/dh.h +++ b/include/libssh/dh.h @@ -30,14 +30,30 @@ struct dh_ctx; #define DH_CLIENT_KEYPAIR 0 #define DH_SERVER_KEYPAIR 1 -int ssh_dh_init(void); -void ssh_dh_finalize(void); +/* functions implemented by crypto backends */ +int ssh_dh_init_common(struct ssh_crypto_struct *crypto); +void ssh_dh_cleanup(struct ssh_crypto_struct *crypto); -int ssh_dh_import_next_pubkey_blob(ssh_session session, ssh_string pubkey_blob); +int ssh_dh_get_parameters(struct dh_ctx *ctx, + const_bignum *modulus, const_bignum *generator); +int ssh_dh_set_parameters(struct dh_ctx *ctx, + const bignum modulus, const bignum generator); + +int ssh_dh_keypair_gen_keys(struct dh_ctx *ctx, int peer); +int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer, + const_bignum *priv, const_bignum *pub); +int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer, + const bignum priv, const bignum pub); int ssh_dh_compute_shared_secret(struct dh_ctx *ctx, int local, int remote, bignum *dest); -int ssh_client_dh_init(ssh_session session); + +/* common functions */ +int ssh_dh_init(void); +void ssh_dh_finalize(void); + +int ssh_dh_import_next_pubkey_blob(ssh_session session, + ssh_string pubkey_blob); ssh_key ssh_dh_get_current_server_publickey(ssh_session session); int ssh_dh_get_current_server_publickey_blob(ssh_session session, @@ -46,22 +62,10 @@ ssh_key ssh_dh_get_next_server_publickey(ssh_session session); int ssh_dh_get_next_server_publickey_blob(ssh_session session, ssh_string *pubkey_blob); +int ssh_client_dh_init(ssh_session session); #ifdef WITH_SERVER void ssh_server_dh_init(ssh_session session); #endif /* WITH_SERVER */ - -int ssh_dh_init_common(struct ssh_crypto_struct *crypto); -void ssh_dh_cleanup(struct ssh_crypto_struct *crypto); int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet); -int ssh_dh_get_parameters(struct dh_ctx *ctx, - const_bignum *modulus, const_bignum *generator); -int ssh_dh_set_parameters(struct dh_ctx *ctx, - bignum modulus, bignum generator); -int ssh_dh_keypair_gen_keys(struct dh_ctx *ctx, int peer); -int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer, - const_bignum *priv, const_bignum *pub); -int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer, - bignum priv, bignum pub); - #endif /* DH_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ec995db0..f37ac91e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -199,6 +199,7 @@ if (WITH_GCRYPT) gcrypt_missing.c pki_gcrypt.c ecdh_gcrypt.c + dh_key.c ) elseif (WITH_MBEDTLS) set(libssh_SRCS @@ -208,6 +209,7 @@ elseif (WITH_MBEDTLS) mbedcrypto_missing.c pki_mbedcrypto.c ecdh_mbedcrypto.c + dh_key.c ) else (WITH_GCRYPT) set(libssh_SRCS @@ -216,6 +218,7 @@ else (WITH_GCRYPT) pki_crypto.c ecdh_crypto.c libcrypto.c + dh_key.c ) if(OPENSSL_VERSION VERSION_LESS "1.1.0") set(libssh_SRCS ${libssh_SRCS} libcrypto-compat.c) diff --git a/src/dh.c b/src/dh.c index 704af184..bf52b498 100644 --- a/src/dh.c +++ b/src/dh.c @@ -49,7 +49,6 @@ static unsigned char p_group1_value[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #define P_GROUP1_LEN 128 /* Size in bytes of the p number */ - static unsigned char p_group14_value[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, @@ -212,32 +211,14 @@ static unsigned char p_group18_value[] = { 0xFF, 0xFF, 0xFF, 0xFF}; #define P_GROUP18_LEN 1024 /* Size in bytes of the p number for group 18 */ -/* - * How many bits of security we want for fast DH. DH private key size must be - * twice that size. - */ -#define DH_SECURITY_BITS 512 -static unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */ -static bignum g; -static bignum p_group1; -static bignum p_group14; -static bignum p_group16; -static bignum p_group18; +bignum ssh_dh_generator; +bignum ssh_dh_group1; +bignum ssh_dh_group14; +bignum ssh_dh_group16; +bignum ssh_dh_group18; static int dh_crypto_initialized; -struct dh_keypair { - bignum priv_key; - bignum pub_key; -}; - -struct dh_ctx { - /* 0 is client, 1 is server */ - struct dh_keypair keypair[2]; - bignum generator; - bignum modulus; -}; - /** * @internal * @brief Initialize global constants used in DH key agreement @@ -245,45 +226,42 @@ struct dh_ctx { */ int ssh_dh_init(void) { + unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */ int rc; if (dh_crypto_initialized) { return SSH_OK; } + dh_crypto_initialized = 1; - g = bignum_new(); - if (g == NULL) { + ssh_dh_generator = bignum_new(); + if (ssh_dh_generator == NULL) { goto error; } - rc = bignum_set_word(g, g_int); + rc = bignum_set_word(ssh_dh_generator, g_int); if (rc != 1) { goto error; } - bignum_bin2bn(p_group1_value, P_GROUP1_LEN, &p_group1); - if (p_group1 == NULL) { + bignum_bin2bn(p_group1_value, P_GROUP1_LEN, &ssh_dh_group1); + if (ssh_dh_group1 == NULL) { goto error; } - bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14); - if (p_group14 == NULL) { + bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &ssh_dh_group14); + if (ssh_dh_group14 == NULL) { goto error; } - bignum_bin2bn(p_group16_value, P_GROUP16_LEN, &p_group16); - if (p_group16 == NULL) { + bignum_bin2bn(p_group16_value, P_GROUP16_LEN, &ssh_dh_group16); + if (ssh_dh_group16 == NULL) { goto error; } - bignum_bin2bn(p_group18_value, P_GROUP18_LEN, &p_group18); - if (p_group18 == NULL) { + bignum_bin2bn(p_group18_value, P_GROUP18_LEN, &ssh_dh_group18); + if (ssh_dh_group18 == NULL) { goto error; } - dh_crypto_initialized = 1; - return 0; error: - bignum_safe_free(g); - bignum_safe_free(p_group1); - bignum_safe_free(p_group14); - bignum_safe_free(p_group16); + ssh_dh_finalize(); return SSH_ERROR; } @@ -297,218 +275,15 @@ void ssh_dh_finalize(void) return; } - bignum_safe_free(g); - bignum_safe_free(p_group1); - bignum_safe_free(p_group14); - bignum_safe_free(p_group16); - bignum_safe_free(p_group18); + bignum_safe_free(ssh_dh_generator); + bignum_safe_free(ssh_dh_group1); + bignum_safe_free(ssh_dh_group14); + bignum_safe_free(ssh_dh_group16); + bignum_safe_free(ssh_dh_group18); dh_crypto_initialized = 0; } -static void ssh_dh_free_modulus(struct dh_ctx *ctx) -{ - if ((ctx->modulus != p_group1) && - (ctx->modulus != p_group14) && - (ctx->modulus != p_group16) && - (ctx->modulus != p_group18)) { - bignum_safe_free(ctx->modulus); - } - ctx->modulus = NULL; -} - -static void ssh_dh_free_generator(struct dh_ctx *ctx) -{ - if (ctx->generator != g) { - bignum_safe_free(ctx->generator); - } -} - -static void ssh_dh_free_dh_keypair(struct dh_keypair *keypair) -{ - bignum_safe_free(keypair->priv_key); - bignum_safe_free(keypair->pub_key); -} - -static int ssh_dh_init_dh_keypair(struct dh_keypair *keypair) -{ - int rc; - - keypair->priv_key = bignum_new(); - if (keypair->priv_key == NULL) { - rc = SSH_ERROR; - goto done; - } - keypair->pub_key = bignum_new(); - if (keypair->pub_key == NULL) { - rc = SSH_ERROR; - goto done; - } - - rc = SSH_OK; -done: - if (rc != SSH_OK) { - ssh_dh_free_dh_keypair(keypair); - } - return rc; -} - -int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer, - const_bignum *priv, const_bignum *pub) -{ - if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) || - ((priv == NULL) && (pub == NULL)) || (ctx == NULL)) { - return SSH_ERROR; - } - - if (priv) { - /* check that we have something in it */ - if (bignum_num_bits(ctx->keypair[peer].priv_key)) { - *priv = ctx->keypair[peer].priv_key; - } else { - return SSH_ERROR; - } - } - - if (pub) { - /* check that we have something in it */ - if (bignum_num_bits(ctx->keypair[peer].pub_key)) { - *pub = ctx->keypair[peer].pub_key; - } else { - return SSH_ERROR; - } - } - - return SSH_OK; -} - -int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer, - bignum priv, bignum pub) -{ - if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) || - ((priv == NULL) && (pub == NULL)) || (ctx == NULL)) { - return SSH_ERROR; - } - - if (priv) { - bignum_safe_free(ctx->keypair[peer].priv_key); - ctx->keypair[peer].priv_key = priv; - } - if (pub) { - bignum_safe_free(ctx->keypair[peer].pub_key); - ctx->keypair[peer].pub_key = pub; - } - return SSH_OK; -} - -int ssh_dh_get_parameters(struct dh_ctx *ctx, - const_bignum *modulus, const_bignum *generator) -{ - if (ctx == NULL) { - return SSH_ERROR; - } - if (modulus) { - *modulus = ctx->modulus; - } - if (generator) { - *generator = ctx->generator; - } - - return SSH_OK; -} - -int ssh_dh_set_parameters(struct dh_ctx *ctx, - bignum modulus, bignum generator) -{ - int rc; - - if ((ctx == NULL) || ((modulus == NULL) && (generator == NULL))) { - return SSH_ERROR; - } - /* when setting modulus or generator, - * make sure to invalidate existing keys */ - ssh_dh_free_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); - ssh_dh_free_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); - - rc = ssh_dh_init_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); - if (rc != SSH_OK) { - goto done; - } - rc = ssh_dh_init_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); - if (rc != SSH_OK) { - goto done; - } - - if (modulus) { - ssh_dh_free_modulus(ctx); - ctx->modulus = modulus; - } - if (generator) { - ssh_dh_free_generator(ctx); - ctx->generator = generator; - } - -done: - return rc; -} - -/** - * @internal - * @brief allocate and initialize ephemeral values used in dh kex - */ -int ssh_dh_init_common(struct ssh_crypto_struct *crypto) -{ - struct dh_ctx *ctx = NULL; - int rc; - - ctx = calloc(1, sizeof(*ctx)); - if (ctx == NULL) { - return SSH_ERROR; - } - - switch (crypto->kex_type) { - case SSH_KEX_DH_GROUP1_SHA1: - rc = ssh_dh_set_parameters(ctx, p_group1, g); - break; - case SSH_KEX_DH_GROUP14_SHA1: - rc = ssh_dh_set_parameters(ctx, p_group14, g); - break; - case SSH_KEX_DH_GROUP16_SHA512: - rc = ssh_dh_set_parameters(ctx, p_group16, g); - break; - case SSH_KEX_DH_GROUP18_SHA512: - rc = ssh_dh_set_parameters(ctx, p_group18, g); - break; - default: - rc = SSH_OK; - break; - } - - crypto->dh_ctx = ctx; - - if (rc != SSH_OK) { - ssh_dh_cleanup(crypto); - } - return rc; -} - -void ssh_dh_cleanup(struct ssh_crypto_struct *crypto) -{ - struct dh_ctx *ctx = crypto->dh_ctx; - - if (ctx == NULL) { - return; - } - - ssh_dh_free_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); - ssh_dh_free_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); - - ssh_dh_free_modulus(ctx); - ssh_dh_free_generator(ctx); - free(ctx); - crypto->dh_ctx = NULL; -} - int ssh_dh_import_next_pubkey_blob(ssh_session session, ssh_string pubkey_blob) { return ssh_pki_import_pubkey_blob(pubkey_blob, @@ -516,120 +291,6 @@ int ssh_dh_import_next_pubkey_blob(ssh_session session, ssh_string pubkey_blob) } -#ifdef DEBUG_CRYPTO -static void ssh_dh_debug(ssh_session session) -{ - struct ssh_crypto_struct *crypto = session->next_crypto; - const_bignum x, y, e, f; - ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e); - ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR, &y, &f); - ssh_print_bignum("p", crypto->dh_ctx->modulus); - ssh_print_bignum("g", crypto->dh_ctx->generator); - ssh_print_bignum("x", x); - ssh_print_bignum("y", y); - ssh_print_bignum("e", e); - ssh_print_bignum("f", f); - - ssh_print_hexa("Session server cookie", - session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", - session->next_crypto->client_kex.cookie, 16); - ssh_print_bignum("k", session->next_crypto->shared_secret); -} -#else -#define ssh_dh_debug(session) -#endif - -/** @internal - * @brief generates a secret DH parameter of at least DH_SECURITY_BITS - * security as well as the corresponding public key. - * @param[out] parms a dh_kex paramters structure with preallocated bignum - * where to store the parameters - * @return SSH_OK on success, SSH_ERROR on error - */ -int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer) -{ - bignum tmp = NULL; - bignum_CTX ctx = NULL; - int rc = 0; - int bits = 0; - int p_bits = 0; - - ctx = bignum_ctx_new(); - if (bignum_ctx_invalid(ctx)){ - goto error; - } - tmp = bignum_new(); - if (tmp == NULL) { - goto error; - } - p_bits = bignum_num_bits(dh_ctx->modulus); - /* we need at most DH_SECURITY_BITS */ - bits = MIN(DH_SECURITY_BITS * 2, p_bits); - /* ensure we're not too close of p so rnd()%p stays uniform */ - if (bits <= p_bits && bits + 64 > p_bits) { - bits += 64; - } - rc = bignum_rand(tmp, bits); - if (rc != 1) { - goto error; - } - rc = bignum_mod(dh_ctx->keypair[peer].priv_key, tmp, dh_ctx->modulus, ctx); - if (rc != 1) { - goto error; - } - /* Now compute the corresponding public key */ - rc = bignum_mod_exp(dh_ctx->keypair[peer].pub_key, dh_ctx->generator, - dh_ctx->keypair[peer].priv_key, dh_ctx->modulus, ctx); - if (rc != 1) { - goto error; - } - bignum_safe_free(tmp); - bignum_ctx_free(ctx); - return SSH_OK; -error: - bignum_safe_free(tmp); - bignum_ctx_free(ctx); - return SSH_ERROR; -} - -/** @internal - * @brief generates a shared secret between the local peer and the remote peer - * @param[in] local peer identifier - * @param[in] remote peer identifier - * @param[out] dest a preallocated bignum where to store parameter - * @return SSH_OK on success, SSH_ERROR on error - */ -int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote, - bignum *dest) -{ - int rc; - bignum_CTX ctx = bignum_ctx_new(); - if (bignum_ctx_invalid(ctx)) { - return -1; - } - - if (*dest == NULL) { - *dest = bignum_new(); - if (*dest == NULL) { - rc = 0; - goto done; - } - } - - rc = bignum_mod_exp(*dest, dh_ctx->keypair[remote].pub_key, - dh_ctx->keypair[local].priv_key, - dh_ctx->modulus, ctx); - -done: - bignum_ctx_free(ctx); - ssh_dh_debug(session); - if (rc != 1) { - return SSH_ERROR; - } - return SSH_OK; -} - static SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply); static ssh_packet_callback dh_client_callbacks[]= { diff --git a/src/dh_key.c b/src/dh_key.c new file mode 100644 index 00000000..cc69dfe7 --- /dev/null +++ b/src/dh_key.c @@ -0,0 +1,378 @@ +/* + * dh-int.c - Diffie-Helman algorithm code against SSH 2 + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2018 by Aris Adamantiadis + * Copyright (c) 2009-2013 by Andreas Schneider + * Copyright (c) 2012 by Dmitriy Kuznetsov + * Copyright (c) 2019 by Simo Sorce + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include "libssh/priv.h" +#include "libssh/crypto.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/dh.h" +#include "libssh/ssh2.h" +#include "libssh/pki.h" +#include "libssh/bignum.h" + +extern bignum ssh_dh_generator; +extern bignum ssh_dh_group1; +extern bignum ssh_dh_group14; +extern bignum ssh_dh_group16; +extern bignum ssh_dh_group18; + +/* + * How many bits of security we want for fast DH. DH private key size must be + * twice that size. + */ +#define DH_SECURITY_BITS 512 + +struct dh_keypair { + bignum priv_key; + bignum pub_key; +}; + +struct dh_ctx { + /* 0 is client, 1 is server */ + struct dh_keypair keypair[2]; + bignum generator; + bignum modulus; +}; + +static void ssh_dh_free_modulus(struct dh_ctx *ctx) +{ + if ((ctx->modulus != ssh_dh_group1) && + (ctx->modulus != ssh_dh_group14) && + (ctx->modulus != ssh_dh_group16) && + (ctx->modulus != ssh_dh_group18)) { + bignum_safe_free(ctx->modulus); + } + ctx->modulus = NULL; +} + +static void ssh_dh_free_generator(struct dh_ctx *ctx) +{ + if (ctx->generator != ssh_dh_generator) { + bignum_safe_free(ctx->generator); + } +} + +static void ssh_dh_free_dh_keypair(struct dh_keypair *keypair) +{ + bignum_safe_free(keypair->priv_key); + bignum_safe_free(keypair->pub_key); +} + +static int ssh_dh_init_dh_keypair(struct dh_keypair *keypair) +{ + int rc; + + keypair->priv_key = bignum_new(); + if (keypair->priv_key == NULL) { + rc = SSH_ERROR; + goto done; + } + keypair->pub_key = bignum_new(); + if (keypair->pub_key == NULL) { + rc = SSH_ERROR; + goto done; + } + + rc = SSH_OK; +done: + if (rc != SSH_OK) { + ssh_dh_free_dh_keypair(keypair); + } + return rc; +} + +int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer, + const_bignum *priv, const_bignum *pub) +{ + if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) || + ((priv == NULL) && (pub == NULL)) || (ctx == NULL)) { + return SSH_ERROR; + } + + if (priv) { + /* check that we have something in it */ + if (bignum_num_bits(ctx->keypair[peer].priv_key)) { + *priv = ctx->keypair[peer].priv_key; + } else { + return SSH_ERROR; + } + } + + if (pub) { + /* check that we have something in it */ + if (bignum_num_bits(ctx->keypair[peer].pub_key)) { + *pub = ctx->keypair[peer].pub_key; + } else { + return SSH_ERROR; + } + } + + return SSH_OK; +} + +int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer, + bignum priv, bignum pub) +{ + if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) || + ((priv == NULL) && (pub == NULL)) || (ctx == NULL)) { + return SSH_ERROR; + } + + if (priv) { + bignum_safe_free(ctx->keypair[peer].priv_key); + ctx->keypair[peer].priv_key = priv; + } + if (pub) { + bignum_safe_free(ctx->keypair[peer].pub_key); + ctx->keypair[peer].pub_key = pub; + } + return SSH_OK; +} + +int ssh_dh_get_parameters(struct dh_ctx *ctx, + const_bignum *modulus, const_bignum *generator) +{ + if (ctx == NULL) { + return SSH_ERROR; + } + if (modulus) { + *modulus = ctx->modulus; + } + if (generator) { + *generator = ctx->generator; + } + + return SSH_OK; +} + +int ssh_dh_set_parameters(struct dh_ctx *ctx, + bignum modulus, bignum generator) +{ + int rc; + + if ((ctx == NULL) || ((modulus == NULL) && (generator == NULL))) { + return SSH_ERROR; + } + /* when setting modulus or generator, + * make sure to invalidate existing keys */ + ssh_dh_free_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); + ssh_dh_free_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); + + rc = ssh_dh_init_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); + if (rc != SSH_OK) { + goto done; + } + rc = ssh_dh_init_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); + if (rc != SSH_OK) { + goto done; + } + + if (modulus) { + ssh_dh_free_modulus(ctx); + ctx->modulus = modulus; + } + if (generator) { + ssh_dh_free_generator(ctx); + ctx->generator = generator; + } + +done: + return rc; +} + +/** + * @internal + * @brief allocate and initialize ephemeral values used in dh kex + */ +int ssh_dh_init_common(struct ssh_crypto_struct *crypto) +{ + struct dh_ctx *ctx = NULL; + int rc; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return SSH_ERROR; + } + + switch (crypto->kex_type) { + case SSH_KEX_DH_GROUP1_SHA1: + rc = ssh_dh_set_parameters(ctx, ssh_dh_group1, ssh_dh_generator); + break; + case SSH_KEX_DH_GROUP14_SHA1: + rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator); + break; + case SSH_KEX_DH_GROUP16_SHA512: + rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator); + break; + case SSH_KEX_DH_GROUP18_SHA512: + rc = ssh_dh_set_parameters(ctx, ssh_dh_group18, ssh_dh_generator); + break; + default: + rc = SSH_OK; + break; + } + + crypto->dh_ctx = ctx; + + if (rc != SSH_OK) { + ssh_dh_cleanup(crypto); + } + return rc; +} + +void ssh_dh_cleanup(struct ssh_crypto_struct *crypto) +{ + struct dh_ctx *ctx = crypto->dh_ctx; + + if (ctx == NULL) { + return; + } + + ssh_dh_free_dh_keypair(&ctx->keypair[DH_CLIENT_KEYPAIR]); + ssh_dh_free_dh_keypair(&ctx->keypair[DH_SERVER_KEYPAIR]); + + ssh_dh_free_modulus(ctx); + ssh_dh_free_generator(ctx); + free(ctx); + crypto->dh_ctx = NULL; +} + +#ifdef DEBUG_CRYPTO +static void ssh_dh_debug(ssh_session session) +{ + struct ssh_crypto_struct *crypto = session->next_crypto; + const_bignum x, y, e, f; + ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e); + ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR, &y, &f); + ssh_print_bignum("p", crypto->dh_ctx->modulus); + ssh_print_bignum("g", crypto->dh_ctx->generator); + ssh_print_bignum("x", x); + ssh_print_bignum("y", y); + ssh_print_bignum("e", e); + ssh_print_bignum("f", f); + + ssh_print_hexa("Session server cookie", + session->next_crypto->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", + session->next_crypto->client_kex.cookie, 16); + ssh_print_bignum("k", session->next_crypto->shared_secret); +} +#else +#define ssh_dh_debug(session) +#endif + +/** @internal + * @brief generates a secret DH parameter of at least DH_SECURITY_BITS + * security as well as the corresponding public key. + * @param[out] parms a dh_kex paramters structure with preallocated bignum + * where to store the parameters + * @return SSH_OK on success, SSH_ERROR on error + */ +int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer) +{ + bignum tmp = NULL; + bignum_CTX ctx = NULL; + int rc = 0; + int bits = 0; + int p_bits = 0; + + ctx = bignum_ctx_new(); + if (bignum_ctx_invalid(ctx)){ + goto error; + } + tmp = bignum_new(); + if (tmp == NULL) { + goto error; + } + p_bits = bignum_num_bits(dh_ctx->modulus); + /* we need at most DH_SECURITY_BITS */ + bits = MIN(DH_SECURITY_BITS * 2, p_bits); + /* ensure we're not too close of p so rnd()%p stays uniform */ + if (bits <= p_bits && bits + 64 > p_bits) { + bits += 64; + } + rc = bignum_rand(tmp, bits); + if (rc != 1) { + goto error; + } + rc = bignum_mod(dh_ctx->keypair[peer].priv_key, tmp, dh_ctx->modulus, ctx); + if (rc != 1) { + goto error; + } + /* Now compute the corresponding public key */ + rc = bignum_mod_exp(dh_ctx->keypair[peer].pub_key, dh_ctx->generator, + dh_ctx->keypair[peer].priv_key, dh_ctx->modulus, ctx); + if (rc != 1) { + goto error; + } + bignum_safe_free(tmp); + bignum_ctx_free(ctx); + return SSH_OK; +error: + bignum_safe_free(tmp); + bignum_ctx_free(ctx); + return SSH_ERROR; +} + +/** @internal + * @brief generates a shared secret between the local peer and the remote peer + * @param[in] local peer identifier + * @param[in] remote peer identifier + * @param[out] dest a preallocated bignum where to store parameter + * @return SSH_OK on success, SSH_ERROR on error + */ +int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote, + bignum *dest) +{ + int rc; + bignum_CTX ctx = bignum_ctx_new(); + if (bignum_ctx_invalid(ctx)) { + return -1; + } + + if (*dest == NULL) { + *dest = bignum_new(); + if (*dest == NULL) { + rc = 0; + goto done; + } + } + + rc = bignum_mod_exp(*dest, dh_ctx->keypair[remote].pub_key, + dh_ctx->keypair[local].priv_key, + dh_ctx->modulus, ctx); + +done: + bignum_ctx_free(ctx); + ssh_dh_debug(session); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; +}