Skip to content

Commit

Permalink
Fix DSA keys validation. Add sha256 fingerprints
Browse files Browse the repository at this point in the history
Related to #9
  • Loading branch information
Olli Jarva committed Apr 13, 2016
1 parent e328660 commit c7569df
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 87 deletions.
5 changes: 3 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ Usage:
"dfybgtAR1ow3Qkb9GPQ6wkFHQqcFDe6faKCxH6iDRteo4D8L8B"
"xwzN42uZSB0nfmjkIxFTcEU3mFSXEbWByg78aoddMrAAjatyrh"
"H1pON6P0= ojarva@ojar-laptop")
print(ssh.bits) # 768
print(ssh.hash()) # 56:84:1e:90:08:3b:60:c7:29:70:5f:5e:25:a6:3b:86
print(ssh.bits) # 768
print(ssh.hash_md5()) # 56:84:1e:90:08:3b:60:c7:29:70:5f:5e:25:a6:3b:86
print(ssh.hash_sha256()) # SHA256:xk3IEJIdIoR9MmSRXTP98rjDdZocmXJje/28ohMQEwM


Exceptions
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setup(
name='sshpubkeys',
version='1.1.0',
version='1.2.0',
description='SSH public key parser',
long_description=long_description,
url='https://github.com/ojarva/python-sshpubkeys',
Expand Down
28 changes: 21 additions & 7 deletions sshpubkeys/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import struct
import sys
import base64
import warnings
import binascii
import ecdsa

Expand Down Expand Up @@ -68,9 +69,8 @@ class SSHKey(object): # pylint:disable=too-many-instance-attributes

VALID_DSA_PARAMETERS = [
(1024, 160),
(2048, 224),
(2048, 256),
(3072, 256),
(2048, 160),
(3072, 160),
]

ECDSA_CURVE_DATA = {
Expand All @@ -97,7 +97,15 @@ def __init__(self, keydata):
self.parse()

def hash(self):
""" Calculates fingerprint hash.
""" Calculates md5 fingerprint.
Deprecated, use .hash_md5() instead.
"""
warnings.warn("hash() is deprecated. Use hash_md5() or hash_sha256() instead.")
return self.hash_md5()

def hash_md5(self):
""" Calculates md5 fingerprint.
Shamelessly copied from http://stackoverflow.com/questions/6682815/deriving-an-ssh-fingerprint-from-a-public-key-in-python
Expand All @@ -106,6 +114,11 @@ def hash(self):
fp_plain = hashlib.md5(self.decoded_key).hexdigest()
return ':'.join(a + b for a, b in zip(fp_plain[::2], fp_plain[1::2]))

def hash_sha256(self):
""" Calculates sha256 fingerprint. """
fp_plain = hashlib.sha256(self.decoded_key).digest()
return "SHA256:" + base64.b64encode(fp_plain).replace(b"=", b"")

def _unpack_by_int(self):
""" Returns next data field. """
# Unpack length of data field
Expand Down Expand Up @@ -211,10 +224,11 @@ def _process_ecdsa_sha(self):

data = self._unpack_by_int()
try:
# data starts with \x04, which should be discarded.
ecdsa_key = ecdsa.VerifyingKey.from_string(data[1:], curve, hash_algorithm)
except AssertionError:
raise InvalidKeyException("Invalid ecdsa key")
self.bits = int(curve_information.replace(b"nistp", b"")) # TODO
self.bits = int(curve_information.replace(b"nistp", b"")) # TODO: this is rather ugly way to extract bit length
self.ecdsa = ecdsa_key

def _process_ed25516(self):
Expand All @@ -233,8 +247,8 @@ def _process_ed25516(self):
raise InvalidKeyException("ed25519 verifying key must be >0.")

self.bits = verifying_key_length
if self.bits not in (512, 256):
raise InvalidKeyLengthException("ed25519 keys must be 256 or 512 bits (was %s bits)" % self.bits)
if self.bits != 256:
raise InvalidKeyLengthException("ed25519 keys must be 256 bits (was %s bits)" % self.bits)

def _process_key(self):
if self.key_type == b"ssh-rsa":
Expand Down
17 changes: 9 additions & 8 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@


class TestKeys(unittest.TestCase):
def check_key(self, pubkey, bits, fingerprint):
def check_key(self, pubkey, bits, fingerprint_md5, fingerprint_sha256):
""" Checks valid key """
ssh = SSHKey(pubkey)
self.assertEqual(ssh.bits, bits)
self.assertEqual(ssh.hash(), fingerprint)
self.assertEqual(ssh.hash_md5(), fingerprint_md5)
if fingerprint_sha256 is not None:
self.assertEqual(ssh.hash_sha256(), fingerprint_sha256)

def check_fail(self, pubkey, expected_error):
""" Checks that key check raises specified exception """
Expand All @@ -27,13 +29,12 @@ def check_fail(self, pubkey, expected_error):
def loop_valid(keyset, prefix):
""" Loop over list of valid keys and dynamically create tests """
for i, items in enumerate(keyset):
def ch(pubkey, bits, fingerprint):
return lambda self: self.check_key(pubkey, bits, fingerprint)
def ch(pubkey, bits, fingerprint_md5, fingerprint_sha256):
return lambda self: self.check_key(pubkey, bits, fingerprint_md5, fingerprint_sha256)
prefix_tmp = "%s_%s" % (prefix, i)
if len(items) == 4: # If there is an extra item, use that as test name.
prefix_tmp = items.pop()
pubkey, bits, fingerprint = items
setattr(TestKeys, "test_%s" % prefix_tmp, ch(pubkey, bits, fingerprint))
prefix_tmp = items.pop()
pubkey, bits, fingerprint_md5, fingerprint_sha256 = items
setattr(TestKeys, "test_%s" % prefix_tmp, ch(pubkey, bits, fingerprint_md5, fingerprint_sha256))


def loop_invalid(keyset, prefix):
Expand Down
5 changes: 5 additions & 0 deletions tests/invalid_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@
["", InvalidKeyException, "empty_key"],
["- -", InvalidKeyException, "no_content"],
["ssh-invalid-key-type AAAAFHNzaC1pbnZhbGlkLWtleS10eXBlAAAAgMEgAGDgAKEBAMAgoECggSCAQIEAoIAhASEhAKDAAOAAwGCBIKAAYGCgYKDhAEBAwICAoIDgIADAYKAAYGAAYAEgwGBBIGDhIOBg4CAAoQDAIEAgIEEggEDgQGCAIGAhAQDgoKAhIAAggSBA4CBggOEBIEDgoMDAwKBg4MDAYIDgAAAAIAAAAAAAAAAAAAAAAIAggEEggSBAIIBgAMDAQADAIQEgAAAAgJAQABBgICBQUABQcFAAcJBgUIAAIHAAkJBQEIAQMEAgIHBQEFCAUBBQUHAQMGBgQBCAEEAgICBQYDBwIJBAYFCQYDBwIAAQIHCQMEBwYCBAICAAEFBQAHAgAIBQAJBwgEBwYGCAEHBgkBAwEBAQcHAQMGCQMCBQIABAUJAgEIAgAAAAgIGAAYIDgoSBA4ABAgAEAIEEAoEAg4IDAgQCAwCDgQMAgQABgoGAAgMCgoOCA4QABAGBAQQDgoKABASBhAQDgoIBgIQEgAQEAQSBg4IEBICEggKBAAABgYKDAgGDgYAAgoGCAoODgYGEAwIBAoACgQADBAGDgASCgQMDgAABAISA", NotImplementedError, "not_implemented_key_type"],

# 512 bit ed25519 keys are not yet supported by OpenSSH
["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAQIMcGCUjHYKD/rfvSGiNSB1ij8hScTB7e1bo3XK2oaGGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", InvalidKeyLengthException, "512bit_ed25519"],
["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAQIBBAIDAICBAgEABACAggGCgAABAwOEAwGBAgEEg4SBAAKBBIGEAgSEgAIEgoMCgICAgYMCgYQDgoKBgIIDgwCA=", InvalidKeyLengthException, "512bit_ed25519"],

]

0 comments on commit c7569df

Please sign in to comment.