diff options
-rw-r--r-- | CHANGELOG.rst | 5 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 19 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/padding.py | 39 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/padding.rst | 26 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/rsa.rst | 18 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 140 |
6 files changed, 170 insertions, 77 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 34c544d9..9e89e563 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,11 @@ Changelog .. note:: This version is not yet released and is under active development. +* Deprecated ``salt_length`` on + :class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1` and added it + to :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`. It will be + removed from ``MGF1`` in two releases per our :doc:`/api-stability` policy. + 0.3 - 2014-03-27 ~~~~~~~~~~~~~~~~ diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 3293741c..0c632aee 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -701,15 +701,20 @@ class _HMACContext(object): return self._backend._ffi.buffer(buf)[:outlen[0]] -def _get_rsa_pss_salt_length(mgf, key_size, digest_size): - if mgf._salt_length is MGF1.MAX_LENGTH: +def _get_rsa_pss_salt_length(pss, key_size, digest_size): + if pss._mgf._salt_length is not None: + salt = pss._mgf._salt_length + else: + salt = pss._salt_length + + if salt is MGF1.MAX_LENGTH or salt is PSS.MAX_LENGTH: # bit length - 1 per RFC 3447 emlen = int(math.ceil((key_size - 1) / 8.0)) salt_length = emlen - digest_size - 2 assert salt_length >= 0 return salt_length else: - return mgf._salt_length + return salt @utils.register_interface(interfaces.AsymmetricSignatureContext) @@ -803,7 +808,7 @@ class _RSASignatureContext(object): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._private_key.key_size, self._hash_ctx.algorithm.digest_size ) @@ -871,7 +876,7 @@ class _RSASignatureContext(object): data_to_sign, evp_md, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._private_key.key_size, len(data_to_sign) ) @@ -988,7 +993,7 @@ class _RSAVerificationContext(object): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._public_key.key_size, self._hash_ctx.algorithm.digest_size ) @@ -1068,7 +1073,7 @@ class _RSAVerificationContext(object): evp_md, buf, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._public_key.key_size, len(data_to_verify) ) diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py index 02aff280..8a1929bb 100644 --- a/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -13,6 +13,8 @@ from __future__ import absolute_import, division, print_function +import warnings + import six from cryptography import utils @@ -26,26 +28,49 @@ class PKCS1v15(object): @utils.register_interface(interfaces.AsymmetricPadding) class PSS(object): + MAX_LENGTH = object() name = "EMSA-PSS" - def __init__(self, mgf): + def __init__(self, mgf, salt_length=None): self._mgf = mgf + if salt_length is None: + warnings.warn( + "salt_length is deprecated on MGF1 and should be added via the" + " PSS constructor.", + PendingDeprecationWarning + ) + else: + if (not isinstance(salt_length, six.integer_types) and + salt_length is not self.MAX_LENGTH): + raise TypeError("salt_length must be an integer") + + if salt_length is not self.MAX_LENGTH and salt_length < 0: + raise ValueError("salt_length must be zero or greater") + + self._salt_length = salt_length + class MGF1(object): MAX_LENGTH = object() - def __init__(self, algorithm, salt_length): + def __init__(self, algorithm, salt_length=None): if not isinstance(algorithm, interfaces.HashAlgorithm): raise TypeError("Expected instance of interfaces.HashAlgorithm.") self._algorithm = algorithm - if (not isinstance(salt_length, six.integer_types) and - salt_length is not self.MAX_LENGTH): - raise TypeError("salt_length must be an integer") + if salt_length is not None: + warnings.warn( + "salt_length is deprecated on MGF1 and should be added via the" + " PSS constructor.", + PendingDeprecationWarning + ) + if (not isinstance(salt_length, six.integer_types) and + salt_length is not self.MAX_LENGTH): + raise TypeError("salt_length must be an integer") - if salt_length is not self.MAX_LENGTH and salt_length < 0: - raise ValueError("salt_length must be zero or greater") + if salt_length is not self.MAX_LENGTH and salt_length < 0: + raise ValueError("salt_length must be zero or greater") self._salt_length = salt_length diff --git a/docs/hazmat/primitives/asymmetric/padding.rst b/docs/hazmat/primitives/asymmetric/padding.rst index 2a5de3c7..6d584730 100644 --- a/docs/hazmat/primitives/asymmetric/padding.rst +++ b/docs/hazmat/primitives/asymmetric/padding.rst @@ -10,10 +10,13 @@ Padding correct padding signatures can be forged, messages decrypted, and private keys compromised. -.. class:: PSS(mgf) +.. class:: PSS(mgf, salt_length) .. versionadded:: 0.3 + .. versionchanged:: 0.4 + Added ``salt_length`` parameter. + PSS (Probabilistic Signature Scheme) is a signature scheme defined in :rfc:`3447`. It is more complex than PKCS1 but possesses a `security proof`_. This is the `recommended padding algorithm`_ for RSA signatures. @@ -21,6 +24,14 @@ Padding :param mgf: A mask generation function object. At this time the only supported MGF is :class:`MGF1`. + :param int salt_length: The length of the salt. It is recommended that this + be set to ``PSS.MAX_LENGTH``. + + .. attribute:: MAX_LENGTH + + Pass this attribute to ``salt_length`` to get the maximum salt length + available. + .. class:: PKCS1v15() .. versionadded:: 0.3 @@ -31,10 +42,13 @@ Padding Mask generation functions ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. class:: MGF1(algorithm, salt_length) +.. class:: MGF1(algorithm) .. versionadded:: 0.3 + .. versionchanged:: 0.4 + Deprecated ``salt_length`` parameter. + MGF1 (Mask Generation Function 1) is used as the mask generation function in :class:`PSS` padding. It takes a hash algorithm and a salt length. @@ -42,14 +56,6 @@ Mask generation functions :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` provider. - :param int salt_length: The length of the salt. It is recommended that this - be set to ``MGF1.MAX_LENGTH``. - - .. attribute:: MAX_LENGTH - - Pass this attribute to ``salt_length`` to get the maximum salt length - available. - .. _`Padding is critical`: http://rdist.root.org/2009/10/06/why-rsa-encryption-padding-is-critical/ .. _`security proof`: http://eprint.iacr.org/2001/062.pdf diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 182e35d2..b3962cf9 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -73,10 +73,8 @@ RSA ... ) >>> signer = private_key.signer( ... padding.PSS( - ... mgf=padding.MGF1( - ... algorithm=hashes.SHA256(), - ... salt_length=padding.MGF1.MAX_LENGTH - ... ) + ... mgf=padding.MGF1(hashes.SHA256()), + ... salt_length=padding.PSS.MAX_LENGTH ... ), ... hashes.SHA256(), ... default_backend() @@ -158,10 +156,8 @@ RSA ... ) >>> signer = private_key.signer( ... padding.PSS( - ... mgf=padding.MGF1( - ... algorithm=hashes.SHA256(), - ... salt_length=padding.MGF1.MAX_LENGTH - ... ) + ... mgf=padding.MGF1(hashes.SHA256()), + ... salt_length=padding.PSS.MAX_LENGTH ... ), ... hashes.SHA256(), ... default_backend() @@ -173,10 +169,8 @@ RSA >>> verifier = public_key.verifier( ... signature, ... padding.PSS( - ... mgf=padding.MGF1( - ... algorithm=hashes.SHA256(), - ... salt_length=padding.MGF1.MAX_LENGTH - ... ) + ... mgf=padding.MGF1(hashes.SHA256()), + ... salt_length=padding.PSS.MAX_LENGTH ... ), ... hashes.SHA256(), ... default_backend() diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index c458a662..c159ab8b 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -454,10 +454,8 @@ class TestRSASignature(object): ) signer = private_key.signer( padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1(), backend @@ -471,6 +469,23 @@ class TestRSASignature(object): verifier = public_key.verifier( signature, padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH + ), + hashes.SHA1(), + backend + ) + verifier.update(binascii.unhexlify(example["message"])) + verifier.verify() + + def test_deprecated_pss_mgf1_salt_length(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + signer = private_key.signer( + padding.PSS( mgf=padding.MGF1( algorithm=hashes.SHA1(), salt_length=padding.MGF1.MAX_LENGTH @@ -479,7 +494,21 @@ class TestRSASignature(object): hashes.SHA1(), backend ) - verifier.update(binascii.unhexlify(example["message"])) + signer.update(b"so deprecated") + signature = signer.finalize() + assert len(signature) == math.ceil(private_key.key_size / 8.0) + verifier = private_key.public_key().verifier( + signature, + padding.PSS( + mgf=padding.MGF1( + algorithm=hashes.SHA1(), + salt_length=padding.MGF1.MAX_LENGTH + ) + ), + hashes.SHA1(), + backend + ) + verifier.update(b"so deprecated") verifier.verify() @pytest.mark.parametrize( @@ -498,10 +527,8 @@ class TestRSASignature(object): ) public_key = private_key.public_key() pss = padding.PSS( - mgf=padding.MGF1( - algorithm=hash_alg, - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(hash_alg), + salt_length=padding.PSS.MAX_LENGTH ) signer = private_key.signer( pss, @@ -531,10 +558,8 @@ class TestRSASignature(object): ) signer = private_key.signer( padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA512(), backend @@ -555,10 +580,8 @@ class TestRSASignature(object): with pytest.raises(ValueError): private_key.signer( padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA512(), backend @@ -572,10 +595,8 @@ class TestRSASignature(object): ) signer = private_key.signer( padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=1000000 - ) + mgf=padding.MGF1(hashes.SHA1()), + salt_length=1000000 ), hashes.SHA1(), backend @@ -722,10 +743,8 @@ class TestRSAVerification(object): verifier = public_key.verifier( binascii.unhexlify(example["signature"]), padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=20 - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=20 ), hashes.SHA1(), backend @@ -749,10 +768,8 @@ class TestRSAVerification(object): verifier = public_key.verifier( signature, padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1(), backend @@ -779,10 +796,8 @@ class TestRSAVerification(object): verifier = public_key.verifier( signature, padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1(), backend @@ -809,10 +824,8 @@ class TestRSAVerification(object): verifier = public_key.verifier( signature, padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA1(), backend @@ -904,10 +917,8 @@ class TestRSAVerification(object): public_key.verifier( signature, padding.PSS( - mgf=padding.MGF1( - algorithm=hashes.SHA1(), - salt_length=padding.MGF1.MAX_LENGTH - ) + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA512(), backend @@ -1113,7 +1124,54 @@ class TestRSAPKCS1Verification(object): )) +class TestPSS(object): + def test_deprecation_warning(self): + pytest.deprecated_call( + padding.PSS, + **{"mgf": padding.MGF1(hashes.SHA1(), 20)} + ) + + 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_deprecation_warning(self): + pytest.deprecated_call( + padding.MGF1, + **{"algorithm": hashes.SHA1(), "salt_length": 20} + ) + def test_invalid_hash_algorithm(self): with pytest.raises(TypeError): padding.MGF1(b"not_a_hash", 0) |