aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-04-21 12:53:47 -0700
committerAlex Gaynor <alex.gaynor@gmail.com>2014-04-21 12:53:47 -0700
commit30752cdde9c149ede7c3eec5aea4e72944d99ac4 (patch)
tree46bad2831d88981e1232ff831721a33924a85076 /cryptography
parent30bb5941489c7a0b1c24ca546e8f253c97a3a318 (diff)
parent8e764396471beb13d0cdfbc9a299b9445f96abb2 (diff)
downloadcryptography-30752cdde9c149ede7c3eec5aea4e72944d99ac4.tar.gz
cryptography-30752cdde9c149ede7c3eec5aea4e72944d99ac4.tar.bz2
cryptography-30752cdde9c149ede7c3eec5aea4e72944d99ac4.zip
Merge pull request #888 from reaperhulk/rsa-decrypt
RSA PKCS1v15 Decryption Support
Diffstat (limited to 'cryptography')
-rw-r--r--cryptography/hazmat/backends/interfaces.py6
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py81
-rw-r--r--cryptography/hazmat/bindings/openssl/err.py2
-rw-r--r--cryptography/hazmat/primitives/asymmetric/rsa.py9
4 files changed, 98 insertions, 0 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index 92413d8c..677f4c67 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -117,6 +117,12 @@ class RSABackend(object):
Return True if the hash algorithm is supported for MGF1 in PSS.
"""
+ @abc.abstractmethod
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ """
+ Returns decrypted bytes.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class DSABackend(object):
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 86fa704b..5e13bfc1 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -473,6 +473,87 @@ class Backend(object):
y=self._bn_to_int(ctx.pub_key)
)
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ if isinstance(padding, PKCS1v15):
+ padding_enum = self._lib.RSA_PKCS1_PADDING
+ else:
+ raise UnsupportedAlgorithm(
+ "{0} is not supported by this backend".format(
+ padding.name
+ ),
+ _Reasons.UNSUPPORTED_PADDING
+ )
+
+ key_size_bytes = int(math.ceil(private_key.key_size / 8.0))
+ if key_size_bytes < len(ciphertext):
+ raise ValueError("Ciphertext too large for key size")
+
+ if self._lib.Cryptography_HAS_PKEY_CTX:
+ return self._decrypt_rsa_pkey_ctx(private_key, ciphertext,
+ padding_enum)
+ else:
+ return self._decrypt_rsa_098(private_key, ciphertext, padding_enum)
+
+ def _decrypt_rsa_pkey_ctx(self, private_key, ciphertext, padding_enum):
+ evp_pkey = self._rsa_private_key_to_evp_pkey(private_key)
+ pkey_ctx = self._lib.EVP_PKEY_CTX_new(
+ evp_pkey, self._ffi.NULL
+ )
+ assert pkey_ctx != self._ffi.NULL
+ pkey_ctx = self._ffi.gc(pkey_ctx, self._lib.EVP_PKEY_CTX_free)
+ res = self._lib.EVP_PKEY_decrypt_init(pkey_ctx)
+ assert res == 1
+ res = self._lib.EVP_PKEY_CTX_set_rsa_padding(
+ pkey_ctx, padding_enum)
+ assert res > 0
+ buf_size = self._lib.EVP_PKEY_size(evp_pkey)
+ assert buf_size > 0
+ outlen = self._ffi.new("size_t *", buf_size)
+ buf = self._ffi.new("char[]", buf_size)
+ res = self._lib.Cryptography_EVP_PKEY_decrypt(
+ pkey_ctx,
+ buf,
+ outlen,
+ ciphertext,
+ len(ciphertext)
+ )
+ if res <= 0:
+ errors = self._consume_errors()
+ assert errors
+ assert errors[0].lib == self._lib.ERR_LIB_RSA
+ assert (
+ errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_01 or
+ errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_02
+ )
+ raise ValueError("Decryption failed")
+
+ return self._ffi.buffer(buf)[:outlen[0]]
+
+ def _decrypt_rsa_098(self, private_key, ciphertext, padding_enum):
+ rsa_cdata = self._rsa_cdata_from_private_key(private_key)
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ key_size = self._lib.RSA_size(rsa_cdata)
+ assert key_size > 0
+ buf = self._ffi.new("unsigned char[]", key_size)
+ res = self._lib.RSA_private_decrypt(
+ len(ciphertext),
+ ciphertext,
+ buf,
+ rsa_cdata,
+ padding_enum
+ )
+ if res < 0:
+ errors = self._consume_errors()
+ assert errors
+ assert errors[0].lib == self._lib.ERR_LIB_RSA
+ assert (
+ errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_01 or
+ errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_02
+ )
+ raise ValueError("Decryption failed")
+
+ return self._ffi.buffer(buf)[:res]
+
class GetCipherByName(object):
def __init__(self, fmt):
diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py
index f51393aa..c08c880c 100644
--- a/cryptography/hazmat/bindings/openssl/err.py
+++ b/cryptography/hazmat/bindings/openssl/err.py
@@ -216,6 +216,8 @@ static const int PEM_R_UNSUPPORTED_ENCRYPTION;
static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE;
static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY;
+static const int RSA_R_BLOCK_TYPE_IS_NOT_01;
+static const int RSA_R_BLOCK_TYPE_IS_NOT_02;
"""
FUNCTIONS = """
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 5b15350a..cffd4e98 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -189,6 +189,15 @@ class RSAPrivateKey(object):
return backend.create_rsa_signature_ctx(self, padding, algorithm)
+ def decrypt(self, ciphertext, padding, backend):
+ if not isinstance(backend, RSABackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement RSABackend",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ return backend.decrypt_rsa(self, ciphertext, padding)
+
@property
def key_size(self):
return utils.bit_length(self.modulus)