From 4ccceaf4484dce24c5f0994b52079293a5fdb37c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 11:26:37 -0600 Subject: add RSA PKCS1 signing (and structure for PSS + verification) --- cryptography/exceptions.py | 4 + cryptography/hazmat/backends/openssl/backend.py | 120 ++++++++++++++++++++- .../hazmat/primitives/asymmetric/padding.py | 22 ++++ cryptography/hazmat/primitives/asymmetric/rsa.py | 3 + docs/exceptions.rst | 6 ++ docs/hazmat/primitives/asymmetric/index.rst | 10 ++ docs/hazmat/primitives/asymmetric/padding.rst | 20 ++++ docs/hazmat/primitives/asymmetric/rsa.rst | 107 ++++++++++++++++++ docs/hazmat/primitives/index.rst | 2 +- docs/hazmat/primitives/rsa.rst | 77 ------------- tests/hazmat/primitives/test_rsa.py | 60 +++++++++++ 11 files changed, 352 insertions(+), 79 deletions(-) create mode 100644 cryptography/hazmat/primitives/asymmetric/padding.py create mode 100644 docs/hazmat/primitives/asymmetric/index.rst create mode 100644 docs/hazmat/primitives/asymmetric/padding.rst create mode 100644 docs/hazmat/primitives/asymmetric/rsa.rst delete mode 100644 docs/hazmat/primitives/rsa.rst diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index f9849e2f..1144cb94 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -46,3 +46,7 @@ class InvalidKey(Exception): class InvalidToken(Exception): pass + + +class UnsupportedAsymmetricPadding(Exception): + pass diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index de6f841c..126d8f0c 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, + UnsupportedAsymmetricPadding ) from cryptography.hazmat.backends.interfaces import ( CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend @@ -321,6 +322,30 @@ 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 _rsa_cdata_from_public_key(self, public_key): + ctx = self._lib.RSA_new() + ctx = self._ffi.gc(ctx, self._lib.RSA_free) + ctx.e = self._int_to_bn(public_key.e) + ctx.n = self._int_to_bn(public_key.n) + 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 +597,97 @@ 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 interface 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 UnsupportedAsymmetricPadding + + 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 was already finalized") + + self._hash_ctx.update(data) + + def finalize(self): + if self._hash_ctx is None: + raise AlreadyFinalized("Context was already 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 = 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..ca00e94b --- /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 PKCS1(object): + name = "EMSA-PKCS1-v1_5" diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py index 01218592..a63d4308 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -135,6 +135,9 @@ class RSAPrivateKey(object): def generate(self, 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) diff --git a/docs/exceptions.rst b/docs/exceptions.rst index 8ca9df29..38bd0e47 100644 --- a/docs/exceptions.rst +++ b/docs/exceptions.rst @@ -42,3 +42,9 @@ Exceptions This is raised when the verify method of a one time password function's computed token does not match the expected token. + + +.. class:: UnsupportedAsymmetricPadding + + This is raised when the chosen asymmetric padding is not supported by the + backend. diff --git a/docs/hazmat/primitives/asymmetric/index.rst b/docs/hazmat/primitives/asymmetric/index.rst new file mode 100644 index 00000000..10319fad --- /dev/null +++ b/docs/hazmat/primitives/asymmetric/index.rst @@ -0,0 +1,10 @@ +.. hazmat:: + +Asymmetric Algorithms +===================== + +.. toctree:: + :maxdepth: 1 + + rsa + padding diff --git a/docs/hazmat/primitives/asymmetric/padding.rst b/docs/hazmat/primitives/asymmetric/padding.rst new file mode 100644 index 00000000..d3f713ae --- /dev/null +++ b/docs/hazmat/primitives/asymmetric/padding.rst @@ -0,0 +1,20 @@ +.. hazmat:: + +Padding +======= + +.. currentmodule:: cryptography.hazmat.primitives.asymmetric.padding + +.. warning:: + `Padding is critical`_ when signing or encrypting data using RSA. Without + correct padding signatures can be forged, messages decrypted, and private + keys compromised. + +.. class:: PKCS1() + + .. versionadded:: 0.3 + + PKCS1 (also known as PKCS1 v1.5) is a simple padding scheme developed for + use with RSA keys. It is also defined in :rfc:`3447`. + +.. _`Padding is critical`: http://rdist.root.org/2009/10/06/why-rsa-encryption-padding-is-critical/ diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst new file mode 100644 index 00000000..82cf3528 --- /dev/null +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -0,0 +1,107 @@ +.. hazmat:: + +RSA +=== + +.. currentmodule:: cryptography.hazmat.primitives.asymmetric.rsa + +`RSA`_ is a `public-key`_ algorithm for encrypting and signing messages. + +.. class:: RSAPrivateKey(p, q, private_exponent, dmp1, dmq1, iqmp, public_exponent, modulus) + + .. versionadded:: 0.2 + + An RSA private key is required for decryption and signing of messages. + + You should use :meth:`~generate` to generate new keys. + + .. warning:: + This method only checks a limited set of properties of its arguments. + Using an RSA private key that you do not trust or with incorrect + parameters may lead to insecure operation, crashes, and other undefined + behavior. We recommend that you only ever load private keys that were + generated with software you trust. + + + This class conforms to the + :class:`~cryptography.hazmat.primitives.interfaces.RSAPrivateKey` + interface. + + :raises TypeError: This is raised when the arguments are not all integers. + + :raises ValueError: This is raised when the values of ``p``, ``q``, + ``private_exponent``, ``public_exponent``, or + ``modulus`` do not match the bounds specified in + :rfc:`3447`. + + .. classmethod:: generate(public_exponent, key_size, backend) + + Generate a new ``RSAPrivateKey`` instance using ``backend``. + + :param int public_exponent: The public exponent of the new key. + Usually one of the small Fermat primes 3, 5, 17, 257, 65537. If in + doubt you should `use 65537`_. + :param int key_size: The length of the modulus in bits. For keys + generated in 2014 this should be `at least 2048`_. (See page 41.) + Must be at least 512. Some backends may have additional + limitations. + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` + provider. + :return: A new instance of ``RSAPrivateKey``. + + .. method:: signer(padding, algorithm, backend) + + .. versionadded:: 0.3 + + :param padding: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + :param algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` + provider. + + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` + provider. + + :returns: + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext` + + .. doctest:: + + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.asymmetric import rsa, padding + >>> private_key = rsa.RSAPrivateKey.generate(65537, 2048, default_backend()) + >>> signer = private_key.signer(padding.PKCS1(), hashes.SHA256(), default_backend()) + >>> signer.update(b"this is some data I'd like") + >>> signer.update(b" to sign") + >>> signature = signer.finalize() + + +.. class:: RSAPublicKey(public_exponent, modulus) + + .. versionadded:: 0.2 + + An RSA public key is required for encryption and verification of messages. + + Normally you do not need to directly construct public keys because you'll + be loading them from a file, generating them automatically or receiving + them from a 3rd party. + + This class conforms to the + :class:`~cryptography.hazmat.primitives.interfaces.RSAPublicKey` + interface. + + :raises TypeError: This is raised when the arguments are not all integers. + + :raises ValueError: This is raised when the values of ``public_exponent`` + or ``modulus`` do not match the bounds specified in + :rfc:`3447`. + +.. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem) +.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography +.. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html +.. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index 5199d493..90deec8b 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -11,7 +11,7 @@ Primitives symmetric-encryption padding key-derivation-functions - rsa + asymmetric/index constant-time interfaces twofactor diff --git a/docs/hazmat/primitives/rsa.rst b/docs/hazmat/primitives/rsa.rst deleted file mode 100644 index 4e1f8e49..00000000 --- a/docs/hazmat/primitives/rsa.rst +++ /dev/null @@ -1,77 +0,0 @@ -.. hazmat:: - -RSA -=== - -.. currentmodule:: cryptography.hazmat.primitives.asymmetric.rsa - -`RSA`_ is a `public-key`_ algorithm for encrypting and signing messages. - -.. class:: RSAPrivateKey(p, q, private_exponent, dmp1, dmq1, iqmp, public_exponent, modulus) - - .. versionadded:: 0.2 - - An RSA private key is required for decryption and signing of messages. - - You should use :meth:`~generate` to generate new keys. - - .. warning:: - This method only checks a limited set of properties of its arguments. - Using an RSA private key that you do not trust or with incorrect - parameters may lead to insecure operation, crashes, and other undefined - behavior. We recommend that you only ever load private keys that were - generated with software you trust. - - - This class conforms to the - :class:`~cryptography.hazmat.primitives.interfaces.RSAPrivateKey` - interface. - - :raises TypeError: This is raised when the arguments are not all integers. - - :raises ValueError: This is raised when the values of ``p``, ``q``, - ``private_exponent``, ``public_exponent``, or - ``modulus`` do not match the bounds specified in - :rfc:`3447`. - - .. classmethod:: generate(public_exponent, key_size, backend) - - Generate a new ``RSAPrivateKey`` instance using ``backend``. - - :param int public_exponent: The public exponent of the new key. - Usually one of the small Fermat primes 3, 5, 17, 257, 65537. If in - doubt you should `use 65537`_. - :param int key_size: The length of the modulus in bits. For keys - generated in 2014 this should be `at least 2048`_. (See page 41.) - Must be at least 512. Some backends may have additional - limitations. - :param backend: A - :class:`~cryptography.hazmat.backends.interfaces.RSABackend` - provider. - :return: A new instance of ``RSAPrivateKey``. - - -.. class:: RSAPublicKey(public_exponent, modulus) - - .. versionadded:: 0.2 - - An RSA public key is required for encryption and verification of messages. - - Normally you do not need to directly construct public keys because you'll - be loading them from a file, generating them automatically or receiving - them from a 3rd party. - - This class conforms to the - :class:`~cryptography.hazmat.primitives.interfaces.RSAPublicKey` - interface. - - :raises TypeError: This is raised when the arguments are not all integers. - - :raises ValueError: This is raised when the values of ``public_exponent`` - or ``modulus`` do not match the bounds specified in - :rfc:`3447`. - -.. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem) -.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography -.. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html -.. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index df3a70f5..b2ea9bad 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -14,16 +14,25 @@ from __future__ import absolute_import, division, print_function +import binascii import itertools import os import pytest +from cryptography import exceptions, utils +from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import padding from ...utils import load_pkcs1_vectors, load_vectors_from_file +@utils.register_interface(interfaces.AsymmetricPadding) +class FakePadding(object): + name = "UNSUPPORTED-PADDING" + + def _modinv(e, m): """ Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 @@ -55,6 +64,17 @@ def _check_rsa_private_key(skey): assert skey.key_size == pkey.key_size +def _flatten_pkcs1_examples(vectors): + flattened_vectors = [] + for vector in vectors: + examples = vector[0].pop("examples") + for example in examples: + merged_vector = (vector[0], vector[1], example) + flattened_vectors.append(merged_vector) + + return flattened_vectors + + def test_modular_inverse(): p = int( "d1f9f6c09fd3d38987f7970247b85a6da84907753d42ec52bc23b745093f4fff5cff3" @@ -363,3 +383,43 @@ class TestRSA(object): # Test a public_exponent that is not odd. with pytest.raises(ValueError): rsa.RSAPublicKey(public_exponent=6, modulus=15) + + +@pytest.mark.rsa +class TestRSASignature(object): + @pytest.mark.parametrize( + "pkcs1_example", + _flatten_pkcs1_examples(load_vectors_from_file( + os.path.join( + "asymmetric", "RSA", "pkcs1v15sign-vectors.txt"), + load_pkcs1_vectors + )) + ) + def test_pkcs1v15_signing(self, pkcs1_example, backend): + private, public, example = pkcs1_example + private_key = rsa.RSAPrivateKey(**private) + public_key = rsa.RSAPublicKey(**public) + signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) + signer.update(binascii.unhexlify(example["message"])) + signature = signer.finalize() + assert binascii.hexlify(signature) == example["signature"] + + def test_use_after_finalize(self, backend): + private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) + signer.update(b"sign me") + signer.finalize() + with pytest.raises(exceptions.AlreadyFinalized): + signer.finalize() + with pytest.raises(exceptions.AlreadyFinalized): + signer.update(b"more data") + + def test_unsupported_padding(self, backend): + private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + with pytest.raises(exceptions.UnsupportedAsymmetricPadding): + private_key.signer(FakePadding(), hashes.SHA1(), backend) + + def test_padding_incorrect_type(self, backend): + private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + with pytest.raises(TypeError): + private_key.signer("notpadding", hashes.SHA1(), backend) -- cgit v1.2.3 From da19e2b59957870fdb04e1027a9221b8fa2c810e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 11:42:03 -0600 Subject: some style fixes --- tests/hazmat/primitives/test_rsa.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index b2ea9bad..11fb3729 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -29,7 +29,7 @@ from ...utils import load_pkcs1_vectors, load_vectors_from_file @utils.register_interface(interfaces.AsymmetricPadding) -class FakePadding(object): +class DummyPadding(object): name = "UNSUPPORTED-PADDING" @@ -415,9 +415,13 @@ class TestRSASignature(object): signer.update(b"more data") def test_unsupported_padding(self, backend): - private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) with pytest.raises(exceptions.UnsupportedAsymmetricPadding): - private_key.signer(FakePadding(), hashes.SHA1(), backend) + private_key.signer(DummyPadding(), hashes.SHA1(), backend) def test_padding_incorrect_type(self, backend): private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) -- cgit v1.2.3 From a33df8ed655b507e72bdb4aab377dcf7a81954c5 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 15:05:37 -0600 Subject: fix pep8 --- tests/hazmat/primitives/test_rsa.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 11fb3729..8229b76b 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -398,7 +398,6 @@ class TestRSASignature(object): def test_pkcs1v15_signing(self, pkcs1_example, backend): private, public, example = pkcs1_example private_key = rsa.RSAPrivateKey(**private) - public_key = rsa.RSAPublicKey(**public) signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) signer.update(binascii.unhexlify(example["message"])) signature = signer.finalize() -- cgit v1.2.3 From dff1d43d0ae65da599eb60f99d411112b39fc8e9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 15:09:56 -0600 Subject: add basic multibackend so we can do signatures using default_backend --- cryptography/hazmat/backends/multibackend.py | 7 +++++++ tests/hazmat/backends/test_multibackend.py | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 4de02026..eb58dd68 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,9 @@ 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/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index ce77ce2f..e3f83d3a 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -20,6 +20,7 @@ from cryptography.hazmat.backends.interfaces import ( ) from cryptography.hazmat.backends.multibackend import MultiBackend from cryptography.hazmat.primitives import hashes, hmac +from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes @@ -85,6 +86,9 @@ class DummyRSABackend(object): def generate_rsa_private_key(self, public_exponent, private_key): pass + def create_rsa_signature_ctx(self, private_key, padding, algorithm): + pass + class TestMultiBackend(object): def test_ciphers(self): @@ -158,6 +162,13 @@ class TestMultiBackend(object): key_size=1024, public_exponent=65537 ) + backend.create_rsa_signature_ctx("private_key", padding.PKCS1(), + hashes.MD5()) + backend = MultiBackend([]) with pytest.raises(UnsupportedAlgorithm): backend.generate_rsa_private_key(key_size=1024, public_exponent=3) + + with pytest.raises(UnsupportedAlgorithm): + backend.create_rsa_signature_ctx("private_key", padding.PKCS1(), + hashes.MD5()) -- cgit v1.2.3 From 0849575a216238410853f97780bb8646688078b9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 15:11:14 -0600 Subject: keyword args --- tests/hazmat/primitives/test_rsa.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 8229b76b..14c3acf0 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -404,7 +404,11 @@ class TestRSASignature(object): assert binascii.hexlify(signature) == example["signature"] def test_use_after_finalize(self, backend): - private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) signer.update(b"sign me") signer.finalize() -- cgit v1.2.3 From 0d6722a1ef6ad504d4f669f9d7a8c0952a38445b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 15:58:09 -0600 Subject: more pep8 --- cryptography/hazmat/backends/multibackend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index eb58dd68..f0017191 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -112,4 +112,3 @@ class MultiBackend(object): for b in self._filtered_backends(RSABackend): return b.create_rsa_signature_ctx(private_key, padding, algorithm) raise UnsupportedAlgorithm - -- cgit v1.2.3 From 160e51a207db264c48162b02e22005356100a396 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 17:57:06 -0600 Subject: remove rsa_cdata_from_public_key (unused until verify PR) --- cryptography/hazmat/backends/openssl/backend.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 126d8f0c..64f8dc25 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -336,13 +336,6 @@ class Backend(object): ctx.iqmp = self._int_to_bn(private_key.iqmp) return ctx - def _rsa_cdata_from_public_key(self, public_key): - ctx = self._lib.RSA_new() - ctx = self._ffi.gc(ctx, self._lib.RSA_free) - ctx.e = self._int_to_bn(public_key.e) - ctx.n = self._int_to_bn(public_key.n) - return ctx - def create_rsa_signature_ctx(self, private_key, padding, algorithm): return _RSASignatureContext(self, private_key, padding, algorithm) -- cgit v1.2.3 From 8dd9713ae2a69a3e870275c088df08ce2a50dce9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 23 Feb 2014 19:26:05 -0600 Subject: incorporate review feedback. kwarg! --- docs/hazmat/primitives/asymmetric/rsa.rst | 6 +++++- tests/hazmat/primitives/test_rsa.py | 11 ++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 82cf3528..5e71c7c8 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -74,7 +74,11 @@ RSA >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa, padding - >>> private_key = rsa.RSAPrivateKey.generate(65537, 2048, default_backend()) + >>> private_key = rsa.RSAPrivateKey.generate( + ... public_exponent=65537, + ... key_size=2048, + ... backend=default_backend() + ... ) >>> signer = private_key.signer(padding.PKCS1(), hashes.SHA256(), default_backend()) >>> signer.update(b"this is some data I'd like") >>> signer.update(b" to sign") diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 14c3acf0..d190ea95 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -397,7 +397,16 @@ class TestRSASignature(object): ) def test_pkcs1v15_signing(self, pkcs1_example, backend): private, public, example = pkcs1_example - private_key = rsa.RSAPrivateKey(**private) + private_key = rsa.RSAPrivateKey( + p=private["p"], + q=private["q"], + private_exponent=private["private_exponent"], + dmp1=private["dmp1"], + dmq1=private["dmq1"], + iqmp=private["iqmp"], + public_exponent=private["public_exponent"], + modulus=private["modulus"] + ) signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) signer.update(binascii.unhexlify(example["message"])) signature = signer.finalize() -- cgit v1.2.3 From 0377f5a78de949f2f1e719ac89cf8b98b910bf81 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 24 Feb 2014 19:04:46 -0600 Subject: rename PKCS1->PKCS1v15 & UnsupportedAsymmetricPadding->UnsupportedPadding --- cryptography/exceptions.py | 2 +- cryptography/hazmat/primitives/asymmetric/padding.py | 2 +- docs/exceptions.rst | 5 ++--- docs/hazmat/primitives/asymmetric/padding.rst | 6 +++--- docs/hazmat/primitives/asymmetric/rsa.rst | 2 +- tests/hazmat/primitives/test_rsa.py | 6 +++--- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index 1144cb94..b4962591 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -48,5 +48,5 @@ class InvalidToken(Exception): pass -class UnsupportedAsymmetricPadding(Exception): +class UnsupportedPadding(Exception): pass diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py index ca00e94b..6bafe314 100644 --- a/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -18,5 +18,5 @@ from cryptography.hazmat.primitives import interfaces @utils.register_interface(interfaces.AsymmetricPadding) -class PKCS1(object): +class PKCS1v15(object): name = "EMSA-PKCS1-v1_5" diff --git a/docs/exceptions.rst b/docs/exceptions.rst index 38bd0e47..0982426f 100644 --- a/docs/exceptions.rst +++ b/docs/exceptions.rst @@ -44,7 +44,6 @@ Exceptions computed token does not match the expected token. -.. class:: UnsupportedAsymmetricPadding +.. class:: UnsupportedPadding - This is raised when the chosen asymmetric padding is not supported by the - backend. + This is raised when the chosen padding is not supported by the backend. diff --git a/docs/hazmat/primitives/asymmetric/padding.rst b/docs/hazmat/primitives/asymmetric/padding.rst index d3f713ae..7aec3bd3 100644 --- a/docs/hazmat/primitives/asymmetric/padding.rst +++ b/docs/hazmat/primitives/asymmetric/padding.rst @@ -10,11 +10,11 @@ Padding correct padding signatures can be forged, messages decrypted, and private keys compromised. -.. class:: PKCS1() +.. class:: PKCS1v15() .. versionadded:: 0.3 - PKCS1 (also known as PKCS1 v1.5) is a simple padding scheme developed for - use with RSA keys. It is also defined in :rfc:`3447`. + PKCS1 v1.5 (also known as simply PKCS1) is a simple padding scheme + developed for use with RSA keys. It is defined in :rfc:`3447`. .. _`Padding is critical`: http://rdist.root.org/2009/10/06/why-rsa-encryption-padding-is-critical/ diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 5e71c7c8..64928878 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -79,7 +79,7 @@ RSA ... key_size=2048, ... backend=default_backend() ... ) - >>> signer = private_key.signer(padding.PKCS1(), hashes.SHA256(), default_backend()) + >>> signer = private_key.signer(padding.PKCS1v15(), hashes.SHA256(), default_backend()) >>> signer.update(b"this is some data I'd like") >>> signer.update(b" to sign") >>> signature = signer.finalize() diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index d190ea95..c66a1581 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -407,7 +407,7 @@ class TestRSASignature(object): public_exponent=private["public_exponent"], modulus=private["modulus"] ) - signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) + signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend) signer.update(binascii.unhexlify(example["message"])) signature = signer.finalize() assert binascii.hexlify(signature) == example["signature"] @@ -418,7 +418,7 @@ class TestRSASignature(object): key_size=512, backend=backend ) - signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend) + signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend) signer.update(b"sign me") signer.finalize() with pytest.raises(exceptions.AlreadyFinalized): @@ -432,7 +432,7 @@ class TestRSASignature(object): key_size=512, backend=backend ) - with pytest.raises(exceptions.UnsupportedAsymmetricPadding): + with pytest.raises(exceptions.UnsupportedPadding): private_key.signer(DummyPadding(), hashes.SHA1(), backend) def test_padding_incorrect_type(self, backend): -- cgit v1.2.3 From 8584d6066ae50a0c9917d409df994e469f3fbbee Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 24 Feb 2014 19:05:52 -0600 Subject: change the rest of the UnsupportedAsymmetricPadding instances --- cryptography/hazmat/backends/openssl/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 64f8dc25..d7c90a8d 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -18,7 +18,7 @@ import itertools from cryptography import utils from cryptography.exceptions import ( UnsupportedAlgorithm, InvalidTag, InternalError, AlreadyFinalized, - UnsupportedAsymmetricPadding + UnsupportedPadding ) from cryptography.hazmat.backends.interfaces import ( CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend @@ -606,7 +606,7 @@ class _RSASignatureContext(object): else: self._finalize_method = self._finalize_pkcs1 else: - raise UnsupportedAsymmetricPadding + raise UnsupportedPadding self._padding = padding self._algorithm = algorithm -- cgit v1.2.3 From bfba58bf535048d9b29fd2b7b6044695aa02a4c7 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 24 Feb 2014 19:32:57 -0600 Subject: update multibackend with new PKCS1v15 class name --- tests/hazmat/backends/test_multibackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index e3f83d3a..be1e76e2 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -162,7 +162,7 @@ class TestMultiBackend(object): key_size=1024, public_exponent=65537 ) - backend.create_rsa_signature_ctx("private_key", padding.PKCS1(), + backend.create_rsa_signature_ctx("private_key", padding.PKCS1v15(), hashes.MD5()) backend = MultiBackend([]) @@ -170,5 +170,5 @@ class TestMultiBackend(object): backend.generate_rsa_private_key(key_size=1024, public_exponent=3) with pytest.raises(UnsupportedAlgorithm): - backend.create_rsa_signature_ctx("private_key", padding.PKCS1(), + backend.create_rsa_signature_ctx("private_key", padding.PKCS1v15(), hashes.MD5()) -- cgit v1.2.3 From 49e4c9c4f72311070aa444d7c35cfc27453e4ad9 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 24 Feb 2014 21:21:08 -0600 Subject: more kwargs --- tests/hazmat/primitives/test_rsa.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index c66a1581..647c51b4 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -436,6 +436,10 @@ class TestRSASignature(object): private_key.signer(DummyPadding(), hashes.SHA1(), backend) def test_padding_incorrect_type(self, backend): - private_key = rsa.RSAPrivateKey.generate(65537, 512, backend) + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) with pytest.raises(TypeError): private_key.signer("notpadding", hashes.SHA1(), backend) -- cgit v1.2.3 From c5b4d7239614ce3718a05601184276ca2691c84e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 25 Feb 2014 13:17:34 -0600 Subject: address review comments --- cryptography/hazmat/backends/openssl/backend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index d7c90a8d..61bfe624 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -606,7 +606,9 @@ class _RSASignatureContext(object): else: self._finalize_method = self._finalize_pkcs1 else: - raise UnsupportedPadding + raise UnsupportedPadding( + "{0} is not supported by this backend".format(padding.name) + ) self._padding = padding self._algorithm = algorithm @@ -678,6 +680,7 @@ class _RSASignatureContext(object): 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]] -- cgit v1.2.3 From 62a9217aa3f6cf34d4cafcd1d147082bc7c7918c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 25 Feb 2014 13:56:53 -0600 Subject: reorganize docs slightly --- docs/hazmat/primitives/asymmetric/rsa.rst | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 64928878..682820b3 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -54,20 +54,7 @@ RSA .. versionadded:: 0.3 - :param padding: An instance of a - :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` - provider. - - :param algorithm: An instance of a - :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` - provider. - - :param backend: A - :class:`~cryptography.hazmat.backends.interfaces.RSABackend` - provider. - - :returns: - :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext` + Sign data which can be verified later by others using the public key. .. doctest:: @@ -79,11 +66,30 @@ RSA ... key_size=2048, ... backend=default_backend() ... ) - >>> signer = private_key.signer(padding.PKCS1v15(), hashes.SHA256(), default_backend()) + >>> signer = private_key.signer( + ... padding.PKCS1v15(), + ... hashes.SHA256(), + ... default_backend() + ... ) >>> signer.update(b"this is some data I'd like") >>> signer.update(b" to sign") >>> signature = signer.finalize() + :param padding: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + :param algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` + provider. + + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` + provider. + + :returns: + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext` + .. class:: RSAPublicKey(public_exponent, modulus) -- cgit v1.2.3 From 572cb46dcca8d1b06f44ab2135f866002b3e32e2 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 25 Feb 2014 18:03:03 -0600 Subject: some error msg improvements --- cryptography/hazmat/backends/openssl/backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 61bfe624..00fdc266 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -597,7 +597,7 @@ class _RSASignatureContext(object): self._private_key = private_key if not isinstance(padding, interfaces.AsymmetricPadding): raise TypeError( - "Expected interface of interfaces.AsymmetricPadding") + "Expected provider of interfaces.AsymmetricPadding") if padding.name == "EMSA-PKCS1-v1_5": if self._backend._lib.Cryptography_HAS_PKEY_CTX: @@ -616,13 +616,13 @@ class _RSASignatureContext(object): def update(self, data): if self._hash_ctx is None: - raise AlreadyFinalized("Context was already finalized") + raise AlreadyFinalized("Context has already been finalized") self._hash_ctx.update(data) def finalize(self): if self._hash_ctx is None: - raise AlreadyFinalized("Context was already finalized") + 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) -- cgit v1.2.3