From 6d7fe00c7066d8d474981b21cdadb16f021d3e6a Mon Sep 17 00:00:00 2001 From: Jared Date: Sun, 29 May 2016 17:32:37 -0700 Subject: NIST SP 800-108 Counter Mode KDF (#2748) * NIST SP 800-108 Counter Mode and Feedback Mode KDF * CounterKDF unit tests * Refactor to support multiple key based KDF modes. * Extracting supported algorithms for KBKDF Counter Mode test vectors * Adding support for different rlen and counter location in KBKDF * support for multiple L lengths and 24 bit counter length. * Adding KBKDF Documentation. * Refactoring KBKDF to KBKDFHMAC to describe hash algorithm used. --- tests/hazmat/primitives/test_kbkdf.py | 151 ++++++++++++++++++++++++++ tests/hazmat/primitives/test_kbkdf_vectors.py | 23 ++++ tests/hazmat/primitives/utils.py | 52 +++++++++ 3 files changed, 226 insertions(+) create mode 100644 tests/hazmat/primitives/test_kbkdf.py create mode 100644 tests/hazmat/primitives/test_kbkdf_vectors.py (limited to 'tests/hazmat') diff --git a/tests/hazmat/primitives/test_kbkdf.py b/tests/hazmat/primitives/test_kbkdf.py new file mode 100644 index 00000000..45a53ac0 --- /dev/null +++ b/tests/hazmat/primitives/test_kbkdf.py @@ -0,0 +1,151 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import pytest + +from cryptography.exceptions import ( + AlreadyFinalized, InvalidKey, _Reasons +) +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.kbkdf import ( + CounterLocation, KBKDFHMAC, Mode +) + +from ...doubles import DummyHashAlgorithm +from ...utils import raises_unsupported_algorithm + + +class TestKBKDFHMAC(object): + def test_invalid_key(self): + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + key = kdf.derive(b"material") + + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + with pytest.raises(InvalidKey): + kdf.verify(b"material2", key) + + def test_already_finalized(self): + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + kdf.derive(b'material') + + with pytest.raises(AlreadyFinalized): + kdf.derive(b'material2') + + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + key = kdf.derive(b'material') + + with pytest.raises(AlreadyFinalized): + kdf.verify(b'material', key) + + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + kdf.verify(b'material', key) + + with pytest.raises(AlreadyFinalized): + kdf.verify(b"material", key) + + def test_key_length(self): + kdf = KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 85899345920, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + with pytest.raises(ValueError): + kdf.derive(b'material') + + def test_rlen(self): + with pytest.raises(ValueError): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 5, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_r_type(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, b'r', 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_l_type(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, 4, b'l', + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_l(self): + with pytest.raises(ValueError): + KBKDFHMAC(hashes.SHA1(), Mode.CounterMode, 32, 4, None, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_unsupported_mode(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA256(), None, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_unsupported_location(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + None, b'label', b'context', None, + backend=default_backend()) + + def test_unsupported_parameters(self): + with pytest.raises(ValueError): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + b'fixed', backend=default_backend()) + + def test_unsupported_hash(self): + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): + KBKDFHMAC(object(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_unsupported_algorithm(self): + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): + KBKDFHMAC(DummyHashAlgorithm(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=default_backend()) + + def test_invalid_backend(self): + mock_backend = object + + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', b'context', + None, backend=mock_backend()) + + def test_unicode_error_label(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, u'label', b'context', + backend=default_backend()) + + def test_unicode_error_context(self): + with pytest.raises(TypeError): + KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', u'context', + None, backend=default_backend()) + + def test_unicode_error_key_material(self): + with pytest.raises(TypeError): + kdf = KBKDFHMAC(hashes.SHA256(), Mode.CounterMode, 32, 4, 4, + CounterLocation.BeforeFixed, b'label', + b'context', None, backend=default_backend()) + kdf.derive(u'material') diff --git a/tests/hazmat/primitives/test_kbkdf_vectors.py b/tests/hazmat/primitives/test_kbkdf_vectors.py new file mode 100644 index 00000000..c8263e2b --- /dev/null +++ b/tests/hazmat/primitives/test_kbkdf_vectors.py @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import os + +import pytest + +from cryptography.hazmat.backends.interfaces import HMACBackend + +from .utils import generate_kbkdf_counter_mode_test +from ...utils import load_nist_kbkdf_vectors + + +@pytest.mark.requires_backend_interface(interface=HMACBackend) +class TestCounterKDFCounterMode(object): + test_HKDFSHA1 = generate_kbkdf_counter_mode_test( + load_nist_kbkdf_vectors, + os.path.join("KDF"), + ["nist-800-108-KBKDF-CTR.txt"] + ) diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index e148bc63..e45466d8 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -18,6 +18,9 @@ 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, HKDFExpand +from cryptography.hazmat.primitives.kdf.kbkdf import ( + CounterLocation, KBKDFHMAC, Mode +) from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from ...utils import load_vectors_from_file @@ -370,6 +373,55 @@ def generate_hkdf_test(param_loader, path, file_names, algorithm): return test_hkdf +def generate_kbkdf_counter_mode_test(param_loader, path, file_names): + all_params = _load_all_params(path, file_names, param_loader) + + @pytest.mark.parametrize("params", all_params) + def test_kbkdf(self, backend, params): + kbkdf_counter_mode_test(backend, params) + return test_kbkdf + + +def kbkdf_counter_mode_test(backend, params): + supported_algorithms = { + 'hmac_sha1': hashes.SHA1, + 'hmac_sha224': hashes.SHA224, + 'hmac_sha256': hashes.SHA256, + 'hmac_sha384': hashes.SHA384, + 'hmac_sha512': hashes.SHA512, + } + + supportd_counter_locations = { + "before_fixed": CounterLocation.BeforeFixed, + "after_fixed": CounterLocation.AfterFixed, + } + + algorithm = supported_algorithms.get(params.get('prf')) + if algorithm is None or not backend.hmac_supported(algorithm()): + pytest.skip('Does not support algorithm') + + ctr_loc = supportd_counter_locations.get(params.get("ctrlocation")) + if ctr_loc is None or not isinstance(ctr_loc, CounterLocation): + pytest.skip("Does not support counter location".format( + location=params.get('ctrlocation') + )) + + ctrkdf = KBKDFHMAC( + algorithm(), + Mode.CounterMode, + params['l'] // 8, + params['rlen'] // 8, + None, + ctr_loc, + None, + None, + binascii.unhexlify(params['fixedinputdata']), + backend=backend) + + ko = ctrkdf.derive(binascii.unhexlify(params['ki'])) + assert binascii.hexlify(ko) == params["ko"] + + def generate_rsa_verification_test(param_loader, path, file_names, hash_alg, pad_factory): all_params = _load_all_params(path, file_names, param_loader) -- cgit v1.2.3