aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-03-21 13:47:27 -0700
committerAlex Gaynor <alex.gaynor@gmail.com>2014-03-21 13:47:27 -0700
commit0476a96972f76b18651a1cbac3d4d9ee0235c2ab (patch)
treee7c667ceaf62be79ea3151c58ecd34f250fd1470 /cryptography
parent58467ce5e5a78bea2be15a5a96dc8c816d1733b8 (diff)
parentb8666f76d91b42cd88eb5601ce40482c72d036e4 (diff)
downloadcryptography-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.py136
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()