Merge branch 'master' of https://github.com/pyca/cryptography into verify

Conflicts:
	tests/hazmat/primitives/test_hashes.py
	tests/hazmat/primitives/test_hmac.py
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..c71377d 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, digest):
+        if isinstance(digest, six.text_type):
+            raise TypeError("Unicode-objects must be encoded before verifying")
+        hash_digest = self.finalize()
+        if not constant_time.bytes_eq(digest, hash_digest):
+            raise InvalidSignature("Digest did not match hash 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..76d658a 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, signature):
+        if isinstance(signature, six.text_type):
+            raise TypeError("Unicode-objects must be encoded before verifying")
+        digest = self.finalize()
+        if not constant_time.bytes_eq(digest, signature):
+            raise InvalidSignature("Signature did not match digest.")
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index e87c9ca..c6377df 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -162,3 +162,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/exceptions.rst b/docs/exceptions.rst
index 087066b..8be2c48 100644
--- a/docs/exceptions.rst
+++ b/docs/exceptions.rst
@@ -8,6 +8,12 @@
     This is raised when a context is used after being finalized.
 
 
+.. class:: InvalidSignature
+
+    This is raised when the verify function of a hash function does not
+    compare equal.
+
+
 .. class:: NotYetFinalized
 
     This is raised when the AEAD tag property is accessed on a context
diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst
index 3834737..f00dd3f 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -70,6 +70,14 @@
 
         :return bytes: The message digest as bytes.
 
+    .. method:: verify(digest)
+
+        Finalize the current context and securely compare that digest to ``digest``.
+
+        :param bytes digest: Received hash digest
+        :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+        :raises cryptography.exceptions.InvalidSignature: If hash digest does not match digest
+
 
 .. _cryptographic-hash-algorithms:
 
diff --git a/docs/hazmat/primitives/hmac.rst b/docs/hazmat/primitives/hmac.rst
index 0547b7d..b8f94fd 100644
--- a/docs/hazmat/primitives/hmac.rst
+++ b/docs/hazmat/primitives/hmac.rst
@@ -71,3 +71,11 @@
 
         :return bytes: The message digest as bytes.
         :raises cryptography.exceptions.AlreadyFinalized:
+
+    .. method:: verify(signature)
+
+        Finalize the current context and securely compare digest to ``signature``.
+
+        :param bytes signature: The bytes of the HMAC signature recieved.
+        :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+        :raises cryptography.exceptions.InvalidSignature: If signature does not match digest
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index 45faaab..69d0773 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -20,7 +20,9 @@
 import six
 
 from cryptography import utils
-from cryptography.exceptions import AlreadyFinalized, UnsupportedAlgorithm
+from cryptography.exceptions import (
+    AlreadyFinalized, UnsupportedAlgorithm, InvalidSignature
+)
 from cryptography.hazmat.primitives import hashes, interfaces
 
 from .utils import generate_base_hash_test
@@ -64,6 +66,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(''))
+
     def test_unsupported_hash(self, backend):
         with pytest.raises(UnsupportedAlgorithm):
             hashes.Hash(UnsupportedDummyHash(), backend)
diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py
index 6d8cc27..7acb78b 100644
--- a/tests/hazmat/primitives/test_hmac.py
+++ b/tests/hazmat/primitives/test_hmac.py
@@ -20,7 +20,9 @@
 import six
 
 from cryptography import utils
-from cryptography.exceptions import AlreadyFinalized, UnsupportedAlgorithm
+from cryptography.exceptions import (
+    AlreadyFinalized, UnsupportedAlgorithm, InvalidSignature
+)
 from cryptography.hazmat.primitives import hashes, hmac, interfaces
 
 from .utils import generate_base_hmac_test
@@ -71,6 +73,29 @@
         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(''))
+
     def test_unsupported_hash(self, backend):
         with pytest.raises(UnsupportedAlgorithm):
             hmac.HMAC(b"key", UnsupportedDummyHash(), backend)