From a22eefeb8441b7b34b003176aa0fc15359926c6e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 5 Apr 2018 16:31:58 -0700 Subject: [PATCH] fix JceNameLocalDelegatedKey.generate to only accept number of bits to generate rather than bytes for symmetric and bits for RSA --- .../delegated_keys/jce.py | 10 ++-- .../materials/wrapped.py | 4 +- test/functional/delegated_keys/__init__.py | 13 +++++ test/functional/delegated_keys/test_jce.py | 48 +++++++++++++++++++ test/functional/functional_test_utils.py | 5 +- 5 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 test/functional/delegated_keys/__init__.py create mode 100644 test/functional/delegated_keys/test_jce.py diff --git a/src/dynamodb_encryption_sdk/delegated_keys/jce.py b/src/dynamodb_encryption_sdk/delegated_keys/jce.py index 24a37b80..917b5f4f 100644 --- a/src/dynamodb_encryption_sdk/delegated_keys/jce.py +++ b/src/dynamodb_encryption_sdk/delegated_keys/jce.py @@ -11,6 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Delegated key that JCE StandardName algorithm values to determine behavior.""" +from __future__ import division + import logging import os @@ -32,17 +34,17 @@ def _generate_symmetric_key(key_length): """Generate a new AES key. - :param int key_length: Required key length in bytes + :param int key_length: Required key length in bits :returns: raw key, symmetric key identifier, and RAW encoding identifier :rtype: tuple of bytes, EncryptionKeyType, and KeyEncodingType """ - return os.urandom(key_length), EncryptionKeyType.SYMMETRIC, KeyEncodingType.RAW + return os.urandom(key_length // 8), EncryptionKeyType.SYMMETRIC, KeyEncodingType.RAW def _generate_rsa_key(key_length): """Generate a new RSA private key. - :param int key_length: Required key length in bytes + :param int key_length: Required key length in bits :returns: DER-encoded private key, private key identifier, and DER encoding identifier :rtype: tuple of bytes, EncryptionKeyType, and KeyEncodingType """ @@ -151,7 +153,7 @@ def generate(cls, algorithm, key_length=None): """Generate an instance of this DelegatedKey using the specified algorithm and key length. :param str algorithm: Text description of algorithm to be used - :param int key_length: Size of key to generate + :param int key_length: Size in bits of key to generate :returns: Generated delegated key :rtype: dynamodb_encryption_sdk.delegated_keys.DelegatedKey """ diff --git a/src/dynamodb_encryption_sdk/materials/wrapped.py b/src/dynamodb_encryption_sdk/materials/wrapped.py index 4346c9f4..937ec9f5 100644 --- a/src/dynamodb_encryption_sdk/materials/wrapped.py +++ b/src/dynamodb_encryption_sdk/materials/wrapped.py @@ -11,8 +11,6 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Cryptographic materials to use ephemeral content encryption keys wrapped by delegated keys.""" -from __future__ import division - import base64 import copy @@ -140,7 +138,7 @@ def _generate_content_key(self): args = self._content_key_algorithm.split('/', 1) content_algorithm = args[0] try: - content_key_length = int(args[1]) // 8 + content_key_length = int(args[1]) except IndexError: content_key_length = None content_key = JceNameLocalDelegatedKey.generate( diff --git a/test/functional/delegated_keys/__init__.py b/test/functional/delegated_keys/__init__.py new file mode 100644 index 00000000..2add15ef --- /dev/null +++ b/test/functional/delegated_keys/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Dummy stub to make linters work better.""" diff --git a/test/functional/delegated_keys/test_jce.py b/test/functional/delegated_keys/test_jce.py new file mode 100644 index 00000000..2c88902e --- /dev/null +++ b/test/functional/delegated_keys/test_jce.py @@ -0,0 +1,48 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Functional test suite for ``dynamodb_encryption_sdk.delegated_keys.jce``.""" +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +import pytest + +from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey + +pytestmark = [pytest.mark.functional, pytest.mark.local] + + +def _find_aes_key_length(key): + return len(key) * 8 + + +def _find_rsa_key_length(key): + loaded_key = serialization.load_der_private_key(data=key, password=None, backend=default_backend()) + return loaded_key._key_size + + +@pytest.mark.parametrize('algorithm, requested_bits, expected_bits, length_finder', ( + ('AES', 256, 256, _find_aes_key_length), + ('AESWrap', 256, 256, _find_aes_key_length), + ('RSA', 4096, 4096, _find_rsa_key_length), + ('HmacSHA512', 256, 256, _find_aes_key_length), + ('HmacSHA256', 256, 256, _find_aes_key_length), + ('HmacSHA384', 256, 256, _find_aes_key_length), + ('HmacSHA224', 256, 256, _find_aes_key_length), + ('SHA512withRSA', 4096, 4096, _find_rsa_key_length), + ('SHA256withRSA', 4096, 4096, _find_rsa_key_length), + ('SHA384withRSA', 4096, 4096, _find_rsa_key_length), + ('SHA224withRSA', 4096, 4096, _find_rsa_key_length) +)) +def test_generate_correct_key_length(algorithm, requested_bits, expected_bits, length_finder): + test = JceNameLocalDelegatedKey.generate(algorithm, requested_bits) + + assert length_finder(test.key) == expected_bits diff --git a/test/functional/functional_test_utils.py b/test/functional/functional_test_utils.py index 7e9e6a8b..fdc4c6fd 100644 --- a/test/functional/functional_test_utils.py +++ b/test/functional/functional_test_utils.py @@ -211,7 +211,7 @@ def _some_algorithm_pairs(): def _all_possible_cmps(algorithm_generator): """Generate all possible cryptographic materials providers based on the supplied generator.""" # The AES combinations do the same thing, but this makes sure that the AESWrap name works as expected. - yield _build_wrapped_jce_cmp('AESWrap', 32, 'HmacSHA256', 32) + yield _build_wrapped_jce_cmp('AESWrap', 256, 'HmacSHA256', 256) for builder_info, args in itertools.product(_cmp_builders.items(), algorithm_generator()): builder_type, builder_func = builder_info @@ -229,9 +229,6 @@ def _all_possible_cmps(algorithm_generator): sig_key_length=signing_key_length ) - if encryption_algorithm == 'AES': - encryption_key_length //= 8 - yield pytest.param( builder_func( encryption_algorithm,