aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2014-06-06 21:05:23 -0500
committerPaul Kehrer <paul.l.kehrer@gmail.com>2014-06-12 09:41:18 -0500
commit6af3c6ed6955030208ecfc09e02492de47c1fa10 (patch)
tree13b1bb608a05930580adc5e8ea5c68d11c524c96 /cryptography
parentd19e1f04b32bf95be2302d19abf56b3d07774124 (diff)
downloadcryptography-6af3c6ed6955030208ecfc09e02492de47c1fa10.tar.gz
cryptography-6af3c6ed6955030208ecfc09e02492de47c1fa10.tar.bz2
cryptography-6af3c6ed6955030208ecfc09e02492de47c1fa10.zip
backend specific RSA*Key implementation for OpenSSL
Diffstat (limited to 'cryptography')
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py264
-rw-r--r--cryptography/hazmat/primitives/asymmetric/rsa.py2
2 files changed, 197 insertions, 69 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index ab3f9471..770b2af4 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -41,6 +41,9 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
from cryptography.hazmat.primitives.ciphers.modes import (
CBC, CFB, CFB8, CTR, ECB, GCM, OFB
)
+from cryptography.hazmat.primitives.interfaces import (
+ RSAPrivateKeyWithNumbers, RSAPublicKeyWithNumbers
+)
_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
@@ -374,27 +377,34 @@ class Backend(object):
return self._rsa_cdata_to_private_key(ctx)
+ def _generate_rsa_private_key(self, public_exponent, key_size):
+ rsa._verify_rsa_parameters(public_exponent, key_size)
+
+ rsa_cdata = self._lib.RSA_new()
+ assert rsa_cdata != self._ffi.NULL
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+
+ bn = self._int_to_bn(public_exponent)
+ bn = self._ffi.gc(bn, self._lib.BN_free)
+
+ res = self._lib.RSA_generate_key_ex(
+ rsa_cdata, key_size, bn, self._ffi.NULL
+ )
+ assert res == 1
+
+ return _RSAPrivateKey(self, rsa_cdata)
+
def generate_rsa_parameters_supported(self, public_exponent, key_size):
return (public_exponent >= 3 and public_exponent & 1 != 0 and
key_size >= 512)
def load_rsa_private_numbers(self, numbers):
- return rsa.RSAPrivateKey(
- p=numbers.p,
- q=numbers.q,
- private_exponent=numbers.d,
- dmp1=numbers.dmp1,
- dmq1=numbers.dmq1,
- iqmp=numbers.iqmp,
- public_exponent=numbers.public_numbers.e,
- modulus=numbers.public_numbers.n
- )
+ rsa_cdata = self._rsa_cdata_from_private_numbers(numbers)
+ return _RSAPrivateKey(self, rsa_cdata)
def load_rsa_public_numbers(self, numbers):
- return rsa.RSAPublicKey(
- public_exponent=numbers.e,
- modulus=numbers.n
- )
+ rsa_cdata = self._rsa_cdata_from_public_numbers(numbers)
+ return _RSAPublicKey(self, rsa_cdata)
def _new_evp_pkey(self):
evp_pkey = self._lib.EVP_PKEY_new()
@@ -445,8 +455,7 @@ class Backend(object):
if type == self._lib.EVP_PKEY_RSA:
rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
assert rsa_cdata != self._ffi.NULL
- rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
- return self._rsa_cdata_to_private_key(rsa_cdata)
+ return _RSAPrivateKey(self, rsa_cdata)
elif type == self._lib.EVP_PKEY_DSA:
dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
assert dsa_cdata != self._ffi.NULL
@@ -538,12 +547,47 @@ class Backend(object):
return ctx
+ def _rsa_cdata_from_private_numbers(self, private_numbers):
+ # Does not GC the RSA cdata. You *must* make sure it's freed
+ # correctly yourself!
+ ctx = self._lib.RSA_new()
+ assert ctx != self._ffi.NULL
+ ctx.p = self._int_to_bn(private_numbers.p)
+ ctx.q = self._int_to_bn(private_numbers.q)
+ ctx.d = self._int_to_bn(private_numbers.d)
+ ctx.dmp1 = self._int_to_bn(private_numbers.dmp1)
+ ctx.dmq1 = self._int_to_bn(private_numbers.dmq1)
+ ctx.iqmp = self._int_to_bn(private_numbers.iqmp)
+ ctx.e = self._int_to_bn(private_numbers.public_numbers.e)
+ ctx.n = self._int_to_bn(private_numbers.public_numbers.n)
+ res = self._lib.RSA_blinding_on(ctx, self._ffi.NULL)
+ assert res == 1
+
+ return ctx
+
+ def _rsa_cdata_from_public_numbers(self, public_numbers):
+ # Does not GC the RSA cdata. You *must* make sure it's freed
+ # correctly yourself!
+
+ ctx = self._lib.RSA_new()
+ assert ctx != self._ffi.NULL
+ ctx.e = self._int_to_bn(public_numbers.e)
+ ctx.n = self._int_to_bn(public_numbers.n)
+ res = self._lib.RSA_blinding_on(ctx, self._ffi.NULL)
+ assert res == 1
+
+ return ctx
+
def create_rsa_signature_ctx(self, private_key, padding, algorithm):
- return _RSASignatureContext(self, private_key, padding, algorithm)
+ rsa_cdata = self._rsa_cdata_from_private_key(private_key)
+ key = _RSAPrivateKey(self, rsa_cdata)
+ return _RSASignatureContext(self, key, padding, algorithm)
def create_rsa_verification_ctx(self, public_key, signature, padding,
algorithm):
- return _RSAVerificationContext(self, public_key, signature, padding,
+ rsa_cdata = self._rsa_cdata_from_public_key(public_key)
+ key = _RSAPublicKey(self, rsa_cdata)
+ return _RSAVerificationContext(self, key, signature, padding,
algorithm)
def mgf1_hash_supported(self, algorithm):
@@ -704,13 +748,18 @@ class Backend(object):
def _enc_dec_rsa_pkey_ctx(self, key, data, padding_enum):
if isinstance(key, rsa.RSAPublicKey):
+ evp_pkey = self._rsa_public_key_to_evp_pkey(key)
+ elif isinstance(key, rsa.RSAPrivateKey):
+ evp_pkey = self._rsa_private_key_to_evp_pkey(key)
+ else:
+ evp_pkey = key._evp_pkey
+
+ if isinstance(key, (rsa.RSAPublicKey, _RSAPublicKey)):
init = self._lib.EVP_PKEY_encrypt_init
crypt = self._lib.Cryptography_EVP_PKEY_encrypt
- evp_pkey = self._rsa_public_key_to_evp_pkey(key)
else:
init = self._lib.EVP_PKEY_decrypt_init
crypt = self._lib.Cryptography_EVP_PKEY_decrypt
- evp_pkey = self._rsa_private_key_to_evp_pkey(key)
pkey_ctx = self._lib.EVP_PKEY_CTX_new(
evp_pkey, self._ffi.NULL
@@ -740,13 +789,19 @@ class Backend(object):
def _enc_dec_rsa_098(self, key, data, padding_enum):
if isinstance(key, rsa.RSAPublicKey):
- crypt = self._lib.RSA_public_encrypt
rsa_cdata = self._rsa_cdata_from_public_key(key)
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ elif isinstance(key, rsa.RSAPrivateKey):
+ rsa_cdata = self._rsa_cdata_from_private_key(key)
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ else:
+ rsa_cdata = key._rsa_cdata
+
+ if isinstance(key, (rsa.RSAPublicKey, _RSAPublicKey)):
+ crypt = self._lib.RSA_public_encrypt
else:
crypt = self._lib.RSA_private_decrypt
- rsa_cdata = self._rsa_cdata_from_private_key(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)
@@ -766,7 +821,7 @@ class Backend(object):
errors = self._consume_errors()
assert errors
assert errors[0].lib == self._lib.ERR_LIB_RSA
- if isinstance(key, rsa.RSAPublicKey):
+ if isinstance(key, (_RSAPublicKey, rsa.RSAPublicKey)):
assert (errors[0].reason ==
self._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
raise ValueError(
@@ -1468,6 +1523,10 @@ class _RSASignatureContext(object):
raise TypeError(
"Expected provider of interfaces.AsymmetricPadding.")
+ self.pkey_size = self._backend._lib.EVP_PKEY_size(
+ self._private_key._evp_pkey
+ )
+
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._finalize_method = self._finalize_pkey_ctx
@@ -1483,8 +1542,8 @@ class _RSASignatureContext(object):
# 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:
+ assert self.pkey_size > 0
+ if self.pkey_size - algorithm.digest_size - 2 < 0:
raise ValueError("Digest too large for key size. Use a larger "
"key.")
@@ -1514,20 +1573,15 @@ class _RSASignatureContext(object):
self._hash_ctx.update(data)
def finalize(self):
- evp_pkey = self._backend._rsa_private_key_to_evp_pkey(
- self._private_key)
-
evp_md = self._backend._lib.EVP_get_digestbyname(
self._algorithm.name.encode("ascii"))
assert evp_md != self._backend._ffi.NULL
- pkey_size = self._backend._lib.EVP_PKEY_size(evp_pkey)
- assert pkey_size > 0
- return self._finalize_method(evp_pkey, pkey_size, evp_md)
+ return self._finalize_method(evp_md)
- def _finalize_pkey_ctx(self, evp_pkey, pkey_size, evp_md):
+ def _finalize_pkey_ctx(self, evp_md):
pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
- evp_pkey, self._backend._ffi.NULL
+ self._private_key._evp_pkey, self._backend._ffi.NULL
)
assert pkey_ctx != self._backend._ffi.NULL
pkey_ctx = self._backend._ffi.gc(pkey_ctx,
@@ -1590,17 +1644,17 @@ class _RSASignatureContext(object):
return self._backend._ffi.buffer(buf)[:]
- def _finalize_pkcs1(self, evp_pkey, pkey_size, evp_md):
+ def _finalize_pkcs1(self, evp_md):
if self._hash_ctx._ctx is None:
raise AlreadyFinalized("Context has already been finalized.")
- sig_buf = self._backend._ffi.new("char[]", pkey_size)
+ sig_buf = self._backend._ffi.new("char[]", self.pkey_size)
sig_len = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.EVP_SignFinal(
self._hash_ctx._ctx._ctx,
sig_buf,
sig_len,
- evp_pkey
+ self._private_key._evp_pkey
)
self._hash_ctx.finalize()
if res == 0:
@@ -1613,15 +1667,11 @@ class _RSASignatureContext(object):
return self._backend._ffi.buffer(sig_buf)[:sig_len[0]]
- def _finalize_pss(self, evp_pkey, pkey_size, evp_md):
+ def _finalize_pss(self, evp_md):
data_to_sign = self._hash_ctx.finalize()
- 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)
+ padded = self._backend._ffi.new("unsigned char[]", self.pkey_size)
res = self._backend._lib.RSA_padding_add_PKCS1_PSS(
- rsa_cdata,
+ self._private_key._rsa_cdata,
padded,
data_to_sign,
evp_md,
@@ -1639,12 +1689,12 @@ class _RSASignatureContext(object):
raise ValueError("Salt length too long for key size. Try using "
"MAX_LENGTH instead.")
- sig_buf = self._backend._ffi.new("char[]", pkey_size)
+ sig_buf = self._backend._ffi.new("char[]", self.pkey_size)
sig_len = self._backend._lib.RSA_private_encrypt(
- pkey_size,
+ self.pkey_size,
padded,
sig_buf,
- rsa_cdata,
+ self._private_key._rsa_cdata,
self._backend._lib.RSA_NO_PADDING
)
assert sig_len != -1
@@ -1662,6 +1712,10 @@ class _RSAVerificationContext(object):
raise TypeError(
"Expected provider of interfaces.AsymmetricPadding.")
+ self.pkey_size = self._backend._lib.EVP_PKEY_size(
+ self._public_key._evp_pkey
+ )
+
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._verify_method = self._verify_pkey_ctx
@@ -1677,8 +1731,8 @@ class _RSAVerificationContext(object):
# 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:
+ assert self.pkey_size > 0
+ if self.pkey_size - algorithm.digest_size - 2 < 0:
raise ValueError(
"Digest too large for key size. Check that you have the "
"correct key and digest algorithm."
@@ -1710,18 +1764,15 @@ class _RSAVerificationContext(object):
self._hash_ctx.update(data)
def verify(self):
- evp_pkey = self._backend._rsa_public_key_to_evp_pkey(
- self._public_key)
-
evp_md = self._backend._lib.EVP_get_digestbyname(
self._algorithm.name.encode("ascii"))
assert evp_md != self._backend._ffi.NULL
- self._verify_method(evp_pkey, evp_md)
+ self._verify_method(evp_md)
- def _verify_pkey_ctx(self, evp_pkey, evp_md):
+ def _verify_pkey_ctx(self, evp_md):
pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
- evp_pkey, self._backend._ffi.NULL
+ self._public_key._evp_pkey, self._backend._ffi.NULL
)
assert pkey_ctx != self._backend._ffi.NULL
pkey_ctx = self._backend._ffi.gc(pkey_ctx,
@@ -1772,7 +1823,7 @@ class _RSAVerificationContext(object):
assert errors
raise InvalidSignature
- def _verify_pkcs1(self, evp_pkey, evp_md):
+ def _verify_pkcs1(self, evp_md):
if self._hash_ctx._ctx is None:
raise AlreadyFinalized("Context has already been finalized.")
@@ -1780,7 +1831,7 @@ class _RSAVerificationContext(object):
self._hash_ctx._ctx._ctx,
self._signature,
len(self._signature),
- evp_pkey
+ self._public_key._evp_pkey
)
self._hash_ctx.finalize()
# The previous call can return negative numbers in the event of an
@@ -1792,29 +1843,23 @@ class _RSAVerificationContext(object):
assert errors
raise InvalidSignature
- def _verify_pss(self, evp_pkey, evp_md):
- pkey_size = self._backend._lib.EVP_PKEY_size(evp_pkey)
- assert pkey_size > 0
- 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)
- buf = self._backend._ffi.new("unsigned char[]", pkey_size)
+ def _verify_pss(self, evp_md):
+ buf = self._backend._ffi.new("unsigned char[]", self.pkey_size)
res = self._backend._lib.RSA_public_decrypt(
len(self._signature),
self._signature,
buf,
- rsa_cdata,
+ self._public_key._rsa_cdata,
self._backend._lib.RSA_NO_PADDING
)
- if res != pkey_size:
+ if res != self.pkey_size:
errors = self._backend._consume_errors()
assert errors
raise InvalidSignature
data_to_verify = self._hash_ctx.finalize()
res = self._backend._lib.RSA_verify_PKCS1_PSS(
- rsa_cdata,
+ self._public_key._rsa_cdata,
data_to_verify,
evp_md,
buf,
@@ -2110,4 +2155,87 @@ class _EllipticCurvePublicKey(object):
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+@utils.register_interface(RSAPrivateKeyWithNumbers)
+class _RSAPrivateKey(object):
+ def __init__(self, backend, rsa_cdata):
+ self._backend = backend
+ self._rsa_cdata = self._backend._ffi.gc(
+ rsa_cdata, self._backend._lib.RSA_free
+ )
+
+ evp_pkey = self._backend._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
+ assert res == 1
+ self._evp_pkey = evp_pkey
+
+ self.key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n)
+
+ def signer(self, padding, algorithm):
+ return _RSASignatureContext(self._backend, self, padding, algorithm)
+
+ def decrypt(self, ciphertext, padding):
+ return self._backend.decrypt_rsa(self, ciphertext, padding)
+
+ def public_key(self):
+ ctx = self._backend._lib.RSA_new()
+ assert ctx != self._backend._ffi.NULL
+ ctx.e = self._backend._lib.BN_dup(self._rsa_cdata.e)
+ ctx.n = self._backend._lib.BN_dup(self._rsa_cdata.n)
+ res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL)
+ assert res == 1
+ return _RSAPublicKey(self._backend, ctx)
+
+ def private_numbers(self):
+ return rsa.RSAPrivateNumbers(
+ p=self._backend._bn_to_int(self._rsa_cdata.p),
+ q=self._backend._bn_to_int(self._rsa_cdata.q),
+ d=self._backend._bn_to_int(self._rsa_cdata.d),
+ dmp1=self._backend._bn_to_int(self._rsa_cdata.dmp1),
+ dmq1=self._backend._bn_to_int(self._rsa_cdata.dmq1),
+ iqmp=self._backend._bn_to_int(self._rsa_cdata.iqmp),
+ public_numbers=rsa.RSAPublicNumbers(
+ e=self._backend._bn_to_int(self._rsa_cdata.e),
+ n=self._backend._bn_to_int(self._rsa_cdata.n),
+ )
+ )
+
+
+@utils.register_interface(RSAPublicKeyWithNumbers)
+class _RSAPublicKey(object):
+ def __init__(self, backend, rsa_cdata):
+ self._backend = backend
+ self._rsa_cdata = self._backend._ffi.gc(
+ rsa_cdata, self._backend._lib.RSA_free
+ )
+
+ evp_pkey = self._backend._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
+ assert res == 1
+ self._evp_pkey = evp_pkey
+
+ self.key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n)
+
+ def verifier(self, signature, padding, algorithm):
+ return _RSAVerificationContext(
+ self._backend, self, signature, padding, algorithm
+ )
+
+ def encrypt(self, plaintext, padding):
+ return self._backend.encrypt_rsa(self, plaintext, padding)
+
+ def public_numbers(self):
+ return rsa.RSAPublicNumbers(
+ e=self._backend._bn_to_int(self._rsa_cdata.e),
+ n=self._backend._bn_to_int(self._rsa_cdata.n),
+ )
+
+
backend = Backend()
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index be7edad3..dee2d7a5 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -29,7 +29,7 @@ def generate_private_key(public_exponent, key_size, backend):
)
_verify_rsa_parameters(public_exponent, key_size)
- return backend.generate_rsa_private_key(public_exponent, key_size)
+ return backend._generate_rsa_private_key(public_exponent, key_size)
def _verify_rsa_parameters(public_exponent, key_size):