Brainpool curves (#4129)

* added brainpool ec-curves key_length >= 256bit

* limit brainpool curves to the set that appear required + docs

* oops

* typos all around me

* add brainpool ECDH kex tests

* switch to using rfc 7027 vectors

* review feedback

* empty commits are the best
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 79ce603..2f8924f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,10 @@
 
 * **BACKWARDS INCOMPATIBLE:** Support for Python 2.6 has been dropped.
 * Resolved a bug in ``HKDF`` that incorrectly constrained output size.
+* Added :class:`~cryptography.hazmat.primitives.asymmetric.ec.BrainpoolP256R1`,
+  :class:`~cryptography.hazmat.primitives.asymmetric.ec.BrainpoolP384R1`, and
+  :class:`~cryptography.hazmat.primitives.asymmetric.ec.BrainpoolP512R1` to
+  support inter-operating with systems like German smart meters.
 * Added token rotation support to :doc:`Fernet </fernet>` with
   :meth:`~cryptography.fernet.MultiFernet.rotate`.
 * Fixed a memory leak in
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 5d6251f..edcfdfc 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -414,6 +414,28 @@
     SECG curve ``secp256k1``.
 
 
+.. class:: BrainpoolP256R1
+
+    .. versionadded:: 2.2
+
+    Brainpool curve specified in :rfc:`5639`. These curves are discouraged
+    for new systems.
+
+.. class:: BrainpoolP384R1
+
+    .. versionadded:: 2.2
+
+    Brainpool curve specified in :rfc:`5639`. These curves are discouraged
+    for new systems.
+
+.. class:: BrainpoolP512R1
+
+    .. versionadded:: 2.2
+
+    Brainpool curve specified in :rfc:`5639`. These curves are discouraged
+    for new systems.
+
+
 Key Interfaces
 ~~~~~~~~~~~~~~
 
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py
index 7931b08..83266bb 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -228,6 +228,24 @@
     key_size = 192
 
 
+@utils.register_interface(EllipticCurve)
+class BrainpoolP256R1(object):
+    name = "brainpoolP256r1"
+    key_size = 256
+
+
+@utils.register_interface(EllipticCurve)
+class BrainpoolP384R1(object):
+    name = "brainpoolP384r1"
+    key_size = 384
+
+
+@utils.register_interface(EllipticCurve)
+class BrainpoolP512R1(object):
+    name = "brainpoolP512r1"
+    key_size = 512
+
+
 _CURVE_TYPES = {
     "prime192v1": SECP192R1,
     "prime256v1": SECP256R1,
@@ -250,6 +268,10 @@
     "sect283r1": SECT283R1,
     "sect409r1": SECT409R1,
     "sect571r1": SECT571R1,
+
+    "brainpoolP256r1": BrainpoolP256R1,
+    "brainpoolP384r1": BrainpoolP384R1,
+    "brainpoolP512r1": BrainpoolP512R1,
 }
 
 
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index 02b1cdc..1b491a1 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -26,7 +26,7 @@
 from ...doubles import DummyKeySerializationEncryption
 from ...utils import (
     load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors,
-    load_kasvs_ecdh_vectors, load_vectors_from_file,
+    load_kasvs_ecdh_vectors, load_nist_vectors, load_vectors_from_file,
     raises_unsupported_algorithm
 )
 
@@ -1101,6 +1101,33 @@
         else:
             assert z == vector['Z']
 
+    @pytest.mark.parametrize(
+        "vector",
+        load_vectors_from_file(
+            os.path.join("asymmetric", "ECDH", "brainpool.txt"),
+            load_nist_vectors
+        )
+    )
+    def test_brainpool_kex(self, backend, vector):
+        curve = ec._CURVE_TYPES[vector['curve'].decode('ascii')]
+        _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), curve)
+        key = ec.EllipticCurvePrivateNumbers(
+            int(vector['da'], 16),
+            ec.EllipticCurvePublicNumbers(
+                int(vector['x_qa'], 16), int(vector['y_qa'], 16), curve()
+            )
+        ).private_key(backend)
+        peer = ec.EllipticCurvePrivateNumbers(
+            int(vector['db'], 16),
+            ec.EllipticCurvePublicNumbers(
+                int(vector['x_qb'], 16), int(vector['y_qb'], 16), curve()
+            )
+        ).private_key(backend)
+        shared_secret = key.exchange(ec.ECDH(), peer.public_key())
+        assert shared_secret == binascii.unhexlify(vector["x_z"])
+        shared_secret_2 = peer.exchange(ec.ECDH(), key.public_key())
+        assert shared_secret_2 == binascii.unhexlify(vector["x_z"])
+
     def test_exchange_unsupported_algorithm(self, backend):
         _skip_curve_unsupported(backend, ec.SECP256R1())