📄 sshdss.c
字号:
PUT_32BIT(p, 7); p += 4; memcpy(p, "ssh-dss", 7); p += 7; PUT_32BIT(p, plen); p += 4; for (i = plen; i--;) *p++ = bignum_byte(dss->p, i); PUT_32BIT(p, qlen); p += 4; for (i = qlen; i--;) *p++ = bignum_byte(dss->q, i); PUT_32BIT(p, glen); p += 4; for (i = glen; i--;) *p++ = bignum_byte(dss->g, i); PUT_32BIT(p, ylen); p += 4; for (i = ylen; i--;) *p++ = bignum_byte(dss->y, i); assert(p == blob + bloblen); *len = bloblen; return blob;}static unsigned char *dss_private_blob(void *key, int *len){ struct dss_key *dss = (struct dss_key *) key; int xlen, bloblen; int i; unsigned char *blob, *p; xlen = (bignum_bitcount(dss->x) + 8) / 8; /* * mpint x, string[20] the SHA of p||q||g. Total 4 + xlen. */ 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); assert(p == blob + bloblen); *len = bloblen; return blob;}static void *dss_createkey(unsigned char *pub_blob, int pub_len, unsigned char *priv_blob, int priv_len){ struct dss_key *dss; char *pb = (char *) priv_blob; char *hash; int hashlen; SHA_State s; unsigned char digest[20]; Bignum ytest; dss = dss_newkey((char *) pub_blob, pub_len); dss->x = getmp(&pb, &priv_len); /* * Check the obsolete hash in the old DSS key format. */ 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; } } /* * Now ensure g^x mod p really is y. */ ytest = modpow(dss->g, dss->x, dss->p); if (0 != bignum_cmp(ytest, dss->y)) { dss_freekey(dss); return NULL; } freebn(ytest); return dss;}static void *dss_openssh_createkey(unsigned char **blob, int *len){ char **b = (char **) blob; struct dss_key *dss; dss = snew(struct dss_key); if (!dss) return NULL; dss->p = getmp(b, len); dss->q = getmp(b, len); dss->g = getmp(b, len); dss->y = getmp(b, len); dss->x = getmp(b, len); if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x) { sfree(dss->p); sfree(dss->q); sfree(dss->g); sfree(dss->y); sfree(dss->x); sfree(dss); return NULL; } return dss;}static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len){ struct dss_key *dss = (struct dss_key *) key; int bloblen, i; bloblen = ssh2_bignum_length(dss->p) + ssh2_bignum_length(dss->q) + ssh2_bignum_length(dss->g) + ssh2_bignum_length(dss->y) + ssh2_bignum_length(dss->x); if (bloblen > len) return bloblen; bloblen = 0;#define ENC(x) \ PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \ for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i); ENC(dss->p); ENC(dss->q); ENC(dss->g); ENC(dss->y); ENC(dss->x); return bloblen;}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: * * - invent a random k between 1 and q-1 (exclusive). * - Compute r = (g^k mod p) mod q. * - Compute s = k^-1 * (hash + x*r) mod q. * * This has the dangerous properties that: * * - if an attacker in possession of the public key _and_ the * signature (for example, the host you just authenticated * to) can guess your k, he can reverse the computation of s * and work out x = r^-1 * (s*k - hash) mod q. That is, he * can deduce the private half of your key, and masquerade * as you for as long as the key is still valid. * * - since r is a function purely of k and the public key, if * the attacker only has a _range of possibilities_ for k * it's easy for him to work through them all and check each * one against r; he'll never be unsure of whether he's got * the right one. * * - if you ever sign two different hashes with the same k, it * will be immediately obvious because the two signatures * will have the same r, and moreover an attacker in * possession of both signatures (and the public key of * course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q, * and from there deduce x as before. * * - the Bleichenbacher attack on DSA makes use of methods of * generating k which are significantly non-uniformly * distributed; in particular, generating a 160-bit random * number and reducing it mod q is right out. * * For this reason we must be pretty careful about how we * generate our k. Since this code runs on Windows, with no * particularly good system entropy sources, we can't trust our * RNG itself to produce properly unpredictable data. Hence, we * use a totally different scheme instead. * * What we do is to take a SHA-512 (_big_) hash of the private * key x, and then feed this into another SHA-512 hash that * also includes the message hash being signed. That is: * * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) * * This number is 512 bits long, so reducing it mod q won't be * noticeably non-uniform. So * * k = proto_k mod q * * This has the interesting property that it's _deterministic_: * signing the same hash twice with the same key yields the * same signature. * * Despite this determinism, it's still not predictable to an * attacker, because in order to repeat the SHA-512 * construction that created it, the attacker would have to * know the private key value x - and by assumption he doesn't, * because if he knew that he wouldn't be attacking k! * * (This trick doesn't, _per se_, protect against reuse of k. * Reuse of k is left to chance; all it does is prevent * _excessively high_ chances of reuse of k due to entropy * problems.) * * Thanks to Colin Plumb for the general idea of using x to * ensure k is hard to guess, and to the Cambridge University * Computer Security Group for helping to argue out all the * fine details. */ struct dss_key *dss = (struct dss_key *) key; SHA512_State ss; unsigned char digest[20], digest512[64]; Bignum proto_k, k, gkp, hash, kinv, hxr, r, s; unsigned char *bytes; int nbytes, i; SHA_Simple(data, datalen, digest); /* * Hash some identifying text plus x. */ SHA512_Init(&ss); SHA512_Bytes(&ss, "DSA deterministic k generator", 30); sha512_mpint(&ss, dss->x); SHA512_Final(&ss, digest512); /* * Now hash that digest plus the message hash. */ SHA512_Init(&ss); SHA512_Bytes(&ss, digest512, sizeof(digest512)); SHA512_Bytes(&ss, digest, sizeof(digest)); SHA512_Final(&ss, digest512); memset(&ss, 0, sizeof(ss)); /* * Now convert the result into a bignum, and reduce it mod q. */ proto_k = bignum_from_bytes(digest512, 64); k = bigmod(proto_k, dss->q); freebn(proto_k); memset(digest512, 0, sizeof(digest512)); /* * Now we have k, so just go ahead and compute the signature. */ gkp = modpow(dss->g, k, dss->p); /* g^k mod p */ r = bigmod(gkp, dss->q); /* r = (g^k mod p) mod q */ freebn(gkp); hash = bignum_from_bytes(digest, 20); kinv = modinv(k, dss->q); /* k^-1 mod q */ hxr = bigmuladd(dss->x, r, hash); /* hash + x*r */ s = modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash + x*r) mod q */ freebn(hxr); freebn(kinv); freebn(hash); /* * Signature blob is * * string "ssh-dss" * string two 20-byte numbers r and s, end to end * * i.e. 4+7 + 4+40 bytes. */ nbytes = 4 + 7 + 4 + 40; bytes = snewn(nbytes, unsigned char); PUT_32BIT(bytes, 7); memcpy(bytes + 4, "ssh-dss", 7); PUT_32BIT(bytes + 4 + 7, 40); for (i = 0; i < 20; i++) { bytes[4 + 7 + 4 + i] = bignum_byte(r, 19 - i); bytes[4 + 7 + 4 + 20 + i] = bignum_byte(s, 19 - i); } freebn(r); freebn(s); *siglen = nbytes; return bytes;}const struct ssh_signkey ssh_dss = { dss_newkey, dss_freekey, dss_fmtkey, dss_public_blob, dss_private_blob, dss_createkey, dss_openssh_createkey, dss_openssh_fmtkey, dss_pubkey_bits, dss_fingerprint, dss_verifysig, dss_sign, "ssh-dss", "dss"};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -