浏览代码

PuTTY snapshot 4f9a90fc (Turn SSH hashes into a classoid - 2018-09-19)

Source commit: 23a7ec13af6f7010b1d0c2959413aa2842487490
Martin Prikryl 6 年之前
父节点
当前提交
4e2f697146

+ 2 - 2
source/putty/cproxy.c

@@ -19,10 +19,10 @@
 static void hmacmd5_chap(const unsigned char *challenge, int challen,
 			 const char *passwd, unsigned char *response)
 {
-    void *hmacmd5_ctx;
+    struct hmacmd5_context *hmacmd5_ctx;
     int pwlen;
 
-    hmacmd5_ctx = hmacmd5_make_context(NULL);
+    hmacmd5_ctx = hmacmd5_make_context();
 
     pwlen = strlen(passwd);
     if (pwlen>64) {

+ 1 - 1
source/putty/import.c

@@ -549,7 +549,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
             des3_decrypt_pubkey_ossh(keybuf, key->iv,
                                      key->keyblob->u, key->keyblob->len);
         else {
-            void *ctx;
+            AESContext *ctx;
             assert(key->encryption == OP_E_AES);
             ctx = aes_make_context();
             aes128_key(ctx, keybuf);

+ 11 - 0
source/putty/marshal.h

@@ -25,6 +25,17 @@ struct BinarySink {
     ((obj)->binarysink_->write = (writefn), \
      (obj)->binarysink_->binarysink_ = (obj)->binarysink_)
 
+/*
+ * To define a larger structure type as a valid BinarySink in such a
+ * way that it will delegate the write method to some other object,
+ * put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and
+ * when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point
+ * at the object it wants to delegate to.
+ */
+#define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_
+#define BinarySink_DELEGATE_INIT(obj, othersink) \
+    ((obj)->binarysink_ = BinarySink_UPCAST(othersink))
+
 /*
  * The implementing type's write function will want to downcast its
  * 'BinarySink *' parameter back to the more specific type. Also,

+ 57 - 63
source/putty/ssh.c

@@ -328,10 +328,10 @@ const static struct ssh_signkey_with_user_pref_id hostkey_algs[] = {
     { &ssh_rsa, HK_RSA },
 };
 
-const static struct ssh_mac *const macs[] = {
+const static struct ssh2_macalg *const macs[] = {
     &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
 };
-const static struct ssh_mac *const buggymacs[] = {
+const static struct ssh2_macalg *const buggymacs[] = {
     &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
 };
 
@@ -668,8 +668,7 @@ enum RekeyClass {
 
 struct ssh_tag {
     char *v_c, *v_s;
-    void *exhash;
-    BinarySink *exhash_bs;
+    ssh_hash *exhash;
 
     Socket s;
 
@@ -2914,10 +2913,9 @@ static void do_ssh1_login(void *vctx)
     sfree(s->rsabuf);
 
     {
-        const struct ssh_cipher *cipher =
-            (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
-             s->cipher_type == SSH_CIPHER_DES ? &ssh_des :
-             &ssh_3des);
+        const struct ssh1_cipheralg *cipher =
+            (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish :
+             s->cipher_type == SSH_CIPHER_DES ? &ssh1_des : &ssh1_3des);
         ssh1_bpp_new_cipher(ssh->bpp, cipher, ssh->session_key);
         logeventf(ssh, "Initialised %s encryption", cipher->text_name);
     }
@@ -4781,11 +4779,10 @@ static void add_to_commasep(strbuf *buf, const char *data)
 static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
                        char chr, int keylen)
 {
-    const struct ssh_hash *h = ssh->kex->hash;
+    const struct ssh_hashalg *h = ssh->kex->hash;
     int keylen_padded;
     unsigned char *key;
-    void *s, *s2;
-    BinarySink *bs;
+    ssh_hash *s, *s2;
 
     if (keylen == 0)
         return;
@@ -4806,32 +4803,30 @@ static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
     key = strbuf_append(out, keylen_padded);
 
     /* First hlen bytes. */
-    s = h->init();
-    bs = h->sink(s);
+    s = ssh_hash_new(h);
     if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
-	put_mp_ssh2(bs, K);
-    put_data(bs, H, h->hlen);
-    put_byte(bs, chr);
-    put_data(bs, ssh->v2_session_id, ssh->v2_session_id_len);
-    h->final(s, key);
+        put_mp_ssh2(s, K);
+    put_data(s, H, h->hlen);
+    put_byte(s, chr);
+    put_data(s, ssh->v2_session_id, ssh->v2_session_id_len);
+    ssh_hash_final(s, key);
 
     /* Subsequent blocks of hlen bytes. */
     if (keylen_padded > h->hlen) {
         int offset;
 
-        s = h->init();
-        bs = h->sink(s);
+        s = ssh_hash_new(h);
         if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
-            put_mp_ssh2(bs, K);
-        put_data(bs, H, h->hlen);
+            put_mp_ssh2(s, K);
+        put_data(s, H, h->hlen);
 
         for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) {
-            put_data(bs, key + offset - h->hlen, h->hlen);
-            s2 = h->copy(s);
-            h->final(s2, key + offset);
+            put_data(s, key + offset - h->hlen, h->hlen);
+            s2 = ssh_hash_copy(s);
+            ssh_hash_final(s2, key + offset);
         }
 
-        h->free(s);
+        ssh_hash_free(s);
     }
 }
 
@@ -4851,11 +4846,11 @@ struct kexinit_algorithm {
             int warn;
         } hk;
 	struct {
-	    const struct ssh2_cipher *cipher;
+            const struct ssh2_cipheralg *cipher;
 	    int warn;
 	} cipher;
 	struct {
-	    const struct ssh_mac *mac;
+            const struct ssh2_macalg *mac;
 	    int etm;
 	} mac;
 	const struct ssh_compress *comp;
@@ -5024,11 +5019,11 @@ static void do_ssh2_transport(void *vctx)
 	void *our_kexinit;
 	int our_kexinitlen;
 	int kex_init_value, kex_reply_value;
-	const struct ssh_mac *const *maclist;
+        const struct ssh2_macalg *const *maclist;
 	int nmacs;
         struct {
-            const struct ssh2_cipher *cipher;
-            const struct ssh_mac *mac;
+            const struct ssh2_cipheralg *cipher;
+            const struct ssh2_macalg *mac;
             int etm_mode;
             const struct ssh_compress *comp;
         } in, out;
@@ -5601,14 +5596,13 @@ static void do_ssh2_transport(void *vctx)
 	get_string(pktin);  /* server->client language */
 	s->ignorepkt = get_bool(pktin) && !s->guessok;
 
-	ssh->exhash = ssh->kex->hash->init();
-        ssh->exhash_bs = ssh->kex->hash->sink(ssh->exhash);
-	put_stringz(ssh->exhash_bs, ssh->v_c);
-	put_stringz(ssh->exhash_bs, ssh->v_s);
-	put_string(ssh->exhash_bs, s->our_kexinit, s->our_kexinitlen);
+        ssh->exhash = ssh_hash_new(ssh->kex->hash);
+        put_stringz(ssh->exhash, ssh->v_c);
+        put_stringz(ssh->exhash, ssh->v_s);
+        put_string(ssh->exhash, s->our_kexinit, s->our_kexinitlen);
 	sfree(s->our_kexinit);
         /* Include the type byte in the hash of server's KEXINIT */
-        put_string(ssh->exhash_bs,
+        put_string(ssh->exhash,
                    (const char *)BinarySource_UPCAST(pktin)->data - 1,
                    BinarySource_UPCAST(pktin)->len + 1);
 
@@ -5845,18 +5839,18 @@ static void do_ssh2_transport(void *vctx)
          * involve user interaction. */
         set_busy_status(ssh->frontend, BUSY_NOT);
 
-        put_stringpl(ssh->exhash_bs, s->hostkeydata);
+        put_stringpl(ssh->exhash, s->hostkeydata);
         if (dh_is_gex(ssh->kex)) {
             if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
-                put_uint32(ssh->exhash_bs, DH_MIN_SIZE);
-            put_uint32(ssh->exhash_bs, s->pbits);
+                put_uint32(ssh->exhash, DH_MIN_SIZE);
+            put_uint32(ssh->exhash, s->pbits);
             if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
-                put_uint32(ssh->exhash_bs, DH_MAX_SIZE);
-            put_mp_ssh2(ssh->exhash_bs, s->p);
-            put_mp_ssh2(ssh->exhash_bs, s->g);
+                put_uint32(ssh->exhash, DH_MAX_SIZE);
+            put_mp_ssh2(ssh->exhash, s->p);
+            put_mp_ssh2(ssh->exhash, s->g);
         }
-        put_mp_ssh2(ssh->exhash_bs, s->e);
-        put_mp_ssh2(ssh->exhash_bs, s->f);
+        put_mp_ssh2(ssh->exhash, s->e);
+        put_mp_ssh2(ssh->exhash, s->f);
 
         dh_cleanup(ssh->kex_ctx);
         freebn(s->f);
@@ -5894,19 +5888,19 @@ static void do_ssh2_transport(void *vctx)
         }
 
         s->hostkeydata = get_string(pktin);
-        put_stringpl(ssh->exhash_bs, s->hostkeydata);
+        put_stringpl(ssh->exhash, s->hostkeydata);
         s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
 
         {
             strbuf *pubpoint = strbuf_new();
             ssh_ecdhkex_getpublic(s->eckey, BinarySink_UPCAST(pubpoint));
-            put_string(ssh->exhash_bs, pubpoint->u, pubpoint->len);
+            put_string(ssh->exhash, pubpoint->u, pubpoint->len);
             strbuf_free(pubpoint);
         }
 
         {
             ptrlen keydata = get_string(pktin);
-            put_stringpl(ssh->exhash_bs, keydata);
+            put_stringpl(ssh->exhash, keydata);
             s->K = ssh_ecdhkex_getkey(s->eckey, keydata.ptr, keydata.len);
             if (!get_err(pktin) && !s->K) {
                 ssh_ecdhkex_freekey(s->eckey);
@@ -6101,7 +6095,7 @@ static void do_ssh2_transport(void *vctx)
                 if (ssh->hostkey_alg) {
                     s->hkey = ssh_key_new_pub(ssh->hostkey_alg,
                                               s->hostkeydata);
-                    put_string(ssh->exhash_bs,
+                    put_string(ssh->exhash,
                                s->hostkeydata.ptr, s->hostkeydata.len);
                 }
                 /*
@@ -6150,18 +6144,18 @@ static void do_ssh2_transport(void *vctx)
         set_busy_status(ssh->frontend, BUSY_NOT);
 
         if (!s->hkey)
-            put_stringz(ssh->exhash_bs, "");
+            put_stringz(ssh->exhash, "");
         if (dh_is_gex(ssh->kex)) {
             /* min,  preferred, max */
-            put_uint32(ssh->exhash_bs, s->pbits);
-            put_uint32(ssh->exhash_bs, s->pbits);
-            put_uint32(ssh->exhash_bs, s->pbits * 2);
+            put_uint32(ssh->exhash, s->pbits);
+            put_uint32(ssh->exhash, s->pbits);
+            put_uint32(ssh->exhash, s->pbits * 2);
 
-            put_mp_ssh2(ssh->exhash_bs, s->p);
-            put_mp_ssh2(ssh->exhash_bs, s->g);
+            put_mp_ssh2(ssh->exhash, s->p);
+            put_mp_ssh2(ssh->exhash, s->g);
         }
-        put_mp_ssh2(ssh->exhash_bs, s->e);
-        put_mp_ssh2(ssh->exhash_bs, s->f);
+        put_mp_ssh2(ssh->exhash, s->e);
+        put_mp_ssh2(ssh->exhash, s->f);
 
         /*
          * MIC verification is done below, after we compute the hash
@@ -6193,7 +6187,7 @@ static void do_ssh2_transport(void *vctx)
         }
 
         s->hostkeydata = get_string(pktin);
-        put_stringpl(ssh->exhash_bs, s->hostkeydata);
+        put_stringpl(ssh->exhash, s->hostkeydata);
 	s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
 
         rsakeydata = get_string(pktin);
@@ -6204,7 +6198,7 @@ static void do_ssh2_transport(void *vctx)
             crStopV;
         }
 
-        put_stringpl(ssh->exhash_bs, rsakeydata);
+        put_stringpl(ssh->exhash, rsakeydata);
 
         /*
          * Next, set up a shared secret K, of precisely KLEN -
@@ -6250,7 +6244,7 @@ static void do_ssh2_transport(void *vctx)
             put_string(s->pktout, outstr, outstrlen);
             ssh_pkt_write(ssh, s->pktout);
 
-	    put_string(ssh->exhash_bs, outstr, outstrlen);
+            put_string(ssh->exhash, outstr, outstrlen);
 
             strbuf_free(buf);
             sfree(outstr);
@@ -6271,9 +6265,9 @@ static void do_ssh2_transport(void *vctx)
         }
     }
 
-    put_mp_ssh2(ssh->exhash_bs, s->K);
-    assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
-    ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
+    put_mp_ssh2(ssh->exhash, s->K);
+    assert(ssh_hash_alg(ssh->exhash)->hlen <= sizeof(s->exchange_hash));
+    ssh_hash_final(ssh->exhash, s->exchange_hash);
 
 #ifndef NO_GSSAPI
     if (ssh->kex->main_type == KEXTYPE_GSS) {

+ 119 - 67
source/putty/ssh.h

@@ -331,11 +331,12 @@ int detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32 len,
 /*
  * SSH2 RSA key exchange functions
  */
-struct ssh_hash;
+struct ssh_hashalg;
 struct RSAKey *ssh_rsakex_newkey(const void *data, int len);
 void ssh_rsakex_freekey(struct RSAKey *key);
 int ssh_rsakex_klen(struct RSAKey *key);
-void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
+                        unsigned char *in, int inlen,
                         unsigned char *out, int outlen, struct RSAKey *key);
 
 /*
@@ -355,6 +356,9 @@ Bignum ssh_ecdhkex_getkey(struct ec_key *key,
 Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
                   unsigned char *digest, int digest_len);
 
+struct ssh2_cipheralg;
+typedef const struct ssh2_cipheralg *ssh2_cipher;
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -371,11 +375,12 @@ void MD5Init(struct MD5Context *context);
 void MD5Final(unsigned char digest[16], struct MD5Context *context);
 void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
 
-void *hmacmd5_make_context(void *);
-void hmacmd5_free_context(void *handle);
-void hmacmd5_key(void *handle, void const *key, int len);
-void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
-		     unsigned char *hmac);
+struct hmacmd5_context;
+struct hmacmd5_context *hmacmd5_make_context(void);
+void hmacmd5_free_context(struct hmacmd5_context *ctx);
+void hmacmd5_key(struct hmacmd5_context *ctx, void const *key, int len);
+void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
+                     unsigned char const *blk, int len, unsigned char *hmac);
 
 int supports_sha_ni(void);
 
@@ -420,27 +425,39 @@ void SHA384_Init(SHA384_State * s);
 void SHA384_Final(SHA384_State * s, unsigned char *output);
 void SHA384_Simple(const void *p, int len, unsigned char *output);
 
-struct ssh_mac;
-struct ssh_cipher {
-    void *(*make_context)(void);
-    void (*free_context)(void *);
-    void (*sesskey) (void *, const void *key);	/* for SSH-1 */
-    void (*encrypt) (void *, void *blk, int len);
-    void (*decrypt) (void *, void *blk, int len);
+struct ssh2_macalg;
+
+struct ssh1_cipheralg;
+typedef const struct ssh1_cipheralg *ssh1_cipher;
+
+struct ssh1_cipheralg {
+    ssh1_cipher *(*new)(void);
+    void (*free)(ssh1_cipher *);
+    void (*sesskey)(ssh1_cipher *, const void *key);
+    void (*encrypt)(ssh1_cipher *, void *blk, int len);
+    void (*decrypt)(ssh1_cipher *, void *blk, int len);
     int blksize;
     const char *text_name;
 };
 
-struct ssh2_cipher {
-    void *(*make_context)(void);
-    void (*free_context)(void *);
-    void (*setiv) (void *, const void *iv);	/* for SSH-2 */
-    void (*setkey) (void *, const void *key);/* for SSH-2 */
-    void (*encrypt) (void *, void *blk, int len);
-    void (*decrypt) (void *, void *blk, int len);
+#define ssh1_cipher_new(alg) ((alg)->new())
+#define ssh1_cipher_free(ctx) ((*(ctx))->free(ctx))
+#define ssh1_cipher_sesskey(ctx, key) ((*(ctx))->sesskey(ctx, key))
+#define ssh1_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len))
+#define ssh1_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len))
+
+struct ssh2_cipheralg {
+    ssh2_cipher *(*new)(const struct ssh2_cipheralg *alg);
+    void (*free)(ssh2_cipher *);
+    void (*setiv)(ssh2_cipher *, const void *iv);
+    void (*setkey)(ssh2_cipher *, const void *key);
+    void (*encrypt)(ssh2_cipher *, void *blk, int len);
+    void (*decrypt)(ssh2_cipher *, void *blk, int len);
     /* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */
-    void (*encrypt_length) (void *, void *blk, int len, unsigned long seq);
-    void (*decrypt_length) (void *, void *blk, int len, unsigned long seq);
+    void (*encrypt_length)(ssh2_cipher *, void *blk, int len,
+                           unsigned long seq);
+    void (*decrypt_length)(ssh2_cipher *, void *blk, int len,
+                           unsigned long seq);
     const char *name;
     int blksize;
     /* real_keybits is the number of bits of entropy genuinely used by
@@ -461,46 +478,80 @@ struct ssh2_cipher {
 #define SSH_CIPHER_SEPARATE_LENGTH      2
     const char *text_name;
     /* If set, this takes priority over other MAC. */
-    const struct ssh_mac *required_mac;
+    const struct ssh2_macalg *required_mac;
 };
 
+#define ssh2_cipher_new(alg) ((alg)->new(alg))
+#define ssh2_cipher_free(ctx) ((*(ctx))->free(ctx))
+#define ssh2_cipher_setiv(ctx, iv) ((*(ctx))->setiv(ctx, iv))
+#define ssh2_cipher_setkey(ctx, key) ((*(ctx))->setkey(ctx, key))
+#define ssh2_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len))
+#define ssh2_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len))
+#define ssh2_cipher_encrypt_length(ctx, blk, len, seq) \
+    ((*(ctx))->encrypt_length(ctx, blk, len, seq))
+#define ssh2_cipher_decrypt_length(ctx, blk, len, seq) \
+    ((*(ctx))->decrypt_length(ctx, blk, len, seq))
+#define ssh2_cipher_alg(ctx) (*(ctx))
+
 struct ssh2_ciphers {
     int nciphers;
-    const struct ssh2_cipher *const *list;
+    const struct ssh2_cipheralg *const *list;
 };
 
-struct ssh_mac {
+struct ssh2_macalg;
+typedef struct ssh2_mac {
+    const struct ssh2_macalg *vt;
+    BinarySink_DELEGATE_IMPLEMENTATION;
+} ssh2_mac;
+
+struct ssh2_macalg {
     /* Passes in the cipher context */
-    void *(*make_context)(void *);
-    void (*free_context)(void *);
-    void (*setkey) (void *, const void *key);
-    /* whole-packet operations */
-    void (*generate) (void *, void *blk, int len, unsigned long seq);
-    int (*verify) (void *, const void *blk, int len, unsigned long seq);
-    /* partial-packet operations */
-    void (*start) (void *);
-    BinarySink *(*sink) (void *);
-    void (*genresult) (void *, unsigned char *);
-    int (*verresult) (void *, unsigned char const *);
+    ssh2_mac *(*new)(const struct ssh2_macalg *alg, ssh2_cipher *cipher);
+    void (*free)(ssh2_mac *);
+    void (*setkey)(ssh2_mac *, const void *key);
+    void (*start)(ssh2_mac *);
+    void (*genresult)(ssh2_mac *, unsigned char *);
     const char *name, *etm_name;
     int len, keylen;
     const char *text_name;
 };
 
-struct ssh_hash {
-    void *(*init)(void); /* also allocates context */
-    void *(*copy)(const void *);
-    BinarySink *(*sink) (void *);
-    void (*final)(void *, unsigned char *); /* also frees context */
-    void (*free)(void *);
+#define ssh2_mac_new(alg, cipher) ((alg)->new(alg, cipher))
+#define ssh2_mac_free(ctx) ((ctx)->vt->free(ctx))
+#define ssh2_mac_setkey(ctx, key) ((ctx)->vt->free(ctx, key))
+#define ssh2_mac_start(ctx) ((ctx)->vt->start(ctx))
+#define ssh2_mac_genresult(ctx, out) ((ctx)->vt->genresult(ctx, out))
+#define ssh2_mac_alg(ctx) ((ctx)->vt)
+
+/* Centralised 'methods' for ssh2_mac, defined in sshmac.c */
+int ssh2_mac_verresult(ssh2_mac *, const void *);
+void ssh2_mac_generate(ssh2_mac *, void *, int, unsigned long seq);
+int ssh2_mac_verify(ssh2_mac *, const void *, int, unsigned long seq);
+
+typedef struct ssh_hash {
+    const struct ssh_hashalg *vt;
+    BinarySink_DELEGATE_IMPLEMENTATION;
+} ssh_hash;
+
+struct ssh_hashalg {
+    ssh_hash *(*new)(const struct ssh_hashalg *alg);
+    ssh_hash *(*copy)(ssh_hash *);
+    void (*final)(ssh_hash *, unsigned char *); /* ALSO FREES THE ssh_hash! */
+    void (*free)(ssh_hash *);
     int hlen; /* output length in bytes */
     const char *text_name;
 };   
 
+#define ssh_hash_new(alg) ((alg)->new(alg))
+#define ssh_hash_copy(ctx) ((ctx)->vt->copy(ctx))
+#define ssh_hash_final(ctx, out) ((ctx)->vt->final(ctx, out))
+#define ssh_hash_free(ctx) ((ctx)->vt->free(ctx))
+#define ssh_hash_alg(ctx) ((ctx)->vt)
+
 struct ssh_kex {
     const char *name, *groupname;
     enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH, KEXTYPE_GSS } main_type;
-    const struct ssh_hash *hash;
+    const struct ssh_hashalg *hash;
     const void *extra;                 /* private to the kex methods */
 };
 
@@ -576,19 +627,19 @@ struct ssh2_userkey {
 /* The maximum length of any hash algorithm used in kex. (bytes) */
 #define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */
 
-extern const struct ssh_cipher ssh_3des;
-extern const struct ssh_cipher ssh_des;
-extern const struct ssh_cipher ssh_blowfish_ssh1;
+extern const struct ssh1_cipheralg ssh1_3des;
+extern const struct ssh1_cipheralg ssh1_des;
+extern const struct ssh1_cipheralg ssh1_blowfish;
 extern const struct ssh2_ciphers ssh2_3des;
 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 ssh2_ciphers ssh2_ccp;
-extern const struct ssh_hash ssh_sha1;
-extern const struct ssh_hash ssh_sha256;
-extern const struct ssh_hash ssh_sha384;
-extern const struct ssh_hash ssh_sha512;
+extern const struct ssh_hashalg ssh_sha1;
+extern const struct ssh_hashalg ssh_sha256;
+extern const struct ssh_hashalg ssh_sha384;
+extern const struct ssh_hashalg ssh_sha512;
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
@@ -601,22 +652,23 @@ extern const ssh_keyalg ssh_ecdsa_ed25519;
 extern const ssh_keyalg ssh_ecdsa_nistp256;
 extern const ssh_keyalg ssh_ecdsa_nistp384;
 extern const ssh_keyalg ssh_ecdsa_nistp521;
-extern const struct ssh_mac ssh_hmac_md5;
-extern const struct ssh_mac ssh_hmac_sha1;
-extern const struct ssh_mac ssh_hmac_sha1_buggy;
-extern const struct ssh_mac ssh_hmac_sha1_96;
-extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
-extern const struct ssh_mac ssh_hmac_sha256;
-
-void *aes_make_context(void);
-void aes_free_context(void *handle);
-void aes128_key(void *handle, const void *key);
-void aes192_key(void *handle, const void *key);
-void aes256_key(void *handle, const void *key);
-void aes_iv(void *handle, const void *iv);
-void aes_ssh2_encrypt_blk(void *handle, void *blk, int len);
-void aes_ssh2_decrypt_blk(void *handle, void *blk, int len);
-void aes_ssh2_sdctr(void *handle, void *blk, int len);
+extern const struct ssh2_macalg ssh_hmac_md5;
+extern const struct ssh2_macalg ssh_hmac_sha1;
+extern const struct ssh2_macalg ssh_hmac_sha1_buggy;
+extern const struct ssh2_macalg ssh_hmac_sha1_96;
+extern const struct ssh2_macalg ssh_hmac_sha1_96_buggy;
+extern const struct ssh2_macalg ssh_hmac_sha256;
+
+typedef struct AESContext AESContext;
+AESContext *aes_make_context(void);
+void aes_free_context(AESContext *ctx);
+void aes128_key(AESContext *ctx, const void *key);
+void aes192_key(AESContext *ctx, const void *key);
+void aes256_key(AESContext *ctx, const void *key);
+void aes_iv(AESContext *ctx, const void *iv);
+void aes_ssh2_encrypt_blk(AESContext *ctx, void *blk, int len);
+void aes_ssh2_decrypt_blk(AESContext *ctx, void *blk, int len);
+void aes_ssh2_sdctr(AESContext *ctx, void *blk, int len);
 
 /*
  * PuTTY version number formatted as an SSH version string. 

+ 8 - 10
source/putty/ssh1bpp.c

@@ -17,8 +17,7 @@ struct ssh1_bpp_state {
     int chunk;
     PktIn *pktin;
 
-    const struct ssh_cipher *cipher;
-    void *cipher_ctx;
+    ssh1_cipher *cipher;
 
     struct crcda_ctx *crcda_ctx;
 
@@ -51,7 +50,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
 {
     struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
     if (s->cipher)
-        s->cipher->free_context(s->cipher_ctx);
+        ssh1_cipher_free(s->cipher);
     if (s->compctx)
         zlib_compress_cleanup(s->compctx);
     if (s->decompctx)
@@ -64,7 +63,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
 }
 
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
-                         const struct ssh_cipher *cipher,
+                         const struct ssh1_cipheralg *cipher,
                          const void *session_key)
 {
     struct ssh1_bpp_state *s;
@@ -73,10 +72,9 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
 
     assert(!s->cipher);
 
-    s->cipher = cipher;
-    if (s->cipher) {
-        s->cipher_ctx = cipher->make_context();
-        cipher->sesskey(s->cipher_ctx, session_key);
+    if (cipher) {
+        s->cipher = ssh1_cipher_new(cipher);
+        ssh1_cipher_sesskey(s->cipher, session_key);
 
         assert(!s->crcda_ctx);
         s->crcda_ctx = crcda_make_context();
@@ -146,7 +144,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
         }
 
         if (s->cipher)
-            s->cipher->decrypt(s->cipher_ctx, s->data, s->biglen);
+            ssh1_cipher_decrypt(s->cipher, s->data, s->biglen);
 
         s->realcrc = crc32_compute(s->data, s->biglen - 4);
         s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
@@ -273,7 +271,7 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
     PUT_32BIT(pkt->data + pktoffs, len);
 
     if (s->cipher)
-        s->cipher->encrypt(s->cipher_ctx, pkt->data + pktoffs + 4, biglen);
+        ssh1_cipher_encrypt(s->cipher, pkt->data + pktoffs + 4, biglen);
 
     bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
                  biglen + 4); /* len(length+padding+type+data+CRC) */

+ 80 - 83
source/putty/ssh2bpp.c

@@ -11,11 +11,9 @@
 
 struct ssh2_bpp_direction {
     unsigned long sequence;
-    const struct ssh2_cipher *cipher;
-    void *cipher_ctx;
-    const struct ssh_mac *mac;
+    ssh2_cipher *cipher;
+    ssh2_mac *mac;
     int etm_mode;
-    void *mac_ctx;
     const struct ssh_compress *comp;
     void *comp_ctx;
 };
@@ -28,7 +26,6 @@ struct ssh2_bpp_state {
     unsigned char *data;
     unsigned cipherblk;
     PktIn *pktin;
-    BinarySink *sc_mac_bs;
 
     struct ssh2_bpp_direction in, out;
     int pending_newkeys;
@@ -60,16 +57,16 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
 {
     struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
     sfree(s->buf);
-    if (s->out.cipher_ctx)
-        s->out.cipher->free_context(s->out.cipher_ctx);
-    if (s->out.mac_ctx)
-        s->out.mac->free_context(s->out.mac_ctx);
+    if (s->out.cipher)
+        ssh2_cipher_free(s->out.cipher);
+    if (s->out.mac)
+        ssh2_mac_free(s->out.mac);
     if (s->out.comp_ctx)
         s->out.comp->compress_cleanup(s->out.comp_ctx);
-    if (s->in.cipher_ctx)
-        s->in.cipher->free_context(s->in.cipher_ctx);
-    if (s->in.mac_ctx)
-        s->in.mac->free_context(s->in.mac_ctx);
+    if (s->in.cipher)
+        ssh2_cipher_free(s->in.cipher);
+    if (s->in.mac)
+        ssh2_mac_free(s->in.mac);
     if (s->in.comp_ctx)
         s->in.comp->decompress_cleanup(s->in.comp_ctx);
     if (s->pktin)
@@ -79,32 +76,34 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
 
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
-    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
-    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
     const struct ssh_compress *compression)
 {
     struct ssh2_bpp_state *s;
     assert(bpp->vt == &ssh2_bpp_vtable);
     s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
 
-    if (s->out.cipher_ctx)
-        s->out.cipher->free_context(s->out.cipher_ctx);
-    if (s->out.mac_ctx)
-        s->out.mac->free_context(s->out.mac_ctx);
+    if (s->out.cipher)
+        ssh2_cipher_free(s->out.cipher);
+    if (s->out.mac)
+        ssh2_mac_free(s->out.mac);
     if (s->out.comp_ctx)
         s->out.comp->compress_cleanup(s->out.comp_ctx);
 
-    s->out.cipher = cipher;
     if (cipher) {
-        s->out.cipher_ctx = cipher->make_context();
-        cipher->setkey(s->out.cipher_ctx, ckey);
-        cipher->setiv(s->out.cipher_ctx, iv);
+        s->out.cipher = ssh2_cipher_new(cipher);
+        ssh2_cipher_setkey(s->out.cipher, ckey);
+        ssh2_cipher_setiv(s->out.cipher, iv);
+    } else {
+        s->out.cipher = NULL;
     }
-    s->out.mac = mac;
     s->out.etm_mode = etm_mode;
     if (mac) {
-        s->out.mac_ctx = mac->make_context(s->out.cipher_ctx);
-        mac->setkey(s->out.mac_ctx, mac_key);
+        s->out.mac = ssh2_mac_new(mac, s->out.cipher);
+        mac->setkey(s->out.mac, mac_key);
+    } else {
+        s->out.mac = NULL;
     }
 
     s->out.comp = compression;
@@ -116,32 +115,34 @@ void ssh2_bpp_new_outgoing_crypto(
 
 void ssh2_bpp_new_incoming_crypto(
     BinaryPacketProtocol *bpp,
-    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
-    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
     const struct ssh_compress *compression)
 {
     struct ssh2_bpp_state *s;
     assert(bpp->vt == &ssh2_bpp_vtable);
     s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
 
-    if (s->in.cipher_ctx)
-        s->in.cipher->free_context(s->in.cipher_ctx);
-    if (s->in.mac_ctx)
-        s->in.mac->free_context(s->in.mac_ctx);
+    if (s->in.cipher)
+        ssh2_cipher_free(s->in.cipher);
+    if (s->in.mac)
+        ssh2_mac_free(s->in.mac);
     if (s->in.comp_ctx)
         s->in.comp->decompress_cleanup(s->in.comp_ctx);
 
-    s->in.cipher = cipher;
     if (cipher) {
-        s->in.cipher_ctx = cipher->make_context();
-        cipher->setkey(s->in.cipher_ctx, ckey);
-        cipher->setiv(s->in.cipher_ctx, iv);
+        s->in.cipher = ssh2_cipher_new(cipher);
+        ssh2_cipher_setkey(s->in.cipher, ckey);
+        ssh2_cipher_setiv(s->in.cipher, iv);
+    } else {
+        s->in.cipher = NULL;
     }
-    s->in.mac = mac;
     s->in.etm_mode = etm_mode;
     if (mac) {
-        s->in.mac_ctx = mac->make_context(s->in.cipher_ctx);
-        mac->setkey(s->in.mac_ctx, mac_key);
+        s->in.mac = ssh2_mac_new(mac, s->in.cipher);
+        mac->setkey(s->in.mac, mac_key);
+    } else {
+        s->in.mac = NULL;
     }
 
     s->in.comp = compression;
@@ -165,14 +166,15 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
         s->maxlen = 0;
         s->length = 0;
         if (s->in.cipher)
-            s->cipherblk = s->in.cipher->blksize;
+            s->cipherblk = ssh2_cipher_alg(s->in.cipher)->blksize;
         else
             s->cipherblk = 8;
         if (s->cipherblk < 8)
             s->cipherblk = 8;
-        s->maclen = s->in.mac ? s->in.mac->len : 0;
+        s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0;
 
-        if (s->in.cipher && (s->in.cipher->flags & SSH_CIPHER_IS_CBC) &&
+        if (s->in.cipher &&
+            (ssh2_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
             s->in.mac && !s->in.etm_mode) {
             /*
              * When dealing with a CBC-mode cipher, we want to avoid the
@@ -206,9 +208,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                                   s->bpp.in_raw, s->buf, s->maclen));
 
             s->packetlen = 0;
-            s->in.mac->start(s->in.mac_ctx);
-            s->sc_mac_bs = s->in.mac->sink(s->in.mac_ctx);
-            put_uint32(s->sc_mac_bs, s->in.sequence);
+            ssh2_mac_start(s->in.mac);
+            put_uint32(s->in.mac, s->in.sequence);
 
             for (;;) { /* Once around this loop per cipher block. */
                 /* Read another cipher-block's worth, and tack it on to
@@ -219,18 +220,16 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                                       s->cipherblk));
                 /* Decrypt one more block (a little further back in
                  * the stream). */
-                s->in.cipher->decrypt(
-                    s->in.cipher_ctx,
-                    s->buf + s->packetlen, s->cipherblk);
+                ssh2_cipher_decrypt(s->in.cipher,
+                                    s->buf + s->packetlen, s->cipherblk);
 
                 /* Feed that block to the MAC. */
-                put_data(s->sc_mac_bs,
+                put_data(s->in.mac,
                          s->buf + s->packetlen, s->cipherblk);
                 s->packetlen += s->cipherblk;
 
                 /* See if that gives us a valid packet. */
-                if (s->in.mac->verresult(
-                        s->in.mac_ctx, s->buf + s->packetlen) &&
+                if (ssh2_mac_verresult(s->in.mac, s->buf + s->packetlen) &&
                     ((s->len = toint(GET_32BIT(s->buf))) ==
                      s->packetlen-4))
                     break;
@@ -265,13 +264,13 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                                   s->bpp.in_raw, s->buf, 4));
 
             /* Cipher supports length decryption, so do it */
-            if (s->in.cipher &&
-                (s->in.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+            if (s->in.cipher && (ssh2_cipher_alg(s->in.cipher)->flags &
+                                 SSH_CIPHER_SEPARATE_LENGTH)) {
                 /* Keep the packet the same though, so the MAC passes */
                 unsigned char len[4];
                 memcpy(len, s->buf, 4);
-                s->in.cipher->decrypt_length(
-                    s->in.cipher_ctx, len, 4, s->in.sequence);
+                ssh2_cipher_decrypt_length(
+                    s->in.cipher, len, 4, s->in.sequence);
                 s->len = toint(GET_32BIT(len));
             } else {
                 s->len = toint(GET_32BIT(s->buf));
@@ -313,16 +312,16 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
             /*
              * Check the MAC.
              */
-            if (s->in.mac && !s->in.mac->verify(
-                    s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+            if (s->in.mac && !ssh2_mac_verify(
+                    s->in.mac, s->data, s->len + 4, s->in.sequence)) {
                 s->bpp.error = dupprintf("Incorrect MAC received on packet");
                 crStopV;
             }
 
             /* Decrypt everything between the length field and the MAC. */
             if (s->in.cipher)
-                s->in.cipher->decrypt(
-                    s->in.cipher_ctx, s->data + 4, s->packetlen - 4);
+                ssh2_cipher_decrypt(
+                    s->in.cipher, s->data + 4, s->packetlen - 4);
         } else {
             if (s->bufsize < s->cipherblk) {
                 s->bufsize = s->cipherblk;
@@ -337,8 +336,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                                   s->bpp.in_raw, s->buf, s->cipherblk));
 
             if (s->in.cipher)
-                s->in.cipher->decrypt(
-                    s->in.cipher_ctx, s->buf, s->cipherblk);
+                ssh2_cipher_decrypt(
+                    s->in.cipher, s->buf, s->cipherblk);
 
             /*
              * Now get the length figure.
@@ -381,15 +380,15 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
 
             /* Decrypt everything _except_ the MAC. */
             if (s->in.cipher)
-                s->in.cipher->decrypt(
-                    s->in.cipher_ctx,
+                ssh2_cipher_decrypt(
+                    s->in.cipher,
                     s->data + s->cipherblk, s->packetlen - s->cipherblk);
 
             /*
              * Check the MAC.
              */
-            if (s->in.mac && !s->in.mac->verify(
-                    s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+            if (s->in.mac && !ssh2_mac_verify(
+                    s->in.mac, s->data, s->len + 4, s->in.sequence)) {
                 s->bpp.error = dupprintf("Incorrect MAC received on packet");
                 crStopV;
             }
@@ -524,7 +523,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
                    pkt->downstream_id, pkt->additional_log_text);
     }
 
-    cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
+    cipherblk = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 8;
     cipherblk = cipherblk < 8 ? 8 : cipherblk;  /* or 8 if blksize < 8 */
 
     if (s->out.comp && s->out.comp_ctx) {
@@ -541,7 +540,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
              * make the overall packet length come to pkt->minlen.
              */
             if (s->out.mac)
-                minlen -= s->out.mac->len;
+                minlen -= ssh2_mac_alg(s->out.mac)->len;
             minlen -= 8;              /* length field + min padding */
         }
 
@@ -564,7 +563,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
         (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
         % cipherblk;
     assert(padding <= 255);
-    maclen = s->out.mac ? s->out.mac->len : 0;
+    maclen = s->out.mac ? ssh2_mac_alg(s->out.mac)->len : 0;
     origlen = pkt->length;
     for (i = 0; i < padding; i++)
         put_byte(pkt, random_byte());
@@ -573,9 +572,9 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
 
     /* Encrypt length if the scheme requires it */
     if (s->out.cipher &&
-        (s->out.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
-        s->out.cipher->encrypt_length(s->out.cipher_ctx, pkt->data, 4,
-                                      s->out.sequence);
+        (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+        ssh2_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
+                                   s->out.sequence);
     }
 
     put_padding(pkt, maclen, 0);
@@ -585,21 +584,19 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
          * OpenSSH-defined encrypt-then-MAC protocol.
          */
         if (s->out.cipher)
-            s->out.cipher->encrypt(s->out.cipher_ctx,
-                                   pkt->data + 4, origlen + padding - 4);
-        s->out.mac->generate(s->out.mac_ctx, pkt->data, origlen + padding,
-                             s->out.sequence);
+            ssh2_cipher_encrypt(s->out.cipher,
+                                pkt->data + 4, origlen + padding - 4);
+        ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
+                          s->out.sequence);
     } else {
         /*
          * SSH-2 standard protocol.
          */
         if (s->out.mac)
-            s->out.mac->generate(
-                s->out.mac_ctx, pkt->data, origlen + padding,
-                s->out.sequence);
+            ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
+                              s->out.sequence);
         if (s->out.cipher)
-            s->out.cipher->encrypt(s->out.cipher_ctx,
-                                   pkt->data, origlen + padding);
+            ssh2_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
     }
 
     s->out.sequence++;       /* whether or not we MACed */
@@ -634,7 +631,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
         int block, length;
         PktOut *ignore_pkt;
 
-        block = s->out.cipher ? s->out.cipher->blksize : 0;
+        block = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 0;
         if (block < 8)
             block = 8;
         length = pkt->length;
@@ -642,7 +639,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
         length += block-1;
         length -= (length % block);
         if (s->out.mac)
-            length += s->out.mac->len;
+            length += ssh2_mac_alg(s->out.mac)->len;
 
         if (length < pkt->minlen) {
             /*
@@ -655,7 +652,7 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
              * contained string.
              */
             if (s->out.mac)
-                length -= s->out.mac->len;
+                length -= ssh2_mac_alg(s->out.mac)->len;
             length -= 8;               /* length field + min padding */
             length -= 5;               /* type code + string length prefix */
 

+ 82 - 39
source/putty/sshaes.c

@@ -970,38 +970,35 @@ static void aes_decrypt_cbc_sw(unsigned char *blk, int len, AESContext * ctx)
     memcpy(ctx->iv, iv, sizeof(iv));
 }
 
-void *aes_make_context(void)
+AESContext *aes_make_context(void)
 {
     return snew(AESContext);
 }
 
-void aes_free_context(void *handle)
+void aes_free_context(AESContext *ctx)
 {
-    sfree(handle);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-void aes128_key(void *handle, const void *key)
+void aes128_key(AESContext *ctx, const void *key)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, key, 16);
 }
 
-void aes192_key(void *handle, const void *key)
+void aes192_key(AESContext *ctx, const void *key)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, key, 24);
 }
 
-void aes256_key(void *handle, const void *key)
+void aes256_key(AESContext *ctx, const void *key)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, key, 32);
 }
 
-void aes_iv(void *handle, const void *viv)
+void aes_iv(AESContext *ctx, const void *viv)
 {
     const unsigned char *iv = (const unsigned char *)viv;
-    AESContext *ctx = (AESContext *)handle;
     if (ctx->isNI) {
         memcpy(ctx->iv, iv, sizeof(ctx->iv));
     }
@@ -1012,21 +1009,18 @@ void aes_iv(void *handle, const void *viv)
     }
 }
 
-void aes_ssh2_encrypt_blk(void *handle, void *blk, int len)
+void aes_ssh2_encrypt_blk(AESContext *ctx, void *blk, int len)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_encrypt_cbc(blk, len, ctx);
 }
 
-void aes_ssh2_decrypt_blk(void *handle, void *blk, int len)
+void aes_ssh2_decrypt_blk(AESContext *ctx, void *blk, int len)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_decrypt_cbc(blk, len, ctx);
 }
 
-void aes_ssh2_sdctr(void *handle, void *blk, int len)
+void aes_ssh2_sdctr(AESContext *ctx, void *blk, int len)
 {
-    AESContext *ctx = (AESContext *)handle;
     aes_sdctr(blk, len, ctx);
 }
 
@@ -1048,63 +1042,112 @@ void aes256_decrypt_pubkey(const void *key, void *blk, int len)
     smemclr(&ctx, sizeof(ctx));
 }
 
-static const struct ssh2_cipher ssh_aes128_ctr = {
-    aes_make_context, aes_free_context, aes_iv, aes128_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+struct aes_ssh2_ctx {
+    AESContext context;
+    ssh2_cipher vt;
+};
+
+ssh2_cipher *aes_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+    struct aes_ssh2_ctx *ctx = snew(struct aes_ssh2_ctx);
+    ctx->vt = alg;
+    return &ctx->vt;
+}
+
+static void aes_ssh2_free(ssh2_cipher *cipher)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
+}
+
+static void aes_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    aes_iv(&ctx->context, iv);
+}
+
+static void aes_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    aes_setup(&ctx->context, key, ctx->vt->padded_keybytes);
+}
+
+static void aes_ssh2_encrypt(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    aes_encrypt_cbc(blk, len, &ctx->context);
+}
+
+static void aes_ssh2_decrypt(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    aes_decrypt_cbc(blk, len, &ctx->context);
+}
+
+static void aes_ssh2_sdctr_method(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct aes_ssh2_ctx *ctx = FROMFIELD(cipher, struct aes_ssh2_ctx, vt);
+    aes_sdctr(blk, len, &ctx->context);
+}
+
+static const struct ssh2_cipheralg ssh_aes128_ctr = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
     "aes128-ctr",
     16, 128, 16, 0, "AES-128 SDCTR",
     NULL
 };
 
-static const struct ssh2_cipher ssh_aes192_ctr = {
-    aes_make_context, aes_free_context, aes_iv, aes192_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes192_ctr = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
     "aes192-ctr",
     16, 192, 24, 0, "AES-192 SDCTR",
     NULL
 };
 
-static const struct ssh2_cipher ssh_aes256_ctr = {
-    aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes256_ctr = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_sdctr_method, aes_ssh2_sdctr_method, NULL, NULL,
     "aes256-ctr",
     16, 256, 32, 0, "AES-256 SDCTR",
     NULL
 };
 
-static const struct ssh2_cipher ssh_aes128 = {
-    aes_make_context, aes_free_context, aes_iv, aes128_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes128 = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
     "aes128-cbc",
     16, 128, 16, SSH_CIPHER_IS_CBC, "AES-128 CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_aes192 = {
-    aes_make_context, aes_free_context, aes_iv, aes192_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes192 = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
     "aes192-cbc",
     16, 192, 24, SSH_CIPHER_IS_CBC, "AES-192 CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_aes256 = {
-    aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_aes256 = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
     "aes256-cbc",
     16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_rijndael_lysator = {
-    aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
+static const struct ssh2_cipheralg ssh_rijndael_lysator = {
+    aes_ssh2_new, aes_ssh2_free, aes_ssh2_setiv, aes_ssh2_setkey,
+    aes_ssh2_encrypt, aes_ssh2_decrypt, NULL, NULL,
     "[email protected]",
     16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
     NULL
 };
 
-static const struct ssh2_cipher *const aes_list[] = {
+static const struct ssh2_cipheralg *const aes_list[] = {
     &ssh_aes256_ctr,
     &ssh_aes256,
     &ssh_rijndael_lysator,

+ 24 - 20
source/putty/ssharcf.c

@@ -9,6 +9,7 @@
 
 typedef struct {
     unsigned char i, j, s[256];
+    ssh2_cipher vt;
 } ArcfourContext;
 
 static void arcfour_block(void *handle, void *vblk, int len)
@@ -61,14 +62,18 @@ static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key,
  * to leak data about the key.
  */
 
-static void *arcfour_make_context(void)
+static ssh2_cipher *arcfour_new(const struct ssh2_cipheralg *alg)
 {
-    return snew(ArcfourContext);
+    ArcfourContext *ctx = snew(ArcfourContext);
+    ctx->vt = alg;
+    return &ctx->vt;
 }
 
-static void arcfour_free_context(void *handle)
+static void arcfour_free(ssh2_cipher *cipher)
 {
-    sfree(handle);
+    ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
 static void arcfour_stir(ArcfourContext *ctx)
@@ -80,42 +85,41 @@ static void arcfour_stir(ArcfourContext *ctx)
     sfree(junk);
 }
 
-static void arcfour128_key(void *handle, const void *key)
+static void arcfour_ssh2_setiv(ssh2_cipher *cipher, const void *key)
 {
-    ArcfourContext *ctx = (ArcfourContext *)handle;
-    arcfour_setkey(ctx, key, 16);
-    arcfour_stir(ctx);
+    /* As a pure stream cipher, Arcfour has no IV separate from the key */
 }
 
-static void arcfour256_key(void *handle, const void *key)
+static void arcfour_ssh2_setkey(ssh2_cipher *cipher, const void *key)
 {
-    ArcfourContext *ctx = (ArcfourContext *)handle;
-    arcfour_setkey(ctx, key, 32);
+    ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+    arcfour_setkey(ctx, key, ctx->vt->padded_keybytes);
     arcfour_stir(ctx);
 }
 
-static void arcfour_iv(void *handle, const void *iv)
+static void arcfour_ssh2_block(ssh2_cipher *cipher, void *blk, int len)
 {
-
+    ArcfourContext *ctx = FROMFIELD(cipher, ArcfourContext, vt);
+    arcfour_block(ctx, blk, len);
 }
 
-const struct ssh2_cipher ssh_arcfour128_ssh2 = {
-    arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour128_key,
-    arcfour_block, arcfour_block, NULL, NULL,
+const struct ssh2_cipheralg ssh_arcfour128_ssh2 = {
+    arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
+    arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
     "arcfour128",
     1, 128, 16, 0, "Arcfour-128",
     NULL
 };
 
-const struct ssh2_cipher ssh_arcfour256_ssh2 = {
-    arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour256_key,
-    arcfour_block, arcfour_block, NULL, NULL,
+const struct ssh2_cipheralg ssh_arcfour256_ssh2 = {
+    arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
+    arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
     "arcfour256",
     1, 256, 32, 0, "Arcfour-256",
     NULL
 };
 
-static const struct ssh2_cipher *const arcfour_list[] = {
+static const struct ssh2_cipheralg *const arcfour_list[] = {
     &ssh_arcfour256_ssh2,
     &ssh_arcfour128_ssh2,
 };

+ 89 - 45
source/putty/sshblowf.c

@@ -554,106 +554,150 @@ static void blowfish_setkey(BlowfishContext *ctx,
 
 /* -- Interface with PuTTY -- */
 
-#define SSH_SESSION_KEY_LENGTH	32
+#define SSH1_SESSION_KEY_LENGTH 32
 
-void *blowfish_make_context(void)
+BlowfishContext *blowfish_make_context(void)
 {
     return snew(BlowfishContext);
 }
 
-static void *blowfish_ssh1_make_context(void)
+void blowfish_free_context(BlowfishContext *ctx)
 {
+    sfree(ctx);
+}
+
+static void blowfish_iv(BlowfishContext *ctx, const void *viv)
+{
+    const unsigned char *iv = (const unsigned char *)viv;
+    ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
+    ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
+}
+
+struct blowfish_ssh1_ctx {
     /* In SSH-1, need one key for each direction */
-    return snewn(2, BlowfishContext);
+    BlowfishContext contexts[2];
+    ssh1_cipher vt;
+};
+
+static ssh1_cipher *blowfish_ssh1_new(void)
+{
+    struct blowfish_ssh1_ctx *ctx = snew(struct blowfish_ssh1_ctx);
+    ctx->vt = &ssh1_blowfish;
+    return &ctx->vt;
 }
 
-void blowfish_free_context(void *handle)
+static void blowfish_ssh1_free(ssh1_cipher *cipher)
 {
-    sfree(handle);
+    struct blowfish_ssh1_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void blowfish_key(void *handle, const void *key)
+static void blowfish_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_setkey(ctx, key, 16);
+    struct blowfish_ssh1_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+    blowfish_setkey(&ctx->contexts[0], key, SSH1_SESSION_KEY_LENGTH);
+    ctx->contexts[0].iv0 = ctx->contexts[0].iv1 = 0;
+    ctx->contexts[1] = ctx->contexts[0]; /* structure copy */
 }
 
-static void blowfish256_key(void *handle, const void *key)
+static void blowfish_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_setkey(ctx, key, 32);
+    struct blowfish_ssh1_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+    blowfish_lsb_encrypt_cbc(blk, len, ctx->contexts);
 }
 
-static void blowfish_iv(void *handle, const void *viv)
+static void blowfish_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
 {
-    const unsigned char *iv = (const unsigned char *)viv;
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
-    ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
+    struct blowfish_ssh1_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt);
+    blowfish_lsb_decrypt_cbc(blk, len, ctx->contexts+1);
+}
+
+struct blowfish_ssh2_ctx {
+    BlowfishContext context;
+    ssh2_cipher vt;
+};
+
+static ssh2_cipher *blowfish_ssh2_new(const struct ssh2_cipheralg *alg)
+{
+    struct blowfish_ssh2_ctx *ctx = snew(struct blowfish_ssh2_ctx);
+    ctx->vt = alg;
+    return &ctx->vt;
 }
 
-static void blowfish_sesskey(void *handle, const void *key)
+static void blowfish_ssh2_free(ssh2_cipher *cipher)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_setkey(ctx, key, SSH_SESSION_KEY_LENGTH);
-    ctx->iv0 = 0;
-    ctx->iv1 = 0;
-    ctx[1] = ctx[0];		       /* structure copy */
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void blowfish_ssh1_encrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_lsb_encrypt_cbc(blk, len, ctx);
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    blowfish_iv(&ctx->context, iv);
 }
 
-static void blowfish_ssh1_decrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh2_setkey(ssh2_cipher *cipher, const void *key)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_lsb_decrypt_cbc(blk, len, ctx+1);
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    blowfish_setkey(&ctx->context, key, ctx->vt->padded_keybytes);
 }
 
-static void blowfish_ssh2_encrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_msb_encrypt_cbc(blk, len, ctx);
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    blowfish_msb_encrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void blowfish_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_msb_decrypt_cbc(blk, len, ctx);
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    blowfish_msb_decrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_sdctr(void *handle, void *blk, int len)
+static void blowfish_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
 {
-    BlowfishContext *ctx = (BlowfishContext *)handle;
-    blowfish_msb_sdctr(blk, len, ctx);
+    struct blowfish_ssh2_ctx *ctx =
+        FROMFIELD(cipher, struct blowfish_ssh2_ctx, vt);
+    blowfish_msb_sdctr(blk, len, &ctx->context);
 }
 
-const struct ssh_cipher ssh_blowfish_ssh1 = {
-    blowfish_ssh1_make_context, blowfish_free_context, blowfish_sesskey,
+const struct ssh1_cipheralg ssh1_blowfish = {
+    blowfish_ssh1_new, blowfish_ssh1_free,
+    blowfish_ssh1_sesskey,
     blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk,
     8, "Blowfish-128 CBC"
 };
 
-static const struct ssh2_cipher ssh_blowfish_ssh2 = {
-    blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish_key,
+static const struct ssh2_cipheralg ssh_blowfish_ssh2 = {
+    blowfish_ssh2_new, blowfish_ssh2_free,
+    blowfish_ssh2_setiv, blowfish_ssh2_setkey,
     blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk, NULL, NULL,
     "blowfish-cbc",
     8, 128, 16, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_blowfish_ssh2_ctr = {
-    blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish256_key,
+static const struct ssh2_cipheralg ssh_blowfish_ssh2_ctr = {
+    blowfish_ssh2_new, blowfish_ssh2_free,
+    blowfish_ssh2_setiv, blowfish_ssh2_setkey,
     blowfish_ssh2_sdctr, blowfish_ssh2_sdctr, NULL, NULL,
     "blowfish-ctr",
     8, 256, 32, 0, "Blowfish-256 SDCTR",
     NULL
 };
 
-static const struct ssh2_cipher *const blowfish_list[] = {
+static const struct ssh2_cipheralg *const blowfish_list[] = {
     &ssh_blowfish_ssh2_ctr,
     &ssh_blowfish_ssh2
 };

+ 2 - 2
source/putty/sshblowf.h

@@ -5,8 +5,8 @@
 
 typedef struct BlowfishContext BlowfishContext;
 
-void *blowfish_make_context(void);
-void blowfish_free_context(void *handle);
+BlowfishContext *blowfish_make_context(void);
+void blowfish_free_context(BlowfishContext *ctx);
 void blowfish_initkey(BlowfishContext *ctx);
 void blowfish_expandkey(BlowfishContext *ctx,
                         const void *key, short keybytes,

+ 5 - 5
source/putty/sshbpp.h

@@ -32,20 +32,20 @@ struct BinaryPacketProtocol {
 
 BinaryPacketProtocol *ssh1_bpp_new(void);
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
-                         const struct ssh_cipher *cipher,
+                         const struct ssh1_cipheralg *cipher,
                          const void *session_key);
 void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
 
 BinaryPacketProtocol *ssh2_bpp_new(void);
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
-    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
-    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
     const struct ssh_compress *compression);
 void ssh2_bpp_new_incoming_crypto(
     BinaryPacketProtocol *bpp,
-    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
-    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const struct ssh2_macalg *mac, int etm_mode, const void *mac_key,
     const struct ssh_compress *compression);
 
 BinaryPacketProtocol *ssh2_bare_bpp_new(void);

+ 39 - 80
source/putty/sshccp.c

@@ -20,7 +20,7 @@
  * This has an intricate link between the cipher and the MAC. The
  * keying of both is done in by the cipher and setting of the IV is
  * done by the MAC. One cannot operate without the other. The
- * configuration of the ssh2_cipher structure ensures that the MAC is
+ * configuration of the ssh2_cipheralg structure ensures that the MAC is
  * set (and others ignored) if this cipher is chosen.
  *
  * This cipher also encrypts the length using a different
@@ -866,26 +866,32 @@ struct ccp_context {
     struct poly1305 mac;
 
     BinarySink_IMPLEMENTATION;
+    ssh2_cipher cvt;
+    ssh2_mac mac_if;
 };
 
-static void *poly_make_context(void *ctx)
+static ssh2_mac *poly_ssh2_new(
+    const struct ssh2_macalg *alg, ssh2_cipher *cipher)
 {
-    return ctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
+    ctx->mac_if.vt = alg;
+    BinarySink_DELEGATE_INIT(&ctx->mac_if, ctx);
+    return &ctx->mac_if;
 }
 
-static void poly_free_context(void *ctx)
+static void poly_ssh2_free(ssh2_mac *mac)
 {
     /* Not allocated, just forwarded, no need to free */
 }
 
-static void poly_setkey(void *ctx, const void *key)
+static void poly_setkey(ssh2_mac *mac, const void *key)
 {
     /* Uses the same context as ChaCha20, so ignore */
 }
 
-static void poly_start(void *handle)
+static void poly_start(ssh2_mac *mac)
 {
-    struct ccp_context *ctx = (struct ccp_context *)handle;
+    struct ccp_context *ctx = FROMFIELD(mac, struct ccp_context, mac_if);
 
     ctx->mac_initialised = 0;
     memset(ctx->mac_iv, 0, 8);
@@ -925,110 +931,63 @@ static void poly_BinarySink_write(BinarySink *bs, const void *blkv, size_t len)
     }
 }
 
-static BinarySink *poly_sink(void *handle)
+static void poly_genresult(ssh2_mac *mac, unsigned char *blk)
 {
-    struct ccp_context *ctx = (struct ccp_context *)handle;
-    return BinarySink_UPCAST(ctx);
-}
-
-static void poly_genresult(void *handle, unsigned char *blk)
-{
-    struct ccp_context *ctx = (struct ccp_context *)handle;
+    struct ccp_context *ctx = FROMFIELD(mac, struct ccp_context, mac_if);
     poly1305_finalise(&ctx->mac, blk);
 }
 
-static int poly_verresult(void *handle, unsigned char const *blk)
-{
-    struct ccp_context *ctx = (struct ccp_context *)handle;
-    int res;
-    unsigned char mac[16];
-    poly1305_finalise(&ctx->mac, mac);
-    res = smemeq(blk, mac, 16);
-    return res;
-}
-
-/* The generic poly operation used before generate and verify */
-static void poly_op(void *handle, const unsigned char *blk, int len,
-                    unsigned long seq)
-{
-    struct ccp_context *ctx = (struct ccp_context *)handle;
-    poly_start(ctx);
-    /* the data receiver expects the first 4 bytes to be the IV */
-    put_uint32(ctx, seq);
-    put_data(ctx, blk, len);
-}
-
-static void poly_generate(void *handle, void *vblk, int len, unsigned long seq)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    poly_op(handle, blk, len, seq);
-    poly_genresult(handle, blk+len);
-}
-
-static int poly_verify(void *handle, const void *vblk, int len,
-                       unsigned long seq)
-{
-    const unsigned char *blk = (const unsigned char *)vblk;
-    poly_op(handle, blk, len, seq);
-    return poly_verresult(handle, blk+len);
-}
-
-static const struct ssh_mac ssh2_poly1305 = {
-    poly_make_context, poly_free_context,
-    poly_setkey,
-
-    /* whole-packet operations */
-    poly_generate, poly_verify,
-
-    /* partial-packet operations */
-    poly_start, poly_sink, poly_genresult, poly_verresult,
+static const struct ssh2_macalg ssh2_poly1305 = {
+    poly_ssh2_new, poly_ssh2_free, poly_setkey,
+    poly_start, poly_genresult,
 
     "", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */
     16, 0, "Poly1305"
 };
 
-static void *ccp_make_context(void)
+static ssh2_cipher *ccp_new(const struct ssh2_cipheralg *alg)
 {
     struct ccp_context *ctx = snew(struct ccp_context);
     BinarySink_INIT(ctx, poly_BinarySink_write);
     poly1305_init(&ctx->mac);
-    return ctx;
+    ctx->cvt = alg;
+    return &ctx->cvt;
 }
 
-static void ccp_free_context(void *vctx)
+static void ccp_free(ssh2_cipher *cipher)
 {
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
     smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher));
     smemclr(&ctx->mac, sizeof(ctx->mac));
     sfree(ctx);
 }
 
-static void ccp_iv(void *vctx, const void *iv)
+static void ccp_iv(ssh2_cipher *cipher, const void *iv)
 {
-    /* struct ccp_context *ctx = (struct ccp_context *)vctx; */
+    /* struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt); */
     /* IV is set based on the sequence number */
 }
 
-static void ccp_key(void *vctx, const void *vkey)
+static void ccp_key(ssh2_cipher *cipher, const void *vkey)
 {
     const unsigned char *key = (const unsigned char *)vkey;
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     /* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
     chacha20_key(&ctx->a_cipher, key + 32);
     /* Initialise the b_cipher (for content and MAC) with the second 256 bits */
     chacha20_key(&ctx->b_cipher, key);
 }
 
-static void ccp_encrypt(void *vctx, void *blk, int len)
+static void ccp_encrypt(ssh2_cipher *cipher, void *blk, int len)
 {
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     chacha20_encrypt(&ctx->b_cipher, blk, len);
 }
 
-static void ccp_decrypt(void *vctx, void *blk, int len)
+static void ccp_decrypt(ssh2_cipher *cipher, void *blk, int len)
 {
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     chacha20_decrypt(&ctx->b_cipher, blk, len);
 }
 
@@ -1049,26 +1008,26 @@ static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
     smemclr(iv, sizeof(iv));
 }
 
-static void ccp_encrypt_length(void *vctx, void *blk, int len,
+static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
                                unsigned long seq)
 {
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     ccp_length_op(ctx, blk, len, seq);
     chacha20_encrypt(&ctx->a_cipher, blk, len);
 }
 
-static void ccp_decrypt_length(void *vctx, void *blk, int len,
+static void ccp_decrypt_length(ssh2_cipher *cipher, void *blk, int len,
                                unsigned long seq)
 {
-    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    struct ccp_context *ctx = FROMFIELD(cipher, struct ccp_context, cvt);
     ccp_length_op(ctx, blk, len, seq);
     chacha20_decrypt(&ctx->a_cipher, blk, len);
 }
 
-static const struct ssh2_cipher ssh2_chacha20_poly1305 = {
+static const struct ssh2_cipheralg ssh2_chacha20_poly1305 = {
 
-    ccp_make_context,
-    ccp_free_context,
+    ccp_new,
+    ccp_free,
     ccp_iv,
     ccp_key,
     ccp_encrypt,
@@ -1082,7 +1041,7 @@ static const struct ssh2_cipher ssh2_chacha20_poly1305 = {
     &ssh2_poly1305
 };
 
-static const struct ssh2_cipher *const ccp_list[] = {
+static const struct ssh2_cipheralg *const ccp_list[] = {
     &ssh2_chacha20_poly1305
 };
 

+ 165 - 91
source/putty/sshdes.c

@@ -747,108 +747,201 @@ static void des_sdctr3(unsigned char *blk,
     scheds->iv1 = iv1;
 }
 
-static void *des3_make_context(void)
+static void des3_key(DESContext *contexts, const void *vkey)
 {
-    return snewn(3, DESContext);
+    const unsigned char *key = (const unsigned char *)vkey;
+    des_key_setup(GET_32BIT_MSB_FIRST(key),
+                  GET_32BIT_MSB_FIRST(key + 4), &contexts[0]);
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
+                  GET_32BIT_MSB_FIRST(key + 12), &contexts[1]);
+    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
+                  GET_32BIT_MSB_FIRST(key + 20), &contexts[2]);
 }
 
-static void *des3_ssh1_make_context(void)
+static void des_iv(DESContext *context, const void *viv)
 {
-    /* Need 3 keys for each direction, in SSH-1 */
-    return snewn(6, DESContext);
+    const unsigned char *iv = (const unsigned char *)viv;
+    context->iv0 = GET_32BIT_MSB_FIRST(iv);
+    context->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
 }
 
-static void *des_make_context(void)
+static void des_key(DESContext *context, const void *vkey)
 {
-    return snew(DESContext);
+    const unsigned char *key = (const unsigned char *)vkey;
+    des_key_setup(GET_32BIT_MSB_FIRST(key),
+                  GET_32BIT_MSB_FIRST(key + 4), context);
 }
 
-static void *des_ssh1_make_context(void)
+struct des3_ssh1_ctx {
+    /* 3 cipher context for each direction */
+    DESContext contexts[6];
+    ssh1_cipher vt;
+};
+
+struct des_ssh1_ctx {
+    /* 1 cipher context for each direction */
+    DESContext contexts[2];
+    ssh1_cipher vt;
+};
+
+static ssh1_cipher *des3_ssh1_new(void)
 {
-    /* Need one key for each direction, in SSH-1 */
-    return snewn(2, DESContext);
+    struct des3_ssh1_ctx *ctx = snew(struct des3_ssh1_ctx);
+    ctx->vt = &ssh1_3des;
+    return &ctx->vt;
 }
 
-static void des3_free_context(void *handle)   /* used for both 3DES and DES */
+static ssh1_cipher *des_ssh1_new(void)
 {
-    sfree(handle);
+    struct des_ssh1_ctx *ctx = snew(struct des_ssh1_ctx);
+    ctx->vt = &ssh1_des;
+    return &ctx->vt;
 }
 
-static void des3_key(void *handle, const void *vkey)
+static void des3_ssh1_free(ssh1_cipher *cipher)
 {
-    const unsigned char *key = (const unsigned char *)vkey;
-    DESContext *keys = (DESContext *) handle;
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
-		  GET_32BIT_MSB_FIRST(key + 12), &keys[1]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
-		  GET_32BIT_MSB_FIRST(key + 20), &keys[2]);
+    struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void des3_iv(void *handle, const void *viv)
+static void des_ssh1_free(ssh1_cipher *cipher)
 {
-    const unsigned char *iv = (const unsigned char *)viv;
-    DESContext *keys = (DESContext *) handle;
-    keys[0].iv0 = GET_32BIT_MSB_FIRST(iv);
-    keys[0].iv1 = GET_32BIT_MSB_FIRST(iv + 4);
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void des_key(void *handle, const void *vkey)
+static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
 {
-    const unsigned char *key = (const unsigned char *)vkey;
-    DESContext *keys = (DESContext *) handle;
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
+    struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
+    des3_key(ctx->contexts, key);
+    des3_key(ctx->contexts+3, key);
+}
+
+static void des3_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+{
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    des_3cbc_encrypt(blk, len, ctx->contexts);
+}
+
+static void des3_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+{
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    des_3cbc_decrypt(blk, len, ctx->contexts+3);
 }
 
-static void des3_sesskey(void *handle, const void *key)
+static void des_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
 {
-    DESContext *keys = (DESContext *) handle;
-    des3_key(keys, key);
-    des3_key(keys+3, key);
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    des_key(ctx->contexts, key);
+    des_key(ctx->contexts+1, key);
 }
 
-static void des3_encrypt_blk(void *handle, void *blk, int len)
+static void des_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_3cbc_encrypt(blk, len, keys);
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    des_cbc_encrypt(blk, len, ctx->contexts);
 }
 
-static void des3_decrypt_blk(void *handle, void *blk, int len)
+static void des_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+{
+    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    des_cbc_decrypt(blk, len, ctx->contexts+1);
+}
+
+struct des3_ssh2_ctx {
+    DESContext contexts[3];
+    ssh2_cipher vt;
+};
+
+struct des_ssh2_ctx {
+    DESContext context;
+    ssh2_cipher vt;
+};
+
+static ssh2_cipher *des3_ssh2_new(const struct ssh2_cipheralg *alg)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_3cbc_decrypt(blk, len, keys+3);
+    struct des3_ssh2_ctx *ctx = snew(struct des3_ssh2_ctx);
+    ctx->vt = alg;
+    return &ctx->vt;
 }
 
-static void des3_ssh2_encrypt_blk(void *handle, void *blk, int len)
+static ssh2_cipher *des_ssh2_new(const struct ssh2_cipheralg *alg)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_cbc3_encrypt(blk, len, keys);
+    struct des_ssh2_ctx *ctx = snew(struct des_ssh2_ctx);
+    ctx->vt = alg;
+    return &ctx->vt;
 }
 
-static void des3_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh2_free(ssh2_cipher *cipher)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_cbc3_decrypt(blk, len, keys);
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void des3_ssh2_sdctr(void *handle, void *blk, int len)
+static void des_ssh2_free(ssh2_cipher *cipher)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_sdctr3(blk, len, keys);
+    struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void des_ssh2_encrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_cbc_encrypt(blk, len, keys);
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    des_iv(&ctx->contexts[0], iv);
+    /* SSH-2 treats triple-DES as a single block cipher to wrap in
+     * CBC, so there's only one IV required, not three */
 }
 
-static void des_ssh2_decrypt_blk(void *handle, void *blk, int len)
+static void des3_ssh2_setkey(ssh2_cipher *cipher, const void *key)
 {
-    DESContext *keys = (DESContext *) handle;
-    des_cbc_decrypt(blk, len, keys);
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    des3_key(ctx->contexts, key);
+}
+
+static void des_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+{
+    struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+    des_iv(&ctx->context, iv);
+}
+
+static void des_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+{
+    struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+    des_key(&ctx->context, key);
+}
+
+static void des3_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    des_cbc3_encrypt(blk, len, ctx->contexts);
+}
+
+static void des3_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    des_cbc3_decrypt(blk, len, ctx->contexts);
+}
+
+static void des3_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct des3_ssh2_ctx *ctx = FROMFIELD(cipher, struct des3_ssh2_ctx, vt);
+    des_sdctr3(blk, len, ctx->contexts);
+}
+
+static void des_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+    des_cbc_encrypt(blk, len, &ctx->context);
+}
+
+static void des_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+{
+    struct des_ssh2_ctx *ctx = FROMFIELD(cipher, struct des_ssh2_ctx, vt);
+    des_cbc_decrypt(blk, len, &ctx->context);
 }
 
 void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
@@ -957,16 +1050,16 @@ void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
     des_cbc_decrypt(blk, len, &dc);
 }
 
-static const struct ssh2_cipher ssh_3des_ssh2 = {
-    des3_make_context, des3_free_context, des3_iv, des3_key,
+static const struct ssh2_cipheralg ssh_3des_ssh2 = {
+    des3_ssh2_new, des3_ssh2_free, des3_ssh2_setiv, des3_ssh2_setkey,
     des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk, NULL, NULL,
     "3des-cbc",
     8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
-    des3_make_context, des3_free_context, des3_iv, des3_key,
+static const struct ssh2_cipheralg ssh_3des_ssh2_ctr = {
+    des3_ssh2_new, des3_ssh2_free, des3_ssh2_setiv, des3_ssh2_setkey,
     des3_ssh2_sdctr, des3_ssh2_sdctr, NULL, NULL,
     "3des-ctr",
     8, 168, 24, 0, "triple-DES SDCTR",
@@ -981,23 +1074,23 @@ static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
  * apparently aren't the only people to do so, so we sigh 
  * and implement it anyway.
  */
-static const struct ssh2_cipher ssh_des_ssh2 = {
-    des_make_context, des3_free_context, des3_iv, des_key,
+static const struct ssh2_cipheralg ssh_des_ssh2 = {
+    des_ssh2_new, des_ssh2_free, des_ssh2_setiv, des_ssh2_setkey,
     des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
     "des-cbc",
     8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
     NULL
 };
 
-static const struct ssh2_cipher ssh_des_sshcom_ssh2 = {
-    des_make_context, des3_free_context, des3_iv, des_key,
+static const struct ssh2_cipheralg ssh_des_sshcom_ssh2 = {
+    des_ssh2_new, des_ssh2_free, des_ssh2_setiv, des_ssh2_setkey,
     des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
     "[email protected]",
     8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
     NULL
 };
 
-static const struct ssh2_cipher *const des3_list[] = {
+static const struct ssh2_cipheralg *const des3_list[] = {
     &ssh_3des_ssh2_ctr,
     &ssh_3des_ssh2
 };
@@ -1007,7 +1100,7 @@ const struct ssh2_ciphers ssh2_3des = {
     des3_list
 };
 
-static const struct ssh2_cipher *const des_list[] = {
+static const struct ssh2_cipheralg *const des_list[] = {
     &ssh_des_ssh2,
     &ssh_des_sshcom_ssh2
 };
@@ -1017,34 +1110,15 @@ const struct ssh2_ciphers ssh2_des = {
     des_list
 };
 
-const struct ssh_cipher ssh_3des = {
-    des3_ssh1_make_context, des3_free_context, des3_sesskey,
-    des3_encrypt_blk, des3_decrypt_blk,
+const struct ssh1_cipheralg ssh1_3des = {
+    des3_ssh1_new, des3_ssh1_free, des3_ssh1_sesskey,
+    des3_ssh1_encrypt_blk, des3_ssh1_decrypt_blk,
     8, "triple-DES inner-CBC"
 };
 
-static void des_sesskey(void *handle, const void *key)
-{
-    DESContext *keys = (DESContext *) handle;
-    des_key(keys, key);
-    des_key(keys+1, key);
-}
-
-static void des_encrypt_blk(void *handle, void *blk, int len)
-{
-    DESContext *keys = (DESContext *) handle;
-    des_cbc_encrypt(blk, len, keys);
-}
-
-static void des_decrypt_blk(void *handle, void *blk, int len)
-{
-    DESContext *keys = (DESContext *) handle;
-    des_cbc_decrypt(blk, len, keys+1);
-}
-
-const struct ssh_cipher ssh_des = {
-    des_ssh1_make_context, des3_free_context, des_sesskey,
-    des_encrypt_blk, des_decrypt_blk,
+const struct ssh1_cipheralg ssh1_des = {
+    des_ssh1_new, des_ssh1_free, des_ssh1_sesskey,
+    des_ssh1_encrypt_blk, des_ssh1_decrypt_blk,
     8, "single-DES CBC"
 };
 

+ 9 - 9
source/putty/sshecc.c

@@ -1688,7 +1688,7 @@ static int BinarySource_get_point(BinarySource *src, struct ec_point *point)
 
 struct ecsign_extra {
     struct ec_curve *(*curve)(void);
-    const struct ssh_hash *hash;
+    const struct ssh_hashalg *hash;
 
     /* These fields are used by the OpenSSH PEM format importer/exporter */
     const unsigned char *oid;
@@ -2227,7 +2227,7 @@ static int ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
         Bignum r, s;
         unsigned char digest[512 / 8];
         int digestLen;
-        void *hashctx;
+        ssh_hash *hashctx;
 
         BinarySource_BARE_INIT(src, sigstr.ptr, sigstr.len);
 
@@ -2241,9 +2241,9 @@ static int ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
 
         digestLen = extra->hash->hlen;
         assert(digestLen <= sizeof(digest));
-        hashctx = extra->hash->init();
-        put_data(extra->hash->sink(hashctx), data.ptr, data.len);
-        extra->hash->final(hashctx, digest);
+        hashctx = ssh_hash_new(extra->hash);
+        put_data(hashctx, data.ptr, data.len);
+        ssh_hash_final(hashctx, digest);
 
         /* Verify the signature */
         ret = _ecdsa_verify(&ec->publicKey, digest, digestLen, r, s);
@@ -2363,14 +2363,14 @@ static void ecdsa_sign(ssh_key *key, const void *data, int datalen,
             put_byte(bs, bignum_byte(s, i));
         freebn(s);
     } else {
-        void *hashctx;
+        ssh_hash *hashctx;
         strbuf *substr;
 
         digestLen = extra->hash->hlen;
         assert(digestLen <= sizeof(digest));
-        hashctx = extra->hash->init();
-        put_data(extra->hash->sink(hashctx), data, datalen);
-        extra->hash->final(hashctx, digest);
+        hashctx = ssh_hash_new(extra->hash);
+        put_data(hashctx, data, datalen);
+        ssh_hash_final(hashctx, digest);
 
         /* Do the signature */
         _ecdsa_sign(ec->privateKey, ec->publicKey.curve, digest, digestLen, &r, &s);

+ 42 - 0
source/putty/sshmac.c

@@ -0,0 +1,42 @@
+/*
+ * Centralised parts of the SSH-2 MAC API, which don't need to vary
+ * with the MAC implementation.
+ */
+
+#include <assert.h>
+
+#include "ssh.h"
+
+int ssh2_mac_verresult(ssh2_mac *mac, const void *candidate)
+{
+    unsigned char correct[64]; /* at least as big as all known MACs */
+    int toret;
+
+    assert(mac->vt->len <= sizeof(correct));
+    ssh2_mac_genresult(mac, correct);
+    toret = smemeq(correct, candidate, mac->vt->len);
+
+    smemclr(correct, sizeof(correct));
+
+    return toret;
+}
+
+static void ssh2_mac_prepare(ssh2_mac *mac, const void *blk, int len,
+                             unsigned long seq)
+{
+    ssh2_mac_start(mac);
+    put_uint32(mac, seq);
+    put_data(mac, blk, len);
+}
+
+void ssh2_mac_generate(ssh2_mac *mac, void *blk, int len, unsigned long seq)
+{
+    ssh2_mac_prepare(mac, blk, len, seq);
+    return ssh2_mac_genresult(mac, (unsigned char *)blk + len);
+}
+
+int ssh2_mac_verify(ssh2_mac *mac, const void *blk, int len, unsigned long seq)
+{
+    ssh2_mac_prepare(mac, blk, len, seq);
+    return ssh2_mac_verresult(mac, (const unsigned char *)blk + len);
+}

+ 51 - 78
source/putty/sshmd5.c

@@ -228,20 +228,40 @@ void MD5Simple(void const *p, unsigned len, unsigned char output[16])
  * useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).
  */
 
-void *hmacmd5_make_context(void *cipher_ctx)
+struct hmacmd5_context {
+    struct MD5Context md5[3];
+    ssh2_mac mac;
+};
+
+struct hmacmd5_context *hmacmd5_make_context(void)
+{
+    struct hmacmd5_context *ctx = snew(struct hmacmd5_context);
+    BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->md5[2]);
+    return ctx;
+}
+
+static ssh2_mac *hmacmd5_ssh2_new(const struct ssh2_macalg *alg,
+                                  ssh2_cipher *cipher)
+{
+    struct hmacmd5_context *ctx = hmacmd5_make_context();
+    ctx->mac.vt = alg;
+    return &ctx->mac;
+}
+
+void hmacmd5_free_context(struct hmacmd5_context *ctx)
 {
-    return snewn(3, struct MD5Context);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-void hmacmd5_free_context(void *handle)
+static void hmacmd5_ssh2_free(ssh2_mac *mac)
 {
-    smemclr(handle, 3*sizeof(struct MD5Context));
-    sfree(handle);
+    struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
+    hmacmd5_free_context(ctx);
 }
 
-void hmacmd5_key(void *handle, void const *keyv, int len)
+void hmacmd5_key(struct hmacmd5_context *ctx, void const *keyv, int len)
 {
-    struct MD5Context *keys = (struct MD5Context *)handle;
     unsigned char foo[64];
     unsigned char const *key = (unsigned char const *)keyv;
     int i;
@@ -249,106 +269,59 @@ void hmacmd5_key(void *handle, void const *keyv, int len)
     memset(foo, 0x36, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
-    MD5Init(&keys[0]);
-    put_data(&keys[0], foo, 64);
+    MD5Init(&ctx->md5[0]);
+    put_data(&ctx->md5[0], foo, 64);
 
     memset(foo, 0x5C, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
-    MD5Init(&keys[1]);
-    put_data(&keys[1], foo, 64);
+    MD5Init(&ctx->md5[1]);
+    put_data(&ctx->md5[1], foo, 64);
 
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void hmacmd5_key_16(void *handle, const void *key)
+static void hmacmd5_ssh2_setkey(ssh2_mac *mac, const void *key)
 {
-    hmacmd5_key(handle, key, 16);
+    struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
+    hmacmd5_key(ctx, key, ctx->mac.vt->keylen);
 }
 
-static void hmacmd5_start(void *handle)
+static void hmacmd5_start(ssh2_mac *mac)
 {
-    struct MD5Context *keys = (struct MD5Context *)handle;
-
-    keys[2] = keys[0];		      /* structure copy */
-    BinarySink_COPIED(&keys[2]);
-}
+    struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
 
-static BinarySink *hmacmd5_sink(void *handle)
-{
-    struct MD5Context *keys = (struct MD5Context *)handle;
-    return BinarySink_UPCAST(&keys[2]);
+    ctx->md5[2] = ctx->md5[0]; /* structure copy */
+    BinarySink_COPIED(&ctx->md5[2]);
 }
 
-static void hmacmd5_genresult(void *handle, unsigned char *hmac)
+static void hmacmd5_genresult(ssh2_mac *mac, unsigned char *hmac)
 {
-    struct MD5Context *keys = (struct MD5Context *)handle;
+    struct hmacmd5_context *ctx = FROMFIELD(mac, struct hmacmd5_context, mac);
     struct MD5Context s;
     unsigned char intermediate[16];
 
-    s = keys[2];		       /* structure copy */
+    s = ctx->md5[2];              /* structure copy */
     BinarySink_COPIED(&s);
     MD5Final(intermediate, &s);
-    s = keys[1];		       /* structure copy */
+    s = ctx->md5[1];              /* structure copy */
     BinarySink_COPIED(&s);
     put_data(&s, intermediate, 16);
     MD5Final(hmac, &s);
+    smemclr(intermediate, sizeof(intermediate));
 }
 
-static int hmacmd5_verresult(void *handle, unsigned char const *hmac)
-{
-    unsigned char correct[16];
-    hmacmd5_genresult(handle, correct);
-    return smemeq(correct, hmac, 16);
-}
-
-static void hmacmd5_do_hmac_internal(void *handle,
-				     unsigned char const *blk, int len,
-				     unsigned char const *blk2, int len2,
-				     unsigned char *hmac)
-{
-    BinarySink *bs = hmacmd5_sink(handle);
-    hmacmd5_start(handle);
-    put_data(bs, blk, len);
-    if (blk2) put_data(bs, blk2, len2);
-    hmacmd5_genresult(handle, hmac);
-}
-
-void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
-		     unsigned char *hmac)
-{
-    hmacmd5_do_hmac_internal(handle, blk, len, NULL, 0, hmac);
-}
-
-static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,
-				unsigned long seq, unsigned char *hmac)
-{
-    unsigned char seqbuf[16];
-
-    PUT_32BIT_MSB_FIRST(seqbuf, seq);
-    hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);
-}
-
-static void hmacmd5_generate(void *handle, void *vblk, int len,
-			     unsigned long seq)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);
-}
-
-static int hmacmd5_verify(void *handle, const void *vblk, int len,
-			  unsigned long seq)
+void hmacmd5_do_hmac(struct hmacmd5_context *ctx,
+                     unsigned char const *blk, int len, unsigned char *hmac)
 {
-    const unsigned char *blk = (const unsigned char *)vblk;
-    unsigned char correct[16];
-    hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);
-    return smemeq(correct, blk + len, 16);
+    ssh2_mac_start(&ctx->mac);
+    put_data(&ctx->mac, blk, len);
+    return ssh2_mac_genresult(&ctx->mac, hmac);
 }
 
-const struct ssh_mac ssh_hmac_md5 = {
-    hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
-    hmacmd5_generate, hmacmd5_verify,
-    hmacmd5_start, hmacmd5_sink, hmacmd5_genresult, hmacmd5_verresult,
+const struct ssh2_macalg ssh_hmac_md5 = {
+    hmacmd5_ssh2_new, hmacmd5_ssh2_free, hmacmd5_ssh2_setkey,
+    hmacmd5_start, hmacmd5_genresult,
     "hmac-md5", "[email protected]",
     16, 16,
     "HMAC-MD5"

+ 12 - 10
source/putty/sshrsa.c

@@ -783,7 +783,7 @@ int ssh_rsakex_klen(struct RSAKey *rsa)
     return bignum_bitcount(rsa->modulus);
 }
 
-static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
+static void oaep_mask(const struct ssh_hashalg *h, void *seed, int seedlen,
 		      void *vdata, int datalen)
 {
     unsigned char *data = (unsigned char *)vdata;
@@ -791,16 +791,14 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
 
     while (datalen > 0) {
         int i, max = (datalen > h->hlen ? h->hlen : datalen);
-        void *s;
-        BinarySink *bs;
+        ssh_hash *s;
         unsigned char hash[SSH2_KEX_MAX_HASH_LEN];
 
 	assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);
-        s = h->init();
-        bs = h->sink(s);
-        put_data(bs, seed, seedlen);
-        put_uint32(bs, count);
-        h->final(s, hash);
+        s = ssh_hash_new(h);
+        put_data(s, seed, seedlen);
+        put_uint32(s, count);
+        ssh_hash_final(s, hash);
         count++;
 
         for (i = 0; i < max; i++)
@@ -811,7 +809,8 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
     }
 }
 
-void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
+void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
+                        unsigned char *in, int inlen,
                         unsigned char *out, int outlen, struct RSAKey *rsa)
 {
     Bignum b1, b2;
@@ -866,7 +865,10 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
         out[i + 1] = random_byte();
     /* At position 1+HLEN, the data block DB, consisting of: */
     /* The hash of the label (we only support an empty label here) */
-    h->final(h->init(), out + HLEN + 1);
+    {
+        ssh_hash *s = ssh_hash_new(h);
+        ssh_hash_final(s, out + HLEN + 1);
+    }
     /* A bunch of zero octets */
     memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
     /* A single 1 octet, followed by the input message data. */

+ 64 - 95
source/putty/sshsh256.c

@@ -204,51 +204,51 @@ void SHA256_Simple(const void *p, int len, unsigned char *output) {
  * Thin abstraction for things where hashes are pluggable.
  */
 
-static void *sha256_init(void)
-{
-    SHA256_State *s;
+struct sha256_hash {
+    SHA256_State state;
+    ssh_hash hash;
+};
 
-    s = snew(SHA256_State);
-    SHA256_Init(s);
-    return s;
+static ssh_hash *sha256_new(const struct ssh_hashalg *alg)
+{
+    struct sha256_hash *h = snew(struct sha256_hash);
+    SHA256_Init(&h->state);
+    h->hash.vt = alg;
+    BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+    return &h->hash;
 }
 
-static void *sha256_copy(const void *vold)
+static ssh_hash *sha256_copy(ssh_hash *hashold)
 {
-    const SHA256_State *old = (const SHA256_State *)vold;
-    SHA256_State *s;
+    struct sha256_hash *hold, *hnew;
+    ssh_hash *hashnew = sha256_new(hashold->vt);
 
-    s = snew(SHA256_State);
-    *s = *old;
-    BinarySink_COPIED(s);
-    return s;
-}
+    hold = FROMFIELD(hashold, struct sha256_hash, hash);
+    hnew = FROMFIELD(hashnew, struct sha256_hash, hash);
 
-static void sha256_free(void *handle)
-{
-    SHA256_State *s = handle;
+    hnew->state = hold->state;
+    BinarySink_COPIED(&hnew->state);
 
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    return hashnew;
 }
 
-static BinarySink *sha256_sink(void *handle)
+static void sha256_free(ssh_hash *hash)
 {
-    SHA256_State *s = handle;
-    return BinarySink_UPCAST(s);
+    struct sha256_hash *h = FROMFIELD(hash, struct sha256_hash, hash);
+
+    smemclr(h, sizeof(*h));
+    sfree(h);
 }
 
-static void sha256_final(void *handle, unsigned char *output)
+static void sha256_final(ssh_hash *hash, unsigned char *output)
 {
-    SHA256_State *s = handle;
-
-    SHA256_Final(s, output);
-    sha256_free(s);
+    struct sha256_hash *h = FROMFIELD(hash, struct sha256_hash, hash);
+    SHA256_Final(&h->state, output);
+    sha256_free(hash);
 }
 
-const struct ssh_hash ssh_sha256 = {
-    sha256_init, sha256_copy, sha256_sink, sha256_final, sha256_free,
-    32, "SHA-256"
+const struct ssh_hashalg ssh_sha256 = {
+    sha256_new, sha256_copy, sha256_final, sha256_free, 32, "SHA-256"
 };
 
 /* ----------------------------------------------------------------------
@@ -256,111 +256,80 @@ const struct ssh_hash ssh_sha256 = {
  * HMAC wrapper on it.
  */
 
-static void *sha256_make_context(void *cipher_ctx)
+struct hmacsha256 {
+    SHA256_State sha[3];
+    ssh2_mac mac;
+};
+
+static ssh2_mac *hmacsha256_new(
+    const struct ssh2_macalg *alg, ssh2_cipher *cipher)
 {
-    return snewn(3, SHA256_State);
+    struct hmacsha256 *ctx = snew(struct hmacsha256);
+    ctx->mac.vt = alg;
+    BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->sha[2]);
+    return &ctx->mac;
 }
 
-static void sha256_free_context(void *handle)
+static void hmacsha256_free(ssh2_mac *mac)
 {
-    smemclr(handle, 3 * sizeof(SHA256_State));
-    sfree(handle);
+    struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void sha256_key_internal(void *handle,
+static void sha256_key_internal(struct hmacsha256 *ctx,
                                 const unsigned char *key, int len)
 {
-    SHA256_State *keys = (SHA256_State *)handle;
     unsigned char foo[64];
     int i;
 
     memset(foo, 0x36, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
-    SHA256_Init(&keys[0]);
-    put_data(&keys[0], foo, 64);
+    SHA256_Init(&ctx->sha[0]);
+    put_data(&ctx->sha[0], foo, 64);
 
     memset(foo, 0x5C, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
-    SHA256_Init(&keys[1]);
-    put_data(&keys[1], foo, 64);
+    SHA256_Init(&ctx->sha[1]);
+    put_data(&ctx->sha[1], foo, 64);
 
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void sha256_key(void *handle, const void *key)
+static void hmacsha256_key(ssh2_mac *mac, const void *key)
 {
-    sha256_key_internal(handle, key, 32);
+    struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
+    sha256_key_internal(ctx, key, ctx->mac.vt->keylen);
 }
 
-static void hmacsha256_start(void *handle)
+static void hmacsha256_start(ssh2_mac *mac)
 {
-    SHA256_State *keys = (SHA256_State *)handle;
+    struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
 
-    keys[2] = keys[0];		      /* structure copy */
-    BinarySink_COPIED(&keys[2]);
-}
-
-static BinarySink *hmacsha256_sink(void *handle)
-{
-    SHA256_State *keys = (SHA256_State *)handle;
-    return BinarySink_UPCAST(&keys[2]);
+    ctx->sha[2] = ctx->sha[0];         /* structure copy */
+    BinarySink_COPIED(&ctx->sha[2]);
 }
 
-static void hmacsha256_genresult(void *handle, unsigned char *hmac)
+static void hmacsha256_genresult(ssh2_mac *mac, unsigned char *hmac)
 {
-    SHA256_State *keys = (SHA256_State *)handle;
+    struct hmacsha256 *ctx = FROMFIELD(mac, struct hmacsha256, mac);
     SHA256_State s;
     unsigned char intermediate[32];
 
-    s = keys[2];		       /* structure copy */
+    s = ctx->sha[2];                   /* structure copy */
     BinarySink_COPIED(&s);
     SHA256_Final(&s, intermediate);
-    s = keys[1];		       /* structure copy */
+    s = ctx->sha[1];                   /* structure copy */
     BinarySink_COPIED(&s);
     put_data(&s, intermediate, 32);
     SHA256_Final(&s, hmac);
 }
 
-static void sha256_do_hmac(void *handle, const unsigned char *blk, int len,
-			 unsigned long seq, unsigned char *hmac)
-{
-    BinarySink *bs = hmacsha256_sink(handle);
-    hmacsha256_start(handle);
-    put_uint32(bs, seq);
-    put_data(bs, blk, len);
-    hmacsha256_genresult(handle, hmac);
-}
-
-static void sha256_generate(void *handle, void *vblk, int len,
-			  unsigned long seq)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    sha256_do_hmac(handle, blk, len, seq, blk + len);
-}
-
-static int hmacsha256_verresult(void *handle, unsigned char const *hmac)
-{
-    unsigned char correct[32];
-    hmacsha256_genresult(handle, correct);
-    return smemeq(correct, hmac, 32);
-}
-
-static int sha256_verify(void *handle, const void *vblk, int len,
-		       unsigned long seq)
-{
-    const unsigned char *blk = (const unsigned char *)vblk;
-    unsigned char correct[32];
-    sha256_do_hmac(handle, blk, len, seq, correct);
-    return smemeq(correct, blk + len, 32);
-}
-
-const struct ssh_mac ssh_hmac_sha256 = {
-    sha256_make_context, sha256_free_context, sha256_key,
-    sha256_generate, sha256_verify,
-    hmacsha256_start, hmacsha256_sink,
-    hmacsha256_genresult, hmacsha256_verresult,
+const struct ssh2_macalg ssh_hmac_sha256 = {
+    hmacsha256_new, hmacsha256_free, hmacsha256_key,
+    hmacsha256_start, hmacsha256_genresult,
     "hmac-sha2-256", "[email protected]",
     32, 32,
     "HMAC-SHA-256"

+ 42 - 45
source/putty/sshsh512.c

@@ -327,74 +327,71 @@ void SHA384_Simple(const void *p, int len, unsigned char *output) {
  * Thin abstraction for things where hashes are pluggable.
  */
 
-static void *sha512_init(void)
-{
-    SHA512_State *s;
+struct sha512_hash {
+    SHA512_State state;
+    ssh_hash hash;
+};
 
-    s = snew(SHA512_State);
-    SHA512_Init(s);
-    return s;
+static ssh_hash *sha512_new(const struct ssh_hashalg *alg)
+{
+    struct sha512_hash *h = snew(struct sha512_hash);
+    SHA512_Init(&h->state);
+    h->hash.vt = alg;
+    BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+    return &h->hash;
 }
 
-static void *sha512_copy(const void *vold)
+static ssh_hash *sha512_copy(ssh_hash *hashold)
 {
-    const SHA512_State *old = (const SHA512_State *)vold;
-    SHA512_State *s;
+    struct sha512_hash *hold, *hnew;
+    ssh_hash *hashnew = sha512_new(hashold->vt);
 
-    s = snew(SHA512_State);
-    *s = *old;
-    BinarySink_COPIED(s);
-    return s;
-}
+    hold = FROMFIELD(hashold, struct sha512_hash, hash);
+    hnew = FROMFIELD(hashnew, struct sha512_hash, hash);
 
-static void sha512_free(void *handle)
-{
-    SHA512_State *s = handle;
+    hnew->state = hold->state;
+    BinarySink_COPIED(&hnew->state);
 
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    return hashnew;
 }
 
-static BinarySink *sha512_sink(void *handle)
+static void sha512_free(ssh_hash *hash)
 {
-    SHA512_State *s = handle;
-    return BinarySink_UPCAST(s);
+    struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+
+    smemclr(h, sizeof(*h));
+    sfree(h);
 }
 
-static void sha512_final(void *handle, unsigned char *output)
+static void sha512_final(ssh_hash *hash, unsigned char *output)
 {
-    SHA512_State *s = handle;
-
-    SHA512_Final(s, output);
-    sha512_free(s);
+    struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+    SHA512_Final(&h->state, output);
+    sha512_free(hash);
 }
 
-const struct ssh_hash ssh_sha512 = {
-    sha512_init, sha512_copy, sha512_sink, sha512_final, sha512_free,
-    64, "SHA-512"
+const struct ssh_hashalg ssh_sha512 = {
+    sha512_new, sha512_copy, sha512_final, sha512_free, 64, "SHA-512"
 };
 
-static void *sha384_init(void)
+static ssh_hash *sha384_new(const struct ssh_hashalg *alg)
 {
-    SHA512_State *s;
-
-    s = snew(SHA512_State);
-    SHA384_Init(s);
-    return s;
+    struct sha512_hash *h = snew(struct sha512_hash);
+    SHA384_Init(&h->state);
+    h->hash.vt = alg;
+    BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+    return &h->hash;
 }
 
-static void sha384_final(void *handle, unsigned char *output)
+static void sha384_final(ssh_hash *hash, unsigned char *output)
 {
-    SHA512_State *s = handle;
-
-    SHA384_Final(s, output);
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    struct sha512_hash *h = FROMFIELD(hash, struct sha512_hash, hash);
+    SHA384_Final(&h->state, output);
+    sha512_free(hash);
 }
 
-const struct ssh_hash ssh_sha384 = {
-    sha384_init, sha512_copy, sha512_sink, sha384_final, sha512_free,
-    48, "SHA-384"
+const struct ssh_hashalg ssh_sha384 = {
+    sha384_new, sha512_copy, sha384_final, sha512_free, 48, "SHA-384"
 };
 
 #ifdef TEST

+ 76 - 141
source/putty/sshsha.c

@@ -232,50 +232,51 @@ void SHA_Simple(const void *p, int len, unsigned char *output)
  * Thin abstraction for things where hashes are pluggable.
  */
 
-static void *sha1_init(void)
-{
-    SHA_State *s;
+struct sha1_hash {
+    SHA_State state;
+    ssh_hash hash;
+};
 
-    s = snew(SHA_State);
-    SHA_Init(s);
-    return s;
+static ssh_hash *sha1_new(const struct ssh_hashalg *alg)
+{
+    struct sha1_hash *h = snew(struct sha1_hash);
+    SHA_Init(&h->state);
+    h->hash.vt = alg;
+    BinarySink_DELEGATE_INIT(&h->hash, &h->state);
+    return &h->hash;
 }
 
-static void *sha1_copy(const void *vold)
+static ssh_hash *sha1_copy(ssh_hash *hashold)
 {
-    const SHA_State *old = (const SHA_State *)vold;
-    SHA_State *s;
+    struct sha1_hash *hold, *hnew;
+    ssh_hash *hashnew = sha1_new(hashold->vt);
 
-    s = snew(SHA_State);
-    *s = *old;
-    BinarySink_COPIED(s);
-    return s;
-}
+    hold = FROMFIELD(hashold, struct sha1_hash, hash);
+    hnew = FROMFIELD(hashnew, struct sha1_hash, hash);
 
-static void sha1_free(void *handle)
-{
-    SHA_State *s = handle;
+    hnew->state = hold->state;
+    BinarySink_COPIED(&hnew->state);
 
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    return hashnew;
 }
 
-static BinarySink *sha1_sink(void *handle)
+static void sha1_free(ssh_hash *hash)
 {
-    SHA_State *s = handle;
-    return BinarySink_UPCAST(s);
+    struct sha1_hash *h = FROMFIELD(hash, struct sha1_hash, hash);
+
+    smemclr(h, sizeof(*h));
+    sfree(h);
 }
 
-static void sha1_final(void *handle, unsigned char *output)
+static void sha1_final(ssh_hash *hash, unsigned char *output)
 {
-    SHA_State *s = handle;
-
-    SHA_Final(s, output);
-    sha1_free(s);
+    struct sha1_hash *h = FROMFIELD(hash, struct sha1_hash, hash);
+    SHA_Final(&h->state, output);
+    sha1_free(hash);
 }
 
-const struct ssh_hash ssh_sha1 = {
-    sha1_init, sha1_copy, sha1_sink, sha1_final, sha1_free, 20, "SHA-1"
+const struct ssh_hashalg ssh_sha1 = {
+    sha1_new, sha1_copy, sha1_final, sha1_free, 20, "SHA-1"
 };
 
 /* ----------------------------------------------------------------------
@@ -283,20 +284,30 @@ const struct ssh_hash ssh_sha1 = {
  * HMAC wrapper on it.
  */
 
-static void *sha1_make_context(void *cipher_ctx)
+struct hmacsha1 {
+    SHA_State sha[3];
+    ssh2_mac mac;
+};
+
+static ssh2_mac *hmacsha1_new(
+    const struct ssh2_macalg *alg, ssh2_cipher *cipher)
 {
-    return snewn(3, SHA_State);
+    struct hmacsha1 *ctx = snew(struct hmacsha1);
+    ctx->mac.vt = alg;
+    BinarySink_DELEGATE_INIT(&ctx->mac, &ctx->sha[2]);
+    return &ctx->mac;
 }
 
-static void sha1_free_context(void *handle)
+static void hmacsha1_free(ssh2_mac *mac)
 {
-    smemclr(handle, 3 * sizeof(SHA_State));
-    sfree(handle);
+    struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
 }
 
-static void sha1_key_internal(void *handle, const unsigned char *key, int len)
+static void sha1_key_internal(SHA_State *keys,
+                              const unsigned char *key, int len)
 {
-    SHA_State *keys = (SHA_State *)handle;
     unsigned char foo[64];
     int i;
 
@@ -315,108 +326,38 @@ static void sha1_key_internal(void *handle, const unsigned char *key, int len)
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void sha1_key(void *handle, const void *key)
-{
-    sha1_key_internal(handle, key, 20);
-}
-
-static void sha1_key_buggy(void *handle, const void *key)
+static void hmacsha1_key(ssh2_mac *mac, const void *key)
 {
-    sha1_key_internal(handle, key, 16);
+    struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
+    /* Reading the key length out of the ssh2_macalg structure means
+     * this same method can be used for the _buggy variants which use
+     * a shorter key */
+    sha1_key_internal(ctx->sha, key, ctx->mac.vt->keylen);
 }
 
-static void hmacsha1_start(void *handle)
+static void hmacsha1_start(ssh2_mac *mac)
 {
-    SHA_State *keys = (SHA_State *)handle;
+    struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
 
-    keys[2] = keys[0];		      /* structure copy */
-    BinarySink_COPIED(&keys[2]);
-}
-
-static BinarySink *hmacsha1_sink(void *handle)
-{
-    SHA_State *keys = (SHA_State *)handle;
-    return BinarySink_UPCAST(&keys[2]);
+    ctx->sha[2] = ctx->sha[0];         /* structure copy */
+    BinarySink_COPIED(&ctx->sha[2]);
 }
 
-static void hmacsha1_genresult(void *handle, unsigned char *hmac)
+static void hmacsha1_genresult(ssh2_mac *mac, unsigned char *hmac)
 {
-    SHA_State *keys = (SHA_State *)handle;
+    struct hmacsha1 *ctx = FROMFIELD(mac, struct hmacsha1, mac);
     SHA_State s;
     unsigned char intermediate[20];
 
-    s = keys[2];		       /* structure copy */
+    s = ctx->sha[2];                   /* structure copy */
     BinarySink_COPIED(&s);
     SHA_Final(&s, intermediate);
-    s = keys[1];		       /* structure copy */
+    s = ctx->sha[1];                   /* structure copy */
     BinarySink_COPIED(&s);
     put_data(&s, intermediate, 20);
-    SHA_Final(&s, hmac);
-}
-
-static void sha1_do_hmac(void *handle, const unsigned char *blk, int len,
-			 unsigned long seq, unsigned char *hmac)
-{
-    BinarySink *bs = hmacsha1_sink(handle);
-    hmacsha1_start(handle);
-    put_uint32(bs, seq);
-    put_data(bs, blk, len);
-    hmacsha1_genresult(handle, hmac);
-}
-
-static void sha1_generate(void *handle, void *vblk, int len,
-			  unsigned long seq)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    sha1_do_hmac(handle, blk, len, seq, blk + len);
-}
-
-static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
-{
-    unsigned char correct[20];
-    hmacsha1_genresult(handle, correct);
-    return smemeq(correct, hmac, 20);
-}
-
-static int sha1_verify(void *handle, const void *vblk, int len,
-		       unsigned long seq)
-{
-    const unsigned char *blk = (const unsigned char *)vblk;
-    unsigned char correct[20];
-    sha1_do_hmac(handle, blk, len, seq, correct);
-    return smemeq(correct, blk + len, 20);
-}
-
-static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
-{
-    unsigned char full[20];
-    hmacsha1_genresult(handle, full);
-    memcpy(hmac, full, 12);
-}
-
-static void sha1_96_generate(void *handle, void *vblk, int len,
-			     unsigned long seq)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    unsigned char full[20];
-    sha1_do_hmac(handle, blk, len, seq, full);
-    memcpy(blk + len, full, 12);
-}
-
-static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
-{
-    unsigned char correct[20];
-    hmacsha1_genresult(handle, correct);
-    return smemeq(correct, hmac, 12);
-}
-
-static int sha1_96_verify(void *handle, const void *vblk, int len,
-		       unsigned long seq)
-{
-    const unsigned char *blk = (const unsigned char *)vblk;
-    unsigned char correct[20];
-    sha1_do_hmac(handle, blk, len, seq, correct);
-    return smemeq(correct, blk + len, 12);
+    SHA_Final(&s, intermediate);
+    memcpy(hmac, intermediate, ctx->mac.vt->len);
+    smemclr(intermediate, sizeof(intermediate));
 }
 
 void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
@@ -432,39 +373,33 @@ void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
     SHA_Final(&states[1], output);
 }
 
-const struct ssh_mac ssh_hmac_sha1 = {
-    sha1_make_context, sha1_free_context, sha1_key,
-    sha1_generate, sha1_verify,
-    hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
+const struct ssh2_macalg ssh_hmac_sha1 = {
+    hmacsha1_new, hmacsha1_free, hmacsha1_key,
+    hmacsha1_start, hmacsha1_genresult,
     "hmac-sha1", "[email protected]",
     20, 20,
     "HMAC-SHA1"
 };
 
-const struct ssh_mac ssh_hmac_sha1_96 = {
-    sha1_make_context, sha1_free_context, sha1_key,
-    sha1_96_generate, sha1_96_verify,
-    hmacsha1_start, hmacsha1_sink,
-    hmacsha1_96_genresult, hmacsha1_96_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_96 = {
+    hmacsha1_new, hmacsha1_free, hmacsha1_key,
+    hmacsha1_start, hmacsha1_genresult,
     "hmac-sha1-96", "[email protected]",
     12, 20,
     "HMAC-SHA1-96"
 };
 
-const struct ssh_mac ssh_hmac_sha1_buggy = {
-    sha1_make_context, sha1_free_context, sha1_key_buggy,
-    sha1_generate, sha1_verify,
-    hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_buggy = {
+    hmacsha1_new, hmacsha1_free, hmacsha1_key,
+    hmacsha1_start, hmacsha1_genresult,
     "hmac-sha1", NULL,
     20, 16,
     "bug-compatible HMAC-SHA1"
 };
 
-const struct ssh_mac ssh_hmac_sha1_96_buggy = {
-    sha1_make_context, sha1_free_context, sha1_key_buggy,
-    sha1_96_generate, sha1_96_verify,
-    hmacsha1_start, hmacsha1_sink,
-    hmacsha1_96_genresult, hmacsha1_96_verresult,
+const struct ssh2_macalg ssh_hmac_sha1_96_buggy = {
+    hmacsha1_new, hmacsha1_free, hmacsha1_key,
+    hmacsha1_start, hmacsha1_genresult,
     "hmac-sha1-96", NULL,
     12, 16,
     "bug-compatible HMAC-SHA1-96"