diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2014-03-21 13:47:27 -0700 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2014-03-21 13:47:27 -0700 |
commit | 0476a96972f76b18651a1cbac3d4d9ee0235c2ab (patch) | |
tree | e7c667ceaf62be79ea3151c58ecd34f250fd1470 /cryptography | |
parent | 58467ce5e5a78bea2be15a5a96dc8c816d1733b8 (diff) | |
parent | b8666f76d91b42cd88eb5601ce40482c72d036e4 (diff) | |
download | cryptography-0476a96972f76b18651a1cbac3d4d9ee0235c2ab.tar.gz cryptography-0476a96972f76b18651a1cbac3d4d9ee0235c2ab.tar.bz2 cryptography-0476a96972f76b18651a1cbac3d4d9ee0235c2ab.zip |
Merge pull request #833 from reaperhulk/rsa-pss-signing
RSA PSS Signing
Diffstat (limited to 'cryptography')
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 136 |
1 files changed, 126 insertions, 10 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index fa21260c..d2744cf3 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -15,6 +15,7 @@ from __future__ import absolute_import, division, print_function import collections import itertools +import math import six @@ -696,21 +697,57 @@ 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: + # 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 + + @utils.register_interface(interfaces.AsymmetricSignatureContext) class _RSASignatureContext(object): def __init__(self, backend, private_key, padding, algorithm): self._backend = backend self._private_key = private_key + if not isinstance(padding, interfaces.AsymmetricPadding): raise TypeError( "Expected provider of interfaces.AsymmetricPadding") - if padding.name == "EMSA-PKCS1-v1_5": + if isinstance(padding, PKCS1v15): if self._backend._lib.Cryptography_HAS_PKEY_CTX: self._finalize_method = self._finalize_pkey_ctx self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING else: self._finalize_method = self._finalize_pkcs1 + elif isinstance(padding, PSS): + if not isinstance(padding._mgf, MGF1): + raise UnsupportedAlgorithm( + "Only MGF1 is supported by this backend" + ) + + # Size of key in bytes - 2 is the maximum + # PSS signature length (salt length is checked later) + key_size_bytes = int(math.ceil(private_key.key_size / 8.0)) + if key_size_bytes - algorithm.digest_size - 2 < 0: + raise ValueError("Digest too large for key size. Use a larger " + "key.") + + if not self._backend.mgf1_hash_supported(padding._mgf._algorithm): + raise UnsupportedHash( + "When OpenSSL is older than 1.0.1 then only SHA1 is " + "supported with MGF1." + ) + + if self._backend._lib.Cryptography_HAS_PKEY_CTX: + self._finalize_method = self._finalize_pkey_ctx + self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING + else: + self._finalize_method = self._finalize_pss else: raise UnsupportedPadding( "{0} is not supported by this backend".format(padding.name) @@ -755,6 +792,26 @@ class _RSASignatureContext(object): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( pkey_ctx, self._padding_enum) assert res > 0 + if isinstance(self._padding, PSS): + res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( + pkey_ctx, + _get_rsa_pss_salt_length( + self._padding._mgf, + self._private_key.key_size, + self._hash_ctx.algorithm.digest_size + ) + ) + assert res > 0 + + if self._backend._lib.Cryptography_HAS_MGF1_MD: + # MGF1 MD is configurable in OpenSSL 1.0.1+ + mgf1_md = self._backend._lib.EVP_get_digestbyname( + self._padding._mgf._algorithm.name.encode("ascii")) + assert mgf1_md != self._backend._ffi.NULL + res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md( + pkey_ctx, mgf1_md + ) + assert res > 0 data_to_sign = self._hash_ctx.finalize() self._hash_ctx = None buflen = self._backend._ffi.new("size_t *") @@ -769,7 +826,14 @@ class _RSASignatureContext(object): buf = self._backend._ffi.new("unsigned char[]", buflen[0]) res = self._backend._lib.EVP_PKEY_sign( pkey_ctx, buf, buflen, data_to_sign, len(data_to_sign)) - assert res == 1 + if res != 1: + errors = self._backend._consume_errors() + assert errors[0].lib == self._backend._lib.ERR_LIB_RSA + assert (errors[0].reason == + self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) + raise ValueError("Salt length too long for key size. Try using " + "MAX_LENGTH instead.") + return self._backend._ffi.buffer(buf)[:] def _finalize_pkcs1(self, evp_pkey, pkey_size, evp_md): @@ -786,6 +850,44 @@ class _RSASignatureContext(object): assert res == 1 return self._backend._ffi.buffer(sig_buf)[:sig_len[0]] + def _finalize_pss(self, evp_pkey, pkey_size, evp_md): + data_to_sign = self._hash_ctx.finalize() + self._hash_ctx = None + padded = self._backend._ffi.new("unsigned char[]", pkey_size) + rsa_cdata = self._backend._lib.EVP_PKEY_get1_RSA(evp_pkey) + assert rsa_cdata != self._backend._ffi.NULL + rsa_cdata = self._backend._ffi.gc(rsa_cdata, + self._backend._lib.RSA_free) + res = self._backend._lib.RSA_padding_add_PKCS1_PSS( + rsa_cdata, + padded, + data_to_sign, + evp_md, + _get_rsa_pss_salt_length( + self._padding._mgf, + self._private_key.key_size, + len(data_to_sign) + ) + ) + if res != 1: + errors = self._backend._consume_errors() + assert errors[0].lib == self._backend._lib.ERR_LIB_RSA + assert (errors[0].reason == + self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) + raise ValueError("Salt length too long for key size. Try using " + "MAX_LENGTH instead.") + + sig_buf = self._backend._ffi.new("char[]", pkey_size) + sig_len = self._backend._lib.RSA_private_encrypt( + pkey_size, + padded, + sig_buf, + rsa_cdata, + self._backend._lib.RSA_NO_PADDING + ) + assert sig_len != -1 + return self._backend._ffi.buffer(sig_buf)[:sig_len] + @utils.register_interface(interfaces.AsymmetricVerificationContext) class _RSAVerificationContext(object): @@ -793,6 +895,7 @@ class _RSAVerificationContext(object): self._backend = backend self._public_key = public_key self._signature = signature + if not isinstance(padding, interfaces.AsymmetricPadding): raise TypeError( "Expected provider of interfaces.AsymmetricPadding") @@ -809,6 +912,15 @@ class _RSAVerificationContext(object): "Only MGF1 is supported by this backend" ) + # Size of key in bytes - 2 is the maximum + # PSS signature length (salt length is checked later) + key_size_bytes = int(math.ceil(public_key.key_size / 8.0)) + if key_size_bytes - algorithm.digest_size - 2 < 0: + raise ValueError( + "Digest too large for key size. Check that you have the " + "correct key and digest algorithm." + ) + if not self._backend.mgf1_hash_supported(padding._mgf._algorithm): raise UnsupportedHash( "When OpenSSL is older than 1.0.1 then only SHA1 is " @@ -862,7 +974,13 @@ class _RSAVerificationContext(object): assert res > 0 if isinstance(self._padding, PSS): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( - pkey_ctx, self._get_salt_length()) + pkey_ctx, + _get_rsa_pss_salt_length( + self._padding._mgf, + self._public_key.key_size, + self._hash_ctx.algorithm.digest_size + ) + ) assert res > 0 if self._backend._lib.Cryptography_HAS_MGF1_MD: # MGF1 MD is configurable in OpenSSL 1.0.1+ @@ -937,18 +1055,16 @@ class _RSAVerificationContext(object): data_to_verify, evp_md, buf, - self._get_salt_length() + _get_rsa_pss_salt_length( + self._padding._mgf, + self._public_key.key_size, + len(data_to_verify) + ) ) if res != 1: errors = self._backend._consume_errors() assert errors raise InvalidSignature - def _get_salt_length(self): - if self._padding._mgf._salt_length is MGF1.MAX_LENGTH: - return -2 - else: - return self._padding._mgf._salt_length - backend = Backend() |