diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-08-29 09:36:09 +0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2016-08-28 21:36:09 -0400 |
commit | 306ce512a28cdba29adf98125d894b90bb6bd78b (patch) | |
tree | 369dd5e4ea6b99638f2750e9f4873439f9545551 | |
parent | d6871fe568983b46a3b688c3222289357a7f56cd (diff) | |
download | cryptography-306ce512a28cdba29adf98125d894b90bb6bd78b.tar.gz cryptography-306ce512a28cdba29adf98125d894b90bb6bd78b.tar.bz2 cryptography-306ce512a28cdba29adf98125d894b90bb6bd78b.zip |
blake2b/blake2s support (#3116)
* blake2b/blake2s support
Doesn't support keying, personalization, salting, or tree hashes so
the API is pretty simple right now.
* implement digest_size via utils.read_only_property
* un-keyed for spelling's sake
* test copying + digest_size checks
* unkeyed is too a word
* line wrap
* reword the docs
* use the evp algorithm name in the error
This will make BLAKE2 alternate digest size errors a bit less confusing
* add changelog entry and docs about supported digest_size
-rw-r--r-- | CHANGELOG.rst | 4 | ||||
-rw-r--r-- | docs/hazmat/primitives/cryptographic-hashes.rst | 31 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 13 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/hashes.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/hashes.py | 42 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_hash_vectors.py | 34 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_hashes.py | 48 |
7 files changed, 174 insertions, 4 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 667720c2..18542035 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,10 @@ Changelog .. note:: This version is not yet released and is under active development. +* Added support for :class:`~cryptography.hazmat.primitives.hashes.BLAKE2b` and + :class:`~cryptography.hazmat.primitives.hashes.BLAKE2s` when using OpenSSL + 1.1.0. + 1.5 - 2016-08-26 ~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst index d0414efa..b0e9c16a 100644 --- a/docs/hazmat/primitives/cryptographic-hashes.rst +++ b/docs/hazmat/primitives/cryptographic-hashes.rst @@ -117,6 +117,36 @@ SHA-2 family SHA-512 is a cryptographic hash function from the SHA-2 family and is standardized by NIST. It produces a 512-bit message digest. +BLAKE2 +~~~~~~ + +`BLAKE2`_ is a cryptographic hash function specified in :rfc:`7693`. + +.. note:: + + While the RFC specifies keying, personalization, and salting features, + these are not supported at this time due to limitations in OpenSSL 1.1.0. + +.. class:: BLAKE2b(digest_size) + + BLAKE2b is optimized for 64-bit platforms and produces an 1 to 64-byte + message digest. + + :param int digest_size: The desired size of the hash output in bytes. Only + ``64`` is supported at this time. + + :raises ValueError: If the ``digest_size`` is invalid. + +.. class:: BLAKE2s(digest_size) + + BLAKE2s is optimized for 8 to 32-bit platforms and produces a + 1 to 32-byte message digest. + + :param int digest_size: The desired size of the hash output in bytes. Only + ``32`` is supported at this time. + + :raises ValueError: If the ``digest_size`` is invalid. + RIPEMD160 ~~~~~~~~~ @@ -193,3 +223,4 @@ Interfaces .. _`Lifetimes of cryptographic hash functions`: http://valerieaurora.org/hash.html +.. _`BLAKE2`: https://blake2.net diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index a1de1a89..7d16e05e 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -185,8 +185,19 @@ class Backend(object): def create_hmac_ctx(self, key, algorithm): return _HMACContext(self, key, algorithm) + def _build_openssl_digest_name(self, algorithm): + if algorithm.name == "blake2b" or algorithm.name == "blake2s": + alg = "{0}{1}".format( + algorithm.name, algorithm.digest_size * 8 + ).encode("ascii") + else: + alg = algorithm.name.encode("ascii") + + return alg + def hash_supported(self, algorithm): - digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii")) + name = self._build_openssl_digest_name(algorithm) + digest = self._lib.EVP_get_digestbyname(name) return digest != self._ffi.NULL def hmac_supported(self, algorithm): diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index 2c8fce1a..92ea53bb 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -22,12 +22,12 @@ class _HashContext(object): ctx = self._backend._ffi.gc( ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free ) - evp_md = self._backend._lib.EVP_get_digestbyname( - algorithm.name.encode("ascii")) + name = self._backend._build_openssl_digest_name(algorithm) + evp_md = self._backend._lib.EVP_get_digestbyname(name) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( "{0} is not a supported hash on this backend.".format( - algorithm.name), + name), _Reasons.UNSUPPORTED_HASH ) res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md, diff --git a/src/cryptography/hazmat/primitives/hashes.py b/src/cryptography/hazmat/primitives/hashes.py index 6bc8500b..0714c118 100644 --- a/src/cryptography/hazmat/primitives/hashes.py +++ b/src/cryptography/hazmat/primitives/hashes.py @@ -161,3 +161,45 @@ class MD5(object): name = "md5" digest_size = 16 block_size = 64 + + +@utils.register_interface(HashAlgorithm) +class BLAKE2b(object): + name = "blake2b" + _max_digest_size = 64 + _min_digest_size = 1 + block_size = 128 + + def __init__(self, digest_size): + if ( + digest_size > self._max_digest_size or + digest_size < self._min_digest_size + ): + raise ValueError("Digest size must be {0}-{1}".format( + self._min_digest_size, self._max_digest_size) + ) + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") + + +@utils.register_interface(HashAlgorithm) +class BLAKE2s(object): + name = "blake2s" + block_size = 64 + _max_digest_size = 32 + _min_digest_size = 1 + + def __init__(self, digest_size): + if ( + digest_size > self._max_digest_size or + digest_size < self._min_digest_size + ): + raise ValueError("Digest size must be {0}-{1}".format( + self._min_digest_size, self._max_digest_size) + ) + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") diff --git a/tests/hazmat/primitives/test_hash_vectors.py b/tests/hazmat/primitives/test_hash_vectors.py index c6e98283..8757df24 100644 --- a/tests/hazmat/primitives/test_hash_vectors.py +++ b/tests/hazmat/primitives/test_hash_vectors.py @@ -158,3 +158,37 @@ class TestMD5(object): ], hashes.MD5(), ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.hash_supported( + hashes.BLAKE2b(digest_size=64)), + skip_message="Does not support BLAKE2b", +) +@pytest.mark.requires_backend_interface(interface=HashBackend) +class TestBLAKE2b(object): + test_b2b = generate_hash_test( + load_hash_vectors, + os.path.join("hashes", "blake2"), + [ + "blake2b.txt", + ], + hashes.BLAKE2b(digest_size=64), + ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.hash_supported( + hashes.BLAKE2s(digest_size=32)), + skip_message="Does not support BLAKE2s", +) +@pytest.mark.requires_backend_interface(interface=HashBackend) +class TestBLAKE2s256(object): + test_b2s = generate_hash_test( + load_hash_vectors, + os.path.join("hashes", "blake2"), + [ + "blake2s.txt", + ], + hashes.BLAKE2s(digest_size=32), + ) diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py index a109c219..fe96b322 100644 --- a/tests/hazmat/primitives/test_hashes.py +++ b/tests/hazmat/primitives/test_hashes.py @@ -159,6 +159,54 @@ class TestMD5(object): ) +@pytest.mark.supported( + only_if=lambda backend: backend.hash_supported( + hashes.BLAKE2b(digest_size=64)), + skip_message="Does not support BLAKE2b", +) +@pytest.mark.requires_backend_interface(interface=HashBackend) +class TestBLAKE2b(object): + test_BLAKE2b = generate_base_hash_test( + hashes.BLAKE2b(digest_size=64), + digest_size=64, + block_size=128, + ) + + def test_invalid_digest_size(self, backend): + with pytest.raises(ValueError): + hashes.BLAKE2b(digest_size=65) + + with pytest.raises(ValueError): + hashes.BLAKE2b(digest_size=0) + + with pytest.raises(ValueError): + hashes.BLAKE2b(digest_size=-1) + + +@pytest.mark.supported( + only_if=lambda backend: backend.hash_supported( + hashes.BLAKE2s(digest_size=32)), + skip_message="Does not support BLAKE2s", +) +@pytest.mark.requires_backend_interface(interface=HashBackend) +class TestBLAKE2s(object): + test_BLAKE2s = generate_base_hash_test( + hashes.BLAKE2s(digest_size=32), + digest_size=32, + block_size=64, + ) + + def test_invalid_digest_size(self, backend): + with pytest.raises(ValueError): + hashes.BLAKE2s(digest_size=33) + + with pytest.raises(ValueError): + hashes.BLAKE2s(digest_size=0) + + with pytest.raises(ValueError): + hashes.BLAKE2s(digest_size=-1) + + def test_invalid_backend(): pretend_backend = object() |