Add verify function to hmac and hashes.
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py
index e9d8819..44363c2 100644
--- a/cryptography/exceptions.py
+++ b/cryptography/exceptions.py
@@ -30,3 +30,7 @@
class InvalidTag(Exception):
pass
+
+
+class InvalidSignature(Exception):
+ pass
diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py
index bee188b..b3c626d 100644
--- a/cryptography/hazmat/primitives/hashes.py
+++ b/cryptography/hazmat/primitives/hashes.py
@@ -16,8 +16,8 @@
import six
from cryptography import utils
-from cryptography.exceptions import AlreadyFinalized
-from cryptography.hazmat.primitives import interfaces
+from cryptography.exceptions import AlreadyFinalized, InvalidSignature
+from cryptography.hazmat.primitives import constant_time, interfaces
@utils.register_interface(interfaces.HashContext)
@@ -55,6 +55,13 @@
self._ctx = None
return digest
+ def verify(self, sig):
+ if isinstance(sig, six.text_type):
+ raise TypeError("Unicode-objects must be encoded before verifying")
+ digest = self.finalize()
+ if not constant_time.bytes_eq(digest, sig):
+ raise InvalidSignature("Signature did not match digest.")
+
@utils.register_interface(interfaces.HashAlgorithm)
class SHA1(object):
diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py
index 618bccc..8ade84a 100644
--- a/cryptography/hazmat/primitives/hmac.py
+++ b/cryptography/hazmat/primitives/hmac.py
@@ -16,8 +16,8 @@
import six
from cryptography import utils
-from cryptography.exceptions import AlreadyFinalized
-from cryptography.hazmat.primitives import interfaces
+from cryptography.exceptions import AlreadyFinalized, InvalidSignature
+from cryptography.hazmat.primitives import constant_time, interfaces
@utils.register_interface(interfaces.HashContext)
@@ -57,3 +57,10 @@
digest = self._ctx.finalize()
self._ctx = None
return digest
+
+ def verify(self, sig):
+ if isinstance(sig, six.text_type):
+ raise TypeError("Unicode-objects must be encoded before verifying")
+ digest = self.finalize()
+ if not constant_time.bytes_eq(digest, sig):
+ raise InvalidSignature("Signature did not match digest.")
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index e3f4f58..76dc933 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -152,3 +152,9 @@
"""
return a HashContext that is a copy of the current context.
"""
+
+ @abc.abstractmethod
+ def verify(self, sig):
+ """
+ compare digest to sig and raise exception if not equal.
+ """
diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst
index 90ca198..02c7b5e 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -67,6 +67,13 @@
:return bytes: The message digest as bytes.
+ .. method:: verify(sig)
+
+ Finalize the current context and securely compare digest to sig.
+
+ :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+ :raises cryptography.exceptions.InvalidSignature: If sig does not match digest
+
.. _cryptographic-hash-algorithms:
diff --git a/docs/hazmat/primitives/hmac.rst b/docs/hazmat/primitives/hmac.rst
index 0c0d022..b556bd6 100644
--- a/docs/hazmat/primitives/hmac.rst
+++ b/docs/hazmat/primitives/hmac.rst
@@ -69,3 +69,10 @@
:return bytes: The message digest as bytes.
:raises cryptography.exceptions.AlreadyFinalized:
+
+ .. method:: verify(sig)
+
+ Finalize the current context and securely compare digest to sig.
+
+ :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+ :raises cryptography.exceptions.InvalidSignature: If sig does not match digest
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index ff42e8f..cd58b06 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -19,7 +19,7 @@
import six
-from cryptography.exceptions import AlreadyFinalized
+from cryptography.exceptions import AlreadyFinalized, InvalidSignature
from cryptography.hazmat.primitives import hashes
from .utils import generate_base_hash_test
@@ -57,6 +57,29 @@
with pytest.raises(AlreadyFinalized):
h.finalize()
+ def test_verify(self, backend):
+ h = hashes.Hash(hashes.SHA1(), backend=backend)
+ digest = h.finalize()
+
+ h = hashes.Hash(hashes.SHA1(), backend=backend)
+ h.verify(digest)
+
+ with pytest.raises(AlreadyFinalized):
+ h.verify(b'')
+
+ def test_invalid_verify(self, backend):
+ h = hashes.Hash(hashes.SHA1(), backend=backend)
+ with pytest.raises(InvalidSignature):
+ h.verify(b'')
+
+ with pytest.raises(AlreadyFinalized):
+ h.verify(b'')
+
+ def test_verify_reject_unicode(self, backend):
+ h = hashes.Hash(hashes.SHA1(), backend=backend)
+ with pytest.raises(TypeError):
+ h.verify(six.u(''))
+
class TestSHA1(object):
test_SHA1 = generate_base_hash_test(
diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py
index 992bcb1..4836018 100644
--- a/tests/hazmat/primitives/test_hmac.py
+++ b/tests/hazmat/primitives/test_hmac.py
@@ -19,7 +19,7 @@
import six
-from cryptography.exceptions import AlreadyFinalized
+from cryptography.exceptions import AlreadyFinalized, InvalidSignature
from cryptography.hazmat.primitives import hashes, hmac
from .utils import generate_base_hmac_test
@@ -63,3 +63,26 @@
with pytest.raises(AlreadyFinalized):
h.finalize()
+
+ def test_verify(self, backend):
+ h = hmac.HMAC(b'', hashes.SHA1(), backend=backend)
+ digest = h.finalize()
+
+ h = hmac.HMAC(b'', hashes.SHA1(), backend=backend)
+ h.verify(digest)
+
+ with pytest.raises(AlreadyFinalized):
+ h.verify(b'')
+
+ def test_invalid_verify(self, backend):
+ h = hmac.HMAC(b'', hashes.SHA1(), backend=backend)
+ with pytest.raises(InvalidSignature):
+ h.verify(b'')
+
+ with pytest.raises(AlreadyFinalized):
+ h.verify(b'')
+
+ def test_verify_reject_unicode(self, backend):
+ h = hmac.HMAC(b'', hashes.SHA1(), backend=backend)
+ with pytest.raises(TypeError):
+ h.verify(six.u(''))