| # This file is dual licensed under the terms of the Apache License, Version |
| # 2.0, and the BSD License. See the LICENSE file in the root of this repository |
| # for complete details. |
| |
| from __future__ import absolute_import, division, print_function |
| |
| import binascii |
| import itertools |
| import math |
| import os |
| |
| import pytest |
| |
| from cryptography.exceptions import ( |
| AlreadyFinalized, InvalidSignature, _Reasons |
| ) |
| from cryptography.hazmat.backends.interfaces import ( |
| PEMSerializationBackend, RSABackend |
| ) |
| from cryptography.hazmat.primitives import hashes, serialization |
| 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_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_nist_vectors, load_pkcs1_vectors, load_rsa_nist_vectors, |
| load_vectors_from_file, raises_unsupported_algorithm |
| ) |
| |
| |
| class DummyMGF(object): |
| _salt_length = 0 |
| |
| |
| 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): |
| flattened_vectors = [] |
| for vector in vectors: |
| examples = vector[0].pop("examples") |
| for example in examples: |
| merged_vector = (vector[0], vector[1], example) |
| flattened_vectors.append(merged_vector) |
| |
| 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-{0}-{1}.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 {0} 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" |
| "617ce43d00121a9accc0051f519c76e08cf02fc18acfe4c9e6aea18da470a2b611d2e" |
| "56a7b35caa2c0239bc041a53cc5875ca0b668ae6377d4b23e932d8c995fd1e58ecfd8" |
| "c4b73259c0d8a54d691cca3f6fb85c8a5c1baf588e898d481", 16 |
| ) |
| q = int( |
| "d1519255eb8f678c86cfd06802d1fbef8b664441ac46b73d33d13a8404580a33a8e74" |
| "cb2ea2e2963125b3d454d7a922cef24dd13e55f989cbabf64255a736671f4629a47b5" |
| "b2347cfcd669133088d1c159518531025297c2d67c9da856a12e80222cd03b4c6ec0f" |
| "86c957cb7bb8de7a127b645ec9e820aa94581e4762e209f01", 16 |
| ) |
| assert rsa._modinv(q, p) == int( |
| "0275e06afa722999315f8f322275483e15e2fb46d827b17800f99110b269a6732748f" |
| "624a382fa2ed1ec68c99f7fc56fb60e76eea51614881f497ba7034c17dde955f92f15" |
| "772f8b2b41f3e56d88b1e096cdd293eba4eae1e82db815e0fadea0c4ec971bc6fd875" |
| "c20e67e48c31a611e98d32c6213ae4c4d7b53023b2f80c538", 16 |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSA(object): |
| @pytest.mark.parametrize( |
| ("public_exponent", "key_size"), |
| itertools.product( |
| (3, 5, 65537), |
| (1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1536, 2048) |
| ) |
| ) |
| def test_generate_rsa_keys(self, backend, public_exponent, key_size): |
| skey = rsa.generate_private_key(public_exponent, key_size, backend) |
| assert skey.key_size == key_size |
| |
| _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): |
| rsa.generate_private_key(public_exponent=1, |
| key_size=2048, |
| backend=backend) |
| |
| with pytest.raises(ValueError): |
| rsa.generate_private_key(public_exponent=4, |
| key_size=2048, |
| backend=backend) |
| |
| def test_cant_generate_insecure_tiny_key(self, backend): |
| with pytest.raises(ValueError): |
| rsa.generate_private_key(public_exponent=65537, |
| key_size=511, |
| backend=backend) |
| |
| with pytest.raises(ValueError): |
| rsa.generate_private_key(public_exponent=65537, |
| key_size=256, |
| backend=backend) |
| |
| @pytest.mark.parametrize( |
| "pkcs1_example", |
| load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "pss-vect.txt"), |
| load_pkcs1_vectors |
| ) |
| ) |
| def test_load_pss_vect_example_keys(self, pkcs1_example): |
| secret, public = pkcs1_example |
| |
| private_num = rsa.RSAPrivateNumbers( |
| p=secret["p"], |
| q=secret["q"], |
| d=secret["private_exponent"], |
| dmp1=secret["dmp1"], |
| dmq1=secret["dmq1"], |
| iqmp=secret["iqmp"], |
| public_numbers=rsa.RSAPublicNumbers( |
| e=secret["public_exponent"], |
| n=secret["modulus"] |
| ) |
| ) |
| _check_rsa_private_numbers(private_num) |
| |
| public_num = rsa.RSAPublicNumbers( |
| e=public["public_exponent"], |
| n=public["modulus"] |
| ) |
| assert public_num |
| |
| public_num2 = private_num.public_numbers |
| assert public_num2 |
| |
| 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() |
| |
| with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): |
| rsa.generate_private_key(65537, 2048, pretend_backend) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSASignature(object): |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| @pytest.mark.parametrize( |
| "pkcs1_example", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs1v15sign-vectors.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_pkcs1v15_signing(self, pkcs1_example, backend): |
| private, public, example = pkcs1_example |
| private_key = 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) |
| signature = private_key.sign( |
| binascii.unhexlify(example["message"]), |
| padding.PKCS1v15(), |
| hashes.SHA1() |
| ) |
| assert binascii.hexlify(signature) == example["signature"] |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| @pytest.mark.parametrize( |
| "pkcs1_example", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "pss-vect.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_pss_signing(self, pkcs1_example, backend): |
| private, public, example = pkcs1_example |
| private_key = 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) |
| public_key = rsa.RSAPublicNumbers( |
| e=public["public_exponent"], |
| n=public["modulus"] |
| ).public_key(backend) |
| signature = private_key.sign( |
| binascii.unhexlify(example["message"]), |
| padding.PSS( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA1() |
| ) |
| assert len(signature) == math.ceil(private_key.key_size / 8.0) |
| # PSS signatures contain randomness so we can't do an exact |
| # signature check. Instead we'll verify that the signature created |
| # successfully verifies. |
| public_key.verify( |
| signature, |
| binascii.unhexlify(example["message"]), |
| padding.PSS( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA1(), |
| ) |
| |
| @pytest.mark.parametrize( |
| "hash_alg", |
| [hashes.SHA224(), hashes.SHA256(), hashes.SHA384(), hashes.SHA512()] |
| ) |
| def test_pss_signing_sha2(self, hash_alg, backend): |
| _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 |
| ) |
| 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: ( |
| backend.hash_supported(hashes.SHA512()) and |
| backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ) |
| ), |
| skip_message="Does not support SHA512." |
| ) |
| def test_pss_minimum_key_size_for_digest(self, backend): |
| private_key = RSA_KEY_522.private_key(backend) |
| private_key.sign( |
| b"no failure", |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA512() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.hash_supported(hashes.SHA512()), |
| skip_message="Does not support SHA512." |
| ) |
| 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.sign( |
| b"msg", |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA512() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| def test_pss_signing_salt_length_too_long(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with pytest.raises(ValueError): |
| 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( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_use_after_finalize(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with pytest.warns(CryptographyDeprecationWarning): |
| signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1()) |
| signer.update(b"sign me") |
| signer.finalize() |
| with pytest.raises(AlreadyFinalized): |
| signer.finalize() |
| with pytest.raises(AlreadyFinalized): |
| signer.update(b"more data") |
| |
| def test_unsupported_padding(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): |
| 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.sign(b"msg", "notpadding", hashes.SHA1()) |
| |
| @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_pss_mgf(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): |
| private_key.sign( |
| b"msg", |
| padding.PSS( |
| mgf=DummyMGF(), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA1() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_pkcs1_digest_too_large_for_key_size(self, backend): |
| private_key = RSA_KEY_599.private_key(backend) |
| with pytest.raises(ValueError): |
| private_key.sign( |
| b"failure coming", |
| padding.PKCS1v15(), |
| hashes.SHA512() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_pkcs1_minimum_key_size(self, backend): |
| private_key = RSA_KEY_745.private_key(backend) |
| private_key.sign( |
| b"no failure", |
| padding.PKCS1v15(), |
| hashes.SHA512() |
| ) |
| |
| 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) |
| class TestRSAVerification(object): |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| @pytest.mark.parametrize( |
| "pkcs1_example", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs1v15sign-vectors.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_pkcs1v15_verification(self, pkcs1_example, backend): |
| private, public, example = pkcs1_example |
| public_key = rsa.RSAPublicNumbers( |
| e=public["public_exponent"], |
| n=public["modulus"] |
| ).public_key(backend) |
| public_key.verify( |
| binascii.unhexlify(example["signature"]), |
| binascii.unhexlify(example["message"]), |
| padding.PKCS1v15(), |
| hashes.SHA1() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_invalid_pkcs1v15_signature_wrong_data(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| signature = private_key.sign( |
| b"sign me", padding.PKCS1v15(), hashes.SHA1() |
| ) |
| with pytest.raises(InvalidSignature): |
| 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( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_invalid_pkcs1v15_signature_wrong_key(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| private_key2 = RSA_KEY_512_ALT.private_key(backend) |
| public_key = private_key2.public_key() |
| msg = b"sign me" |
| signature = private_key.sign(msg, padding.PKCS1v15(), hashes.SHA1()) |
| with pytest.raises(InvalidSignature): |
| public_key.verify( |
| signature, msg, padding.PKCS1v15(), hashes.SHA1() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=20 |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| @pytest.mark.parametrize( |
| "pkcs1_example", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "pss-vect.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_pss_verification(self, pkcs1_example, backend): |
| private, public, example = pkcs1_example |
| public_key = rsa.RSAPublicNumbers( |
| e=public["public_exponent"], |
| n=public["modulus"] |
| ).public_key(backend) |
| public_key.verify( |
| binascii.unhexlify(example["signature"]), |
| binascii.unhexlify(example["message"]), |
| padding.PSS( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| salt_length=20 |
| ), |
| hashes.SHA1() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| def test_invalid_pss_signature_wrong_data(self, backend): |
| public_key = rsa.RSAPublicNumbers( |
| n=int( |
| b"dffc2137d5e810cde9e4b4612f5796447218bab913b3fa98bdf7982e4fa6" |
| b"ec4d6653ef2b29fb1642b095befcbea6decc178fb4bed243d3c3592c6854" |
| b"6af2d3f3", 16 |
| ), |
| e=65537 |
| ).public_key(backend) |
| signature = binascii.unhexlify( |
| b"0e68c3649df91c5bc3665f96e157efa75b71934aaa514d91e94ca8418d100f45" |
| b"6f05288e58525f99666bab052adcffdf7186eb40f583bd38d98c97d3d524808b" |
| ) |
| with pytest.raises(InvalidSignature): |
| 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( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| def test_invalid_pss_signature_wrong_key(self, backend): |
| signature = binascii.unhexlify( |
| b"3a1880165014ba6eb53cc1449d13e5132ebcc0cfd9ade6d7a2494a0503bd0826" |
| b"f8a46c431e0d7be0ca3e453f8b2b009e2733764da7927cc6dbe7a021437a242e" |
| ) |
| public_key = rsa.RSAPublicNumbers( |
| n=int( |
| b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" |
| b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" |
| b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" |
| b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" |
| b"030d3581e13522e1", 16 |
| ), |
| e=65537 |
| ).public_key(backend) |
| with pytest.raises(InvalidSignature): |
| 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( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): |
| signature = binascii.unhexlify( |
| b"cb43bde4f7ab89eb4a79c6e8dd67e0d1af60715da64429d90c716a490b799c29" |
| b"194cf8046509c6ed851052367a74e2e92d9b38947ed74332acb115a03fcc0222" |
| ) |
| public_key = rsa.RSAPublicNumbers( |
| n=int( |
| b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" |
| b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" |
| b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" |
| b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" |
| b"030d3581e13522", 16 |
| ), |
| e=65537 |
| ).public_key(backend) |
| with pytest.raises(InvalidSignature): |
| 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( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_use_after_finalize(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| 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): |
| verifier.verify() |
| with pytest.raises(AlreadyFinalized): |
| verifier.update(b"more data") |
| |
| def test_unsupported_padding(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): |
| 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.verify(b"sig", b"msg", "notpadding", hashes.SHA1()) |
| |
| @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_pss_mgf(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): |
| public_key.verify( |
| b"sig", |
| b"msg", |
| padding.PSS( |
| mgf=DummyMGF(), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA1() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.hash_supported(hashes.SHA512()), |
| skip_message="Does not support SHA512." |
| ) |
| def test_pss_verify_digest_too_large_for_key_size(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| signature = binascii.unhexlify( |
| b"8b9a3ae9fb3b64158f3476dd8d8a1f1425444e98940e0926378baa9944d219d8" |
| b"534c050ef6b19b1bdc6eb4da422e89161106a6f5b5cc16135b11eb6439b646bd" |
| ) |
| public_key = private_key.public_key() |
| with pytest.raises(ValueError): |
| public_key.verify( |
| signature, |
| b"msg doesn't matter", |
| padding.PSS( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ), |
| hashes.SHA512() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS." |
| ) |
| def test_pss_verify_salt_length_too_long(self, backend): |
| signature = binascii.unhexlify( |
| b"8b9a3ae9fb3b64158f3476dd8d8a1f1425444e98940e0926378baa9944d219d8" |
| b"534c050ef6b19b1bdc6eb4da422e89161106a6f5b5cc16135b11eb6439b646bd" |
| ) |
| public_key = rsa.RSAPublicNumbers( |
| n=int( |
| b"d309e4612809437548b747d7f9eb9cd3340f54fe42bb3f84a36933b0839c" |
| b"11b0c8b7f67e11f7252370161e31159c49c784d4bc41c42a78ce0f0b40a3" |
| b"ca8ffb91", 16 |
| ), |
| e=65537 |
| ).public_key(backend) |
| with pytest.raises(InvalidSignature): |
| 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) |
| class TestRSAPSSMGF1Verification(object): |
| test_rsa_pss_mgf1_sha1 = pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA1()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS using MGF1 with SHA1." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGenPSS_186-2.rsp", |
| "SigGenPSS_186-3.rsp", |
| "SigVerPSS_186-3.rsp", |
| ], |
| hashes.SHA1(), |
| lambda params, hash_alg: padding.PSS( |
| mgf=padding.MGF1( |
| algorithm=hash_alg, |
| ), |
| salt_length=params["salt_length"] |
| ) |
| )) |
| |
| test_rsa_pss_mgf1_sha224 = pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA224()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS using MGF1 with SHA224." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGenPSS_186-2.rsp", |
| "SigGenPSS_186-3.rsp", |
| "SigVerPSS_186-3.rsp", |
| ], |
| hashes.SHA224(), |
| lambda params, hash_alg: padding.PSS( |
| mgf=padding.MGF1( |
| algorithm=hash_alg, |
| ), |
| salt_length=params["salt_length"] |
| ) |
| )) |
| |
| test_rsa_pss_mgf1_sha256 = pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA256()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS using MGF1 with SHA256." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGenPSS_186-2.rsp", |
| "SigGenPSS_186-3.rsp", |
| "SigVerPSS_186-3.rsp", |
| ], |
| hashes.SHA256(), |
| lambda params, hash_alg: padding.PSS( |
| mgf=padding.MGF1( |
| algorithm=hash_alg, |
| ), |
| salt_length=params["salt_length"] |
| ) |
| )) |
| |
| test_rsa_pss_mgf1_sha384 = pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA384()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS using MGF1 with SHA384." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGenPSS_186-2.rsp", |
| "SigGenPSS_186-3.rsp", |
| "SigVerPSS_186-3.rsp", |
| ], |
| hashes.SHA384(), |
| lambda params, hash_alg: padding.PSS( |
| mgf=padding.MGF1( |
| algorithm=hash_alg, |
| ), |
| salt_length=params["salt_length"] |
| ) |
| )) |
| |
| test_rsa_pss_mgf1_sha512 = pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PSS( |
| mgf=padding.MGF1(hashes.SHA512()), |
| salt_length=padding.PSS.MAX_LENGTH |
| ) |
| ), |
| skip_message="Does not support PSS using MGF1 with SHA512." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGenPSS_186-2.rsp", |
| "SigGenPSS_186-3.rsp", |
| "SigVerPSS_186-3.rsp", |
| ], |
| hashes.SHA512(), |
| lambda params, hash_alg: padding.PSS( |
| mgf=padding.MGF1( |
| algorithm=hash_alg, |
| ), |
| salt_length=params["salt_length"] |
| ) |
| )) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSAPKCS1Verification(object): |
| test_rsa_pkcs1v15_verify_sha1 = pytest.mark.supported( |
| only_if=lambda backend: ( |
| backend.hash_supported(hashes.SHA1()) and |
| backend.rsa_padding_supported(padding.PKCS1v15()) |
| ), |
| skip_message="Does not support SHA1 and PKCS1v1.5." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGen15_186-2.rsp", |
| "SigGen15_186-3.rsp", |
| "SigVer15_186-3.rsp", |
| ], |
| hashes.SHA1(), |
| lambda params, hash_alg: padding.PKCS1v15() |
| )) |
| |
| test_rsa_pkcs1v15_verify_sha224 = pytest.mark.supported( |
| only_if=lambda backend: ( |
| backend.hash_supported(hashes.SHA224()) and |
| backend.rsa_padding_supported(padding.PKCS1v15()) |
| ), |
| skip_message="Does not support SHA224 and PKCS1v1.5." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGen15_186-2.rsp", |
| "SigGen15_186-3.rsp", |
| "SigVer15_186-3.rsp", |
| ], |
| hashes.SHA224(), |
| lambda params, hash_alg: padding.PKCS1v15() |
| )) |
| |
| test_rsa_pkcs1v15_verify_sha256 = pytest.mark.supported( |
| only_if=lambda backend: ( |
| backend.hash_supported(hashes.SHA256()) and |
| backend.rsa_padding_supported(padding.PKCS1v15()) |
| ), |
| skip_message="Does not support SHA256 and PKCS1v1.5." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGen15_186-2.rsp", |
| "SigGen15_186-3.rsp", |
| "SigVer15_186-3.rsp", |
| ], |
| hashes.SHA256(), |
| lambda params, hash_alg: padding.PKCS1v15() |
| )) |
| |
| test_rsa_pkcs1v15_verify_sha384 = pytest.mark.supported( |
| only_if=lambda backend: ( |
| backend.hash_supported(hashes.SHA384()) and |
| backend.rsa_padding_supported(padding.PKCS1v15()) |
| ), |
| skip_message="Does not support SHA384 and PKCS1v1.5." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGen15_186-2.rsp", |
| "SigGen15_186-3.rsp", |
| "SigVer15_186-3.rsp", |
| ], |
| hashes.SHA384(), |
| lambda params, hash_alg: padding.PKCS1v15() |
| )) |
| |
| test_rsa_pkcs1v15_verify_sha512 = pytest.mark.supported( |
| only_if=lambda backend: ( |
| backend.hash_supported(hashes.SHA512()) and |
| backend.rsa_padding_supported(padding.PKCS1v15()) |
| ), |
| skip_message="Does not support SHA512 and PKCS1v1.5." |
| )(generate_rsa_verification_test( |
| load_rsa_nist_vectors, |
| os.path.join("asymmetric", "RSA", "FIPS_186-2"), |
| [ |
| "SigGen15_186-2.rsp", |
| "SigGen15_186-3.rsp", |
| "SigVer15_186-3.rsp", |
| ], |
| hashes.SHA512(), |
| lambda params, hash_alg: padding.PKCS1v15() |
| )) |
| |
| |
| 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( |
| mgf=padding.MGF1( |
| hashes.SHA1() |
| ), |
| salt_length=b"not_a_length" |
| ) |
| |
| def test_invalid_salt_length_negative_integer(self): |
| with pytest.raises(ValueError): |
| padding.PSS( |
| mgf=padding.MGF1( |
| hashes.SHA1() |
| ), |
| salt_length=-1 |
| ) |
| |
| def test_valid_pss_parameters(self): |
| algorithm = hashes.SHA1() |
| salt_length = algorithm.digest_size |
| mgf = padding.MGF1(algorithm) |
| pss = padding.PSS(mgf=mgf, salt_length=salt_length) |
| assert pss._mgf == mgf |
| assert pss._salt_length == salt_length |
| |
| def test_valid_pss_parameters_maximum(self): |
| algorithm = hashes.SHA1() |
| mgf = padding.MGF1(algorithm) |
| pss = padding.PSS(mgf=mgf, salt_length=padding.PSS.MAX_LENGTH) |
| assert pss._mgf == mgf |
| assert pss._salt_length == padding.PSS.MAX_LENGTH |
| |
| |
| class TestMGF1(object): |
| def test_invalid_hash_algorithm(self): |
| with pytest.raises(TypeError): |
| padding.MGF1(b"not_a_hash") |
| |
| def test_valid_mgf1_parameters(self): |
| algorithm = hashes.SHA1() |
| mgf = padding.MGF1(algorithm) |
| assert mgf._algorithm == algorithm |
| |
| |
| class TestOAEP(object): |
| def test_invalid_algorithm(self): |
| mgf = padding.MGF1(hashes.SHA1()) |
| with pytest.raises(TypeError): |
| padding.OAEP( |
| mgf=mgf, |
| algorithm=b"", |
| label=None |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSADecryption(object): |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| @pytest.mark.parametrize( |
| "vector", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs1v15crypt-vectors.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_decrypt_pkcs1v15_vectors(self, vector, backend): |
| private, public, example = 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) |
| ciphertext = binascii.unhexlify(example["encryption"]) |
| assert len(ciphertext) == math.ceil(skey.key_size / 8.0) |
| 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, DummyAsymmetricPadding()) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_decrypt_invalid_decrypt(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with pytest.raises(ValueError): |
| private_key.decrypt( |
| b"\x00" * 64, |
| padding.PKCS1v15() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_decrypt_ciphertext_too_large(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| with pytest.raises(ValueError): |
| private_key.decrypt( |
| b"\x00" * 65, |
| padding.PKCS1v15() |
| ) |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| def test_decrypt_ciphertext_too_small(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| ct = binascii.unhexlify( |
| b"50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b80804f1" |
| b"69d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d8ea0" |
| ) |
| with pytest.raises(ValueError): |
| private_key.decrypt( |
| ct, |
| padding.PKCS1v15() |
| ) |
| |
| @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." |
| ) |
| @pytest.mark.parametrize( |
| "vector", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "oaep-vect.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_decrypt_oaep_vectors(self, vector, backend): |
| private, public, example = 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=hashes.SHA1()), |
| algorithm=hashes.SHA1(), |
| 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.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): |
| private_key.decrypt( |
| b"0" * 64, |
| padding.OAEP( |
| mgf=DummyMGF(), |
| algorithm=hashes.SHA1(), |
| label=None |
| ) |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSAEncryption(object): |
| @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." |
| ) |
| @pytest.mark.parametrize( |
| ("key_data", "pad"), |
| itertools.product( |
| (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), |
| [ |
| padding.OAEP( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| algorithm=hashes.SHA1(), |
| label=None |
| ) |
| ] |
| ) |
| ) |
| def test_rsa_encrypt_oaep(self, key_data, pad, backend): |
| private_key = key_data.private_key(backend) |
| pt = b"encrypt me!" |
| 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) |
| 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) == math.ceil(public_key.key_size / 8.0) |
| recovered_pt = private_key.decrypt(ct, pad) |
| assert recovered_pt == pt |
| |
| @pytest.mark.supported( |
| only_if=lambda backend: backend.rsa_padding_supported( |
| padding.PKCS1v15() |
| ), |
| skip_message="Does not support PKCS1v1.5." |
| ) |
| @pytest.mark.parametrize( |
| ("key_data", "pad"), |
| itertools.product( |
| (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), |
| [padding.PKCS1v15()] |
| ) |
| ) |
| def test_rsa_encrypt_pkcs1v15(self, key_data, pad, backend): |
| private_key = key_data.private_key(backend) |
| pt = b"encrypt me!" |
| 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) |
| recovered_pt = private_key.decrypt(ct, pad) |
| assert recovered_pt == pt |
| |
| @pytest.mark.parametrize( |
| ("key_data", "pad"), |
| itertools.product( |
| (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), |
| ( |
| padding.OAEP( |
| mgf=padding.MGF1(algorithm=hashes.SHA1()), |
| algorithm=hashes.SHA1(), |
| label=None |
| ), |
| padding.PKCS1v15() |
| ) |
| ) |
| ) |
| def test_rsa_encrypt_key_too_small(self, key_data, pad, backend): |
| private_key = key_data.private_key(backend) |
| public_key = private_key.public_key() |
| # Slightly smaller than the key size but not enough for padding. |
| with pytest.raises(ValueError): |
| public_key.encrypt( |
| b"\x00" * (private_key.key_size // 8 - 1), |
| pad |
| ) |
| |
| # Larger than the key size. |
| with pytest.raises(ValueError): |
| public_key.encrypt( |
| b"\x00" * (private_key.key_size // 8 + 5), |
| pad |
| ) |
| |
| def test_unsupported_padding(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): |
| public_key.encrypt(b"somedata", DummyAsymmetricPadding()) |
| with pytest.raises(TypeError): |
| public_key.encrypt(b"somedata", padding=object()) |
| |
| def test_unsupported_oaep_mgf(self, backend): |
| private_key = RSA_KEY_512.private_key(backend) |
| public_key = private_key.public_key() |
| |
| with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): |
| public_key.encrypt( |
| b"ciphertext", |
| padding.OAEP( |
| mgf=DummyMGF(), |
| algorithm=hashes.SHA1(), |
| label=None |
| ) |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| class TestRSANumbers(object): |
| def test_rsa_public_numbers(self): |
| public_numbers = rsa.RSAPublicNumbers(e=1, n=15) |
| assert public_numbers.e == 1 |
| assert public_numbers.n == 15 |
| |
| def test_rsa_private_numbers(self): |
| public_numbers = rsa.RSAPublicNumbers(e=1, n=15) |
| private_numbers = rsa.RSAPrivateNumbers( |
| p=3, |
| q=5, |
| d=1, |
| dmp1=1, |
| dmq1=1, |
| iqmp=2, |
| public_numbers=public_numbers |
| ) |
| |
| assert private_numbers.p == 3 |
| assert private_numbers.q == 5 |
| assert private_numbers.d == 1 |
| assert private_numbers.dmp1 == 1 |
| assert private_numbers.dmq1 == 1 |
| assert private_numbers.iqmp == 2 |
| assert private_numbers.public_numbers == public_numbers |
| |
| def test_rsa_private_numbers_create_key(self, backend): |
| private_key = RSA_KEY_1024.private_key(backend) |
| assert private_key |
| |
| def test_rsa_public_numbers_create_key(self, backend): |
| public_key = RSA_KEY_1024.public_numbers.public_key(backend) |
| assert public_key |
| |
| def test_public_numbers_invalid_types(self): |
| with pytest.raises(TypeError): |
| rsa.RSAPublicNumbers(e=None, n=15) |
| |
| with pytest.raises(TypeError): |
| rsa.RSAPublicNumbers(e=1, n=None) |
| |
| @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=p, q=q, |
| d=d, |
| dmp1=dmp1, |
| dmq1=dmq1, |
| iqmp=iqmp, |
| public_numbers=public_numbers |
| ) |
| |
| @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. |
| |
| with pytest.raises(ValueError): |
| rsa.RSAPublicNumbers(e=e, n=n).public_key(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. |
| |
| with pytest.raises(ValueError): |
| rsa.RSAPrivateNumbers( |
| p=p, |
| q=q, |
| d=d, |
| dmp1=dmp1, |
| dmq1=dmq1, |
| iqmp=iqmp, |
| public_numbers=rsa.RSAPublicNumbers( |
| e=e, |
| n=n |
| ) |
| ).private_key(backend) |
| |
| def test_public_number_repr(self): |
| num = RSAPublicNumbers(1, 1) |
| assert repr(num) == "<RSAPublicNumbers(e=1, n=1)>" |
| |
| |
| class TestRSANumbersEquality(object): |
| def test_public_numbers_eq(self): |
| num = RSAPublicNumbers(1, 2) |
| num2 = RSAPublicNumbers(1, 2) |
| assert num == num2 |
| |
| def test_public_numbers_ne(self): |
| num = RSAPublicNumbers(1, 2) |
| assert num != RSAPublicNumbers(2, 2) |
| assert num != RSAPublicNumbers(1, 3) |
| assert num != object() |
| |
| def test_private_numbers_eq(self): |
| pub = RSAPublicNumbers(1, 2) |
| num = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, pub) |
| pub2 = RSAPublicNumbers(1, 2) |
| num2 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, pub2) |
| assert num == num2 |
| |
| def test_private_numbers_ne(self): |
| pub = RSAPublicNumbers(1, 2) |
| num = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, pub) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 3, 4, 5, 7, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 3, 4, 4, 6, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 3, 5, 5, 6, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 4, 4, 5, 6, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 3, 3, 4, 5, 6, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 2, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 3, 4, 5, 6, RSAPublicNumbers(2, 2) |
| ) |
| assert num != RSAPrivateNumbers( |
| 1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 3) |
| ) |
| 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( |
| "vector", |
| _flatten_pkcs1_examples(load_vectors_from_file( |
| os.path.join( |
| "asymmetric", "RSA", "pkcs1v15crypt-vectors.txt"), |
| load_pkcs1_vectors |
| )) |
| ) |
| def test_recover_prime_factors(self, vector): |
| private, public, example = vector |
| p, q = rsa.rsa_recover_prime_factors( |
| private["modulus"], |
| private["public_exponent"], |
| 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 |
| # 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): |
| rsa.rsa_recover_prime_factors(34, 3, 7) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) |
| class TestRSAPrivateKeySerialization(object): |
| @pytest.mark.parametrize( |
| ("fmt", "password"), |
| itertools.product( |
| [ |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.PrivateFormat.PKCS8 |
| ], |
| [ |
| b"s", |
| b"longerpassword", |
| b"!*$&(@#$*&($T@%_somesymbols", |
| b"\x01" * 1000, |
| ] |
| ) |
| ) |
| def test_private_bytes_encrypted_pem(self, backend, fmt, password): |
| key = RSA_KEY_2048.private_key(backend) |
| serialized = key.private_bytes( |
| serialization.Encoding.PEM, |
| fmt, |
| serialization.BestAvailableEncryption(password) |
| ) |
| loaded_key = serialization.load_pem_private_key( |
| serialized, password, backend |
| ) |
| loaded_priv_num = loaded_key.private_numbers() |
| 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), |
| (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"], |
| [serialization.PrivateFormat.PKCS8, b"longerpassword"], |
| [serialization.PrivateFormat.PKCS8, b"!*$&(@#$*&($T@%_somesymbol"], |
| [serialization.PrivateFormat.PKCS8, b"\x01" * 1000] |
| ] |
| ) |
| def test_private_bytes_encrypted_der(self, backend, fmt, password): |
| key = RSA_KEY_2048.private_key(backend) |
| serialized = key.private_bytes( |
| serialization.Encoding.DER, |
| fmt, |
| serialization.BestAvailableEncryption(password) |
| ) |
| loaded_key = serialization.load_der_private_key( |
| serialized, password, backend |
| ) |
| loaded_priv_num = loaded_key.private_numbers() |
| priv_num = key.private_numbers() |
| assert loaded_priv_num == priv_num |
| |
| @pytest.mark.parametrize( |
| ("encoding", "fmt", "loader_func"), |
| [ |
| [ |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.load_pem_private_key |
| ], |
| [ |
| serialization.Encoding.DER, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.load_der_private_key |
| ], |
| [ |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.PKCS8, |
| serialization.load_pem_private_key |
| ], |
| [ |
| serialization.Encoding.DER, |
| serialization.PrivateFormat.PKCS8, |
| serialization.load_der_private_key |
| ], |
| ] |
| ) |
| def test_private_bytes_unencrypted(self, backend, encoding, fmt, |
| loader_func): |
| key = RSA_KEY_2048.private_key(backend) |
| serialized = key.private_bytes( |
| encoding, fmt, serialization.NoEncryption() |
| ) |
| loaded_key = loader_func(serialized, None, backend) |
| loaded_priv_num = loaded_key.private_numbers() |
| priv_num = key.private_numbers() |
| assert loaded_priv_num == priv_num |
| |
| @pytest.mark.parametrize( |
| ("key_path", "encoding", "loader_func"), |
| [ |
| [ |
| os.path.join( |
| "asymmetric", |
| "Traditional_OpenSSL_Serialization", |
| "testrsa.pem" |
| ), |
| serialization.Encoding.PEM, |
| serialization.load_pem_private_key |
| ], |
| [ |
| os.path.join("asymmetric", "DER_Serialization", "testrsa.der"), |
| serialization.Encoding.DER, |
| serialization.load_der_private_key |
| ], |
| ] |
| ) |
| def test_private_bytes_traditional_openssl_unencrypted( |
| self, backend, key_path, encoding, loader_func |
| ): |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| key = loader_func(key_bytes, None, backend) |
| serialized = key.private_bytes( |
| encoding, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.NoEncryption() |
| ) |
| assert serialized == key_bytes |
| |
| def test_private_bytes_traditional_der_encrypted_invalid(self, backend): |
| key = RSA_KEY_2048.private_key(backend) |
| with pytest.raises(ValueError): |
| key.private_bytes( |
| serialization.Encoding.DER, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.BestAvailableEncryption(b"password") |
| ) |
| |
| def test_private_bytes_invalid_encoding(self, backend): |
| key = RSA_KEY_2048.private_key(backend) |
| with pytest.raises(TypeError): |
| key.private_bytes( |
| "notencoding", |
| serialization.PrivateFormat.PKCS8, |
| serialization.NoEncryption() |
| ) |
| |
| def test_private_bytes_invalid_format(self, backend): |
| key = RSA_KEY_2048.private_key(backend) |
| with pytest.raises(TypeError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| "invalidformat", |
| serialization.NoEncryption() |
| ) |
| |
| def test_private_bytes_invalid_encryption_algorithm(self, backend): |
| key = RSA_KEY_2048.private_key(backend) |
| with pytest.raises(TypeError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| "notanencalg" |
| ) |
| |
| def test_private_bytes_unsupported_encryption_type(self, backend): |
| key = RSA_KEY_2048.private_key(backend) |
| with pytest.raises(ValueError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| DummyKeySerializationEncryption() |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=RSABackend) |
| @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) |
| class TestRSAPEMPublicKeySerialization(object): |
| @pytest.mark.parametrize( |
| ("key_path", "loader_func", "encoding", "format"), |
| [ |
| ( |
| os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"), |
| serialization.load_pem_public_key, |
| serialization.Encoding.PEM, |
| serialization.PublicFormat.PKCS1, |
| ), ( |
| os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.der"), |
| serialization.load_der_public_key, |
| serialization.Encoding.DER, |
| serialization.PublicFormat.PKCS1, |
| ), ( |
| os.path.join("asymmetric", "PKCS8", "unenc-rsa-pkcs8.pub.pem"), |
| serialization.load_pem_public_key, |
| serialization.Encoding.PEM, |
| serialization.PublicFormat.SubjectPublicKeyInfo, |
| ), ( |
| os.path.join( |
| "asymmetric", |
| "DER_Serialization", |
| "unenc-rsa-pkcs8.pub.der" |
| ), |
| serialization.load_der_public_key, |
| serialization.Encoding.DER, |
| serialization.PublicFormat.SubjectPublicKeyInfo, |
| ) |
| ] |
| ) |
| def test_public_bytes_match(self, key_path, loader_func, encoding, format, |
| backend): |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| key = loader_func(key_bytes, 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() |
| 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() |
| 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) |