diff options
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/serialization.rst | 5 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/serialization.py | 49 | ||||
-rw-r--r-- | src/cryptography/utils.py | 7 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_serialization.py | 69 |
5 files changed, 77 insertions, 59 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0394ebd9..cf6d2252 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,9 +19,9 @@ Changelog * Added support for encoding and decoding :rfc:`6979` signatures in :doc:`/hazmat/primitives/asymmetric/utils`. * Added - :func:`~cryptography.hazmat.primitives.serialization.load_ssh_public_key` - to support the loading of OpenSSH public keys (:rfc:`4253`). Currently, only RSA - is supported. + :func:`~cryptography.hazmat.primitives.serialization.load_ssh_public_key` to + support the loading of OpenSSH public keys (:rfc:`4253`). Currently, only RSA + keys are supported. 0.6.1 - 2014-10-15 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 45c7a5bc..a9392c7b 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -199,7 +199,7 @@ KEY-----`` or ``-----BEGIN DSA PRIVATE KEY-----``. OpenSSH Public Key ~~~~~~~~~~~~~~~~~~ -The format used by OpenSSH to store public keys as specified in :rfc:`4253`. +The format used by OpenSSH to store public keys, as specified in :rfc:`4253`. Currently, only RSA public keys are supported. Any other type of key will result in an exception being thrown. @@ -224,7 +224,8 @@ purposes):: :param bytes data: The OpenSSH encoded key data. - :param backend: A backend provider. + :param backend: An + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` provider. :returns: A new instance of a public key type. diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py index 8a4c8bd8..858ec043 100644 --- a/src/cryptography/hazmat/primitives/serialization.py +++ b/src/cryptography/hazmat/primitives/serialization.py @@ -49,7 +49,7 @@ def load_pem_public_key(data, backend): def load_ssh_public_key(data, backend): key_parts = data.split(b' ') - if len(key_parts) < 2 or len(key_parts) > 3: + if len(key_parts) != 2 and len(key_parts) != 3: raise ValueError( 'Key is not in the proper format or contains extra data.') @@ -62,21 +62,21 @@ def load_ssh_public_key(data, backend): if not key_type.startswith(b'ssh-rsa'): raise UnsupportedAlgorithm('Only RSA keys are currently supported.') - return _load_ssh_rsa_public_key(key_type, key_body, backend) + return _load_ssh_rsa_public_key(key_body, backend) -def _load_ssh_rsa_public_key(key_type, key_body, backend): +def _load_ssh_rsa_public_key(key_body, backend): data = base64.b64decode(key_body) - key_body_type, rest = _read_next_string(data) + key_type, rest = _read_next_string(data) e, rest = _read_next_mpint(rest) n, rest = _read_next_mpint(rest) - if key_type != key_body_type: + if key_type != b'ssh-rsa': raise ValueError( 'Key header and key body contain different key type values.') - if len(rest) != 0: + if rest: raise ValueError('Key body contains extra bytes.') return backend.load_rsa_public_numbers(RSAPublicNumbers(e, n)) @@ -84,26 +84,37 @@ def _load_ssh_rsa_public_key(key_type, key_body, backend): def _read_next_string(data): """Retrieves the next RFC 4251 string value from the data.""" - str_len, = struct.unpack('>I', data[0:4]) + str_len, = struct.unpack('>I', data[:4]) return data[4:4 + str_len], data[4 + str_len:] def _read_next_mpint(data): - """Reads the next mpint from the data. Currently, all mpints are - interpreted as unsigned.""" + """ + Reads the next mpint from the data. + + Currently, all mpints are interpreted as unsigned. + """ mpint_data, rest = _read_next_string(data) - if sys.version_info >= (3, 2): - # If we're using >= 3.2, use int.from_bytes for identical results. - return int.from_bytes(mpint_data, byteorder='big', signed=False), rest + return _int_from_bytes(mpint_data, byteorder='big', signed=False), rest + + + +if hasattr(int, "from_bytes"): + _int_from_bytes = int.from_bytes +else: + def _int_from_bytes(data, byteorder, signed=False): + assert byteorder == 'big' + assert not signed - if len(mpint_data) % 4 != 0: - mpint_data = (b'\x00' * (4 - (len(mpint_data) % 4))) + mpint_data + if len(data) % 4 != 0: + data = (b'\x00' * (4 - (len(data) % 4))) + data - result = 0 + result = 0 - while len(mpint_data) > 0: - result = (result << 32) + struct.unpack('>I', mpint_data[0:4])[0] - mpint_data = mpint_data[4:] + while len(data) > 0: + digit, = struct.unpack('>I', data[:4]) + result = (result << 32) + digit + data = data[4:] - return result, rest + return result diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 63464dfa..78f73464 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -48,8 +48,9 @@ def verify_interface(iface, klass): ) -def bit_length(x): - if sys.version_info >= (2, 7): +if sys.version_info >= (2, 7): + def bit_length(x): return x.bit_length() - else: +else: + def bit_length(x): return len(bin(x)) - (2 + (x <= 0)) diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 9180b9aa..2b9d05a7 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -704,53 +704,58 @@ class TestSSHSerialization(object): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_rsa_key_types_dont_match(self, backend): - ssh_key = textwrap.dedent("""\ - ssh-bad AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk - FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll - PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK - vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f - sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy - ///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX - 2MzHvnbv testkey@localhost extra""").encode() # ssh-bad + ssh_key = ( + b"ssh-bad AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk" + b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll" + b"PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK" + b"vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f" + b"sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy" + b"///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX" + b"2MzHvnbv testkey@localhost extra" + ) with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_rsa_extra_string_after_comment(self, backend): - ssh_key = textwrap.dedent("""\ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk - FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll - PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK - vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f - sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy - ///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX - 2MzHvnbv testkey@localhost extra""").encode() # Extra appended + ssh_key = ( + b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk" + b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll" + b"PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK" + b"vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f" + b"sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy" + b"///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX" + # Extra section appended + b"2MzHvnbv testkey@localhost extra" + ) with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_rsa_extra_data_after_modulo(self, backend): - ssh_key = textwrap.dedent("""\ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk - FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll - PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK - vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f - sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy - ///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX - 2MzHvnbvAQ== testkey@localhost""").encode() # Extra 0x01 appended + ssh_key = ( + b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk" + b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll" + b"PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK" + b"vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f" + b"sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy" + b"///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX" + b"2MzHvnbvAQ== testkey@localhost" + ) with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_rsa(self, backend): - ssh_key = textwrap.dedent("""\ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk - FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll - PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK - vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f - sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy - ///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX - 2MzHvnbv testkey@localhost""").encode() + ssh_key = ( + b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk" + b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll" + b"PDA7ZRBnrfiHpSQYQ874AZaAoIjgkv7DBfsE6gcDQLub0PFjWyrYQUJhtOLQEK" + b"vY/G0vt2iRL3juawWmCFdTK3W3XvwAdgGk71i6lHt+deOPNEPN2H58E4odrZ2f" + b"sxn/adpDqfb2sM0kPwQs0aWvrrKGvUaustkivQE4XWiSFnB0oJB/lKK/CKVKuy" + b"///ImSCGHQRvhwariN2tvZ6CBNSLh3iQgeB0AkyJlng7MXB2qYq/Ci2FUOryCX" + b"2MzHvnbv testkey@localhost" + ) key = load_ssh_public_key(ssh_key, backend) |