X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/9e55cd457afb2e58007e96f54c82194334ef2266..edb548cd8283b5e3f680a0a8b641f9afb12d4ba4:/sshdss.c diff --git a/sshdss.c b/sshdss.c index 65844a9b..532c13f2 100644 --- a/sshdss.c +++ b/sshdss.c @@ -1,3 +1,7 @@ +/* + * Digital Signature Standard implementation for PuTTY. + */ + #include #include #include @@ -5,18 +9,6 @@ #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); } - static void sha_mpint(SHA_State * s, Bignum b) { unsigned char lenbuf[4]; @@ -28,7 +20,7 @@ static void sha_mpint(SHA_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void sha512_mpint(SHA512_State * s, Bignum b) @@ -42,7 +34,7 @@ static void sha512_mpint(SHA512_State * s, Bignum b) lenbuf[0] = bignum_byte(b, len); SHA512_Bytes(s, lenbuf, 1); } - memset(lenbuf, 0, sizeof(lenbuf)); + smemclr(lenbuf, sizeof(lenbuf)); } static void getstring(char **data, int *datalen, char **p, int *length) @@ -50,7 +42,9 @@ static void getstring(char **data, int *datalen, char **p, int *length) *p = NULL; if (*datalen < 4) return; - *length = GET_32BIT(*data); + *length = toint(GET_32BIT(*data)); + if (*length < 0) + return; *datalen -= 4; *data += 4; if (*datalen < *length) @@ -70,7 +64,7 @@ static Bignum getmp(char **data, int *datalen) return NULL; if (p[0] & 0x80) return NULL; /* negative mp */ - b = bignum_from_bytes(p, length); + b = bignum_from_bytes((unsigned char *)p, length); return b; } @@ -78,7 +72,7 @@ static Bignum get160(char **data, int *datalen) { Bignum b; - b = bignum_from_bytes(*data, 20); + b = bignum_from_bytes((unsigned char *)*data, 20); *data += 20; *datalen -= 20; @@ -91,7 +85,7 @@ static void *dss_newkey(char *data, int len) int slen; struct dss_key *dss; - dss = smalloc(sizeof(struct dss_key)); + dss = snew(struct dss_key); if (!dss) return NULL; getstring(&data, &len, &p, &slen); @@ -106,7 +100,7 @@ static void *dss_newkey(char *data, int len) } #endif - if (!p || memcmp(p, "ssh-dss", 7)) { + if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) { sfree(dss); return NULL; } @@ -141,7 +135,7 @@ static char *dss_fmtkey(void *key) len += 4 * (bignum_bitcount(dss->q) + 15) / 16; len += 4 * (bignum_bitcount(dss->g) + 15) / 16; len += 4 * (bignum_bitcount(dss->y) + 15) / 16; - p = smalloc(len); + p = snewn(len, char); if (!p) return NULL; @@ -188,7 +182,7 @@ static char *dss_fingerprint(void *key) int numlen, i; MD5Init(&md5c); - MD5Update(&md5c, "\0\0\0\7ssh-dss", 11); + MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-dss", 11); #define ADD_BIGNUM(bignum) \ numlen = (bignum_bitcount(bignum)+8)/8; \ @@ -209,7 +203,7 @@ static char *dss_fingerprint(void *key) for (i = 0; i < 16; i++) sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "", digest[i]); - ret = smalloc(strlen(buffer) + 1); + ret = snewn(strlen(buffer) + 1, char); if (ret) strcpy(ret, buffer); return ret; @@ -239,14 +233,14 @@ static int dss_verifysig(void *key, char *sig, int siglen, #endif /* * Commercial SSH (2.0.13) and OpenSSH disagree over the format - * of a DSA signature. OpenSSH is in line with the IETF drafts: + * of a DSA signature. OpenSSH is in line with RFC 4253: * it uses a string "ssh-dss", followed by a 40-byte string * containing two 160-bit integers end-to-end. Commercial SSH * can't be bothered with the header bit, and considers a DSA * signature blob to be _just_ the 40-byte string containing * the two 160-bit integers. We tell them apart by measuring * the length: length 40 means the commercial-SSH bug, anything - * else is assumed to be IETF-compliant. + * else is assumed to be RFC-compliant. */ if (siglen != 40) { /* bug not present; read admin fields */ getstring(&sig, &siglen, &p, &slen); @@ -268,7 +262,7 @@ static int dss_verifysig(void *key, char *sig, int siglen, /* * Step 2. u1 <- SHA(message) * w mod q. */ - SHA_Simple(data, datalen, hash); + SHA_Simple(data, datalen, (unsigned char *)hash); p = hash; slen = 20; sha = get160(&p, &slen); @@ -295,6 +289,8 @@ static int dss_verifysig(void *key, char *sig, int siglen, freebn(w); freebn(sha); + freebn(u1); + freebn(u2); freebn(gu1p); freebn(yu2p); freebn(gu1yu2p); @@ -322,7 +318,7 @@ static unsigned char *dss_public_blob(void *key, int *len) * 27 + sum of lengths. (five length fields, 20+7=27). */ bloblen = 27 + plen + qlen + glen + ylen; - blob = smalloc(bloblen); + blob = snewn(bloblen, unsigned char); p = blob; PUT_32BIT(p, 7); p += 4; @@ -355,31 +351,19 @@ static unsigned char *dss_private_blob(void *key, int *len) int xlen, bloblen; int i; unsigned char *blob, *p; - SHA_State s; - unsigned char digest[20]; xlen = (bignum_bitcount(dss->x) + 8) / 8; /* - * mpint x, string[20] the SHA of p||q||g. Total 28 + xlen. - * (two length fields and twenty bytes, 20+8=28). + * mpint x, string[20] the SHA of p||q||g. Total 4 + xlen. */ - bloblen = 28 + xlen; - blob = smalloc(bloblen); + bloblen = 4 + xlen; + blob = snewn(bloblen, unsigned char); p = blob; PUT_32BIT(p, xlen); p += 4; for (i = xlen; i--;) *p++ = bignum_byte(dss->x, i); - PUT_32BIT(p, 20); - SHA_Init(&s); - sha_mpint(&s, dss->p); - sha_mpint(&s, dss->q); - sha_mpint(&s, dss->g); - SHA_Final(&s, digest); - p += 4; - for (i = 0; i < 20; i++) - *p++ = digest[i]; assert(p == blob + bloblen); *len = bloblen; return blob; @@ -398,24 +382,22 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, dss = dss_newkey((char *) pub_blob, pub_len); dss->x = getmp(&pb, &priv_len); - getstring(&pb, &priv_len, &hash, &hashlen); /* - * Verify details of the key. First check that the hash is - * indeed a hash of p||q||g. + * Check the obsolete hash in the old DSS key format. */ - if (hashlen != 20) { - dss_freekey(dss); - return NULL; - } - SHA_Init(&s); - sha_mpint(&s, dss->p); - sha_mpint(&s, dss->q); - sha_mpint(&s, dss->g); - SHA_Final(&s, digest); - if (0 != memcmp(hash, digest, 20)) { - dss_freekey(dss); - return NULL; + hashlen = -1; + getstring(&pb, &priv_len, &hash, &hashlen); + if (hashlen == 20) { + SHA_Init(&s); + sha_mpint(&s, dss->p); + sha_mpint(&s, dss->q); + sha_mpint(&s, dss->g); + SHA_Final(&s, digest); + if (0 != memcmp(hash, digest, 20)) { + dss_freekey(dss); + return NULL; + } } /* @@ -424,6 +406,7 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len, ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); + freebn(ytest); return NULL; } freebn(ytest); @@ -436,7 +419,7 @@ static void *dss_openssh_createkey(unsigned char **blob, int *len) char **b = (char **) blob; struct dss_key *dss; - dss = smalloc(sizeof(struct dss_key)); + dss = snew(struct dss_key); if (!dss) return NULL; @@ -487,7 +470,19 @@ static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len) return bloblen; } -unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) +static int dss_pubkey_bits(void *blob, int len) +{ + struct dss_key *dss; + int ret; + + dss = dss_newkey((char *) blob, len); + ret = bignum_bitcount(dss->p); + dss_freekey(dss); + + return ret; +} + +static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) { /* * The basic DSS signing algorithm is: @@ -585,7 +580,7 @@ unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) SHA512_Bytes(&ss, digest, sizeof(digest)); SHA512_Final(&ss, digest512); - memset(&ss, 0, sizeof(ss)); + smemclr(&ss, sizeof(ss)); /* * Now convert the result into a bignum, and reduce it mod q. @@ -594,7 +589,7 @@ unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) k = bigmod(proto_k, dss->q); freebn(proto_k); - memset(digest512, 0, sizeof(digest512)); + smemclr(digest512, sizeof(digest512)); /* * Now we have k, so just go ahead and compute the signature. @@ -620,7 +615,7 @@ unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen) * i.e. 4+7 + 4+40 bytes. */ nbytes = 4 + 7 + 4 + 40; - bytes = smalloc(nbytes); + bytes = snewn(nbytes, unsigned char); PUT_32BIT(bytes, 7); memcpy(bytes + 4, "ssh-dss", 7); PUT_32BIT(bytes + 4 + 7, 40); @@ -644,6 +639,7 @@ const struct ssh_signkey ssh_dss = { dss_createkey, dss_openssh_createkey, dss_openssh_fmtkey, + dss_pubkey_bits, dss_fingerprint, dss_verifysig, dss_sign,