Changeset View
Changeset View
Standalone View
Standalone View
kexdh.c
/* $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 | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
Show All 13 Lines | |||||
#include "includes.h" | #include "includes.h" | ||||
#ifdef WITH_OPENSSL | #ifdef WITH_OPENSSL | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <openssl/evp.h> | |||||
#include "openbsd-compat/openssl-compat.h" | #include "openbsd-compat/openssl-compat.h" | ||||
#include <openssl/dh.h> | |||||
#include "ssh2.h" | |||||
#include "sshkey.h" | #include "sshkey.h" | ||||
#include "cipher.h" | |||||
#include "kex.h" | #include "kex.h" | ||||
#include "ssherr.h" | |||||
#include "sshbuf.h" | #include "sshbuf.h" | ||||
#include "digest.h" | #include "digest.h" | ||||
#include "ssherr.h" | |||||
#include "dh.h" | |||||
int | int | ||||
kex_dh_hash( | kex_dh_keygen(struct kex *kex) | ||||
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) | |||||
{ | { | ||||
struct sshbuf *b; | switch (kex->kex_type) { | ||||
int r; | case KEX_DH_GRP1_SHA1: | ||||
kex->dh = dh_new_group1(); | |||||
if (*hashlen < ssh_digest_bytes(hash_alg)) | 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; | return SSH_ERR_INVALID_ARGUMENT; | ||||
if ((b = sshbuf_new()) == NULL) | } | ||||
if (kex->dh == NULL) | |||||
return SSH_ERR_ALLOC_FAIL; | return SSH_ERR_ALLOC_FAIL; | ||||
if ((r = sshbuf_put_cstring(b, client_version_string)) != 0 || | return (dh_gen_key(kex->dh, kex->we_need * 8)); | ||||
(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); | 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 | #endif | ||||
if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { | |||||
sshbuf_free(b); | if (!dh_pub_is_valid(kex->dh, dh_pub)) { | ||||
return SSH_ERR_LIBCRYPTO_ERROR; | r = SSH_ERR_MESSAGE_INCOMPLETE; | ||||
goto out; | |||||
} | } | ||||
sshbuf_free(b); | klen = DH_size(kex->dh); | ||||
*hashlen = ssh_digest_bytes(hash_alg); | if ((kbuf = malloc(klen)) == NULL || | ||||
#ifdef DEBUG_KEX | (shared_secret = BN_new()) == NULL) { | ||||
dump_digest("hash", hash, *hashlen); | 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; | |||||
} | |||||
#ifdef DEBUG_KEXDH | |||||
dump_digest("shared secret", kbuf, kout); | |||||
#endif | #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 */ | #endif /* WITH_OPENSSL */ |