SSH 2 support, phase 1, debugging. Currently does Diffie-Hellman and gets
[u/mdw/putty] / sshsha.c
index d766743..487d173 100644 (file)
--- a/sshsha.c
+++ b/sshsha.c
@@ -1,12 +1,27 @@
 /*
- * SHA core transform algorithm, used here solely as a `stirring'
- * function for the PuTTY random number pool. Implemented directly
- * from the specification by Simon Tatham.
+ * SHA1 hash algorithm. Used in SSH2 as a MAC, and the transform is
+ * also used as a `stirring' function for the PuTTY random number
+ * pool. Implemented directly from the specification by Simon
+ * Tatham.
  */
 
 #include "ssh.h"
 
-#define rol(x,y) ( ((x) << (y)) | (((word32)x) >> (32-y)) )
+typedef unsigned int uint32;
+
+/* ----------------------------------------------------------------------
+ * Core SHA algorithm: processes 16-word blocks into a message digest.
+ */
+
+#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )
+
+void SHA_Core_Init(uint32 h[5]) {
+    h[0] = 0x67452301;
+    h[1] = 0xefcdab89;
+    h[2] = 0x98badcfe;
+    h[3] = 0x10325476;
+    h[4] = 0xc3d2e1f0;
+}
 
 void SHATransform(word32 *digest, word32 *block) {
     word32 w[80];
@@ -50,3 +65,165 @@ void SHATransform(word32 *digest, word32 *block) {
     digest[3] += d;
     digest[4] += e;
 }
+
+/* ----------------------------------------------------------------------
+ * Outer SHA algorithm: take an arbitrary length byte string,
+ * convert it into 16-word blocks with the prescribed padding at
+ * the end, and pass those blocks to the core SHA algorithm.
+ */
+
+void SHA_Init(SHA_State *s) {
+    SHA_Core_Init(s->h);
+    s->blkused = 0;
+    s->lenhi = s->lenlo = 0;
+}
+
+void SHA_Bytes(SHA_State *s, void *p, int len) {
+    unsigned char *q = (unsigned char *)p;
+    uint32 wordblock[16];
+    uint32 lenw = len;
+    int i;
+
+    /*
+     * Update the length field.
+     */
+    s->lenlo += lenw;
+    s->lenhi += (s->lenlo < lenw);
+
+    if (s->blkused && s->blkused+len < 64) {
+        /*
+         * Trivial case: just add to the block.
+         */
+        memcpy(s->block + s->blkused, q, len);
+        s->blkused += len;
+    } else {
+        /*
+         * We must complete and process at least one block.
+         */
+        while (s->blkused + len >= 64) {
+            memcpy(s->block + s->blkused, q, 64 - s->blkused);
+            q += 64 - s->blkused;
+            len -= 64 - s->blkused;
+            /* Now process the block. Gather bytes big-endian into words */
+            for (i = 0; i < 16; i++) {
+                wordblock[i] =
+                    ( ((uint32)s->block[i*4+0]) << 24 ) |
+                    ( ((uint32)s->block[i*4+1]) << 16 ) |
+                    ( ((uint32)s->block[i*4+2]) <<  8 ) |
+                    ( ((uint32)s->block[i*4+3]) <<  0 );
+            }
+            SHATransform(s->h, wordblock);
+            s->blkused = 0;
+        }
+        memcpy(s->block, q, len);
+        s->blkused = len;
+    }
+}
+
+void SHA_Final(SHA_State *s, unsigned char *output) {
+    int i;
+    int pad;
+    unsigned char c[64];
+    uint32 lenhi, lenlo;
+
+    if (s->blkused >= 56)
+        pad = 56 + 64 - s->blkused;
+    else
+        pad = 56 - s->blkused;
+
+    lenhi = (s->lenhi << 3) | (s->lenlo >> (32-3));
+    lenlo = (s->lenlo << 3);
+
+    memset(c, 0, pad);
+    c[0] = 0x80;
+    SHA_Bytes(s, &c, pad);
+
+    c[0] = (lenhi >> 24) & 0xFF;
+    c[1] = (lenhi >> 16) & 0xFF;
+    c[2] = (lenhi >>  8) & 0xFF;
+    c[3] = (lenhi >>  0) & 0xFF;
+    c[4] = (lenlo >> 24) & 0xFF;
+    c[5] = (lenlo >> 16) & 0xFF;
+    c[6] = (lenlo >>  8) & 0xFF;
+    c[7] = (lenlo >>  0) & 0xFF;
+
+    SHA_Bytes(s, &c, 8);
+
+    for (i = 0; i < 5; i++) {
+        output[i*4  ] = (s->h[i] >> 24) & 0xFF;
+        output[i*4+1] = (s->h[i] >> 16) & 0xFF;
+        output[i*4+2] = (s->h[i] >>  8) & 0xFF;
+        output[i*4+3] = (s->h[i]      ) & 0xFF;
+    }
+}
+
+void SHA_Simple(void *p, int len, unsigned char *output) {
+    SHA_State s;
+
+    SHA_Init(&s);
+    SHA_Bytes(&s, p, len);
+    SHA_Final(&s, output);
+}
+
+/* ----------------------------------------------------------------------
+ * The above is the SHA-1 algorithm itself. Now we implement the
+ * HMAC wrapper on it.
+ */
+
+static SHA_State sha1_mac_s1, sha1_mac_s2;
+
+static void sha1_sesskey(unsigned char *key, int len) {
+    unsigned char foo[64];
+    int i;
+
+    memset(foo, 0x36, 64);
+    for (i = 0; i < len && i < 64; i++)
+        foo[i] ^= key[i];
+    SHA_Init(&sha1_mac_s1);
+    SHA_Bytes(&sha1_mac_s1, foo, 64);
+
+    memset(foo, 0x5C, 64);
+    for (i = 0; i < len && i < 64; i++)
+        foo[i] ^= key[i];
+    SHA_Init(&sha1_mac_s2);
+    SHA_Bytes(&sha1_mac_s2, foo, 64);
+
+    memset(foo, 0, 64);                /* burn the evidence */
+}
+
+static void sha1_do_hmac(unsigned char *blk, int len, unsigned long seq,
+                         unsigned char *hmac) {
+    SHA_State s;
+    unsigned char intermediate[20];
+
+    intermediate[0] = (unsigned char)((seq >> 24) & 0xFF);
+    intermediate[1] = (unsigned char)((seq >> 16) & 0xFF);
+    intermediate[2] = (unsigned char)((seq >>  8) & 0xFF);
+    intermediate[3] = (unsigned char)((seq      ) & 0xFF);
+
+    s = sha1_mac_s1;                   /* structure copy */
+    SHA_Bytes(&s, intermediate, 4);
+    SHA_Bytes(&s, blk, len);
+    SHA_Final(&s, intermediate);
+    s = sha1_mac_s2;                   /* structure copy */
+    SHA_Bytes(&s, intermediate, 20);
+    SHA_Final(&s, hmac);
+}
+
+static void sha1_generate(unsigned char *blk, int len, unsigned long seq) {
+    sha1_do_hmac(blk, len, seq, blk+len);
+}
+
+static int sha1_verify(unsigned char *blk, int len, unsigned long seq) {
+    unsigned char correct[20];
+    sha1_do_hmac(blk, len, seq, correct);
+    return !memcmp(correct, blk+len, 20);
+}
+
+struct ssh_mac ssh_sha1 = {
+    sha1_sesskey,
+    sha1_generate,
+    sha1_verify,
+    "hmac-sha1",
+    20
+};