From e9d22379dd246d88d483a010be5737718e700f9b Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 5 May 2014 13:25:11 +0800 Subject: Added HKDFExpandOnly --- cryptography/hazmat/primitives/kdf/hkdf.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index 03500aaa..78c5bfc1 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -100,3 +100,25 @@ class HKDF(object): 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 HKDFExpandOnly(HKDF): + def __init__(self, algorithm, length, info, backend): + HKDF.__init__(self, algorithm, length, None, info, backend) + + 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." + ) + + if self._used: + raise AlreadyFinalized + + self._used = True + return self._expand(key_material) + + def verify(self, key_material, expected_key): + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey -- cgit v1.2.3 From f1981a612ea70710138a701138c269655b16985b Mon Sep 17 00:00:00 2001 From: Ayrx Date: Mon, 5 May 2014 14:18:54 +0800 Subject: Added tests for HKDFExpandOnly --- tests/hazmat/primitives/test_hkdf.py | 60 +++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py index 2e3c0c3d..f46ca1c0 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, HKDFExpandOnly from ...utils import raises_unsupported_algorithm @@ -151,6 +153,62 @@ class TestHKDF(object): hkdf.verify(b"foo", six.u("bar")) +@pytest.mark.hmac +class TestHKDFExpandOnly(object): + def test_derive(self, backend): + prk = binascii.unhexlify( + b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5" + ) + + okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" + "5bf34007208d5b887185865") + + info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") + hkdf = HKDFExpandOnly(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" + "5bf34007208d5b887185865") + + info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") + hkdf = HKDFExpandOnly(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 = HKDFExpandOnly(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 = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + + hkdf.derive("first") + + with pytest.raises(AlreadyFinalized): + hkdf.derive("second") + + def test_unicode_error(self, backend): + info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") + hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + + with pytest.raises(TypeError): + hkdf.derive(six.u("first")) + + def test_invalid_backend(): pretend_backend = object() -- cgit v1.2.3 From 7cc0c834ff41f6309d0ccba7a5e3534932702bf8 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Tue, 6 May 2014 13:33:48 +0800 Subject: Fixed test failures --- cryptography/hazmat/primitives/kdf/hkdf.py | 1 + tests/hazmat/primitives/test_hkdf.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index 78c5bfc1..6d34a0f7 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -101,6 +101,7 @@ class HKDF(object): if not constant_time.bytes_eq(self.derive(key_material), expected_key): raise InvalidKey + @utils.register_interface(interfaces.KeyDerivationFunction) class HKDFExpandOnly(HKDF): def __init__(self, algorithm, length, info, backend): diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py index f46ca1c0..904ed69c 100644 --- a/tests/hazmat/primitives/test_hkdf.py +++ b/tests/hazmat/primitives/test_hkdf.py @@ -161,7 +161,7 @@ class TestHKDFExpandOnly(object): ) okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" - "5bf34007208d5b887185865") + b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) @@ -174,7 +174,7 @@ class TestHKDFExpandOnly(object): ) okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c" - "5bf34007208d5b887185865") + b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) @@ -196,10 +196,10 @@ class TestHKDFExpandOnly(object): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) - hkdf.derive("first") + hkdf.derive(b"first") with pytest.raises(AlreadyFinalized): - hkdf.derive("second") + hkdf.derive(b"second") def test_unicode_error(self, backend): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") -- cgit v1.2.3 From 9d72f12dcb28191f87fde9740899a39060e14495 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Tue, 6 May 2014 20:27:51 +0800 Subject: Added documentation --- .../hazmat/primitives/key-derivation-functions.rst | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst index 269f949d..9b76bf64 100644 --- a/docs/hazmat/primitives/key-derivation-functions.rst +++ b/docs/hazmat/primitives/key-derivation-functions.rst @@ -219,6 +219,92 @@ 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:: HKDFExpandOnly(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:: + + HKDFExpandOnly 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 HKDFExpandOnly + >>> from cryptography.hazmat.backends import default_backend + >>> backend = default_backend() + >>> info = b"hkdf-example" + >>> key_material = os.urandom(16) + >>> hkdf = HKDFExpandOnly( + ... algorithm=hashes.SHA256(), + ... length=32, + ... info=info, + ... backend=backend + ... ) + >>> key = hkdf.derive(key_material) + >>> hkdf = HKDFExpandOnly( + ... 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` + + .. method:: derive(key_material) + + :param bytes key_material: The input key material. + :return bytes: The derived key. + + 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. + + 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 -- cgit v1.2.3 From c0ce911b4e971f3090d406cb88dea532647eeac6 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 7 May 2014 16:22:09 +0800 Subject: Renamed HKDFExpandOnly to HKDFExpand --- cryptography/hazmat/primitives/kdf/hkdf.py | 2 +- docs/hazmat/primitives/key-derivation-functions.rst | 10 +++++----- tests/hazmat/primitives/test_hkdf.py | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index 6d34a0f7..44e14817 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -103,7 +103,7 @@ class HKDF(object): @utils.register_interface(interfaces.KeyDerivationFunction) -class HKDFExpandOnly(HKDF): +class HKDFExpand(HKDF): def __init__(self, algorithm, length, info, backend): HKDF.__init__(self, algorithm, length, None, info, backend) diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst index 9b76bf64..11fbd4e0 100644 --- a/docs/hazmat/primitives/key-derivation-functions.rst +++ b/docs/hazmat/primitives/key-derivation-functions.rst @@ -220,7 +220,7 @@ Different KDFs are suitable for different tasks such as: raises an exception if they do not match. -.. class:: HKDFExpandOnly(algorithm, length, info, backend) +.. class:: HKDFExpand(algorithm, length, info, backend) .. versionadded:: 0.5 @@ -230,7 +230,7 @@ Different KDFs are suitable for different tasks such as: .. warning:: - HKDFExpandOnly should only be used if the key material is + 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. @@ -239,19 +239,19 @@ Different KDFs are suitable for different tasks such as: >>> import os >>> from cryptography.hazmat.primitives import hashes - >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpandOnly + >>> 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 = HKDFExpandOnly( + >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, ... backend=backend ... ) >>> key = hkdf.derive(key_material) - >>> hkdf = HKDFExpandOnly( + >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py index 904ed69c..bee42172 100644 --- a/tests/hazmat/primitives/test_hkdf.py +++ b/tests/hazmat/primitives/test_hkdf.py @@ -23,7 +23,7 @@ from cryptography.exceptions import ( AlreadyFinalized, InvalidKey, _Reasons ) from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpandOnly +from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand from ...utils import raises_unsupported_algorithm @@ -154,7 +154,7 @@ class TestHKDF(object): @pytest.mark.hmac -class TestHKDFExpandOnly(object): +class TestHKDFExpand(object): def test_derive(self, backend): prk = binascii.unhexlify( b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5" @@ -164,7 +164,7 @@ class TestHKDFExpandOnly(object): b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") - hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) assert binascii.hexlify(hkdf.derive(prk)) == okm @@ -177,7 +177,7 @@ class TestHKDFExpandOnly(object): b"5bf34007208d5b887185865") info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") - hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) assert hkdf.verify(prk, binascii.unhexlify(okm)) is None @@ -187,14 +187,14 @@ class TestHKDFExpandOnly(object): ) info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") - hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + 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 = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) hkdf.derive(b"first") @@ -203,7 +203,7 @@ class TestHKDFExpandOnly(object): def test_unicode_error(self, backend): info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9") - hkdf = HKDFExpandOnly(hashes.SHA256(), 42, info, backend) + hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend) with pytest.raises(TypeError): hkdf.derive(six.u("first")) -- cgit v1.2.3 From ac1a079f9baf441c262fd11628f3e3d06f73129d Mon Sep 17 00:00:00 2001 From: Ayrx Date: Wed, 7 May 2014 17:02:21 +0800 Subject: Modified HKDF to use HKDFExpand --- cryptography/hazmat/primitives/kdf/hkdf.py | 81 +++++++++++++++++------------- tests/hazmat/primitives/test_hkdf.py | 3 ++ tests/hazmat/primitives/utils.py | 5 +- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index 44e14817..d49cc5bd 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -34,16 +34,6 @@ class HKDF(object): self._algorithm = algorithm - max_length = 255 * (algorithm.digest_size // 8) - - if length > max_length: - raise ValueError( - "Can not derive keys larger than {0} octets.".format( - max_length - )) - - self._length = length - if isinstance(salt, six.text_type): raise TypeError( "Unicode-objects must be encoded before using them as a salt.") @@ -53,37 +43,17 @@ class HKDF(object): self._salt = salt - if isinstance(info, six.text_type): - raise TypeError( - "Unicode-objects must be encoded before using them as info.") - - if info is None: - info = b"" - - self._info = info self._backend = backend self._used = False + 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 _expand(self, key_material): - output = [b""] - counter = 1 - - while (self._algorithm.digest_size // 8) * len(output) < self._length: - h = hmac.HMAC(key_material, self._algorithm, backend=self._backend) - h.update(output[-1]) - h.update(self._info) - h.update(six.int2byte(counter)) - output.append(h.finalize()) - counter += 1 - - return b"".join(output)[:self._length] - def derive(self, key_material): if isinstance(key_material, six.text_type): raise TypeError( @@ -95,7 +65,7 @@ class HKDF(object): raise AlreadyFinalized self._used = True - return self._expand(self._extract(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): @@ -105,7 +75,50 @@ class HKDF(object): @utils.register_interface(interfaces.KeyDerivationFunction) class HKDFExpand(HKDF): def __init__(self, algorithm, length, info, backend): - HKDF.__init__(self, algorithm, length, None, 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: + raise ValueError( + "Can not derive keys larger than {0} octets.".format( + max_length + )) + + self._length = length + + if isinstance(info, six.text_type): + raise TypeError( + "Unicode-objects must be encoded before using them as info.") + + if info is None: + info = b"" + + self._info = info + + self._used = False + + def _expand(self, key_material): + output = [b""] + counter = 1 + + while (self._algorithm.digest_size // 8) * len(output) < self._length: + h = hmac.HMAC(key_material, self._algorithm, backend=self._backend) + h.update(output[-1]) + h.update(self._info) + h.update(six.int2byte(counter)) + output.append(h.finalize()) + counter += 1 + + return b"".join(output)[:self._length] def derive(self, key_material): if isinstance(key_material, six.text_type): diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py index bee42172..598f09f0 100644 --- a/tests/hazmat/primitives/test_hkdf.py +++ b/tests/hazmat/primitives/test_hkdf.py @@ -214,3 +214,6 @@ def test_invalid_backend(): 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..7cf5efd0 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,10 +347,9 @@ 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 ) -- cgit v1.2.3 From 2d6cd449f1ad3448798d77d9216aded95b861a8d Mon Sep 17 00:00:00 2001 From: Ayrx Date: Fri, 9 May 2014 14:48:06 +0800 Subject: Minor fixes --- cryptography/hazmat/primitives/kdf/hkdf.py | 8 +------- tests/hazmat/primitives/utils.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py index d49cc5bd..daa8fcc7 100644 --- a/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/cryptography/hazmat/primitives/kdf/hkdf.py @@ -45,8 +45,6 @@ class HKDF(object): self._backend = backend - self._used = False - self._hkdf_expand = HKDFExpand(self._algorithm, length, info, backend) def _extract(self, key_material): @@ -61,10 +59,6 @@ class HKDF(object): "material." ) - if self._used: - raise AlreadyFinalized - - self._used = True return self._hkdf_expand.derive(self._extract(key_material)) def verify(self, key_material, expected_key): @@ -73,7 +67,7 @@ class HKDF(object): @utils.register_interface(interfaces.KeyDerivationFunction) -class HKDFExpand(HKDF): +class HKDFExpand(object): def __init__(self, algorithm, length, info, backend): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 7cf5efd0..a496459b 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -354,7 +354,7 @@ def hkdf_expand_test(backend, algorithm, params): backend=backend ) - okm = hkdf._expand(binascii.unhexlify(params["prk"])) + okm = hkdf.derive(binascii.unhexlify(params["prk"])) assert okm == binascii.unhexlify(params["okm"]) -- cgit v1.2.3 From dbd7a2554435b07d4e4fd8efcb72314b3b4d6962 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 9 May 2014 09:31:51 -0500 Subject: Multibackend now supports all of RSABackend Also convert some examples to doctest --- cryptography/hazmat/backends/multibackend.py | 18 ++++++ docs/hazmat/primitives/asymmetric/rsa.rst | 88 +++++++++++++++++----------- tests/hazmat/backends/test_multibackend.py | 30 ++++++++++ 3 files changed, 102 insertions(+), 34 deletions(-) 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/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 2700154c..7bae7142 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -154,21 +154,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) @@ -306,27 +324,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/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() -- cgit v1.2.3 From c929e40631c6bfe048e4ed43ca6102d932fa153e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 9 May 2014 11:09:13 -0500 Subject: Improve RSABackend interface docs (fixes #1022) --- docs/hazmat/backends/interfaces.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 -- cgit v1.2.3 From 006670cf9ad880ee46cdd41b4e65c2acf689f29a Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 9 May 2014 11:12:43 -0500 Subject: Use bold instead of italics --- docs/hazmat/primitives/symmetric-encryption.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 `, in an "encrypt-then-MAC" formulation as `described by Colin Percival`_. -- cgit v1.2.3 From c48100a9126990552197b5431d22f7a9e065baf7 Mon Sep 17 00:00:00 2001 From: Ayrx Date: Sat, 10 May 2014 13:01:46 +0800 Subject: Added missing exception to documentation --- docs/hazmat/primitives/key-derivation-functions.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst index 11fbd4e0..fdc540b9 100644 --- a/docs/hazmat/primitives/key-derivation-functions.rst +++ b/docs/hazmat/primitives/key-derivation-functions.rst @@ -276,12 +276,16 @@ Different KDFs are suitable for different tasks such as: :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. @@ -300,6 +304,8 @@ Different KDFs are suitable for different tasks such as: :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 -- cgit v1.2.3 From 1dac4440e24a3290309387d9d384dbd6fcf74826 Mon Sep 17 00:00:00 2001 From: PhiBo Date: Sun, 11 May 2014 21:39:40 +0200 Subject: Function to get current compression and expansion --- cryptography/hazmat/bindings/openssl/ssl.py | 5 +++++ 1 file changed, 5 insertions(+) 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); -- cgit v1.2.3 From bb91c812c8eb32690abaf3314e2df8d53d9c4fe8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 12 May 2014 15:03:04 -0400 Subject: add HKDFExpand to changelog --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) 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 ~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From a841f2b9236540b2ca19fb559f8d410952b37c07 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 15 May 2014 10:26:02 -0400 Subject: Simplify some code that was needlessly complex --- cryptography/hazmat/primitives/twofactor/hotp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 -- cgit v1.2.3 From aabc5a1386a75239418981742e85cd235f7d4a53 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 15 May 2014 13:42:41 -0400 Subject: Fixed #1039 -- provide links to PSS and PKCS1v15 in the RSA docs --- docs/hazmat/primitives/asymmetric/rsa.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 7bae7142..0f7e4093 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` @@ -254,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` -- cgit v1.2.3 From af075f0e1e80d5ceb55f20dc95a305a741678469 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 15 May 2014 14:02:41 -0400 Subject: Missing semicolon --- docs/hazmat/primitives/asymmetric/rsa.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 0f7e4093..234a5c66 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -86,8 +86,8 @@ RSA :param padding: An instance of a :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` provider. Valid values are - :class:~cryptography.hazmat.primitives.asymmetric.padding.PSS` and - :class:~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` + :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 @@ -258,8 +258,8 @@ RSA :param padding: An instance of a :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` provider. Valid values are - :class:~cryptography.hazmat.primitives.asymmetric.padding.PSS` and - :class:~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` + :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 -- cgit v1.2.3