diff options
Diffstat (limited to 'kexdh.c')
-rw-r--r-- | kexdh.c | 203 |
1 files changed, 154 insertions, 49 deletions
@@ -1,6 +1,6 @@ -/* $OpenBSD: kexdh.c,v 1.26 2016/05/02 10:26:04 djm Exp $ */ +/* $OpenBSD: kexdh.c,v 1.32 2019/01/21 10:40:11 djm Exp $ */ /* - * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2019 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,67 +30,172 @@ #include <sys/types.h> #include <signal.h> - -#include <openssl/evp.h> +#include <stdio.h> +#include <string.h> #include "openbsd-compat/openssl-compat.h" +#include <openssl/dh.h> -#include "ssh2.h" #include "sshkey.h" -#include "cipher.h" #include "kex.h" -#include "ssherr.h" #include "sshbuf.h" #include "digest.h" +#include "ssherr.h" +#include "dh.h" int -kex_dh_hash( - int hash_alg, - const char *client_version_string, - const char *server_version_string, - const u_char *ckexinit, size_t ckexinitlen, - const u_char *skexinit, size_t skexinitlen, - const u_char *serverhostkeyblob, size_t sbloblen, - const BIGNUM *client_dh_pub, - const BIGNUM *server_dh_pub, - const BIGNUM *shared_secret, - u_char *hash, size_t *hashlen) +kex_dh_keygen(struct kex *kex) { - struct sshbuf *b; - int r; - - if (*hashlen < ssh_digest_bytes(hash_alg)) + switch (kex->kex_type) { + case KEX_DH_GRP1_SHA1: + kex->dh = dh_new_group1(); + break; + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: + kex->dh = dh_new_group14(); + break; + case KEX_DH_GRP16_SHA512: + kex->dh = dh_new_group16(); + break; + case KEX_DH_GRP18_SHA512: + kex->dh = dh_new_group18(); + break; + default: return SSH_ERR_INVALID_ARGUMENT; - if ((b = sshbuf_new()) == NULL) - return SSH_ERR_ALLOC_FAIL; - if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || - (r = sshbuf_put_cstring(b, server_version_string)) != 0 || - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - (r = sshbuf_put_u32(b, ckexinitlen+1)) != 0 || - (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || - (r = sshbuf_put(b, ckexinit, ckexinitlen)) != 0 || - (r = sshbuf_put_u32(b, skexinitlen+1)) != 0 || - (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || - (r = sshbuf_put(b, skexinit, skexinitlen)) != 0 || - (r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) != 0 || - (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || - (r = sshbuf_put_bignum2(b, shared_secret)) != 0) { - sshbuf_free(b); - return r; } -#ifdef DEBUG_KEX - sshbuf_dump(b, stderr); + if (kex->dh == NULL) + return SSH_ERR_ALLOC_FAIL; + return (dh_gen_key(kex->dh, kex->we_need * 8)); +} + +int +kex_dh_compute_key(struct kex *kex, BIGNUM *dh_pub, struct sshbuf *out) +{ + BIGNUM *shared_secret = NULL; + u_char *kbuf = NULL; + size_t klen = 0; + int kout, r; + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_pub= "); + BN_print_fp(stderr, dh_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_pub)); + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "\n"); #endif - if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { - sshbuf_free(b); - return SSH_ERR_LIBCRYPTO_ERROR; + + if (!dh_pub_is_valid(kex->dh, dh_pub)) { + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - sshbuf_free(b); - *hashlen = ssh_digest_bytes(hash_alg); -#ifdef DEBUG_KEX - dump_digest("hash", hash, *hashlen); +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); #endif - return 0; + r = sshbuf_put_bignum2(out, shared_secret); + out: + freezero(kbuf, klen); + BN_clear_free(shared_secret); + return r; +} + +int +kex_dh_keypair(struct kex *kex) +{ + const BIGNUM *pub_key; + struct sshbuf *buf = NULL; + int r; + + if ((r = kex_dh_keygen(kex)) != 0) + return r; + DH_get0_key(kex->dh, &pub_key, NULL); + if ((buf = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_bignum2(buf, pub_key)) != 0 || + (r = sshbuf_get_u32(buf, NULL)) != 0) + goto out; +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, pub_key); + fprintf(stderr, "\n"); +#endif + kex->client_pub = buf; + buf = NULL; + out: + sshbuf_free(buf); + return r; +} + +int +kex_dh_enc(struct kex *kex, const struct sshbuf *client_blob, + struct sshbuf **server_blobp, struct sshbuf **shared_secretp) +{ + const BIGNUM *pub_key; + struct sshbuf *server_blob = NULL; + int r; + + *server_blobp = NULL; + *shared_secretp = NULL; + + if ((r = kex_dh_keygen(kex)) != 0) + goto out; + DH_get0_key(kex->dh, &pub_key, NULL); + if ((server_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_bignum2(server_blob, pub_key)) != 0 || + (r = sshbuf_get_u32(server_blob, NULL)) != 0) + goto out; + if ((r = kex_dh_dec(kex, client_blob, shared_secretp)) != 0) + goto out; + *server_blobp = server_blob; + server_blob = NULL; + out: + DH_free(kex->dh); + kex->dh = NULL; + sshbuf_free(server_blob); + return r; +} + +int +kex_dh_dec(struct kex *kex, const struct sshbuf *dh_blob, + struct sshbuf **shared_secretp) +{ + struct sshbuf *buf = NULL; + BIGNUM *dh_pub = NULL; + int r; + + *shared_secretp = NULL; + + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_stringb(buf, dh_blob)) != 0 || + (r = sshbuf_get_bignum2(buf, &dh_pub)) != 0) + goto out; + sshbuf_reset(buf); + if ((r = kex_dh_compute_key(kex, dh_pub, buf)) != 0) + goto out; + *shared_secretp = buf; + buf = NULL; + out: + DH_free(kex->dh); + kex->dh = NULL; + sshbuf_free(buf); + return r; } #endif /* WITH_OPENSSL */ |