aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-05-16 10:21:39 -0400
committerAlex Gaynor <alex.gaynor@gmail.com>2014-05-16 10:21:39 -0400
commit59bcae860e9b0b2d603608965e5ef3d913b2c92e (patch)
treee628e26852fa8692b45fc475fd83f6efb1ce4341
parente653e490b4d648daf3cb85d8050d04b47469a939 (diff)
parentd1c0fb8bbe0984d54ba0f4b7a8861bca0e446e19 (diff)
downloadcryptography-59bcae860e9b0b2d603608965e5ef3d913b2c92e.tar.gz
cryptography-59bcae860e9b0b2d603608965e5ef3d913b2c92e.tar.bz2
cryptography-59bcae860e9b0b2d603608965e5ef3d913b2c92e.zip
Merge branch 'master' into pypy-2.3
-rw-r--r--CHANGELOG.rst2
-rw-r--r--cryptography/hazmat/backends/multibackend.py18
-rw-r--r--cryptography/hazmat/bindings/openssl/ssl.py5
-rw-r--r--cryptography/hazmat/primitives/kdf/hkdf.py64
-rw-r--r--cryptography/hazmat/primitives/twofactor/hotp.py4
-rw-r--r--docs/hazmat/backends/interfaces.rst14
-rw-r--r--docs/hazmat/primitives/asymmetric/rsa.rst98
-rw-r--r--docs/hazmat/primitives/key-derivation-functions.rst92
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst2
-rw-r--r--tests/hazmat/backends/test_multibackend.py30
-rw-r--r--tests/hazmat/primitives/test_hkdf.py63
-rw-r--r--tests/hazmat/primitives/utils.py7
12 files changed, 337 insertions, 62 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 54d6229f..8a2635ed 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@ Changelog
.. note:: This version is not yet released and is under active development.
+* Added :class:`~cryptography.hazmat.primitives.HKDFExpand`.
+
0.4 - 2014-05-03
~~~~~~~~~~~~~~~~
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 753f4fc6..c5c652db 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -146,6 +146,24 @@ class MultiBackend(object):
raise UnsupportedAlgorithm("RSA is not supported by the backend",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+ def mgf1_hash_supported(self, algorithm):
+ for b in self._filtered_backends(RSABackend):
+ return b.mgf1_hash_supported(algorithm)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ for b in self._filtered_backends(RSABackend):
+ return b.decrypt_rsa(private_key, ciphertext, padding)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def encrypt_rsa(self, public_key, plaintext, padding):
+ for b in self._filtered_backends(RSABackend):
+ return b.encrypt_rsa(public_key, plaintext, padding)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
def generate_dsa_parameters(self, key_size):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_parameters(key_size)
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index cd8fa1cf..94b96d98 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -159,6 +159,7 @@ static const long TLSEXT_NAMETYPE_host_name;
typedef ... SSL_CIPHER;
typedef ... Cryptography_STACK_OF_SSL_CIPHER;
+typedef ... COMP_METHOD;
"""
FUNCTIONS = """
@@ -198,6 +199,10 @@ int SSL_shutdown(SSL *);
const char *SSL_get_cipher_list(const SSL *, int);
Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *);
+const COMP_METHOD *SSL_get_current_compression(SSL *);
+const COMP_METHOD *SSL_get_current_expansion(SSL *);
+const char *SSL_COMP_get_name(const COMP_METHOD *);
+
/* context */
void SSL_CTX_free(SSL_CTX *);
long SSL_CTX_set_timeout(SSL_CTX *, long);
diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py
index 03500aaa..daa8fcc7 100644
--- a/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -34,6 +34,51 @@ class HKDF(object):
self._algorithm = algorithm
+ if isinstance(salt, six.text_type):
+ raise TypeError(
+ "Unicode-objects must be encoded before using them as a salt.")
+
+ if salt is None:
+ salt = b"\x00" * (self._algorithm.digest_size // 8)
+
+ self._salt = salt
+
+ self._backend = backend
+
+ self._hkdf_expand = HKDFExpand(self._algorithm, length, info, backend)
+
+ def _extract(self, key_material):
+ h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
+ h.update(key_material)
+ return h.finalize()
+
+ def derive(self, key_material):
+ if isinstance(key_material, six.text_type):
+ raise TypeError(
+ "Unicode-objects must be encoded before using them as key "
+ "material."
+ )
+
+ return self._hkdf_expand.derive(self._extract(key_material))
+
+ def verify(self, key_material, expected_key):
+ if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+ raise InvalidKey
+
+
+@utils.register_interface(interfaces.KeyDerivationFunction)
+class HKDFExpand(object):
+ def __init__(self, algorithm, length, info, backend):
+ if not isinstance(backend, HMACBackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement HMACBackend",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ self._algorithm = algorithm
+
+ self._backend = backend
+
max_length = 255 * (algorithm.digest_size // 8)
if length > max_length:
@@ -44,15 +89,6 @@ class HKDF(object):
self._length = length
- if isinstance(salt, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as a salt.")
-
- if salt is None:
- salt = b"\x00" * (self._algorithm.digest_size // 8)
-
- self._salt = salt
-
if isinstance(info, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as info.")
@@ -61,15 +97,9 @@ class HKDF(object):
info = b""
self._info = info
- self._backend = backend
self._used = False
- def _extract(self, key_material):
- h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
- h.update(key_material)
- return h.finalize()
-
def _expand(self, key_material):
output = [b""]
counter = 1
@@ -87,7 +117,7 @@ class HKDF(object):
def derive(self, key_material):
if isinstance(key_material, six.text_type):
raise TypeError(
- "Unicode-objects must be encoded before using them as key "
+ "Unicode-objects must be encoded before using them as key"
"material."
)
@@ -95,7 +125,7 @@ class HKDF(object):
raise AlreadyFinalized
self._used = True
- return self._expand(self._extract(key_material))
+ return self._expand(key_material)
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py
index 41c467c8..1a0f4472 100644
--- a/cryptography/hazmat/primitives/twofactor/hotp.py
+++ b/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -64,8 +64,6 @@ class HOTP(object):
ctx.update(struct.pack(">Q", counter))
hmac_value = ctx.finalize()
- offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
-
- offset = int(offset_bits)
+ offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
p = hmac_value[offset:offset + 4]
return struct.unpack(">I", p)[0] & 0x7fffffff
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index f363b541..1e1a6b28 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -275,6 +275,14 @@ A specific ``backend`` may provide one or more of these interfaces.
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
provider.
+ :return bytes: The decrypted data.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+ MGF, hash function, or padding is chosen.
+
+ :raises ValueError: When decryption fails or key size does not match
+ ciphertext length.
+
.. method:: encrypt_rsa(public_key, plaintext, padding)
:param public_key: An instance of an
@@ -287,6 +295,12 @@ A specific ``backend`` may provide one or more of these interfaces.
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
provider.
+ :return bytes: The encrypted data.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+ MGF, hash function, or padding is chosen.
+
+ :raises ValueError: When plaintext is too long for the key size.
.. class:: TraditionalOpenSSLSerializationBackend
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 2700154c..234a5c66 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -85,7 +85,10 @@ RSA
:param padding: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
- provider.
+ provider. Valid values are
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+ (``PSS`` is recommended for all new applications).
:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -154,21 +157,39 @@ RSA
:class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
it may also be raised for invalid label values.
- .. code-block:: python
+ .. doctest::
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.asymmetric import padding
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import padding
- plaintext = private_key.decrypt(
- ciphertext,
- padding.OAEP(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- algorithm=hashes.SHA1(),
- label=None
- ),
- default_backend()
- )
+ >>> # Generate a key
+ >>> private_key = rsa.RSAPrivateKey.generate(
+ ... public_exponent=65537,
+ ... key_size=2048,
+ ... backend=default_backend()
+ ... )
+ >>> public_key = private_key.public_key()
+ >>> # encrypt some data
+ >>> ciphertext = public_key.encrypt(
+ ... b"encrypted data",
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
+ >>> # Now do the actual decryption
+ >>> plaintext = private_key.decrypt(
+ ... ciphertext,
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
.. class:: RSAPublicKey(public_exponent, modulus)
@@ -236,7 +257,10 @@ RSA
:param padding: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
- provider.
+ provider. Valid values are
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+ (``PSS`` is recommended for all new applications).
:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -306,27 +330,29 @@ RSA
:class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
it may also be raised for invalid label values.
- .. code-block:: python
-
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.asymmetric import padding, rsa
-
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=2048,
- backend=default_backend()
- )
- public_key = private_key.public_key()
- ciphertext = public_key.encrypt(
- plaintext,
- padding.OAEP(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- algorithm=hashes.SHA1(),
- label=None
- ),
- default_backend()
- )
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import padding
+
+ >>> # Generate a key
+ >>> private_key = rsa.RSAPrivateKey.generate(
+ ... public_exponent=65537,
+ ... key_size=2048,
+ ... backend=default_backend()
+ ... )
+ >>> public_key = private_key.public_key()
+ >>> # encrypt some data
+ >>> ciphertext = public_key.encrypt(
+ ... b"encrypted data",
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
Handling partial RSA private keys
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
index ee8f8ab6..de6bf5f8 100644
--- a/docs/hazmat/primitives/key-derivation-functions.rst
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -219,6 +219,98 @@ Different KDFs are suitable for different tasks such as:
``key_material`` generates the same key as the ``expected_key``, and
raises an exception if they do not match.
+
+.. class:: HKDFExpand(algorithm, length, info, backend)
+
+ .. versionadded:: 0.5
+
+ HKDF consists of two stages, extract and expand. This class exposes an
+ expand only version of HKDF that is suitable when the key material is
+ already cryptographically strong.
+
+ .. warning::
+
+ HKDFExpand should only be used if the key material is
+ cryptographically strong. You should use
+ :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF` if
+ you are unsure.
+
+ .. doctest::
+
+ >>> import os
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> backend = default_backend()
+ >>> info = b"hkdf-example"
+ >>> key_material = os.urandom(16)
+ >>> hkdf = HKDFExpand(
+ ... algorithm=hashes.SHA256(),
+ ... length=32,
+ ... info=info,
+ ... backend=backend
+ ... )
+ >>> key = hkdf.derive(key_material)
+ >>> hkdf = HKDFExpand(
+ ... algorithm=hashes.SHA256(),
+ ... length=32,
+ ... info=info,
+ ... backend=backend
+ ... )
+ >>> hkdf.verify(key_material, key)
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param int length: The desired length of the derived key. Maximum is
+ ``255 * (algorithm.digest_size // 8)``.
+
+ :param bytes info: Application specific context information. If ``None``
+ is explicitly passed an empty byte string will be used.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+ provider.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the
+ provided ``backend`` does not implement
+ :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+ :raises TypeError: This is raised if the provided ``info`` is a unicode object
+
+ .. method:: derive(key_material)
+
+ :param bytes key_material: The input key material.
+ :return bytes: The derived key.
+
+ :raises TypeError: This is raised if the provided ``key_material`` is
+ a unicode object
+
+ Derives a new key from the input key material by performing both the
+ extract and expand operations.
+
+ .. method:: verify(key_material, expected_key)
+
+ :param key_material bytes: The input key material. This is the same as
+ ``key_material`` in :meth:`derive`.
+ :param expected_key bytes: The expected result of deriving a new key,
+ this is the same as the return value of
+ :meth:`derive`.
+ :raises cryptography.exceptions.InvalidKey: This is raised when the
+ derived key does not match
+ the expected key.
+ :raises cryptography.exceptions.AlreadyFinalized: This is raised when
+ :meth:`derive` or
+ :meth:`verify` is
+ called more than
+ once.
+ :raises TypeError: This is raised if the provided ``key_material`` is
+ a unicode object
+
+ This checks whether deriving a new key from the supplied
+ ``key_material`` generates the same key as the ``expected_key``, and
+ raises an exception if they do not match.
+
.. _`NIST SP 800-132`: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
.. _`Password Storage Cheat Sheet`: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
.. _`PBKDF2`: https://en.wikipedia.org/wiki/PBKDF2
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 78bf6637..e5d8c65b 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -20,7 +20,7 @@ provides secrecy but not authenticity. That means an attacker can't see the
message but an attacker can create bogus messages and force the application to
decrypt them.
-For this reason it is *strongly* recommended to combine encryption with a
+For this reason it is **strongly** recommended to combine encryption with a
message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`, in
an "encrypt-then-MAC" formulation as `described by Colin Percival`_.
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index fd2a30cd..088465a1 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -98,6 +98,15 @@ class DummyRSABackend(object):
algorithm):
pass
+ def mgf1_hash_supported(self, algorithm):
+ pass
+
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ pass
+
+ def encrypt_rsa(self, public_key, plaintext, padding):
+ pass
+
@utils.register_interface(DSABackend)
class DummyDSABackend(object):
@@ -211,6 +220,12 @@ class TestMultiBackend(object):
backend.create_rsa_verification_ctx("public_key", "sig",
padding.PKCS1v15(), hashes.MD5())
+ backend.mgf1_hash_supported(hashes.MD5())
+
+ backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+ backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
backend = MultiBackend([])
with raises_unsupported_algorithm(
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
@@ -229,6 +244,21 @@ class TestMultiBackend(object):
backend.create_rsa_verification_ctx(
"public_key", "sig", padding.PKCS1v15(), hashes.MD5())
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.mgf1_hash_supported(hashes.MD5())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
def test_dsa(self):
backend = MultiBackend([
DummyDSABackend()
diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py
index 2e3c0c3d..598f09f0 100644
--- a/tests/hazmat/primitives/test_hkdf.py
+++ b/tests/hazmat/primitives/test_hkdf.py
@@ -13,6 +13,8 @@
from __future__ import absolute_import, division, print_function
+import binascii
+
import pytest
import six
@@ -21,7 +23,7 @@ from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, _Reasons
)
from cryptography.hazmat.primitives import hashes
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
from ...utils import raises_unsupported_algorithm
@@ -151,8 +153,67 @@ class TestHKDF(object):
hkdf.verify(b"foo", six.u("bar"))
+@pytest.mark.hmac
+class TestHKDFExpand(object):
+ def test_derive(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+ b"5bf34007208d5b887185865")
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ assert binascii.hexlify(hkdf.derive(prk)) == okm
+
+ def test_verify(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+ b"5bf34007208d5b887185865")
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ assert hkdf.verify(prk, binascii.unhexlify(okm)) is None
+
+ def test_invalid_verify(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ with pytest.raises(InvalidKey):
+ hkdf.verify(prk, b"wrong key")
+
+ def test_already_finalized(self, backend):
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ hkdf.derive(b"first")
+
+ with pytest.raises(AlreadyFinalized):
+ hkdf.derive(b"second")
+
+ def test_unicode_error(self, backend):
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ with pytest.raises(TypeError):
+ hkdf.derive(six.u("first"))
+
+
def test_invalid_backend():
pretend_backend = object()
with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
HKDF(hashes.SHA256(), 16, None, None, pretend_backend)
+
+ with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ HKDFExpand(hashes.SHA256(), 16, None, pretend_backend)
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index 6c3f4c95..a496459b 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -26,7 +26,7 @@ from cryptography.exceptions import (
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from ...utils import load_vectors_from_file
@@ -347,15 +347,14 @@ def hkdf_extract_test(backend, algorithm, params):
def hkdf_expand_test(backend, algorithm, params):
- hkdf = HKDF(
+ hkdf = HKDFExpand(
algorithm,
int(params["l"]),
- salt=binascii.unhexlify(params["salt"]) or None,
info=binascii.unhexlify(params["info"]) or None,
backend=backend
)
- okm = hkdf._expand(binascii.unhexlify(params["prk"]))
+ okm = hkdf.derive(binascii.unhexlify(params["prk"]))
assert okm == binascii.unhexlify(params["okm"])