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
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 667720c..1854203 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,10 @@
 
 .. 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 d0414ef..b0e9c16 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -117,6 +117,36 @@
     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 @@
 
 
 .. _`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 a1de1a8..7d16e05 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -185,8 +185,19 @@
     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 2c8fce1..92ea53b 100644
--- a/src/cryptography/hazmat/backends/openssl/hashes.py
+++ b/src/cryptography/hazmat/backends/openssl/hashes.py
@@ -22,12 +22,12 @@
             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 6bc8500..0714c11 100644
--- a/src/cryptography/hazmat/primitives/hashes.py
+++ b/src/cryptography/hazmat/primitives/hashes.py
@@ -161,3 +161,45 @@
     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 c6e9828..8757df2 100644
--- a/tests/hazmat/primitives/test_hash_vectors.py
+++ b/tests/hazmat/primitives/test_hash_vectors.py
@@ -158,3 +158,37 @@
         ],
         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 a109c21..fe96b32 100644
--- a/tests/hazmat/primitives/test_hashes.py
+++ b/tests/hazmat/primitives/test_hashes.py
@@ -159,6 +159,54 @@
     )
 
 
+@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()