X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/7bd3364494a4b173f5bffc24bfa52fab6b26806a..9c0998350a44539ab4f53beeb135aa0051924386:/sshrsa.c diff --git a/sshrsa.c b/sshrsa.c index f7817361..12229e63 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -10,23 +10,14 @@ #include "ssh.h" #include "misc.h" -#define GET_32BIT(cp) \ - (((unsigned long)(unsigned char)(cp)[0] << 24) | \ - ((unsigned long)(unsigned char)(cp)[1] << 16) | \ - ((unsigned long)(unsigned char)(cp)[2] << 8) | \ - ((unsigned long)(unsigned char)(cp)[3])) - -#define PUT_32BIT(cp, value) { \ - (cp)[0] = (unsigned char)((value) >> 24); \ - (cp)[1] = (unsigned char)((value) >> 16); \ - (cp)[2] = (unsigned char)((value) >> 8); \ - (cp)[3] = (unsigned char)(value); } - -int makekey(unsigned char *data, struct RSAKey *result, +int makekey(unsigned char *data, int len, struct RSAKey *result, unsigned char **keystr, int order) { unsigned char *p = data; - int i; + int i, n; + + if (len < 4) + return -1; if (result) { result->bits = 0; @@ -35,36 +26,53 @@ int makekey(unsigned char *data, struct RSAKey *result, } else p += 4; + len -= 4; + /* * order=0 means exponent then modulus (the keys sent by the * server). order=1 means modulus then exponent (the keys * stored in a keyfile). */ - if (order == 0) - p += ssh1_read_bignum(p, result ? &result->exponent : NULL); + if (order == 0) { + n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL); + if (n < 0) return -1; + p += n; + len -= n; + } + + n = ssh1_read_bignum(p, len, result ? &result->modulus : NULL); + if (n < 0 || (result && bignum_bitcount(result->modulus) == 0)) return -1; if (result) - result->bytes = (((p[0] << 8) + p[1]) + 7) / 8; + result->bytes = n - 2; if (keystr) *keystr = p + 2; - p += ssh1_read_bignum(p, result ? &result->modulus : NULL); - if (order == 1) - p += ssh1_read_bignum(p, result ? &result->exponent : NULL); - + p += n; + len -= n; + + if (order == 1) { + n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL); + if (n < 0) return -1; + p += n; + len -= n; + } return p - data; } -int makeprivate(unsigned char *data, struct RSAKey *result) +int makeprivate(unsigned char *data, int len, struct RSAKey *result) { - return ssh1_read_bignum(data, &result->private_exponent); + return ssh1_read_bignum(data, len, &result->private_exponent); } -void rsaencrypt(unsigned char *data, int length, struct RSAKey *key) +int rsaencrypt(unsigned char *data, int length, struct RSAKey *key) { Bignum b1, b2; int i; unsigned char *p; + if (key->bytes < length + 4) + return 0; /* RSA key too short! */ + memmove(data + key->bytes - length, data, length); data[0] = 0; data[1] = 2; @@ -87,6 +95,8 @@ void rsaencrypt(unsigned char *data, int length, struct RSAKey *key) freebn(b1); freebn(b2); + + return 1; } static void sha512_mpint(SHA512_State * s, Bignum b) @@ -378,13 +388,25 @@ unsigned char *rsa_public_blob(struct RSAKey *key, int *len) } /* Given a public blob, determine its length. */ -int rsa_public_blob_len(void *data) +int rsa_public_blob_len(void *data, int maxlen) { unsigned char *p = (unsigned char *)data; + int n; + if (maxlen < 4) + return -1; p += 4; /* length word */ - p += ssh1_read_bignum(p, NULL); /* exponent */ - p += ssh1_read_bignum(p, NULL); /* modulus */ + maxlen -= 4; + + n = ssh1_read_bignum(p, maxlen, NULL); /* exponent */ + if (n < 0) + return -1; + p += n; + + n = ssh1_read_bignum(p, maxlen, NULL); /* modulus */ + if (n < 0) + return -1; + p += n; return p - (unsigned char *)data; } @@ -768,6 +790,7 @@ static unsigned char *rsa2_sign(void *key, char *data, int datalen, SHA_Simple(data, datalen, hash); nbytes = (bignum_bitcount(rsa->modulus) - 1) / 8; + assert(1 <= nbytes - 20 - ASN1_LEN); bytes = snewn(nbytes, unsigned char); bytes[0] = 1; @@ -813,3 +836,157 @@ const struct ssh_signkey ssh_rsa = { "ssh-rsa", "rsa2" }; + +void *ssh_rsakex_newkey(char *data, int len) +{ + return rsa2_newkey(data, len); +} + +void ssh_rsakex_freekey(void *key) +{ + rsa2_freekey(key); +} + +int ssh_rsakex_klen(void *key) +{ + struct RSAKey *rsa = (struct RSAKey *) key; + + return bignum_bitcount(rsa->modulus); +} + +static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen, + void *vdata, int datalen) +{ + unsigned char *data = (unsigned char *)vdata; + unsigned count = 0; + + while (datalen > 0) { + int i, max = (datalen > h->hlen ? h->hlen : datalen); + void *s; + unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN]; + + assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN); + PUT_32BIT(counter, count); + s = h->init(); + h->bytes(s, seed, seedlen); + h->bytes(s, counter, 4); + h->final(s, hash); + count++; + + for (i = 0; i < max; i++) + data[i] ^= hash[i]; + + data += max; + datalen -= max; + } +} + +void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen, + unsigned char *out, int outlen, + void *key) +{ + Bignum b1, b2; + struct RSAKey *rsa = (struct RSAKey *) key; + int k, i; + char *p; + const int HLEN = h->hlen; + + /* + * Here we encrypt using RSAES-OAEP. Essentially this means: + * + * - we have a SHA-based `mask generation function' which + * creates a pseudo-random stream of mask data + * deterministically from an input chunk of data. + * + * - we have a random chunk of data called a seed. + * + * - we use the seed to generate a mask which we XOR with our + * plaintext. + * + * - then we use _the masked plaintext_ to generate a mask + * which we XOR with the seed. + * + * - then we concatenate the masked seed and the masked + * plaintext, and RSA-encrypt that lot. + * + * The result is that the data input to the encryption function + * is random-looking and (hopefully) contains no exploitable + * structure such as PKCS1-v1_5 does. + * + * For a precise specification, see RFC 3447, section 7.1.1. + * Some of the variable names below are derived from that, so + * it'd probably help to read it anyway. + */ + + /* k denotes the length in octets of the RSA modulus. */ + k = (7 + bignum_bitcount(rsa->modulus)) / 8; + + /* The length of the input data must be at most k - 2hLen - 2. */ + assert(inlen > 0 && inlen <= k - 2*HLEN - 2); + + /* The length of the output data wants to be precisely k. */ + assert(outlen == k); + + /* + * Now perform EME-OAEP encoding. First set up all the unmasked + * output data. + */ + /* Leading byte zero. */ + out[0] = 0; + /* At position 1, the seed: HLEN bytes of random data. */ + for (i = 0; i < HLEN; i++) + out[i + 1] = random_byte(); + /* At position 1+HLEN, the data block DB, consisting of: */ + /* The hash of the label (we only support an empty label here) */ + h->final(h->init(), out + HLEN + 1); + /* A bunch of zero octets */ + memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1)); + /* A single 1 octet, followed by the input message data. */ + out[outlen - inlen - 1] = 1; + memcpy(out + outlen - inlen, in, inlen); + + /* + * Now use the seed data to mask the block DB. + */ + oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1); + + /* + * And now use the masked DB to mask the seed itself. + */ + oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN); + + /* + * Now `out' contains precisely the data we want to + * RSA-encrypt. + */ + b1 = bignum_from_bytes(out, outlen); + b2 = modpow(b1, rsa->exponent, rsa->modulus); + p = (char *)out; + for (i = outlen; i--;) { + *p++ = bignum_byte(b2, i); + } + freebn(b1); + freebn(b2); + + /* + * And we're done. + */ +} + +static const struct ssh_kex ssh_rsa_kex_sha1 = { + "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1 +}; + +static const struct ssh_kex ssh_rsa_kex_sha256 = { + "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256 +}; + +static const struct ssh_kex *const rsa_kex_list[] = { + &ssh_rsa_kex_sha256, + &ssh_rsa_kex_sha1 +}; + +const struct ssh_kexes ssh_rsa_kex = { + sizeof(rsa_kex_list) / sizeof(*rsa_kex_list), + rsa_kex_list +};