From f67429b9d199931eb695524724a947847ed1f808 Mon Sep 17 00:00:00 2001 From: Aviv Palivoda Date: Thu, 30 Jun 2016 21:42:46 +0300 Subject: One shot sign/verify DSA (#3003) * Add sign and verify methods to DSA * Documented DSA sign/verify methods * Added CHANGELOG entry --- CHANGELOG.rst | 5 ++ docs/hazmat/primitives/asymmetric/dsa.rst | 53 ++++++++++++++++++++++ src/cryptography/hazmat/backends/openssl/dsa.py | 10 ++++ .../hazmat/primitives/asymmetric/dsa.py | 12 +++++ tests/hazmat/primitives/test_dsa.py | 20 ++++++++ 5 files changed, 100 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 28530853..f044c7e7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,11 @@ Changelog * Added :func:`~cryptography.hazmat.primitives.asymmetric.padding.calculate_max_pss_salt_length`. +* Added "one shot" + :meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey.sign` + and + :meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey.verify` + methods to DSA keys. 1.4 - 2016-06-04 diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst index 93d0db6f..0eb68ce6 100644 --- a/docs/hazmat/primitives/asymmetric/dsa.rst +++ b/docs/hazmat/primitives/asymmetric/dsa.rst @@ -79,6 +79,16 @@ provider. >>> signer.update(data) >>> signature = signer.finalize() +There is a shortcut to sign sufficiently short messages directly: + +.. doctest:: + + >>> data = b"this is some data I'd like to sign" + >>> signature = private_key.sign( + ... data, + ... hashes.SHA256() + ... ) + The ``signature`` is a ``bytes`` object, whose contents is DER encoded as described in :rfc:`3279`. This can be decoded using :func:`~cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature`. @@ -102,6 +112,16 @@ You can get a public key object with >>> verifier.update(data) >>> verifier.verify() +There is a shortcut to verify sufficiently short messages directly: + +.. doctest:: + + >>> public_key.verify( + ... signature, + ... data, + ... hashes.SHA256() + ... ) + ``verifier()`` takes the signature in the same format as is returned by ``signer.finalize()``. @@ -289,6 +309,21 @@ Key interfaces The bit length of :attr:`~DSAParameterNumbers.q`. + .. method:: sign(data, algorithm) + + .. versionadded:: 1.5 + + 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 algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + provider. + + :return: bytes: Signature. + .. class:: DSAPrivateKeyWithSerialization @@ -400,6 +435,24 @@ Key interfaces :return bytes: Serialized key. + .. method:: verify(signature, data, algorithm) + + .. versionadded:: 1.5 + + 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 algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + provider. + + :raises cryptography.exceptions.InvalidSignature: If the signature does + not validate. + .. class:: DSAPublicKeyWithSerialization diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py index 1012d044..43702861 100644 --- a/src/cryptography/hazmat/backends/openssl/dsa.py +++ b/src/cryptography/hazmat/backends/openssl/dsa.py @@ -197,6 +197,11 @@ class _DSAPrivateKey(object): self._dsa_cdata ) + def sign(self, data, algorithm): + signer = self.signer(algorithm) + signer.update(data) + return signer.finalize() + @utils.register_interface(dsa.DSAPublicKeyWithSerialization) class _DSAPublicKey(object): @@ -263,3 +268,8 @@ class _DSAPublicKey(object): self._evp_pkey, None ) + + def verify(self, signature, data, algorithm): + verifier = self.verifier(signature, algorithm) + verifier.update(data) + verifier.verify() diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py index 511d3464..03e6a53e 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -55,6 +55,12 @@ class DSAPrivateKey(object): Returns an AsymmetricSignatureContext used for signing data. """ + @abc.abstractmethod + def sign(self, data, algorithm): + """ + Signs the data + """ + @six.add_metaclass(abc.ABCMeta) class DSAPrivateKeyWithSerialization(DSAPrivateKey): @@ -103,6 +109,12 @@ class DSAPublicKey(object): Returns the key serialized as bytes. """ + @abc.abstractmethod + def verify(self, signature, data, algorithm): + """ + Verifies the signature of the data. + """ + DSAPublicKeyWithSerialization = DSAPublicKey diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index 46c86d54..dde60607 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -606,6 +606,16 @@ class TestDSAVerification(object): with pytest.raises(AlreadyFinalized): verifier.update(b"more data") + def test_verify(self, backend): + message = b"one little message" + algorithm = hashes.SHA1() + private_key = DSA_KEY_1024.private_key(backend) + signer = private_key.signer(algorithm) + signer.update(message) + signature = signer.finalize() + public_key = private_key.public_key() + public_key.verify(signature, message, algorithm) + @pytest.mark.requires_backend_interface(interface=DSABackend) class TestDSASignature(object): @@ -661,6 +671,16 @@ class TestDSASignature(object): with pytest.raises(AlreadyFinalized): signer.update(b"more data") + def test_sign(self, backend): + private_key = DSA_KEY_1024.private_key(backend) + message = b"one little message" + algorithm = hashes.SHA1() + signature = private_key.sign(message, algorithm) + public_key = private_key.public_key() + verifier = public_key.verifier(signature, algorithm) + verifier.update(message) + verifier.verify() + class TestDSANumbers(object): def test_dsa_parameter_numbers(self): -- cgit v1.2.3