Skip to content

Commit

Permalink
Add convenience methods to sign and verify w/ RSA (#2945)
Browse files Browse the repository at this point in the history
This patch adds wrapper methods to allow the user to sign and verify a
single message block without having to go through the multi-step
process of creating a signer or verifier, updating it with the one
message, and finalizing the result. This will make signing and
verifying data more user-friendly when only using small messages.

Partial bug #1529
  • Loading branch information
cmurphy authored and reaperhulk committed Jun 4, 2016
1 parent 4f125c1 commit 34d5c39
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 3 deletions.
76 changes: 73 additions & 3 deletions docs/hazmat/primitives/asymmetric/rsa.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ secure hash function and padding:
>>> signer.update(message)
>>> signature = signer.finalize()

There is a shortcut to sign sufficiently short messages directly:

.. doctest::

>>> message = b"A message I want to sign"
>>> signature = private_key.sign(
... message,
... padding.PSS(
... mgf=padding.MGF1(hashes.SHA256()),
... salt_length=padding.PSS.MAX_LENGTH
... ),
... hashes.SHA256()
... )

Valid paddings for signatures are
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`. ``PSS``
Expand Down Expand Up @@ -190,6 +204,20 @@ a public key to use in verification using
If the signature does not match, ``verify()`` will raise an
:class:`~cryptography.exceptions.InvalidSignature` exception.

There is a shortcut to verify sufficiently short messages directly:

.. doctest::

>>> public_key.verify(
... signature,
... message,
... padding.PSS(
... mgf=padding.MGF1(hashes.SHA256()),
... salt_length=padding.PSS.MAX_LENGTH
... ),
... hashes.SHA256()
... )

Encryption
~~~~~~~~~~

Expand Down Expand Up @@ -486,7 +514,8 @@ Key interfaces

.. versionadded:: 0.3

Sign data which can be verified later by others using the public key.
Get signer to sign data which can be verified later by others using
the public key.

:param padding: An instance of a
:class:`~cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding`
Expand Down Expand Up @@ -525,6 +554,25 @@ Key interfaces

The bit length of the modulus.

.. method:: sign(data, padding, algorithm)

.. versionadded:: 1.4

Sign one block of data which can be verified later by others using the
public key.

:param bytes data: The message string to sign.

:param padding: An instance of an
:class:`~cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding`
provider.

:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
provider.

:return: bytes: Signature.


.. class:: RSAPrivateKeyWithSerialization

Expand Down Expand Up @@ -580,8 +628,8 @@ Key interfaces

.. versionadded:: 0.3

Verify data was signed by the private key associated with this public
key.
Get verifier to verify data was signed by the private key associated
with this public key.

:param bytes signature: The signature to verify.

Expand Down Expand Up @@ -645,6 +693,28 @@ Key interfaces

:return bytes: Serialized key.

.. method:: verify(signature, data, padding, algorithm)

.. versionadded:: 1.4

Verify one block of data which can be verified later by others using the
public key.

:param bytes signature: The signature to verify.

:param bytes data: The message string that was signed.

:param padding: An instance of an
:class:`~cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding`
provider.

:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
provider.

:raises cryptography.exceptions.InvalidSignature: If the signature does
not validate.


.. class:: RSAPublicKeyWithSerialization

Expand Down
11 changes: 11 additions & 0 deletions src/cryptography/hazmat/backends/openssl/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ def private_bytes(self, encoding, format, encryption_algorithm):
self._rsa_cdata
)

def sign(self, data, padding, algorithm):
signer = self.signer(padding, algorithm)
signer.update(data)
signature = signer.finalize()
return signature


@utils.register_interface(RSAPublicKeyWithSerialization)
class _RSAPublicKey(object):
Expand Down Expand Up @@ -661,3 +667,8 @@ def public_bytes(self, encoding, format):
self._evp_pkey,
self._rsa_cdata
)

def verify(self, signature, data, padding, algorithm):
verifier = self.verifier(signature, padding, algorithm)
verifier.update(data)
verifier.verify()
12 changes: 12 additions & 0 deletions src/cryptography/hazmat/primitives/asymmetric/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ def public_key(self):
The RSAPublicKey associated with this private key.
"""

@abc.abstractmethod
def sign(self, data, padding, algorithm):
"""
Signs the data.
"""


@six.add_metaclass(abc.ABCMeta)
class RSAPrivateKeyWithSerialization(RSAPrivateKey):
Expand Down Expand Up @@ -88,6 +94,12 @@ def public_bytes(self, encoding, format):
Returns the key serialized as bytes.
"""

@abc.abstractmethod
def verify(self, signature, data, padding, algorithm):
"""
Verifies the signature of the data.
"""


RSAPublicKeyWithSerialization = RSAPublicKey

Expand Down
22 changes: 22 additions & 0 deletions tests/hazmat/primitives/test_rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,17 @@ def test_pkcs1_minimum_key_size(self, backend):
signer.update(b"no failure")
signer.finalize()

def test_sign(self, backend):
private_key = RSA_KEY_512.private_key(backend)
message = b"one little message"
pkcs = padding.PKCS1v15()
algorithm = hashes.SHA1()
signature = private_key.sign(message, pkcs, algorithm)
public_key = private_key.public_key()
verifier = public_key.verifier(signature, pkcs, algorithm)
verifier.update(message)
verifier.verify()


@pytest.mark.requires_backend_interface(interface=RSABackend)
class TestRSAVerification(object):
Expand Down Expand Up @@ -836,6 +847,17 @@ def test_pss_verify_salt_length_too_long(self, backend):
with pytest.raises(InvalidSignature):
verifier.verify()

def test_verify(self, backend):
private_key = RSA_KEY_512.private_key(backend)
message = b"one little message"
pkcs = padding.PKCS1v15()
algorithm = hashes.SHA1()
signer = private_key.signer(pkcs, algorithm)
signer.update(message)
signature = signer.finalize()
public_key = private_key.public_key()
public_key.verify(signature, message, pkcs, algorithm)


@pytest.mark.requires_backend_interface(interface=RSABackend)
class TestRSAPSSMGF1Verification(object):
Expand Down

0 comments on commit 34d5c39

Please sign in to comment.