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

Fix wrong output from ssh1_rsa_fingerprint.

I broke it last year in commit 4988fd410, when I made hash contexts
expose a BinarySink interface. I went round finding no end of long-
winded ways of pushing things into hash contexts, often reimplementing
some standard thing like the wire formatting of an mpint, and rewrote
them more concisely using one or two put_foo calls.

But I failed to notice that the hash preimage used in SSH-1 key
fingerprints is _not_ implementable by put_ssh1_mpint! It consists of
the two public-key integers encoded in multi-byte binary big-endian
form, but without any preceding length field at all. I must have
looked too hastily, 'recognised' it as just implementing an mpint
formatter yet again, and replaced it with put_ssh1_mpint. So SSH-1 key
fingerprints have been completely wrong in the snapshots for months.

Fixed now, and this time, added a comment to warn me in case I get the
urge to simplify the code again, and a regression test in cryptsuite.
This commit is contained in:
Simon Tatham 2019-01-05 08:14:32 +00:00
parent e5e520d48e
commit 4a0fa90979
2 changed files with 27 additions and 2 deletions

View File

@ -237,9 +237,19 @@ char *rsa_ssh1_fingerprint(RSAKey *key)
strbuf *out;
int i;
/*
* The hash preimage for SSH-1 key fingerprinting consists of the
* modulus and exponent _without_ any preceding length field -
* just the minimum number of bytes to represent each integer,
* stored big-endian, concatenated with no marker at the division
* between them.
*/
MD5Init(&md5c);
put_mp_ssh1(&md5c, key->modulus);
put_mp_ssh1(&md5c, key->exponent);
for (size_t i = (mp_get_nbits(key->modulus) + 7) / 8; i-- > 0 ;)
put_byte(&md5c, mp_get_byte(key->modulus, i));
for (size_t i = (mp_get_nbits(key->exponent) + 7) / 8; i-- > 0 ;)
put_byte(&md5c, mp_get_byte(key->exponent, i));
MD5Final(digest, &md5c);
out = strbuf_new();

View File

@ -40,6 +40,12 @@ def ssh2_mpint(x):
bytevals = [0xFF & (x >> (8*n)) for n in range(nbits(x)//8, -1, -1)]
return struct.pack(">L" + "B" * len(bytevals), len(bytevals), *bytevals)
def rsa_bare(e, n):
rsa = rsa_new()
get_rsa_ssh1_pub(ssh_uint32(nbits(n)) + ssh1_mpint(e) + ssh1_mpint(n),
rsa, 'exponent_first')
return rsa
def find_non_square_mod(p):
# Find a non-square mod p, using the Jacobi symbol
# calculation function from eccref.py.
@ -737,6 +743,15 @@ class ecc(unittest.TestCase):
self.assertEqual(int(x), int(rGi.x))
self.assertEqual(int(y), int(rGi.y))
class crypt(unittest.TestCase):
def testSSH1Fingerprint(self):
# Example key and reference fingerprint value generated by
# OpenSSH 6.7 ssh-keygen
rsa = rsa_bare(65537, 984185866443261798625575612408956568591522723900235822424492423996716524817102482330189709310179009158443944785704183009867662230534501187034891091310377917105259938712348098594526746211645472854839799025154390701673823298369051411)
fp = rsa_ssh1_fingerprint(rsa)
self.assertEqual(
fp, b"768 96:12:c8:bc:e6:03:75:86:e8:c7:b9:af:d8:0c:15:75")
class standard_test_vectors(unittest.TestCase):
def testAES(self):
def vector(cipher, key, plaintext, ciphertext):