diff --git a/sshrsa.c b/sshrsa.c index 32842939..fac01faf 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -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(); diff --git a/test/cryptsuite.py b/test/cryptsuite.py index c792db4b..b3b23960 100755 --- a/test/cryptsuite.py +++ b/test/cryptsuite.py @@ -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):