Browse Source

PuTTY snapshot c6a8731b (Add a consistency test for every ssh_cipheralg - 2019-01-18)

Source commit: 836bcaca19806cd96f676ba8b84461c76c7f3594
Martin Prikryl 6 years ago
parent
commit
21e25e016d

+ 9 - 0
source/putty/WINDOWS/winmiscs.c

@@ -275,3 +275,12 @@ uintmax_t strtoumax(const char *nptr, char **endptr, int base)
 }
 
 #endif
+
+#if defined _M_ARM || defined _M_ARM64
+
+bool platform_aes_hw_available(void)
+{
+    return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
+}
+
+#endif

+ 2 - 4
source/putty/defs.h

@@ -102,11 +102,9 @@ typedef struct ssh_compression_alg ssh_compression_alg;
 typedef struct ssh2_userkey ssh2_userkey;
 typedef struct ssh2_macalg ssh2_macalg;
 typedef struct ssh2_mac ssh2_mac;
-typedef struct ssh2_cipheralg ssh2_cipheralg;
-typedef struct ssh2_cipher ssh2_cipher;
+typedef struct ssh_cipheralg ssh_cipheralg;
+typedef struct ssh_cipher ssh_cipher;
 typedef struct ssh2_ciphers ssh2_ciphers;
-typedef struct ssh1_cipheralg ssh1_cipheralg;
-typedef struct ssh1_cipher ssh1_cipher;
 typedef struct dh_ctx dh_ctx;
 typedef struct ecdh_key ecdh_key;
 

+ 17 - 17
source/putty/import.c

@@ -547,11 +547,11 @@ static ssh2_userkey *openssh_pem_read(
             des3_decrypt_pubkey_ossh(keybuf, key->iv,
                                      key->keyblob->u, key->keyblob->len);
         else {
-            ssh2_cipher *cipher = ssh2_cipher_new(&ssh_aes128_cbc);
-            ssh2_cipher_setkey(cipher, keybuf);
-            ssh2_cipher_setiv(cipher, key->iv);
-            ssh2_cipher_decrypt(cipher, key->keyblob->u, key->keyblob->len);
-            ssh2_cipher_free(cipher);
+            ssh_cipher *cipher = ssh_cipher_new(&ssh_aes128_cbc);
+            ssh_cipher_setkey(cipher, keybuf);
+            ssh_cipher_setiv(cipher, key->iv);
+            ssh_cipher_decrypt(cipher, key->keyblob->u, key->keyblob->len);
+            ssh_cipher_free(cipher);
         }
 
         smemclr(&md5c, sizeof(md5c));
@@ -1388,16 +1388,16 @@ static ssh2_userkey *openssh_new_read(
                 goto error;
             }
             {
-                ssh2_cipher *cipher = ssh2_cipher_new(
+                ssh_cipher *cipher = ssh_cipher_new(
                     key->cipher == ON_E_AES256CBC ?
                     &ssh_aes256_cbc : &ssh_aes256_sdctr);
-                ssh2_cipher_setkey(cipher, keybuf);
-                ssh2_cipher_setiv(cipher, keybuf + 32);
+                ssh_cipher_setkey(cipher, keybuf);
+                ssh_cipher_setiv(cipher, keybuf + 32);
                 /* Decrypt the private section in place, casting away
                  * the const from key->private being a ptrlen */
-                ssh2_cipher_decrypt(cipher, (char *)key->private.ptr,
-                                    key->private.len);
-                ssh2_cipher_free(cipher);
+                ssh_cipher_decrypt(cipher, (char *)key->private.ptr,
+                                   key->private.len);
+                ssh_cipher_free(cipher);
             }
             break;
           default:
@@ -1588,17 +1588,17 @@ static bool openssh_new_write(
              * material: 32 bytes AES key + 16 bytes iv.
              */
             unsigned char keybuf[48];
-            ssh2_cipher *cipher;
+            ssh_cipher *cipher;
 
             openssh_bcrypt(passphrase,
                            bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
                            keybuf, sizeof(keybuf));
 
-            cipher = ssh2_cipher_new(&ssh_aes256_sdctr);
-            ssh2_cipher_setkey(cipher, keybuf);
-            ssh2_cipher_setiv(cipher, keybuf + 32);
-            ssh2_cipher_encrypt(cipher, cpblob->u, cpblob->len);
-            ssh2_cipher_free(cipher);
+            cipher = ssh_cipher_new(&ssh_aes256_sdctr);
+            ssh_cipher_setkey(cipher, keybuf);
+            ssh_cipher_setiv(cipher, keybuf + 32);
+            ssh_cipher_encrypt(cipher, cpblob->u, cpblob->len);
+            ssh_cipher_free(cipher);
 
             smemclr(keybuf, sizeof(keybuf));
         }

+ 12 - 18
source/putty/mpint.c

@@ -1112,19 +1112,14 @@ mp_int *mp_rshift_safe(mp_int *x, size_t bits)
     /*
      * That's done the shifting by words; now we do the shifting by
      * bits.
-     *
-     * I assume here that register-controlled right shifts are
-     * time-constant. If they're not, I could replace this with
-     * another loop over bit positions.
      */
-    size_t upshift = BIGNUM_INT_BITS - bitshift;
-    size_t no_shift = (upshift >> BIGNUM_INT_BITS_BITS);
-    upshift &= ~-(size_t)no_shift;
-    BignumInt upshifted_mask = ~-(BignumInt)no_shift;
-
-    for (size_t i = 0; i < r->nw; i++) {
-        r->w[i] = (r->w[i] >> bitshift) |
-            ((mp_word(r, i+1) << upshift) & upshifted_mask);
+    for (unsigned bit = 0; bit < BIGNUM_INT_BITS_BITS; bit++) {
+        unsigned shift = 1 << bit, upshift = BIGNUM_INT_BITS - shift;
+        BignumInt mask = -(BignumInt)((bitshift >> bit) & 1);
+        for (size_t i = 0; i < r->nw; i++) {
+            BignumInt w = ((r->w[i] >> shift) | (mp_word(r, i+1) << upshift));
+            r->w[i] ^= (r->w[i] ^ w) & mask;
+        }
     }
 
     return r;
@@ -1863,12 +1858,11 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out)
      * actual input will be close to a fixed power of two regardless
      * of where the MSB was.
      *
-     * I do this in another log n individual passes, not so much
-     * because I'm worried about the time-invariance of the CPU's
-     * register-controlled shift operation, but in case the compiler
-     * code-generates uint64_t shifts out of a variable number of
-     * smaller-word shift instructions, e.g. by splitting up into
-     * cases.
+     * I do this in another log n individual passes, partly in case
+     * the CPU's register-controlled shift operation isn't
+     * time-constant, and also in case the compiler code-generates
+     * uint64_t shifts out of a variable number of smaller-word shift
+     * instructions, e.g. by splitting up into cases.
      */
     for (size_t i = BIGNUM_INT_BITS_BITS; i-- > 0;) {
         size_t sl = 1 << i;               /* left shift count */

+ 66 - 77
source/putty/ssh.h

@@ -512,15 +512,17 @@ int rsa_ssh1_public_blob_len(ptrlen data);
 void freersapriv(RSAKey *key);
 void freersakey(RSAKey *key);
 
-unsigned long crc32_compute(const void *s, size_t len);
-unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
+uint32_t crc32_rfc1662(ptrlen data);
+uint32_t crc32_ssh1(ptrlen data);
+uint32_t crc32_update(uint32_t crc_input, ptrlen data);
 
 /* SSH CRC compensation attack detector */
 struct crcda_ctx;
 struct crcda_ctx *crcda_make_context(void);
 void crcda_free_context(struct crcda_ctx *ctx);
-bool detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32_t len,
-                   unsigned char *IV);
+bool detect_attack(struct crcda_ctx *ctx,
+                   const unsigned char *buf, uint32_t len,
+                   const unsigned char *IV);
 
 /*
  * SSH2 RSA key exchange functions
@@ -552,8 +554,8 @@ mp_int *dss_gen_k(const char *id_string,
                      mp_int *modulus, mp_int *private_key,
                      unsigned char *digest, int digest_len);
 
-struct ssh2_cipher {
-    const ssh2_cipheralg *vt;
+struct ssh_cipher {
+    const ssh_cipheralg *vt;
 };
 
 typedef struct {
@@ -623,39 +625,19 @@ 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 ssh1_cipher {
-    const ssh1_cipheralg *vt;
-};
-
-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;
-};
-
-#define ssh1_cipher_new(alg) ((alg)->new())
-#define ssh1_cipher_free(ctx) ((ctx)->vt->free(ctx))
-#define ssh1_cipher_sesskey(ctx, key) ((ctx)->vt->sesskey(ctx, key))
-#define ssh1_cipher_encrypt(ctx, blk, len) ((ctx)->vt->encrypt(ctx, blk, len))
-#define ssh1_cipher_decrypt(ctx, blk, len) ((ctx)->vt->decrypt(ctx, blk, len))
-
-struct ssh2_cipheralg {
-    ssh2_cipher *(*new)(const 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);
+struct ssh_cipheralg {
+    ssh_cipher *(*new)(const ssh_cipheralg *alg);
+    void (*free)(ssh_cipher *);
+    void (*setiv)(ssh_cipher *, const void *iv);
+    void (*setkey)(ssh_cipher *, const void *key);
+    void (*encrypt)(ssh_cipher *, void *blk, int len);
+    void (*decrypt)(ssh_cipher *, void *blk, int len);
     /* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */
-    void (*encrypt_length)(ssh2_cipher *, void *blk, int len,
+    void (*encrypt_length)(ssh_cipher *, void *blk, int len,
                            unsigned long seq);
-    void (*decrypt_length)(ssh2_cipher *, void *blk, int len,
+    void (*decrypt_length)(ssh_cipher *, void *blk, int len,
                            unsigned long seq);
-    const char *name;
+    const char *ssh2_id;
     int blksize;
     /* real_keybits is the number of bits of entropy genuinely used by
      * the cipher scheme; it's used for deciding how big a
@@ -681,21 +663,21 @@ struct ssh2_cipheralg {
     const void *extra;
 };
 
-#define ssh2_cipher_new(alg) ((alg)->new(alg))
-#define ssh2_cipher_free(ctx) ((ctx)->vt->free(ctx))
-#define ssh2_cipher_setiv(ctx, iv) ((ctx)->vt->setiv(ctx, iv))
-#define ssh2_cipher_setkey(ctx, key) ((ctx)->vt->setkey(ctx, key))
-#define ssh2_cipher_encrypt(ctx, blk, len) ((ctx)->vt->encrypt(ctx, blk, len))
-#define ssh2_cipher_decrypt(ctx, blk, len) ((ctx)->vt->decrypt(ctx, blk, len))
-#define ssh2_cipher_encrypt_length(ctx, blk, len, seq) \
+#define ssh_cipher_new(alg) ((alg)->new(alg))
+#define ssh_cipher_free(ctx) ((ctx)->vt->free(ctx))
+#define ssh_cipher_setiv(ctx, iv) ((ctx)->vt->setiv(ctx, iv))
+#define ssh_cipher_setkey(ctx, key) ((ctx)->vt->setkey(ctx, key))
+#define ssh_cipher_encrypt(ctx, blk, len) ((ctx)->vt->encrypt(ctx, blk, len))
+#define ssh_cipher_decrypt(ctx, blk, len) ((ctx)->vt->decrypt(ctx, blk, len))
+#define ssh_cipher_encrypt_length(ctx, blk, len, seq) \
     ((ctx)->vt->encrypt_length(ctx, blk, len, seq))
-#define ssh2_cipher_decrypt_length(ctx, blk, len, seq) \
+#define ssh_cipher_decrypt_length(ctx, blk, len, seq) \
     ((ctx)->vt->decrypt_length(ctx, blk, len, seq))
-#define ssh2_cipher_alg(ctx) ((ctx)->vt)
+#define ssh_cipher_alg(ctx) ((ctx)->vt)
 
 struct ssh2_ciphers {
     int nciphers;
-    const ssh2_cipheralg *const *list;
+    const ssh_cipheralg *const *list;
 };
 
 struct ssh2_mac {
@@ -705,7 +687,7 @@ struct ssh2_mac {
 
 struct ssh2_macalg {
     /* Passes in the cipher context */
-    ssh2_mac *(*new)(const ssh2_macalg *alg, ssh2_cipher *cipher);
+    ssh2_mac *(*new)(const ssh2_macalg *alg, ssh_cipher *cipher);
     void (*free)(ssh2_mac *);
     void (*setkey)(ssh2_mac *, ptrlen key);
     void (*start)(ssh2_mac *);
@@ -852,36 +834,35 @@ struct ssh2_userkey {
 /* The maximum length of any hash algorithm. (bytes) */
 #define MAX_HASH_LEN (64)              /* longest is SHA-512 */
 
-extern const ssh1_cipheralg ssh1_3des;
-extern const ssh1_cipheralg ssh1_des;
-extern const ssh1_cipheralg ssh1_blowfish;
-extern const ssh2_cipheralg ssh_3des_ssh2_ctr;
-extern const ssh2_cipheralg ssh_3des_ssh2;
-extern const ssh2_cipheralg ssh_des_ssh2;
-extern const ssh2_cipheralg ssh_des_sshcom_ssh2;
-extern const ssh2_cipheralg ssh_aes256_sdctr;
-extern const ssh2_cipheralg ssh_aes256_sdctr_hw;
-extern const ssh2_cipheralg ssh_aes256_sdctr_sw;
-extern const ssh2_cipheralg ssh_aes256_cbc;
-extern const ssh2_cipheralg ssh_aes256_cbc_hw;
-extern const ssh2_cipheralg ssh_aes256_cbc_sw;
-extern const ssh2_cipheralg ssh_aes192_sdctr;
-extern const ssh2_cipheralg ssh_aes192_sdctr_hw;
-extern const ssh2_cipheralg ssh_aes192_sdctr_sw;
-extern const ssh2_cipheralg ssh_aes192_cbc;
-extern const ssh2_cipheralg ssh_aes192_cbc_hw;
-extern const ssh2_cipheralg ssh_aes192_cbc_sw;
-extern const ssh2_cipheralg ssh_aes128_sdctr;
-extern const ssh2_cipheralg ssh_aes128_sdctr_hw;
-extern const ssh2_cipheralg ssh_aes128_sdctr_sw;
-extern const ssh2_cipheralg ssh_aes128_cbc;
-extern const ssh2_cipheralg ssh_aes128_cbc_hw;
-extern const ssh2_cipheralg ssh_aes128_cbc_sw;
-extern const ssh2_cipheralg ssh_blowfish_ssh2_ctr;
-extern const ssh2_cipheralg ssh_blowfish_ssh2;
-extern const ssh2_cipheralg ssh_arcfour256_ssh2;
-extern const ssh2_cipheralg ssh_arcfour128_ssh2;
-extern const ssh2_cipheralg ssh2_chacha20_poly1305;
+extern const ssh_cipheralg ssh_3des_ssh1;
+extern const ssh_cipheralg ssh_blowfish_ssh1;
+extern const ssh_cipheralg ssh_3des_ssh2_ctr;
+extern const ssh_cipheralg ssh_3des_ssh2;
+extern const ssh_cipheralg ssh_des;
+extern const ssh_cipheralg ssh_des_sshcom_ssh2;
+extern const ssh_cipheralg ssh_aes256_sdctr;
+extern const ssh_cipheralg ssh_aes256_sdctr_hw;
+extern const ssh_cipheralg ssh_aes256_sdctr_sw;
+extern const ssh_cipheralg ssh_aes256_cbc;
+extern const ssh_cipheralg ssh_aes256_cbc_hw;
+extern const ssh_cipheralg ssh_aes256_cbc_sw;
+extern const ssh_cipheralg ssh_aes192_sdctr;
+extern const ssh_cipheralg ssh_aes192_sdctr_hw;
+extern const ssh_cipheralg ssh_aes192_sdctr_sw;
+extern const ssh_cipheralg ssh_aes192_cbc;
+extern const ssh_cipheralg ssh_aes192_cbc_hw;
+extern const ssh_cipheralg ssh_aes192_cbc_sw;
+extern const ssh_cipheralg ssh_aes128_sdctr;
+extern const ssh_cipheralg ssh_aes128_sdctr_hw;
+extern const ssh_cipheralg ssh_aes128_sdctr_sw;
+extern const ssh_cipheralg ssh_aes128_cbc;
+extern const ssh_cipheralg ssh_aes128_cbc_hw;
+extern const ssh_cipheralg ssh_aes128_cbc_sw;
+extern const ssh_cipheralg ssh_blowfish_ssh2_ctr;
+extern const ssh_cipheralg ssh_blowfish_ssh2;
+extern const ssh_cipheralg ssh_arcfour256_ssh2;
+extern const ssh_cipheralg ssh_arcfour128_ssh2;
+extern const ssh_cipheralg ssh2_chacha20_poly1305;
 extern const ssh2_ciphers ssh2_3des;
 extern const ssh2_ciphers ssh2_des;
 extern const ssh2_ciphers ssh2_aes;
@@ -918,6 +899,14 @@ extern const ssh2_macalg ssh_hmac_sha256;
 extern const ssh2_macalg ssh2_poly1305;
 extern const ssh_compression_alg ssh_zlib;
 
+/*
+ * On some systems, you have to detect hardware crypto acceleration by
+ * asking the local OS API rather than OS-agnostically asking the CPU
+ * itself. If so, then this function should be implemented in each
+ * platform subdirectory.
+ */
+bool platform_aes_hw_available(void);
+
 /*
  * PuTTY version number formatted as an SSH version string. 
  */

+ 34 - 18
source/putty/ssh1bpp.c

@@ -13,13 +13,14 @@ struct ssh1_bpp_state {
     int crState;
     long len, pad, biglen, length, maxlen;
     unsigned char *data;
-    unsigned long realcrc, gotcrc;
+    uint32_t realcrc, gotcrc;
     int chunk;
     PktIn *pktin;
 
-    ssh1_cipher *cipher;
+    ssh_cipher *cipher_in, *cipher_out;
 
     struct crcda_ctx *crcda_ctx;
+    uint8_t iv[8];                     /* for crcda */
 
     bool pending_compression_request;
     ssh_compressor *compctx;
@@ -56,8 +57,10 @@ BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx)
 static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
 {
     struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp);
-    if (s->cipher)
-        ssh1_cipher_free(s->cipher);
+    if (s->cipher_in)
+        ssh_cipher_free(s->cipher_in);
+    if (s->cipher_out)
+        ssh_cipher_free(s->cipher_out);
     if (s->compctx)
         ssh_compressor_free(s->compctx);
     if (s->decompctx)
@@ -69,23 +72,32 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
 }
 
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
-                         const ssh1_cipheralg *cipher,
+                         const ssh_cipheralg *cipher,
                          const void *session_key)
 {
     struct ssh1_bpp_state *s;
     assert(bpp->vt == &ssh1_bpp_vtable);
     s = container_of(bpp, struct ssh1_bpp_state, bpp);
 
-    assert(!s->cipher);
+    assert(!s->cipher_in);
+    assert(!s->cipher_out);
 
     if (cipher) {
-        s->cipher = ssh1_cipher_new(cipher);
-        ssh1_cipher_sesskey(s->cipher, session_key);
+        s->cipher_in = ssh_cipher_new(cipher);
+        s->cipher_out = ssh_cipher_new(cipher);
+        ssh_cipher_setkey(s->cipher_in, session_key);
+        ssh_cipher_setkey(s->cipher_out, session_key);
 
         assert(!s->crcda_ctx);
         s->crcda_ctx = crcda_make_context();
 
         bpp_logevent("Initialised %s encryption", cipher->text_name);
+
+        memset(s->iv, 0, sizeof(s->iv));
+
+        assert(cipher->blksize <= sizeof(s->iv));
+        ssh_cipher_setiv(s->cipher_in, s->iv);
+        ssh_cipher_setiv(s->cipher_out, s->iv);
     }
 }
 
@@ -154,17 +166,21 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
 
         BPP_READ(s->data, s->biglen);
 
-        if (s->cipher && detect_attack(s->crcda_ctx,
-                                       s->data, s->biglen, NULL)) {
+        if (s->cipher_in && detect_attack(s->crcda_ctx,
+                                          s->data, s->biglen, s->iv)) {
             ssh_sw_abort(s->bpp.ssh,
                          "Network attack (CRC compensation) detected!");
             crStopV;
         }
+        /* Save the last cipher block, to be passed to the next call
+         * to detect_attack */
+        assert(s->biglen >= 8);
+        memcpy(s->iv, s->data + s->biglen - 8, sizeof(s->iv));
 
-        if (s->cipher)
-            ssh1_cipher_decrypt(s->cipher, s->data, s->biglen);
+        if (s->cipher_in)
+            ssh_cipher_decrypt(s->cipher_in, s->data, s->biglen);
 
-        s->realcrc = crc32_compute(s->data, s->biglen - 4);
+        s->realcrc = crc32_ssh1(make_ptrlen(s->data, s->biglen - 4));
         s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
         if (s->gotcrc != s->realcrc) {
             ssh_sw_abort(s->bpp.ssh, "Incorrect CRC received on packet");
@@ -280,7 +296,7 @@ static PktOut *ssh1_bpp_new_pktout(int pkt_type)
 static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt)
 {
     int pad, biglen, i, pktoffs;
-    unsigned long crc;
+    uint32_t crc;
     int len;
 
     if (s->bpp.logctx) {
@@ -315,13 +331,13 @@ static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt)
 
     for (i = pktoffs; i < 4+8; i++)
         pkt->data[i] = random_byte();
-    crc = crc32_compute(pkt->data + pktoffs + 4,
-                        biglen - 4); /* all ex len */
+    crc = crc32_ssh1(
+        make_ptrlen(pkt->data + pktoffs + 4, biglen - 4)); /* all ex len */
     PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);
     PUT_32BIT(pkt->data + pktoffs, len);
 
-    if (s->cipher)
-        ssh1_cipher_encrypt(s->cipher, pkt->data + pktoffs + 4, biglen);
+    if (s->cipher_out)
+        ssh_cipher_encrypt(s->cipher_out, pkt->data + pktoffs + 4, biglen);
 
     bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
                  biglen + 4); /* len(length+padding+type+data+CRC) */

+ 1 - 0
source/putty/ssh1connection.h

@@ -50,6 +50,7 @@ struct ssh1_connection_state {
     struct outstanding_succfail *succfail_head, *succfail_tail;
 
     bool compressing;                  /* used in server mode only */
+    bool sent_exit_status;             /* also for server mode */
 
     ConnectionLayer cl;
     PacketProtocolLayer ppl;

+ 3 - 3
source/putty/ssh1login.c

@@ -362,9 +362,9 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
     ssh_bpp_handle_output(s->ppl.bpp);
 
     {
-        const ssh1_cipheralg *cipher =
-            (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish :
-             s->cipher_type == SSH_CIPHER_DES ? &ssh1_des : &ssh1_3des);
+        const ssh_cipheralg *cipher =
+            (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
+             s->cipher_type == SSH_CIPHER_DES ? &ssh_des : &ssh_3des_ssh1);
         ssh1_bpp_new_cipher(s->ppl.bpp, cipher, s->session_key);
     }
 

+ 35 - 36
source/putty/ssh2bpp.c

@@ -11,7 +11,7 @@
 
 struct ssh2_bpp_direction {
     unsigned long sequence;
-    ssh2_cipher *cipher;
+    ssh_cipher *cipher;
     ssh2_mac *mac;
     bool etm_mode;
     const ssh_compression_alg *pending_compression;
@@ -73,7 +73,7 @@ static void ssh2_bpp_free_outgoing_crypto(struct ssh2_bpp_state *s)
      * We must free the MAC before the cipher, because sometimes the
      * MAC is not actually separately allocated but just a different
      * facet of the same object as the cipher, in which case
-     * ssh2_mac_free does nothing and ssh2_cipher_free does the actual
+     * ssh2_mac_free does nothing and ssh_cipher_free does the actual
      * freeing. So if we freed the cipher first and then tried to
      * dereference the MAC's vtable pointer to find out how to free
      * that too, we'd be accessing freed memory.
@@ -81,7 +81,7 @@ static void ssh2_bpp_free_outgoing_crypto(struct ssh2_bpp_state *s)
     if (s->out.mac)
         ssh2_mac_free(s->out.mac);
     if (s->out.cipher)
-        ssh2_cipher_free(s->out.cipher);
+        ssh_cipher_free(s->out.cipher);
     if (s->out_comp)
         ssh_compressor_free(s->out_comp);
 }
@@ -92,7 +92,7 @@ static void ssh2_bpp_free_incoming_crypto(struct ssh2_bpp_state *s)
     if (s->in.mac)
         ssh2_mac_free(s->in.mac);
     if (s->in.cipher)
-        ssh2_cipher_free(s->in.cipher);
+        ssh_cipher_free(s->in.cipher);
     if (s->in_decomp)
         ssh_decompressor_free(s->in_decomp);
 }
@@ -109,7 +109,7 @@ static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
 
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
-    const ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
     const ssh_compression_alg *compression, bool delayed_compression)
 {
@@ -120,16 +120,16 @@ void ssh2_bpp_new_outgoing_crypto(
     ssh2_bpp_free_outgoing_crypto(s);
 
     if (cipher) {
-        s->out.cipher = ssh2_cipher_new(cipher);
-        ssh2_cipher_setkey(s->out.cipher, ckey);
-        ssh2_cipher_setiv(s->out.cipher, iv);
+        s->out.cipher = ssh_cipher_new(cipher);
+        ssh_cipher_setkey(s->out.cipher, ckey);
+        ssh_cipher_setiv(s->out.cipher, iv);
 
         s->cbc_ignore_workaround = (
-            (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
+            (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
             !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
 
         bpp_logevent("Initialised %s outbound encryption",
-                     ssh2_cipher_alg(s->out.cipher)->text_name);
+                     ssh_cipher_alg(s->out.cipher)->text_name);
     } else {
         s->out.cipher = NULL;
         s->cbc_ignore_workaround = false;
@@ -143,7 +143,7 @@ void ssh2_bpp_new_outgoing_crypto(
                      ssh2_mac_alg(s->out.mac)->text_name,
                      etm_mode ? " (in ETM mode)" : "",
                      (s->out.cipher &&
-                      ssh2_cipher_alg(s->out.cipher)->required_mac ?
+                      ssh_cipher_alg(s->out.cipher)->required_mac ?
                       " (required by cipher)" : ""));
     } else {
         s->out.mac = NULL;
@@ -171,7 +171,7 @@ void ssh2_bpp_new_outgoing_crypto(
 
 void ssh2_bpp_new_incoming_crypto(
     BinaryPacketProtocol *bpp,
-    const ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
     const ssh_compression_alg *compression, bool delayed_compression)
 {
@@ -182,12 +182,12 @@ void ssh2_bpp_new_incoming_crypto(
     ssh2_bpp_free_incoming_crypto(s);
 
     if (cipher) {
-        s->in.cipher = ssh2_cipher_new(cipher);
-        ssh2_cipher_setkey(s->in.cipher, ckey);
-        ssh2_cipher_setiv(s->in.cipher, iv);
+        s->in.cipher = ssh_cipher_new(cipher);
+        ssh_cipher_setkey(s->in.cipher, ckey);
+        ssh_cipher_setiv(s->in.cipher, iv);
 
         bpp_logevent("Initialised %s inbound encryption",
-                     ssh2_cipher_alg(s->in.cipher)->text_name);
+                     ssh_cipher_alg(s->in.cipher)->text_name);
     } else {
         s->in.cipher = NULL;
     }
@@ -200,7 +200,7 @@ void ssh2_bpp_new_incoming_crypto(
                      ssh2_mac_alg(s->in.mac)->text_name,
                      etm_mode ? " (in ETM mode)" : "",
                      (s->in.cipher &&
-                      ssh2_cipher_alg(s->in.cipher)->required_mac ?
+                      ssh_cipher_alg(s->in.cipher)->required_mac ?
                       " (required by cipher)" : ""));
     } else {
         s->in.mac = NULL;
@@ -283,7 +283,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
         s->maxlen = 0;
         s->length = 0;
         if (s->in.cipher)
-            s->cipherblk = ssh2_cipher_alg(s->in.cipher)->blksize;
+            s->cipherblk = ssh_cipher_alg(s->in.cipher)->blksize;
         else
             s->cipherblk = 8;
         if (s->cipherblk < 8)
@@ -291,7 +291,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
         s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0;
 
         if (s->in.cipher &&
-            (ssh2_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
+            (ssh_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
@@ -333,8 +333,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                 BPP_READ(s->buf + (s->packetlen + s->maclen), s->cipherblk);
                 /* Decrypt one more block (a little further back in
                  * the stream). */
-                ssh2_cipher_decrypt(s->in.cipher,
-                                    s->buf + s->packetlen, s->cipherblk);
+                ssh_cipher_decrypt(s->in.cipher,
+                                   s->buf + s->packetlen, s->cipherblk);
 
                 /* Feed that block to the MAC. */
                 put_data(s->in.mac,
@@ -376,12 +376,12 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
             BPP_READ(s->buf, 4);
 
             /* Cipher supports length decryption, so do it */
-            if (s->in.cipher && (ssh2_cipher_alg(s->in.cipher)->flags &
+            if (s->in.cipher && (ssh_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);
-                ssh2_cipher_decrypt_length(
+                ssh_cipher_decrypt_length(
                     s->in.cipher, len, 4, s->in.sequence);
                 s->len = toint(GET_32BIT(len));
             } else {
@@ -430,7 +430,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
 
             /* Decrypt everything between the length field and the MAC. */
             if (s->in.cipher)
-                ssh2_cipher_decrypt(
+                ssh_cipher_decrypt(
                     s->in.cipher, s->data + 4, s->packetlen - 4);
         } else {
             if (s->bufsize < s->cipherblk) {
@@ -445,8 +445,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
             BPP_READ(s->buf, s->cipherblk);
 
             if (s->in.cipher)
-                ssh2_cipher_decrypt(
-                    s->in.cipher, s->buf, s->cipherblk);
+                ssh_cipher_decrypt(s->in.cipher, s->buf, s->cipherblk);
 
             /*
              * Now get the length figure.
@@ -488,7 +487,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
 
             /* Decrypt everything _except_ the MAC. */
             if (s->in.cipher)
-                ssh2_cipher_decrypt(
+                ssh_cipher_decrypt(
                     s->in.cipher,
                     s->data + s->cipherblk, s->packetlen - s->cipherblk);
 
@@ -684,7 +683,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 ? ssh2_cipher_alg(s->out.cipher)->blksize : 8;
+    cipherblk = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 8;
     cipherblk = cipherblk < 8 ? 8 : cipherblk;  /* or 8 if blksize < 8 */
 
     if (s->out_comp) {
@@ -733,9 +732,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 &&
-        (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
-        ssh2_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
-                                   s->out.sequence);
+        (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+        ssh_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
+                                  s->out.sequence);
     }
 
     put_padding(pkt, maclen, 0);
@@ -745,8 +744,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
          * OpenSSH-defined encrypt-then-MAC protocol.
          */
         if (s->out.cipher)
-            ssh2_cipher_encrypt(s->out.cipher,
-                                pkt->data + 4, origlen + padding - 4);
+            ssh_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 {
@@ -757,7 +756,7 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
             ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
                               s->out.sequence);
         if (s->out.cipher)
-            ssh2_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
+            ssh_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
     }
 
     s->out.sequence++;       /* whether or not we MACed */
@@ -791,7 +790,7 @@ static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt)
         int block, length;
         PktOut *ignore_pkt;
 
-        block = s->out.cipher ? ssh2_cipher_alg(s->out.cipher)->blksize : 0;
+        block = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 0;
         if (block < 8)
             block = 8;
         length = pkt->length;
@@ -874,7 +873,7 @@ static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
          * from out_raw).
          */
         if (bufchain_size(s->bpp.out_raw) <
-            (ssh2_cipher_alg(s->out.cipher)->blksize +
+            (ssh_cipher_alg(s->out.cipher)->blksize +
              ssh2_mac_alg(s->out.mac)->len)) {
             /*
              * There's less data in out_raw than the MAC size plus the

+ 3 - 3
source/putty/ssh2transport.c

@@ -652,7 +652,7 @@ static void ssh2_write_kexinit_lists(
             if (!c) warn = true;
             else for (j = 0; j < c->nciphers; j++) {
                     alg = ssh2_kexinit_addalg(kexlists[k],
-                                              c->list[j]->name);
+                                              c->list[j]->ssh2_id);
                     alg->u.cipher.cipher = c->list[j];
                     alg->u.cipher.warn = warn;
                 }
@@ -1200,7 +1200,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
 
     if (s->warn_cscipher) {
         s->dlgret = seat_confirm_weak_crypto_primitive(
-            s->ppl.seat, "client-to-server cipher", s->out.cipher->name,
+            s->ppl.seat, "client-to-server cipher", s->out.cipher->ssh2_id,
             ssh2_transport_dialog_callback, s);
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {
@@ -1211,7 +1211,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
 
     if (s->warn_sccipher) {
         s->dlgret = seat_confirm_weak_crypto_primitive(
-            s->ppl.seat, "server-to-client cipher", s->in.cipher->name,
+            s->ppl.seat, "server-to-client cipher", s->in.cipher->ssh2_id,
             ssh2_transport_dialog_callback, s);
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {

+ 2 - 2
source/putty/ssh2transport.h

@@ -36,7 +36,7 @@ struct kexinit_algorithm {
             bool warn;
         } hk;
         struct {
-            const ssh2_cipheralg *cipher;
+            const ssh_cipheralg *cipher;
             bool warn;
         } cipher;
         struct {
@@ -103,7 +103,7 @@ typedef enum RekeyClass {
 } RekeyClass;
 
 typedef struct transport_direction {
-    const ssh2_cipheralg *cipher;
+    const ssh_cipheralg *cipher;
     const ssh2_macalg *mac;
     bool etm_mode;
     const ssh_compression_alg *comp;

+ 408 - 88
source/putty/sshaes.c

@@ -13,6 +13,7 @@
  */
 #define HW_AES_NONE 0
 #define HW_AES_NI 1
+#define HW_AES_NEON 2
 
 #ifdef _FORCE_AES_NI
 #   define HW_AES HW_AES_NI
@@ -32,6 +33,37 @@
 #   endif
 #endif
 
+#ifdef _FORCE_AES_NEON
+#   define HW_AES HW_AES_NEON
+#elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    /* Arm can potentially support both endiannesses, but this code
+     * hasn't been tested on anything but little. If anyone wants to
+     * run big-endian, they'll need to fix it first. */
+#elif defined __ARM_FEATURE_CRYPTO
+    /* If the Arm crypto extension is available already, we can
+     * support NEON AES without having to enable anything by hand */
+#   define HW_AES HW_AES_NEON
+#elif defined(__clang__)
+#   if __has_attribute(target) && __has_include(<arm_neon.h>) &&       \
+    (defined(__aarch64__))
+        /* clang can enable the crypto extension in AArch64 using
+         * __attribute__((target)) */
+#       define HW_AES HW_AES_NEON
+#       define USE_CLANG_ATTR_TARGET_AARCH64
+#   endif
+#elif defined _MSC_VER
+    /* Visual Studio supports the crypto extension when targeting
+     * AArch64, but as of VS2017, the AArch32 header doesn't quite
+     * manage it (declaring the aese/aesd intrinsics without a round
+     * key operand). */
+#   if defined _M_ARM64
+#       define HW_AES HW_AES_NEON
+#       if defined _M_ARM64
+#           define USE_ARM64_NEON_H /* unusual header name in this case */
+#       endif
+#   endif
+#endif
+
 #if defined _FORCE_SOFTWARE_AES || !defined HW_AES
 #   undef HW_AES
 #   define HW_AES HW_AES_NONE
@@ -39,6 +71,8 @@
 
 #if HW_AES == HW_AES_NI
 #define HW_NAME_SUFFIX " (AES-NI accelerated)"
+#elif HW_AES == HW_AES_NEON
+#define HW_NAME_SUFFIX " (NEON accelerated)"
 #else
 #define HW_NAME_SUFFIX " (!NONEXISTENT ACCELERATED VERSION!)"
 #endif
@@ -53,34 +87,34 @@
  * instance of.
  */
 
-static ssh2_cipher *aes_select(const ssh2_cipheralg *alg);
-static ssh2_cipher *aes_sw_new(const ssh2_cipheralg *alg);
-static void aes_sw_free(ssh2_cipher *);
-static void aes_sw_setiv_cbc(ssh2_cipher *, const void *iv);
-static void aes_sw_setiv_sdctr(ssh2_cipher *, const void *iv);
-static void aes_sw_setkey(ssh2_cipher *, const void *key);
-static ssh2_cipher *aes_hw_new(const ssh2_cipheralg *alg);
-static void aes_hw_free(ssh2_cipher *);
-static void aes_hw_setiv_cbc(ssh2_cipher *, const void *iv);
-static void aes_hw_setiv_sdctr(ssh2_cipher *, const void *iv);
-static void aes_hw_setkey(ssh2_cipher *, const void *key);
+static ssh_cipher *aes_select(const ssh_cipheralg *alg);
+static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg);
+static void aes_sw_free(ssh_cipher *);
+static void aes_sw_setiv_cbc(ssh_cipher *, const void *iv);
+static void aes_sw_setiv_sdctr(ssh_cipher *, const void *iv);
+static void aes_sw_setkey(ssh_cipher *, const void *key);
+static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg);
+static void aes_hw_free(ssh_cipher *);
+static void aes_hw_setiv_cbc(ssh_cipher *, const void *iv);
+static void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv);
+static void aes_hw_setkey(ssh_cipher *, const void *key);
 
 struct aes_extra {
-    const ssh2_cipheralg *sw, *hw;
+    const ssh_cipheralg *sw, *hw;
 };
 
 #define VTABLES(cid, pid, bits, name, encsuffix, decsuffix, setiv)      \
-    static void cid##_sw##encsuffix(ssh2_cipher *, void *blk, int len); \
-    static void cid##_sw##decsuffix(ssh2_cipher *, void *blk, int len); \
-    const ssh2_cipheralg ssh_##cid##_sw = {                             \
+    static void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len); \
+    static void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len); \
+    const ssh_cipheralg ssh_##cid##_sw = {                             \
         aes_sw_new, aes_sw_free, aes_sw_##setiv, aes_sw_setkey,         \
         cid##_sw##encsuffix, cid##_sw##decsuffix, NULL, NULL,           \
         pid, 16, bits, bits/8, 0, name " (unaccelerated)",              \
         NULL, NULL };                                                   \
                                                                         \
-    static void cid##_hw##encsuffix(ssh2_cipher *, void *blk, int len); \
-    static void cid##_hw##decsuffix(ssh2_cipher *, void *blk, int len); \
-    const ssh2_cipheralg ssh_##cid##_hw = {                             \
+    static void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len); \
+    static void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len); \
+    const ssh_cipheralg ssh_##cid##_hw = {                             \
         aes_hw_new, aes_hw_free, aes_hw_##setiv, aes_hw_setkey,         \
         cid##_hw##encsuffix, cid##_hw##decsuffix, NULL, NULL,           \
         pid, 16, bits, bits/8, 0, name HW_NAME_SUFFIX,                  \
@@ -89,7 +123,7 @@ struct aes_extra {
     const struct aes_extra extra_##cid = {                              \
         &ssh_##cid##_sw, &ssh_##cid##_hw };                             \
                                                                         \
-    const ssh2_cipheralg ssh_##cid = {                                  \
+    const ssh_cipheralg ssh_##cid = {                                  \
         aes_select, NULL, NULL, NULL, NULL, NULL, NULL, NULL,           \
         pid, 16, bits, bits/8, 0, name " (dummy selector vtable)",      \
         NULL, &extra_##cid };                                           \
@@ -101,14 +135,14 @@ VTABLES(aes128_sdctr, "aes128-ctr", 128, "AES-128 SDCTR",,, setiv_sdctr)
 VTABLES(aes192_sdctr, "aes192-ctr", 192, "AES-192 SDCTR",,, setiv_sdctr)
 VTABLES(aes256_sdctr, "aes256-ctr", 256, "AES-256 SDCTR",,, setiv_sdctr)
 
-static const ssh2_cipheralg ssh_rijndael_lysator = {
+static const ssh_cipheralg ssh_rijndael_lysator = {
     /* Same as aes256_cbc, but with a different protocol ID */
     aes_select, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     "[email protected]", 16, 256, 256/8, 0,
     "AES-256 CBC (dummy selector vtable)", NULL, &extra_aes256_cbc
 };
 
-static const ssh2_cipheralg *const aes_list[] = {
+static const ssh_cipheralg *const aes_list[] = {
     &ssh_aes256_sdctr,
     &ssh_aes256_cbc,
     &ssh_rijndael_lysator,
@@ -134,18 +168,20 @@ static bool aes_hw_available_cached(void)
 {
     static bool initialised = false;
     static bool hw_available;
-    if (!initialised)
+    if (!initialised) {
         hw_available = aes_hw_available();
+        initialised = true;
+    }
     return hw_available;
 }
 
-static ssh2_cipher *aes_select(const ssh2_cipheralg *alg)
+static ssh_cipher *aes_select(const ssh_cipheralg *alg)
 {
     const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
-    const ssh2_cipheralg *real_alg =
+    const ssh_cipheralg *real_alg =
         aes_hw_available_cached() ? extra->hw : extra->sw;
 
-    return ssh2_cipher_new(real_alg);
+    return ssh_cipher_new(real_alg);
 }
 
 /* ----------------------------------------------------------------------
@@ -951,36 +987,36 @@ struct aes_sw_context {
             uint8_t *keystream_pos;
         } sdctr;
     } iv;
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
 };
 
-static ssh2_cipher *aes_sw_new(const ssh2_cipheralg *alg)
+static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg)
 {
     aes_sw_context *ctx = snew(aes_sw_context);
     ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static void aes_sw_free(ssh2_cipher *ciph)
+static void aes_sw_free(ssh_cipher *ciph)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
     smemclr(ctx, sizeof(*ctx));
     sfree(ctx);
 }
 
-static void aes_sw_setkey(ssh2_cipher *ciph, const void *vkey)
+static void aes_sw_setkey(ssh_cipher *ciph, const void *vkey)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
     aes_sliced_key_setup(&ctx->sk, vkey, ctx->ciph.vt->real_keybits);
 }
 
-static void aes_sw_setiv_cbc(ssh2_cipher *ciph, const void *iv)
+static void aes_sw_setiv_cbc(ssh_cipher *ciph, const void *iv)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
     memcpy(ctx->iv.cbc.prevblk, iv, 16);
 }
 
-static void aes_sw_setiv_sdctr(ssh2_cipher *ciph, const void *viv)
+static void aes_sw_setiv_sdctr(ssh_cipher *ciph, const void *viv)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
     const uint8_t *iv = (const uint8_t *)viv;
@@ -1014,7 +1050,7 @@ static inline void memxor16(void *vout, const void *vlhs, const void *vrhs)
 }
 
 static inline void aes_cbc_sw_encrypt(
-    ssh2_cipher *ciph, void *vblk, int blklen)
+    ssh_cipher *ciph, void *vblk, int blklen)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
 
@@ -1045,7 +1081,7 @@ static inline void aes_cbc_sw_encrypt(
 }
 
 static inline void aes_cbc_sw_decrypt(
-    ssh2_cipher *ciph, void *vblk, int blklen)
+    ssh_cipher *ciph, void *vblk, int blklen)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
     uint8_t *blk = (uint8_t *)vblk;
@@ -1096,7 +1132,7 @@ static inline void aes_cbc_sw_decrypt(
 }
 
 static inline void aes_sdctr_sw(
-    ssh2_cipher *ciph, void *vblk, int blklen)
+    ssh_cipher *ciph, void *vblk, int blklen)
 {
     aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
 
@@ -1146,13 +1182,13 @@ static inline void aes_sdctr_sw(
 
 #define SW_ENC_DEC(len)                                 \
     static void aes##len##_cbc_sw_encrypt(              \
-        ssh2_cipher *ciph, void *vblk, int blklen)      \
+        ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
     static void aes##len##_cbc_sw_decrypt(              \
-        ssh2_cipher *ciph, void *vblk, int blklen)      \
+        ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
     static void aes##len##_sdctr_sw(                    \
-        ssh2_cipher *ciph, void *vblk, int blklen)      \
+        ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_sdctr_sw(ciph, vblk, blklen); }
 
 SW_ENC_DEC(128)
@@ -1331,10 +1367,10 @@ struct aes_ni_context {
     __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
 
     void *pointer_to_free;
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
 };
 
-static ssh2_cipher *aes_hw_new(const ssh2_cipheralg *alg)
+static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
 {
     if (!aes_hw_available_cached())
         return NULL;
@@ -1357,7 +1393,7 @@ static ssh2_cipher *aes_hw_new(const ssh2_cipheralg *alg)
     return &ctx->ciph;
 }
 
-static void aes_hw_free(ssh2_cipher *ciph)
+static void aes_hw_free(ssh_cipher *ciph)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
     void *allocation = ctx->pointer_to_free;
@@ -1365,7 +1401,7 @@ static void aes_hw_free(ssh2_cipher *ciph)
     sfree(allocation);
 }
 
-static void aes_hw_setkey(ssh2_cipher *ciph, const void *vkey)
+static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
     const unsigned char *key = (const unsigned char *)vkey;
@@ -1374,13 +1410,13 @@ static void aes_hw_setkey(ssh2_cipher *ciph, const void *vkey)
                       ctx->keysched_e, ctx->keysched_d);
 }
 
-static FUNC_ISA void aes_hw_setiv_cbc(ssh2_cipher *ciph, const void *iv)
+static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
     ctx->iv = _mm_loadu_si128(iv);
 }
 
-static FUNC_ISA void aes_hw_setiv_sdctr(ssh2_cipher *ciph, const void *iv)
+static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
     __m128i counter = _mm_loadu_si128(iv);
@@ -1390,7 +1426,7 @@ static FUNC_ISA void aes_hw_setiv_sdctr(ssh2_cipher *ciph, const void *iv)
 typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
 
 static FUNC_ISA inline void aes_cbc_ni_encrypt(
-    ssh2_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
 
@@ -1405,7 +1441,7 @@ static FUNC_ISA inline void aes_cbc_ni_encrypt(
 }
 
 static FUNC_ISA inline void aes_cbc_ni_decrypt(
-    ssh2_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
 
@@ -1420,7 +1456,7 @@ static FUNC_ISA inline void aes_cbc_ni_decrypt(
 }
 
 static FUNC_ISA inline void aes_sdctr_ni(
-    ssh2_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
 {
     aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
 
@@ -1437,19 +1473,330 @@ static FUNC_ISA inline void aes_sdctr_ni(
 
 #define NI_ENC_DEC(len)                                                 \
     static FUNC_ISA void aes##len##_cbc_hw_encrypt(                     \
-        ssh2_cipher *ciph, void *vblk, int blklen)                      \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
     { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
     static FUNC_ISA void aes##len##_cbc_hw_decrypt(                     \
-        ssh2_cipher *ciph, void *vblk, int blklen)                      \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
     { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
     static FUNC_ISA void aes##len##_sdctr_hw(                           \
-        ssh2_cipher *ciph, void *vblk, int blklen)                      \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
     { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
 
 NI_ENC_DEC(128)
 NI_ENC_DEC(192)
 NI_ENC_DEC(256)
 
+/* ----------------------------------------------------------------------
+ * Hardware-accelerated implementation of AES using Arm NEON.
+ */
+
+#elif HW_AES == HW_AES_NEON
+
+/*
+ * Manually set the target architecture, if we decided above that we
+ * need to.
+ */
+#ifdef USE_CLANG_ATTR_TARGET_AARCH64
+/*
+ * A spot of cheating: redefine some ACLE feature macros before
+ * including arm_neon.h. Otherwise we won't get the AES intrinsics
+ * defined by that header, because it will be looking at the settings
+ * for the whole translation unit rather than the ones we're going to
+ * put on some particular functions using __attribute__((target)).
+ */
+#define __ARM_NEON 1
+#define __ARM_FEATURE_CRYPTO 1
+#define FUNC_ISA __attribute__ ((target("neon,crypto")))
+#endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
+
+#ifndef FUNC_ISA
+#define FUNC_ISA
+#endif
+
+#ifdef USE_ARM64_NEON_H
+#include <arm64_neon.h>
+#else
+#include <arm_neon.h>
+#endif
+
+static bool aes_hw_available(void)
+{
+    /*
+     * For Arm, we delegate to a per-platform AES detection function,
+     * because it has to be implemented by asking the operating system
+     * rather than directly querying the CPU.
+     *
+     * That's because Arm systems commonly have multiple cores that
+     * are not all alike, so any method of querying whether NEON
+     * crypto instructions work on the _current_ CPU - even one as
+     * crude as just trying one and catching the SIGILL - wouldn't
+     * give an answer that you could still rely on the first time the
+     * OS migrated your process to another CPU.
+     */
+    return platform_aes_hw_available();
+}
+
+/*
+ * Core NEON encrypt/decrypt functions, one per length and direction.
+ */
+
+#define NEON_CIPHER(len, repmacro)                              \
+    static FUNC_ISA inline uint8x16_t aes_neon_##len##_e(       \
+        uint8x16_t v, const uint8x16_t *keysched)               \
+    {                                                           \
+        repmacro(v = vaesmcq_u8(vaeseq_u8(v, *keysched++)););   \
+        v = vaeseq_u8(v, *keysched++);                          \
+        return veorq_u8(v, *keysched);                          \
+    }                                                           \
+    static FUNC_ISA inline uint8x16_t aes_neon_##len##_d(       \
+        uint8x16_t v, const uint8x16_t *keysched)               \
+    {                                                           \
+        repmacro(v = vaesimcq_u8(vaesdq_u8(v, *keysched++)););  \
+        v = vaesdq_u8(v, *keysched++);                          \
+        return veorq_u8(v, *keysched);                          \
+    }
+
+NEON_CIPHER(128, REP9)
+NEON_CIPHER(192, REP11)
+NEON_CIPHER(256, REP13)
+
+/*
+ * The main key expansion.
+ */
+static FUNC_ISA void aes_neon_key_expand(
+    const unsigned char *key, size_t key_words,
+    uint8x16_t *keysched_e, uint8x16_t *keysched_d)
+{
+    size_t rounds = key_words + 6;
+    size_t sched_words = (rounds + 1) * 4;
+
+    /*
+     * Store the key schedule as 32-bit integers during expansion, so
+     * that it's easy to refer back to individual previous words. We
+     * collect them into the final uint8x16_t form at the end.
+     */
+    uint32_t sched[MAXROUNDKEYS * 4];
+
+    unsigned rconpos = 0;
+
+    for (size_t i = 0; i < sched_words; i++) {
+	if (i < key_words) {
+            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
+        } else {
+	    uint32_t temp = sched[i - 1];
+
+            bool rotate_and_round_constant = (i % key_words == 0);
+            bool sub = rotate_and_round_constant ||
+                (key_words == 8 && i % 8 == 4);
+
+            if (rotate_and_round_constant)
+                temp = (temp << 24) | (temp >> 8);
+
+            if (sub) {
+                uint32x4_t v32 = vdupq_n_u32(temp);
+                uint8x16_t v8 = vreinterpretq_u8_u32(v32);
+                v8 = vaeseq_u8(v8, vdupq_n_u8(0));
+                v32 = vreinterpretq_u32_u8(v8);
+                temp = vget_lane_u32(vget_low_u32(v32), 0);
+            }
+
+            if (rotate_and_round_constant) {
+                assert(rconpos < lenof(key_setup_round_constants));
+                temp ^= key_setup_round_constants[rconpos++];
+            }
+
+            sched[i] = sched[i - key_words] ^ temp;
+	}
+    }
+
+    /*
+     * Combine the key schedule words into uint8x16_t vectors and
+     * store them in the output context.
+     */
+    for (size_t round = 0; round <= rounds; round++)
+        keysched_e[round] = vreinterpretq_u8_u32(vld1q_u32(sched + 4*round));
+
+    smemclr(sched, sizeof(sched));
+
+    /*
+     * Now prepare the modified keys for the inverse cipher.
+     */
+    for (size_t eround = 0; eround <= rounds; eround++) {
+        size_t dround = rounds - eround;
+        uint8x16_t rkey = keysched_e[eround];
+        if (eround && dround)      /* neither first nor last */
+            rkey = vaesimcq_u8(rkey);
+        keysched_d[dround] = rkey;
+    }
+}
+
+/*
+ * Auxiliary routine to reverse the byte order of a vector, so that
+ * the SDCTR IV can be made big-endian for feeding to the cipher.
+ *
+ * In fact we don't need to reverse the vector _all_ the way; we leave
+ * the two lanes in MSW,LSW order, because that makes no difference to
+ * the efficiency of the increment. That way we only have to reverse
+ * bytes within each lane in this function.
+ */
+static FUNC_ISA inline uint8x16_t aes_neon_sdctr_reverse(uint8x16_t v)
+{
+    return vrev64q_u8(v);
+}
+
+/*
+ * Auxiliary routine to increment the 128-bit counter used in SDCTR
+ * mode. There's no instruction to treat a 128-bit vector as a single
+ * long integer, so instead we have to increment the bottom half
+ * unconditionally, and the top half if the bottom half started off as
+ * all 1s (in which case there was about to be a carry).
+ */
+static FUNC_ISA inline uint8x16_t aes_neon_sdctr_increment(uint8x16_t in)
+{
+#ifdef __aarch64__
+    /* There will be a carry if the low 64 bits are all 1s. */
+    uint64x1_t all1 = vcreate_u64(0xFFFFFFFFFFFFFFFF);
+    uint64x1_t carry = vceq_u64(vget_high_u64(vreinterpretq_u64_u8(in)), all1);
+
+    /* Make a word whose bottom half is unconditionally all 1s, and
+     * the top half is 'carry', i.e. all 0s most of the time but all
+     * 1s if we need to increment the top half. Then that word is what
+     * we need to _subtract_ from the input counter. */
+    uint64x2_t subtrahend = vcombine_u64(carry, all1);
+#else
+    /* AArch32 doesn't have comparisons that operate on a 64-bit lane,
+     * so we start by comparing each 32-bit half of the low 64 bits
+     * _separately_ to all-1s. */
+    uint32x2_t all1 = vdup_n_u32(0xFFFFFFFF);
+    uint32x2_t carry = vceq_u32(
+        vget_high_u32(vreinterpretq_u32_u8(in)), all1);
+
+    /* Swap the 32-bit words of the compare output, and AND with the
+     * unswapped version. Now carry is all 1s iff the bottom half of
+     * the input counter was all 1s, and all 0s otherwise. */
+    carry = vand_u32(carry, vrev64_u32(carry));
+
+    /* Now make the vector to subtract in the same way as above. */
+    uint64x2_t subtrahend = vreinterpretq_u64_u32(vcombine_u32(carry, all1));
+#endif
+
+    return vreinterpretq_u8_u64(
+        vsubq_u64(vreinterpretq_u64_u8(in), subtrahend));
+}
+
+/*
+ * The SSH interface and the cipher modes.
+ */
+
+typedef struct aes_neon_context aes_neon_context;
+struct aes_neon_context {
+    uint8x16_t keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
+
+    ssh_cipher ciph;
+};
+
+static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
+{
+    if (!aes_hw_available_cached())
+        return NULL;
+
+    aes_neon_context *ctx = snew(aes_neon_context);
+    ctx->ciph.vt = alg;
+    return &ctx->ciph;
+}
+
+static void aes_hw_free(ssh_cipher *ciph)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+    smemclr(ctx, sizeof(*ctx));
+    sfree(ctx);
+}
+
+static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+    const unsigned char *key = (const unsigned char *)vkey;
+
+    aes_neon_key_expand(key, ctx->ciph.vt->real_keybits / 32,
+                      ctx->keysched_e, ctx->keysched_d);
+}
+
+static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+    ctx->iv = vld1q_u8(iv);
+}
+
+static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+    uint8x16_t counter = vld1q_u8(iv);
+    ctx->iv = aes_neon_sdctr_reverse(counter);
+}
+
+typedef uint8x16_t (*aes_neon_fn)(uint8x16_t v, const uint8x16_t *keysched);
+
+static FUNC_ISA inline void aes_cbc_neon_encrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        uint8x16_t plaintext = vld1q_u8(blk);
+        uint8x16_t cipher_input = veorq_u8(plaintext, ctx->iv);
+        uint8x16_t ciphertext = encrypt(cipher_input, ctx->keysched_e);
+        vst1q_u8(blk, ciphertext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static FUNC_ISA inline void aes_cbc_neon_decrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn decrypt)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        uint8x16_t ciphertext = vld1q_u8(blk);
+        uint8x16_t decrypted = decrypt(ciphertext, ctx->keysched_d);
+        uint8x16_t plaintext = veorq_u8(decrypted, ctx->iv);
+        vst1q_u8(blk, plaintext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static FUNC_ISA inline void aes_sdctr_neon(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
+{
+    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        uint8x16_t counter = aes_neon_sdctr_reverse(ctx->iv);
+        uint8x16_t keystream = encrypt(counter, ctx->keysched_e);
+        uint8x16_t input = vld1q_u8(blk);
+        uint8x16_t output = veorq_u8(input, keystream);
+        vst1q_u8(blk, output);
+        ctx->iv = aes_neon_sdctr_increment(ctx->iv);
+    }
+}
+
+#define NEON_ENC_DEC(len)                                               \
+    static FUNC_ISA void aes##len##_cbc_hw_encrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_neon_encrypt(ciph, vblk, blklen, aes_neon_##len##_e); }   \
+    static FUNC_ISA void aes##len##_cbc_hw_decrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_neon_decrypt(ciph, vblk, blklen, aes_neon_##len##_d); }   \
+    static FUNC_ISA void aes##len##_sdctr_hw(                           \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_sdctr_neon(ciph, vblk, blklen, aes_neon_##len##_e); }         \
+
+NEON_ENC_DEC(128)
+NEON_ENC_DEC(192)
+NEON_ENC_DEC(256)
+
 /* ----------------------------------------------------------------------
  * Stub functions if we have no hardware-accelerated AES. In this
  * case, aes_hw_new returns NULL (though it should also never be
@@ -1465,54 +1812,27 @@ bool aes_hw_available(void)
     return false;
 }
 
-static ssh2_cipher *aes_hw_new(const ssh2_cipheralg *alg)
+static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
 {
     return NULL;
 }
 
 #define STUB_BODY { unreachable("Should never be called"); }
 
-static void aes_hw_free(ssh2_cipher *ciph) STUB_BODY
-static void aes_hw_setkey(ssh2_cipher *ciph, const void *key) STUB_BODY
-static void aes_hw_setiv_cbc(ssh2_cipher *ciph, const void *iv) STUB_BODY
-static void aes_hw_setiv_sdctr(ssh2_cipher *ciph, const void *iv) STUB_BODY
-#define STUB_ENC_DEC(len)                                               \
-    static void aes##len##_cbc_hw_encrypt(                              \
-        ssh2_cipher *ciph, void *vblk, int blklen) STUB_BODY            \
-    static void aes##len##_cbc_hw_decrypt(                              \
-        ssh2_cipher *ciph, void *vblk, int blklen) STUB_BODY            \
-    static void aes##len##_sdctr_hw(                                    \
-        ssh2_cipher *ciph, void *vblk, int blklen) STUB_BODY
+static void aes_hw_free(ssh_cipher *ciph) STUB_BODY
+static void aes_hw_setkey(ssh_cipher *ciph, const void *key) STUB_BODY
+static void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) STUB_BODY
+static void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) STUB_BODY
+#define STUB_ENC_DEC(len)                                       \
+    static void aes##len##_cbc_hw_encrypt(                      \
+        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
+    static void aes##len##_cbc_hw_decrypt(                      \
+        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
+    static void aes##len##_sdctr_hw(                            \
+        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY
 
 STUB_ENC_DEC(128)
 STUB_ENC_DEC(192)
 STUB_ENC_DEC(256)
 
 #endif /* HW_AES */
-
-/* ----------------------------------------------------------------------
- * Auxiliary routines for use of AES in other contexts than the main
- * SSH packet protocol.
- */
-
-void aes256_encrypt_pubkey(const void *key, void *blk, int len)
-{
-    char iv[16];
-    memset(iv, 0, 16);
-    ssh2_cipher *cipher = ssh2_cipher_new(&ssh_aes256_cbc);
-    ssh2_cipher_setkey(cipher, key);
-    ssh2_cipher_setiv(cipher, iv);
-    ssh2_cipher_encrypt(cipher, blk, len);
-    ssh2_cipher_free(cipher);
-}
-
-void aes256_decrypt_pubkey(const void *key, void *blk, int len)
-{
-    char iv[16];
-    memset(iv, 0, 16);
-    ssh2_cipher *cipher = ssh2_cipher_new(&ssh_aes256_cbc);
-    ssh2_cipher_setkey(cipher, key);
-    ssh2_cipher_setiv(cipher, iv);
-    ssh2_cipher_decrypt(cipher, blk, len);
-    ssh2_cipher_free(cipher);
-}

+ 9 - 9
source/putty/ssharcf.c

@@ -9,7 +9,7 @@
 
 typedef struct {
     unsigned char i, j, s[256];
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
 } ArcfourContext;
 
 static void arcfour_block(void *handle, void *vblk, int len)
@@ -62,14 +62,14 @@ static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key,
  * to leak data about the key.
  */
 
-static ssh2_cipher *arcfour_new(const ssh2_cipheralg *alg)
+static ssh_cipher *arcfour_new(const ssh_cipheralg *alg)
 {
     ArcfourContext *ctx = snew(ArcfourContext);
     ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static void arcfour_free(ssh2_cipher *cipher)
+static void arcfour_free(ssh_cipher *cipher)
 {
     ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
     smemclr(ctx, sizeof(*ctx));
@@ -85,25 +85,25 @@ static void arcfour_stir(ArcfourContext *ctx)
     sfree(junk);
 }
 
-static void arcfour_ssh2_setiv(ssh2_cipher *cipher, const void *key)
+static void arcfour_ssh2_setiv(ssh_cipher *cipher, const void *key)
 {
     /* As a pure stream cipher, Arcfour has no IV separate from the key */
 }
 
-static void arcfour_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+static void arcfour_ssh2_setkey(ssh_cipher *cipher, const void *key)
 {
     ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
     arcfour_setkey(ctx, key, ctx->ciph.vt->padded_keybytes);
     arcfour_stir(ctx);
 }
 
-static void arcfour_ssh2_block(ssh2_cipher *cipher, void *blk, int len)
+static void arcfour_ssh2_block(ssh_cipher *cipher, void *blk, int len)
 {
     ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
     arcfour_block(ctx, blk, len);
 }
 
-const ssh2_cipheralg ssh_arcfour128_ssh2 = {
+const ssh_cipheralg ssh_arcfour128_ssh2 = {
     arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
     arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
     "arcfour128",
@@ -111,7 +111,7 @@ const ssh2_cipheralg ssh_arcfour128_ssh2 = {
     NULL
 };
 
-const ssh2_cipheralg ssh_arcfour256_ssh2 = {
+const ssh_cipheralg ssh_arcfour256_ssh2 = {
     arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey,
     arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL,
     "arcfour256",
@@ -119,7 +119,7 @@ const ssh2_cipheralg ssh_arcfour256_ssh2 = {
     NULL
 };
 
-static const ssh2_cipheralg *const arcfour_list[] = {
+static const ssh_cipheralg *const arcfour_list[] = {
     &ssh_arcfour256_ssh2,
     &ssh_arcfour128_ssh2,
 };

+ 152 - 0
source/putty/sshauxcrypt.c

@@ -0,0 +1,152 @@
+/*
+ * sshauxcrypt.c: wrapper functions on ciphers for use in other
+ * contexts than the main SSH packet protocol, such as encrypting
+ * private key files and performing XDM-AUTHORIZATION-1.
+ *
+ * These all work through the standard cipher APIs, so they don't need
+ * to live in the same actual source files as the ciphers they wrap,
+ * and I think it keeps things tidier to have them out of the way here
+ * instead.
+ */
+
+#include "ssh.h"
+
+static ssh_cipher *aes256_pubkey_cipher(const void *key)
+{
+    /*
+     * PuTTY's own .PPK format for SSH-2 private key files is
+     * encrypted with 256-bit AES in CBC mode.
+     */
+    char iv[16];
+    memset(iv, 0, 16);
+    ssh_cipher *cipher = ssh_cipher_new(&ssh_aes256_cbc);
+    ssh_cipher_setkey(cipher, key);
+    ssh_cipher_setiv(cipher, iv);
+    return cipher;
+}
+
+void aes256_encrypt_pubkey(const void *key, void *blk, int len)
+{
+    ssh_cipher *c = aes256_pubkey_cipher(key);
+    ssh_cipher_encrypt(c, blk, len);
+    ssh_cipher_free(c);
+}
+
+void aes256_decrypt_pubkey(const void *key, void *blk, int len)
+{
+    ssh_cipher *c = aes256_pubkey_cipher(key);
+    ssh_cipher_decrypt(c, blk, len);
+    ssh_cipher_free(c);
+}
+
+static ssh_cipher *des3_pubkey_cipher(const void *vkey)
+{
+    /*
+     * SSH-1 private key files are encrypted with triple-DES in SSH-1
+     * style (three separate CBC layers), but the same key is used for
+     * the first and third layers.
+     */
+    ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh1);
+    uint8_t keys3[24], iv[8];
+
+    memcpy(keys3, vkey, 16);
+    memcpy(keys3 + 16, vkey, 8);
+    ssh_cipher_setkey(c, keys3);
+    smemclr(keys3, sizeof(keys3));
+
+    memset(iv, 0, 8);
+    ssh_cipher_setiv(c, iv);
+
+    return c;
+}
+
+void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_cipher(vkey);
+    ssh_cipher_decrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+void des3_encrypt_pubkey(const void *vkey, void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_cipher(vkey);
+    ssh_cipher_encrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+static ssh_cipher *des3_pubkey_ossh_cipher(const void *vkey, const void *viv)
+{
+    /*
+     * OpenSSH PEM private key files are encrypted with triple-DES in
+     * SSH-2 style (one CBC layer), with three distinct keys, and an
+     * IV also generated from the passphrase.
+     */
+    ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh2);
+    ssh_cipher_setkey(c, vkey);
+    ssh_cipher_setiv(c, viv);
+    return c;
+}
+
+void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv,
+			      void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_decrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv,
+			      void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_encrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+static ssh_cipher *des_xdmauth_cipher(const void *vkeydata)
+{
+    /*
+     * XDM-AUTHORIZATION-1 uses single-DES, but packs the key into 7
+     * bytes, so here we have to repack it manually into the canonical
+     * form where it occupies 8 bytes each with the low bit unused.
+     */
+    const unsigned char *keydata = (const unsigned char *)vkeydata;
+    unsigned char key[8];
+    int i, nbits, j;
+    unsigned int bits;
+
+    bits = 0;
+    nbits = 0;
+    j = 0;
+    for (i = 0; i < 8; i++) {
+	if (nbits < 7) {
+	    bits = (bits << 8) | keydata[j];
+	    nbits += 8;
+	    j++;
+	}
+	key[i] = (bits >> (nbits - 7)) << 1;
+	bits &= ~(0x7F << (nbits - 7));
+	nbits -= 7;
+    }
+
+    ssh_cipher *c = ssh_cipher_new(&ssh_des);
+    ssh_cipher_setkey(c, key);
+    smemclr(key, sizeof(key));
+    ssh_cipher_setiv(c, key);
+    return c;
+}
+
+void des_encrypt_xdmauth(const void *keydata, void *blk, int len)
+{
+    ssh_cipher *c = des_xdmauth_cipher(keydata);
+    ssh_cipher_encrypt(c, blk, len);
+    ssh_cipher_free(c);
+}
+
+void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
+{
+    ssh_cipher *c = des_xdmauth_cipher(keydata);
+    ssh_cipher_decrypt(c, blk, len);
+    ssh_cipher_free(c);
+}
+

+ 50 - 73
source/putty/sshblowf.c

@@ -566,138 +566,115 @@ void blowfish_free_context(BlowfishContext *ctx)
     sfree(ctx);
 }
 
-static void blowfish_iv(BlowfishContext *ctx, const void *viv)
+static void blowfish_iv_be(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 */
-    BlowfishContext contexts[2];
-    ssh1_cipher ciph;
+static void blowfish_iv_le(BlowfishContext *ctx, const void *viv)
+{
+    const unsigned char *iv = (const unsigned char *)viv;
+    ctx->iv0 = GET_32BIT_LSB_FIRST(iv);
+    ctx->iv1 = GET_32BIT_LSB_FIRST(iv + 4);
+}
+
+struct blowfish_ctx {
+    BlowfishContext context;
+    ssh_cipher ciph;
 };
 
-static ssh1_cipher *blowfish_ssh1_new(void)
+static ssh_cipher *blowfish_new(const ssh_cipheralg *alg)
 {
-    struct blowfish_ssh1_ctx *ctx = snew(struct blowfish_ssh1_ctx);
-    ctx->ciph.vt = &ssh1_blowfish;
+    struct blowfish_ctx *ctx = snew(struct blowfish_ctx);
+    ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static void blowfish_ssh1_free(ssh1_cipher *cipher)
+static void blowfish_free(ssh_cipher *cipher)
 {
-    struct blowfish_ssh1_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh1_ctx, ciph);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
     smemclr(ctx, sizeof(*ctx));
     sfree(ctx);
 }
 
-static void blowfish_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
-{
-    struct blowfish_ssh1_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh1_ctx, ciph);
-    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 blowfish_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ssh1_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh1_ctx, ciph);
-    blowfish_lsb_encrypt_cbc(blk, len, ctx->contexts);
-}
-
-static void blowfish_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+static void blowfish_ssh_setkey(ssh_cipher *cipher, const void *key)
 {
-    struct blowfish_ssh1_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh1_ctx, ciph);
-    blowfish_lsb_decrypt_cbc(blk, len, ctx->contexts+1);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
+    blowfish_setkey(&ctx->context, key, ctx->ciph.vt->padded_keybytes);
 }
 
-struct blowfish_ssh2_ctx {
-    BlowfishContext context;
-    ssh2_cipher ciph;
-};
-
-static ssh2_cipher *blowfish_ssh2_new(const ssh2_cipheralg *alg)
+static void blowfish_ssh1_setiv(ssh_cipher *cipher, const void *iv)
 {
-    struct blowfish_ssh2_ctx *ctx = snew(struct blowfish_ssh2_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
+    blowfish_iv_le(&ctx->context, iv);
 }
 
-static void blowfish_ssh2_free(ssh2_cipher *cipher)
+static void blowfish_ssh2_setiv(ssh_cipher *cipher, const void *iv)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
+    blowfish_iv_be(&ctx->context, iv);
 }
 
-static void blowfish_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+static void blowfish_ssh1_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
-    blowfish_iv(&ctx->context, iv);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
+    blowfish_lsb_encrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+static void blowfish_ssh1_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
-    blowfish_setkey(&ctx->context, key, ctx->ciph.vt->padded_keybytes);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
+    blowfish_lsb_decrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void blowfish_ssh2_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
     blowfish_msb_encrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void blowfish_ssh2_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
     blowfish_msb_decrypt_cbc(blk, len, &ctx->context);
 }
 
-static void blowfish_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
+static void blowfish_ssh2_sdctr(ssh_cipher *cipher, void *blk, int len)
 {
-    struct blowfish_ssh2_ctx *ctx =
-        container_of(cipher, struct blowfish_ssh2_ctx, ciph);
+    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
     blowfish_msb_sdctr(blk, len, &ctx->context);
 }
 
-const ssh1_cipheralg ssh1_blowfish = {
-    blowfish_ssh1_new, blowfish_ssh1_free,
-    blowfish_ssh1_sesskey,
+const ssh_cipheralg ssh_blowfish_ssh1 = {
+    blowfish_new, blowfish_free,
+    blowfish_ssh1_setiv, blowfish_ssh_setkey,
     blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk,
-    8, "Blowfish-128 CBC"
+    NULL, NULL, NULL,
+    8, 128, SSH1_SESSION_KEY_LENGTH, SSH_CIPHER_IS_CBC, "Blowfish-256 CBC",
+    NULL
 };
 
-const ssh2_cipheralg ssh_blowfish_ssh2 = {
-    blowfish_ssh2_new, blowfish_ssh2_free,
-    blowfish_ssh2_setiv, blowfish_ssh2_setkey,
+const ssh_cipheralg ssh_blowfish_ssh2 = {
+    blowfish_new, blowfish_free,
+    blowfish_ssh2_setiv, blowfish_ssh_setkey,
     blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk, NULL, NULL,
     "blowfish-cbc",
     8, 128, 16, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC",
     NULL
 };
 
-const ssh2_cipheralg ssh_blowfish_ssh2_ctr = {
-    blowfish_ssh2_new, blowfish_ssh2_free,
-    blowfish_ssh2_setiv, blowfish_ssh2_setkey,
+const ssh_cipheralg ssh_blowfish_ssh2_ctr = {
+    blowfish_new, blowfish_free,
+    blowfish_ssh2_setiv, blowfish_ssh_setkey,
     blowfish_ssh2_sdctr, blowfish_ssh2_sdctr, NULL, NULL,
     "blowfish-ctr",
     8, 256, 32, 0, "Blowfish-256 SDCTR",
     NULL
 };
 
-static const ssh2_cipheralg *const blowfish_list[] = {
+static const ssh_cipheralg *const blowfish_list[] = {
     &ssh_blowfish_ssh2_ctr,
     &ssh_blowfish_ssh2
 };

+ 3 - 3
source/putty/sshbpp.h

@@ -54,7 +54,7 @@ void ssh_bpp_free(BinaryPacketProtocol *bpp);
 
 BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx);
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
-                         const ssh1_cipheralg *cipher,
+                         const ssh_cipheralg *cipher,
                          const void *session_key);
 /* This is only called from outside the BPP in server mode; in client
  * mode the BPP detects compression start time automatically by
@@ -104,12 +104,12 @@ BinaryPacketProtocol *ssh2_bpp_new(
     LogContext *logctx, struct DataTransferStats *stats, bool is_server);
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
-    const ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
     const ssh_compression_alg *compression, bool delayed_compression);
 void ssh2_bpp_new_incoming_crypto(
     BinaryPacketProtocol *bpp,
-    const ssh2_cipheralg *cipher, const void *ckey, const void *iv,
+    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
     const ssh_compression_alg *compression, bool delayed_compression);
 

+ 13 - 13
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_cipheralg structure ensures that the MAC is
+ * configuration of the ssh_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
@@ -867,12 +867,12 @@ struct ccp_context {
     struct poly1305 mac;
 
     BinarySink_IMPLEMENTATION;
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
     ssh2_mac mac_if;
 };
 
 static ssh2_mac *poly_ssh2_new(
-    const ssh2_macalg *alg, ssh2_cipher *cipher)
+    const ssh2_macalg *alg, ssh_cipher *cipher)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
     ctx->mac_if.vt = alg;
@@ -946,7 +946,7 @@ const ssh2_macalg ssh2_poly1305 = {
     16, 0, "Poly1305"
 };
 
-static ssh2_cipher *ccp_new(const ssh2_cipheralg *alg)
+static ssh_cipher *ccp_new(const ssh_cipheralg *alg)
 {
     struct ccp_context *ctx = snew(struct ccp_context);
     BinarySink_INIT(ctx, poly_BinarySink_write);
@@ -955,7 +955,7 @@ static ssh2_cipher *ccp_new(const ssh2_cipheralg *alg)
     return &ctx->ciph;
 }
 
-static void ccp_free(ssh2_cipher *cipher)
+static void ccp_free(ssh_cipher *cipher)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
     smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
@@ -964,14 +964,14 @@ static void ccp_free(ssh2_cipher *cipher)
     sfree(ctx);
 }
 
-static void ccp_iv(ssh2_cipher *cipher, const void *iv)
+static void ccp_iv(ssh_cipher *cipher, const void *iv)
 {
     /* struct ccp_context *ctx =
            container_of(cipher, struct ccp_context, ciph); */
     /* IV is set based on the sequence number */
 }
 
-static void ccp_key(ssh2_cipher *cipher, const void *vkey)
+static void ccp_key(ssh_cipher *cipher, const void *vkey)
 {
     const unsigned char *key = (const unsigned char *)vkey;
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
@@ -981,13 +981,13 @@ static void ccp_key(ssh2_cipher *cipher, const void *vkey)
     chacha20_key(&ctx->b_cipher, key);
 }
 
-static void ccp_encrypt(ssh2_cipher *cipher, void *blk, int len)
+static void ccp_encrypt(ssh_cipher *cipher, void *blk, int len)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
     chacha20_encrypt(&ctx->b_cipher, blk, len);
 }
 
-static void ccp_decrypt(ssh2_cipher *cipher, void *blk, int len)
+static void ccp_decrypt(ssh_cipher *cipher, void *blk, int len)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
     chacha20_decrypt(&ctx->b_cipher, blk, len);
@@ -1010,7 +1010,7 @@ static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
     smemclr(iv, sizeof(iv));
 }
 
-static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
+static void ccp_encrypt_length(ssh_cipher *cipher, void *blk, int len,
                                unsigned long seq)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
@@ -1018,7 +1018,7 @@ static void ccp_encrypt_length(ssh2_cipher *cipher, void *blk, int len,
     chacha20_encrypt(&ctx->a_cipher, blk, len);
 }
 
-static void ccp_decrypt_length(ssh2_cipher *cipher, void *blk, int len,
+static void ccp_decrypt_length(ssh_cipher *cipher, void *blk, int len,
                                unsigned long seq)
 {
     struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
@@ -1026,7 +1026,7 @@ static void ccp_decrypt_length(ssh2_cipher *cipher, void *blk, int len,
     chacha20_decrypt(&ctx->a_cipher, blk, len);
 }
 
-const ssh2_cipheralg ssh2_chacha20_poly1305 = {
+const ssh_cipheralg ssh2_chacha20_poly1305 = {
 
     ccp_new,
     ccp_free,
@@ -1043,7 +1043,7 @@ const ssh2_cipheralg ssh2_chacha20_poly1305 = {
     &ssh2_poly1305
 };
 
-static const ssh2_cipheralg *const ccp_list[] = {
+static const ssh_cipheralg *const ccp_list[] = {
     &ssh2_chacha20_poly1305
 };
 

+ 84 - 205
source/putty/sshcrc.c

@@ -1,229 +1,108 @@
 /*
- * CRC32 implementation.
- *
- * The basic concept of a CRC is that you treat your bit-string
- * abcdefg... as a ludicrously long polynomial M=a+bx+cx^2+dx^3+...
- * over Z[2]. You then take a modulus polynomial P, and compute the
- * remainder of M on division by P. Thus, an erroneous message N
- * will only have the same CRC if the difference E = M-N is an
- * exact multiple of P. (Note that as we are working over Z[2], M-N
- * = N-M = M+N; but that's not very important.)
- *
- * What makes the CRC good is choosing P to have good properties:
- *
- *  - If its first and last terms are both nonzero then it cannot
- *    be a factor of any single term x^i. Therefore if M and N
- *    differ by exactly one bit their CRCs will guaranteeably
- *    be distinct.
- *
- *  - If it has a prime (irreducible) factor with three terms then
- *    it cannot divide a polynomial of the form x^i(1+x^j).
- *    Therefore if M and N differ by exactly _two_ bits they will
- *    have different CRCs.
- *
- *  - If it has a factor (x+1) then it cannot divide a polynomial
- *    with an odd number of terms. Therefore if M and N differ by
- *    _any odd_ number of bits they will have different CRCs.
- *
- *  - If the error term E is of the form x^i*B(x) where B(x) has
- *    order less than P (i.e. a short _burst_ of errors) then P
- *    cannot divide E (since no polynomial can divide a shorter
- *    one), so any such error burst will be spotted.
- *
- * The CRC32 standard polynomial is
- *   x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
- *
- * In fact, we don't compute M mod P; we compute M*x^32 mod P.
- *
- * The concrete implementation of the CRC is this: we maintain at
- * all times a 32-bit word which is the current remainder of the
- * polynomial mod P. Whenever we receive an extra bit, we multiply
- * the existing remainder by x, add (XOR) the x^32 term thus
- * generated to the new x^32 term caused by the incoming bit, and
- * remove the resulting combined x^32 term if present by replacing
- * it with (P-x^32).
- *
- * Bit 0 of the word is the x^31 term and bit 31 is the x^0 term.
- * Thus, multiplying by x means shifting right. So the actual
- * algorithm goes like this:
- *
- *   x32term = (crcword & 1) ^ newbit;
- *   crcword = (crcword >> 1) ^ (x32term * 0xEDB88320);
- *
- * In practice, we pre-compute what will happen to crcword on any
- * given sequence of eight incoming bits, and store that in a table
- * which we then use at run-time to do the job:
- * 
- *   outgoingplusnew = (crcword & 0xFF) ^ newbyte;
- *   crcword = (crcword >> 8) ^ table[outgoingplusnew];
- *
- * where table[outgoingplusnew] is computed by setting crcword=0
- * and then iterating the first code fragment eight times (taking
- * the incoming byte low bit first).
- *
- * Note that all shifts are rightward and thus no assumption is
- * made about exact word length! (Although word length must be at
- * _least_ 32 bits, but ANSI C guarantees this for `unsigned long'
- * anyway.)
+ * CRC32 implementation, as used in SSH-1.
+ *
+ * This particular form of the CRC uses the polynomial
+ * P(x) = x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
+ * and represents polynomials in bit-reversed form, so that the x^0
+ * coefficient (constant term) appears in the bit with place value
+ * 2^31, and the x^31 coefficient in the bit with place value 2^0. In
+ * this representation, (x^32 mod P) = 0xEDB88320, so multiplying the
+ * current state by x is done by shifting right by one bit, and XORing
+ * that constant into the result if the bit shifted out was 1.
+ *
+ * There's a bewildering array of subtly different variants of CRC out
+ * there, using different polynomials, both bit orders, and varying
+ * the start and end conditions. There are catalogue websites such as
+ * http://reveng.sourceforge.net/crc-catalogue/ , which generally seem
+ * to have the convention of indexing CRCs by their 'check value',
+ * defined as whatever you get if you hash the 9-byte test string
+ * "123456789".
+ *
+ * The crc32_rfc1662() function below, which starts off the CRC state
+ * at 0xFFFFFFFF and complements it after feeding all the data, gives
+ * the check value 0xCBF43926, and matches the hash function that the
+ * above catalogue refers to as "CRC-32/ISO-HDLC"; among other things,
+ * it's also the "FCS-32" checksum described in RFC 1662 section C.3
+ * (hence the name I've given it here).
+ *
+ * The crc32_ssh1() function implements the variant form used by
+ * SSH-1, which uses the same update function, but starts the state at
+ * zero and doesn't complement it at the end of the computation. The
+ * check value for that version is 0x2DFD2D88, which that CRC
+ * catalogue doesn't list at all.
  */
 
+#include <stdint.h>
 #include <stdlib.h>
 
 #include "ssh.h"
 
-/* ----------------------------------------------------------------------
- * Multi-function module. Can be compiled three ways.
- *
- *  - Compile with no special #defines. Will generate a table
- *    that's already initialised at compile time, and one function
- *    crc32_compute(buf,len) that uses it. Normal usage.
- *
- *  - Compile with INITFUNC defined. Will generate an uninitialised
- *    array as the table, and as well as crc32_compute(buf,len) it
- *    will also generate void crc32_init(void) which sets up the
- *    table at run time. Useful if binary size is important.
- *
- *  - Compile with GENPROGRAM defined. Will create a standalone
- *    program that does the initialisation and outputs the table as
- *    C code.
+/*
+ * Multiply a CRC value by x^4. This implementation strategy avoids
+ * using a lookup table (which would be a side-channel hazard, since
+ * SSH-1 applies this CRC to decrypted session data).
+ *
+ * The basic idea is that you'd like to "multiply" the shifted-out 4
+ * bits by the CRC polynomial value 0xEDB88320, or rather by that
+ * value shifted right 3 bits (since you want the _last_ bit shifted
+ * out, i.e. the one originally at the 2^3 position, to generate
+ * 0xEDB88320 itself). But the scare-quoted "multiply" would have to
+ * be a multiplication of polynomials over GF(2), which differs from
+ * integer multiplication in that you don't have any carries. In other
+ * words, you make a copy of one input shifted left by the index of
+ * each set bit in the other, so that adding them all together would
+ * give you the ordinary integer product, and then you XOR them
+ * together instead.
+ *
+ * With a 4-bit multiplier, the two kinds of multiplication coincide
+ * provided the multiplicand has no two set bits at positions
+ * differing by less than 4, because then no two copies of the
+ * multiplier can overlap to generate a carry. So I break up the
+ * intended multiplicand K = 0xEDB88320 >> 3 into three sub-constants
+ * a,b,c with that property, such that a^b^c = K. Then I can multiply
+ * m by each of them separately, and XOR together the results.
  */
-
-#define POLY (0xEDB88320L)
-
-#ifdef GENPROGRAM
-#define INITFUNC		       /* the gen program needs the init func :-) */
-#endif
-
-#ifdef INITFUNC
+static inline uint32_t crc32_shift_4(uint32_t v)
+{
+    const uint32_t a = 0x11111044, b = 0x08840020, c = 0x04220000;
+    uint32_t m = v & 0xF;
+    return (v >> 4) ^ (a*m) ^ (b*m) ^ (c*m);
+}
 
 /*
- * This variant of the code generates the table at run-time from an
- * init function.
+ * The 8-bit shift you need every time you absorb an input byte,
+ * implemented simply by iterating the 4-bit shift twice.
  */
-static unsigned long crc32_table[256];
-
-void crc32_init(void)
+static inline uint32_t crc32_shift_8(uint32_t v)
 {
-    unsigned long crcword;
-    int i;
-
-    for (i = 0; i < 256; i++) {
-	unsigned long newbyte, x32term;
-	int j;
-	crcword = 0;
-	newbyte = i;
-	for (j = 0; j < 8; j++) {
-	    x32term = (crcword ^ newbyte) & 1;
-	    crcword = (crcword >> 1) ^ (x32term * POLY);
-	    newbyte >>= 1;
-	}
-	crc32_table[i] = crcword;
-    }
+    return crc32_shift_4(crc32_shift_4(v));
 }
 
-#else
-
 /*
- * This variant of the code has the data already prepared.
+ * Update an existing hash value with extra bytes of data.
  */
-static const unsigned long crc32_table[256] = {
-    0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
-    0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
-    0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
-    0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
-    0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
-    0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
-    0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
-    0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
-    0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
-    0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
-    0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
-    0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
-    0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
-    0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
-    0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
-    0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
-    0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
-    0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
-    0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
-    0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
-    0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
-    0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
-    0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
-    0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
-    0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
-    0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
-    0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
-    0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
-    0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
-    0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
-    0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
-    0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
-    0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
-    0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
-    0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
-    0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
-    0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
-    0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
-    0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
-    0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
-    0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
-    0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
-    0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
-    0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
-    0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
-    0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
-    0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
-    0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
-    0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
-    0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
-    0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
-    0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
-    0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
-    0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
-    0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
-    0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
-    0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
-    0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
-    0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
-    0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
-    0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
-    0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
-    0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
-    0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
-};
-
-#endif
-
-#ifdef GENPROGRAM
-int main(void)
+uint32_t crc32_update(uint32_t crc, ptrlen data)
 {
-    int i;
-
-    crc32_init();
-    for (i = 0; i < 256; i++) {
-	printf("%s0x%08lXL%s",
-	       (i % 4 == 0 ? "    " : " "),
-	       crc32_table[i],
-	       (i % 4 == 3 ? (i == 255 ? "\n" : ",\n") : ","));
-    }
-
-    return 0;
+    const uint8_t *p = (const uint8_t *)data.ptr;
+    for (size_t len = data.len; len-- > 0 ;)
+        crc = crc32_shift_8(crc ^ *p++);
+    return crc;
 }
-#endif
 
-unsigned long crc32_update(unsigned long crcword, const void *buf, size_t len)
+/*
+ * The SSH-1 variant of CRC-32.
+ */
+uint32_t crc32_ssh1(ptrlen data)
 {
-    const unsigned char *p = (const unsigned char *) buf;
-    while (len--) {
-	unsigned long newbyte = *p++;
-	newbyte ^= crcword & 0xFFL;
-	crcword = (crcword >> 8) ^ crc32_table[newbyte];
-    }
-    return crcword;
+    return crc32_update(0, data);
 }
 
-unsigned long crc32_compute(const void *buf, size_t len)
+/*
+ * The official version of CRC-32. Nothing in PuTTY proper uses this,
+ * but it's useful to expose it to testcrypt so that we can implement
+ * standard test vectors.
+ */
+uint32_t crc32_rfc1662(ptrlen data)
 {
-    return crc32_update(0L, buf, len);
+    return crc32_update(0xFFFFFFFF, data) ^ 0xFFFFFFFF;
 }

+ 10 - 8
source/putty/sshcrcda.c

@@ -69,16 +69,17 @@ void crcda_free_context(struct crcda_ctx *ctx)
     }
 }
 
-static void crc_update(uint32_t *a, void *b)
+static void crc_update(uint32_t *a, const void *b)
 {
-    *a = crc32_update(*a, b, 4);
+    *a = crc32_update(*a, make_ptrlen(b, 4));
 }
 
 /* detect if a block is used in a particular pattern */
-static bool check_crc(uint8_t *S, uint8_t *buf, uint32_t len, uint8_t *IV)
+static bool check_crc(const uint8_t *S, const uint8_t *buf,
+                      uint32_t len, const uint8_t *IV)
 {
     uint32_t crc;
-    uint8_t *c;
+    const uint8_t *c;
 
     crc = 0;
     if (IV && !CMP(S, IV)) {
@@ -98,13 +99,14 @@ static bool check_crc(uint8_t *S, uint8_t *buf, uint32_t len, uint8_t *IV)
 }
 
 /* Detect a crc32 compensation attack on a packet */
-bool detect_attack(
-    struct crcda_ctx *ctx, uint8_t *buf, uint32_t len, uint8_t *IV)
+bool detect_attack(struct crcda_ctx *ctx,
+                   const unsigned char *buf, uint32_t len,
+                   const unsigned char *IV)
 {
     register uint32_t i, j;
     uint32_t l;
-    register uint8_t *c;
-    uint8_t *d;
+    register const uint8_t *c;
+    const uint8_t *d;
 
     assert(!(len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
              len % SSH_BLOCKSIZE != 0));

+ 57 - 206
source/putty/sshdes.c

@@ -774,32 +774,18 @@ static void des_key(DESContext *context, const void *vkey)
 }
 
 struct des3_ssh1_ctx {
-    /* 3 cipher context for each direction */
-    DESContext contexts[6];
-    ssh1_cipher ciph;
-};
-
-struct des_ssh1_ctx {
-    /* 1 cipher context for each direction */
-    DESContext contexts[2];
-    ssh1_cipher ciph;
+    DESContext contexts[3];
+    ssh_cipher ciph;
 };
 
-static ssh1_cipher *des3_ssh1_new(void)
+static ssh_cipher *des3_ssh1_new(const ssh_cipheralg *alg)
 {
     struct des3_ssh1_ctx *ctx = snew(struct des3_ssh1_ctx);
-    ctx->ciph.vt = &ssh1_3des;
-    return &ctx->ciph;
-}
-
-static ssh1_cipher *des_ssh1_new(void)
-{
-    struct des_ssh1_ctx *ctx = snew(struct des_ssh1_ctx);
-    ctx->ciph.vt = &ssh1_des;
+    ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static void des3_ssh1_free(ssh1_cipher *cipher)
+static void des3_ssh1_free(ssh_cipher *cipher)
 {
     struct des3_ssh1_ctx *ctx = container_of(
         cipher, struct des3_ssh1_ctx, ciph);
@@ -807,83 +793,65 @@ static void des3_ssh1_free(ssh1_cipher *cipher)
     sfree(ctx);
 }
 
-static void des_ssh1_free(ssh1_cipher *cipher)
-{
-    struct des_ssh1_ctx *ctx = container_of(
-        cipher, struct des_ssh1_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
+static void des3_ssh1_setkey(ssh_cipher *cipher, const void *key)
 {
     struct des3_ssh1_ctx *ctx = container_of(
         cipher, struct des3_ssh1_ctx, ciph);
     des3_key(ctx->contexts, key);
-    des3_key(ctx->contexts+3, key);
 }
 
-static void des3_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+static void des3_ssh1_setiv(ssh_cipher *cipher, const void *iv)
 {
     struct des3_ssh1_ctx *ctx = container_of(
         cipher, struct des3_ssh1_ctx, ciph);
-    des_3cbc_encrypt(blk, len, ctx->contexts);
+    /* SSH-1's idea of triple-DES CBC is three actual instances of the
+     * whole of DES-CBC, i.e. three separate CBC layers each with
+     * their own IV. So in principle we ought to be able to accept 24
+     * bytes of IV here. However, SSH-1 initialises all IVs to zero
+     * anyway, so we fudge it by just setting them all the same. */
+    for (int i = 0; i < 3; i++)
+        des_iv(&ctx->contexts[i], iv);
 }
 
-static void des3_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+static void des3_ssh1_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
     struct des3_ssh1_ctx *ctx = container_of(
         cipher, struct des3_ssh1_ctx, ciph);
-    des_3cbc_decrypt(blk, len, ctx->contexts+3);
-}
-
-static void des_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
-{
-    struct des_ssh1_ctx *ctx = container_of(
-        cipher, struct des_ssh1_ctx, ciph);
-    des_key(ctx->contexts, key);
-    des_key(ctx->contexts+1, key);
-}
-
-static void des_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
-{
-    struct des_ssh1_ctx *ctx = container_of(
-        cipher, struct des_ssh1_ctx, ciph);
-    des_cbc_encrypt(blk, len, ctx->contexts);
+    des_3cbc_encrypt(blk, len, ctx->contexts);
 }
 
-static void des_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
+static void des3_ssh1_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct des_ssh1_ctx *ctx = container_of(
-        cipher, struct des_ssh1_ctx, ciph);
-    des_cbc_decrypt(blk, len, ctx->contexts+1);
+    struct des3_ssh1_ctx *ctx = container_of(
+        cipher, struct des3_ssh1_ctx, ciph);
+    des_3cbc_decrypt(blk, len, ctx->contexts);
 }
 
 struct des3_ssh2_ctx {
     DESContext contexts[3];
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
 };
 
-struct des_ssh2_ctx {
+struct des_ctx {
     DESContext context;
-    ssh2_cipher ciph;
+    ssh_cipher ciph;
 };
 
-static ssh2_cipher *des3_ssh2_new(const ssh2_cipheralg *alg)
+static ssh_cipher *des3_ssh2_new(const ssh_cipheralg *alg)
 {
     struct des3_ssh2_ctx *ctx = snew(struct des3_ssh2_ctx);
     ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static ssh2_cipher *des_ssh2_new(const ssh2_cipheralg *alg)
+static ssh_cipher *des_new(const ssh_cipheralg *alg)
 {
-    struct des_ssh2_ctx *ctx = snew(struct des_ssh2_ctx);
+    struct des_ctx *ctx = snew(struct des_ctx);
     ctx->ciph.vt = alg;
     return &ctx->ciph;
 }
 
-static void des3_ssh2_free(ssh2_cipher *cipher)
+static void des3_ssh2_free(ssh_cipher *cipher)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
@@ -891,15 +859,14 @@ static void des3_ssh2_free(ssh2_cipher *cipher)
     sfree(ctx);
 }
 
-static void des_ssh2_free(ssh2_cipher *cipher)
+static void des_free(ssh_cipher *cipher)
 {
-    struct des_ssh2_ctx *ctx = container_of(
-        cipher, struct des_ssh2_ctx, ciph);
+    struct des_ctx *ctx = container_of(cipher, struct des_ctx, ciph);
     smemclr(ctx, sizeof(*ctx));
     sfree(ctx);
 }
 
-static void des3_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+static void des3_ssh2_setiv(ssh_cipher *cipher, const void *iv)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
@@ -908,169 +875,59 @@ static void des3_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
      * CBC, so there's only one IV required, not three */
 }
 
-static void des3_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+static void des3_ssh2_setkey(ssh_cipher *cipher, const void *key)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
     des3_key(ctx->contexts, key);
 }
 
-static void des_ssh2_setiv(ssh2_cipher *cipher, const void *iv)
+static void des_setiv(ssh_cipher *cipher, const void *iv)
 {
-    struct des_ssh2_ctx *ctx = container_of(
-        cipher, struct des_ssh2_ctx, ciph);
+    struct des_ctx *ctx = container_of(cipher, struct des_ctx, ciph);
     des_iv(&ctx->context, iv);
 }
 
-static void des_ssh2_setkey(ssh2_cipher *cipher, const void *key)
+static void des_setkey(ssh_cipher *cipher, const void *key)
 {
-    struct des_ssh2_ctx *ctx = container_of(
-        cipher, struct des_ssh2_ctx, ciph);
+    struct des_ctx *ctx = container_of(cipher, struct des_ctx, ciph);
     des_key(&ctx->context, key);
 }
 
-static void des3_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void des3_ssh2_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
     des_cbc3_encrypt(blk, len, ctx->contexts);
 }
 
-static void des3_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void des3_ssh2_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
     des_cbc3_decrypt(blk, len, ctx->contexts);
 }
 
-static void des3_ssh2_sdctr(ssh2_cipher *cipher, void *blk, int len)
+static void des3_ssh2_sdctr(ssh_cipher *cipher, void *blk, int len)
 {
     struct des3_ssh2_ctx *ctx = container_of(
         cipher, struct des3_ssh2_ctx, ciph);
     des_sdctr3(blk, len, ctx->contexts);
 }
 
-static void des_ssh2_encrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void des_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct des_ssh2_ctx *ctx = container_of(
-        cipher, struct des_ssh2_ctx, ciph);
+    struct des_ctx *ctx = container_of(cipher, struct des_ctx, ciph);
     des_cbc_encrypt(blk, len, &ctx->context);
 }
 
-static void des_ssh2_decrypt_blk(ssh2_cipher *cipher, void *blk, int len)
+static void des_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
 {
-    struct des_ssh2_ctx *ctx = container_of(
-        cipher, struct des_ssh2_ctx, ciph);
+    struct des_ctx *ctx = container_of(cipher, struct des_ctx, ciph);
     des_cbc_decrypt(blk, len, &ctx->context);
 }
 
-void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    unsigned char *blk = (unsigned char *)vblk;
-    DESContext ourkeys[3];
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
-		  GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]);
-    des_3cbc_decrypt(blk, len, ourkeys);
-    smemclr(ourkeys, sizeof(ourkeys));
-}
-
-void des3_encrypt_pubkey(const void *vkey, void *vblk, int len)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    unsigned char *blk = (unsigned char *)vblk;
-    DESContext ourkeys[3];
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
-		  GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[2]);
-    des_3cbc_encrypt(blk, len, ourkeys);
-    smemclr(ourkeys, sizeof(ourkeys));
-}
-
-void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv,
-			      void *vblk, int len)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    const unsigned char *iv = (const unsigned char *)viv;
-    unsigned char *blk = (unsigned char *)vblk;
-    DESContext ourkeys[3];
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
-		  GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
-		  GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);
-    ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv);
-    ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4);
-    des_cbc3_decrypt(blk, len, ourkeys);
-    smemclr(ourkeys, sizeof(ourkeys));
-}
-
-void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv,
-			      void *vblk, int len)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    const unsigned char *iv = (const unsigned char *)viv;
-    unsigned char *blk = (unsigned char *)vblk;
-    DESContext ourkeys[3];
-    des_key_setup(GET_32BIT_MSB_FIRST(key),
-		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 8),
-		  GET_32BIT_MSB_FIRST(key + 12), &ourkeys[1]);
-    des_key_setup(GET_32BIT_MSB_FIRST(key + 16),
-		  GET_32BIT_MSB_FIRST(key + 20), &ourkeys[2]);
-    ourkeys[0].iv0 = GET_32BIT_MSB_FIRST(iv);
-    ourkeys[0].iv1 = GET_32BIT_MSB_FIRST(iv+4);
-    des_cbc3_encrypt(blk, len, ourkeys);
-    smemclr(ourkeys, sizeof(ourkeys));
-}
-
-static void des_keysetup_xdmauth(const void *vkeydata, DESContext *dc)
-{
-    const unsigned char *keydata = (const unsigned char *)vkeydata;
-    unsigned char key[8];
-    int i, nbits, j;
-    unsigned int bits;
-
-    bits = 0;
-    nbits = 0;
-    j = 0;
-    for (i = 0; i < 8; i++) {
-	if (nbits < 7) {
-	    bits = (bits << 8) | keydata[j];
-	    nbits += 8;
-	    j++;
-	}
-	key[i] = (bits >> (nbits - 7)) << 1;
-	bits &= ~(0x7F << (nbits - 7));
-	nbits -= 7;
-    }
-
-    des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc);
-}
-
-void des_encrypt_xdmauth(const void *keydata, void *blk, int len)
-{
-    DESContext dc;
-    des_keysetup_xdmauth(keydata, &dc);
-    des_cbc_encrypt(blk, len, &dc);
-}
-
-void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
-{
-    DESContext dc;
-    des_keysetup_xdmauth(keydata, &dc);
-    des_cbc_decrypt(blk, len, &dc);
-}
-
-const ssh2_cipheralg ssh_3des_ssh2 = {
+const ssh_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",
@@ -1078,7 +935,7 @@ const ssh2_cipheralg ssh_3des_ssh2 = {
     NULL
 };
 
-const ssh2_cipheralg ssh_3des_ssh2_ctr = {
+const ssh_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",
@@ -1094,44 +951,38 @@ const ssh2_cipheralg ssh_3des_ssh2_ctr = {
  * apparently aren't the only people to do so, so we sigh 
  * and implement it anyway.
  */
-const 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,
+const ssh_cipheralg ssh_des = {
+    des_new, des_free, des_setiv, des_setkey,
+    des_encrypt_blk, des_decrypt_blk, NULL, NULL,
     "des-cbc",
     8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
     NULL
 };
 
-const 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,
+const ssh_cipheralg ssh_des_sshcom_ssh2 = {
+    des_new, des_free, des_setiv, des_setkey,
+    des_encrypt_blk, des_decrypt_blk, NULL, NULL,
     "[email protected]",
     8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
     NULL
 };
 
-static const ssh2_cipheralg *const des3_list[] = {
+static const ssh_cipheralg *const des3_list[] = {
     &ssh_3des_ssh2_ctr,
     &ssh_3des_ssh2
 };
 
 const ssh2_ciphers ssh2_3des = { lenof(des3_list), des3_list };
 
-static const ssh2_cipheralg *const des_list[] = {
-    &ssh_des_ssh2,
+static const ssh_cipheralg *const des_list[] = {
+    &ssh_des,
     &ssh_des_sshcom_ssh2
 };
 
 const ssh2_ciphers ssh2_des = { lenof(des_list), des_list };
 
-const 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"
-};
-
-const 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"
+const ssh_cipheralg ssh_3des_ssh1 = {
+    des3_ssh1_new, des3_ssh1_free, des3_ssh1_setiv, des3_ssh1_setkey,
+    des3_ssh1_encrypt_blk, des3_ssh1_decrypt_blk, NULL, NULL, NULL,
+    8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES inner-CBC", NULL
 };

+ 1 - 2
source/putty/sshmd5.c

@@ -282,8 +282,7 @@ struct hmacmd5_context *hmacmd5_make_context(void)
     return ctx;
 }
 
-static ssh2_mac *hmacmd5_ssh2_new(const ssh2_macalg *alg,
-                                  ssh2_cipher *cipher)
+static ssh2_mac *hmacmd5_ssh2_new(const ssh2_macalg *alg, ssh_cipher *cipher)
 {
     struct hmacmd5_context *ctx = hmacmd5_make_context();
     ctx->mac.vt = alg;

+ 1 - 2
source/putty/sshsh256.c

@@ -256,8 +256,7 @@ struct hmacsha256 {
     ssh2_mac mac;
 };
 
-static ssh2_mac *hmacsha256_new(
-    const ssh2_macalg *alg, ssh2_cipher *cipher)
+static ssh2_mac *hmacsha256_new(const ssh2_macalg *alg, ssh_cipher *cipher)
 {
     struct hmacsha256 *ctx = snew(struct hmacsha256);
     ctx->mac.vt = alg;

+ 1 - 2
source/putty/sshsha.c

@@ -285,8 +285,7 @@ struct hmacsha1 {
     ssh2_mac mac;
 };
 
-static ssh2_mac *hmacsha1_new(
-    const ssh2_macalg *alg, ssh2_cipher *cipher)
+static ssh2_mac *hmacsha1_new(const ssh2_macalg *alg, ssh_cipher *cipher)
 {
     struct hmacsha1 *ctx = snew(struct hmacsha1);
     ctx->mac.vt = alg;