diff options
Diffstat (limited to 'tests/hazmat/primitives/test_ec.py')
| -rw-r--r-- | tests/hazmat/primitives/test_ec.py | 734 |
1 files changed, 649 insertions, 85 deletions
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index cc185145..987c0ff0 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -4,24 +4,30 @@ from __future__ import absolute_import, division, print_function +import binascii import itertools import os +from binascii import hexlify import pytest -from cryptography import exceptions, utils +from cryptography import exceptions, utils, x509 from cryptography.hazmat.backends.interfaces import ( EllipticCurveBackend, PEMSerializationBackend ) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_rfc6979_signature + Prehashed, encode_dss_signature ) +from cryptography.utils import CryptographyDeprecationWarning +from .fixtures_ec import EC_KEY_SECP384R1 +from ...doubles import DummyKeySerializationEncryption from ...utils import ( load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors, - load_vectors_from_file, raises_unsupported_algorithm + load_kasvs_ecdh_vectors, load_nist_vectors, load_vectors_from_file, + raises_unsupported_algorithm ) _HASH_TYPES = { @@ -33,25 +39,13 @@ _HASH_TYPES = { } -def _skip_if_no_serialization(key, backend): - if not isinstance( - key, ( - ec.EllipticCurvePrivateKeyWithSerialization, - ec.EllipticCurvePublicKeyWithSerialization - ) - ): - pytest.skip( - "{0} does not support EC key serialization".format(backend) - ) - - def _skip_ecdsa_vector(backend, curve_type, hash_type): if not backend.elliptic_curve_signature_algorithm_supported( ec.ECDSA(hash_type()), curve_type() ): pytest.skip( - "ECDSA not supported with this hash {0} and curve {1}".format( + "ECDSA not supported with this hash {} and curve {}".format( hash_type().name, curve_type().name ) ) @@ -60,12 +54,29 @@ def _skip_ecdsa_vector(backend, curve_type, hash_type): def _skip_curve_unsupported(backend, curve): if not backend.elliptic_curve_supported(curve): pytest.skip( - "Curve {0} is not supported by this backend {1}".format( + "Curve {} is not supported by this backend {}".format( curve.name, backend ) ) +def _skip_exchange_algorithm_unsupported(backend, algorithm, curve): + if not backend.elliptic_curve_exchange_algorithm_supported( + algorithm, curve + ): + pytest.skip( + "Exchange with {} curve is not supported by {}".format( + curve.name, backend + ) + ) + + +def test_get_curve_for_oid(): + assert ec.get_curve_for_oid(ec.EllipticCurveOID.SECP256R1) == ec.SECP256R1 + with pytest.raises(LookupError): + ec.get_curve_for_oid(x509.ObjectIdentifier("1.1.1.1")) + + @utils.register_interface(ec.EllipticCurve) class DummyCurve(object): name = "dummy-curve" @@ -77,20 +88,51 @@ class DummySignatureAlgorithm(object): algorithm = None -@utils.register_interface(serialization.KeySerializationEncryption) -class DummyKeyEncryption(object): - pass - - @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) def test_skip_curve_unsupported(backend): with pytest.raises(pytest.skip.Exception): _skip_curve_unsupported(backend, DummyCurve()) -def test_skip_no_serialization(): +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_skip_exchange_algorithm_unsupported(backend): + with pytest.raises(pytest.skip.Exception): + _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), DummyCurve()) + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_skip_ecdsa_vector(backend): with pytest.raises(pytest.skip.Exception): - _skip_if_no_serialization("fakebackend", "fakekey") + _skip_ecdsa_vector(backend, DummyCurve, hashes.SHA256) + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_derive_private_key_success(backend): + curve = ec.SECP256K1() + _skip_curve_unsupported(backend, curve) + + private_numbers = ec.generate_private_key(curve, backend).private_numbers() + + derived_key = ec.derive_private_key( + private_numbers.private_value, curve, backend + ) + + assert private_numbers == derived_key.private_numbers() + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_derive_private_key_errors(backend): + curve = ec.SECP256K1() + _skip_curve_unsupported(backend, curve) + + with pytest.raises(TypeError): + ec.derive_private_key('one', curve, backend) + + with pytest.raises(TypeError): + ec.derive_private_key(10, 'five', backend) + + with pytest.raises(ValueError): + ec.derive_private_key(-7, curve, backend) def test_ec_numbers(): @@ -106,43 +148,135 @@ def test_ec_numbers(): assert numbers.public_numbers.y == 3 assert isinstance(numbers.public_numbers.curve, DummyCurve) + +@pytest.mark.parametrize( + ("private_value", "x", "y", "curve"), + [ + (None, 2, 3, DummyCurve()), + (1, None, 3, DummyCurve()), + (1, 2, None, DummyCurve()), + (1, 2, 3, None), + ] +) +def test_invalid_ec_numbers_args(private_value, x, y, curve): with pytest.raises(TypeError): ec.EllipticCurvePrivateNumbers( - None, - ec.EllipticCurvePublicNumbers( - 2, 3, DummyCurve() - ) + private_value, ec.EllipticCurvePublicNumbers(x, y, curve) ) + +def test_invalid_private_numbers_public_numbers(): with pytest.raises(TypeError): - ec.EllipticCurvePrivateNumbers( - 1, - ec.EllipticCurvePublicNumbers( - None, 3, DummyCurve() - ) + ec.EllipticCurvePrivateNumbers(1, None) + + +def test_encode_point(): + # secp256r1 point + x = int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', + 16 + ) + y = int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', + 16 + ) + pn = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()) + with pytest.warns(utils.PersistentlyDeprecated2019): + data = pn.encode_point() + assert data == binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + + +def test_from_encoded_point(): + # secp256r1 point + data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e" + ) + with pytest.warns(CryptographyDeprecationWarning): + pn = ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP256R1(), data ) + assert pn.x == int( + '233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22aec', + 16 + ) + assert pn.y == int( + '3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460b35f442e', + 16 + ) - with pytest.raises(TypeError): - ec.EllipticCurvePrivateNumbers( - 1, - ec.EllipticCurvePublicNumbers( - 2, None, DummyCurve() + +def test_from_encoded_point_invalid_length(): + bad_data = binascii.unhexlify( + "04233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22ae" + "c3ea2c10a84153862be4ec82940f0543f9ba866af9751a6ee79d38460" + ) + with pytest.raises(ValueError): + with pytest.warns(CryptographyDeprecationWarning): + ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP384R1(), bad_data ) - ) - with pytest.raises(TypeError): - ec.EllipticCurvePrivateNumbers( - 1, - ec.EllipticCurvePublicNumbers( - 2, 3, None + +def test_from_encoded_point_unsupported_point_no_backend(): + # set to point type 2. + unsupported_type = binascii.unhexlify( + "02233ea3b0027127084cd2cd336a13aeef69c598d8af61369a36454a17c6c22a" + ) + with pytest.raises(ValueError): + with pytest.warns(CryptographyDeprecationWarning): + ec.EllipticCurvePublicNumbers.from_encoded_point( + ec.SECP256R1(), unsupported_type ) - ) + +def test_from_encoded_point_not_a_curve(): with pytest.raises(TypeError): - ec.EllipticCurvePrivateNumbers( - 1, - None - ) + with pytest.warns(CryptographyDeprecationWarning): + ec.EllipticCurvePublicNumbers.from_encoded_point( + "notacurve", b"\x04data" + ) + + +def test_ec_public_numbers_repr(): + pn = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1()) + assert repr(pn) == "<EllipticCurvePublicNumbers(curve=secp256r1, x=2, y=3>" + + +def test_ec_public_numbers_hash(): + pn1 = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1()) + pn2 = ec.EllipticCurvePublicNumbers(2, 3, ec.SECP256R1()) + pn3 = ec.EllipticCurvePublicNumbers(1, 3, ec.SECP256R1()) + + assert hash(pn1) == hash(pn2) + assert hash(pn1) != hash(pn3) + + +def test_ec_private_numbers_hash(): + numbers1 = ec.EllipticCurvePrivateNumbers( + 1, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve()) + ) + numbers2 = ec.EllipticCurvePrivateNumbers( + 1, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve()) + ) + numbers3 = ec.EllipticCurvePrivateNumbers( + 2, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve()) + ) + + assert hash(numbers1) == hash(numbers2) + assert hash(numbers1) != hash(numbers3) + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_ec_key_key_size(backend): + curve = ec.SECP256R1() + _skip_curve_unsupported(backend, curve) + key = ec.generate_private_key(curve, backend) + assert key.key_size == 256 + assert key.public_key().key_size == 256 @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) @@ -173,12 +307,11 @@ class TestECWithNumbers(object): ).private_key(backend) assert key - if isinstance(key, ec.EllipticCurvePrivateKeyWithSerialization): - priv_num = key.private_numbers() - assert priv_num.private_value == vector['d'] - assert priv_num.public_numbers.x == vector['x'] - assert priv_num.public_numbers.y == vector['y'] - assert curve_type().name == priv_num.public_numbers.curve.name + priv_num = key.private_numbers() + assert priv_num.private_value == vector['d'] + assert priv_num.public_numbers.x == vector['x'] + assert priv_num.public_numbers.y == vector['y'] + assert curve_type().name == priv_num.public_numbers.curve.name @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) @@ -212,11 +345,13 @@ class TestECDSAVectors(object): pkey = key.public_key() assert pkey - signer = key.signer(ec.ECDSA(hash_type())) + with pytest.warns(CryptographyDeprecationWarning): + signer = key.signer(ec.ECDSA(hash_type())) signer.update(b"YELLOW SUBMARINE") signature = signer.finalize() - verifier = pkey.verifier(signature, ec.ECDSA(hash_type())) + with pytest.warns(CryptographyDeprecationWarning): + verifier = pkey.verifier(signature, ec.ECDSA(hash_type())) verifier.update(b"YELLOW SUBMARINE") verifier.verify() @@ -254,14 +389,26 @@ class TestECDSAVectors(object): with raises_unsupported_algorithm( exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM - ): + ), pytest.warns(CryptographyDeprecationWarning): key.signer(DummySignatureAlgorithm()) with raises_unsupported_algorithm( exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): + key.sign(b"somedata", DummySignatureAlgorithm()) + + with raises_unsupported_algorithm( + exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ), pytest.warns(CryptographyDeprecationWarning): key.public_key().verifier(b"", DummySignatureAlgorithm()) + with raises_unsupported_algorithm( + exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ): + key.public_key().verify( + b"signature", b"data", DummySignatureAlgorithm() + ) + assert backend.elliptic_curve_signature_algorithm_supported( DummySignatureAlgorithm(), ec.SECP192R1() @@ -303,6 +450,35 @@ class TestECDSAVectors(object): with pytest.raises(ValueError): numbers.private_key(backend) + def test_load_invalid_public_ec_key_from_numbers(self, backend): + _skip_curve_unsupported(backend, ec.SECP521R1()) + + # Bad X coordinate + numbers = ec.EllipticCurvePublicNumbers( + int("000003647356b91f8ace114c7247ecf4f4a622553fc025e04a178f179ef27" + "9090c184af678a4c78f635483bdd8aa544851c6ef291c1f0d6a241ebfd145" + "77d1d30d9903ce", 16), + int("000001499bc7e079322ea0fcfbd6b40103fa6a1536c2257b182db0df4b369" + "6ec643adf100eb4f2025d1b873f82e5a475d6e4400ba777090eeb4563a115" + "09e4c87319dc26", 16), + ec.SECP521R1() + ) + with pytest.raises(ValueError): + numbers.public_key(backend) + + # Bad Y coordinate + numbers = ec.EllipticCurvePublicNumbers( + int("0000019aadc221cc0525118ab6d5aa1f64720603de0be128cbfea0b381ad8" + "02a2facc6370bb58cf88b3f0c692bc654ee19d6cad198f10d4b681b396f20" + "d2e40603fa945b", 16), + int("0000025da392803a320717a08d4cb3dea932039badff363b71bdb8064e726" + "6c7f4f4b748d4d425347fc33e3885d34b750fa7fcd5691f4d90c89522ce33" + "feff5db10088a5", 16), + ec.SECP521R1() + ) + with pytest.raises(ValueError): + numbers.public_key(backend) + @pytest.mark.parametrize( "vector", itertools.chain( @@ -330,14 +506,13 @@ class TestECDSAVectors(object): curve_type() ).public_key(backend) - signature = encode_rfc6979_signature(vector['r'], vector['s']) + signature = encode_dss_signature(vector['r'], vector['s']) - verifier = key.verifier( + key.verify( signature, + vector['message'], ec.ECDSA(hash_type()) ) - verifier.update(vector['message']) - assert verifier.verify() @pytest.mark.parametrize( "vector", @@ -359,19 +534,107 @@ class TestECDSAVectors(object): curve_type() ).public_key(backend) - signature = encode_rfc6979_signature(vector['r'], vector['s']) - - verifier = key.verifier( - signature, - ec.ECDSA(hash_type()) - ) - verifier.update(vector['message']) + signature = encode_dss_signature(vector['r'], vector['s']) if vector["fail"] is True: with pytest.raises(exceptions.InvalidSignature): - verifier.verify() + key.verify( + signature, + vector['message'], + ec.ECDSA(hash_type()) + ) else: - verifier.verify() + key.verify( + signature, + vector['message'], + ec.ECDSA(hash_type()) + ) + + def test_sign(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + algorithm = ec.ECDSA(hashes.SHA1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signature = private_key.sign(message, algorithm) + public_key = private_key.public_key() + public_key.verify(signature, message, algorithm) + + def test_sign_prehashed(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + algorithm = ec.ECDSA(Prehashed(hashes.SHA1())) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signature = private_key.sign(data, algorithm) + public_key = private_key.public_key() + public_key.verify(signature, message, ec.ECDSA(hashes.SHA1())) + + def test_sign_prehashed_digest_mismatch(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + algorithm = ec.ECDSA(Prehashed(hashes.SHA256())) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(ValueError): + private_key.sign(data, algorithm) + + def test_verify(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + algorithm = ec.ECDSA(hashes.SHA1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signature = private_key.sign(message, algorithm) + public_key = private_key.public_key() + public_key.verify(signature, message, algorithm) + + def test_verify_prehashed(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + algorithm = ec.ECDSA(hashes.SHA1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signature = private_key.sign(message, algorithm) + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + public_key = private_key.public_key() + public_key.verify( + signature, data, ec.ECDSA(Prehashed(hashes.SHA1())) + ) + + def test_verify_prehashed_digest_mismatch(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + public_key = private_key.public_key() + with pytest.raises(ValueError): + public_key.verify( + b"\x00" * 32, data, ec.ECDSA(Prehashed(hashes.SHA256())) + ) + + def test_prehashed_unsupported_in_signer_ctx(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + private_key.signer(ec.ECDSA(Prehashed(hashes.SHA1()))) + + def test_prehashed_unsupported_in_verifier_ctx(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + public_key = private_key.public_key() + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + public_key.verifier( + b"0" * 64, + ec.ECDSA(Prehashed(hashes.SHA1())) + ) class TestECNumbersEquality(object): @@ -437,7 +700,6 @@ class TestECSerialization(object): lambda pemfile: pemfile.read().encode() ) key = serialization.load_pem_private_key(key_bytes, None, backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( serialization.Encoding.PEM, fmt, @@ -451,6 +713,21 @@ class TestECSerialization(object): 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), + (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8), + ] + ) + def test_private_bytes_rejects_invalid(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"), [ [serialization.PrivateFormat.PKCS8, b"s"], @@ -467,7 +744,6 @@ class TestECSerialization(object): lambda pemfile: pemfile.read().encode() ) key = serialization.load_pem_private_key(key_bytes, None, backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( serialization.Encoding.DER, fmt, @@ -514,7 +790,6 @@ class TestECSerialization(object): lambda pemfile: pemfile.read().encode() ) key = serialization.load_pem_private_key(key_bytes, None, backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( encoding, fmt, serialization.NoEncryption() ) @@ -566,7 +841,6 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(ValueError): key.private_bytes( serialization.Encoding.DER, @@ -583,7 +857,6 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( "notencoding", @@ -600,7 +873,6 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( serialization.Encoding.PEM, @@ -617,7 +889,6 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( serialization.Encoding.PEM, @@ -634,12 +905,11 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(ValueError): key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, - DummyKeyEncryption() + DummyKeySerializationEncryption() ) def test_public_bytes_from_derived_public_key(self, backend): @@ -651,7 +921,6 @@ class TestECSerialization(object): pemfile.read().encode(), None, backend ) ) - _skip_if_no_serialization(key, backend) public = key.public_key() pem = public.public_bytes( serialization.Encoding.PEM, @@ -689,12 +958,39 @@ class TestEllipticCurvePEMPublicKeySerialization(object): key_path, lambda pemfile: pemfile.read(), mode="rb" ) key = loader_func(key_bytes, backend) - _skip_if_no_serialization(key, backend) serialized = key.public_bytes( encoding, serialization.PublicFormat.SubjectPublicKeyInfo, ) assert serialized == key_bytes + def test_public_bytes_openssh(self, backend): + _skip_curve_unsupported(backend, ec.SECP192R1()) + _skip_curve_unsupported(backend, ec.SECP256R1()) + + key_bytes = load_vectors_from_file( + os.path.join( + "asymmetric", "PEM_Serialization", "ec_public_key.pem" + ), + lambda pemfile: pemfile.read(), mode="rb" + ) + key = serialization.load_pem_public_key(key_bytes, backend) + + ssh_bytes = key.public_bytes( + serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH + ) + assert ssh_bytes == ( + b"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy" + b"NTYAAABBBCS8827s9rUZyxZTi/um01+oIlWrwLHOjQxRU9CDAndom00zVAw5BRrI" + b"KtHB+SWD4P+sVJTARSq1mHt8kOIWrPc=" + ) + + key = ec.generate_private_key(ec.SECP192R1(), backend).public_key() + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.OpenSSH, + serialization.PublicFormat.OpenSSH + ) + def test_public_bytes_invalid_encoding(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) key = load_vectors_from_file( @@ -705,13 +1001,40 @@ class TestEllipticCurvePEMPublicKeySerialization(object): pemfile.read().encode(), backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.public_bytes( "notencoding", serialization.PublicFormat.SubjectPublicKeyInfo ) + @pytest.mark.parametrize( + ("encoding", "fmt"), + list(itertools.product( + [ + serialization.Encoding.Raw, + serialization.Encoding.X962, + serialization.Encoding.PEM, + serialization.Encoding.DER + ], + [ + serialization.PublicFormat.Raw, + ] + )) + list(itertools.product( + [serialization.Encoding.Raw], + [ + serialization.PublicFormat.SubjectPublicKeyInfo, + serialization.PublicFormat.PKCS1, + serialization.PublicFormat.UncompressedPoint, + serialization.PublicFormat.CompressedPoint, + ] + )) + ) + def test_public_bytes_rejects_invalid(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( @@ -722,7 +1045,6 @@ class TestEllipticCurvePEMPublicKeySerialization(object): pemfile.read().encode(), backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.public_bytes(serialization.Encoding.PEM, "invalidformat") @@ -736,8 +1058,250 @@ class TestEllipticCurvePEMPublicKeySerialization(object): pemfile.read().encode(), backend ) ) - _skip_if_no_serialization(key, backend) with pytest.raises(ValueError): key.public_bytes( serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 ) + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "EC", "compressed_points.txt"), + load_nist_vectors + ) + ) + def test_from_encoded_point_compressed(self, vector, backend): + curve = { + b"SECP256R1": ec.SECP256R1(), + b"SECP256K1": ec.SECP256K1(), + }[vector["curve"]] + _skip_curve_unsupported(backend, curve) + point = binascii.unhexlify(vector["point"]) + pn = ec.EllipticCurvePublicKey.from_encoded_point(curve, point) + public_num = pn.public_numbers() + assert public_num.x == int(vector["x"], 16) + assert public_num.y == int(vector["y"], 16) + + def test_from_encoded_point_notoncurve(self): + uncompressed_point = binascii.unhexlify( + "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac" + "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f" + "6e" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP256R1(), uncompressed_point + ) + + def test_from_encoded_point_uncompressed(self): + uncompressed_point = binascii.unhexlify( + "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac" + "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f" + "6d" + ) + pn = ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP256R1(), uncompressed_point + ) + assert pn.public_numbers().x == int( + '7399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac68', + 16 + ) + assert pn.public_numbers().y == int( + '6699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f6d', + 16 + ) + + def test_from_encoded_point_invalid_length(self): + bad_data = binascii.unhexlify( + "047399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac" + "686699ececc4f5f0d756d3c450708a0694eb0a07a68b805070b40b058d27271f" + "6d" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP384R1(), bad_data + ) + + def test_from_encoded_point_empty_byte_string(self): + with pytest.raises(ValueError): + ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP384R1(), b"" + ) + + def test_from_encoded_point_not_a_curve(self): + with pytest.raises(TypeError): + ec.EllipticCurvePublicKey.from_encoded_point( + "notacurve", b"\x04data" + ) + + def test_from_encoded_point_unsupported_encoding(self): + unsupported_type = binascii.unhexlify( + "057399336a9edf2197c2f8eb3d39aed9c34a66e45d918a07dc7684c42c9b37ac6" + "8" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicKey.from_encoded_point( + ec.SECP256R1(), unsupported_type + ) + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "EC", "compressed_points.txt"), + load_nist_vectors + ) + ) + def test_serialize_point(self, vector, backend): + curve = { + b"SECP256R1": ec.SECP256R1(), + b"SECP256K1": ec.SECP256K1(), + }[vector["curve"]] + _skip_curve_unsupported(backend, curve) + point = binascii.unhexlify(vector["point"]) + key = ec.EllipticCurvePublicKey.from_encoded_point(curve, point) + key2 = ec.EllipticCurvePublicKey.from_encoded_point( + curve, + key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.UncompressedPoint + ) + ) + assert key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.CompressedPoint + ) == point + assert key2.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.CompressedPoint + ) == point + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +class TestECDSAVerification(object): + def test_signature_not_bytes(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + key = ec.generate_private_key(ec.SECP256R1(), backend) + public_key = key.public_key() + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + public_key.verifier(1234, ec.ECDSA(hashes.SHA256())) + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +class TestECDH(object): + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join( + "asymmetric", "ECDH", + "KASValidityTest_ECCStaticUnified_NOKC_ZZOnly_init.fax"), + load_kasvs_ecdh_vectors + ) + ) + def test_key_exchange_with_vectors(self, backend, vector): + _skip_exchange_algorithm_unsupported( + backend, ec.ECDH(), ec._CURVE_TYPES[vector['curve']] + ) + + key_numbers = vector['IUT'] + private_numbers = ec.EllipticCurvePrivateNumbers( + key_numbers['d'], + ec.EllipticCurvePublicNumbers( + key_numbers['x'], + key_numbers['y'], + ec._CURVE_TYPES[vector['curve']]() + ) + ) + # Errno 5-7 indicates a bad public or private key, this doesn't test + # the ECDH code at all + if vector['fail'] and vector['errno'] in [5, 6, 7]: + with pytest.raises(ValueError): + private_numbers.private_key(backend) + return + else: + private_key = private_numbers.private_key(backend) + + peer_numbers = vector['CAVS'] + public_numbers = ec.EllipticCurvePublicNumbers( + peer_numbers['x'], + peer_numbers['y'], + ec._CURVE_TYPES[vector['curve']]() + ) + # Errno 1 and 2 indicates a bad public key, this doesn't test the ECDH + # code at all + if vector['fail'] and vector['errno'] in [1, 2]: + with pytest.raises(ValueError): + public_numbers.public_key(backend) + return + else: + peer_pubkey = public_numbers.public_key(backend) + + z = private_key.exchange(ec.ECDH(), peer_pubkey) + z = int(hexlify(z).decode('ascii'), 16) + # At this point fail indicates that one of the underlying keys was + # changed. This results in a non-matching derived key. + if vector['fail']: + # Errno 8 indicates Z should be changed. + assert vector['errno'] == 8 + assert z != vector['Z'] + else: + assert z == vector['Z'] + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "ECDH", "brainpool.txt"), + load_nist_vectors + ) + ) + def test_brainpool_kex(self, backend, vector): + curve = ec._CURVE_TYPES[vector['curve'].decode('ascii')] + _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), curve) + key = ec.EllipticCurvePrivateNumbers( + int(vector['da'], 16), + ec.EllipticCurvePublicNumbers( + int(vector['x_qa'], 16), int(vector['y_qa'], 16), curve() + ) + ).private_key(backend) + peer = ec.EllipticCurvePrivateNumbers( + int(vector['db'], 16), + ec.EllipticCurvePublicNumbers( + int(vector['x_qb'], 16), int(vector['y_qb'], 16), curve() + ) + ).private_key(backend) + shared_secret = key.exchange(ec.ECDH(), peer.public_key()) + assert shared_secret == binascii.unhexlify(vector["x_z"]) + shared_secret_2 = peer.exchange(ec.ECDH(), key.public_key()) + assert shared_secret_2 == binascii.unhexlify(vector["x_z"]) + + def test_exchange_unsupported_algorithm(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + + key = load_vectors_from_file( + os.path.join( + "asymmetric", "PKCS8", "ec_private_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read().encode(), None, backend + ) + ) + + with raises_unsupported_algorithm( + exceptions._Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ): + key.exchange(None, key.public_key()) + + def test_exchange_non_matching_curve(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + _skip_curve_unsupported(backend, ec.SECP384R1()) + + key = load_vectors_from_file( + os.path.join( + "asymmetric", "PKCS8", "ec_private_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read().encode(), None, backend + ) + ) + public_key = EC_KEY_SECP384R1.public_numbers.public_key(backend) + + with pytest.raises(ValueError): + key.exchange(ec.ECDH(), public_key) |
