From b672f405dd2bfa3bdd7d11c1d616d674fe8d9d7a Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 31 Aug 2005 20:43:06 +0000 Subject: [PATCH] Add infrastructure for supporting multiple hashes in key exchange. Nothing very surprising here. git-svn-id: svn://svn.tartarus.org/sgt/putty@6251 cda61777-01e9-0310-a592-d414129be87e --- ssh.c | 128 ++++++++++++++++++++++++++++++++++----------------------------- ssh.h | 9 +++++ sshdh.c | 6 +-- sshsha.c | 32 ++++++++++++++++ 4 files changed, 114 insertions(+), 61 deletions(-) diff --git a/ssh.c b/ssh.c index 837bbfa3..22cea9fd 100644 --- a/ssh.c +++ b/ssh.c @@ -680,7 +680,7 @@ struct ssh_tag { /* the above field _must_ be first in the structure */ char *v_c, *v_s; - SHA_State exhash; + void *exhash; Socket s; @@ -706,6 +706,7 @@ struct ssh_tag { const struct ssh_kex *kex; const struct ssh_signkey *hostkey; unsigned char v2_session_id[20]; + int v2_session_id_len; void *kex_ctx; char *savedhost; @@ -1562,21 +1563,21 @@ static int ssh_versioncmp(char *a, char *b) /* * Utility routines for putting an SSH-protocol `string' and - * `uint32' into a SHA state. + * `uint32' into a hash state. */ -static void sha_string(SHA_State * s, void *str, int len) +static void hash_string(const struct ssh_hash *h, void *s, void *str, int len) { unsigned char lenblk[4]; PUT_32BIT(lenblk, len); - SHA_Bytes(s, lenblk, 4); - SHA_Bytes(s, str, len); + h->bytes(s, lenblk, 4); + h->bytes(s, str, len); } -static void sha_uint32(SHA_State * s, unsigned i) +static void hash_uint32(const struct ssh_hash *h, void *s, unsigned i) { unsigned char intblk[4]; PUT_32BIT(intblk, i); - SHA_Bytes(s, intblk, 4); + h->bytes(s, intblk, 4); } /* @@ -1972,12 +1973,12 @@ void bndebug(char *string, Bignum b) } #endif -static void sha_mpint(SHA_State * s, Bignum b) +static void hash_mpint(const struct ssh_hash *h, void *s, Bignum b) { unsigned char *p; int len; p = ssh2_mpint_fmt(b, &len); - sha_string(s, p, len); + hash_string(h, s, p, len); sfree(p); } @@ -4947,26 +4948,26 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen) /* * SSH-2 key creation method. */ -static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, - unsigned char *sessid, char chr, +static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, unsigned char *keyspace) { - SHA_State s; - /* First 20 bytes. */ - SHA_Init(&s); + const struct ssh_hash *h = ssh->kex->hash; + void *s; + /* First hlen bytes. */ + s = h->init(); if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) - sha_mpint(&s, K); - SHA_Bytes(&s, H, 20); - SHA_Bytes(&s, &chr, 1); - SHA_Bytes(&s, sessid, 20); - SHA_Final(&s, keyspace); - /* Next 20 bytes. */ - SHA_Init(&s); + hash_mpint(h, s, K); + h->bytes(s, H, h->hlen); + h->bytes(s, &chr, 1); + h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len); + h->final(s, keyspace); + /* Next hlen bytes. */ + s = h->init(); if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY)) - sha_mpint(&s, K); - SHA_Bytes(&s, H, 20); - SHA_Bytes(&s, keyspace, 20); - SHA_Final(&s, keyspace + 20); + hash_mpint(h, s, K); + h->bytes(s, H, h->hlen); + h->bytes(s, keyspace, h->hlen); + h->final(s, keyspace + h->hlen); } /* @@ -5425,14 +5426,15 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } - SHA_Init(&ssh->exhash); - sha_string(&ssh->exhash, ssh->v_c, strlen(ssh->v_c)); - sha_string(&ssh->exhash, ssh->v_s, strlen(ssh->v_s)); - sha_string(&ssh->exhash, s->our_kexinit, s->our_kexinitlen); + ssh->exhash = ssh->kex->hash->init(); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c)); + hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s)); + hash_string(ssh->kex->hash, ssh->exhash, + s->our_kexinit, s->our_kexinitlen); sfree(s->our_kexinit); if (pktin->length > 5) - sha_string(&ssh->exhash, pktin->data + 5, pktin->length - 5); - + hash_string(ssh->kex->hash, ssh->exhash, + pktin->data + 5, pktin->length - 5); if (s->ignorepkt) /* first_kex_packet_follows */ crWaitUntil(pktin); /* Ignore packet */ @@ -5450,10 +5452,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, scbits = s->sccipher_tobe->keylen; s->nbits = (csbits > scbits ? csbits : scbits); } - /* The keys only have 160-bit entropy, since they're based on - * a SHA-1 hash. So cap the key size at 160 bits. */ - if (s->nbits > 160) - s->nbits = 160; + /* The keys only have hlen-bit entropy, since they're based on + * a hash. So cap the key size at hlen bits. */ + if (s->nbits > ssh->kex->hash->hlen * 8) + s->nbits = ssh->kex->hash->hlen * 8; /* * If we're doing Diffie-Hellman group exchange, start by @@ -5525,29 +5527,31 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * involve user interaction. */ set_busy_status(ssh->frontend, BUSY_NOT); - sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen); + hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); if (ssh->kex == &ssh_diffiehellman_gex) { - sha_uint32(&ssh->exhash, s->pbits); - sha_mpint(&ssh->exhash, s->p); - sha_mpint(&ssh->exhash, s->g); + hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits); + hash_mpint(ssh->kex->hash, ssh->exhash, s->p); + hash_mpint(ssh->kex->hash, ssh->exhash, s->g); } - sha_mpint(&ssh->exhash, s->e); - sha_mpint(&ssh->exhash, s->f); - sha_mpint(&ssh->exhash, s->K); - SHA_Final(&ssh->exhash, s->exchange_hash); + hash_mpint(ssh->kex->hash, ssh->exhash, s->e); + hash_mpint(ssh->kex->hash, ssh->exhash, s->f); + hash_mpint(ssh->kex->hash, ssh->exhash, s->K); + assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash)); + ssh->kex->hash->final(ssh->exhash, s->exchange_hash); dh_cleanup(ssh->kex_ctx); ssh->kex_ctx = NULL; #if 0 debug(("Exchange hash is:\n")); - dmemdump(s->exchange_hash, 20); + dmemdump(s->exchange_hash, ssh->kex->hash->hlen); #endif s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen); if (!s->hkey || !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen, - (char *)s->exchange_hash, 20)) { + (char *)s->exchange_hash, + ssh->kex->hash->hlen)) { bombout(("Server's host key did not match the signature supplied")); crStop(0); } @@ -5595,8 +5599,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * authentication. */ if (!s->got_session_id) { + assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id)); memcpy(ssh->v2_session_id, s->exchange_hash, sizeof(s->exchange_hash)); + assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id)); + ssh->v2_session_id_len = ssh->kex->hash->hlen; s->got_session_id = TRUE; } @@ -5632,11 +5639,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ { unsigned char keyspace[40]; - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace); ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace); ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); } @@ -5690,11 +5697,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ { unsigned char keyspace[40]; - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace); ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace); ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); - ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace); + ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); } logeventf(ssh, "Initialised %.200s server->client encryption", @@ -6877,7 +6884,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring_start(s->pktout); ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen); - s->siglen = s->pktout->length - 5 + 4 + 20; + s->siglen = s->pktout->length - 5 + 4 + + ssh->v2_session_id_len; if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) s->siglen -= 4; s->len = 1; /* message type */ @@ -6896,11 +6904,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->q += 4; /* Now the data to be signed... */ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { - PUT_32BIT(s->q, 20); + PUT_32BIT(s->q, ssh->v2_session_id_len); s->q += 4; } - memcpy(s->q, ssh->v2_session_id, 20); - s->q += 20; + memcpy(s->q, ssh->v2_session_id, + ssh->v2_session_id_len); + s->q += ssh->v2_session_id_len; memcpy(s->q, s->pktout->data + 5, s->pktout->length - 5); s->q += s->pktout->length - 5; @@ -7196,16 +7205,19 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * followed by everything so far placed in the * outgoing packet. */ - sigdata_len = s->pktout->length - 5 + 4 + 20; + sigdata_len = s->pktout->length - 5 + 4 + + ssh->v2_session_id_len; if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) sigdata_len -= 4; sigdata = snewn(sigdata_len, unsigned char); p = 0; if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) { - PUT_32BIT(sigdata+p, 20); + PUT_32BIT(sigdata+p, ssh->v2_session_id_len); p += 4; } - memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20; + memcpy(sigdata+p, ssh->v2_session_id, + ssh->v2_session_id_len); + p += ssh->v2_session_id_len; memcpy(sigdata+p, s->pktout->data + 5, s->pktout->length - 5); p += s->pktout->length - 5; diff --git a/ssh.h b/ssh.h index 6a47e4b6..09ee167a 100644 --- a/ssh.h +++ b/ssh.h @@ -175,6 +175,13 @@ struct ssh_mac { char *text_name; }; +struct ssh_hash { + void *(*init)(void); /* also allocates context */ + void (*bytes)(void *, void *, int); + void (*final)(void *, unsigned char *); /* also frees context */ + int hlen; /* output length in bytes */ +}; + struct ssh_kex { /* * Plugging in another KEX algorithm requires structural chaos, @@ -186,6 +193,7 @@ struct ssh_kex { char *name, *groupname; const unsigned char *pdata, *gdata;/* NULL means use group exchange */ int plen, glen; + const struct ssh_hash *hash; }; struct ssh_signkey { @@ -236,6 +244,7 @@ extern const struct ssh2_ciphers ssh2_des; extern const struct ssh2_ciphers ssh2_aes; extern const struct ssh2_ciphers ssh2_blowfish; extern const struct ssh2_ciphers ssh2_arcfour; +extern const struct ssh_hash ssh_sha1; extern const struct ssh_kex ssh_diffiehellman_group1; extern const struct ssh_kex ssh_diffiehellman_group14; extern const struct ssh_kex ssh_diffiehellman_gex; diff --git a/sshdh.c b/sshdh.c index 898ff640..9d24908e 100644 --- a/sshdh.c +++ b/sshdh.c @@ -48,17 +48,17 @@ static const unsigned char G[] = { 2 }; const struct ssh_kex ssh_diffiehellman_group1 = { "diffie-hellman-group1-sha1", "group1", - P1, G, lenof(P1), lenof(G) + P1, G, lenof(P1), lenof(G), &ssh_sha1 }; const struct ssh_kex ssh_diffiehellman_group14 = { "diffie-hellman-group14-sha1", "group14", - P14, G, lenof(P14), lenof(G) + P14, G, lenof(P14), lenof(G), &ssh_sha1 }; const struct ssh_kex ssh_diffiehellman_gex = { "diffie-hellman-group-exchange-sha1", NULL, - NULL, NULL, 0, 0 + NULL, NULL, 0, 0, &ssh_sha1 }; /* diff --git a/sshsha.c b/sshsha.c index aaaad579..29908b62 100644 --- a/sshsha.c +++ b/sshsha.c @@ -188,6 +188,38 @@ void SHA_Simple(void *p, int len, unsigned char *output) SHA_Final(&s, output); } +/* + * Thin abstraction for things where hashes are pluggable. + */ + +static void *sha1_init(void) +{ + SHA_State *s; + + s = snew(SHA_State); + SHA_Init(s); + return s; +} + +static void sha1_bytes(void *handle, void *p, int len) +{ + SHA_State *s = handle; + + SHA_Bytes(s, p, len); +} + +static void sha1_final(void *handle, unsigned char *output) +{ + SHA_State *s = handle; + + SHA_Final(s, output); + sfree(s); +} + +const struct ssh_hash ssh_sha1 = { + sha1_init, sha1_bytes, sha1_final, 20 +}; + /* ---------------------------------------------------------------------- * The above is the SHA-1 algorithm itself. Now we implement the * HMAC wrapper on it. -- 2.11.0