diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2014-02-25 20:58:19 -0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2014-02-25 20:58:19 -0800 |
commit | d8c8f7cde6b43d08f39cd11cd2e2dd3ed7feb5a5 (patch) | |
tree | f912b6b5d7bb466a05c7e802ffb6515716c87087 /cryptography | |
parent | 8bae14b62bc5da70ddfd9cd587f016b8d8a0425a (diff) | |
parent | 572cb46dcca8d1b06f44ab2135f866002b3e32e2 (diff) | |
download | cryptography-d8c8f7cde6b43d08f39cd11cd2e2dd3ed7feb5a5.tar.gz cryptography-d8c8f7cde6b43d08f39cd11cd2e2dd3ed7feb5a5.tar.bz2 cryptography-d8c8f7cde6b43d08f39cd11cd2e2dd3ed7feb5a5.zip |
Merge pull request #673 from reaperhulk/rsa-pkcs1-signature-only
Add RSA PKCS1 signing (and structure for PSS + verification)
Diffstat (limited to 'cryptography')
-rw-r--r-- | cryptography/exceptions.py | 4 | ||||
-rw-r--r-- | cryptography/hazmat/backends/multibackend.py | 6 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 116 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/padding.py | 22 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/rsa.py | 3 |
5 files changed, 150 insertions, 1 deletions
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index f9849e2f..b4962591 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -46,3 +46,7 @@ class InvalidKey(Exception): class InvalidToken(Exception): pass + + +class UnsupportedPadding(Exception): + pass diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 4de02026..f0017191 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -24,6 +24,7 @@ from cryptography.hazmat.backends.interfaces import ( @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) +@utils.register_interface(RSABackend) class MultiBackend(object): name = "multibackend" @@ -106,3 +107,8 @@ class MultiBackend(object): for b in self._filtered_backends(RSABackend): return b.generate_rsa_private_key(public_exponent, key_size) raise UnsupportedAlgorithm + + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + for b in self._filtered_backends(RSABackend): + return b.create_rsa_signature_ctx(private_key, padding, algorithm) + raise UnsupportedAlgorithm diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index de6f841c..00fdc266 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -17,7 +17,8 @@ import itertools from cryptography import utils from cryptography.exceptions import ( - UnsupportedAlgorithm, InvalidTag, InternalError + UnsupportedAlgorithm, InvalidTag, InternalError, AlreadyFinalized, + UnsupportedPadding ) from cryptography.hazmat.backends.interfaces import ( CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend @@ -321,6 +322,23 @@ class Backend(object): modulus=self._bn_to_int(ctx.n), ) + def _rsa_cdata_from_private_key(self, private_key): + ctx = self._lib.RSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.RSA_free) + ctx.p = self._int_to_bn(private_key.p) + ctx.q = self._int_to_bn(private_key.q) + ctx.d = self._int_to_bn(private_key.d) + ctx.e = self._int_to_bn(private_key.e) + ctx.n = self._int_to_bn(private_key.n) + ctx.dmp1 = self._int_to_bn(private_key.dmp1) + ctx.dmq1 = self._int_to_bn(private_key.dmq1) + ctx.iqmp = self._int_to_bn(private_key.iqmp) + return ctx + + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + return _RSASignatureContext(self, private_key, padding, algorithm) + class GetCipherByName(object): def __init__(self, fmt): @@ -572,4 +590,100 @@ class _HMACContext(object): return self._backend._ffi.buffer(buf)[:] +@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 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 + else: + raise UnsupportedPadding( + "{0} is not supported by this backend".format(padding.name) + ) + + self._padding = padding + self._algorithm = algorithm + self._hash_ctx = _HashContext(backend, self._algorithm) + + def update(self, data): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + + self._hash_ctx.update(data) + + def finalize(self): + if self._hash_ctx is None: + raise AlreadyFinalized("Context has already been finalized") + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free) + rsa_cdata = backend._rsa_cdata_from_private_key(self._private_key) + res = self._backend._lib.RSA_blinding_on( + rsa_cdata, self._backend._ffi.NULL) + assert res == 1 + res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) + assert res == 1 + 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, rsa_cdata, evp_md) + + def _finalize_pkey_ctx(self, evp_pkey, pkey_size, rsa_cdata, evp_md): + pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new( + evp_pkey, self._backend._ffi.NULL + ) + assert pkey_ctx != self._backend._ffi.NULL + res = self._backend._lib.EVP_PKEY_sign_init(pkey_ctx) + assert res == 1 + res = self._backend._lib.EVP_PKEY_CTX_set_signature_md( + pkey_ctx, evp_md) + assert res > 0 + + res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( + pkey_ctx, self._padding_enum) + assert res > 0 + data_to_sign = self._hash_ctx.finalize() + self._hash_ctx = None + buflen = self._backend._ffi.new("size_t *") + res = self._backend._lib.EVP_PKEY_sign( + pkey_ctx, + self._backend._ffi.NULL, + buflen, + data_to_sign, + len(data_to_sign) + ) + assert res == 1 + 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 + return self._backend._ffi.buffer(buf)[:] + + def _finalize_pkcs1(self, evp_pkey, pkey_size, rsa_cdata, evp_md): + sig_buf = self._backend._ffi.new("char[]", pkey_size) + sig_len = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_SignFinal( + self._hash_ctx._ctx, + sig_buf, + sig_len, + evp_pkey + ) + self._hash_ctx.finalize() + self._hash_ctx = None + assert res == 1 + return self._backend._ffi.buffer(sig_buf)[:sig_len[0]] + + backend = Backend() diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 00000000..6bafe314 --- /dev/null +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,22 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +from cryptography import utils +from cryptography.hazmat.primitives import interfaces + + +@utils.register_interface(interfaces.AsymmetricPadding) +class PKCS1v15(object): + name = "EMSA-PKCS1-v1_5" diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py index 23572a53..2f9e4247 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -135,6 +135,9 @@ class RSAPrivateKey(object): def generate(cls, public_exponent, key_size, backend): return backend.generate_rsa_private_key(public_exponent, key_size) + def signer(self, padding, algorithm, backend): + return backend.create_rsa_signature_ctx(self, padding, algorithm) + @property def key_size(self): return _bit_length(self.modulus) |