diff options
Diffstat (limited to 'tests/hazmat/primitives/test_serialization.py')
-rw-r--r-- | tests/hazmat/primitives/test_serialization.py | 1459 |
1 files changed, 1198 insertions, 261 deletions
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 5dd724894..32debd46c 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -11,27 +11,58 @@ import textwrap import pytest -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +import six + +from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.backends.interfaces import ( - DERSerializationBackend, DSABackend, EllipticCurveBackend, - PEMSerializationBackend, RSABackend + DERSerializationBackend, + DSABackend, + EllipticCurveBackend, + PEMSerializationBackend, + RSABackend, +) +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed25519, + ed448, + rsa, + x25519, + x448, ) -from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.serialization import ( - BestAvailableEncryption, Encoding, NoEncryption, - PrivateFormat, PublicFormat, - load_der_parameters, load_der_private_key, - load_der_public_key, load_pem_parameters, load_pem_private_key, - load_pem_public_key, load_ssh_public_key + BestAvailableEncryption, + Encoding, + NoEncryption, + PrivateFormat, + PublicFormat, + load_der_parameters, + load_der_private_key, + load_der_public_key, + load_pem_parameters, + load_pem_private_key, + load_pem_public_key, + load_ssh_private_key, + load_ssh_public_key, + ssh, ) from .test_ec import _skip_curve_unsupported from .utils import ( - _check_dsa_private_numbers, _check_rsa_private_numbers, - load_vectors_from_file + _check_dsa_private_numbers, + _check_rsa_private_numbers, + load_vectors_from_file, ) -from ...utils import raises_unsupported_algorithm +from ...doubles import DummyKeySerializationEncryption + + +def _skip_fips_format(key_path, password, backend): + if backend._fips_enabled: + if key_path[0] == "Traditional_OpenSSL_Serialization": + pytest.skip("Traditional OpenSSL format blocked in FIPS mode") + if key_path[0] == "PEM_Serialization" and password is not None: + pytest.skip("Encrypted PEM_Serialization blocked in FIPS mode") class TestBufferProtocolSerialization(object): @@ -43,12 +74,13 @@ class TestBufferProtocolSerialization(object): (["DER_Serialization", "enc2-rsa-pkcs8.der"], bytearray(b"baz")), (["DER_Serialization", "unenc-rsa-pkcs8.der"], None), (["DER_Serialization", "testrsa.der"], None), - ] + ], ) def test_load_der_rsa_private_key(self, key_path, password, backend): data = load_vectors_from_file( os.path.join("asymmetric", *key_path), - lambda derfile: derfile.read(), mode="rb" + lambda derfile: derfile.read(), + mode="rb", ) key = load_der_private_key(bytearray(data), password, backend) assert key @@ -61,21 +93,23 @@ class TestBufferProtocolSerialization(object): [ ( ["PEM_Serialization", "rsa_private_key.pem"], - bytearray(b"123456") + bytearray(b"123456"), ), (["PKCS8", "unenc-rsa-pkcs8.pem"], None), (["PKCS8", "enc-rsa-pkcs8.pem"], bytearray(b"foobar")), (["PKCS8", "enc2-rsa-pkcs8.pem"], bytearray(b"baz")), ( ["Traditional_OpenSSL_Serialization", "key1.pem"], - bytearray(b"123456") + bytearray(b"123456"), ), - ] + ], ) def test_load_pem_rsa_private_key(self, key_path, password, backend): + _skip_fips_format(key_path, password, backend) data = load_vectors_from_file( os.path.join("asymmetric", *key_path), - lambda pemfile: pemfile.read(), mode="rb" + lambda pemfile: pemfile.read(), + mode="rb", ) key = load_pem_private_key(bytearray(data), password, backend) assert key @@ -93,7 +127,7 @@ class TestDERSerialization(object): (["DER_Serialization", "enc2-rsa-pkcs8.der"], b"baz"), (["DER_Serialization", "unenc-rsa-pkcs8.der"], None), (["DER_Serialization", "testrsa.der"], None), - ] + ], ) def test_load_der_rsa_private_key(self, key_path, password, backend): key = load_vectors_from_file( @@ -101,7 +135,7 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) assert key assert isinstance(key, rsa.RSAPrivateKey) @@ -115,7 +149,7 @@ class TestDERSerialization(object): (["DER_Serialization", "dsa.1024.der"], None), (["DER_Serialization", "dsa.2048.der"], None), (["DER_Serialization", "dsa.3072.der"], None), - ] + ], ) def test_load_der_dsa_private_key(self, key_path, password, backend): key = load_vectors_from_file( @@ -123,17 +157,14 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) assert key assert isinstance(key, dsa.DSAPrivateKey) _check_dsa_private_numbers(key.private_numbers()) @pytest.mark.parametrize( - "key_path", - [ - ["DER_Serialization", "enc-rsa-pkcs8.der"], - ] + "key_path", [["DER_Serialization", "enc-rsa-pkcs8.der"]] ) @pytest.mark.requires_backend_interface(interface=RSABackend) def test_password_not_bytes(self, key_path, backend): @@ -146,7 +177,7 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) @pytest.mark.parametrize( @@ -154,7 +185,7 @@ class TestDERSerialization(object): [ (["DER_Serialization", "ec_private_key.der"], None), (["DER_Serialization", "ec_private_key_encrypted.der"], b"123456"), - ] + ], ) @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) def test_load_der_ec_private_key(self, key_path, password, backend): @@ -164,7 +195,7 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) assert key @@ -173,10 +204,7 @@ class TestDERSerialization(object): assert key.curve.key_size == 256 @pytest.mark.parametrize( - "key_path", - [ - ["DER_Serialization", "enc-rsa-pkcs8.der"], - ] + "key_path", [["DER_Serialization", "enc-rsa-pkcs8.der"]] ) @pytest.mark.requires_backend_interface(interface=RSABackend) def test_wrong_password(self, key_path, backend): @@ -189,14 +217,11 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) @pytest.mark.parametrize( - "key_path", - [ - ["DER_Serialization", "unenc-rsa-pkcs8.der"] - ] + "key_path", [["DER_Serialization", "unenc-rsa-pkcs8.der"]] ) @pytest.mark.requires_backend_interface(interface=RSABackend) def test_unused_password(self, key_path, backend): @@ -209,17 +234,14 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) @pytest.mark.parametrize( ("key_path", "password"), itertools.product( - [ - ["DER_Serialization", "enc-rsa-pkcs8.der"], - ], - [b"", None] - ) + [["DER_Serialization", "enc-rsa-pkcs8.der"]], [b"", None] + ), ) @pytest.mark.requires_backend_interface(interface=RSABackend) def test_missing_password(self, key_path, password, backend): @@ -231,16 +253,14 @@ class TestDERSerialization(object): lambda derfile: load_der_private_key( derfile.read(), password, backend ), - mode="rb" + mode="rb", ) def test_wrong_format(self, backend): key_data = b"---- NOT A KEY ----\n" with pytest.raises(ValueError): - load_der_private_key( - key_data, None, backend - ) + load_der_private_key(key_data, None, backend) with pytest.raises(ValueError): load_der_private_key( @@ -249,7 +269,8 @@ class TestDERSerialization(object): def test_corrupt_der_pkcs8(self, backend): # unenc-rsa-pkcs8 with a bunch of data missing. - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ MIICdQIBADALBgkqhkiG9w0BAQEEggJhMIICXQIBAAKBgQC7JHoJfg6yNzLMOWet 8Z49a4KD0dCspMAYvo2YAMB7/wdEycocujbhJ2n/seONi+5XqTqqFkM5VBl8rmkk FPZk/7x0xmdsTPECSWnHK+HhoaNDFPR3j8jQhVo1laxiqcEhAHegi5cwtFosuJAv @@ -263,13 +284,12 @@ class TestDERSerialization(object): z+KOpdpPRR5TQmbEMEspjsFpFymMiuYPgmihQbO2cJl1qScY5OkCQQCJ6m5tcN8l Xxg/SNpjEIv+qAyUD96XVlOJlOIeLHQ8kYE0C6ZA+MsqYIzgAreJk88Yn0lU/X0/ mu/UpE/BRZmR - """).encode() + """ + ).encode() bad_der = base64.b64decode(b"".join(key_data.splitlines())) with pytest.raises(ValueError): - load_der_private_key( - bad_der, None, backend - ) + load_der_private_key(bad_der, None, backend) with pytest.raises(ValueError): load_der_private_key( @@ -278,14 +298,16 @@ class TestDERSerialization(object): def test_corrupt_traditional_format_der(self, backend): # privkey with a bunch of data missing. - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ MIIBPAIBAAJBAKrbeqkuRk8VcRmWFmtP+LviMB3+6dizWW3DwaffznyHGAFwUJ/I Tv0XtbsCyl3QoyKGhrOAy3RvPK5M38iuXT0CAwEAAQJAZ3cnzaHXM/bxGaR5CR1R rD1qFBAVfoQFiOH9uPJgMaoAuoQEisPHVcZDKcOv4wEg6/TInAIXBnEigtqvRzuy mvcpHZwQJdmdHHkGKAs37Dfxi67HbkUCIQCeZGliHXFa071Fp06ZeWlR2ADonTZz rJBhdTe0v5pCeQIhAIZfkiGgGBX4cIuuckzEm43g9WMUjxP/0GlK39vIyihxAiEA mymehFRT0MvqW5xAKAx7Pgkt8HVKwVhc2LwGKHE0DZM= - """).encode() + """ + ).encode() bad_der = base64.b64decode(b"".join(key_data.splitlines())) with pytest.raises(ValueError): @@ -300,20 +322,20 @@ class TestDERSerialization(object): "key_file", [ os.path.join( - "asymmetric", "DER_Serialization", "unenc-rsa-pkcs8.pub.der"), + "asymmetric", "DER_Serialization", "unenc-rsa-pkcs8.pub.der" + ), os.path.join( - "asymmetric", "DER_Serialization", "rsa_public_key.der"), + "asymmetric", "DER_Serialization", "rsa_public_key.der" + ), os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.der"), - ] + ], ) @pytest.mark.requires_backend_interface(interface=RSABackend) def test_load_der_rsa_public_key(self, key_file, backend): key = load_vectors_from_file( key_file, - lambda derfile: load_der_public_key( - derfile.read(), backend - ), - mode="rb" + lambda derfile: load_der_public_key(derfile.read(), backend), + mode="rb", ) assert key assert isinstance(key, rsa.RSAPublicKey) @@ -328,19 +350,19 @@ class TestDERSerialization(object): "key_file", [ os.path.join( - "asymmetric", "DER_Serialization", "unenc-dsa-pkcs8.pub.der"), + "asymmetric", "DER_Serialization", "unenc-dsa-pkcs8.pub.der" + ), os.path.join( - "asymmetric", "DER_Serialization", "dsa_public_key.der"), - ] + "asymmetric", "DER_Serialization", "dsa_public_key.der" + ), + ], ) @pytest.mark.requires_backend_interface(interface=DSABackend) def test_load_der_dsa_public_key(self, key_file, backend): key = load_vectors_from_file( key_file, - lambda derfile: load_der_public_key( - derfile.read(), backend - ), - mode="rb" + lambda derfile: load_der_public_key(derfile.read(), backend), + mode="rb", ) assert key assert isinstance(key, dsa.DSAPublicKey) @@ -350,12 +372,10 @@ class TestDERSerialization(object): _skip_curve_unsupported(backend, ec.SECP256R1()) key = load_vectors_from_file( os.path.join( - "asymmetric", "DER_Serialization", - "ec_public_key.der"), - lambda derfile: load_der_public_key( - derfile.read(), backend + "asymmetric", "DER_Serialization", "ec_public_key.der" ), - mode="rb" + lambda derfile: load_der_public_key(derfile.read(), backend), + mode="rb", ) assert key assert isinstance(key, ec.EllipticCurvePublicKey) @@ -366,9 +386,7 @@ class TestDERSerialization(object): param_data = b"---- NOT A KEY ----\n" with pytest.raises(ValueError): - load_der_parameters( - param_data, backend - ) + load_der_parameters(param_data, backend) @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) @@ -393,16 +411,19 @@ class TestPEMSerialization(object): (["Traditional_OpenSSL_Serialization", "key1.pem"], b"123456"), (["Traditional_OpenSSL_Serialization", "key2.pem"], b"a123456"), (["Traditional_OpenSSL_Serialization", "testrsa.pem"], None), - (["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"], - b"password"), - ] + ( + ["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"], + b"password", + ), + ], ) def test_load_pem_rsa_private_key(self, key_file, password, backend): + _skip_fips_format(key_file, password, backend) key = load_vectors_from_file( os.path.join("asymmetric", *key_file), lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) assert key @@ -417,14 +438,15 @@ class TestPEMSerialization(object): (["Traditional_OpenSSL_Serialization", "dsa.3072.pem"], None), (["PKCS8", "unenc-dsa-pkcs8.pem"], None), (["PEM_Serialization", "dsa_private_key.pem"], b"123456"), - ] + ], ) def test_load_dsa_private_key(self, key_path, password, backend): + _skip_fips_format(key_path, password, backend) key = load_vectors_from_file( os.path.join("asymmetric", *key_path), lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) assert key assert isinstance(key, dsa.DSAPrivateKey) @@ -437,16 +459,17 @@ class TestPEMSerialization(object): (["PKCS8", "ec_private_key_encrypted.pem"], b"123456"), (["PEM_Serialization", "ec_private_key.pem"], None), (["PEM_Serialization", "ec_private_key_encrypted.pem"], b"123456"), - ] + ], ) @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) def test_load_pem_ec_private_key(self, key_path, password, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) + _skip_fips_format(key_path, password, backend) key = load_vectors_from_file( os.path.join("asymmetric", *key_path), lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) assert key @@ -459,16 +482,17 @@ class TestPEMSerialization(object): [ os.path.join("asymmetric", "PKCS8", "unenc-rsa-pkcs8.pub.pem"), os.path.join( - "asymmetric", "PEM_Serialization", "rsa_public_key.pem"), + "asymmetric", "PEM_Serialization", "rsa_public_key.pem" + ), os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"), - ] + ], ) def test_load_pem_rsa_public_key(self, key_file, backend): key = load_vectors_from_file( key_file, lambda pemfile: load_pem_public_key( pemfile.read().encode(), backend - ) + ), ) assert key assert isinstance(key, rsa.RSAPublicKey) @@ -480,16 +504,16 @@ class TestPEMSerialization(object): [ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"), os.path.join( - "asymmetric", "PEM_Serialization", - "dsa_public_key.pem"), - ] + "asymmetric", "PEM_Serialization", "dsa_public_key.pem" + ), + ], ) def test_load_pem_dsa_public_key(self, key_file, backend): key = load_vectors_from_file( key_file, lambda pemfile: load_pem_public_key( pemfile.read().encode(), backend - ) + ), ) assert key assert isinstance(key, dsa.DSAPublicKey) @@ -499,66 +523,70 @@ class TestPEMSerialization(object): _skip_curve_unsupported(backend, ec.SECP256R1()) key = load_vectors_from_file( os.path.join( - "asymmetric", "PEM_Serialization", - "ec_public_key.pem"), + "asymmetric", "PEM_Serialization", "ec_public_key.pem" + ), lambda pemfile: load_pem_public_key( pemfile.read().encode(), backend - ) + ), ) assert key assert isinstance(key, ec.EllipticCurvePublicKey) assert key.curve.name == "secp256r1" assert key.curve.key_size == 256 + @pytest.mark.skip_fips( + reason="Traditional OpenSSL format blocked in FIPS mode" + ) def test_rsa_traditional_encrypted_values(self, backend): pkey = load_vectors_from_file( os.path.join( - "asymmetric", "Traditional_OpenSSL_Serialization", "key1.pem"), + "asymmetric", "Traditional_OpenSSL_Serialization", "key1.pem" + ), lambda pemfile: load_pem_private_key( pemfile.read().encode(), b"123456", backend - ) + ), ) assert pkey numbers = pkey.private_numbers() assert numbers.p == int( "fb7d316fc51531b36d93adaefaf52db6ad5beb793d37c4cf9dfc1ddd17cfbafb", - 16 + 16, ) assert numbers.q == int( "df98264e646de9a0fbeab094e31caad5bc7adceaaae3c800ca0275dd4bb307f5", - 16 + 16, ) assert numbers.d == int( "db4848c36f478dd5d38f35ae519643b6b810d404bcb76c00e44015e56ca1cab0" "7bb7ae91f6b4b43fcfc82a47d7ed55b8c575152116994c2ce5325ec24313b911", - 16 + 16, ) assert numbers.dmp1 == int( "ce997f967192c2bcc3853186f1559fd355c190c58ddc15cbf5de9b6df954c727", - 16 + 16, ) assert numbers.dmq1 == int( "b018a57ab20ffaa3862435445d863369b852cf70a67c55058213e3fe10e3848d", - 16 + 16, ) assert numbers.iqmp == int( "6a8d830616924f5cf2d1bc1973f97fde6b63e052222ac7be06aa2532d10bac76", - 16 + 16, ) assert numbers.public_numbers.e == 65537 assert numbers.public_numbers.n == int( "dba786074f2f0350ce1d99f5aed5b520cfe0deb5429ec8f2a88563763f566e77" "9814b7c310e5326edae31198eed439b845dd2db99eaa60f5c16a43f4be6bcf37", - 16 + 16, ) @pytest.mark.parametrize( "key_path", [ ["Traditional_OpenSSL_Serialization", "testrsa.pem"], - ["PKCS8", "unenc-rsa-pkcs8.pem"] - ] + ["PKCS8", "unenc-rsa-pkcs8.pem"], + ], ) def test_unused_password(self, key_path, backend): key_file = os.path.join("asymmetric", *key_path) @@ -569,15 +597,33 @@ class TestPEMSerialization(object): key_file, lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) + def test_invalid_encoding_with_traditional(self, backend): + key_file = os.path.join( + "asymmetric", "Traditional_OpenSSL_Serialization", "testrsa.pem" + ) + key = load_vectors_from_file( + key_file, + lambda pemfile: load_pem_private_key( + pemfile.read(), None, backend + ), + mode="rb", + ) + + for enc in (Encoding.OpenSSH, Encoding.Raw, Encoding.X962): + with pytest.raises(ValueError): + key.private_bytes( + enc, PrivateFormat.TraditionalOpenSSL, NoEncryption() + ) + @pytest.mark.parametrize( "key_path", [ ["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"], - ["PKCS8", "enc-rsa-pkcs8.pem"] - ] + ["PKCS8", "enc-rsa-pkcs8.pem"], + ], ) def test_password_not_bytes(self, key_path, backend): key_file = os.path.join("asymmetric", *key_path) @@ -588,15 +634,15 @@ class TestPEMSerialization(object): key_file, lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) @pytest.mark.parametrize( "key_path", [ ["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"], - ["PKCS8", "enc-rsa-pkcs8.pem"] - ] + ["PKCS8", "enc-rsa-pkcs8.pem"], + ], ) def test_wrong_password(self, key_path, backend): key_file = os.path.join("asymmetric", *key_path) @@ -607,19 +653,18 @@ class TestPEMSerialization(object): key_file, lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) @pytest.mark.parametrize( ("key_path", "password"), itertools.product( [ - ["Traditional_OpenSSL_Serialization", - "testrsa-encrypted.pem"], + ["Traditional_OpenSSL_Serialization", "testrsa-encrypted.pem"], ["PKCS8", "enc-rsa-pkcs8.pem"], ], - [b"", None] - ) + [b"", None], + ), ) def test_missing_password(self, key_path, password, backend): key_file = os.path.join("asymmetric", *key_path) @@ -629,16 +674,14 @@ class TestPEMSerialization(object): key_file, lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) def test_wrong_private_format(self, backend): key_data = b"---- NOT A KEY ----\n" with pytest.raises(ValueError): - load_pem_private_key( - key_data, None, backend - ) + load_pem_private_key(key_data, None, backend) with pytest.raises(ValueError): load_pem_private_key( @@ -659,7 +702,8 @@ class TestPEMSerialization(object): def test_corrupt_traditional_format(self, backend): # privkey.pem with a bunch of data missing. - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ -----BEGIN RSA PRIVATE KEY----- MIIBPAIBAAJBAKrbeqkuRk8VcRmWFmtP+LviMB3+6dizWW3DwaffznyHGAFwUJ/I Tv0XtbsCyl3QoyKGhrOAy3RvPK5M38iuXT0CAwEAAQJAZ3cnzaHXM/bxGaR5CR1R @@ -668,12 +712,11 @@ class TestPEMSerialization(object): rJBhdTe0v5pCeQIhAIZfkiGgGBX4cIuuckzEm43g9WMUjxP/0GlK39vIyihxAiEA mymehFRT0MvqW5xAKAx7Pgkt8HVKwVhc2LwGKHE0DZM= -----END RSA PRIVATE KEY----- - """).encode() + """ + ).encode() with pytest.raises(ValueError): - load_pem_private_key( - key_data, None, backend - ) + load_pem_private_key(key_data, None, backend) with pytest.raises(ValueError): load_pem_private_key( @@ -682,7 +725,8 @@ class TestPEMSerialization(object): def test_traditional_encrypted_corrupt_format(self, backend): # privkey.pem with a single bit flipped - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ -----BEGIN RSA PRIVATE KEY----- Proc-Type: <,ENCRYPTED DEK-Info: AES-128-CBC,5E22A2BD85A653FB7A3ED20DE84F54CD @@ -695,22 +739,20 @@ class TestPEMSerialization(object): 5A295jD9BQp9CY0nNFSsy+qiXWToq2xT3y5zVNEStmN0SCGNaIlUnJzL9IHW+oMI kPmXZMnAYBWeeCF1gf3J3aE5lZInegHNfEI0+J0LazC2aNU5Dg/BNqrmRqKWEIo/ -----END RSA PRIVATE KEY----- - """).encode() + """ + ).encode() password = b"this password is wrong" with pytest.raises(ValueError): - load_pem_private_key( - key_data, None, backend - ) + load_pem_private_key(key_data, None, backend) with pytest.raises(ValueError): - load_pem_private_key( - key_data, password, backend - ) + load_pem_private_key(key_data, password, backend) def test_unsupported_key_encryption(self, backend): - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: FAKE-123,5E22A2BD85A653FB7A3ED20DE84F54CD @@ -723,18 +765,18 @@ class TestPEMSerialization(object): 5A295jD9BQp9CY0nNFSsy+qiXWToq2xT3y5zVNEStmN0SCGNaIlUnJzL9IHW+oMI kPmXZMnAYBWeeCF1gf3J3aE5lZInegHNfEI0+J0LazC2aNU5Dg/BNqrmRqKWEIo/ -----END RSA PRIVATE KEY----- - """).encode() + """ + ).encode() password = b"password" - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): - load_pem_private_key( - key_data, password, backend - ) + with pytest.raises(ValueError): + load_pem_private_key(key_data, password, backend) def test_corrupt_pkcs8_format(self, backend): # unenc-rsa-pkcs8.pem with a bunch of data missing. - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ -----BEGIN PRIVATE KEY----- MIICdQIBADALBgkqhkiG9w0BAQEEggJhMIICXQIBAAKBgQC7JHoJfg6yNzLMOWet 8Z49a4KD0dCspMAYvo2YAMB7/wdEycocujbhJ2n/seONi+5XqTqqFkM5VBl8rmkk @@ -750,12 +792,11 @@ class TestPEMSerialization(object): Xxg/SNpjEIv+qAyUD96XVlOJlOIeLHQ8kYE0C6ZA+MsqYIzgAreJk88Yn0lU/X0/ mu/UpE/BRZmR -----END PRIVATE KEY----- - """).encode() + """ + ).encode() with pytest.raises(ValueError): - load_pem_private_key( - key_data, None, backend - ) + load_pem_private_key(key_data, None, backend) with pytest.raises(ValueError): load_pem_private_key( @@ -764,7 +805,8 @@ class TestPEMSerialization(object): def test_pks8_encrypted_corrupt_format(self, backend): # enc-rsa-pkcs8.pem with some bits flipped. - key_data = textwrap.dedent("""\ + key_data = textwrap.dedent( + """\ -----BEGIN ENCRYPTED PRIVATE KEY----- MIICojAcBgoqhkiG9w0BDAEDMA4ECHK0M0+QuEL9AgIBIcSCAoDRq+KRY+0XP0tO lwBTzViiXSXoyNnKAZKt5r5K/fGNntv22g/1s/ZNCetrqsJDC5eMUPPacz06jFq/ @@ -782,27 +824,23 @@ class TestPEMSerialization(object): 6JLgl8FrvdfjHwIvmSOO1YMNmILBq000Q8WDqyErBDs4hsvtO6VQ4LeqJj6gClX3 qeJNaJFu -----END ENCRYPTED PRIVATE KEY----- - """).encode() + """ + ).encode() password = b"this password is wrong" with pytest.raises(ValueError): - load_pem_private_key( - key_data, None, backend - ) + load_pem_private_key(key_data, None, backend) with pytest.raises(ValueError): - load_pem_private_key( - key_data, password, backend - ) + load_pem_private_key(key_data, password, backend) def test_rsa_pkcs8_encrypted_values(self, backend): pkey = load_vectors_from_file( - os.path.join( - "asymmetric", "PKCS8", "enc-rsa-pkcs8.pem"), + os.path.join("asymmetric", "PKCS8", "enc-rsa-pkcs8.pem"), lambda pemfile: load_pem_private_key( pemfile.read().encode(), b"foobar", backend - ) + ), ) assert pkey @@ -813,7 +851,8 @@ class TestPEMSerialization(object): "376a7fe5b19f95b35ca358ea5c8abd7ae051d49cd2f1e45969a1ae945460" "3c14b278664a0e414ebc8913acb6203626985525e17a600611b028542dd0" "562aad787fb4f1650aa318cdcff751e1b187cbf6785fbe164e9809491b95" - "dd68480567c99b1a57", 16 + "dd68480567c99b1a57", + 16, ) assert numbers.public_numbers.e == 65537 @@ -823,37 +862,43 @@ class TestPEMSerialization(object): "f3d9785c3a2c09e4c8090909fb3721e19a3009ec21221523a729265707a5" "8f13063671c42a4096cad378ef2510cb59e23071489d8893ac4934dd149f" "34f2d094bea57f1c8027c3a77248ac9b91218737d0c3c3dfa7d7829e6977" - "cf7d995688c86c81", 16 + "cf7d995688c86c81", + 16, ) assert numbers.p == int( "00db122ac857b2c0437d7616daa98e597bb75ca9ad3a47a70bec10c10036" "03328794b225c8e3eee6ffd3fd6d2253d28e071fe27d629ab072faa14377" - "ce6118cb67", 16 + "ce6118cb67", + 16, ) assert numbers.q == int( "00df1b8aa8506fcbbbb9d00257f2975e38b33d2698fd0f37e82d7ef38c56" "f21b6ced63c825383782a7115cfcc093300987dbd2853b518d1c8f26382a" - "2d2586d391", 16 + "2d2586d391", + 16, ) assert numbers.dmp1 == int( "00be18aca13e60712fdf5daa85421eb10d86d654b269e1255656194fb0c4" "2dd01a1070ea12c19f5c39e09587af02f7b1a1030d016a9ffabf3b36d699" - "ceaf38d9bf", 16 + "ceaf38d9bf", + 16, ) assert numbers.dmq1 == int( "71aa8978f90a0c050744b77cf1263725b203ac9f730606d8ae1d289dce4a" "28b8d534e9ea347aeb808c73107e583eb80c546d2bddadcdb3c82693a4c1" - "3d863451", 16 + "3d863451", + 16, ) assert numbers.iqmp == int( "136b7b1afac6e6279f71b24217b7083485a5e827d156024609dae39d48a6" "bdb55af2f062cc4a3b077434e6fffad5faa29a2b5dba2bed3e4621e478c0" - "97ccfe7f", 16 + "97ccfe7f", + 16, ) def test_load_pem_dsa_private_key(self, backend): @@ -861,7 +906,7 @@ class TestPEMSerialization(object): os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"), lambda pemfile: load_pem_private_key( pemfile.read().encode(), None, backend - ) + ), ) assert key assert isinstance(key, dsa.DSAPrivateKey) @@ -872,15 +917,14 @@ class TestPEMSerialization(object): num = key.private_numbers() pub = num.public_numbers parameter_numbers = pub.parameter_numbers - assert num.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203", - 16) + assert num.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203", 16) assert pub.y == int( "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1" "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d" "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350" "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6" "ab6bcce04bfdf5b6", - 16 + 16, ) assert parameter_numbers.p == int( @@ -889,11 +933,12 @@ class TestPEMSerialization(object): "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb" "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05" "940cabe5d2de49a167", - 16 + 16, ) assert parameter_numbers.q == int( - "00adc0e869b36f0ac013a681fdf4d4899d69820451", 16) + "00adc0e869b36f0ac013a681fdf4d4899d69820451", 16 + ) assert parameter_numbers.g == int( "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e" @@ -901,69 +946,61 @@ class TestPEMSerialization(object): "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10" "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc" "68ce5cfff241cd3246", - 16 + 16, ) @pytest.mark.parametrize( - ("key_file", "password"), - [ - ("bad-oid-dsa-key.pem", None), - ] + ("key_file", "password"), [("bad-oid-dsa-key.pem", None)] ) def test_load_bad_oid_key(self, key_file, password, backend): with pytest.raises(ValueError): load_vectors_from_file( - os.path.join( - "asymmetric", "PKCS8", key_file), + os.path.join("asymmetric", "PKCS8", key_file), lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) @pytest.mark.parametrize( - ("key_file", "password"), - [ - ("bad-encryption-oid.pem", b"password"), - ] + ("key_file", "password"), [("bad-encryption-oid.pem", b"password")] ) def test_load_bad_encryption_oid_key(self, key_file, password, backend): - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + with pytest.raises(ValueError): load_vectors_from_file( - os.path.join( - "asymmetric", "PKCS8", key_file), + os.path.join("asymmetric", "PKCS8", key_file), lambda pemfile: load_pem_private_key( pemfile.read().encode(), password, backend - ) + ), ) @pytest.mark.requires_backend_interface(interface=RSABackend) class TestRSASSHSerialization(object): def test_load_ssh_public_key_unsupported(self, backend): - ssh_key = b'ecdsa-sha2-junk AAAAE2VjZHNhLXNoYTItbmlzdHAyNTY=' + ssh_key = b"ecdsa-sha2-junk AAAAE2VjZHNhLXNoYTItbmlzdHAyNTY=" with pytest.raises(UnsupportedAlgorithm): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_bad_format(self, backend): - ssh_key = b'ssh-rsa not-a-real-key' + ssh_key = b"ssh-rsa not-a-real-key" with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_rsa_too_short(self, backend): - ssh_key = b'ssh-rsa' + ssh_key = b"ssh-rsa" with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) def test_load_ssh_public_key_truncated_int(self, backend): - ssh_key = b'ssh-rsa AAAAB3NzaC1yc2EAAAA=' + ssh_key = b"ssh-rsa AAAAB3NzaC1yc2EAAAA=" with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) - ssh_key = b'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAACKr+IHXo' + ssh_key = b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAACKr+IHXo" with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) @@ -1032,16 +1069,18 @@ class TestRSASSHSerialization(object): expected_e = 0x10001 expected_n = int( - '00C3BBF5D13F59322BA0A0B77EA0B6CF570241628AE24B5BA454D' - '23DCA295652B3523B67752653DFFD69587FAD9578DD6406F23691' - 'EA491C3F8B2D391D0312D9653C303B651067ADF887A5241843CEF' - '8019680A088E092FEC305FB04EA070340BB9BD0F1635B2AD84142' - '61B4E2D010ABD8FC6D2FB768912F78EE6B05A60857532B75B75EF' - 'C007601A4EF58BA947B7E75E38F3443CDD87E7C138A1DAD9D9FB3' - '19FF69DA43A9F6F6B0CD243F042CD1A5AFAEB286BD46AEB2D922B' - 'D01385D6892167074A0907F94A2BF08A54ABB2FFFFC89920861D0' - '46F8706AB88DDADBD9E8204D48B87789081E074024C8996783B31' - '7076A98ABF0A2D8550EAF2097D8CCC7BE76EF', 16) + "00C3BBF5D13F59322BA0A0B77EA0B6CF570241628AE24B5BA454D" + "23DCA295652B3523B67752653DFFD69587FAD9578DD6406F23691" + "EA491C3F8B2D391D0312D9653C303B651067ADF887A5241843CEF" + "8019680A088E092FEC305FB04EA070340BB9BD0F1635B2AD84142" + "61B4E2D010ABD8FC6D2FB768912F78EE6B05A60857532B75B75EF" + "C007601A4EF58BA947B7E75E38F3443CDD87E7C138A1DAD9D9FB3" + "19FF69DA43A9F6F6B0CD243F042CD1A5AFAEB286BD46AEB2D922B" + "D01385D6892167074A0907F94A2BF08A54ABB2FFFFC89920861D0" + "46F8706AB88DDADBD9E8204D48B87789081E074024C8996783B31" + "7076A98ABF0A2D8550EAF2097D8CCC7BE76EF", + 16, + ) expected = rsa.RSAPublicNumbers(expected_e, expected_n) @@ -1051,7 +1090,7 @@ class TestRSASSHSerialization(object): @pytest.mark.requires_backend_interface(interface=DSABackend) class TestDSSSSHSerialization(object): def test_load_ssh_public_key_dss_too_short(self, backend): - ssh_key = b'ssh-dss' + ssh_key = b"ssh-dss" with pytest.raises(ValueError): load_ssh_public_key(ssh_key, backend) @@ -1134,14 +1173,16 @@ class TestDSSSSHSerialization(object): "96a7032c01cdd8485b5cbfb73a46bb04708f98a18bc88d4c7812b284da8f900" "6e473e89897f9bc9125c69bbfd8ef691c0e76c1c34e6c843b8fe240e6e5aeb3" "13486e5fa917ab1288ff1a6ebcf9dcdeed3c5fc88474e30476f53a5ec816ef6" - "9f4", 16 + "9f4", + 16, ) expected_p = int( "b9b052d7f07630148d4d838b17790ef4f43437238ebebd5032ea483fd7b7902" "5ec3dc65ebd563ab586a633b4344f6acd10af31353bcf29111fa5e3b8d5c1e8" "7befe3c65f9b8be69c740716698c8366c8ef925b9cec1dcd69e73d926b554e2" "b4b6ddd1453eab39ba0f846e1555adcc33c5a8637128c9ed61104a45505a748" - "f6db", 16 + "f6db", + 16, ) expected_q = 1230879958723280233885494314531920096931919647917 expected_g = int( @@ -1149,11 +1190,12 @@ class TestDSSSSHSerialization(object): "7bc249b6cf8f5f5c5022afefd4df5bf9d13bbdf182df5af2a5c5d1dc7604185" "7d5b0e4b22b856c300f850a3b00bac394b728755b8b7a56522eefc491573967" "debb5982fc94d6a8c291f758feae63ad769a5621947221522a2dc31d18ede6f" - "b656", 16 + "b656", + 16, ) expected = dsa.DSAPublicNumbers( expected_y, - dsa.DSAParameterNumbers(expected_p, expected_q, expected_g) + dsa.DSAParameterNumbers(expected_p, expected_q, expected_g), ) assert numbers == expected @@ -1174,17 +1216,32 @@ class TestECDSASSHSerialization(object): expected_x = int( "44196257377740326295529888716212621920056478823906609851236662550" - "785814128027", 10 + "785814128027", + 10, ) expected_y = int( "12257763433170736656417248739355923610241609728032203358057767672" - "925775019611", 10 + "925775019611", + 10, ) assert key.public_numbers() == ec.EllipticCurvePublicNumbers( expected_x, expected_y, ec.SECP256R1() ) + def test_load_ssh_public_key_byteslike(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + + ssh_key = ( + b"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy" + b"NTYAAABBBGG2MfkHXp0UkxUyllDzWNBAImsvt5t7pFtTXegZK2WbGxml8zMrgWi5" + b"teIg1TO03/FD9hbpBFgBeix3NrCFPls= root@cloud-server-01" + ) + assert load_ssh_public_key(bytearray(ssh_key), backend) + if six.PY3: + assert load_ssh_public_key(memoryview(ssh_key), backend) + assert load_ssh_public_key(memoryview(bytearray(ssh_key)), backend) + def test_load_ssh_public_key_ecdsa_nist_p384(self, backend): _skip_curve_unsupported(backend, ec.SECP384R1()) ssh_key = ( @@ -1197,11 +1254,13 @@ class TestECDSASSHSerialization(object): expected_x = int( "31541830871345183397582554827482786756220448716666815789487537666" - "592636882822352575507883817901562613492450642523901", 10 + "592636882822352575507883817901562613492450642523901", + 10, ) expected_y = int( "15111413269431823234030344298767984698884955023183354737123929430" - "995703524272335782455051101616329050844273733614670", 10 + "995703524272335782455051101616329050844273733614670", + 10, ) assert key.public_numbers() == ec.EllipticCurvePublicNumbers( @@ -1222,12 +1281,14 @@ class TestECDSASSHSerialization(object): expected_x = int( "54124123120178189598842622575230904027376313369742467279346415219" "77809037378785192537810367028427387173980786968395921877911964629" - "142163122798974160187785455", 10 + "142163122798974160187785455", + 10, ) expected_y = int( "16111775122845033200938694062381820957441843014849125660011303579" "15284560361402515564433711416776946492019498546572162801954089916" - "006665939539407104638103918", 10 + "006665939539407104638103918", + 10, ) assert key.public_numbers() == ec.EllipticCurvePublicNumbers( @@ -1274,6 +1335,51 @@ class TestECDSASSHSerialization(object): load_ssh_public_key(ssh_key, backend) +@pytest.mark.supported( + only_if=lambda backend: backend.ed25519_supported(), + skip_message="Requires OpenSSL with Ed25519 support", +) +class TestEd25519SSHSerialization(object): + def test_load_ssh_public_key(self, backend): + ssh_key = ( + b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRaN/E4" + b"GR+xWvBmvxjxrB1vG user@chiron.local" + ) + key = load_ssh_public_key(ssh_key, backend) + assert isinstance(key, ed25519.Ed25519PublicKey) + assert key.public_bytes(Encoding.Raw, PublicFormat.Raw) == ( + b"m\x9f\x82\x99\xa9`\xee\xb5\xa9\xe01\x19\xdd0\x81\x16\x8d\xfc" + b"N\x06G\xecV\xbc\x19\xaf\xc6<k\x07[\xc6" + ) + + def test_public_bytes_openssh(self, backend): + ssh_key = ( + b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRaN/E4" + b"GR+xWvBmvxjxrB1vG" + ) + key = load_ssh_public_key(ssh_key, backend) + assert isinstance(key, ed25519.Ed25519PublicKey) + assert ( + key.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH) == ssh_key + ) + + def test_load_ssh_public_key_not_32_bytes(self, backend): + ssh_key = ( + b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI22fgpmpYO61qeAxGd0wgRaN/E4" + b"GR+xWvBmvxjxrB1vGaGVs user@chiron.local" + ) + with pytest.raises(ValueError): + load_ssh_public_key(ssh_key, backend) + + def test_load_ssh_public_key_trailing_data(self, backend): + ssh_key = ( + b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2fgpmpYO61qeAxGd0wgRa" + b"N/E4GR+xWvBmvxjxrB1vGdHJhaWxpbmdkYXRh user@chiron.local" + ) + with pytest.raises(ValueError): + load_ssh_public_key(ssh_key, backend) + + class TestKeySerializationEncryptionTypes(object): def test_non_bytes_password(self): with pytest.raises(ValueError): @@ -1285,128 +1391,959 @@ class TestKeySerializationEncryptionTypes(object): @pytest.mark.supported( - only_if=lambda backend: backend.x448_supported(), - skip_message="Requires OpenSSL with X448 support" + only_if=lambda backend: backend.ed25519_supported(), + skip_message="Requires OpenSSL with Ed25519 support", ) -class TestX448Serialization(object): +class TestEd25519Serialization(object): def test_load_der_private_key(self, backend): data = load_vectors_from_file( - os.path.join("asymmetric", "X448", "x448-pkcs8-enc.der"), + os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8-enc.der"), lambda derfile: derfile.read(), - mode="rb" + mode="rb", ) unencrypted = load_vectors_from_file( - os.path.join("asymmetric", "X448", "x448-pkcs8.der"), + os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8.der"), lambda derfile: derfile.read(), - mode="rb" + mode="rb", ) key = load_der_private_key(data, b"password", backend) - assert key.private_bytes( - Encoding.DER, PrivateFormat.PKCS8, NoEncryption() - ) == unencrypted + assert ( + key.private_bytes( + Encoding.DER, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) def test_load_pem_private_key(self, backend): data = load_vectors_from_file( - os.path.join("asymmetric", "X448", "x448-pkcs8-enc.pem"), + os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8-enc.pem"), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) unencrypted = load_vectors_from_file( - os.path.join("asymmetric", "X448", "x448-pkcs8.pem"), + os.path.join("asymmetric", "Ed25519", "ed25519-pkcs8.pem"), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) key = load_pem_private_key(data, b"password", backend) - assert key.private_bytes( - Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() - ) == unencrypted + assert ( + key.private_bytes( + Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) @pytest.mark.parametrize( ("key_path", "encoding", "loader"), [ ( - ["X448", "x448-pub.pem"], + ["Ed25519", "ed25519-pub.pem"], Encoding.PEM, - load_pem_public_key + load_pem_public_key, ), ( - ["X448", "x448-pub.der"], + ["Ed25519", "ed25519-pub.der"], Encoding.DER, - load_der_public_key + load_der_public_key, ), - ] + ], ) def test_load_public_key(self, key_path, encoding, loader, backend): data = load_vectors_from_file( os.path.join("asymmetric", *key_path), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) public_key = loader(data, backend) - assert public_key.public_bytes( - encoding, PublicFormat.SubjectPublicKeyInfo - ) == data + assert ( + public_key.public_bytes( + encoding, PublicFormat.SubjectPublicKeyInfo + ) + == data + ) + + def test_openssl_serialization_unsupported(self, backend): + key = ed25519.Ed25519PrivateKey.generate() + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.DER, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.x448_supported(), + skip_message="Requires OpenSSL with X448 support", +) +class TestX448Serialization(object): + def test_load_der_private_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "X448", "x448-pkcs8-enc.der"), + lambda derfile: derfile.read(), + mode="rb", + ) + unencrypted = load_vectors_from_file( + os.path.join("asymmetric", "X448", "x448-pkcs8.der"), + lambda derfile: derfile.read(), + mode="rb", + ) + key = load_der_private_key(data, b"password", backend) + assert ( + key.private_bytes( + Encoding.DER, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) + + def test_load_pem_private_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "X448", "x448-pkcs8-enc.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + unencrypted = load_vectors_from_file( + os.path.join("asymmetric", "X448", "x448-pkcs8.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + key = load_pem_private_key(data, b"password", backend) + assert ( + key.private_bytes( + Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) + + @pytest.mark.parametrize( + ("key_path", "encoding", "loader"), + [ + (["X448", "x448-pub.pem"], Encoding.PEM, load_pem_public_key), + (["X448", "x448-pub.der"], Encoding.DER, load_der_public_key), + ], + ) + def test_load_public_key(self, key_path, encoding, loader, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", *key_path), + lambda pemfile: pemfile.read(), + mode="rb", + ) + public_key = loader(data, backend) + assert ( + public_key.public_bytes( + encoding, PublicFormat.SubjectPublicKeyInfo + ) + == data + ) + + def test_openssl_serialization_unsupported(self, backend): + key = x448.X448PrivateKey.generate() + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.DER, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + + def test_openssh_serialization_unsupported(self, backend): + key = x448.X448PrivateKey.generate() + with pytest.raises(ValueError): + key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, NoEncryption() + ) @pytest.mark.supported( only_if=lambda backend: backend.x25519_supported(), - skip_message="Requires OpenSSL with X25519 support" + skip_message="Requires OpenSSL with X25519 support", ) class TestX25519Serialization(object): def test_load_der_private_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.der"), lambda derfile: derfile.read(), - mode="rb" + mode="rb", ) unencrypted = load_vectors_from_file( os.path.join("asymmetric", "X25519", "x25519-pkcs8.der"), lambda derfile: derfile.read(), - mode="rb" + mode="rb", ) key = load_der_private_key(data, b"password", backend) - assert key.private_bytes( - Encoding.DER, PrivateFormat.PKCS8, NoEncryption() - ) == unencrypted + assert ( + key.private_bytes( + Encoding.DER, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) def test_load_pem_private_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "X25519", "x25519-pkcs8-enc.pem"), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) unencrypted = load_vectors_from_file( os.path.join("asymmetric", "X25519", "x25519-pkcs8.pem"), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) key = load_pem_private_key(data, b"password", backend) - assert key.private_bytes( - Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() - ) == unencrypted + assert ( + key.private_bytes( + Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) @pytest.mark.parametrize( ("key_path", "encoding", "loader"), [ - ( - ["X25519", "x25519-pub.pem"], + (["X25519", "x25519-pub.pem"], Encoding.PEM, load_pem_public_key), + (["X25519", "x25519-pub.der"], Encoding.DER, load_der_public_key), + ], + ) + def test_load_public_key(self, key_path, encoding, loader, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", *key_path), + lambda pemfile: pemfile.read(), + mode="rb", + ) + public_key = loader(data, backend) + assert ( + public_key.public_bytes( + encoding, PublicFormat.SubjectPublicKeyInfo + ) + == data + ) + + def test_openssl_serialization_unsupported(self, backend): + key = x25519.X25519PrivateKey.generate() + with pytest.raises(ValueError): + key.private_bytes( Encoding.PEM, - load_pem_public_key - ), - ( - ["X25519", "x25519-pub.der"], + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + with pytest.raises(ValueError): + key.private_bytes( Encoding.DER, - load_der_public_key - ), - ] + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + + def test_openssh_serialization_unsupported(self, backend): + key = x25519.X25519PrivateKey.generate() + with pytest.raises(ValueError): + key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, NoEncryption() + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.ed448_supported(), + skip_message="Requires OpenSSL with Ed448 support", +) +class TestEd448Serialization(object): + def test_load_der_private_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "Ed448", "ed448-pkcs8-enc.der"), + lambda derfile: derfile.read(), + mode="rb", + ) + unencrypted = load_vectors_from_file( + os.path.join("asymmetric", "Ed448", "ed448-pkcs8.der"), + lambda derfile: derfile.read(), + mode="rb", + ) + key = load_der_private_key(data, b"password", backend) + assert ( + key.private_bytes( + Encoding.DER, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) + + def test_load_pem_private_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "Ed448", "ed448-pkcs8-enc.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + unencrypted = load_vectors_from_file( + os.path.join("asymmetric", "Ed448", "ed448-pkcs8.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + key = load_pem_private_key(data, b"password", backend) + assert ( + key.private_bytes( + Encoding.PEM, PrivateFormat.PKCS8, NoEncryption() + ) + == unencrypted + ) + + @pytest.mark.parametrize( + ("key_path", "encoding", "loader"), + [ + (["Ed448", "ed448-pub.pem"], Encoding.PEM, load_pem_public_key), + (["Ed448", "ed448-pub.der"], Encoding.DER, load_der_public_key), + ], ) def test_load_public_key(self, key_path, encoding, loader, backend): data = load_vectors_from_file( os.path.join("asymmetric", *key_path), lambda pemfile: pemfile.read(), - mode="rb" + mode="rb", ) public_key = loader(data, backend) - assert public_key.public_bytes( - encoding, PublicFormat.SubjectPublicKeyInfo - ) == data + assert ( + public_key.public_bytes( + encoding, PublicFormat.SubjectPublicKeyInfo + ) + == data + ) + + def test_openssl_serialization_unsupported(self, backend): + key = ed448.Ed448PrivateKey.generate() + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.DER, + PrivateFormat.TraditionalOpenSSL, + NoEncryption(), + ) + + def test_openssh_serialization_unsupported(self, backend): + key = ed448.Ed448PrivateKey.generate() + with pytest.raises(ValueError): + key.public_key().public_bytes( + Encoding.OpenSSH, + PublicFormat.OpenSSH, + ) + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, + PrivateFormat.OpenSSH, + NoEncryption(), + ) + + +class TestDHSerialization(object): + """Test all options with least-supported key type.""" + + @pytest.mark.skip_fips(reason="non-FIPS parameters") + def test_dh_public_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "DH", "dhkey.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + public_key = load_pem_private_key(data, None, backend).public_key() + for enc in ( + Encoding.PEM, + Encoding.DER, + Encoding.OpenSSH, + Encoding.Raw, + Encoding.X962, + ): + for fmt in ( + PublicFormat.SubjectPublicKeyInfo, + PublicFormat.PKCS1, + PublicFormat.OpenSSH, + PublicFormat.Raw, + PublicFormat.CompressedPoint, + PublicFormat.UncompressedPoint, + ): + if ( + enc in (Encoding.PEM, Encoding.DER) + and fmt == PublicFormat.SubjectPublicKeyInfo + ): + # tested elsewhere + continue + with pytest.raises(ValueError): + public_key.public_bytes(enc, fmt) + + @pytest.mark.skip_fips(reason="non-FIPS parameters") + def test_dh_private_key(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "DH", "dhkey.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + private_key = load_pem_private_key(data, None, backend) + for enc in ( + Encoding.PEM, + Encoding.DER, + Encoding.OpenSSH, + Encoding.Raw, + Encoding.X962, + ): + for fmt in ( + PrivateFormat.PKCS8, + PrivateFormat.TraditionalOpenSSL, + PrivateFormat.Raw, + ): + if ( + enc in (Encoding.PEM, Encoding.DER) + and fmt is PrivateFormat.PKCS8 + ): + # tested elsewhere + continue + with pytest.raises(ValueError): + private_key.private_bytes(enc, fmt, NoEncryption()) + + +@pytest.mark.requires_backend_interface(interface=RSABackend) +@pytest.mark.requires_backend_interface(interface=DSABackend) +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +class TestOpenSSHSerialization(object): + @pytest.mark.parametrize( + ("key_file", "cert_file"), + [ + ("rsa-psw.key.pub", None), + ("rsa-nopsw.key.pub", "rsa-nopsw.key-cert.pub"), + ("dsa-psw.key.pub", None), + ("dsa-nopsw.key.pub", "dsa-nopsw.key-cert.pub"), + ("ecdsa-psw.key.pub", None), + ("ecdsa-nopsw.key.pub", "ecdsa-nopsw.key-cert.pub"), + ("ed25519-psw.key.pub", None), + ("ed25519-nopsw.key.pub", "ed25519-nopsw.key-cert.pub"), + ], + ) + def test_load_ssh_public_key(self, key_file, cert_file, backend): + if "ed25519" in key_file and not backend.ed25519_supported(): + pytest.skip("Requires OpenSSL with Ed25519 support") + + # normal public key + pub_data = load_vectors_from_file( + os.path.join("asymmetric", "OpenSSH", key_file), + lambda f: f.read(), + mode="rb", + ) + public_key = load_ssh_public_key(pub_data, backend) + nocomment_data = b" ".join(pub_data.split()[:2]) + assert ( + public_key.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH) + == nocomment_data + ) + + self.run_partial_pubkey(pub_data, backend) + + # parse public key with ssh certificate + if cert_file: + cert_data = load_vectors_from_file( + os.path.join("asymmetric", "OpenSSH", cert_file), + lambda f: f.read(), + mode="rb", + ) + cert_key = load_ssh_public_key(cert_data, backend) + assert ( + cert_key.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH) + == nocomment_data + ) + + # try with more spaces + cert_data = b" \t ".join(cert_data.split()) + cert_key = load_ssh_public_key(cert_data, backend) + assert ( + cert_key.public_bytes(Encoding.OpenSSH, PublicFormat.OpenSSH) + == nocomment_data + ) + + self.run_partial_pubkey(cert_data, backend) + + def run_partial_pubkey(self, pubdata, backend): + parts = pubdata.split() + raw = base64.b64decode(parts[1]) + for i in range(1, len(raw)): + frag = base64.b64encode(raw[:i]) + new_pub = b" ".join([parts[0], frag]) + with pytest.raises(ValueError): + load_ssh_public_key(new_pub, backend) + + @pytest.mark.parametrize( + ("key_file",), + [ + ("rsa-nopsw.key",), + ("rsa-psw.key",), + ("dsa-nopsw.key",), + ("dsa-psw.key",), + ("ecdsa-nopsw.key",), + ("ecdsa-psw.key",), + ("ed25519-nopsw.key",), + ("ed25519-psw.key",), + ], + ) + def test_load_ssh_private_key(self, key_file, backend): + if "ed25519" in key_file and not backend.ed25519_supported(): + pytest.skip("Requires OpenSSL with Ed25519 support") + if "-psw" in key_file and not ssh._bcrypt_supported: + pytest.skip("Requires bcrypt module") + + # read public and private key from ssh-keygen + priv_data = load_vectors_from_file( + os.path.join("asymmetric", "OpenSSH", key_file), + lambda f: f.read(), + mode="rb", + ) + pub_data = load_vectors_from_file( + os.path.join("asymmetric", "OpenSSH", key_file + ".pub"), + lambda f: f.read(), + mode="rb", + ) + nocomment_data = b" ".join(pub_data.split()[:2]) + + # load and compare + password = None + if "-psw" in key_file: + password = b"password" + private_key = load_ssh_private_key(priv_data, password, backend) + assert ( + private_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + == nocomment_data + ) + + # bytearray + private_key = load_ssh_private_key( + bytearray(priv_data), password, backend + ) + assert ( + private_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + == nocomment_data + ) + + if six.PY3: + # memoryview(bytes) + private_key = load_ssh_private_key( + memoryview(priv_data), password, backend + ) + assert ( + private_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + == nocomment_data + ) + + # memoryview(bytearray) + private_key = load_ssh_private_key( + memoryview(bytearray(priv_data)), password, backend + ) + assert ( + private_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + == nocomment_data + ) + + # serialize with own code and reload + encryption = NoEncryption() + if password: + encryption = BestAvailableEncryption(password) + priv_data2 = private_key.private_bytes( + Encoding.PEM, + PrivateFormat.OpenSSH, + encryption, + ) + private_key2 = load_ssh_private_key(priv_data2, password, backend) + assert ( + private_key2.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + == nocomment_data + ) + + # make sure multi-line base64 is used + maxline = max(map(len, priv_data2.split(b"\n"))) + assert maxline < 80 + + @pytest.mark.supported( + only_if=lambda backend: ssh._bcrypt_supported, + skip_message="Requires that bcrypt exists", + ) + def test_bcrypt_encryption(self, backend): + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + pub1 = private_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + + for psw in ( + b"1", + b"1234", + b"1234" * 4, + b"x" * 72, + ): + # BestAvailableEncryption does not handle bytes-like? + best = BestAvailableEncryption(psw) + encdata = private_key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, best + ) + decoded_key = load_ssh_private_key(encdata, psw, backend) + pub2 = decoded_key.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + assert pub1 == pub2 + + # bytearray + decoded_key2 = load_ssh_private_key( + bytearray(encdata), psw, backend + ) + pub2 = decoded_key2.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + assert pub1 == pub2 + + if six.PY3: + # memoryview(bytes) + decoded_key2 = load_ssh_private_key( + memoryview(encdata), psw, backend + ) + pub2 = decoded_key2.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + assert pub1 == pub2 + + # memoryview(bytearray) + decoded_key2 = load_ssh_private_key( + memoryview(bytearray(encdata)), psw, backend + ) + pub2 = decoded_key2.public_key().public_bytes( + Encoding.OpenSSH, PublicFormat.OpenSSH + ) + assert pub1 == pub2 + + with pytest.raises(ValueError): + decoded_key = load_ssh_private_key(encdata, None, backend) + with pytest.raises(ValueError): + decoded_key = load_ssh_private_key(encdata, b"wrong", backend) + + @pytest.mark.supported( + only_if=lambda backend: not ssh._bcrypt_supported, + skip_message="Requires that bcrypt is missing", + ) + def test_missing_bcrypt(self, backend): + priv_data = load_vectors_from_file( + os.path.join("asymmetric", "OpenSSH", "ecdsa-psw.key"), + lambda f: f.read(), + mode="rb", + ) + with pytest.raises(UnsupportedAlgorithm): + load_ssh_private_key(priv_data, b"password", backend) + + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(UnsupportedAlgorithm): + private_key.private_bytes( + Encoding.PEM, + PrivateFormat.OpenSSH, + BestAvailableEncryption(b"x"), + ) + + def test_fraglist_corners(self): + f = ssh._FragList() + with pytest.raises(ValueError): + f.put_mpint(-1) + f.put_mpint(0) + f.put_mpint(0x80) + assert f.tobytes() == b"\0\0\0\0" + b"\0\0\0\x02" + b"\0\x80" + + def make_file( + self, + magic=b"openssh-key-v1\0", + ciphername=b"none", + kdfname=b"none", + kdfoptions=b"", + nkeys=1, + pub_type=b"ecdsa-sha2-nistp256", + pub_fields=( + b"nistp256", + b"\x04" * 65, + ), + priv_type=None, + priv_fields=(b"nistp256", b"\x04" * 65, b"\x7F" * 32), + comment=b"comment", + checkval1=b"1234", + checkval2=b"1234", + pad=None, + header=b"-----BEGIN OPENSSH PRIVATE KEY-----\n", + footer=b"-----END OPENSSH PRIVATE KEY-----\n", + cut=8192, + ): + """Create private key file""" + if not priv_type: + priv_type = pub_type + + pub = ssh._FragList() + for elem in (pub_type,) + pub_fields: + pub.put_sshstr(elem) + + secret = ssh._FragList([checkval1, checkval2]) + for i in range(nkeys): + for elem in (priv_type,) + priv_fields + (comment,): + secret.put_sshstr(elem) + + if pad is None: + pad_len = 8 - (secret.size() % 8) + pad = bytearray(range(1, 1 + pad_len)) + secret.put_raw(pad) + + main = ssh._FragList([magic]) + main.put_sshstr(ciphername) + main.put_sshstr(kdfname) + main.put_sshstr(kdfoptions) + main.put_u32(nkeys) + for i in range(nkeys): + main.put_sshstr(pub) + main.put_sshstr(secret) + + res = main.tobytes() + return ssh._ssh_pem_encode(res[:cut], header, footer) + + def test_ssh_make_file(self, backend): + # check if works by default + data = self.make_file() + key = load_ssh_private_key(data, None, backend) + assert isinstance(key, ec.EllipticCurvePrivateKey) + + def test_load_ssh_private_key_errors(self, backend): + # bad kdf + data = self.make_file(kdfname=b"unknown", ciphername=b"aes256-ctr") + with pytest.raises(UnsupportedAlgorithm): + load_ssh_private_key(data, None, backend) + + # bad cipher + data = self.make_file(ciphername=b"unknown", kdfname=b"bcrypt") + with pytest.raises(UnsupportedAlgorithm): + load_ssh_private_key(data, None, backend) + + # bad magic + data = self.make_file(magic=b"unknown") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # too few keys + data = self.make_file(nkeys=0) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # too many keys + data = self.make_file(nkeys=2) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + def test_ssh_errors_bad_values(self, backend): + # bad curve + data = self.make_file(pub_type=b"ecdsa-sha2-nistp444") + with pytest.raises(UnsupportedAlgorithm): + load_ssh_private_key(data, None, backend) + + # curve mismatch + data = self.make_file(priv_type=b"ecdsa-sha2-nistp384") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # invalid bigint + data = self.make_file( + priv_fields=(b"nistp256", b"\x04" * 65, b"\x80" * 32) + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + def test_ssh_errors_pubpriv_mismatch(self, backend): + # ecdsa public-private mismatch + data = self.make_file( + pub_fields=( + b"nistp256", + b"\x04" + b"\x05" * 64, + ) + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # rsa public-private mismatch + data = self.make_file( + pub_type=b"ssh-rsa", + pub_fields=(b"x" * 32,) * 2, + priv_fields=(b"z" * 32,) * 6, + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # dsa public-private mismatch + data = self.make_file( + pub_type=b"ssh-dss", + pub_fields=(b"x" * 32,) * 4, + priv_fields=(b"z" * 32,) * 5, + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # ed25519 public-private mismatch + sk = b"x" * 32 + pk1 = b"y" * 32 + pk2 = b"z" * 32 + data = self.make_file( + pub_type=b"ssh-ed25519", + pub_fields=(pk1,), + priv_fields=( + pk1, + sk + pk2, + ), + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + data = self.make_file( + pub_type=b"ssh-ed25519", + pub_fields=(pk1,), + priv_fields=( + pk2, + sk + pk1, + ), + ) + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + def test_ssh_errors_bad_wrapper(self, backend): + # wrong header + data = self.make_file(header=b"-----BEGIN RSA PRIVATE KEY-----\n") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # wring footer + data = self.make_file(footer=b"-----END RSA PRIVATE KEY-----\n") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + def test_ssh_no_padding(self, backend): + # no padding must work, if data is on block boundary + data = self.make_file(pad=b"", comment=b"") + key = load_ssh_private_key(data, None, backend) + assert isinstance(key, ec.EllipticCurvePrivateKey) + + # no padding with right last byte + data = self.make_file(pad=b"", comment=b"\x08" * 8) + key = load_ssh_private_key(data, None, backend) + assert isinstance(key, ec.EllipticCurvePrivateKey) + + # avoid unexpected padding removal + data = self.make_file(pad=b"", comment=b"1234\x01\x02\x03\x04") + key = load_ssh_private_key(data, None, backend) + assert isinstance(key, ec.EllipticCurvePrivateKey) + + # bad padding with right size + data = self.make_file(pad=b"\x08" * 8, comment=b"") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + def test_ssh_errors_bad_secrets(self, backend): + # checkval mismatch + data = self.make_file(checkval2=b"4321") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + # bad padding, correct=1 + data = self.make_file(pad=b"\x01\x02") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + data = self.make_file(pad=b"") + with pytest.raises(ValueError): + load_ssh_private_key(data, None, backend) + + @pytest.mark.supported( + only_if=lambda backend: backend.elliptic_curve_supported( + ec.SECP192R1() + ), + skip_message="Requires backend support for ec.SECP192R1", + ) + def test_serialize_ssh_private_key_errors_bad_curve(self, backend): + private_key = ec.generate_private_key(ec.SECP192R1(), backend) + with pytest.raises(ValueError): + private_key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, NoEncryption() + ) + + def test_serialize_ssh_private_key_errors(self, backend): + # bad encoding + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(ValueError): + private_key.private_bytes( + Encoding.DER, PrivateFormat.OpenSSH, NoEncryption() + ) + + # bad object type + with pytest.raises(ValueError): + ssh.serialize_ssh_private_key(object(), None) + + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + + # too long password + with pytest.raises(ValueError): + private_key.private_bytes( + Encoding.PEM, + PrivateFormat.OpenSSH, + BestAvailableEncryption(b"p" * 73), + ) + + # unknown encryption class + with pytest.raises(ValueError): + private_key.private_bytes( + Encoding.PEM, + PrivateFormat.OpenSSH, + DummyKeySerializationEncryption(), + ) + + @pytest.mark.parametrize( + ("key_path", "supported"), + [ + (["Traditional_OpenSSL_Serialization", "dsa.1024.pem"], True), + (["Traditional_OpenSSL_Serialization", "dsa.2048.pem"], False), + (["Traditional_OpenSSL_Serialization", "dsa.3072.pem"], False), + ], + ) + def test_dsa_private_key_sizes(self, key_path, supported, backend): + key = load_vectors_from_file( + os.path.join("asymmetric", *key_path), + lambda pemfile: load_pem_private_key( + pemfile.read(), None, backend + ), + mode="rb", + ) + assert isinstance(key, dsa.DSAPrivateKey) + if supported: + res = key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, NoEncryption() + ) + assert isinstance(res, bytes) + else: + with pytest.raises(ValueError): + key.private_bytes( + Encoding.PEM, PrivateFormat.OpenSSH, NoEncryption() + ) |