From b5936a7817b535422574a3627473b13a5f21e5d8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 13 Mar 2014 21:03:00 -0400 Subject: add RSA PSS verification support --- tests/hazmat/primitives/test_rsa.py | 249 +++++++++++++++++++++++++++++++++++- tests/hazmat/primitives/utils.py | 33 +++++ 2 files changed, 281 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 0e88bb7f..8e9637fd 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -25,7 +25,10 @@ from cryptography.exceptions import UnsupportedInterface from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import rsa, padding -from ...utils import load_pkcs1_vectors, load_vectors_from_file +from .utils import generate_rsa_pss_test +from ...utils import ( + load_pkcs1_vectors, load_vectors_from_file, load_rsa_nist_vectors +) @utils.register_interface(interfaces.AsymmetricPadding) @@ -33,6 +36,10 @@ class DummyPadding(object): name = "UNSUPPORTED-PADDING" +class DummyMGF(object): + pass + + def _modinv(e, m): """ Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 @@ -530,6 +537,122 @@ class TestRSAVerification(object): with pytest.raises(exceptions.InvalidSignature): verifier.verify() + @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.RSAPublicKey( + public_exponent=public["public_exponent"], + modulus=public["modulus"] + ) + verifier = public_key.verifier( + binascii.unhexlify(example["signature"]), + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(binascii.unhexlify(example["message"])) + verifier.verify() + + def test_invalid_pss_signature_wrong_data(self, backend): + public_key = rsa.RSAPublicKey( + modulus=int( + b"dffc2137d5e810cde9e4b4612f5796447218bab913b3fa98bdf7982e4fa6" + b"ec4d6653ef2b29fb1642b095befcbea6decc178fb4bed243d3c3592c6854" + b"6af2d3f3", 16 + ), + public_exponent=65537 + ) + signature = binascii.unhexlify( + b"0e68c3649df91c5bc3665f96e157efa75b71934aaa514d91e94ca8418d100f45" + b"6f05288e58525f99666bab052adcffdf7186eb40f583bd38d98c97d3d524808b" + ) + verifier = public_key.verifier( + signature, + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(b"incorrect data") + with pytest.raises(exceptions.InvalidSignature): + verifier.verify() + + def test_invalid_pss_signature_wrong_key(self, backend): + signature = binascii.unhexlify( + b"3a1880165014ba6eb53cc1449d13e5132ebcc0cfd9ade6d7a2494a0503bd0826" + b"f8a46c431e0d7be0ca3e453f8b2b009e2733764da7927cc6dbe7a021437a242e" + ) + public_key = rsa.RSAPublicKey( + modulus=int( + b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" + b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" + b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" + b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" + b"030d3581e13522e1", 16 + ), + public_exponent=65537 + ) + verifier = public_key.verifier( + signature, + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(b"sign me") + with pytest.raises(exceptions.InvalidSignature): + verifier.verify() + + def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): + signature = binascii.unhexlify( + b"cb43bde4f7ab89eb4a79c6e8dd67e0d1af60715da64429d90c716a490b799c29" + b"194cf8046509c6ed851052367a74e2e92d9b38947ed74332acb115a03fcc0222" + ) + public_key = rsa.RSAPublicKey( + modulus=int( + b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" + b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" + b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" + b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" + b"030d3581e13522", 16 + ), + public_exponent=65537 + ) + verifier = public_key.verifier( + signature, + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(b"sign me") + with pytest.raises(exceptions.InvalidSignature): + verifier.verify() + def test_use_after_finalize(self, backend): private_key = rsa.RSAPrivateKey.generate( public_exponent=65537, @@ -583,6 +706,130 @@ class TestRSAVerification(object): public_key.verifier( b"foo", padding.PKCS1v15(), hashes.SHA256(), pretend_backend) + def test_unsupported_pss_mgf(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + public_key = private_key.public_key() + with pytest.raises(TypeError): + public_key.verifier(b"sig", padding.PSS(mgf=DummyMGF()), + hashes.SHA1(), backend) + + def test_pss_verify_salt_length_too_long(self, backend): + signature = binascii.unhexlify( + b"8b9a3ae9fb3b64158f3476dd8d8a1f1425444e98940e0926378baa9944d219d8" + b"534c050ef6b19b1bdc6eb4da422e89161106a6f5b5cc16135b11eb6439b646bd" + ) + public_key = rsa.RSAPublicKey( + modulus=int( + b"d309e4612809437548b747d7f9eb9cd3340f54fe42bb3f84a36933b0839c" + b"11b0c8b7f67e11f7252370161e31159c49c784d4bc41c42a78ce0f0b40a3" + b"ca8ffb91", 16 + ), + public_exponent=65537 + ) + verifier = public_key.verifier( + signature, + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=1000000 + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(b"sign me") + with pytest.raises(exceptions.InvalidSignature): + verifier.verify() + + +@pytest.mark.supported( + only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA1()), + skip_message="Does not support SHA1 with MGF1." +) +@pytest.mark.rsa +class TestRSAPSSMGF1VerificationSHA1(object): + test_rsa_pss_mgf1_sha1 = generate_rsa_pss_test( + load_rsa_nist_vectors, + os.path.join("asymmetric", "RSA", "FIPS_186-2"), + [ + "SigGenPSS_186-2.rsp", + "SigGenPSS_186-3.rsp", + ], + b"SHA1" + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA224()), + skip_message="Does not support SHA224 with MGF1." +) +@pytest.mark.rsa +class TestRSAPSSMGF1VerificationSHA224(object): + test_rsa_pss_mgf1_sha224 = generate_rsa_pss_test( + load_rsa_nist_vectors, + os.path.join("asymmetric", "RSA", "FIPS_186-2"), + [ + "SigGenPSS_186-2.rsp", + "SigGenPSS_186-3.rsp", + ], + b"SHA224" + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA256()), + skip_message="Does not support SHA256 with MGF1." +) +@pytest.mark.rsa +class TestRSAPSSMGF1VerificationSHA256(object): + test_rsa_pss_mgf1_sha256 = generate_rsa_pss_test( + load_rsa_nist_vectors, + os.path.join("asymmetric", "RSA", "FIPS_186-2"), + [ + "SigGenPSS_186-2.rsp", + "SigGenPSS_186-3.rsp", + ], + b"SHA256" + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA384()), + skip_message="Does not support SHA384 with MGF1." +) +@pytest.mark.rsa +class TestRSAPSSMGF1VerificationSHA384(object): + test_rsa_pss_mgf1_sha384 = generate_rsa_pss_test( + load_rsa_nist_vectors, + os.path.join("asymmetric", "RSA", "FIPS_186-2"), + [ + "SigGenPSS_186-2.rsp", + "SigGenPSS_186-3.rsp", + ], + b"SHA384" + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA512()), + skip_message="Does not support SHA512 with MGF1." +) +@pytest.mark.rsa +class TestRSAPSSMGF1VerificationSHA512(object): + test_rsa_pss_mgf1_sha512 = generate_rsa_pss_test( + load_rsa_nist_vectors, + os.path.join("asymmetric", "RSA", "FIPS_186-2"), + [ + "SigGenPSS_186-2.rsp", + "SigGenPSS_186-3.rsp", + ], + b"SHA512" + ) + class TestMGF1(object): def test_invalid_hash_algorithm(self): diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index f0a00319..38f67046 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -21,6 +21,7 @@ import itertools import pytest from cryptography.hazmat.primitives import hashes, hmac +from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.kdf.hkdf import HKDF @@ -373,3 +374,35 @@ def generate_hkdf_test(param_loader, path, file_names, algorithm): hkdf_test(backend, algorithm, params) return test_hkdf + + +def generate_rsa_pss_test(param_loader, path, file_names, hash_name): + all_params = _load_all_params(path, file_names, param_loader) + all_params = [i for i in all_params if i["algorithm"] == hash_name] + + @pytest.mark.parametrize("params", all_params) + def test_rsa_pss(self, backend, params): + rsa_pss_test(backend, params) + + return test_rsa_pss + + +def rsa_pss_test(backend, params): + public_key = rsa.RSAPublicKey( + public_exponent=params["public_exponent"], + modulus=params["modulus"] + ) + hash_cls = getattr(hashes, params["algorithm"].decode("utf8")) + verifier = public_key.verifier( + binascii.unhexlify(params["s"]), + padding.PSS( + mgf=padding.MGF1( + algorithm=hash_cls(), + salt_length=params["salt_length"] + ) + ), + hash_cls(), + backend + ) + verifier.update(binascii.unhexlify(params["msg"])) + verifier.verify() -- cgit v1.2.3 From 24b063da632f89a5ca9693921c59a9a598200d48 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 15 Mar 2014 21:18:16 -0400 Subject: cover a missing line for older openssl --- tests/hazmat/backends/test_openssl.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'tests') diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 599d1531..c5d0a013 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -21,6 +21,7 @@ from cryptography.exceptions import ( ) from cryptography.hazmat.backends.openssl.backend import backend, Backend from cryptography.hazmat.primitives import interfaces, hashes +from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC @@ -137,6 +138,30 @@ class TestOpenSSL(object): with pytest.raises(UnsupportedHash): backend.derive_pbkdf2_hmac(hashes.SHA256(), 10, b"", 1000, b"") + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000100f, + reason="Requires an older OpenSSL. Must be < 1.0.1" + ) + def test_non_sha1_pss_mgf1_hash_algorithm_on_old_openssl(self): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + public_key = private_key.public_key() + with pytest.raises(UnsupportedHash): + public_key.verifier( + b"sig", + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA256(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + # This test is not in the next class because to check if it's really # default we don't want to run the setup_method before it def test_osrandom_engine_is_default(self): -- cgit v1.2.3 From 762014e1a48606447a43187f522196a35628de1b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 16 Mar 2014 17:47:22 -0400 Subject: fix indentation --- tests/hazmat/primitives/utils.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 38f67046..884ce00c 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -388,21 +388,21 @@ def generate_rsa_pss_test(param_loader, path, file_names, hash_name): def rsa_pss_test(backend, params): - public_key = rsa.RSAPublicKey( - public_exponent=params["public_exponent"], - modulus=params["modulus"] - ) - hash_cls = getattr(hashes, params["algorithm"].decode("utf8")) - verifier = public_key.verifier( - binascii.unhexlify(params["s"]), - padding.PSS( - mgf=padding.MGF1( - algorithm=hash_cls(), - salt_length=params["salt_length"] - ) - ), - hash_cls(), - backend - ) - verifier.update(binascii.unhexlify(params["msg"])) - verifier.verify() + public_key = rsa.RSAPublicKey( + public_exponent=params["public_exponent"], + modulus=params["modulus"] + ) + hash_cls = getattr(hashes, params["algorithm"].decode("utf8")) + verifier = public_key.verifier( + binascii.unhexlify(params["s"]), + padding.PSS( + mgf=padding.MGF1( + algorithm=hash_cls(), + salt_length=params["salt_length"] + ) + ), + hash_cls(), + backend + ) + verifier.update(binascii.unhexlify(params["msg"])) + verifier.verify() -- cgit v1.2.3 From dde593335808cae97704f0036e92a220a1653075 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 16 Mar 2014 17:57:20 -0400 Subject: change rsa nist loader to use strings for algorithm name --- tests/test_utils.py | 6 +++--- tests/utils.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test_utils.py b/tests/test_utils.py index 433dab04..6d2ef362 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1075,7 +1075,7 @@ def test_load_rsa_nist_vectors(): "modulus": int("bcb47b2e0dafcba81ff2a2b5cb115ca7e757184c9d72bcdcda" "707a146b3b4e29989d", 16), "public_exponent": 65537, - "algorithm": b"SHA1", + "algorithm": "SHA1", "salt_length": 20, "msg": b"1248f62a4389f42f7b4bb131053d6c88a994db2075b912ccbe3ea7dc6" b"11714f14e", @@ -1086,7 +1086,7 @@ def test_load_rsa_nist_vectors(): "modulus": int("bcb47b2e0dafcba81ff2a2b5cb115ca7e757184c9d72bcdcda" "707a146b3b4e29989d", 16), "public_exponent": 65537, - "algorithm": b"SHA384", + "algorithm": "SHA384", "salt_length": 20, "msg": b"e511903c2f1bfba245467295ac95413ac4746c984c3750a728c388aa6" b"28b0ebf", @@ -1096,7 +1096,7 @@ def test_load_rsa_nist_vectors(): { "modulus": 78187493520, "public_exponent": 65537, - "algorithm": b"SHA512", + "algorithm": "SHA512", "salt_length": 20, "msg": b"3456781293fab829", "s": b"deadbeef0000" diff --git a/tests/utils.py b/tests/utils.py index 720a9054..6a8a2b5e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -329,7 +329,7 @@ def load_rsa_nist_vectors(vector_data): "modulus": n, "public_exponent": e, "salt_length": salt_length, - "algorithm": value.encode("ascii") + "algorithm": value } data.append(test_data) continue -- cgit v1.2.3 From 1cfdca212bb33b1945dc54e9974595bf06060e1f Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 16 Mar 2014 17:57:43 -0400 Subject: pass the hash class rather than using getattr --- tests/hazmat/primitives/test_rsa.py | 10 +++++----- tests/hazmat/primitives/utils.py | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 8e9637fd..a09d6d66 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -759,7 +759,7 @@ class TestRSAPSSMGF1VerificationSHA1(object): "SigGenPSS_186-2.rsp", "SigGenPSS_186-3.rsp", ], - b"SHA1" + hashes.SHA1() ) @@ -776,7 +776,7 @@ class TestRSAPSSMGF1VerificationSHA224(object): "SigGenPSS_186-2.rsp", "SigGenPSS_186-3.rsp", ], - b"SHA224" + hashes.SHA224() ) @@ -793,7 +793,7 @@ class TestRSAPSSMGF1VerificationSHA256(object): "SigGenPSS_186-2.rsp", "SigGenPSS_186-3.rsp", ], - b"SHA256" + hashes.SHA256() ) @@ -810,7 +810,7 @@ class TestRSAPSSMGF1VerificationSHA384(object): "SigGenPSS_186-2.rsp", "SigGenPSS_186-3.rsp", ], - b"SHA384" + hashes.SHA384() ) @@ -827,7 +827,7 @@ class TestRSAPSSMGF1VerificationSHA512(object): "SigGenPSS_186-2.rsp", "SigGenPSS_186-3.rsp", ], - b"SHA512" + hashes.SHA512() ) diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 884ce00c..31491023 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -376,32 +376,32 @@ def generate_hkdf_test(param_loader, path, file_names, algorithm): return test_hkdf -def generate_rsa_pss_test(param_loader, path, file_names, hash_name): +def generate_rsa_pss_test(param_loader, path, file_names, hash_alg): all_params = _load_all_params(path, file_names, param_loader) - all_params = [i for i in all_params if i["algorithm"] == hash_name] + all_params = [i for i in all_params + if i["algorithm"] == hash_alg.name.upper()] @pytest.mark.parametrize("params", all_params) def test_rsa_pss(self, backend, params): - rsa_pss_test(backend, params) + rsa_pss_test(backend, params, hash_alg) return test_rsa_pss -def rsa_pss_test(backend, params): +def rsa_pss_test(backend, params, hash_alg): public_key = rsa.RSAPublicKey( public_exponent=params["public_exponent"], modulus=params["modulus"] ) - hash_cls = getattr(hashes, params["algorithm"].decode("utf8")) verifier = public_key.verifier( binascii.unhexlify(params["s"]), padding.PSS( mgf=padding.MGF1( - algorithm=hash_cls(), + algorithm=hash_alg, salt_length=params["salt_length"] ) ), - hash_cls(), + hash_alg, backend ) verifier.update(binascii.unhexlify(params["msg"])) -- cgit v1.2.3 From c480f6e23fcc162057fb892c676ea61b691fce16 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 17 Mar 2014 17:40:06 -0400 Subject: change exception and improve some language --- tests/hazmat/primitives/test_rsa.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index a09d6d66..955e69c9 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -21,7 +21,9 @@ import os import pytest from cryptography import exceptions, utils -from cryptography.exceptions import UnsupportedInterface +from cryptography.exceptions import ( + UnsupportedAlgorithm, UnsupportedInterface +) from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import rsa, padding @@ -713,7 +715,7 @@ class TestRSAVerification(object): backend=backend ) public_key = private_key.public_key() - with pytest.raises(TypeError): + with pytest.raises(UnsupportedAlgorithm): public_key.verifier(b"sig", padding.PSS(mgf=DummyMGF()), hashes.SHA1(), backend) -- cgit v1.2.3