Add infrastructure for supporting multiple hashes in key exchange.
authorben <ben@cda61777-01e9-0310-a592-d414129be87e>
Wed, 31 Aug 2005 20:43:06 +0000 (20:43 +0000)
committerben <ben@cda61777-01e9-0310-a592-d414129be87e>
Wed, 31 Aug 2005 20:43:06 +0000 (20:43 +0000)
Nothing very surprising here.

git-svn-id: svn://svn.tartarus.org/sgt/putty@6251 cda61777-01e9-0310-a592-d414129be87e

ssh.c
ssh.h
sshdh.c
sshsha.c

diff --git a/ssh.c b/ssh.c
index 837bbfa..22cea9f 100644 (file)
--- 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 6a47e4b..09ee167 100644 (file)
--- 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 898ff64..9d24908 100644 (file)
--- 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
 };
 
 /*
index aaaad57..29908b6 100644 (file)
--- 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.