From dbcbffa06c9930a687010ca816596ca3f5cc78e9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 12 Jan 2019 21:18:21 -0800 Subject: support x448 public/private serialization both raw and pkcs8 (#4653) * support x448 public/private serialization both raw and pkcs8 * add tests for all other asym key types to prevent Raw * more tests * better tests * fix a test * funny story, I'm actually illiterate. * pep8 * require PrivateFormat.Raw or PublicFormat.Raw with Encoding.Raw * missing docs * parametrize * docs fixes * remove dupe line * assert something --- tests/hazmat/primitives/test_dh.py | 31 ++++++++ tests/hazmat/primitives/test_dsa.py | 29 +++++++ tests/hazmat/primitives/test_ec.py | 28 +++++++ tests/hazmat/primitives/test_rsa.py | 26 +++++++ tests/hazmat/primitives/test_serialization.py | 68 +++++++++++++++- tests/hazmat/primitives/test_x448.py | 107 ++++++++++++++++++++++++-- 6 files changed, 281 insertions(+), 8 deletions(-) (limited to 'tests/hazmat') diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index a70ae745..c63e520f 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -424,6 +424,20 @@ class TestDHPrivateKeySerialization(object): priv_num = key.private_numbers() assert loaded_priv_num == priv_num + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), + (serialization.Encoding.DER, serialization.PrivateFormat.Raw), + (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), + ] + ) + def test_private_bytes_rejects_raw(self, encoding, fmt, backend): + parameters = dh.generate_parameters(2, 512, backend) + key = parameters.generate_private_key() + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.parametrize( ("key_path", "loader_func", "encoding", "is_dhx"), [ @@ -806,6 +820,23 @@ class TestDHParameterSerialization(object): else: assert parameter_numbers.q is None + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PublicFormat.Raw), + (serialization.Encoding.PEM, serialization.PublicFormat.Raw), + ( + serialization.Encoding.Raw, + serialization.PublicFormat.SubjectPublicKeyInfo + ), + ] + ) + def test_public_bytes_rejects_raw(self, encoding, fmt, backend): + parameters = dh.generate_parameters(2, 512, backend) + key = parameters.generate_private_key().public_key() + with pytest.raises(ValueError): + key.public_bytes(encoding, fmt) + def test_parameter_bytes_invalid_encoding(self, backend): parameters = dh.generate_parameters(2, 512, backend) with pytest.raises(TypeError): diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index fb415732..5d2f1bd8 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -713,6 +713,19 @@ class TestDSASerialization(object): priv_num = key.private_numbers() assert loaded_priv_num == priv_num + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), + (serialization.Encoding.DER, serialization.PrivateFormat.Raw), + (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), + ] + ) + def test_private_bytes_rejects_raw(self, encoding, fmt, backend): + key = DSA_KEY_1024.private_key(backend) + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.parametrize( ("fmt", "password"), [ @@ -951,3 +964,19 @@ class TestDSAPEMPublicKeySerialization(object): key.public_bytes( serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 ) + + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PublicFormat.Raw), + (serialization.Encoding.PEM, serialization.PublicFormat.Raw), + ( + serialization.Encoding.Raw, + serialization.PublicFormat.SubjectPublicKeyInfo + ), + ] + ) + def test_public_bytes_rejects_raw(self, encoding, fmt, backend): + key = DSA_KEY_2048.private_key(backend).public_key() + with pytest.raises(ValueError): + key.public_bytes(encoding, fmt) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index f883d065..830d89a0 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -705,6 +705,20 @@ class TestECSerialization(object): priv_num = key.private_numbers() assert loaded_priv_num == priv_num + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), + (serialization.Encoding.DER, serialization.PrivateFormat.Raw), + (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), + ] + ) + def test_private_bytes_rejects_raw(self, encoding, fmt, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.parametrize( ("fmt", "password"), [ @@ -985,6 +999,20 @@ class TestEllipticCurvePEMPublicKeySerialization(object): serialization.PublicFormat.SubjectPublicKeyInfo ) + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PublicFormat.Raw), + (serialization.Encoding.PEM, serialization.PublicFormat.Raw), + (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1), + ] + ) + def test_public_bytes_rejects_raw(self, encoding, fmt, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + key = ec.generate_private_key(ec.SECP256R1(), backend).public_key() + with pytest.raises(ValueError): + key.public_bytes(encoding, fmt) + def test_public_bytes_invalid_format(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) key = load_vectors_from_file( diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 268ee9d9..0c25bdbb 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -2061,6 +2061,19 @@ class TestRSAPrivateKeySerialization(object): priv_num = key.private_numbers() assert loaded_priv_num == priv_num + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), + (serialization.Encoding.DER, serialization.PrivateFormat.Raw), + (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), + ] + ) + def test_private_bytes_rejects_raw(self, encoding, fmt, backend): + key = RSA_KEY_2048.private_key(backend) + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.parametrize( ("fmt", "password"), [ @@ -2286,3 +2299,16 @@ class TestRSAPEMPublicKeySerialization(object): key = RSA_KEY_2048.private_key(backend).public_key() with pytest.raises(TypeError): key.public_bytes(serialization.Encoding.PEM, "invalidformat") + + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + (serialization.Encoding.Raw, serialization.PublicFormat.Raw), + (serialization.Encoding.PEM, serialization.PublicFormat.Raw), + (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1), + ] + ) + def test_public_bytes_rejects_raw(self, encoding, fmt, backend): + key = RSA_KEY_2048.private_key(backend).public_key() + with pytest.raises(ValueError): + key.public_bytes(encoding, fmt) diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index a7355221..81d372fc 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -18,7 +18,9 @@ from cryptography.hazmat.backends.interfaces import ( ) from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.serialization import ( - BestAvailableEncryption, load_der_parameters, load_der_private_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_public_key ) @@ -1231,3 +1233,67 @@ class TestKeySerializationEncryptionTypes(object): def test_encryption_with_zero_length_password(self): with pytest.raises(ValueError): BestAvailableEncryption(b"") + + +@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 diff --git a/tests/hazmat/primitives/test_x448.py b/tests/hazmat/primitives/test_x448.py index 71b25341..1833b03d 100644 --- a/tests/hazmat/primitives/test_x448.py +++ b/tests/hazmat/primitives/test_x448.py @@ -11,6 +11,7 @@ import pytest from cryptography.exceptions import _Reasons from cryptography.hazmat.backends.interfaces import DHBackend +from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.x448 import ( X448PrivateKey, X448PublicKey ) @@ -50,7 +51,7 @@ class TestX448Exchange(object): private = binascii.unhexlify(vector["input_scalar"]) public = binascii.unhexlify(vector["input_u"]) shared_key = binascii.unhexlify(vector["output_u"]) - private_key = X448PrivateKey._from_private_bytes(private) + private_key = X448PrivateKey.from_private_bytes(private) public_key = X448PublicKey.from_public_bytes(public) computed_shared_key = private_key.exchange(public_key) assert computed_shared_key == shared_key @@ -64,11 +65,11 @@ class TestX448Exchange(object): b"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4" b"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38" ) - private_key = X448PrivateKey._from_private_bytes(private) + private_key = X448PrivateKey.from_private_bytes(private) public_key = X448PublicKey.from_public_bytes(public) for _ in range(1000): computed_shared_key = private_key.exchange(public_key) - private_key = X448PrivateKey._from_private_bytes( + private_key = X448PrivateKey.from_private_bytes( computed_shared_key ) public_key = X448PublicKey.from_public_bytes(old_private) @@ -103,11 +104,60 @@ class TestX448Exchange(object): ) ] ) - def test_public_bytes(self, private_bytes, public_bytes, backend): - private_key = X448PrivateKey._from_private_bytes(private_bytes) - assert private_key.public_key().public_bytes() == public_bytes + def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend): + private_key = X448PrivateKey.from_private_bytes(private_bytes) + assert private_key.private_bytes( + serialization.Encoding.Raw, + serialization.PrivateFormat.Raw, + serialization.NoEncryption() + ) == private_bytes + assert private_key.public_key().public_bytes( + serialization.Encoding.Raw, serialization.PublicFormat.Raw + ) == public_bytes public_key = X448PublicKey.from_public_bytes(public_bytes) - assert public_key.public_bytes() == public_bytes + assert public_key.public_bytes( + serialization.Encoding.Raw, serialization.PublicFormat.Raw + ) == public_bytes + + @pytest.mark.parametrize( + ("encoding", "fmt", "encryption", "passwd", "load_func"), + [ + ( + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.BestAvailableEncryption(b"password"), + b"password", + serialization.load_pem_private_key + ), + ( + serialization.Encoding.DER, + serialization.PrivateFormat.PKCS8, + serialization.BestAvailableEncryption(b"password"), + b"password", + serialization.load_der_private_key + ), + ( + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + None, + serialization.load_pem_private_key + ), + ( + serialization.Encoding.DER, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + None, + serialization.load_der_private_key + ), + ] + ) + def test_round_trip_private_serialization(self, encoding, fmt, encryption, + passwd, load_func, backend): + key = X448PrivateKey.generate() + serialized = key.private_bytes(encoding, fmt, encryption) + loaded_key = load_func(serialized, passwd, backend) + assert isinstance(loaded_key, X448PrivateKey) def test_generate(self, backend): key = X448PrivateKey.generate() @@ -125,3 +175,46 @@ class TestX448Exchange(object): with pytest.raises(ValueError): X448PublicKey.from_public_bytes(b"a" * 57) + + def test_invalid_private_bytes(self, backend): + key = X448PrivateKey.generate() + with pytest.raises(ValueError): + key.private_bytes( + serialization.Encoding.Raw, + serialization.PrivateFormat.Raw, + None + ) + + with pytest.raises(ValueError): + key.private_bytes( + serialization.Encoding.Raw, + serialization.PrivateFormat.PKCS8, + None + ) + + with pytest.raises(ValueError): + key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.Raw, + serialization.NoEncryption() + ) + + def test_invalid_public_bytes(self, backend): + key = X448PrivateKey.generate().public_key() + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.Raw, + serialization.PublicFormat.SubjectPublicKeyInfo + ) + + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.PEM, + serialization.PublicFormat.PKCS1 + ) + + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.PEM, + serialization.PublicFormat.Raw + ) -- cgit v1.2.3