/* * 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; }