diff options
| author | Alex Stapleton <alexs@prol.etari.at> | 2014-06-14 09:53:35 +0100 |
|---|---|---|
| committer | Alex Stapleton <alexs@prol.etari.at> | 2014-06-14 09:53:35 +0100 |
| commit | c6d202ed4becfcad6c1aacc3e274e00f83352a40 (patch) | |
| tree | 995647cafcf1e96f264bc6afe6710f039a252bce /cryptography | |
| parent | fcef4976e326d310d0cf77a6a7b929313583e4ad (diff) | |
| parent | 626855ab04929de40992fa30f47d2975f32a31d1 (diff) | |
| download | cryptography-c6d202ed4becfcad6c1aacc3e274e00f83352a40.tar.gz cryptography-c6d202ed4becfcad6c1aacc3e274e00f83352a40.tar.bz2 cryptography-c6d202ed4becfcad6c1aacc3e274e00f83352a40.zip | |
Merge pull request #1116 from reaperhulk/rsa-numbers-backend-specific
Provide Backend Specific RSA Keys
Diffstat (limited to 'cryptography')
| -rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 292 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/asymmetric/rsa.py | 13 |
2 files changed, 193 insertions, 112 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index eea58bec..0a7a28b4 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"]) @@ -365,64 +368,62 @@ class Backend(object): def generate_rsa_private_key(self, public_exponent, key_size): rsa._verify_rsa_parameters(public_exponent, key_size) - ctx = self._lib.RSA_new() - assert ctx != self._ffi.NULL - ctx = self._ffi.gc(ctx, self._lib.RSA_free) + 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( - ctx, key_size, bn, self._ffi.NULL + rsa_cdata, key_size, bn, self._ffi.NULL ) assert res == 1 - return self._rsa_cdata_to_private_key(ctx) + 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 - ) - - def load_rsa_public_numbers(self, numbers): - return rsa.RSAPublicKey( - public_exponent=numbers.e, - modulus=numbers.n + rsa._check_private_key_components( + numbers.p, + numbers.q, + numbers.d, + numbers.dmp1, + numbers.dmq1, + numbers.iqmp, + numbers.public_numbers.e, + numbers.public_numbers.n ) - - def _new_evp_pkey(self): - evp_pkey = self._lib.EVP_PKEY_new() - assert evp_pkey != self._ffi.NULL - return self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - def _rsa_private_key_to_evp_pkey(self, private_key): - evp_pkey = self._new_evp_pkey() - rsa_cdata = self._rsa_cdata_from_private_key(private_key) - - res = self._lib.EVP_PKEY_assign_RSA(evp_pkey, rsa_cdata) + rsa_cdata = self._lib.RSA_new() + assert rsa_cdata != self._ffi.NULL + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + rsa_cdata.p = self._int_to_bn(numbers.p) + rsa_cdata.q = self._int_to_bn(numbers.q) + rsa_cdata.d = self._int_to_bn(numbers.d) + rsa_cdata.dmp1 = self._int_to_bn(numbers.dmp1) + rsa_cdata.dmq1 = self._int_to_bn(numbers.dmq1) + rsa_cdata.iqmp = self._int_to_bn(numbers.iqmp) + rsa_cdata.e = self._int_to_bn(numbers.public_numbers.e) + rsa_cdata.n = self._int_to_bn(numbers.public_numbers.n) + res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) assert res == 1 - return evp_pkey + return _RSAPrivateKey(self, rsa_cdata) - def _rsa_public_key_to_evp_pkey(self, public_key): - evp_pkey = self._new_evp_pkey() - rsa_cdata = self._rsa_cdata_from_public_key(public_key) - - res = self._lib.EVP_PKEY_assign_RSA(evp_pkey, rsa_cdata) + def load_rsa_public_numbers(self, numbers): + rsa._check_public_key_components(numbers.e, numbers.n) + rsa_cdata = self._lib.RSA_new() + assert rsa_cdata != self._ffi.NULL + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + rsa_cdata.e = self._int_to_bn(numbers.e) + rsa_cdata.n = self._int_to_bn(numbers.n) + res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) assert res == 1 - return evp_pkey + return _RSAPublicKey(self, rsa_cdata) def _bytes_to_bio(self, data): """ @@ -451,7 +452,7 @@ class Backend(object): 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 @@ -469,18 +470,6 @@ class Backend(object): y=self._bn_to_int(cdata.pub_key) ) - def _rsa_cdata_to_private_key(self, cdata): - return rsa.RSAPrivateKey( - p=self._bn_to_int(cdata.p), - q=self._bn_to_int(cdata.q), - dmp1=self._bn_to_int(cdata.dmp1), - dmq1=self._bn_to_int(cdata.dmq1), - iqmp=self._bn_to_int(cdata.iqmp), - private_exponent=self._bn_to_int(cdata.d), - public_exponent=self._bn_to_int(cdata.e), - modulus=self._bn_to_int(cdata.n), - ) - def _pem_password_cb(self, password): """ Generate a pem_password_cb function pointer that copied the password to @@ -544,11 +533,17 @@ class Backend(object): 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) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + 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) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + key = _RSAPublicKey(self, rsa_cdata) + return _RSAVerificationContext(self, key, signature, padding, algorithm) def mgf1_hash_supported(self, algorithm): @@ -659,14 +654,16 @@ class Backend(object): return True def decrypt_rsa(self, private_key, ciphertext, padding): - key_size_bytes = int(math.ceil(private_key.key_size / 8.0)) - if key_size_bytes != len(ciphertext): - raise ValueError("Ciphertext length must be equal to key size.") - - return self._enc_dec_rsa(private_key, ciphertext, padding) + rsa_cdata = self._rsa_cdata_from_private_key(private_key) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + key = _RSAPrivateKey(self, rsa_cdata) + return key.decrypt(ciphertext, padding) def encrypt_rsa(self, public_key, plaintext, padding): - return self._enc_dec_rsa(public_key, plaintext, padding) + rsa_cdata = self._rsa_cdata_from_public_key(public_key) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + key = _RSAPublicKey(self, rsa_cdata) + return key.encrypt(plaintext, padding) def _enc_dec_rsa(self, key, data, padding): if isinstance(padding, PKCS1v15): @@ -708,14 +705,14 @@ class Backend(object): return self._enc_dec_rsa_098(key, data, padding_enum) def _enc_dec_rsa_pkey_ctx(self, key, data, padding_enum): - if isinstance(key, rsa.RSAPublicKey): + evp_pkey = key._evp_pkey + + if isinstance(key, _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 @@ -744,14 +741,13 @@ class Backend(object): return self._ffi.buffer(buf)[:outlen[0]] def _enc_dec_rsa_098(self, key, data, padding_enum): - if isinstance(key, rsa.RSAPublicKey): + rsa_cdata = key._rsa_cdata + + if isinstance(key, _RSAPublicKey): crypt = self._lib.RSA_public_encrypt - rsa_cdata = self._rsa_cdata_from_public_key(key) 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) @@ -771,7 +767,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): assert (errors[0].reason == self._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) raise ValueError( @@ -1473,6 +1469,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 @@ -1488,8 +1488,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.") @@ -1519,20 +1519,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, @@ -1595,17 +1590,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: @@ -1618,15 +1613,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, @@ -1644,12 +1635,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 @@ -1667,6 +1658,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 @@ -1682,8 +1677,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." @@ -1715,18 +1710,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, @@ -1777,7 +1769,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.") @@ -1785,7 +1777,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 @@ -1797,29 +1789,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, @@ -2115,4 +2101,88 @@ 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 = rsa_cdata + + 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): + key_size_bytes = int(math.ceil(self.key_size / 8.0)) + if key_size_bytes != len(ciphertext): + raise ValueError("Ciphertext length must be equal to key size.") + + return self._backend._enc_dec_rsa(self, ciphertext, padding) + + def public_key(self): + ctx = self._backend._lib.RSA_new() + assert ctx != self._backend._ffi.NULL + ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) + 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 = rsa_cdata + + 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._enc_dec_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..18ca0db2 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -220,7 +220,18 @@ class RSAPrivateKey(object): ) _verify_rsa_parameters(public_exponent, key_size) - return backend.generate_rsa_private_key(public_exponent, key_size) + key = backend.generate_rsa_private_key(public_exponent, key_size) + private_numbers = key.private_numbers() + return RSAPrivateKey( + p=private_numbers.p, + q=private_numbers.q, + dmp1=private_numbers.dmp1, + dmq1=private_numbers.dmq1, + iqmp=private_numbers.iqmp, + private_exponent=private_numbers.d, + public_exponent=private_numbers.public_numbers.e, + modulus=private_numbers.public_numbers.n + ) def signer(self, padding, algorithm, backend): if not isinstance(backend, RSABackend): |
