From 6c5cc49e276a7d0991da5a5a21540f38a8d0b0d6 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 13 Sep 2018 13:29:32 +0100 Subject: [PATCH] Turn SSH-1 ciphers into a classoid. The interchangeable system of SSH-1 ciphers previously followed the same pattern as the backends and the public-key algorithms, in that all the clients would maintain two separate pointers, one to the vtable and the other to the individual instance / context. Now I've merged them, just as I did with those other two, so that you only cart around a single pointer, which has a vtable pointer inside it and a type distinguishing it from an instance of any of the other interchangeable sets of algorithms. --- ssh.c | 7 ++-- ssh.h | 30 ++++++++++----- ssh1bpp.c | 18 ++++----- sshblowf.c | 58 ++++++++++++++++++---------- sshbpp.h | 2 +- sshdes.c | 110 +++++++++++++++++++++++++++++++++-------------------- 6 files changed, 139 insertions(+), 86 deletions(-) diff --git a/ssh.c b/ssh.c index dd1d1c1f..3d4baf47 100644 --- a/ssh.c +++ b/ssh.c @@ -2914,10 +2914,9 @@ static void do_ssh1_login(void *vctx) sfree(s->rsabuf); { - const struct ssh_cipher *cipher = - (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 : - s->cipher_type == SSH_CIPHER_DES ? &ssh_des : - &ssh_3des); + const struct ssh1_cipheralg *cipher = + (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish : + s->cipher_type == SSH_CIPHER_DES ? &ssh1_des : &ssh1_3des); ssh1_bpp_new_cipher(ssh->bpp, cipher, ssh->session_key); logeventf(ssh, "Initialised %s encryption", cipher->text_name); } diff --git a/ssh.h b/ssh.h index 1026a90b..c10d6fb1 100644 --- a/ssh.h +++ b/ssh.h @@ -420,17 +420,27 @@ void SHA384_Init(SHA384_State * s); void SHA384_Final(SHA384_State * s, unsigned char *output); void SHA384_Simple(const void *p, int len, unsigned char *output); -struct ssh_mac; -struct ssh_cipher { - void *(*make_context)(void); - void (*free_context)(void *); - void (*sesskey) (void *, const void *key); /* for SSH-1 */ - void (*encrypt) (void *, void *blk, int len); - void (*decrypt) (void *, void *blk, int len); +struct ssh2_macalg; + +struct ssh1_cipheralg; +typedef const struct ssh1_cipheralg *ssh1_cipher; + +struct ssh1_cipheralg { + ssh1_cipher *(*new)(void); + void (*free)(ssh1_cipher *); + void (*sesskey)(ssh1_cipher *, const void *key); + void (*encrypt)(ssh1_cipher *, void *blk, int len); + void (*decrypt)(ssh1_cipher *, void *blk, int len); int blksize; const char *text_name; }; +#define ssh1_cipher_new(alg) ((alg)->new()) +#define ssh1_cipher_free(ctx) ((*(ctx))->free(ctx)) +#define ssh1_cipher_sesskey(ctx, key) ((*(ctx))->sesskey(ctx, key)) +#define ssh1_cipher_encrypt(ctx, blk, len) ((*(ctx))->encrypt(ctx, blk, len)) +#define ssh1_cipher_decrypt(ctx, blk, len) ((*(ctx))->decrypt(ctx, blk, len)) + struct ssh2_cipher { void *(*make_context)(void); void (*free_context)(void *); @@ -576,9 +586,9 @@ struct ssh2_userkey { /* The maximum length of any hash algorithm used in kex. (bytes) */ #define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */ -extern const struct ssh_cipher ssh_3des; -extern const struct ssh_cipher ssh_des; -extern const struct ssh_cipher ssh_blowfish_ssh1; +extern const struct ssh1_cipheralg ssh1_3des; +extern const struct ssh1_cipheralg ssh1_des; +extern const struct ssh1_cipheralg ssh1_blowfish; extern const struct ssh2_ciphers ssh2_3des; extern const struct ssh2_ciphers ssh2_des; extern const struct ssh2_ciphers ssh2_aes; diff --git a/ssh1bpp.c b/ssh1bpp.c index 5ace35fd..627d55a5 100644 --- a/ssh1bpp.c +++ b/ssh1bpp.c @@ -17,8 +17,7 @@ struct ssh1_bpp_state { int chunk; PktIn *pktin; - const struct ssh_cipher *cipher; - void *cipher_ctx; + ssh1_cipher *cipher; struct crcda_ctx *crcda_ctx; @@ -51,7 +50,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp) { struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp); if (s->cipher) - s->cipher->free_context(s->cipher_ctx); + ssh1_cipher_free(s->cipher); if (s->compctx) zlib_compress_cleanup(s->compctx); if (s->decompctx) @@ -64,7 +63,7 @@ static void ssh1_bpp_free(BinaryPacketProtocol *bpp) } void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, - const struct ssh_cipher *cipher, + const struct ssh1_cipheralg *cipher, const void *session_key) { struct ssh1_bpp_state *s; @@ -73,10 +72,9 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, assert(!s->cipher); - s->cipher = cipher; - if (s->cipher) { - s->cipher_ctx = cipher->make_context(); - cipher->sesskey(s->cipher_ctx, session_key); + if (cipher) { + s->cipher = ssh1_cipher_new(cipher); + ssh1_cipher_sesskey(s->cipher, session_key); assert(!s->crcda_ctx); s->crcda_ctx = crcda_make_context(); @@ -146,7 +144,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp) } if (s->cipher) - s->cipher->decrypt(s->cipher_ctx, s->data, s->biglen); + ssh1_cipher_decrypt(s->cipher, s->data, s->biglen); s->realcrc = crc32_compute(s->data, s->biglen - 4); s->gotcrc = GET_32BIT(s->data + s->biglen - 4); @@ -273,7 +271,7 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) PUT_32BIT(pkt->data + pktoffs, len); if (s->cipher) - s->cipher->encrypt(s->cipher_ctx, pkt->data + pktoffs + 4, biglen); + ssh1_cipher_encrypt(s->cipher, pkt->data + pktoffs + 4, biglen); bufchain_add(s->bpp.out_raw, pkt->data + pktoffs, biglen + 4); /* len(length+padding+type+data+CRC) */ diff --git a/sshblowf.c b/sshblowf.c index 24421059..764f3f65 100644 --- a/sshblowf.c +++ b/sshblowf.c @@ -561,17 +561,32 @@ void *blowfish_make_context(void) return snew(BlowfishContext); } -static void *blowfish_ssh1_make_context(void) -{ - /* In SSH-1, need one key for each direction */ - return snewn(2, BlowfishContext); -} - void blowfish_free_context(void *handle) { sfree(handle); } +struct blowfish_ssh1_ctx { + /* In SSH-1, need one key for each direction */ + BlowfishContext contexts[2]; + ssh1_cipher vt; +}; + +static ssh1_cipher *blowfish_ssh1_new(void) +{ + struct blowfish_ssh1_ctx *ctx = snew(struct blowfish_ssh1_ctx); + ctx->vt = &ssh1_blowfish; + return &ctx->vt; +} + +static void blowfish_ssh1_free(ssh1_cipher *cipher) +{ + struct blowfish_ssh1_ctx *ctx = + FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt); + smemclr(ctx, sizeof(*ctx)); + sfree(ctx); +} + static void blowfish_key(void *handle, const void *key) { BlowfishContext *ctx = (BlowfishContext *)handle; @@ -592,25 +607,27 @@ static void blowfish_iv(void *handle, const void *viv) ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4); } -static void blowfish_sesskey(void *handle, const void *key) +static void blowfish_ssh1_sesskey(ssh1_cipher *cipher, const void *key) { - BlowfishContext *ctx = (BlowfishContext *)handle; - blowfish_setkey(ctx, key, SSH_SESSION_KEY_LENGTH); - ctx->iv0 = 0; - ctx->iv1 = 0; - ctx[1] = ctx[0]; /* structure copy */ + struct blowfish_ssh1_ctx *ctx = + FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt); + blowfish_setkey(&ctx->contexts[0], key, SSH_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(void *handle, void *blk, int len) +static void blowfish_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len) { - BlowfishContext *ctx = (BlowfishContext *)handle; - blowfish_lsb_encrypt_cbc(blk, len, ctx); + struct blowfish_ssh1_ctx *ctx = + FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt); + blowfish_lsb_encrypt_cbc(blk, len, ctx->contexts); } -static void blowfish_ssh1_decrypt_blk(void *handle, void *blk, int len) +static void blowfish_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len) { - BlowfishContext *ctx = (BlowfishContext *)handle; - blowfish_lsb_decrypt_cbc(blk, len, ctx+1); + struct blowfish_ssh1_ctx *ctx = + FROMFIELD(cipher, struct blowfish_ssh1_ctx, vt); + blowfish_lsb_decrypt_cbc(blk, len, ctx->contexts+1); } static void blowfish_ssh2_encrypt_blk(void *handle, void *blk, int len) @@ -631,8 +648,9 @@ static void blowfish_ssh2_sdctr(void *handle, void *blk, int len) blowfish_msb_sdctr(blk, len, ctx); } -const struct ssh_cipher ssh_blowfish_ssh1 = { - blowfish_ssh1_make_context, blowfish_free_context, blowfish_sesskey, +const struct ssh1_cipheralg ssh1_blowfish = { + blowfish_ssh1_new, blowfish_ssh1_free, + blowfish_ssh1_sesskey, blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk, 8, "Blowfish-128 CBC" }; diff --git a/sshbpp.h b/sshbpp.h index c1ac7a13..de11e69d 100644 --- a/sshbpp.h +++ b/sshbpp.h @@ -32,7 +32,7 @@ struct BinaryPacketProtocol { BinaryPacketProtocol *ssh1_bpp_new(void); void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, - const struct ssh_cipher *cipher, + const struct ssh1_cipheralg *cipher, const void *session_key); void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp); diff --git a/sshdes.c b/sshdes.c index 585deb7f..6cc1ce67 100644 --- a/sshdes.c +++ b/sshdes.c @@ -752,10 +752,23 @@ static void *des3_make_context(void) return snewn(3, DESContext); } -static void *des3_ssh1_make_context(void) +struct des3_ssh1_ctx { + /* 3 cipher context for each direction */ + DESContext contexts[6]; + ssh1_cipher vt; +}; + +struct des_ssh1_ctx { + /* 1 cipher context for each direction */ + DESContext contexts[2]; + ssh1_cipher vt; +}; + +static ssh1_cipher *des3_ssh1_new(void) { - /* Need 3 keys for each direction, in SSH-1 */ - return snewn(6, DESContext); + struct des3_ssh1_ctx *ctx = snew(struct des3_ssh1_ctx); + ctx->vt = &ssh1_3des; + return &ctx->vt; } static void *des_make_context(void) @@ -763,10 +776,25 @@ static void *des_make_context(void) return snew(DESContext); } -static void *des_ssh1_make_context(void) +static ssh1_cipher *des_ssh1_new(void) { - /* Need one key for each direction, in SSH-1 */ - return snewn(2, DESContext); + struct des_ssh1_ctx *ctx = snew(struct des_ssh1_ctx); + ctx->vt = &ssh1_des; + return &ctx->vt; +} + +static void des3_ssh1_free(ssh1_cipher *cipher) +{ + struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt); + smemclr(ctx, sizeof(*ctx)); + sfree(ctx); +} + +static void des_ssh1_free(ssh1_cipher *cipher) +{ + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + smemclr(ctx, sizeof(*ctx)); + sfree(ctx); } static void des3_free_context(void *handle) /* used for both 3DES and DES */ @@ -802,23 +830,42 @@ static void des_key(void *handle, const void *vkey) GET_32BIT_MSB_FIRST(key + 4), &keys[0]); } -static void des3_sesskey(void *handle, const void *key) +static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key) { - DESContext *keys = (DESContext *) handle; - des3_key(keys, key); - des3_key(keys+3, key); + struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt); + des3_key(ctx->contexts, key); + des3_key(ctx->contexts+3, key); } -static void des3_encrypt_blk(void *handle, void *blk, int len) +static void des3_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len) { - DESContext *keys = (DESContext *) handle; - des_3cbc_encrypt(blk, len, keys); + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + des_3cbc_encrypt(blk, len, ctx->contexts); } -static void des3_decrypt_blk(void *handle, void *blk, int len) +static void des3_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len) { - DESContext *keys = (DESContext *) handle; - des_3cbc_decrypt(blk, len, keys+3); + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + des_3cbc_decrypt(blk, len, ctx->contexts+3); +} + +static void des_ssh1_sesskey(ssh1_cipher *cipher, const void *key) +{ + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + des_key(ctx->contexts, key); + des_key(ctx->contexts+1, key); +} + +static void des_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len) +{ + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + des_cbc_encrypt(blk, len, ctx->contexts); +} + +static void des_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len) +{ + struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt); + des_cbc_decrypt(blk, len, ctx->contexts+1); } static void des3_ssh2_encrypt_blk(void *handle, void *blk, int len) @@ -1017,34 +1064,15 @@ const struct ssh2_ciphers ssh2_des = { des_list }; -const struct ssh_cipher ssh_3des = { - des3_ssh1_make_context, des3_free_context, des3_sesskey, - des3_encrypt_blk, des3_decrypt_blk, +const struct ssh1_cipheralg ssh1_3des = { + des3_ssh1_new, des3_ssh1_free, des3_ssh1_sesskey, + des3_ssh1_encrypt_blk, des3_ssh1_decrypt_blk, 8, "triple-DES inner-CBC" }; -static void des_sesskey(void *handle, const void *key) -{ - DESContext *keys = (DESContext *) handle; - des_key(keys, key); - des_key(keys+1, key); -} - -static void des_encrypt_blk(void *handle, void *blk, int len) -{ - DESContext *keys = (DESContext *) handle; - des_cbc_encrypt(blk, len, keys); -} - -static void des_decrypt_blk(void *handle, void *blk, int len) -{ - DESContext *keys = (DESContext *) handle; - des_cbc_decrypt(blk, len, keys+1); -} - -const struct ssh_cipher ssh_des = { - des_ssh1_make_context, des3_free_context, des_sesskey, - des_encrypt_blk, des_decrypt_blk, +const struct ssh1_cipheralg ssh1_des = { + des_ssh1_new, des_ssh1_free, des_ssh1_sesskey, + des_ssh1_encrypt_blk, des_ssh1_decrypt_blk, 8, "single-DES CBC" };