diff options
Diffstat (limited to 'tests/hazmat/primitives/test_rsa.py')
| -rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 1273 |
1 files changed, 809 insertions, 464 deletions
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index eb12df8d..e6482651 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -6,12 +6,10 @@ from __future__ import absolute_import, division, print_function import binascii import itertools -import math import os import pytest -from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidSignature, _Reasons ) @@ -19,38 +17,43 @@ from cryptography.hazmat.backends.interfaces import ( PEMSerializationBackend, RSABackend ) from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import padding, rsa +from cryptography.hazmat.primitives.asymmetric import ( + padding, rsa, utils as asym_utils +) from cryptography.hazmat.primitives.asymmetric.rsa import ( RSAPrivateNumbers, RSAPublicNumbers ) +from cryptography.utils import CryptographyDeprecationWarning from .fixtures_rsa import ( RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027, RSA_KEY_1028, RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031, RSA_KEY_1536, RSA_KEY_2048, - RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, RSA_KEY_745, - RSA_KEY_768, + RSA_KEY_2048_ALT, RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, + RSA_KEY_745, RSA_KEY_768, ) from .utils import ( _check_rsa_private_numbers, generate_rsa_verification_test ) +from ...doubles import ( + DummyAsymmetricPadding, DummyHashAlgorithm, DummyKeySerializationEncryption +) from ...utils import ( - load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file, - raises_unsupported_algorithm + load_nist_vectors, load_pkcs1_vectors, load_rsa_nist_vectors, + load_vectors_from_file, raises_unsupported_algorithm ) -@utils.register_interface(padding.AsymmetricPadding) -class DummyPadding(object): - name = "UNSUPPORTED-PADDING" - - class DummyMGF(object): _salt_length = 0 -@utils.register_interface(serialization.KeySerializationEncryption) -class DummyKeyEncryption(object): - pass +def _check_rsa_private_numbers_if_serializable(key): + if isinstance(key, rsa.RSAPrivateKeyWithSerialization): + _check_rsa_private_numbers(key.private_numbers()) + + +def test_check_rsa_private_numbers_if_serializable(): + _check_rsa_private_numbers_if_serializable("notserializable") def _flatten_pkcs1_examples(vectors): @@ -64,6 +67,60 @@ def _flatten_pkcs1_examples(vectors): return flattened_vectors +def _build_oaep_sha2_vectors(): + base_path = os.path.join("asymmetric", "RSA", "oaep-custom") + vectors = [] + hashalgs = [ + hashes.SHA1(), + hashes.SHA224(), + hashes.SHA256(), + hashes.SHA384(), + hashes.SHA512(), + ] + for mgf1alg, oaepalg in itertools.product(hashalgs, hashalgs): + if mgf1alg.name == "sha1" and oaepalg.name == "sha1": + # We need to generate the cartesian product of the permutations + # of all the SHAs above, but SHA1/SHA1 is something we already + # tested previously and thus did not generate custom vectors for. + continue + + examples = _flatten_pkcs1_examples( + load_vectors_from_file( + os.path.join( + base_path, + "oaep-{}-{}.txt".format( + mgf1alg.name, oaepalg.name + ) + ), + load_pkcs1_vectors + ) + ) + # We've loaded the files, but the loaders don't give us any information + # about the mgf1 or oaep hash algorithms. We know this info so we'll + # just add that to the end of the tuple + for private, public, vector in examples: + vectors.append((private, public, vector, mgf1alg, oaepalg)) + return vectors + + +def _skip_pss_hash_algorithm_unsupported(backend, hash_alg): + if not backend.rsa_padding_supported( + padding.PSS( + mgf=padding.MGF1(hash_alg), + salt_length=padding.PSS.MAX_LENGTH + ) + ): + pytest.skip( + "Does not support {} in MGF1 using PSS.".format(hash_alg.name) + ) + + +@pytest.mark.requires_backend_interface(interface=RSABackend) +def test_skip_pss_hash_algorithm_unsupported(backend): + with pytest.raises(pytest.skip.Exception): + _skip_pss_hash_algorithm_unsupported(backend, DummyHashAlgorithm()) + + def test_modular_inverse(): p = int( "d1f9f6c09fd3d38987f7970247b85a6da84907753d42ec52bc23b745093f4fff5cff3" @@ -85,21 +142,6 @@ def test_modular_inverse(): ) -def _skip_if_no_serialization(key, backend): - if not isinstance( - key, - (rsa.RSAPrivateKeyWithSerialization, rsa.RSAPublicKeyWithSerialization) - ): - pytest.skip( - "{0} does not support RSA key serialization".format(backend) - ) - - -def test_skip_if_no_serialization(): - with pytest.raises(pytest.skip.Exception): - _skip_if_no_serialization("notakeywithserialization", "backend") - - @pytest.mark.requires_backend_interface(interface=RSABackend) class TestRSA(object): @pytest.mark.parametrize( @@ -113,10 +155,9 @@ class TestRSA(object): skey = rsa.generate_private_key(public_exponent, key_size, backend) assert skey.key_size == key_size - if isinstance(skey, rsa.RSAPrivateKeyWithSerialization): - _check_rsa_private_numbers(skey.private_numbers()) - pkey = skey.public_key() - assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers) + _check_rsa_private_numbers_if_serializable(skey) + pkey = skey.public_key() + assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers) def test_generate_bad_public_exponent(self, backend): with pytest.raises(ValueError): @@ -177,6 +218,133 @@ class TestRSA(object): assert public_num.n == public_num2.n assert public_num.e == public_num2.e + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "RSA", "oaep-label.txt"), + load_nist_vectors) + ) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=b"label" + ) + ), + skip_message="Does not support RSA OAEP labels" + ) + def test_oaep_label_decrypt(self, vector, backend): + private_key = serialization.load_der_private_key( + binascii.unhexlify(vector["key"]), None, backend + ) + assert vector["oaepdigest"] == b"SHA512" + decrypted = private_key.decrypt( + binascii.unhexlify(vector["input"]), + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA512()), + algorithm=hashes.SHA512(), + label=binascii.unhexlify(vector["oaeplabel"]) + ) + ) + assert vector["output"][1:-1] == decrypted + + @pytest.mark.parametrize( + ("msg", "label"), + [ + (b"amazing encrypted msg", b"some label"), + (b"amazing encrypted msg", b""), + ] + ) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=b"label" + ) + ), + skip_message="Does not support RSA OAEP labels" + ) + def test_oaep_label_roundtrip(self, msg, label, backend): + private_key = RSA_KEY_2048.private_key(backend) + ct = private_key.public_key().encrypt( + msg, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=label + ) + ) + pt = private_key.decrypt( + ct, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=label + ) + ) + assert pt == msg + + @pytest.mark.parametrize( + ("enclabel", "declabel"), + [ + (b"label1", b"label2"), + (b"label3", b""), + (b"", b"label4"), + ] + ) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=b"label" + ) + ), + skip_message="Does not support RSA OAEP labels" + ) + def test_oaep_wrong_label(self, enclabel, declabel, backend): + private_key = RSA_KEY_2048.private_key(backend) + msg = b"test" + ct = private_key.public_key().encrypt( + msg, padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=enclabel + ) + ) + with pytest.raises(ValueError): + private_key.decrypt( + ct, padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=declabel + ) + ) + + @pytest.mark.supported( + only_if=lambda backend: not backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=b"label" + ) + ), + skip_message="Requires backend without RSA OAEP label support" + ) + def test_unsupported_oaep_label_decrypt(self, backend): + private_key = RSA_KEY_512.private_key(backend) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): + private_key.decrypt( + b"0" * 64, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=b"label" + ) + ) + def test_rsa_generate_invalid_backend(): pretend_backend = object() @@ -215,9 +383,11 @@ class TestRSASignature(object): n=private["modulus"] ) ).private_key(backend) - signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) - signer.update(binascii.unhexlify(example["message"])) - signature = signer.finalize() + signature = private_key.sign( + binascii.unhexlify(example["message"]), + padding.PKCS1v15(), + hashes.SHA1() + ) assert binascii.hexlify(signature) == example["signature"] @pytest.mark.supported( @@ -255,56 +425,43 @@ class TestRSASignature(object): e=public["public_exponent"], n=public["modulus"] ).public_key(backend) - signer = private_key.signer( + signature = private_key.sign( + binascii.unhexlify(example["message"]), padding.PSS( mgf=padding.MGF1(algorithm=hashes.SHA1()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1() ) - signer.update(binascii.unhexlify(example["message"])) - signature = signer.finalize() - assert len(signature) == math.ceil(private_key.key_size / 8.0) + assert len(signature) == (private_key.key_size + 7) // 8 # PSS signatures contain randomness so we can't do an exact # signature check. Instead we'll verify that the signature created # successfully verifies. - verifier = public_key.verifier( + public_key.verify( signature, + binascii.unhexlify(example["message"]), padding.PSS( mgf=padding.MGF1(algorithm=hashes.SHA1()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1(), ) - verifier.update(binascii.unhexlify(example["message"])) - verifier.verify() @pytest.mark.parametrize( "hash_alg", [hashes.SHA224(), hashes.SHA256(), hashes.SHA384(), hashes.SHA512()] ) def test_pss_signing_sha2(self, hash_alg, backend): - if not backend.rsa_padding_supported( - padding.PSS( - mgf=padding.MGF1(hash_alg), - salt_length=padding.PSS.MAX_LENGTH - ) - ): - pytest.skip( - "Does not support {0} in MGF1 using PSS.".format(hash_alg.name) - ) + _skip_pss_hash_algorithm_unsupported(backend, hash_alg) private_key = RSA_KEY_768.private_key(backend) public_key = private_key.public_key() pss = padding.PSS( mgf=padding.MGF1(hash_alg), salt_length=padding.PSS.MAX_LENGTH ) - signer = private_key.signer(pss, hash_alg) - signer.update(b"testing signature") - signature = signer.finalize() - verifier = public_key.verifier(signature, pss, hash_alg) - verifier.update(b"testing signature") - verifier.verify() + msg = b"testing signature" + signature = private_key.sign(msg, pss, hash_alg) + public_key.verify(signature, msg, pss, hash_alg) @pytest.mark.supported( only_if=lambda backend: ( @@ -320,15 +477,14 @@ class TestRSASignature(object): ) def test_pss_minimum_key_size_for_digest(self, backend): private_key = RSA_KEY_522.private_key(backend) - signer = private_key.signer( + private_key.sign( + b"no failure", padding.PSS( mgf=padding.MGF1(hashes.SHA1()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA512() ) - signer.update(b"no failure") - signer.finalize() @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -346,7 +502,8 @@ class TestRSASignature(object): def test_pss_signing_digest_too_large_for_key_size(self, backend): private_key = RSA_KEY_512.private_key(backend) with pytest.raises(ValueError): - private_key.signer( + private_key.sign( + b"msg", padding.PSS( mgf=padding.MGF1(hashes.SHA1()), salt_length=padding.PSS.MAX_LENGTH @@ -365,16 +522,15 @@ class TestRSASignature(object): ) def test_pss_signing_salt_length_too_long(self, backend): private_key = RSA_KEY_512.private_key(backend) - signer = private_key.signer( - padding.PSS( - mgf=padding.MGF1(hashes.SHA1()), - salt_length=1000000 - ), - hashes.SHA1() - ) - signer.update(b"failure coming") with pytest.raises(ValueError): - signer.finalize() + private_key.sign( + b"failure coming", + padding.PSS( + mgf=padding.MGF1(hashes.SHA1()), + salt_length=1000000 + ), + hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -384,7 +540,8 @@ class TestRSASignature(object): ) def test_use_after_finalize(self, backend): private_key = RSA_KEY_512.private_key(backend) - signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) + with pytest.warns(CryptographyDeprecationWarning): + signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) signer.update(b"sign me") signer.finalize() with pytest.raises(AlreadyFinalized): @@ -395,12 +552,12 @@ class TestRSASignature(object): def test_unsupported_padding(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - private_key.signer(DummyPadding(), hashes.SHA1()) + private_key.sign(b"msg", DummyAsymmetricPadding(), hashes.SHA1()) def test_padding_incorrect_type(self, backend): private_key = RSA_KEY_512.private_key(backend) with pytest.raises(TypeError): - private_key.signer("notpadding", hashes.SHA1()) + private_key.sign(b"msg", "notpadding", hashes.SHA1()) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -411,7 +568,8 @@ class TestRSASignature(object): def test_unsupported_pss_mgf(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): - private_key.signer( + private_key.sign( + b"msg", padding.PSS( mgf=DummyMGF(), salt_length=padding.PSS.MAX_LENGTH @@ -427,13 +585,12 @@ class TestRSASignature(object): ) def test_pkcs1_digest_too_large_for_key_size(self, backend): private_key = RSA_KEY_599.private_key(backend) - signer = private_key.signer( - padding.PKCS1v15(), - hashes.SHA512() - ) - signer.update(b"failure coming") with pytest.raises(ValueError): - signer.finalize() + private_key.sign( + b"failure coming", + padding.PKCS1v15(), + hashes.SHA512() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -443,12 +600,104 @@ class TestRSASignature(object): ) def test_pkcs1_minimum_key_size(self, backend): private_key = RSA_KEY_745.private_key(backend) - signer = private_key.signer( + private_key.sign( + b"no failure", padding.PKCS1v15(), hashes.SHA512() ) - signer.update(b"no failure") - signer.finalize() + + def test_sign(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + pkcs = padding.PKCS1v15() + algorithm = hashes.SHA1() + signature = private_key.sign(message, pkcs, algorithm) + public_key = private_key.public_key() + public_key.verify(signature, message, pkcs, algorithm) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0) + ), + skip_message="Does not support PSS." + ) + def test_prehashed_sign(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + digest = h.finalize() + pss = padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0) + prehashed_alg = asym_utils.Prehashed(hashes.SHA1()) + signature = private_key.sign(digest, pss, prehashed_alg) + public_key = private_key.public_key() + public_key.verify(signature, message, pss, hashes.SHA1()) + + @pytest.mark.supported( + only_if=lambda backend: backend.hash_supported( + hashes.BLAKE2s(digest_size=32)), + skip_message="Does not support BLAKE2s", + ) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0) + ), + skip_message="Does not support PSS." + ) + def test_unsupported_hash(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + pss = padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=0) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): + private_key.sign(message, pss, hashes.BLAKE2s(32)) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0) + ), + skip_message="Does not support PSS." + ) + def test_prehashed_digest_mismatch(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + h = hashes.Hash(hashes.SHA512(), backend) + h.update(message) + digest = h.finalize() + pss = padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0) + prehashed_alg = asym_utils.Prehashed(hashes.SHA1()) + with pytest.raises(ValueError): + private_key.sign(digest, pss, prehashed_alg) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PKCS1v15() + ), + skip_message="Does not support PKCS1v1.5." + ) + def test_prehashed_unsupported_in_signer_ctx(self, backend): + private_key = RSA_KEY_512.private_key(backend) + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + private_key.signer( + padding.PKCS1v15(), + asym_utils.Prehashed(hashes.SHA1()) + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PKCS1v15() + ), + skip_message="Does not support PKCS1v1.5." + ) + def test_prehashed_unsupported_in_verifier_ctx(self, backend): + public_key = RSA_KEY_512.private_key(backend).public_key() + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + public_key.verifier( + b"0" * 64, + padding.PKCS1v15(), + asym_utils.Prehashed(hashes.SHA1()) + ) @pytest.mark.requires_backend_interface(interface=RSABackend) @@ -473,13 +722,12 @@ class TestRSAVerification(object): e=public["public_exponent"], n=public["modulus"] ).public_key(backend) - verifier = public_key.verifier( + public_key.verify( binascii.unhexlify(example["signature"]), + binascii.unhexlify(example["message"]), padding.PKCS1v15(), hashes.SHA1() ) - verifier.update(binascii.unhexlify(example["message"])) - verifier.verify() @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -490,17 +738,51 @@ class TestRSAVerification(object): def test_invalid_pkcs1v15_signature_wrong_data(self, backend): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() - signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) - signer.update(b"sign me") - signature = signer.finalize() - verifier = public_key.verifier( - signature, - padding.PKCS1v15(), - hashes.SHA1() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() ) - verifier.update(b"incorrect data") with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, + b"incorrect data", + padding.PKCS1v15(), + hashes.SHA1() + ) + + def test_invalid_signature_sequence_removed(self, backend): + """ + This test comes from wycheproof + """ + key_der = binascii.unhexlify( + b"30820122300d06092a864886f70d01010105000382010f003082010a02820101" + b"00a2b451a07d0aa5f96e455671513550514a8a5b462ebef717094fa1fee82224" + b"e637f9746d3f7cafd31878d80325b6ef5a1700f65903b469429e89d6eac88450" + b"97b5ab393189db92512ed8a7711a1253facd20f79c15e8247f3d3e42e46e48c9" + b"8e254a2fe9765313a03eff8f17e1a029397a1fa26a8dce26f490ed81299615d9" + b"814c22da610428e09c7d9658594266f5c021d0fceca08d945a12be82de4d1ece" + b"6b4c03145b5d3495d4ed5411eb878daf05fd7afc3e09ada0f1126422f590975a" + b"1969816f48698bcbba1b4d9cae79d460d8f9f85e7975005d9bc22c4e5ac0f7c1" + b"a45d12569a62807d3b9a02e5a530e773066f453d1f5b4c2e9cf7820283f742b9" + b"d50203010001" + ) + sig = binascii.unhexlify( + b"498209f59a0679a1f926eccf3056da2cba553d7ab3064e7c41ad1d739f038249" + b"f02f5ad12ee246073d101bc3cdb563e8b6be61562056422b7e6c16ad53deb12a" + b"f5de744197753a35859833f41bb59c6597f3980132b7478fd0b95fd27dfad64a" + b"20fd5c25312bbd41a85286cd2a83c8df5efa0779158d01b0747ff165b055eb28" + b"80ea27095700a295593196d8c5922cf6aa9d7e29b5056db5ded5eb20aeb31b89" + b"42e26b15a5188a4934cd7e39cfe379a197f49a204343a493452deebca436ee61" + b"4f4daf989e355544489f7e69ffa8ccc6a1e81cf0ab33c3e6d7591091485a6a31" + b"bda3b33946490057b9a3003d3fd9daf7c4778b43fd46144d945d815f12628ff4" + ) + public_key = serialization.load_der_public_key(key_der, backend) + with pytest.raises(InvalidSignature): + public_key.verify( + sig, + binascii.unhexlify(b"313233343030"), + padding.PKCS1v15(), + hashes.SHA256() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -512,17 +794,12 @@ class TestRSAVerification(object): private_key = RSA_KEY_512.private_key(backend) private_key2 = RSA_KEY_512_ALT.private_key(backend) public_key = private_key2.public_key() - signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) - signer.update(b"sign me") - signature = signer.finalize() - verifier = public_key.verifier( - signature, - padding.PKCS1v15(), - hashes.SHA1() - ) - verifier.update(b"sign me") + msg = b"sign me" + signature = private_key.sign(msg, padding.PKCS1v15(), hashes.SHA1()) with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, msg, padding.PKCS1v15(), hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -547,16 +824,15 @@ class TestRSAVerification(object): e=public["public_exponent"], n=public["modulus"] ).public_key(backend) - verifier = public_key.verifier( + public_key.verify( binascii.unhexlify(example["signature"]), + binascii.unhexlify(example["message"]), padding.PSS( mgf=padding.MGF1(algorithm=hashes.SHA1()), salt_length=20 ), hashes.SHA1() ) - verifier.update(binascii.unhexlify(example["message"])) - verifier.verify() @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -580,17 +856,16 @@ class TestRSAVerification(object): b"0e68c3649df91c5bc3665f96e157efa75b71934aaa514d91e94ca8418d100f45" b"6f05288e58525f99666bab052adcffdf7186eb40f583bd38d98c97d3d524808b" ) - verifier = public_key.verifier( - signature, - padding.PSS( - mgf=padding.MGF1(algorithm=hashes.SHA1()), - salt_length=padding.PSS.MAX_LENGTH - ), - hashes.SHA1() - ) - verifier.update(b"incorrect data") with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, + b"incorrect data", + padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -616,17 +891,16 @@ class TestRSAVerification(object): ), e=65537 ).public_key(backend) - verifier = public_key.verifier( - signature, - padding.PSS( - mgf=padding.MGF1(algorithm=hashes.SHA1()), - salt_length=padding.PSS.MAX_LENGTH - ), - hashes.SHA1() - ) - verifier.update(b"sign me") with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, + b"sign me", + padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -652,17 +926,16 @@ class TestRSAVerification(object): ), e=65537 ).public_key(backend) - verifier = public_key.verifier( - signature, - padding.PSS( - mgf=padding.MGF1(algorithm=hashes.SHA1()), - salt_length=padding.PSS.MAX_LENGTH - ), - hashes.SHA1() - ) - verifier.update(b"sign me") with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, + b"sign me", + padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -673,15 +946,16 @@ class TestRSAVerification(object): def test_use_after_finalize(self, backend): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() - signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) - signer.update(b"sign me") - signature = signer.finalize() - - verifier = public_key.verifier( - signature, - padding.PKCS1v15(), - hashes.SHA1() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() ) + + with pytest.warns(CryptographyDeprecationWarning): + verifier = public_key.verifier( + signature, + padding.PKCS1v15(), + hashes.SHA1() + ) verifier.update(b"sign me") verifier.verify() with pytest.raises(AlreadyFinalized): @@ -693,13 +967,33 @@ class TestRSAVerification(object): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - public_key.verifier(b"sig", DummyPadding(), hashes.SHA1()) + public_key.verify( + b"sig", b"msg", DummyAsymmetricPadding(), hashes.SHA1() + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.PKCS1v15() + ), + skip_message="Does not support PKCS1v1.5." + ) + def test_signature_not_bytes(self, backend): + public_key = RSA_KEY_512.public_numbers.public_key(backend) + signature = 1234 + + with pytest.raises(TypeError), \ + pytest.warns(CryptographyDeprecationWarning): + public_key.verifier( + signature, + padding.PKCS1v15(), + hashes.SHA1() + ) def test_padding_incorrect_type(self, backend): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() with pytest.raises(TypeError): - public_key.verifier(b"sig", "notpadding", hashes.SHA1()) + public_key.verify(b"sig", b"msg", "notpadding", hashes.SHA1()) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -711,8 +1005,9 @@ class TestRSAVerification(object): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): - public_key.verifier( + public_key.verify( b"sig", + b"msg", padding.PSS( mgf=DummyMGF(), salt_length=padding.PSS.MAX_LENGTH @@ -741,8 +1036,9 @@ class TestRSAVerification(object): ) public_key = private_key.public_key() with pytest.raises(ValueError): - public_key.verifier( + public_key.verify( signature, + b"msg doesn't matter", padding.PSS( mgf=padding.MGF1(algorithm=hashes.SHA1()), salt_length=padding.PSS.MAX_LENGTH @@ -772,19 +1068,50 @@ class TestRSAVerification(object): ), e=65537 ).public_key(backend) - verifier = public_key.verifier( - signature, - padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - ), - salt_length=1000000 - ), - hashes.SHA1() - ) - verifier.update(b"sign me") with pytest.raises(InvalidSignature): - verifier.verify() + public_key.verify( + signature, + b"sign me", + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + ), + salt_length=1000000 + ), + hashes.SHA1() + ) + + def test_verify(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + pkcs = padding.PKCS1v15() + algorithm = hashes.SHA1() + signature = private_key.sign(message, pkcs, algorithm) + public_key = private_key.public_key() + public_key.verify(signature, message, pkcs, algorithm) + + def test_prehashed_verify(self, backend): + private_key = RSA_KEY_512.private_key(backend) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + digest = h.finalize() + prehashed_alg = asym_utils.Prehashed(hashes.SHA1()) + pkcs = padding.PKCS1v15() + signature = private_key.sign(message, pkcs, hashes.SHA1()) + public_key = private_key.public_key() + public_key.verify(signature, digest, pkcs, prehashed_alg) + + def test_prehashed_digest_mismatch(self, backend): + public_key = RSA_KEY_512.private_key(backend).public_key() + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + prehashed_alg = asym_utils.Prehashed(hashes.SHA512()) + pkcs = padding.PKCS1v15() + with pytest.raises(ValueError): + public_key.verify(b"\x00" * 64, data, pkcs, prehashed_alg) @pytest.mark.requires_backend_interface(interface=RSABackend) @@ -1009,6 +1336,10 @@ class TestRSAPKCS1Verification(object): class TestPSS(object): + def test_calculate_max_pss_salt_length(self): + with pytest.raises(TypeError): + padding.calculate_max_pss_salt_length(object(), hashes.SHA256()) + def test_invalid_salt_length_not_integer(self): with pytest.raises(TypeError): padding.PSS( @@ -1096,14 +1427,14 @@ class TestRSADecryption(object): ) ).private_key(backend) ciphertext = binascii.unhexlify(example["encryption"]) - assert len(ciphertext) == math.ceil(skey.key_size / 8.0) + assert len(ciphertext) == (skey.key_size + 7) // 8 message = skey.decrypt(ciphertext, padding.PKCS1v15()) assert message == binascii.unhexlify(example["message"]) def test_unsupported_padding(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - private_key.decrypt(b"0" * 64, DummyPadding()) + private_key.decrypt(b"0" * 64, DummyAsymmetricPadding()) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -1193,6 +1524,119 @@ class TestRSADecryption(object): ) assert message == binascii.unhexlify(example["message"]) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA224()), + algorithm=hashes.SHA224(), + label=None + ) + ), + skip_message="Does not support OAEP using SHA224 MGF1 and SHA224 hash." + ) + @pytest.mark.parametrize( + "vector", + _build_oaep_sha2_vectors() + ) + def test_decrypt_oaep_sha2_vectors(self, vector, backend): + private, public, example, mgf1_alg, hash_alg = vector + skey = rsa.RSAPrivateNumbers( + p=private["p"], + q=private["q"], + d=private["private_exponent"], + dmp1=private["dmp1"], + dmq1=private["dmq1"], + iqmp=private["iqmp"], + public_numbers=rsa.RSAPublicNumbers( + e=private["public_exponent"], + n=private["modulus"] + ) + ).private_key(backend) + message = skey.decrypt( + binascii.unhexlify(example["encryption"]), + padding.OAEP( + mgf=padding.MGF1(algorithm=mgf1_alg), + algorithm=hash_alg, + label=None + ) + ) + assert message == binascii.unhexlify(example["message"]) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ), + skip_message="Does not support OAEP." + ) + def test_invalid_oaep_decryption(self, backend): + # More recent versions of OpenSSL may raise RSA_R_OAEP_DECODING_ERROR + # This test triggers it and confirms that we properly handle it. Other + # backends should also return the proper ValueError. + private_key = RSA_KEY_512.private_key(backend) + + ciphertext = private_key.public_key().encrypt( + b'secure data', + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + + private_key_alt = RSA_KEY_512_ALT.private_key(backend) + + with pytest.raises(ValueError): + private_key_alt.decrypt( + ciphertext, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ), + skip_message="Does not support OAEP." + ) + def test_invalid_oaep_decryption_data_to_large_for_modulus(self, backend): + key = RSA_KEY_2048_ALT.private_key(backend) + + ciphertext = ( + b'\xb1ph\xc0\x0b\x1a|\xe6\xda\xea\xb5\xd7%\x94\x07\xf96\xfb\x96' + b'\x11\x9b\xdc4\xea.-\x91\x80\x13S\x94\x04m\xe9\xc5/F\x1b\x9b:\\' + b'\x1d\x04\x16ML\xae\xb32J\x01yuA\xbb\x83\x1c\x8f\xf6\xa5\xdbp\xcd' + b'\nx\xc7\xf6\x15\xb2/\xdcH\xae\xe7\x13\x13by\r4t\x99\x0fc\x1f\xc1' + b'\x1c\xb1\xdd\xc5\x08\xd1\xee\xa1XQ\xb8H@L5v\xc3\xaf\xf2\r\x97' + b'\xed\xaa\xe7\xf1\xd4xai\xd3\x83\xd9\xaa9\xbfx\xe1\x87F \x01\xff' + b'L\xccv}ae\xb3\xfa\xf2B\xb8\xf9\x04H\x94\x85\xcb\x86\xbb\\ghx!W31' + b'\xc7;t\na_E\xc2\x16\xb0;\xa1\x18\t\x1b\xe1\xdb\x80>)\x15\xc6\x12' + b'\xcb\xeeg`\x8b\x9b\x1b\x05y4\xb0\x84M6\xcd\xa1\x827o\xfd\x96\xba' + b'Z#\x8d\xae\x01\xc9\xf2\xb6\xde\x89{8&eQ\x1e8\x03\x01#?\xb66\\' + b'\xad.\xe9\xfa!\x95 c{\xcaz\xe0*\tP\r\x91\x9a)B\xb5\xadN\xf4$\x83' + b'\t\xb5u\xab\x19\x99' + ) + + with pytest.raises(ValueError): + key.decrypt( + ciphertext, + padding.OAEP( + algorithm=hashes.SHA1(), + mgf=padding.MGF1(hashes.SHA1()), + label=None + ) + ) + def test_unsupported_oaep_mgf(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): @@ -1239,7 +1683,48 @@ class TestRSAEncryption(object): public_key = private_key.public_key() ct = public_key.encrypt(pt, pad) assert ct != pt - assert len(ct) == math.ceil(public_key.key_size / 8.0) + assert len(ct) == (public_key.key_size + 7) // 8 + recovered_pt = private_key.decrypt(ct, pad) + assert recovered_pt == pt + + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA512(), + label=None + ) + ), + skip_message="Does not support OAEP using SHA256 MGF1 and SHA512 hash." + ) + @pytest.mark.parametrize( + ("mgf1hash", "oaephash"), + itertools.product([ + hashes.SHA1(), + hashes.SHA224(), + hashes.SHA256(), + hashes.SHA384(), + hashes.SHA512(), + ], [ + hashes.SHA1(), + hashes.SHA224(), + hashes.SHA256(), + hashes.SHA384(), + hashes.SHA512(), + ]) + ) + def test_rsa_encrypt_oaep_sha2(self, mgf1hash, oaephash, backend): + pad = padding.OAEP( + mgf=padding.MGF1(algorithm=mgf1hash), + algorithm=oaephash, + label=None + ) + private_key = RSA_KEY_2048.private_key(backend) + pt = b"encrypt me using sha2 hashes!" + public_key = private_key.public_key() + ct = public_key.encrypt(pt, pad) + assert ct != pt + assert len(ct) == (public_key.key_size + 7) // 8 recovered_pt = private_key.decrypt(ct, pad) assert recovered_pt == pt @@ -1264,7 +1749,7 @@ class TestRSAEncryption(object): public_key = private_key.public_key() ct = public_key.encrypt(pt, pad) assert ct != pt - assert len(ct) == math.ceil(public_key.key_size / 8.0) + assert len(ct) == (public_key.key_size + 7) // 8 recovered_pt = private_key.decrypt(ct, pad) assert recovered_pt == pt @@ -1306,7 +1791,7 @@ class TestRSAEncryption(object): public_key = private_key.public_key() with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - public_key.encrypt(b"somedata", DummyPadding()) + public_key.encrypt(b"somedata", DummyAsymmetricPadding()) with pytest.raises(TypeError): public_key.encrypt(b"somedata", padding=object()) @@ -1367,304 +1852,81 @@ class TestRSANumbers(object): with pytest.raises(TypeError): rsa.RSAPublicNumbers(e=1, n=None) - def test_private_numbers_invalid_types(self): - public_numbers = rsa.RSAPublicNumbers(e=1, n=15) - - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=None, - q=5, - d=1, - dmp1=1, - dmq1=1, - iqmp=2, - public_numbers=public_numbers - ) - - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=3, - q=None, - d=1, - dmp1=1, - dmq1=1, - iqmp=2, - public_numbers=public_numbers - ) - - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=3, - q=5, - d=None, - dmp1=1, - dmq1=1, - iqmp=2, - public_numbers=public_numbers - ) - - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=3, - q=5, - d=1, - dmp1=None, - dmq1=1, - iqmp=2, - public_numbers=public_numbers - ) - - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=3, - q=5, - d=1, - dmp1=1, - dmq1=None, - iqmp=2, - public_numbers=public_numbers - ) - + @pytest.mark.parametrize( + ("p", "q", "d", "dmp1", "dmq1", "iqmp", "public_numbers"), + [ + (None, 5, 1, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)), + (3, None, 1, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)), + (3, 5, None, 1, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)), + (3, 5, 1, None, 1, 2, rsa.RSAPublicNumbers(e=1, n=15)), + (3, 5, 1, 1, None, 2, rsa.RSAPublicNumbers(e=1, n=15)), + (3, 5, 1, 1, 1, None, rsa.RSAPublicNumbers(e=1, n=15)), + (3, 5, 1, 1, 1, 2, None), + ] + ) + def test_private_numbers_invalid_types(self, p, q, d, dmp1, dmq1, iqmp, + public_numbers): with pytest.raises(TypeError): rsa.RSAPrivateNumbers( - p=3, - q=5, - d=1, - dmp1=1, - dmq1=1, - iqmp=None, + p=p, q=q, + d=d, + dmp1=dmp1, + dmq1=dmq1, + iqmp=iqmp, public_numbers=public_numbers ) - with pytest.raises(TypeError): - rsa.RSAPrivateNumbers( - p=3, - q=5, - d=1, - dmp1=1, - dmq1=1, - iqmp=2, - public_numbers=None - ) - - def test_invalid_public_numbers_argument_values(self, backend): + @pytest.mark.parametrize( + ("e", "n"), + [ + (7, 2), # modulus < 3 + (1, 15), # public_exponent < 3 + (17, 15), # public_exponent > modulus + (14, 15), # public_exponent not odd + ] + ) + def test_invalid_public_numbers_argument_values(self, e, n, backend): # Start with public_exponent=7, modulus=15. Then change one value at a # time to test the bounds. - # Test a modulus < 3. - - with pytest.raises(ValueError): - rsa.RSAPublicNumbers(e=7, n=2).public_key(backend) - - # Test a public_exponent < 3 with pytest.raises(ValueError): - rsa.RSAPublicNumbers(e=1, n=15).public_key(backend) + rsa.RSAPublicNumbers(e=e, n=n).public_key(backend) - # Test a public_exponent > modulus - with pytest.raises(ValueError): - rsa.RSAPublicNumbers(e=17, n=15).public_key(backend) - - # Test a public_exponent that is not odd. - with pytest.raises(ValueError): - rsa.RSAPublicNumbers(e=14, n=15).public_key(backend) - - def test_invalid_private_numbers_argument_values(self, backend): + @pytest.mark.parametrize( + ("p", "q", "d", "dmp1", "dmq1", "iqmp", "e", "n"), + [ + (3, 11, 3, 1, 3, 2, 7, 2), # modulus < 3 + (3, 11, 3, 1, 3, 2, 7, 35), # modulus != p * q + (37, 11, 3, 1, 3, 2, 7, 33), # p > modulus + (3, 37, 3, 1, 3, 2, 7, 33), # q > modulus + (3, 11, 3, 35, 3, 2, 7, 33), # dmp1 > modulus + (3, 11, 3, 1, 35, 2, 7, 33), # dmq1 > modulus + (3, 11, 3, 1, 3, 35, 7, 33), # iqmp > modulus + (3, 11, 37, 1, 3, 2, 7, 33), # d > modulus + (3, 11, 3, 1, 3, 2, 1, 33), # public_exponent < 3 + (3, 11, 3, 1, 3, 35, 65537, 33), # public_exponent > modulus + (3, 11, 3, 1, 3, 2, 6, 33), # public_exponent is not odd + (3, 11, 3, 2, 3, 2, 7, 33), # dmp1 is not odd + (3, 11, 3, 1, 4, 2, 7, 33), # dmq1 is not odd + ] + ) + def test_invalid_private_numbers_argument_values(self, p, q, d, dmp1, dmq1, + iqmp, e, n, backend): # Start with p=3, q=11, private_exponent=3, public_exponent=7, # modulus=33, dmp1=1, dmq1=3, iqmp=2. Then change one value at # a time to test the bounds. - # Test a modulus < 3. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=2 - ) - ).private_key(backend) - - # Test a modulus != p * q. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=35 - ) - ).private_key(backend) - - # Test a p > modulus. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=37, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a q > modulus. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=37, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a dmp1 > modulus. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=35, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a dmq1 > modulus. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=35, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test an iqmp > modulus. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=35, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a private_exponent > modulus - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=37, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a public_exponent < 3 - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=1, - n=33 - ) - ).private_key(backend) - - # Test a public_exponent > modulus with pytest.raises(ValueError): rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=35, + p=p, + q=q, + d=d, + dmp1=dmp1, + dmq1=dmq1, + iqmp=iqmp, public_numbers=rsa.RSAPublicNumbers( - e=65537, - n=33 - ) - ).private_key(backend) - - # Test a public_exponent that is not odd. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=6, - n=33 - ) - ).private_key(backend) - - # Test a dmp1 that is not odd. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=2, - dmq1=3, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 - ) - ).private_key(backend) - - # Test a dmq1 that is not odd. - with pytest.raises(ValueError): - rsa.RSAPrivateNumbers( - p=3, - q=11, - d=3, - dmp1=1, - dmq1=4, - iqmp=2, - public_numbers=rsa.RSAPublicNumbers( - e=7, - n=33 + e=e, + n=n ) ).private_key(backend) @@ -1721,6 +1983,22 @@ class TestRSANumbersEquality(object): ) assert num != object() + def test_public_numbers_hash(self): + pub1 = RSAPublicNumbers(3, 17) + pub2 = RSAPublicNumbers(3, 17) + pub3 = RSAPublicNumbers(7, 21) + + assert hash(pub1) == hash(pub2) + assert hash(pub1) != hash(pub3) + + def test_private_numbers_hash(self): + priv1 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2)) + priv2 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2)) + priv3 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 3)) + + assert hash(priv1) == hash(priv2) + assert hash(priv1) != hash(priv3) + class TestRSAPrimeFactorRecovery(object): @pytest.mark.parametrize( @@ -1739,10 +2017,11 @@ class TestRSAPrimeFactorRecovery(object): private["private_exponent"] ) # Unfortunately there is no convention on which prime should be p - # and which one q. The function we use always makes p < q, but the - # NIST vectors are not so consistent. Accordingly we verify we've + # and which one q. The function we use always makes p > q, but the + # NIST vectors are not so consistent. Accordingly, we verify we've # recovered the proper (p, q) by sorting them and asserting on that. assert sorted([p, q]) == sorted([private["p"], private["q"]]) + assert p > q def test_invalid_recover_prime_factors(self): with pytest.raises(ValueError): @@ -1769,7 +2048,6 @@ class TestRSAPrivateKeySerialization(object): ) def test_private_bytes_encrypted_pem(self, backend, fmt, password): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( serialization.Encoding.PEM, fmt, @@ -1783,6 +2061,20 @@ class TestRSAPrivateKeySerialization(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): + key = RSA_KEY_2048.private_key(backend) + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + + @pytest.mark.parametrize( ("fmt", "password"), [ [serialization.PrivateFormat.PKCS8, b"s"], @@ -1793,7 +2085,6 @@ class TestRSAPrivateKeySerialization(object): ) def test_private_bytes_encrypted_der(self, backend, fmt, password): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( serialization.Encoding.DER, fmt, @@ -1834,7 +2125,6 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_unencrypted(self, backend, encoding, fmt, loader_func): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) serialized = key.private_bytes( encoding, fmt, serialization.NoEncryption() ) @@ -1878,7 +2168,6 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_traditional_der_encrypted_invalid(self, backend): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) with pytest.raises(ValueError): key.private_bytes( serialization.Encoding.DER, @@ -1888,7 +2177,6 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_invalid_encoding(self, backend): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( "notencoding", @@ -1898,7 +2186,6 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_invalid_format(self, backend): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( serialization.Encoding.PEM, @@ -1908,7 +2195,6 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_invalid_encryption_algorithm(self, backend): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.private_bytes( serialization.Encoding.PEM, @@ -1918,12 +2204,11 @@ class TestRSAPrivateKeySerialization(object): def test_private_bytes_unsupported_encryption_type(self, backend): key = RSA_KEY_2048.private_key(backend) - _skip_if_no_serialization(key, backend) with pytest.raises(ValueError): key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, - DummyKeyEncryption() + DummyKeySerializationEncryption() ) @@ -1966,18 +2251,78 @@ class TestRSAPEMPublicKeySerialization(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, format) assert serialized == key_bytes + def test_public_bytes_openssh(self, backend): + key_bytes = load_vectors_from_file( + os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.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"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7JHoJfg6yNzLMOWet8Z49a4KD" + b"0dCspMAYvo2YAMB7/wdEycocujbhJ2n/seONi+5XqTqqFkM5VBl8rmkkFPZk/7x0" + b"xmdsTPECSWnHK+HhoaNDFPR3j8jQhVo1laxiqcEhAHegi5cwtFosuJAvSKAFKEvy" + b"D43si00DQnXWrYHAEQ==" + ) + + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.PEM, serialization.PublicFormat.OpenSSH + ) + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.DER, serialization.PublicFormat.OpenSSH + ) + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.OpenSSH, + serialization.PublicFormat.PKCS1, + ) + with pytest.raises(ValueError): + key.public_bytes( + serialization.Encoding.OpenSSH, + serialization.PublicFormat.SubjectPublicKeyInfo, + ) + def test_public_bytes_invalid_encoding(self, backend): key = RSA_KEY_2048.private_key(backend).public_key() - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.public_bytes("notencoding", serialization.PublicFormat.PKCS1) def test_public_bytes_invalid_format(self, backend): key = RSA_KEY_2048.private_key(backend).public_key() - _skip_if_no_serialization(key, backend) with pytest.raises(TypeError): key.public_bytes(serialization.Encoding.PEM, "invalidformat") + + @pytest.mark.parametrize( + ("encoding", "fmt"), + [ + ( + serialization.Encoding.Raw, + serialization.PublicFormat.SubjectPublicKeyInfo + ), + (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1), + ] + list(itertools.product( + [ + serialization.Encoding.Raw, + serialization.Encoding.X962, + serialization.Encoding.PEM, + serialization.Encoding.DER + ], + [ + serialization.PublicFormat.Raw, + serialization.PublicFormat.UncompressedPoint, + serialization.PublicFormat.CompressedPoint + ] + )) + ) + def test_public_bytes_rejects_invalid(self, encoding, fmt, backend): + key = RSA_KEY_2048.private_key(backend).public_key() + with pytest.raises(ValueError): + key.public_bytes(encoding, fmt) |
