aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Reid <dreid@dreid.org>2013-10-30 11:35:37 -0700
committerDavid Reid <dreid@dreid.org>2013-10-30 11:35:37 -0700
commit69aeb49a817d9a7437e238fc60b0be14a939be4f (patch)
treedbf862097e18036fbaeec232e447610d7c6d3df6
parentd5e9c8ddf0e644a740945a29cfa486ba25426300 (diff)
downloadcryptography-69aeb49a817d9a7437e238fc60b0be14a939be4f.tar.gz
cryptography-69aeb49a817d9a7437e238fc60b0be14a939be4f.tar.bz2
cryptography-69aeb49a817d9a7437e238fc60b0be14a939be4f.zip
Reduce the surface of the primitive hash interface. Add more interfaces, drop direct hashlib compatibility.
-rw-r--r--cryptography/hazmat/primitives/hashes.py54
-rw-r--r--cryptography/hazmat/primitives/interfaces.py46
-rw-r--r--tests/hazmat/primitives/test_hashes.py14
-rw-r--r--tests/hazmat/primitives/utils.py26
4 files changed, 94 insertions, 46 deletions
diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py
index 3ccb59d1..97e9bf4a 100644
--- a/cryptography/hazmat/primitives/hashes.py
+++ b/cryptography/hazmat/primitives/hashes.py
@@ -13,89 +13,95 @@
from __future__ import absolute_import, division, print_function
-import abc
+import six
-import binascii
+from cryptography.hazmat.primitives import interfaces
-import six
+@interfaces.register(interfaces.HashContext)
+class Hash(object):
+ def __init__(self, algorithm, backend=None, ctx=None):
+ self.algorithm = algorithm
-class BaseHash(six.with_metaclass(abc.ABCMeta)):
- def __init__(self, data=None, backend=None, ctx=None):
if backend is None:
from cryptography.hazmat.bindings import _default_backend
backend = _default_backend
+
self._backend = backend
+
if ctx is None:
- self._ctx = self._backend.hashes.create_ctx(self)
+ self._ctx = self._backend.hashes.create_ctx(self.algorithm)
else:
self._ctx = None
- if data is not None:
- self.update(data)
-
def update(self, data):
if isinstance(data, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
self._backend.hashes.update_ctx(self._ctx, data)
def copy(self):
- return self.__class__(backend=self._backend, ctx=self._copy_ctx())
-
- def digest(self):
- return self._backend.hashes.finalize_ctx(self._copy_ctx(),
- self.digest_size)
+ return self.__class__(self.algorithm, backend=self._backend,
+ ctx=self._copy_ctx())
- def hexdigest(self):
- return str(binascii.hexlify(self.digest()).decode("ascii"))
+ def finalize(self):
+ return self._backend.hashes.finalize_ctx(self._ctx,
+ self.algorithm.digest_size)
def _copy_ctx(self):
return self._backend.hashes.copy_ctx(self._ctx)
-class SHA1(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class SHA1(object):
name = "sha1"
digest_size = 20
block_size = 64
-class SHA224(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class SHA224(object):
name = "sha224"
digest_size = 28
block_size = 64
-class SHA256(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class SHA256(object):
name = "sha256"
digest_size = 32
block_size = 64
-class SHA384(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class SHA384(object):
name = "sha384"
digest_size = 48
block_size = 128
-class SHA512(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class SHA512(object):
name = "sha512"
digest_size = 64
block_size = 128
-class RIPEMD160(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class RIPEMD160(object):
name = "ripemd160"
digest_size = 20
block_size = 64
-class Whirlpool(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class Whirlpool(object):
name = "whirlpool"
digest_size = 64
block_size = 64
-class MD5(BaseHash):
+@interfaces.register(interfaces.HashAlgorithm)
+class MD5(object):
name = "md5"
digest_size = 16
block_size = 64
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 217490fd..ebf5e31e 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -59,3 +59,49 @@ class PaddingContext(six.with_metaclass(abc.ABCMeta)):
"""
finalize return bytes
"""
+
+
+class HashAlgorithm(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def name(self):
+ """
+ A string naming this algorithm. (ex. sha256, md5)
+ """
+
+ @abc.abstractproperty
+ def digest_size(self):
+ """
+ The size of the resulting digest in bytes.
+ """
+
+ @abc.abstractproperty
+ def block_size(self):
+ """
+ The internal block size of the hash algorithm in bytes.
+ """
+
+
+class HashContext(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def algorithm(self):
+ """
+ A HashAlgorithm that will be used by this context.
+ """
+
+ @abc.abstractmethod
+ def update(self, data):
+ """
+ hash data as bytes
+ """
+
+ @abc.abstractmethod
+ def finalize(self):
+ """
+ finalize this copy of the hash and return the digest as bytes.
+ """
+
+ @abc.abstractmethod
+ def copy(self):
+ """
+ return a HashContext that is a copy of the current context.
+ """
diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py
index 797fe4ff..c7a18b83 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -27,21 +27,17 @@ from .utils import generate_base_hash_test
class TestBaseHash(object):
def test_base_hash_reject_unicode(self, backend):
- m = hashes.SHA1(backend=backend)
+ m = hashes.Hash(hashes.SHA1, backend=backend)
with pytest.raises(TypeError):
m.update(six.u("\u00FC"))
- def test_base_hash_hexdigest_string_type(self, backend):
- m = hashes.SHA1(backend=backend, data=b"")
- assert isinstance(m.hexdigest(), str)
-
class TestCopyHash(object):
def test_copy_backend_object(self):
pretend_hashes = pretend.stub(copy_ctx=lambda a: "copiedctx")
pretend_backend = pretend.stub(hashes=pretend_hashes)
pretend_ctx = pretend.stub()
- h = hashes.SHA1(backend=pretend_backend, ctx=pretend_ctx)
+ h = hashes.Hash(hashes.SHA1, backend=pretend_backend, ctx=pretend_ctx)
assert h._backend is pretend_backend
assert h.copy()._backend is h._backend
@@ -51,7 +47,7 @@ class TestDefaultBackendSHA1(object):
"""
This test assumes the presence of SHA1 in the default backend.
"""
- h = hashes.SHA1()
+ h = hashes.Hash(hashes.SHA1)
assert h._backend is _default_backend
@@ -90,7 +86,7 @@ class TestSHA384(object):
hashes.SHA384,
digest_size=48,
block_size=128,
- only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
+ only_if=lambda backend: backend.hashes.supported(hashes.SHA384),
skip_message="Does not support SHA384",
)
@@ -120,7 +116,7 @@ class TestWhirlpool(object):
hashes.Whirlpool,
digest_size=64,
block_size=64,
- only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
+ only_if=lambda backend: backend.hashes.supported(hashes.Whirlpool),
skip_message="Does not support Whirlpool",
)
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index c51fef52..c02d1926 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -4,6 +4,7 @@ import os
import pytest
from cryptography.hazmat.bindings import _ALL_BACKENDS
+from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import hmac
from cryptography.hazmat.primitives.block import BlockCipher
@@ -70,21 +71,19 @@ def hash_test(backend, hash_cls, params, only_if, skip_message):
pytest.skip(skip_message)
msg = params[0]
md = params[1]
- m = hash_cls(backend=backend)
+ m = hashes.Hash(hash_cls, backend=backend)
m.update(binascii.unhexlify(msg))
- assert m.hexdigest() == md.replace(" ", "").lower()
- digst = hash_cls(backend=backend, data=binascii.unhexlify(msg)).hexdigest()
- assert digst == md.replace(" ", "").lower()
+ assert m.finalize() == binascii.unhexlify(md.replace(" ", "").lower())
-def generate_base_hash_test(hash_cls, digest_size, block_size,
+def generate_base_hash_test(algorithm, digest_size, block_size,
only_if=None, skip_message=None):
def test_base_hash(self):
for backend in _ALL_BACKENDS:
yield (
base_hash_test,
backend,
- hash_cls,
+ algorithm,
digest_size,
block_size,
only_if,
@@ -93,13 +92,14 @@ def generate_base_hash_test(hash_cls, digest_size, block_size,
return test_base_hash
-def base_hash_test(backend, digestmod, digest_size, block_size, only_if,
+def base_hash_test(backend, algorithm, digest_size, block_size, only_if,
skip_message):
if only_if is not None and not only_if(backend):
pytest.skip(skip_message)
- m = digestmod(backend=backend)
- assert m.digest_size == digest_size
- assert m.block_size == block_size
+
+ m = hashes.Hash(algorithm, backend=backend)
+ assert m.algorithm.digest_size == digest_size
+ assert m.algorithm.block_size == block_size
m_copy = m.copy()
assert m != m_copy
assert m._ctx != m_copy._ctx
@@ -120,12 +120,12 @@ def generate_long_string_hash_test(hash_factory, md, only_if=None,
return test_long_string_hash
-def long_string_hash_test(backend, hash_factory, md, only_if, skip_message):
+def long_string_hash_test(backend, algorithm, md, only_if, skip_message):
if only_if is not None and not only_if(backend):
pytest.skip(skip_message)
- m = hash_factory(backend=backend)
+ m = hashes.Hash(algorithm, backend=backend)
m.update(b"a" * 1000000)
- assert m.hexdigest() == md.lower()
+ assert m.finalize() == binascii.unhexlify(md.lower())
def generate_hmac_test(param_loader, path, file_names, digestmod,