Prototype KEX crypto interface and kexdhc changes based on this interface

This commit is contained in:
Manoj Ampalam 2015-11-26 23:34:36 -08:00
parent 66426d7369
commit 732f0444ff
6 changed files with 282 additions and 76 deletions

View File

@ -258,6 +258,7 @@
<ClCompile Include="$(OpenSSH-Src-Path)uuencode.c" />
<ClCompile Include="$(OpenSSH-Src-Path)verify.c" />
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c" />
<ClCompile Include="..\..\..\kexdh-openssl.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -288,5 +288,8 @@
<ClCompile Include="$(OpenSSH-Src-Path)xmalloc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\kexdh-openssl.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

28
kex.h
View File

@ -121,6 +121,26 @@ struct newkeys {
struct ssh;
//KEX DH interface start
struct sshbuf;
typedef struct kexdhi_
{
//consume p and g from "in"
int(*read_p_g)(struct kexdhi_*, struct sshbuf* in);
//encode p and g in "out"
int(*get_p_g)(struct kexdhi_*, struct sshbuf* out);
//encode public key in pub
int(*get_pub_key)(struct kexdhi_*, struct sshbuf* pub);
//read other public key from other_pub and encode secretagreement in secret
int(*get_secret)(struct kexdhi_*, struct sshbuf* other_pub, struct sshbuf* secret);
void(*done)(struct kexdhi_*);
}kexdhi;
kexdhi* kexdh_openssl_init(struct ssh* ssh);
//KEX DH interface end
struct kex {
u_char *session_id;
size_t session_id_len;
@ -150,6 +170,8 @@ struct kex {
u_char **, size_t *, const u_char *, size_t, u_int);
int (*kex[KEX_MAX])(struct ssh *);
/* kex specific state */
kexdhi* kexdh;
DH *dh; /* DH */
u_int min, max, nbits; /* GEX */
EC_KEY *ec_client_key; /* ECDH */
@ -191,6 +213,12 @@ int kex_dh_hash(const char *, const char *,
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *);
//version of kex_dh_hash with public keys and secret passed as sshbuf instead of BIGNUM
int kex_dh_hash_(
const char *, const char *, const u_char *, size_t , const u_char *, size_t ,
const u_char *, size_t , const struct sshbuf *, const struct sshbuf *, const struct sshbuf *,
u_char *hash, size_t *);
int kexgex_hash(int, const char *, const char *,
const u_char *, size_t, const u_char *, size_t, const u_char *, size_t,
int, int, int,

152
kexdh-openssl.c Normal file
View File

@ -0,0 +1,152 @@
#include "includes.h"
#ifdef WITH_OPENSSL
#include <openssl/dh.h>
#include <stdarg.h>
#include <stdio.h>
#include "kex.h"
#include "log.h"
#include "packet.h"
#include "dh.h"
#include "ssh2.h"
#include "dispatch.h"
#include "compat.h"
#include "ssherr.h"
#include "sshbuf.h"
typedef struct kexdh_openssl_
{
kexdhi dhi;
struct ssh* ssh;
DH* dh;
}kexdh_openssl;
static int kexdh_get_pub_key(kexdhi* dh_, struct sshbuf* pub)
{
int r = 0;
kexdh_openssl* dh = (kexdh_openssl*)dh_;
struct kex* kex = dh->ssh->kex;
/* generate and send 'e', client DH public key */
switch (kex->kex_type) {
case KEX_DH_GRP1_SHA1:
dh->dh = dh_new_group1();
break;
case KEX_DH_GRP14_SHA1:
dh->dh = dh_new_group14();
break;
default:
r = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
if ((r = dh_gen_key(dh->dh, kex->we_need * 8)) != 0 ||
(r = sshpkt_put_bignum2(pub, dh->dh->pub_key)) != 0)
goto out;
#ifdef DEBUG_KEXDH
DHparams_print_fp(stderr, dh->dh);
fprintf(stderr, "pub= ");
BN_print_fp(stderr, dh->dh->pub_key);
fprintf(stderr, "\n");
#endif
return 0;
out:
return r;
}
static int kexdh_get_secret(kexdhi* dh_, struct sshbuf* other_pub, struct sshbuf* secret)
{
int r = 0, kout;
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
kexdh_openssl* dh = (kexdh_openssl*)dh_;
struct kex* kex = dh->ssh->kex;
size_t klen = 0;
u_char *kbuf = NULL;
/* DH parameter f, server public DH key */
if ((dh_server_pub = BN_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_get_bignum2(other_pub, dh_server_pub)) != 0) {
goto out;
}
#ifdef DEBUG_KEXDH
fprintf(stderr, "dh_server_pub= ");
BN_print_fp(stderr, dh_server_pub);
fprintf(stderr, "\n");
debug("bits %d", BN_num_bits(dh_server_pub));
#endif
if (!dh_pub_is_valid(dh->dh, dh_server_pub)) {
sshpkt_disconnect(dh->ssh, "bad server public DH value");
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_server_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
if ((r == sshbuf_put_bignum2(secret, shared_secret)) != 0) {
goto out;
}
out:
if (dh_server_pub)
BN_clear_free(dh_server_pub);
if (kbuf) {
explicit_bzero(kbuf, klen);
free(kbuf);
}
if (shared_secret)
BN_clear_free(shared_secret);
return r;
}
static void kexdh_done(kexdhi* dh_)
{
kexdh_openssl* dh = (kexdh_openssl*)dh_;
if (dh->dh)
DH_free(dh->dh);
free(dh_);
}
kexdhi* kexdh_openssl_init(struct ssh* ssh)
{
kexdh_openssl *dh = malloc(sizeof(kexdh_openssl));
if (dh)
{
dh->ssh = ssh;
dh->dhi.get_pub_key = kexdh_get_pub_key;
dh->dhi.get_secret = kexdh_get_secret;
dh->dhi.done = kexdh_done;
dh->dhi.get_p_g = NULL;
dh->dhi.read_p_g = NULL;
}
return dh;
}
#endif

48
kexdh.c
View File

@ -90,4 +90,52 @@ kex_dh_hash(
#endif
return 0;
}
kex_dh_hash_(
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 struct sshbuf *client_dh_pub,
const struct sshbuf *server_dh_pub,
const struct sshbuf *shared_secret,
u_char *hash, size_t *hashlen)
{
struct sshbuf *b;
int r;
if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1))
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_b(b, client_dh_pub)) != 0 ||
(r = sshbuf_put_b(b, server_dh_pub)) != 0 ||
(r = sshbuf_put_b(b, shared_secret)) != 0) {
sshbuf_free(b);
return r;
}
#ifdef DEBUG_KEX
sshbuf_dump(b, stderr);
#endif
if (ssh_digest_buffer(SSH_DIGEST_SHA1, b, hash, *hashlen) != 0) {
sshbuf_free(b);
return SSH_ERR_LIBCRYPTO_ERROR;
}
sshbuf_free(b);
*hashlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
#ifdef DEBUG_KEX
dump_digest("hash", hash, *hashlen);
#endif
return 0;
}
#endif /* WITH_OPENSSL */

126
kexdhc.c
View File

@ -56,39 +56,29 @@ kexdh_client(struct ssh *ssh)
{
struct kex *kex = ssh->kex;
int r;
struct sshbuf *client_pub = NULL;
kex->kexdh = kexdh_openssl_init(ssh);
/* generate and send 'e', client DH public key */
switch (kex->kex_type) {
case KEX_DH_GRP1_SHA1:
kex->dh = dh_new_group1();
break;
case KEX_DH_GRP14_SHA1:
kex->dh = dh_new_group14();
break;
default:
r = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
if (kex->dh == NULL) {
if (kex->kexdh == NULL ||
(client_pub = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
debug("sending SSH2_MSG_KEXDH_INIT");
if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
(r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
(r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 ||
if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
(r = kex->kexdh->get_pub_key(kex->kexdh, client_pub)) != 0 ||
(r = sshpkt_putb(ssh, client_pub)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
goto out;
#ifdef DEBUG_KEXDH
DHparams_print_fp(stderr, kex->dh);
fprintf(stderr, "pub= ");
BN_print_fp(stderr, kex->dh->pub_key);
fprintf(stderr, "\n");
#endif
debug("expecting SSH2_MSG_KEXDH_REPLY");
ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh);
r = 0;
out:
out:
if (client_pub)
sshbuf_free(client_pub);
return r;
}
@ -97,12 +87,14 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
{
struct ssh *ssh = ctxt;
struct kex *kex = ssh->kex;
BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
struct sshkey *server_host_key = NULL;
u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL;
u_char *server_host_key_blob = NULL, *signature = NULL;
u_char hash[SSH_DIGEST_MAX_LENGTH];
size_t klen = 0, slen, sbloblen, hashlen;
int kout, r;
size_t slen, sbloblen, hashlen;
const u_char* server_pub_ptr = NULL;
size_t server_pub_len = 0;
struct sshbuf *server_pub = NULL, *client_pub = NULL, *secret = NULL;
int r;
if (kex->verify_host_key == NULL) {
r = SSH_ERR_INVALID_ARGUMENT;
@ -110,13 +102,13 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
}
/* key, cert */
if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
&sbloblen)) != 0 ||
(r = sshkey_from_blob(server_host_key_blob, sbloblen,
&server_host_key)) != 0)
&sbloblen)) != 0 ||
(r = sshkey_from_blob(server_host_key_blob, sbloblen,
&server_host_key)) != 0)
goto out;
if (server_host_key->type != kex->hostkey_type ||
(kex->hostkey_type == KEY_ECDSA &&
server_host_key->ecdsa_nid != kex->hostkey_nid)) {
(kex->hostkey_type == KEY_ECDSA &&
server_host_key->ecdsa_nid != kex->hostkey_nid)) {
r = SSH_ERR_KEY_TYPE_MISMATCH;
goto out;
}
@ -124,54 +116,38 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
/* DH parameter f, server public DH key */
if ((dh_server_pub = BN_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
/* signed H */
if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
(r = sshpkt_get_end(ssh)) != 0)
if ((r = sshpkt_get_string_direct(ssh, &server_pub_ptr, &server_pub_len)) != 0 ||
(r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
(r = sshpkt_get_end(ssh)) != 0)
goto out;
#ifdef DEBUG_KEXDH
fprintf(stderr, "dh_server_pub= ");
BN_print_fp(stderr, dh_server_pub);
fprintf(stderr, "\n");
debug("bits %d", BN_num_bits(dh_server_pub));
#endif
if (!dh_pub_is_valid(kex->dh, dh_server_pub)) {
sshpkt_disconnect(ssh, "bad server public DH value");
r = SSH_ERR_MESSAGE_INCOMPLETE;
goto out;
}
klen = DH_size(kex->dh);
if ((kbuf = malloc(klen)) == NULL ||
(shared_secret = BN_new()) == NULL) {
if ((server_pub = sshbuf_from(server_pub_ptr - 4, server_pub_len + 4)) == NULL ||
(secret = sshbuf_new()) == NULL ||
(client_pub = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 ||
BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
r = SSH_ERR_LIBCRYPTO_ERROR;
if ((r = kex->kexdh->get_secret(kex->kexdh, server_pub, secret)) != 0 ||
(r = kex->kexdh->get_pub_key(kex->kexdh, client_pub)) != 0)
{
goto out;
}
#ifdef DEBUG_KEXDH
dump_digest("shared secret", kbuf, kout);
#endif
/* calc and verify H */
hashlen = sizeof(hash);
if ((r = kex_dh_hash(
if ((r = kex_dh_hash_(
kex->client_version_string,
kex->server_version_string,
sshbuf_ptr(kex->my), sshbuf_len(kex->my),
sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
server_host_key_blob, sbloblen,
kex->dh->pub_key,
dh_server_pub,
shared_secret,
client_pub,
server_pub,
secret,
hash, &hashlen)) != 0)
goto out;
@ -190,20 +166,18 @@ input_kex_dh(int type, u_int32_t seq, void *ctxt)
memcpy(kex->session_id, hash, kex->session_id_len);
}
if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
if ((r = kex_derive_keys(ssh, hash, hashlen, secret)) == 0)
r = kex_send_newkeys(ssh);
out:
explicit_bzero(hash, sizeof(hash));
DH_free(kex->dh);
kex->dh = NULL;
if (dh_server_pub)
BN_clear_free(dh_server_pub);
if (kbuf) {
explicit_bzero(kbuf, klen);
free(kbuf);
}
if (shared_secret)
BN_clear_free(shared_secret);
if (client_pub)
sshbuf_free(client_pub);
if (server_pub)
sshbuf_free(server_pub);
if (secret)
sshbuf_free(secret);
kex->kexdh->done(kex->kexdh);
kex->kexdh = NULL;
sshkey_free(server_host_key);
free(server_host_key_blob);
free(signature);