1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Add infrastructure for supporting multiple hashes in key exchange.

Nothing very surprising here.

[originally from svn r6251]
This commit is contained in:
Ben Harris 2005-08-31 20:43:06 +00:00
parent 11d5c791ac
commit a59356aa74
4 changed files with 114 additions and 61 deletions

128
ssh.c
View File

@ -680,7 +680,7 @@ struct ssh_tag {
/* the above field _must_ be first in the structure */
char *v_c, *v_s;
SHA_State exhash;
void *exhash;
Socket s;
@ -706,6 +706,7 @@ struct ssh_tag {
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
unsigned char v2_session_id[20];
int v2_session_id_len;
void *kex_ctx;
char *savedhost;
@ -1562,21 +1563,21 @@ static int ssh_versioncmp(char *a, char *b)
/*
* Utility routines for putting an SSH-protocol `string' and
* `uint32' into a SHA state.
* `uint32' into a hash state.
*/
static void sha_string(SHA_State * s, void *str, int len)
static void hash_string(const struct ssh_hash *h, void *s, void *str, int len)
{
unsigned char lenblk[4];
PUT_32BIT(lenblk, len);
SHA_Bytes(s, lenblk, 4);
SHA_Bytes(s, str, len);
h->bytes(s, lenblk, 4);
h->bytes(s, str, len);
}
static void sha_uint32(SHA_State * s, unsigned i)
static void hash_uint32(const struct ssh_hash *h, void *s, unsigned i)
{
unsigned char intblk[4];
PUT_32BIT(intblk, i);
SHA_Bytes(s, intblk, 4);
h->bytes(s, intblk, 4);
}
/*
@ -1972,12 +1973,12 @@ void bndebug(char *string, Bignum b)
}
#endif
static void sha_mpint(SHA_State * s, Bignum b)
static void hash_mpint(const struct ssh_hash *h, void *s, Bignum b)
{
unsigned char *p;
int len;
p = ssh2_mpint_fmt(b, &len);
sha_string(s, p, len);
hash_string(h, s, p, len);
sfree(p);
}
@ -4947,26 +4948,26 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen)
/*
* SSH-2 key creation method.
*/
static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
unsigned char *sessid, char chr,
static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
unsigned char *keyspace)
{
SHA_State s;
/* First 20 bytes. */
SHA_Init(&s);
const struct ssh_hash *h = ssh->kex->hash;
void *s;
/* First hlen bytes. */
s = h->init();
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, &chr, 1);
SHA_Bytes(&s, sessid, 20);
SHA_Final(&s, keyspace);
/* Next 20 bytes. */
SHA_Init(&s);
hash_mpint(h, s, K);
h->bytes(s, H, h->hlen);
h->bytes(s, &chr, 1);
h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len);
h->final(s, keyspace);
/* Next hlen bytes. */
s = h->init();
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, keyspace, 20);
SHA_Final(&s, keyspace + 20);
hash_mpint(h, s, K);
h->bytes(s, H, h->hlen);
h->bytes(s, keyspace, h->hlen);
h->final(s, keyspace + h->hlen);
}
/*
@ -5425,14 +5426,15 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
}
}
SHA_Init(&ssh->exhash);
sha_string(&ssh->exhash, ssh->v_c, strlen(ssh->v_c));
sha_string(&ssh->exhash, ssh->v_s, strlen(ssh->v_s));
sha_string(&ssh->exhash, s->our_kexinit, s->our_kexinitlen);
ssh->exhash = ssh->kex->hash->init();
hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
hash_string(ssh->kex->hash, ssh->exhash,
s->our_kexinit, s->our_kexinitlen);
sfree(s->our_kexinit);
if (pktin->length > 5)
sha_string(&ssh->exhash, pktin->data + 5, pktin->length - 5);
hash_string(ssh->kex->hash, ssh->exhash,
pktin->data + 5, pktin->length - 5);
if (s->ignorepkt) /* first_kex_packet_follows */
crWaitUntil(pktin); /* Ignore packet */
@ -5450,10 +5452,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
scbits = s->sccipher_tobe->keylen;
s->nbits = (csbits > scbits ? csbits : scbits);
}
/* The keys only have 160-bit entropy, since they're based on
* a SHA-1 hash. So cap the key size at 160 bits. */
if (s->nbits > 160)
s->nbits = 160;
/* The keys only have hlen-bit entropy, since they're based on
* a hash. So cap the key size at hlen bits. */
if (s->nbits > ssh->kex->hash->hlen * 8)
s->nbits = ssh->kex->hash->hlen * 8;
/*
* If we're doing Diffie-Hellman group exchange, start by
@ -5525,29 +5527,31 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* involve user interaction. */
set_busy_status(ssh->frontend, BUSY_NOT);
sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
if (ssh->kex == &ssh_diffiehellman_gex) {
sha_uint32(&ssh->exhash, s->pbits);
sha_mpint(&ssh->exhash, s->p);
sha_mpint(&ssh->exhash, s->g);
hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
}
sha_mpint(&ssh->exhash, s->e);
sha_mpint(&ssh->exhash, s->f);
sha_mpint(&ssh->exhash, s->K);
SHA_Final(&ssh->exhash, s->exchange_hash);
hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
dh_cleanup(ssh->kex_ctx);
ssh->kex_ctx = NULL;
#if 0
debug(("Exchange hash is:\n"));
dmemdump(s->exchange_hash, 20);
dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
#endif
s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
if (!s->hkey ||
!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
(char *)s->exchange_hash, 20)) {
(char *)s->exchange_hash,
ssh->kex->hash->hlen)) {
bombout(("Server's host key did not match the signature supplied"));
crStop(0);
}
@ -5595,8 +5599,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* authentication.
*/
if (!s->got_session_id) {
assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id));
memcpy(ssh->v2_session_id, s->exchange_hash,
sizeof(s->exchange_hash));
assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id));
ssh->v2_session_id_len = ssh->kex->hash->hlen;
s->got_session_id = TRUE;
}
@ -5632,11 +5639,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
{
unsigned char keyspace[40];
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);
ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);
ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);
ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
}
@ -5690,11 +5697,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
*/
{
unsigned char keyspace[40];
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);
ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);
ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);
ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
}
logeventf(ssh, "Initialised %.200s server->client encryption",
@ -6877,7 +6884,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
ssh2_pkt_addstring_start(s->pktout);
ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
s->siglen = s->pktout->length - 5 + 4 + 20;
s->siglen = s->pktout->length - 5 + 4 +
ssh->v2_session_id_len;
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
s->siglen -= 4;
s->len = 1; /* message type */
@ -6896,11 +6904,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
s->q += 4;
/* Now the data to be signed... */
if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
PUT_32BIT(s->q, 20);
PUT_32BIT(s->q, ssh->v2_session_id_len);
s->q += 4;
}
memcpy(s->q, ssh->v2_session_id, 20);
s->q += 20;
memcpy(s->q, ssh->v2_session_id,
ssh->v2_session_id_len);
s->q += ssh->v2_session_id_len;
memcpy(s->q, s->pktout->data + 5,
s->pktout->length - 5);
s->q += s->pktout->length - 5;
@ -7196,16 +7205,19 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* followed by everything so far placed in the
* outgoing packet.
*/
sigdata_len = s->pktout->length - 5 + 4 + 20;
sigdata_len = s->pktout->length - 5 + 4 +
ssh->v2_session_id_len;
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
sigdata_len -= 4;
sigdata = snewn(sigdata_len, unsigned char);
p = 0;
if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
PUT_32BIT(sigdata+p, 20);
PUT_32BIT(sigdata+p, ssh->v2_session_id_len);
p += 4;
}
memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20;
memcpy(sigdata+p, ssh->v2_session_id,
ssh->v2_session_id_len);
p += ssh->v2_session_id_len;
memcpy(sigdata+p, s->pktout->data + 5,
s->pktout->length - 5);
p += s->pktout->length - 5;

9
ssh.h
View File

@ -175,6 +175,13 @@ struct ssh_mac {
char *text_name;
};
struct ssh_hash {
void *(*init)(void); /* also allocates context */
void (*bytes)(void *, void *, int);
void (*final)(void *, unsigned char *); /* also frees context */
int hlen; /* output length in bytes */
};
struct ssh_kex {
/*
* Plugging in another KEX algorithm requires structural chaos,
@ -186,6 +193,7 @@ struct ssh_kex {
char *name, *groupname;
const unsigned char *pdata, *gdata;/* NULL means use group exchange */
int plen, glen;
const struct ssh_hash *hash;
};
struct ssh_signkey {
@ -236,6 +244,7 @@ extern const struct ssh2_ciphers ssh2_des;
extern const struct ssh2_ciphers ssh2_aes;
extern const struct ssh2_ciphers ssh2_blowfish;
extern const struct ssh2_ciphers ssh2_arcfour;
extern const struct ssh_hash ssh_sha1;
extern const struct ssh_kex ssh_diffiehellman_group1;
extern const struct ssh_kex ssh_diffiehellman_group14;
extern const struct ssh_kex ssh_diffiehellman_gex;

View File

@ -48,17 +48,17 @@ static const unsigned char G[] = { 2 };
const struct ssh_kex ssh_diffiehellman_group1 = {
"diffie-hellman-group1-sha1", "group1",
P1, G, lenof(P1), lenof(G)
P1, G, lenof(P1), lenof(G), &ssh_sha1
};
const struct ssh_kex ssh_diffiehellman_group14 = {
"diffie-hellman-group14-sha1", "group14",
P14, G, lenof(P14), lenof(G)
P14, G, lenof(P14), lenof(G), &ssh_sha1
};
const struct ssh_kex ssh_diffiehellman_gex = {
"diffie-hellman-group-exchange-sha1", NULL,
NULL, NULL, 0, 0
NULL, NULL, 0, 0, &ssh_sha1
};
/*

View File

@ -188,6 +188,38 @@ void SHA_Simple(void *p, int len, unsigned char *output)
SHA_Final(&s, output);
}
/*
* Thin abstraction for things where hashes are pluggable.
*/
static void *sha1_init(void)
{
SHA_State *s;
s = snew(SHA_State);
SHA_Init(s);
return s;
}
static void sha1_bytes(void *handle, void *p, int len)
{
SHA_State *s = handle;
SHA_Bytes(s, p, len);
}
static void sha1_final(void *handle, unsigned char *output)
{
SHA_State *s = handle;
SHA_Final(s, output);
sfree(s);
}
const struct ssh_hash ssh_sha1 = {
sha1_init, sha1_bytes, sha1_final, 20
};
/* ----------------------------------------------------------------------
* The above is the SHA-1 algorithm itself. Now we implement the
* HMAC wrapper on it.