From 75d177f95aecac937805786dfbc08dba7fcbd275 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Sep 2018 17:07:48 -0700 Subject: [PATCH 01/37] initial draft of decrypt oracle --- .gitignore | 4 + decrypt_oracle/.chalice/build-requirements.py | 22 ++ decrypt_oracle/.chalice/config.json | 9 + decrypt_oracle/LICENSE | 201 +++++++++++ decrypt_oracle/MANIFEST.in | 9 + decrypt_oracle/README.rst | 0 decrypt_oracle/app.py | 2 + decrypt_oracle/requirements-actual.txt | 3 + decrypt_oracle/requirements.txt | 2 + decrypt_oracle/setup.cfg | 42 +++ decrypt_oracle/setup.py | 59 ++++ .../__init__.py | 4 + .../app.py | 41 +++ .../key_providers/__init__.py | 0 .../key_providers/counting_master_key.py | 79 +++++ .../key_providers/null_master_key.py | 86 +++++ decrypt_oracle/src/pylintrc | 14 + decrypt_oracle/test/requirements.txt | 4 + decrypt_oracle/tox.ini | 324 ++++++++++++++++++ 19 files changed, 905 insertions(+) create mode 100644 decrypt_oracle/.chalice/build-requirements.py create mode 100644 decrypt_oracle/.chalice/config.json create mode 100644 decrypt_oracle/LICENSE create mode 100644 decrypt_oracle/MANIFEST.in create mode 100644 decrypt_oracle/README.rst create mode 100644 decrypt_oracle/app.py create mode 100644 decrypt_oracle/requirements-actual.txt create mode 100644 decrypt_oracle/requirements.txt create mode 100644 decrypt_oracle/setup.cfg create mode 100644 decrypt_oracle/setup.py create mode 100644 decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py create mode 100644 decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py create mode 100644 decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/__init__.py create mode 100644 decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py create mode 100644 decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py create mode 100644 decrypt_oracle/src/pylintrc create mode 100644 decrypt_oracle/test/requirements.txt create mode 100644 decrypt_oracle/tox.ini diff --git a/.gitignore b/.gitignore index 799ec4dc4..052125ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,7 @@ __pycache__ # Tox .tox + +# Chalice +*/.chalice/deployments +*/.chalice/venv diff --git a/decrypt_oracle/.chalice/build-requirements.py b/decrypt_oracle/.chalice/build-requirements.py new file mode 100644 index 000000000..2b0ca5bbf --- /dev/null +++ b/decrypt_oracle/.chalice/build-requirements.py @@ -0,0 +1,22 @@ +""" + +""" +import os + +BASE = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + + +def main(): + """""" + with open(os.path.join(BASE, 'requirements.txt'), 'wb') as requirements: + requirements.write(b'# Requirements for Chalice packager.' + os.linesep.encode('utf-8')) + requirements.write(b'# Autogenerated. Do not hand-edit.' + os.linesep.encode('utf-8')) + dist_dir = os.path.join(BASE, 'dist') + for found_file in os.listdir(dist_dir): + full_path = os.path.join(dist_dir, found_file) + if os.path.isfile(full_path) and found_file.endswith('.whl'): + requirements.write(full_path.encode('utf-8') + os.linesep.encode('utf-8')) + + +if __name__ == '__main__': + main() diff --git a/decrypt_oracle/.chalice/config.json b/decrypt_oracle/.chalice/config.json new file mode 100644 index 000000000..bed541583 --- /dev/null +++ b/decrypt_oracle/.chalice/config.json @@ -0,0 +1,9 @@ +{ + "version": "2.0", + "app_name": "aws-encryption-sdk-decryption-oracle", + "stages": { + "dev": { + "api_gateway_stage": "api" + } + } +} diff --git a/decrypt_oracle/LICENSE b/decrypt_oracle/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/decrypt_oracle/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License 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. diff --git a/decrypt_oracle/MANIFEST.in b/decrypt_oracle/MANIFEST.in new file mode 100644 index 000000000..ec83907d8 --- /dev/null +++ b/decrypt_oracle/MANIFEST.in @@ -0,0 +1,9 @@ +include README.rst +include CHANGELOG.rst +include CONTRIBUTING.rst +include LICENSE +include requirements-actual.txt + +recursive-include doc * +recursive-include test *.py +recursive-include examples *.py \ No newline at end of file diff --git a/decrypt_oracle/README.rst b/decrypt_oracle/README.rst new file mode 100644 index 000000000..e69de29bb diff --git a/decrypt_oracle/app.py b/decrypt_oracle/app.py new file mode 100644 index 000000000..477526191 --- /dev/null +++ b/decrypt_oracle/app.py @@ -0,0 +1,2 @@ +"""Shim to pull decryption oracle app into expected location.""" +from aws_encryption_sdk_decryption_oracle.app import APP as app diff --git a/decrypt_oracle/requirements-actual.txt b/decrypt_oracle/requirements-actual.txt new file mode 100644 index 000000000..1dd37336b --- /dev/null +++ b/decrypt_oracle/requirements-actual.txt @@ -0,0 +1,3 @@ +# Requirements for actual package +chalice +aws-encryption-sdk diff --git a/decrypt_oracle/requirements.txt b/decrypt_oracle/requirements.txt new file mode 100644 index 000000000..99ea21b58 --- /dev/null +++ b/decrypt_oracle/requirements.txt @@ -0,0 +1,2 @@ +# Requirements for Chalice packager. +# Autogenerated. Do not hand-edit. diff --git a/decrypt_oracle/setup.cfg b/decrypt_oracle/setup.cfg new file mode 100644 index 000000000..dfd64af69 --- /dev/null +++ b/decrypt_oracle/setup.cfg @@ -0,0 +1,42 @@ +[wheel] +universal = 1 + +[metadata] +license_file = LICENSE + +[coverage:run] +branch = True + +[coverage:report] +show_missing = True + +[mypy] +ignore_missing_imports = True + +[flake8] +max_complexity = 10 +max_line_length = 120 +import_order_style = google +application_import_names = aws_encryption_sdk_cli +builtins = raw_input +ignore = + # Ignoring D205 and D400 because of false positives + D205, D400, + # E203 is not PEP8 compliant https://github.com/ambv/black#slices + E203, + # W503 is not PEP8 compliant https://github.com/ambv/black#line-breaks--binary-operators + W503 + +[doc8] +max-line-length = 120 + +[isort] +line_length = 120 +# https://github.com/timothycrosley/isort#multi-line-output-modes +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +combine_as_imports = True +not_skip = __init__.py +known_first_party = aws_encryption_sdk_decryption_oracle +known_third_party =aws_encryption_sdk,chalice,setuptools diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py new file mode 100644 index 000000000..75ec02965 --- /dev/null +++ b/decrypt_oracle/setup.py @@ -0,0 +1,59 @@ +"""API Gateway + Lambda decryption oracle using the AWS Encryption SDK for Python.""" +import os +import re + +from setuptools import find_packages, setup + +VERSION_RE = re.compile(r"""__version__ = ['"]([0-9.]+)['"]""") +HERE = os.path.abspath(os.path.dirname(__file__)) + + +def read(*args): + """Read complete file contents.""" + return open(os.path.join(HERE, *args)).read() + + +def get_version(): + """Read the version from this module.""" + init = read("src", "aws_encryption_sdk_decryption_oracle", "__init__.py") + return VERSION_RE.search(init).group(1) + + +def get_requirements(): + """Read the requirements file.""" + requirements = read("requirements-actual.txt") + return [r for r in requirements.strip().splitlines()] + + +setup( + name="aws-encryption-sdk-decryption-oracle", + packages=find_packages("src"), + package_dir={"": "src"}, + version=get_version(), + author="Amazon Web Services", + maintainer="Amazon Web Services", + author_email="aws-cryptools@amazon.com", + url="https://github.com/awslabs/aws-encryption-sdk-python", + description="API Gateway + Lambda decryption oracle using the AWS Encryption SDK for Python", + long_description=read("README.rst"), + keywords="aws-encryption-sdk aws kms encryption", + license="Apache License 2.0", + install_requires=get_requirements(), + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Natural Language :: English", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Security", + "Topic :: Security :: Cryptography", + ], +) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py new file mode 100644 index 000000000..be019d00f --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py @@ -0,0 +1,4 @@ +""" + +""" +__version__ = "0.0.1" diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py new file mode 100644 index 000000000..9f7104665 --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -0,0 +1,41 @@ +""" + +""" +import base64 + +import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider +from chalice import Chalice + +from .key_providers.counting_master_key import CountingMasterKey +from .key_providers.null_master_key import NullMasterKey + +APP = Chalice(app_name="awses-encryption-oracle-python") + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Dict, Text, NoReturn, Union # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + + +def _master_key_provider(): + # type: () -> KMSMasterKeyProvider + """Build the V0 master key provider.""" + master_key_provider = KMSMasterKeyProvider() + master_key_provider.add_master_key_provider(NullMasterKey()) + master_key_provider.add_master_key_provider(CountingMasterKey()) + return master_key_provider + + +@APP.route("/v0/decrypt", methods=["POST"]) +def basic_decrypt(): + # type: () -> Dict[Text, Union[Text, bool, int]] + """Basic decrypt handler for decryption oracle v0.""" + try: + request = APP.current_request.json_body + ciphertext = base64.b64decode(request["body"].encode("utf-8")) + plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) + return {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} + except Exception as error: # pylint: disable=broad-except + return {"body": str(error), "isBase64Encoded": False, "statusCode": 400} diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py new file mode 100644 index 000000000..70187a79c --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py @@ -0,0 +1,79 @@ +"""Master key that provides speci.""" +from aws_encryption_sdk.exceptions import DecryptKeyError +from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import +from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig +from aws_encryption_sdk.structures import EncryptedDataKey # noqa pylint: disable=unused-import +from aws_encryption_sdk.structures import DataKey + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Dict, Text, NoReturn # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + + +class CountingMasterKeyConfig(MasterKeyConfig): + # pylint: disable=too-few-public-methods + """""" + + provider_id = "test_counting" + + def __init__(self): + # type: () -> None + """""" + super(CountingMasterKeyConfig, self).__init__(key_id=b"test_counting_prov_info") + + +class CountingMasterKey(MasterKey): + """""" + + provider_id = "test_counting" + _config_class = CountingMasterKeyConfig + _encrypted_data_key = b"\x40\x41\x42\x43\x44" + + def _generate_data_key(self, algorithm, encryption_context): + # type: (AlgorithmSuite, Dict[Text, Text]) -> DataKey + """Perform the provider-specific data key generation task. + + :param algorithm: Algorithm on which to base data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in encryption + :returns: Generated data key + :rtype: aws_encryption_sdk.structures.DataKey + """ + data_key = b"".join([chr(i).encode("utf-8") for i in range(1, algorithm.data_key_len + 1)]) + return DataKey(key_provider=self.key_provider, data_key=data_key, encrypted_data_key=self._encrypted_data_key) + + def _encrypt_data_key(self, data_key, algorithm, encryption_context): + # type: (DataKey, AlgorithmSuite, Dict[Text, Text]) -> NoReturn + """Encrypt a data key and return the ciphertext. + + :param data_key: Unencrypted data key + :type data_key: :class:`aws_encryption_sdk.structures.RawDataKey` + or :class:`aws_encryption_sdk.structures.DataKey` + :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in encryption + :raises NotImplementedError: when called + """ + raise NotImplementedError("CountingMasterKey does not support encrypt_data_key") + + def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): + # type: (EncryptedDataKey, AlgorithmSuite, Dict[Text, Text]) -> DataKey + """Decrypt an encrypted data key and return the plaintext. + + :param data_key: Encrypted data key + :type data_key: aws_encryption_sdk.structures.EncryptedDataKey + :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in decryption + :returns: Data key containing decrypted data key + :rtype: aws_encryption_sdk.structures.DataKey + :raises DecryptKeyError: if Master Key is unable to decrypt data key + """ + if encrypted_data_key.encrypted_data_key != self._encrypted_data_key: + raise DecryptKeyError( + 'Master Key "{provider}" unable to decrypt data key'.format(provider=self.key_provider) + ) + + return self._generate_data_key(algorithm, encryption_context) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py new file mode 100644 index 000000000..86790986f --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py @@ -0,0 +1,86 @@ +"""Master key that provides null data keys.""" +from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import +from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig +from aws_encryption_sdk.structures import EncryptedDataKey # noqa pylint: disable=unused-import +from aws_encryption_sdk.structures import DataKey + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Dict, Text, NoReturn # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + + +class NullMasterKeyConfig(MasterKeyConfig): + # pylint: disable=too-few-public-methods + """""" + + provider_id = "null" + + def __init__(self): + # type: () -> None + """""" + super(NullMasterKeyConfig, self).__init__(key_id=b"null") + + +class NullMasterKey(MasterKey): + """""" + + provider_id = "null" + _allowed_provider_ids = (provider_id, "zero") + _config_class = NullMasterKeyConfig + + def owns_data_key(self, data_key): + # type: (DataKey) -> bool + """Determine whether the data key is owned by a ``null`` or ``zero`` provider. + + :param data_key: Data key to evaluate + :type data_key: :class:`aws_encryption_sdk.structures.DataKey`, + :class:`aws_encryption_sdk.structures.RawDataKey`, + or :class:`aws_encryption_sdk.structures.EncryptedDataKey` + :returns: Boolean statement of ownership + :rtype: bool + """ + return data_key.key_provider.provider_id in self._allowed_provider_ids + + def _generate_data_key(self, algorithm, encryption_context): + # type: (AlgorithmSuite, Dict[Text, Text]) -> NoReturn + """NullMasterKey does not support generate_data_key + + :param algorithm: Algorithm on which to base data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in encryption + :raises NotImplementedError: when called + """ + raise NotImplementedError("NullMasterKey does not support generate_data_key") + + def _encrypt_data_key(self, data_key, algorithm, encryption_context): + # type: (DataKey, AlgorithmSuite, Dict[Text, Text]) -> NoReturn + """NullMasterKey does not support encrypt_data_key + + :param data_key: Unencrypted data key + :type data_key: :class:`aws_encryption_sdk.structures.RawDataKey` + or :class:`aws_encryption_sdk.structures.DataKey` + :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in encryption + :raises NotImplementedError: when called + """ + raise NotImplementedError("NullMasterKey does not support encrypt_data_key") + + def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): + # type: (EncryptedDataKey, AlgorithmSuite, Dict[Text, Text]) -> DataKey + """Decrypt an encrypted data key and return the plaintext. + + :param data_key: Encrypted data key + :type data_key: aws_encryption_sdk.structures.EncryptedDataKey + :param algorithm: Algorithm object which directs how this Master Key will encrypt the data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context to use in decryption + :returns: Data key containing decrypted data key + :rtype: aws_encryption_sdk.structures.DataKey + """ + data_key = b"\x00" * algorithm.data_key_len + return DataKey( + key_provider=self.key_provider, data_key=data_key, encrypted_data_key=encrypted_data_key.encrypted_data_key + ) diff --git a/decrypt_oracle/src/pylintrc b/decrypt_oracle/src/pylintrc new file mode 100644 index 000000000..1e65ff36c --- /dev/null +++ b/decrypt_oracle/src/pylintrc @@ -0,0 +1,14 @@ +[MESSAGES CONTROL] +# Disabling messages that we either don't care about +# for tests or are necessary to break for tests. +# +# C0330 : bad-continuation (we let black handle this) +# C0412 : ungrouped-imports (we let isort handle this) +# R0205 : useless-object-inheritance (we need to support Python 2, so no, not useless) +disable = C0330, C0412, R0205 + +[FORMAT] +max-line-length = 120 + +[REPORTS] +msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} diff --git a/decrypt_oracle/test/requirements.txt b/decrypt_oracle/test/requirements.txt new file mode 100644 index 000000000..d1b878ce2 --- /dev/null +++ b/decrypt_oracle/test/requirements.txt @@ -0,0 +1,4 @@ +mock +pytest>=3.3.1 +pytest-cov +pytest-mock \ No newline at end of file diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini new file mode 100644 index 000000000..e8f3e6bdc --- /dev/null +++ b/decrypt_oracle/tox.ini @@ -0,0 +1,324 @@ +[tox] +envlist = + py{27,34,35,36,37}, + bandit, doc8, readme, docs, + {flake8,pylint}{,-tests}, + # prone to false positives + vulture + +# Additional test environments: +# +# vulture :: Runs vulture. Prone to false-positives. +# linters :: Runs all linters over all source code. +# linters-tests :: Runs all linters over all tests. + +# Autoformatter helper environments: +# +# autoformat : Apply all autoformatters +# +# black-check : Check for "black" issues +# blacken : Fix all "black" issues +# +# isort-seed : Generate a known_third_party list for isort. +# NOTE: make the "known_third_party = " line in setup.cfg before running this +# NOTE: currently it incorrectly identifies this library too; make sure you remove it +# isort-check : Check for isort issues +# isort : Fix isort issues + +# Operational helper environments: +# +# docs :: Builds Sphinx documentation. +# serve-docs :: Starts local webserver to serve built documentation. +# build :: Builds source and wheel dist files. +# test-release :: Builds dist files and uploads to testpypi pypirc profile. +# release :: Builds dist files and uploads to pypi pypirc profile. + +[testenv:chalice-prep] +skip_install = true +recreate = true +deps = {[testenv:build]deps} +whitelist_externals = + pip +commands = + python -c \ + "import shutil;\ + shutil.rmtree('{toxinidir}/dist/', ignore_errors=True);\ + shutil.rmtree('{toxinidir}/asdf/', ignore_errors=True);" + {[testenv:build]commands} + python {toxinidir}/.chalice/build-requirements.py + +[testenv:chalice] +recreate = true +deps = + {[testenv:chalice-prep]deps} + chalice +commands = + {[testenv:chalice-prep]commands} + chalice {posargs} + +[testenv:base-command] +commands = pytest --basetemp={envtmpdir} -l --cov aws_encryption_sdk_decryption_oracle test/ {posargs} + +[testenv] +passenv = + # Pass through AWS credentials + AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ + # Pass through AWS profile name (useful for local testing) + AWS_PROFILE +sitepackages = False +deps = + -rtest/requirements.txt +commands = {[testenv:base-command]commands} + +# mypy +[testenv:mypy-coverage] +commands = + # Make mypy linecoverage report readable by coverage + python -c \ + "t = open('.coverage', 'w');\ + c = open('build/coverage.json').read();\ + t.write('!coverage.py: This is a private format, don\'t read it directly!\n');\ + t.write(c);\ + t.close()" + coverage report -m + +[testenv:mypy-common] +basepython = python3 +deps = + coverage + mypy>=0.600 + mypy_extensions + typing>=3.6.2 + +[testenv:mypy-py3] +basepython = {[testenv:mypy-common]basepython} +deps = {[testenv:mypy-common]deps} +commands = + python -m mypy \ + --linecoverage-report build \ + src/aws_encryption_sdk_decryption_oracle/ \ + {posargs} + {[testenv:mypy-coverage]commands} + +[testenv:mypy-py2] +basepython = {[testenv:mypy-common]basepython} +deps = {[testenv:mypy-common]deps} +commands = + python -m mypy \ + --py2 \ + --linecoverage-report build \ + src/aws_encryption_sdk_decryption_oracle/ \ + {posargs} + {[testenv:mypy-coverage]commands} + +# Linters +[testenv:flake8] +basepython = python3 +deps = + flake8 + flake8-docstrings + # https://github.com/JBKahn/flake8-print/pull/30 + flake8-print>=3.1.0 +commands = + flake8 \ + src/aws_encryption_sdk_decryption_oracle/ \ + setup.py \ + #doc/conf.py \ + {posargs} + +[testenv:flake8-tests] +basepython = {[testenv:flake8]basepython} +deps = + flake8 +commands = + flake8 \ + # Ignore F811 redefinition errors in tests (breaks with pytest-mock use) + # E203 is not PEP8 compliant https://github.com/ambv/black#slices + # W503 is not PEP8 compliant https://github.com/ambv/black#line-breaks--binary-operators + --ignore F811,E203,W503 \ + test/ \ + {posargs} + +[testenv:pylint] +basepython = python3 +deps = + -rtest/requirements.txt + pyflakes + pylint +commands = + pylint \ + --rcfile=src/pylintrc \ + src/aws_encryption_sdk_decryption_oracle/ \ + setup.py \ + {posargs} + +[testenv:pylint-tests] +basepython = {[testenv:pylint]basepython} +deps = {[testenv:pylint]deps} +commands = + pylint \ + --rcfile=test/pylintrc \ + test/unit/ \ + test/functional/ \ + test/integration/ \ + {posargs} + +[testenv:blacken-src] +basepython = python3 +deps = + black +commands = + black --line-length 120 \ + src/aws_encryption_sdk_decryption_oracle/ \ + setup.py \ + #doc/conf.py \ + test/ \ + {posargs} + + +[testenv:blacken] +basepython = python3 +deps = + {[testenv:blacken-src]deps} +commands = + {[testenv:blacken-src]commands} + +[testenv:black-check] +basepython = python3 +deps = + {[testenv:blacken]deps} +commands = + {[testenv:blacken-src]commands} --diff + +[testenv:isort-seed] +basepython = python3 +deps = seed-isort-config +commands = seed-isort-config + +[testenv:isort] +basepython = python3 +deps = isort +commands = isort -rc \ + src \ + test \ + #doc \ + setup.py \ + {posargs} + +[testenv:isort-check] +basepython = python3 +deps = {[testenv:isort]deps} +commands = {[testenv:isort]commands} -c + +[testenv:autoformat] +basepython = python3 +deps = + {[testenv:blacken]deps} + {[testenv:isort]deps} +commands = + {[testenv:blacken]commands} + {[testenv:isort]commands} + +[testenv:doc8] +basepython = python3 +deps = + sphinx + doc8 +commands = doc8 doc/index.rst README.rst CHANGELOG.rst + +[testenv:readme] +basepython = python3 +deps = readme_renderer +commands = python setup.py check -r -s + +[testenv:bandit] +basepython = python3 +deps = + # Pull bandit from github because they haven't published 1.4.1 to pypi yet + git+git://github.com/PyCQA/bandit.git@master +commands = bandit -r src/aws_encryption_sdk_decryption_oracle/ + +# Prone to false positives: only run independently +[testenv:vulture] +basepython = python3 +deps = vulture +commands = vulture src/aws_encryption_sdk_decryption_oracle/ + +[testenv:linters] +basepython = python3 +deps = + {[testenv:flake8]deps} + {[testenv:pylint]deps} + {[testenv:doc8]deps} + {[testenv:readme]deps} + {[testenv:bandit]deps} +commands = + {[testenv:flake8]commands} + {[testenv:pylint]commands} + {[testenv:doc8]commands} + {[testenv:readme]commands} + {[testenv:bandit]commands} + +[testenv:linters-tests] +basepython = python3 +deps = + {[testenv:flake8-tests]deps} + {[testenv:pylint-tests]deps} +commands = + {[testenv:flake8-tests]commands} + {[testenv:pylint-tests]commands} + +# Documentation +[testenv:docs] +basepython = python3 +deps = -rdoc/requirements.txt +commands = + sphinx-build -E -c doc/ -b html doc/ doc/build/html + +[testenv:serve-docs] +basepython = python3 +skip_install = true +changedir = doc/build/html +deps = +commands = + python -m http.server {posargs} + +# Release tooling +[testenv:park] +basepython = python3 +skip_install = true +deps = + pypi-parker + setuptools +commands = python setup.py park + +[testenv:build] +basepython = python3 +skip_install = true +deps = + #{[testenv:docs]deps} + wheel + setuptools +commands = + #{[testenv:docs]commands} + python setup.py sdist bdist_wheel + +[testenv:test-release] +basepython = python3 +skip_install = true +deps = + {[testenv:build]deps} + twine +commands = + {[testenv:build]commands} + twine upload --skip-existing --repository testpypi dist/* + +[testenv:release] +basepython = python3 +skip_install = true +deps = + {[testenv:build]deps} + twine +commands = + {[testenv:build]commands} + twine upload --skip-existing --repository pypi dist/* From 23c558aa6df0b8370fa7bbc3064eef1a20a4a08c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 24 Sep 2018 17:09:25 -0700 Subject: [PATCH 02/37] lock chalice environments to Python 3.6 --- decrypt_oracle/tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index e8f3e6bdc..02aaf4c25 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -34,6 +34,7 @@ envlist = # release :: Builds dist files and uploads to pypi pypirc profile. [testenv:chalice-prep] +basepython = python3.6 skip_install = true recreate = true deps = {[testenv:build]deps} @@ -48,6 +49,7 @@ commands = python {toxinidir}/.chalice/build-requirements.py [testenv:chalice] +basepython = python3.6 recreate = true deps = {[testenv:chalice-prep]deps} From 2d44a1900e569b15b40179fa17e49909b30c8d8c Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 11:17:39 -0700 Subject: [PATCH 03/37] set v0/decrypt to binary content type --- decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 9f7104665..fa9243006 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -28,7 +28,7 @@ def _master_key_provider(): return master_key_provider -@APP.route("/v0/decrypt", methods=["POST"]) +@APP.route("/v0/decrypt", methods=["POST"], content_types=['application/octet-stream']) def basic_decrypt(): # type: () -> Dict[Text, Union[Text, bool, int]] """Basic decrypt handler for decryption oracle v0.""" From 57cf967aab11be5a1421f28e13906d7979c62286 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 11:17:58 -0700 Subject: [PATCH 04/37] add custom policy for decrypt oracle Lambda --- decrypt_oracle/.chalice/policy.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 decrypt_oracle/.chalice/policy.json diff --git a/decrypt_oracle/.chalice/policy.json b/decrypt_oracle/.chalice/policy.json new file mode 100644 index 000000000..f02c2e038 --- /dev/null +++ b/decrypt_oracle/.chalice/policy.json @@ -0,0 +1,29 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "Resource": [ + "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + ] + }, + { + "Effect": "Deny", + "NotAction": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + "NotResource": [ + "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + ] + } + ] +} \ No newline at end of file From c8fb677c348cc440b24acd694408e940f7fdfbd0 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 12:23:28 -0700 Subject: [PATCH 05/37] add logging to decrypt oracle --- .../app.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index fa9243006..9d40ae9e4 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -2,6 +2,8 @@ """ import base64 +import json +import logging import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider @@ -10,7 +12,8 @@ from .key_providers.counting_master_key import CountingMasterKey from .key_providers.null_master_key import NullMasterKey -APP = Chalice(app_name="awses-encryption-oracle-python") +APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle") +_LOGGER = logging.getLogger("aws-encryption-sdk-decryption-oracle") try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Dict, Text, NoReturn, Union # noqa pylint: disable=unused-import @@ -28,14 +31,21 @@ def _master_key_provider(): return master_key_provider -@APP.route("/v0/decrypt", methods=["POST"], content_types=['application/octet-stream']) +@APP.route("/v0/decrypt", methods=["POST"], content_types=["application/octet-stream"]) def basic_decrypt(): # type: () -> Dict[Text, Union[Text, bool, int]] """Basic decrypt handler for decryption oracle v0.""" + _LOGGER.debug("Request:") + _LOGGER.debug(json.dumps(APP.current_request.to_dict())) + try: request = APP.current_request.json_body ciphertext = base64.b64decode(request["body"].encode("utf-8")) plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) - return {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} + response = {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} except Exception as error: # pylint: disable=broad-except - return {"body": str(error), "isBase64Encoded": False, "statusCode": 400} + response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} + + _LOGGER.debug("Response:") + _LOGGER.debug(json.dumps(response)) + return response From 23ae278158e8bbbcaab84530ef752471483115b1 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 12:24:08 -0700 Subject: [PATCH 06/37] do not let Chalice generate the IAM policy for the decrypt oracle --- decrypt_oracle/.chalice/config.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/decrypt_oracle/.chalice/config.json b/decrypt_oracle/.chalice/config.json index bed541583..051fdd8cb 100644 --- a/decrypt_oracle/.chalice/config.json +++ b/decrypt_oracle/.chalice/config.json @@ -1,9 +1,10 @@ { - "version": "2.0", - "app_name": "aws-encryption-sdk-decryption-oracle", - "stages": { - "dev": { - "api_gateway_stage": "api" + "version": "2.0", + "app_name": "aws-encryption-sdk-decryption-oracle", + "autogen_policy": false, + "stages": { + "dev": { + "api_gateway_stage": "api" + } } - } } From 9a2094e4e87b4452936975f475227bfeafea5b83 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 12:25:31 -0700 Subject: [PATCH 07/37] move chalice policy to the right location --- decrypt_oracle/.chalice/{policy.json => policy-dev.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename decrypt_oracle/.chalice/{policy.json => policy-dev.json} (100%) diff --git a/decrypt_oracle/.chalice/policy.json b/decrypt_oracle/.chalice/policy-dev.json similarity index 100% rename from decrypt_oracle/.chalice/policy.json rename to decrypt_oracle/.chalice/policy-dev.json From f9dd0a4a47b6cc671487c6cbd7bc0849c0a88faf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 12:34:26 -0700 Subject: [PATCH 08/37] add logging permissions to decrypt oracle policy --- decrypt_oracle/.chalice/policy-dev.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json index f02c2e038..3bc879543 100644 --- a/decrypt_oracle/.chalice/policy-dev.json +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -13,16 +13,33 @@ "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" ] }, + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": [ + "arn:aws:logs:*:*:log-group", + "arn:aws:logs:*:*:log-group:*" + ] + }, { "Effect": "Deny", "NotAction": [ "kms:Decrypt", "kms:Encrypt", - "kms:GenerateDataKey" + "kms:GenerateDataKey", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" ], "NotResource": [ "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", - "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", + "arn:aws:logs:*:*:log-group", + "arn:aws:logs:*:*:log-group:*" ] } ] From 2d655e1e9e7b490a26dc85ec490e7ef3d7641b51 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 12:44:01 -0700 Subject: [PATCH 09/37] switch to using the log handler provided in Chalice app --- .../src/aws_encryption_sdk_decryption_oracle/app.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 9d40ae9e4..9d339c9f5 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -13,7 +13,7 @@ from .key_providers.null_master_key import NullMasterKey APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle") -_LOGGER = logging.getLogger("aws-encryption-sdk-decryption-oracle") +APP.log.setLevel(logging.DEBUG) try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Dict, Text, NoReturn, Union # noqa pylint: disable=unused-import @@ -35,8 +35,8 @@ def _master_key_provider(): def basic_decrypt(): # type: () -> Dict[Text, Union[Text, bool, int]] """Basic decrypt handler for decryption oracle v0.""" - _LOGGER.debug("Request:") - _LOGGER.debug(json.dumps(APP.current_request.to_dict())) + APP.log.debug("Request:") + APP.log.debug(json.dumps(APP.current_request.to_dict())) try: request = APP.current_request.json_body @@ -46,6 +46,6 @@ def basic_decrypt(): except Exception as error: # pylint: disable=broad-except response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} - _LOGGER.debug("Response:") - _LOGGER.debug(json.dumps(response)) + APP.log.debug("Response:") + APP.log.debug(json.dumps(response)) return response From 0020b9f6cbce699b033b66a12976fa39ee69003d Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 13:02:29 -0700 Subject: [PATCH 10/37] switch to Chalice response objects that should take care of ther base64 dance --- .../app.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 9d339c9f5..8f173f165 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -7,7 +7,7 @@ import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider -from chalice import Chalice +from chalice import Chalice, Response from .key_providers.counting_master_key import CountingMasterKey from .key_providers.null_master_key import NullMasterKey @@ -33,7 +33,7 @@ def _master_key_provider(): @APP.route("/v0/decrypt", methods=["POST"], content_types=["application/octet-stream"]) def basic_decrypt(): - # type: () -> Dict[Text, Union[Text, bool, int]] + # type: () -> Response """Basic decrypt handler for decryption oracle v0.""" APP.log.debug("Request:") APP.log.debug(json.dumps(APP.current_request.to_dict())) @@ -42,10 +42,19 @@ def basic_decrypt(): request = APP.current_request.json_body ciphertext = base64.b64decode(request["body"].encode("utf-8")) plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) - response = {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} + response = Response( + body=plaintext, + headers={'Content-Type': 'application/octet-stream'}, + status_code=200 + ) + # response = {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} except Exception as error: # pylint: disable=broad-except - response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} + response = Response( + body=str(error), + status_code=400 + ) + # response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} APP.log.debug("Response:") - APP.log.debug(json.dumps(response)) + APP.log.debug(str(response)) return response From 954a34f4b3ac7ba0af256622ac5967e189c5647b Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 13:12:28 -0700 Subject: [PATCH 11/37] convert to using request raw_body --- .../app.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 8f173f165..3a67d01d1 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -4,6 +4,7 @@ import base64 import json import logging +import traceback import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider @@ -39,22 +40,15 @@ def basic_decrypt(): APP.log.debug(json.dumps(APP.current_request.to_dict())) try: - request = APP.current_request.json_body - ciphertext = base64.b64decode(request["body"].encode("utf-8")) + ciphertext = APP.current_request.raw_body + # ciphertext = base64.b64decode(request["body"].encode("utf-8")) plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) - response = Response( - body=plaintext, - headers={'Content-Type': 'application/octet-stream'}, - status_code=200 - ) + response = Response(body=plaintext, headers={"Content-Type": "application/octet-stream"}, status_code=200) # response = {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} except Exception as error: # pylint: disable=broad-except - response = Response( - body=str(error), - status_code=400 - ) + response = Response(body="\n".join([str(error), traceback.format_exc()]), status_code=400) # response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} APP.log.debug("Response:") - APP.log.debug(str(response)) + APP.log.debug(json.dumps(response.to_dict())) return response From 7e653cdf9048bd4c6b76b9147ede5d753bc449cf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 13:47:54 -0700 Subject: [PATCH 12/37] remove old code and add raw body debug logging --- .../src/aws_encryption_sdk_decryption_oracle/app.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 3a67d01d1..c6c9482ee 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -38,16 +38,14 @@ def basic_decrypt(): """Basic decrypt handler for decryption oracle v0.""" APP.log.debug("Request:") APP.log.debug(json.dumps(APP.current_request.to_dict())) + APP.log.debug(APP.current_request.raw_body) try: ciphertext = APP.current_request.raw_body - # ciphertext = base64.b64decode(request["body"].encode("utf-8")) plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) response = Response(body=plaintext, headers={"Content-Type": "application/octet-stream"}, status_code=200) - # response = {"body": base64.b64encode(plaintext), "isBase64Encoded": True, "statusCode": 200} except Exception as error: # pylint: disable=broad-except response = Response(body="\n".join([str(error), traceback.format_exc()]), status_code=400) - # response = {"body": str(error), "isBase64Encoded": False, "statusCode": 400} APP.log.debug("Response:") APP.log.debug(json.dumps(response.to_dict())) From 9d54f588162965f44345e5b2a9a69d4818cf24f7 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 13:55:16 -0700 Subject: [PATCH 13/37] * turn down error response to only include message and not stacktrace * add documentation describing the API --- .../aws_encryption_sdk_decryption_oracle/app.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index c6c9482ee..8177a8d7b 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -1,10 +1,6 @@ -""" - -""" -import base64 +"""Decrypt Oracle using the AWS Encryption SDK for Python.""" import json import logging -import traceback import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider @@ -35,9 +31,16 @@ def _master_key_provider(): @APP.route("/v0/decrypt", methods=["POST"], content_types=["application/octet-stream"]) def basic_decrypt(): # type: () -> Response - """Basic decrypt handler for decryption oracle v0.""" + """Basic decrypt handler for decryption oracle v0. + + The API expects raw ciphertext bytes as the POST body and responds with either: + + 1. A 200 response code with the raw plaintext bytes as the body. + 2. A 400 response code with whatever error code was encountered as the body. + """ APP.log.debug("Request:") APP.log.debug(json.dumps(APP.current_request.to_dict())) + APP.log.debug("Request body:") APP.log.debug(APP.current_request.raw_body) try: @@ -45,7 +48,7 @@ def basic_decrypt(): plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) response = Response(body=plaintext, headers={"Content-Type": "application/octet-stream"}, status_code=200) except Exception as error: # pylint: disable=broad-except - response = Response(body="\n".join([str(error), traceback.format_exc()]), status_code=400) + response = Response(body=str(error), status_code=400) APP.log.debug("Response:") APP.log.debug(json.dumps(response.to_dict())) From 68a0f85b3ec37232631d3e6460e1ac56a00d819e Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 16:16:53 -0700 Subject: [PATCH 14/37] add plaintext decryption logging --- .../src/aws_encryption_sdk_decryption_oracle/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 8177a8d7b..05c57f1b4 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -40,12 +40,14 @@ def basic_decrypt(): """ APP.log.debug("Request:") APP.log.debug(json.dumps(APP.current_request.to_dict())) - APP.log.debug("Request body:") + APP.log.debug("Ciphertext:") APP.log.debug(APP.current_request.raw_body) try: ciphertext = APP.current_request.raw_body plaintext, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=_master_key_provider()) + APP.log.debug("Plaintext:") + APP.log.debug(plaintext) response = Response(body=plaintext, headers={"Content-Type": "application/octet-stream"}, status_code=200) except Exception as error: # pylint: disable=broad-except response = Response(body=str(error), status_code=400) From ba17fd3b2be5ca42201924cd1c1efb426b49dc43 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 16:25:04 -0700 Subject: [PATCH 15/37] add environment variable controlled debug flag in Chalice decryption oracle --- decrypt_oracle/.chalice/config.json | 5 ++++- .../src/aws_encryption_sdk_decryption_oracle/app.py | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/.chalice/config.json b/decrypt_oracle/.chalice/config.json index 051fdd8cb..71f70db7c 100644 --- a/decrypt_oracle/.chalice/config.json +++ b/decrypt_oracle/.chalice/config.json @@ -4,7 +4,10 @@ "autogen_policy": false, "stages": { "dev": { - "api_gateway_stage": "api" + "api_gateway_stage": "api", + "environment_variables": { + "CHALICE_DEBUG": "yes" + } } } } diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 05c57f1b4..d774a0799 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -1,6 +1,7 @@ """Decrypt Oracle using the AWS Encryption SDK for Python.""" import json import logging +import os import aws_encryption_sdk from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider @@ -9,7 +10,8 @@ from .key_providers.counting_master_key import CountingMasterKey from .key_providers.null_master_key import NullMasterKey -APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle") +CHALICE_DEBUG = os.environ.get("CHALICE_DEBUG", "no") == "yes" +APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle", debug=CHALICE_DEBUG) APP.log.setLevel(logging.DEBUG) try: # Python 3.5.0 and 3.5.1 have incompatible typing modules From 2f12ab4b3f4875538bf47236f3803f3ced490c2a Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Tue, 25 Sep 2018 16:28:42 -0700 Subject: [PATCH 16/37] communicate potential binary contents to response logger --- decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index d774a0799..88a71850a 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -55,5 +55,5 @@ def basic_decrypt(): response = Response(body=str(error), status_code=400) APP.log.debug("Response:") - APP.log.debug(json.dumps(response.to_dict())) + APP.log.debug(json.dumps(response.to_dict(binary_types=["application/octet-stream"]))) return response From aec5a8f6d97eb2bb1bfe4edf02b30e88baaadcd5 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 00:26:39 -0700 Subject: [PATCH 17/37] comments, formatting, and tests --- decrypt_oracle/app.py | 2 +- decrypt_oracle/setup.cfg | 4 +- .../app.py | 19 +- .../{counting_master_key.py => counting.py} | 0 .../{null_master_key.py => null.py} | 21 ++- decrypt_oracle/test/__init__.py | 13 ++ decrypt_oracle/test/integration/__init__.py | 13 ++ .../integration/integration_test_utils.py | 121 +++++++++++++ .../test/integration/test_i_decrypt_oracle.py | 30 ++++ decrypt_oracle/test/requirements.txt | 3 +- .../test/test_n_generate_test_vectors.py | 79 +++++++++ decrypt_oracle/test/unit/__init__.py | 13 ++ .../test/unit/key_providers/__init__.py | 13 ++ .../unit/key_providers/test_u_counting.py | 41 +++++ .../test/unit/key_providers/test_u_null.py | 41 +++++ .../test/vectors/decrypt_vectors.json | 164 ++++++++++++++++++ decrypt_oracle/tox.ini | 8 +- 17 files changed, 572 insertions(+), 13 deletions(-) rename decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/{counting_master_key.py => counting.py} (100%) rename decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/{null_master_key.py => null.py} (82%) create mode 100644 decrypt_oracle/test/__init__.py create mode 100644 decrypt_oracle/test/integration/__init__.py create mode 100644 decrypt_oracle/test/integration/integration_test_utils.py create mode 100644 decrypt_oracle/test/integration/test_i_decrypt_oracle.py create mode 100644 decrypt_oracle/test/test_n_generate_test_vectors.py create mode 100644 decrypt_oracle/test/unit/__init__.py create mode 100644 decrypt_oracle/test/unit/key_providers/__init__.py create mode 100644 decrypt_oracle/test/unit/key_providers/test_u_counting.py create mode 100644 decrypt_oracle/test/unit/key_providers/test_u_null.py create mode 100644 decrypt_oracle/test/vectors/decrypt_vectors.json diff --git a/decrypt_oracle/app.py b/decrypt_oracle/app.py index 477526191..d6c278e57 100644 --- a/decrypt_oracle/app.py +++ b/decrypt_oracle/app.py @@ -1,2 +1,2 @@ -"""Shim to pull decryption oracle app into expected location.""" +"""Shim to pull decryption oracle app into expected location for Chalice.""" from aws_encryption_sdk_decryption_oracle.app import APP as app diff --git a/decrypt_oracle/setup.cfg b/decrypt_oracle/setup.cfg index dfd64af69..118ff330d 100644 --- a/decrypt_oracle/setup.cfg +++ b/decrypt_oracle/setup.cfg @@ -17,7 +17,7 @@ ignore_missing_imports = True max_complexity = 10 max_line_length = 120 import_order_style = google -application_import_names = aws_encryption_sdk_cli +application_import_names = aws_encryption_sdk_decryption_oracle builtins = raw_input ignore = # Ignoring D205 and D400 because of false positives @@ -39,4 +39,4 @@ force_grid_wrap = 0 combine_as_imports = True not_skip = __init__.py known_first_party = aws_encryption_sdk_decryption_oracle -known_third_party =aws_encryption_sdk,chalice,setuptools +known_third_party =aws_encryption_sdk,aws_encryption_sdk_decryption_oracle,chalice,pytest,requests,setuptools diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 88a71850a..323a3fe31 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -7,8 +7,8 @@ from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider from chalice import Chalice, Response -from .key_providers.counting_master_key import CountingMasterKey -from .key_providers.null_master_key import NullMasterKey +from .key_providers.counting import CountingMasterKey +from .key_providers.null import NullMasterKey CHALICE_DEBUG = os.environ.get("CHALICE_DEBUG", "no") == "yes" APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle", debug=CHALICE_DEBUG) @@ -35,10 +35,19 @@ def basic_decrypt(): # type: () -> Response """Basic decrypt handler for decryption oracle v0. - The API expects raw ciphertext bytes as the POST body and responds with either: + **Request** - 1. A 200 response code with the raw plaintext bytes as the body. - 2. A 400 response code with whatever error code was encountered as the body. + * **Method**: POST + * **Body**: Raw ciphertext bytes + * **Headers**: + + * **Content-Type**: ``application/octet-stream`` + * **Accept**: ``application/octet-stream`` + + **Response** + + * 200 response code with the raw plaintext bytes as the body + * 400 response code with whatever error code was encountered as the body """ APP.log.debug("Request:") APP.log.debug(json.dumps(APP.current_request.to_dict())) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py similarity index 100% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting_master_key.py rename to decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py similarity index 82% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py rename to decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py index 86790986f..2f959f9b0 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null_master_key.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py @@ -43,6 +43,18 @@ def owns_data_key(self, data_key): """ return data_key.key_provider.provider_id in self._allowed_provider_ids + @staticmethod + def _null_plaintext_data_key(algorithm): + # type: (AlgorithmSuite) -> bytes + """Build the null data key of the correct length for the requested algorithm suite. + + :param algorithm: Algorithm on which to base data key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :returns: Null data key + :rtype: bytes + """ + return b"\x00" * algorithm.data_key_len + def _generate_data_key(self, algorithm, encryption_context): # type: (AlgorithmSuite, Dict[Text, Text]) -> NoReturn """NullMasterKey does not support generate_data_key @@ -52,7 +64,9 @@ def _generate_data_key(self, algorithm, encryption_context): :param dict encryption_context: Encryption context to use in encryption :raises NotImplementedError: when called """ - raise NotImplementedError("NullMasterKey does not support generate_data_key") + return DataKey( + key_provider=self.key_provider, data_key=self._null_plaintext_data_key(algorithm), encrypted_data_key=b"" + ) def _encrypt_data_key(self, data_key, algorithm, encryption_context): # type: (DataKey, AlgorithmSuite, Dict[Text, Text]) -> NoReturn @@ -80,7 +94,8 @@ def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): :returns: Data key containing decrypted data key :rtype: aws_encryption_sdk.structures.DataKey """ - data_key = b"\x00" * algorithm.data_key_len return DataKey( - key_provider=self.key_provider, data_key=data_key, encrypted_data_key=encrypted_data_key.encrypted_data_key + key_provider=self.key_provider, + data_key=self._null_plaintext_data_key(algorithm), + encrypted_data_key=encrypted_data_key.encrypted_data_key, ) diff --git a/decrypt_oracle/test/__init__.py b/decrypt_oracle/test/__init__.py new file mode 100644 index 000000000..2add15ef3 --- /dev/null +++ b/decrypt_oracle/test/__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/decrypt_oracle/test/integration/__init__.py b/decrypt_oracle/test/integration/__init__.py new file mode 100644 index 000000000..2add15ef3 --- /dev/null +++ b/decrypt_oracle/test/integration/__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/decrypt_oracle/test/integration/integration_test_utils.py b/decrypt_oracle/test/integration/integration_test_utils.py new file mode 100644 index 000000000..0d8fb5cae --- /dev/null +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -0,0 +1,121 @@ +# 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. +"""Helper utilities for use by integration tests.""" +import base64 +import json +import os +from collections import namedtuple + +import pytest +from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider + +HERE = os.path.abspath(os.path.dirname(__file__)) +DEPLOYMENT_REGION = "AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION" +DEPLOYMENT_ID = "AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID" +AWS_KMS_KEY_ID = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID" +_KMS_MKP = None +_ENDPOINT = None + + +def decrypt_endpoint(): + """Build the API endpoint based on environment variables.""" + global _ENDPOINT # pylint: disable=global-statement + + if _ENDPOINT is not None: + return _ENDPOINT + + try: + deployment_id = os.environ[DEPLOYMENT_ID] + region = os.environ[DEPLOYMENT_REGION] + except KeyError: + raise ValueError( + ( + 'Environment variables "{region}" and "{deployment}" ' + "must be set to the correct values for the deployed decrypt oracle." + ).format(region=DEPLOYMENT_REGION, deployment=DEPLOYMENT_ID) + ) + + _ENDPOINT = "https://{deployment_id}.execute-api.{region}.amazonaws.com/api/v0/decrypt".format( + deployment_id=deployment_id, region=region + ) + return _ENDPOINT + + +def get_cmk_arn(): + """Retrieve the target CMK ARN from environment variable.""" + try: + arn = os.environ[AWS_KMS_KEY_ID] + except KeyError: + raise ValueError( + 'Environment variable "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format( + AWS_KMS_KEY_ID + ) + ) + + if arn.startswith("arn:") and ":alias/" not in arn: + return arn + + raise ValueError("KMS CMK ARN provided for integration tests must be a key not an alias") + + +def kms_master_key_provider(cache=True): + """Build the expected KMS Master Key Provider based on environment variables.""" + global _KMS_MKP # pylint: disable=global-statement + + if cache and _KMS_MKP is not None: + return _KMS_MKP + + cmk_arn = get_cmk_arn() + _kms_master_key_provider = KMSMasterKeyProvider() + _kms_master_key_provider.add_master_key(cmk_arn) + + if cache: + _KMS_MKP = _kms_master_key_provider + + return _kms_master_key_provider + + +def test_vectors_filename(): + """Provide the absolute path to the test vectors file.""" + return os.path.join(HERE, "..", "vectors", "decrypt_vectors.json") + + +TestVector = namedtuple("TestVector", ["plaintext", "ciphertext", "key_type", "algorithm_suite"]) + + +def all_test_vectors(): + """Collect and iterate through all test vectors.""" + + with open(test_vectors_filename(), "r") as vectors_file: + raw_vectors = json.load(vectors_file) + + for vector in raw_vectors: + vector_name = "::".join([vector["key-type"], vector["algorithm-suite"]]) + plaintext = base64.b64decode(vector["plaintext"].encode("utf-8")) + ciphertext = base64.b64decode(vector["ciphertext"].encode("utf-8")) + yield pytest.param( + TestVector( + plaintext=plaintext, + ciphertext=ciphertext, + key_type=vector["key-type"], + algorithm_suite=vector["algorithm-suite"], + ), + id=vector_name, + ) + + +def filtered_test_vectors(filter_function): + """Collect and iterate through all test vectors that pass the filter function.""" + for vector_param in all_test_vectors(): + if filter_function(vector_param.values[0]): + yield vector_param diff --git a/decrypt_oracle/test/integration/test_i_decrypt_oracle.py b/decrypt_oracle/test/integration/test_i_decrypt_oracle.py new file mode 100644 index 000000000..5863ecc0e --- /dev/null +++ b/decrypt_oracle/test/integration/test_i_decrypt_oracle.py @@ -0,0 +1,30 @@ +# 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. +"""Integration tests for deployed API.""" +import pytest +import requests + +from .integration_test_utils import all_test_vectors, decrypt_endpoint + +pytestmark = [pytest.mark.integ] + + +@pytest.mark.parametrize("vector", all_test_vectors()) +def test_all_vectors(vector): + response = requests.post( + decrypt_endpoint(), + data=vector.ciphertext, + headers={"Content-Type": "application/octet-stream", "Accept": "application/octet-stream"}, + ) + assert response.status_code == 200 + assert response.content == vector.plaintext diff --git a/decrypt_oracle/test/requirements.txt b/decrypt_oracle/test/requirements.txt index d1b878ce2..11be59be6 100644 --- a/decrypt_oracle/test/requirements.txt +++ b/decrypt_oracle/test/requirements.txt @@ -1,4 +1,5 @@ mock pytest>=3.3.1 pytest-cov -pytest-mock \ No newline at end of file +pytest-mock +requests diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py new file mode 100644 index 000000000..8c0556726 --- /dev/null +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -0,0 +1,79 @@ +# 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. +"""Generate test vectors for use in testing the decryption oracle.""" +import base64 +import binascii +import json +import os + +import aws_encryption_sdk +import pytest +from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.key_providers.kms import KMSMasterKey + +from aws_encryption_sdk_decryption_oracle.key_providers.counting import CountingMasterKey +from aws_encryption_sdk_decryption_oracle.key_providers.null import NullMasterKey + +from .integration.integration_test_utils import test_vectors_filename + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Dict, Text, Iterable # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + +HERE = os.path.abspath(os.path.dirname(__file__)) +GENERATE_VECTORS = "AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_GENERATE_TEST_VECTORS" +PUBLIC_CMK = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" +ENCRYPTION_CONTEXT = {"key1": "val1", "key2": "val2"} + + +def _key_providers(): + # type: () -> Iterable[MasterKeyProvider] + """Generate all master key providers for test vector generation. + Each will be used independently. + """ + yield NullMasterKey() + yield CountingMasterKey() + yield KMSMasterKey(key_id=PUBLIC_CMK) + + +def _generate_vectors(key_provider, plaintext): + # type: (MasterKeyProvider, bytes) -> Iterable[Dict[Text, Text]] + """Generate all desired test vectors for a given key provider and plaintext.""" + for algorithm_suite in aws_encryption_sdk.Algorithm: + ciphertext, _header = aws_encryption_sdk.encrypt( + source=plaintext, + encryption_context=ENCRYPTION_CONTEXT, + key_provider=key_provider, + algorithm=algorithm_suite, + ) + yield { + "key-type": key_provider.provider_id, + "algorithm-suite": binascii.hexlify(algorithm_suite.id_as_bytes()).decode("utf-8"), + "ciphertext": base64.b64encode(ciphertext).decode("utf-8"), + "plaintext": base64.b64encode(plaintext).decode("utf-8"), + } + + +@pytest.mark.generate +@pytest.mark.skipif(GENERATE_VECTORS not in os.environ, reason="Generating test vectors is a rare occurance.") +def test_not_a_test_generate_test_vectors(): + """Generate all expected test vectors and write them to ``test/vectors/decrypt_oracle.json``.""" + vectors = [] + plaintext = os.urandom(64) + for key_provider in _key_providers(): + vectors.extend(_generate_vectors(key_provider, plaintext)) + + with open(test_vectors_filename(), "w") as output: + json.dump(vectors, output, indent=4) diff --git a/decrypt_oracle/test/unit/__init__.py b/decrypt_oracle/test/unit/__init__.py new file mode 100644 index 000000000..2add15ef3 --- /dev/null +++ b/decrypt_oracle/test/unit/__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/decrypt_oracle/test/unit/key_providers/__init__.py b/decrypt_oracle/test/unit/key_providers/__init__.py new file mode 100644 index 000000000..2add15ef3 --- /dev/null +++ b/decrypt_oracle/test/unit/key_providers/__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/decrypt_oracle/test/unit/key_providers/test_u_counting.py b/decrypt_oracle/test/unit/key_providers/test_u_counting.py new file mode 100644 index 000000000..25b057181 --- /dev/null +++ b/decrypt_oracle/test/unit/key_providers/test_u_counting.py @@ -0,0 +1,41 @@ +# 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. +"""Unit test for ``aws_encryption_sdk_decryption_oracle.key_providers.counting``.""" +import aws_encryption_sdk +import pytest + +from aws_encryption_sdk_decryption_oracle.key_providers.counting import CountingMasterKey + +from ...integration.integration_test_utils import filtered_test_vectors + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +@pytest.mark.parametrize("vector", filtered_test_vectors(lambda x: x.key_type == "test_counting")) +def test_counting_master_key_decrypt_vectors(vector): + master_key = CountingMasterKey() + + plaintext, _header = aws_encryption_sdk.decrypt(source=vector.ciphertext, key_provider=master_key) + + assert plaintext == vector.plaintext + + +def test_counting_master_key_cycle(): + plaintext = b"some super secret plaintext" + master_key = CountingMasterKey() + + ciphertext, _header = aws_encryption_sdk.encrypt(source=plaintext, key_provider=master_key) + decrypted, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=master_key) + + assert plaintext != ciphertext + assert plaintext == decrypted diff --git a/decrypt_oracle/test/unit/key_providers/test_u_null.py b/decrypt_oracle/test/unit/key_providers/test_u_null.py new file mode 100644 index 000000000..13b749c3c --- /dev/null +++ b/decrypt_oracle/test/unit/key_providers/test_u_null.py @@ -0,0 +1,41 @@ +# 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. +"""Unit test for ``aws_encryption_sdk_decryption_oracle.key_providers.null``.""" +import aws_encryption_sdk +import pytest + +from aws_encryption_sdk_decryption_oracle.key_providers.null import NullMasterKey + +from ...integration.integration_test_utils import filtered_test_vectors + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +@pytest.mark.parametrize("vector", filtered_test_vectors(lambda x: x.key_type == "null")) +def test_null_master_key_decrypt_vectors(vector): + master_key = NullMasterKey() + + plaintext, _header = aws_encryption_sdk.decrypt(source=vector.ciphertext, key_provider=master_key) + + assert plaintext == vector.plaintext + + +def test_null_master_key_cycle(): + plaintext = b"some super secret plaintext" + master_key = NullMasterKey() + + ciphertext, _header = aws_encryption_sdk.encrypt(source=plaintext, key_provider=master_key) + decrypted, _header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=master_key) + + assert plaintext != ciphertext + assert plaintext == decrypted diff --git a/decrypt_oracle/test/vectors/decrypt_vectors.json b/decrypt_oracle/test/vectors/decrypt_vectors.json new file mode 100644 index 000000000..2428ed333 --- /dev/null +++ b/decrypt_oracle/test/vectors/decrypt_vectors.json @@ -0,0 +1,164 @@ +[ + { + "key-type": "null", + "algorithm-suite": "0014", + "ciphertext": "AYAAFKVkFS0sDs0vvJUbcrJYfDIAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAA9fY0JImvVXETrYe58mTtl/////wAAAAEAAAAAAAAAAAAAAAEAAABAVvLIE6NLRwHHjFwJ0Baw8qu2kAES43JO7z7ZajaPGqWy/kzkKEyobFD9JGd59MAVGLD6BG7lbvmRn1eX+I4S38zCFf2vCNjfBpg1o6Ko+SU=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0046", + "ciphertext": "AYAARhfcwYZeWdc/lhqxZoAlAcAAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAAr9eMmFPQBZHXrIklw71hr/////wAAAAEAAAAAAAAAAAAAAAEAAABAMtulgax134/D65iscHXzt/C9E6bUXxtphDu5vs2o7gtiamLzTNnzxjVp65YByR5XIYxYKy0Mf99ka6TyKAYPEeB4+ota6El5CpuSGbVch8Y=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0078", + "ciphertext": "AYAAeDvX4VGcMWabYWs3vi1yxPEAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAD6P1iQK5FcwnlBuuZCXgY3/////wAAAAEAAAAAAAAAAAAAAAEAAABAETI1AVC1UmA1vu08JBQD//9deYDKJqWkaoHhwWHp0eokbXTAu6r6rR7MZ5fTefiEWewxMs8lNPMpGSfbI0YgJ9EWe21c8+dyKW19VaPbfh4=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0114", + "ciphertext": "AYABFD68ewiBveMkxqcCOjBBiqMAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAC3Vd+w3IgdloCV33wzjMg0/////wAAAAEAAAAAAAAAAAAAAAEAAABAbbX1R7sxj9oCIxl3wBM3qb0KGO3KZjEQZou3wv9T78tiI3MFhCnE0tcf+M6pxJfjRwZAXf7SEDb3CSZB58Me2Hdjj/+vffYY8d14/haaZEI=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0146", + "ciphertext": "AYABRhk2HEnubP1Gq8an5gwiG7kAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAABI8FjdRQ3fJtTaicsapokr/////wAAAAEAAAAAAAAAAAAAAAEAAABA49tP0pnsHYZ00vXh/12w/y2pjvcxXfcwSJo5NpRjzXpxtKBkeb8VGnNF30OlAn3O4OyUKO5GxnfI7GvktF+AKNi8W3hwssal00ToNeTjz80=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0178", + "ciphertext": "AYABeHMaJwxHPIFq9rhoKfmhxMQAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAATYE3tWc9tiKJ80TgE2MmH/////wAAAAEAAAAAAAAAAAAAAAEAAABA/fOJVvIZhIpMqAMSFTypq0A0iv/IjBemqi5JrJpjA2Gc7LVTY4nkEqT8B4IOAGDFtG5/BbKecio84MZKlcVyc8APkUjFJQy46Sh3I6MFJ0E=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0214", + "ciphertext": "AYACFFLm+8jTVF6zTjy1YO0S5/sAXwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkALEE1WHdHTHR3bHAyTVZLR2JNZzJjTXlHT003bnF2WmdkenZ3T2x2bXoyTVlWAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAA3908JXPjOPXg8FD5XTef//////wAAAAEAAAAAAAAAAAAAAAEAAABAs/iav+XyVxVdpdyZuXn3qL6HenXT822owFp9fbXcGJ1Shos+X51BMHO6yUQ55ti4DWz2wS3K2AJALxYfMa3mBUrc4ViXc88lXtDg19OeuIgARzBFAiBE6Otrt+vg6uETRPhDJeudNYe19DsjJjZzVdsqpyDjKAIhAP67izSHSo3HGlTIlCxsEUXtfp1zQGZSO8t/doxJpP+f", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0346", + "ciphertext": "AYADRqL4HWjWoRAYRSsyFbZkke4AdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF0cG5hSXU5dXRYVW1ZczhqQmNncjNHMUV5UDd0end3OFNWUFN3Nk52VG40UGlmU1RSV0ZMWkYwakVOcnk2V0dudz09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAAC6f7jIZ9tdxgODVEit8IO/////wAAAAEAAAAAAAAAAAAAAAEAAABA09BBceV1hD6pHng9KrpfTf0dct+M0lbL5GRUYr1bh0LJDJmmAnr/bXr9lhwMQpChpqEC3qdjyUSDEWalXVKLrt193jaQXwF2vldnaTb9yp0AZzBlAjAD927HbWIPaW2vRGkiW+8hYq7Jqyto1E9yij2CUhTcQkMJoLJ2q8Y8kS1i9oDEA5QCMQCioislsgkOSUKO8/0pnH1ja3nSfkkDWDBwL/Ipkhjhtv+3gUsOts3eVb5csF+ZUIE=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "null", + "algorithm-suite": "0378", + "ciphertext": "AYADeK1U7YUCrsA1r5jO/OsP3wAAdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF2SnducmNOQ0ZoVXhWQU9wNGloRDdDWVZFRmovelVwbllnZjZOcGUrSGJNTzFQdmNOZ0ZvZTEzcjBCVmVPUDY2UT09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEABG51bGwABG51bGwAAAIAAAAADAAAEAAAAAAAAAAAAAAAAAA0bpAoQQExEnGxSoEdJWTK/////wAAAAEAAAAAAAAAAAAAAAEAAABAC63zfdYXfdWveLvX9ZjoZfWe4vz5YJ6rLN+WtmpITjMX39gLRDwpuPQaQLanToTec4jtg4Ae2bPVf0gpO3PDq35T2PSOE4dXrTPyCXWTCWsAZzBlAjBa2m4QR9gNGGxi11NuILl8hmw3WcjK97iU7p3ly6NciiCXuoAvc48k5+UQIaI5cBQCMQC7k7At9aIy6MVI+BOM9r+/naA8WHseErRWpMSLKwBGgK0nCq0+jewPrtn93ALfN7I=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0014", + "ciphertext": "AYAAFCzcPGZAsx7cgallLwaGHBgAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAAA4vmecj6K3x/bTd0hqCkQ1/////wAAAAEAAAAAAAAAAAAAAAEAAABA56+p97ANW4/HNq8nvV5K/Q+muhoF9uRC7DKs3ZaXqAjb3t7moOUeHRRzm72aM+iCM8Pr61Ao4U5dTIDo0OeH67axfH1fiE53+MNB6ltYIgs=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0046", + "ciphertext": "AYAARqsrDLd0ODf2ZZcllViNJPMAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAABbmuP2+LL0TLph3ZmGJSMU/////wAAAAEAAAAAAAAAAAAAAAEAAABA/hpEJhrbqKA7NzdX023cbHCwwX2h7Jv2VSL+Q6kQcnfuLyW0HL/5Sbxz0f0GedybmF1v4zSb8NKAAxNYsOlT6GC8xQgW68VSpUdIW+J2c30=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0078", + "ciphertext": "AYAAeCjoWm0WdkzUXHHWjH0sqaMAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAACIsKmm1OcZgoejkCejfQWk/////wAAAAEAAAAAAAAAAAAAAAEAAABAlDv7nwHDBLLrdnD0Jz+MwLtuqjQZUM3gtEhr93EZr1C0PPRdoKCbknuN/14lRc5+33nboAjsa1EFZZG6UXS8+KniWfwAMHpP4Zj3FNf9qKA=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0114", + "ciphertext": "AYABFOwVhkF6iYnF5kxNESKO9LMAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAAAr0m9IXw7oOorRhMenbGKd/////wAAAAEAAAAAAAAAAAAAAAEAAABAHSTOw8Fskw6q58wEYye2Qx9n6w6WiZFtTq5+dpadaDx563mg0R4I0GXEc9RUlT1b7ej2BvKueRt3UW41Z2fZ9U8NbXdOiQtRYYAWWmsN3vc=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0146", + "ciphertext": "AYABRuKdc1MlVY75uhIrCcxL8bsAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAACz6NciBbTsq76wnSrRmnVL/////wAAAAEAAAAAAAAAAAAAAAEAAABAjp2TUXuQ5njRcyHLjI8fMm1xxL4NR+P1k6SbzSg1kxzjK0Jeyq7FucU9rmrR38FGU2tfOw5w3Wpi6H0uVqHvmFQ1RJHJF1vcYI6+2qRYdn0=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0178", + "ciphertext": "AYABeNThavBBMGae4fa5ictgtsgAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAACfBtoAx6oO90kFk+Lredhy/////wAAAAEAAAAAAAAAAAAAAAEAAABAOciZ1AUkl5KcEu0C3dfOlK9YFeHRTUOrXeSzH8tjqP3KY63h3Qp27Ar138yDlTXagQWHzMoALV2j9/kGPU26bmO20u9iVi+XQ17E2oplQRA=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0214", + "ciphertext": "AYACFBgGcdgiNf9lPHc8tK8lCm0AXwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkALEFwQ1hyV0I0VEF4ZDBaRkN2Z09OSWx5S2F1RGR6K2pTZFEzenEzNEw2SW1qAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAACRVxXhlMvphTb05K20gxlt/////wAAAAEAAAAAAAAAAAAAAAEAAABAkrr8czEOtWIggyTa+jvffU0fBx1Ohda2TrmcxS4sIy0U8KKqAGCI87QKEI9M+6435kUqIwx/JVeAholffVo8z3AaAsTl7Wd0n9bPaj9AEKoARzBFAiBGMrUoLdCf/MhOKzgCEOh/XG4EUt7cZLSBPBt7gUKvoAIhAPhNHD5Fehj2MciWmGkiIL+c9M67U5FJzH8Ls56EF3mz", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0346", + "ciphertext": "AYADRltOp8jzUQJNsTBDh5eqVIcAdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFnMjF1dFFOUGRhbHU0T2pQdkZFeUkyVGFjZk1oSUNCZzFYbWQ3amVDUTlTdDFEMTlOY1lpYTd1UnhxMHpmOWtqZz09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAADiuZYxFAQEOJN4u3rxiir4/////wAAAAEAAAAAAAAAAAAAAAEAAABA1n/dbg88pLRl6geQqeFQL8vxTK3UrcpIIftitz62Lu9UxZpBUjKfJhNCHKHFyEel5LEUY9RFjVHR4omNIAtNkzu53bmdZnuxhPnX1gbjuZAAZzBlAjBQymIejhNaFfrX+KdM8C/xWROWsoCnavj05bed6R9EThWvC1IpzM0aI5HcnkKFP/QCMQC1S2WhwC4tA5GppHSqMEcNCSZg/Jeu8ef+LuE5R6ji1KRx75ArV7/TsVKzAdnTtrU=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "test_counting", + "algorithm-suite": "0378", + "ciphertext": "AYADeAajxv4OAyR3sWebGqOA5kYAdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFsZmM3aWpnejNqVkg2WllhWjlDUXAvZUpzQXNZb1NRdmlhMktvVlN5a3N1YzJzWjBTQmRBcmk4RmZWREZVV0g2Zz09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEADXRlc3RfY291bnRpbmcAF3Rlc3RfY291bnRpbmdfcHJvdl9pbmZvAAVAQUJDRAIAAAAADAAAEAAAAAAAAAAAAAAAAAC9122sPJ5oIpmMtTmlPZby/////wAAAAEAAAAAAAAAAAAAAAEAAABAHhIUVRZV9UNJngc+mZ/yqMfywV1cNQCGi1Bak7Lxh97XNrgVBZcecZqNzgKuxHo63eZdN+iGT2UxGjwerkw0ubWCOT5uiFw/G5gAWhGlDFwAZzBlAjEAp1xNY0T/W56H2kxW0YyFQH3SsYGMH5a+d9MYq976pg8d5uc06sFC+bUt6xcl6n5CAjAVhVMe74JPB6C7RQJOunzQNpFWYrLbJDPArvcbDuwgWhBeEsuF/W6SfntnxOZdd5k=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0014", + "ciphertext": "AYAAFHu12Cl/GJ+wuyiyvX3jXUAAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCXAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAG4wbAYJKoZIhvcNAQcGoF8wXQIBADBYBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDF+XBdyWe79iYMDjBwIBEIArieK4fhIEB5NKBk1Gq8Ex/EHk5tuBU79TaBk9qnBujP7IzCsQJq4X86ZbwgIAAAAADAAAEAAAAAAAAAAAAAAAAACuTwZQtdtdDlDsQNut65mA/////wAAAAEAAAAAAAAAAAAAAAEAAABAqEd7p6jgX86NCM3P/ALU/IPMKLBp/6wbdobzHfZprkF12j+BBII6/0jGNaY/9Lv+T7iAp6thbPhnF+zZUGo9wG1g+FGOoh/bJqJmjahmSmU=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0046", + "ciphertext": "AYAARknf2OJDHfFdm6Y68ygGeQQAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCfAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAHYwdAYJKoZIhvcNAQcGoGcwZQIBADBgBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDNwu1KuD5z9ydDa/gwIBEIAz62uiWtpce4vboadU8+9BxejjZxPrH3o+SaIL+QXeEE0CSxdBrRpI8GK+r1FW85j980GuAgAAAAAMAAAQAAAAAAAAAAAAAAAAAK9X6eSQGz+DewRlrWafTPj/////AAAAAQAAAAAAAAAAAAAAAQAAAEAAIKUYC+eSxrCINKp8Zqz3G3mhfSqDncHw+GtviaF9v+d9RcPOCOPT5xGm9/dCe6VWTFFOG4k016fGkwb6i2WCuyrWvoM9CJekSHTrG6N6+w==", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0078", + "ciphertext": "AYAAeHx8+qnBeUiXAIsaAtkl9KkAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCnAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDExXHlH+EVNasoOfzgIBEIA7wlr/3NL7AtlnOtSPwJfJmzpclVYL4J0sZytJ87feFi66pBsJ7ZB/B8Se4jpTW41Ewzn/FHaFsz04CygCAAAAAAwAABAAAAAAAAAAAAAAAAAAGzHsbqRXT7E4+pibXJWQZP////8AAAABAAAAAAAAAAAAAAABAAAAQOpgvGlyOBNW8QXMP47OSBeQUfTWjwNjTrOiP2W5WiIaEMNFscppCMl1bNWeMuRgBp/X3XoR93RrramyZuSDT9W4hv3ehiXC/P+Qv38tgB4Y", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0114", + "ciphertext": "AYABFFLlxnrmMYPBkzAzoA61JrIAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCXAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAG4wbAYJKoZIhvcNAQcGoF8wXQIBADBYBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDDSmdfbcH2iwrCp/jAIBEIArfGzd4Gs5yyHFNex9gRL7J3P1i7F+pg4XIWTTHedy3xybWsGpAuwYVHRb/gIAAAAADAAAEAAAAAAAAAAAAAAAAADa710jwanJjLUqlBtsI9QR/////wAAAAEAAAAAAAAAAAAAAAEAAABA7ppqLBghfcLpcVd7b3MGqPvyjDgQnBiUuvRUSMduUeXg9X9V9yFGESVqYwEksTmvOvLyl9SSvlNPoiwWu7Ap/bo22E46db2HJ1Jm4OxzNO4=", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0146", + "ciphertext": "AYABRksd8Ue3M0M+x46E24h7xZIAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCfAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAHYwdAYJKoZIhvcNAQcGoGcwZQIBADBgBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHgX8RPZQvZhtfHvSwIBEIAz9CXZyJPevzMhUmj+Vhb+rmkClNt7sKKM+z0l7kjov1YeLUGUPoJSyL2kRHcs3GB5fuByAgAAAAAMAAAQAAAAAAAAAAAAAAAAAJ1bjSwrgMbZ6DlgT7vDl8D/////AAAAAQAAAAAAAAAAAAAAAQAAAEBNv2yVzC7CPFw3AWTLjeehsS9ktk+2tl92R3244444505LKk63pD0c+UQAp6VjuWHkh77TZOPaK80ajoByhQcGOTjV6+ET9e3zIfXZQRT5Gw==", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0178", + "ciphertext": "AYABeK6ySRN1iSjAEKI8J2B2qUwAGgACAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCnAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHVopJeqze5iabR34QIBEIA7EMD5NFvzuXQFLYvqz7XSbDEhb+BavMAZOnT+vlMjeSBcvWb11u4q/GhfqUVvvQz8LLo6xTE0pp3q3N0CAAAAAAwAABAAAAAAAAAAAAAAAAAA6MVe0RBdh+u9FSPVmtTVVP////8AAAABAAAAAAAAAAAAAAABAAAAQGvVMAgQTiM3r3AZZIqUut3BxBjPP+uiLfSIgZDsPj9A7UnU6/gnfdf8CTrHu8BCnbfQnqKA7h+sDCS2raDANK9ksVzUPOGow1ACYCKEPlE6", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0214", + "ciphertext": "AYACFOhfQbhb4tPXRxb0hhmN2gIAXwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkALEEzT3FEcG1qeGg2T3BoMGZyWnphNWFBSnlmUDluNVprR29ld0Y2UkRiU3hZAARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCXAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAG4wbAYJKoZIhvcNAQcGoF8wXQIBADBYBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDE4v4RbkXTrbtJ7ghwIBEIArhIokXsNHwXVxaMTFugK972QpPOxNbol0eDqykCAqvwbTY9hMoj+4RJVRvwIAAAAADAAAEAAAAAAAAAAAAAAAAAA5+O/6DqxlwvUHSQ5OmVgZ/////wAAAAEAAAAAAAAAAAAAAAEAAABAmZTi2+9NxBHiBkEWOpS7Lmn2A2b1WDQhKaZzyjj7lJtmTwuC49P21m0vz3bBjZ3MVPten+OfcvgUtuAosdEcX/dj6UOdDI7ptjJSNn45iAgARzBFAiEA8x0/MKdCEh4K9bCxXsKmA/voO05mXp544NB4G6qQRzgCIG6wm7VKhaaYmXikGUk8mqfB2pV1laXQvoSzRXa2MfqJ", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0346", + "ciphertext": "AYADRrNbZZEej1hJmOTRqRti54AAdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF5bTdrQjU5dzhzdklkN2I5cGxwQnN6WUU4Q0Mwei9BUFdxSDMxdXU5dkFCQ1ZIeDZpQU9uSlpGK0dhY3BEcTl0QT09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCfAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAHYwdAYJKoZIhvcNAQcGoGcwZQIBADBgBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDLoAhd+I8wCAGRICSgIBEIAz9N/21bvspaM2yAi810dNmInX/jg38txVeSLe2VPC98PSFqwavIr9GRtgCo8H4VYfNv5QAgAAAAAMAAAQAAAAAAAAAAAAAAAAACZTjTcxEoVIiholgNAXZ4r/////AAAAAQAAAAAAAAAAAAAAAQAAAEDfGfcQapK3ccz6xcSIurb3aoBcp0JcYw3ItHAv1vOzXH7E2XVj6juKs6TDVRBBfjSWLuANYROTGNAcvdvRu9k5O+9xWhGxEw9gwSRL7Alz/QBnMGUCMAgBPEWM63ZRji9MQNGOKgGzhxDNv9dqquGtrIvVLCPcNLiH7KTu35fZEBdG6+bS/AIxAN6XTRGNro9niFBV7q6UnhUqc4xH4scnbx/nWw6uhQOZ5A33Vvi6Vq4gi+zLKQaftg==", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + }, + { + "key-type": "aws-kms", + "algorithm-suite": "0378", + "ciphertext": "AYADeMAdg7+jZAERULcqp+xnRAEAdwADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREE5ZWNkOW5UZHFLUmZscnNJY3JTaVlieXBnb0RGMXdkN2JqSzd0MXpJVnh6dVQxdDNmaGJ5SnFXODkwTk9tejNCZz09AARrZXkxAAR2YWwxAARrZXkyAAR2YWwyAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjo2NTg5NTY2MDA4MzM6a2V5L2IzNTM3ZWYxLWQ4ZGMtNDc4MC05ZjVhLTU1Nzc2Y2JiMmY3ZgCnAQEBAHhA84wnXjEJdBbBBylRUFcZZK2j7xwh6UyLoL28nQ+0FAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDH+U479uM18mF2Mx7AIBEIA7SoK4/AoT/PA2SaymK2XGFt9eEctrE5XmyrLXENIjzpemn3FsE9riLM1oxKbgyOZp312gSJqkLiJCLAECAAAAAAwAABAAAAAAAAAAAAAAAAAAxoTELXNwGj6BlzC9fVztv/////8AAAABAAAAAAAAAAAAAAABAAAAQNXRsefM0ca+xPMS6b6J6OxcxXSEv5H2jySywt3KA4PCDvtptJvvW76ZttMuJRTnz5KdKSRaktlOVnZMPeZSLM2rEnbugIDdROgrAzc3XExiAGcwZQIxANIZ/wMFVTIvrRj/BVQoiy8lWTSqaV6VkLrnPa3ejlWv1pJ02jd1hay/B6pEBLrJGQIwP5B0HJdZN3xO1aQlhDdER/SicGtblLCbfxB1hX599gkDagC1p+Ga6uyvlGLWmYRf", + "plaintext": "OPMRI6TDNbycvrCdtev3am4ekTVLFcU1bPMsr/NwRWCMZs7ckLpRrwwXDkkFpJe/lzPnEhMNY967iKSgC0mK0g==" + } +] \ No newline at end of file diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index 02aaf4c25..b8ddc258b 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -66,7 +66,11 @@ passenv = # Pass through AWS credentials AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ # Pass through AWS profile name (useful for local testing) - AWS_PROFILE + AWS_PROFILE \ + # Used to manage test generators + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_GENERATE_TEST_VECTORS \ + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION \ + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID sitepackages = False deps = -rtest/requirements.txt @@ -216,9 +220,11 @@ commands = {[testenv:isort]commands} -c basepython = python3 deps = {[testenv:blacken]deps} + {[testenv:isort-seed]deps} {[testenv:isort]deps} commands = {[testenv:blacken]commands} + {[testenv:isort-seed]commands} {[testenv:isort]commands} [testenv:doc8] From c0d80a69bc8fe981958b0b5e9b3f59d292a45fee Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 00:30:16 -0700 Subject: [PATCH 18/37] add comments for build-requirements --- decrypt_oracle/.chalice/build-requirements.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/.chalice/build-requirements.py b/decrypt_oracle/.chalice/build-requirements.py index 2b0ca5bbf..a310953fa 100644 --- a/decrypt_oracle/.chalice/build-requirements.py +++ b/decrypt_oracle/.chalice/build-requirements.py @@ -1,5 +1,7 @@ """ - +Intended to be run after building the package locally, +this helper tool overwrites the "requirements.txt" file that Chalice uses. +This is necessary to work around how Chalice handles package source. """ import os @@ -7,10 +9,11 @@ def main(): - """""" + """Write the requirements file for Chalice to use.""" with open(os.path.join(BASE, 'requirements.txt'), 'wb') as requirements: requirements.write(b'# Requirements for Chalice packager.' + os.linesep.encode('utf-8')) requirements.write(b'# Autogenerated. Do not hand-edit.' + os.linesep.encode('utf-8')) + dist_dir = os.path.join(BASE, 'dist') for found_file in os.listdir(dist_dir): full_path = os.path.join(dist_dir, found_file) From 65055a1ebfda5de31b54c6c95859208e1706dc38 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 10:14:40 -0700 Subject: [PATCH 19/37] add local/integ/generate split testenvs --- decrypt_oracle/tox.ini | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index b8ddc258b..8b24ffcc6 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{27,34,35,36,37}, + py{27,34,35,36,37}-{local,integ}, bandit, doc8, readme, docs, {flake8,pylint}{,-tests}, # prone to false positives @@ -74,7 +74,10 @@ passenv = sitepackages = False deps = -rtest/requirements.txt -commands = {[testenv:base-command]commands} +commands = + local: {[testenv:base-command]commands} -m local + integ: {[testenv:base-command]commands} -m integ + generate: {[testenv:base-command]commands} -m generate # mypy [testenv:mypy-coverage] From 79d109354ad18f340834bd5a905e548e81131062 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 10:16:07 -0700 Subject: [PATCH 20/37] add missing docstrings for special master keys --- .../aws_encryption_sdk_decryption_oracle/__init__.py | 4 +--- .../key_providers/counting.py | 11 ++++++++--- .../key_providers/null.py | 9 +++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py index be019d00f..7d3341c90 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py @@ -1,4 +1,2 @@ -""" - -""" +"""Decrypt Oracle using the AWS Encryption SDK for Python.""" __version__ = "0.0.1" diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py index 70187a79c..e5f529fd9 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py @@ -14,18 +14,23 @@ class CountingMasterKeyConfig(MasterKeyConfig): # pylint: disable=too-few-public-methods - """""" + """Passthrough master key configuration to set the key id to "test_counting_prov_info".""" provider_id = "test_counting" def __init__(self): # type: () -> None - """""" + """Set the key id to "test_counting_prov_info".""" super(CountingMasterKeyConfig, self).__init__(key_id=b"test_counting_prov_info") class CountingMasterKey(MasterKey): - """""" + """Master key that generates deterministic data keys and decrypts a pre-defined + encrypted data key value to that deterministic data keys. + + Generated/decrypted data keys are of the form: ``\01\02\03\04...`` counting + bytes up from one to the data key length required for a given algorithm suite. + """ provider_id = "test_counting" _config_class = CountingMasterKeyConfig diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py index 2f959f9b0..d7ee4f6a9 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py @@ -13,18 +13,19 @@ class NullMasterKeyConfig(MasterKeyConfig): # pylint: disable=too-few-public-methods - """""" + """Passthrough master key configuration to set the key id to "null".""" provider_id = "null" def __init__(self): # type: () -> None - """""" + """Set the key id to "null".""" super(NullMasterKeyConfig, self).__init__(key_id=b"null") class NullMasterKey(MasterKey): - """""" + """Master key that generates null data keys and decrypts any data key with provider id + "null" or "zero" as a null data key.""" provider_id = "null" _allowed_provider_ids = (provider_id, "zero") @@ -56,7 +57,7 @@ def _null_plaintext_data_key(algorithm): return b"\x00" * algorithm.data_key_len def _generate_data_key(self, algorithm, encryption_context): - # type: (AlgorithmSuite, Dict[Text, Text]) -> NoReturn + # type: (AlgorithmSuite, Dict[Text, Text]) -> DataKey """NullMasterKey does not support generate_data_key :param algorithm: Algorithm on which to base data key From 9eec522481c17c3c69943d1551d670b0a9e7517a Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 13:41:57 -0700 Subject: [PATCH 21/37] add decrypto oracle tests to Travis CI --- .travis.yml | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cb53b7989..1661f607d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -203,7 +203,77 @@ matrix: # env: # TEST_VECTOR_HANDLERS=1 # TOXENV=pylint-tests + ################## + # Decrypt Oracle # + ################## + # CPython 2.7 + - python: 2.7 + env: + $DECRYPT_ORACLE=1 + TOXENV=py27-local + # CPython 3.4 + - python: 3.4 + env: + DECRYPT_ORACLE=1 + TOXENV=py34-local + # CPython 3.5 + - python: 3.5 + env: + DECRYPT_ORACLE=1 + TOXENV=py35-local + # CPython 3.6 + - python: 3.6 + env: + DECRYPT_ORACLE=1 + TOXENV=py36-local + # CPython 3.7 + - python: 3.7 + env: + DECRYPT_ORACLE=1 + TOXENV=py37-local + dist: xenial + sudo: true + # Linters + - python: 3.6 + env: + DECRYPT_ORACLE=1 + TOXENV=bandit + - python: 3.6 + env: + DECRYPT_ORACLE=1 + TOXENV=readme + # Pending buildout of docs + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=docs + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=doc8 + # Pending linting cleanup + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=flake8 + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=pylint + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=flake8-tests + #- python: 3.6 + # env: + # DECRYPT_ORACLE=1 + # TOXENV=pylint-tests install: pip install tox script: - - if [[ -n $TEST_VECTOR_HANDLERS ]]; then cd test_vector_handlers; fi + - | + if [[ -n $TEST_VECTOR_HANDLERS ]]; + then cd test_vector_handlers; + elseif [[ -n $DECRYPT_ORACLE ]]; + then cd decrypt_oracle; + fi - tox From 85f9710d17fb4e95439e98bd0f643e9154612923 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 13:46:44 -0700 Subject: [PATCH 22/37] fix issues with Travis config --- .travis.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1661f607d..cfa3c9b55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -209,7 +209,7 @@ matrix: # CPython 2.7 - python: 2.7 env: - $DECRYPT_ORACLE=1 + DECRYPT_ORACLE=1 TOXENV=py27-local # CPython 3.4 - python: 3.4 @@ -270,10 +270,5 @@ matrix: # TOXENV=pylint-tests install: pip install tox script: - - | - if [[ -n $TEST_VECTOR_HANDLERS ]]; - then cd test_vector_handlers; - elseif [[ -n $DECRYPT_ORACLE ]]; - then cd decrypt_oracle; - fi + - if [[ -n $TEST_VECTOR_HANDLERS ]]; then cd test_vector_handlers; else if [[ -n $DECRYPT_ORACLE ]]; then cd decrypt_oracle; fi - tox From 9178dc2e687d1f7c94bafeed01e04ca706059edc Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 13:52:06 -0700 Subject: [PATCH 23/37] fix bash loop fail --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cfa3c9b55..74f9a3f44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -270,5 +270,11 @@ matrix: # TOXENV=pylint-tests install: pip install tox script: - - if [[ -n $TEST_VECTOR_HANDLERS ]]; then cd test_vector_handlers; else if [[ -n $DECRYPT_ORACLE ]]; then cd decrypt_oracle; fi + - | + if [[ -n $TEST_VECTOR_HANDLERS ]]; + then cd test_vector_handlers; + else if [[ -n $DECRYPT_ORACLE ]]; + then cd decrypt_oracle; + fi; + fi - tox From e03c076d6c94087b6aa43bba9a7cfa3a91254baf Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 18:49:07 -0700 Subject: [PATCH 24/37] add minimal readme for decrypt oracle --- decrypt_oracle/README.rst | 45 +++++++++++++++++++ .../app.py | 4 +- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/README.rst b/decrypt_oracle/README.rst index e69de29bb..b419b857c 100644 --- a/decrypt_oracle/README.rst +++ b/decrypt_oracle/README.rst @@ -0,0 +1,45 @@ +#################################### +aws-encryption-sdk-decryption-oracle +#################################### + + +This project provides a REST API to be used as a decryption oracle to verify +that ciphertext can be successfully decrypted by the AWS Encryption SDK for Python. + +The API is deployed on Amazon API Gateway and backed by AWS Lambda. + +API v0 +====== + +**Request** + +* **Method**: POST +* **Body**: Raw ciphertext bytes +* **Headers**: + + * **Content-Type**: ``application/octet-stream`` + * **Accept**: ``application/octet-stream`` + +**Response** + +* 200 response code with the raw plaintext bytes as the body +* 400 response code with whatever error code was encountered as the body + +Development +=========== + +Building +******** + +The Lambda package must be built on an Amazon Linux platform as close as possible to the AWS +Lambda environment. + + +To build the Lambda package, run: ``tox -e chalice -- package {TARGET DIR}`` + +Deployment +********** + +This API is built using Chalice and can be deployed independently of any other infrastructure. + +To build and deploy the API, run: ``tox -e chalice -- deploy`` diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py index 323a3fe31..98527ccb6 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py @@ -41,8 +41,8 @@ def basic_decrypt(): * **Body**: Raw ciphertext bytes * **Headers**: - * **Content-Type**: ``application/octet-stream`` - * **Accept**: ``application/octet-stream`` + * **Content-Type**: ``application/octet-stream`` + * **Accept**: ``application/octet-stream`` **Response** From 6caffb945af30852dd65a89ee1dbbebdbbf6ae03 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 18:54:55 -0700 Subject: [PATCH 25/37] rename from decryption oracle to decrypt oracle --- decrypt_oracle/setup.py | 2 +- .../__init__.py | 0 .../app.py | 4 ++-- .../key_providers/__init__.py | 0 .../key_providers/counting.py | 5 ++++- .../key_providers/null.py | 0 decrypt_oracle/test/test_n_generate_test_vectors.py | 6 +++--- decrypt_oracle/test/unit/key_providers/test_u_counting.py | 4 ++-- decrypt_oracle/test/unit/key_providers/test_u_null.py | 4 ++-- 9 files changed, 14 insertions(+), 11 deletions(-) rename decrypt_oracle/src/{aws_encryption_sdk_decryption_oracle => aws_encryption_sdk_decrypt_oracle}/__init__.py (100%) rename decrypt_oracle/src/{aws_encryption_sdk_decryption_oracle => aws_encryption_sdk_decrypt_oracle}/app.py (94%) rename decrypt_oracle/src/{aws_encryption_sdk_decryption_oracle => aws_encryption_sdk_decrypt_oracle}/key_providers/__init__.py (100%) rename decrypt_oracle/src/{aws_encryption_sdk_decryption_oracle => aws_encryption_sdk_decrypt_oracle}/key_providers/counting.py (96%) rename decrypt_oracle/src/{aws_encryption_sdk_decryption_oracle => aws_encryption_sdk_decrypt_oracle}/key_providers/null.py (100%) diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py index 75ec02965..3452e65f4 100644 --- a/decrypt_oracle/setup.py +++ b/decrypt_oracle/setup.py @@ -26,7 +26,7 @@ def get_requirements(): setup( - name="aws-encryption-sdk-decryption-oracle", + name="aws-encryption-sdk-decrypt-oracle", packages=find_packages("src"), package_dir={"": "src"}, version=get_version(), diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py similarity index 100% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/__init__.py rename to decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py similarity index 94% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py rename to decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py index 98527ccb6..da4946c57 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -11,7 +11,7 @@ from .key_providers.null import NullMasterKey CHALICE_DEBUG = os.environ.get("CHALICE_DEBUG", "no") == "yes" -APP = Chalice(app_name="aws-encryption-sdk-decryption-oracle", debug=CHALICE_DEBUG) +APP = Chalice(app_name="aws-encryption-sdk-decrypt-oracle", debug=CHALICE_DEBUG) APP.log.setLevel(logging.DEBUG) try: # Python 3.5.0 and 3.5.1 have incompatible typing modules @@ -33,7 +33,7 @@ def _master_key_provider(): @APP.route("/v0/decrypt", methods=["POST"], content_types=["application/octet-stream"]) def basic_decrypt(): # type: () -> Response - """Basic decrypt handler for decryption oracle v0. + """Basic decrypt handler for decrypt oracle v0. **Request** diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__init__.py similarity index 100% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/__init__.py rename to decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__init__.py diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py similarity index 96% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py rename to decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py index e5f529fd9..145c499ca 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/counting.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py @@ -1,4 +1,7 @@ -"""Master key that provides speci.""" +""" +Master key that generates deterministic data keys and decrypts a pre-defined +encrypted data key value to that deterministic data keys. +""" from aws_encryption_sdk.exceptions import DecryptKeyError from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig diff --git a/decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py similarity index 100% rename from decrypt_oracle/src/aws_encryption_sdk_decryption_oracle/key_providers/null.py rename to decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py index 8c0556726..c39cf8241 100644 --- a/decrypt_oracle/test/test_n_generate_test_vectors.py +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -10,7 +10,7 @@ # 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. -"""Generate test vectors for use in testing the decryption oracle.""" +"""Generate test vectors for use in testing the decrypt oracle.""" import base64 import binascii import json @@ -21,8 +21,8 @@ from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.key_providers.kms import KMSMasterKey -from aws_encryption_sdk_decryption_oracle.key_providers.counting import CountingMasterKey -from aws_encryption_sdk_decryption_oracle.key_providers.null import NullMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from .integration.integration_test_utils import test_vectors_filename diff --git a/decrypt_oracle/test/unit/key_providers/test_u_counting.py b/decrypt_oracle/test/unit/key_providers/test_u_counting.py index 25b057181..df3943a3a 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_counting.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_counting.py @@ -10,11 +10,11 @@ # 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. -"""Unit test for ``aws_encryption_sdk_decryption_oracle.key_providers.counting``.""" +"""Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.counting``.""" import aws_encryption_sdk import pytest -from aws_encryption_sdk_decryption_oracle.key_providers.counting import CountingMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey from ...integration.integration_test_utils import filtered_test_vectors diff --git a/decrypt_oracle/test/unit/key_providers/test_u_null.py b/decrypt_oracle/test/unit/key_providers/test_u_null.py index 13b749c3c..f9a036be1 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_null.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_null.py @@ -10,11 +10,11 @@ # 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. -"""Unit test for ``aws_encryption_sdk_decryption_oracle.key_providers.null``.""" +"""Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.null``.""" import aws_encryption_sdk import pytest -from aws_encryption_sdk_decryption_oracle.key_providers.null import NullMasterKey +from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from ...integration.integration_test_utils import filtered_test_vectors From f3bb07608fca8e397651f4594abe9c3013dc277f Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Wed, 26 Sep 2018 19:20:19 -0700 Subject: [PATCH 26/37] missed some decryption->decrypt renames --- decrypt_oracle/setup.py | 2 +- decrypt_oracle/tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py index 3452e65f4..58219a1fa 100644 --- a/decrypt_oracle/setup.py +++ b/decrypt_oracle/setup.py @@ -15,7 +15,7 @@ def read(*args): def get_version(): """Read the version from this module.""" - init = read("src", "aws_encryption_sdk_decryption_oracle", "__init__.py") + init = read("src", "aws_encryption_sdk_decrypt_oracle", "__init__.py") return VERSION_RE.search(init).group(1) diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index 8b24ffcc6..94e2773ad 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -59,7 +59,7 @@ commands = chalice {posargs} [testenv:base-command] -commands = pytest --basetemp={envtmpdir} -l --cov aws_encryption_sdk_decryption_oracle test/ {posargs} +commands = pytest --basetemp={envtmpdir} -l --cov aws_encryption_sdk_decrypt_oracle test/ {posargs} [testenv] passenv = From 5d590c2986ae989dad586409c041858f9ac64998 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 27 Sep 2018 10:47:24 -0700 Subject: [PATCH 27/37] add license notices --- decrypt_oracle/app.py | 12 ++++++++++++ .../aws_encryption_sdk_decrypt_oracle/__init__.py | 12 ++++++++++++ .../src/aws_encryption_sdk_decrypt_oracle/app.py | 12 ++++++++++++ .../key_providers/__init__.py | 13 +++++++++++++ .../key_providers/counting.py | 12 ++++++++++++ .../key_providers/null.py | 12 ++++++++++++ 6 files changed, 73 insertions(+) diff --git a/decrypt_oracle/app.py b/decrypt_oracle/app.py index d6c278e57..e206930b6 100644 --- a/decrypt_oracle/app.py +++ b/decrypt_oracle/app.py @@ -1,2 +1,14 @@ +# 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. """Shim to pull decryption oracle app into expected location for Chalice.""" from aws_encryption_sdk_decryption_oracle.app import APP as app diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py index 7d3341c90..6a8068ecc 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py @@ -1,2 +1,14 @@ +# 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. """Decrypt Oracle using the AWS Encryption SDK for Python.""" __version__ = "0.0.1" diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py index da4946c57..45d490a24 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -1,3 +1,15 @@ +# 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. """Decrypt Oracle using the AWS Encryption SDK for Python.""" import json import logging diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__init__.py index e69de29bb..98889fd63 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__init__.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/__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. +"""Special key providers for use by the decrypt oracle.""" diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py index 145c499ca..172c44309 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py @@ -1,3 +1,15 @@ +# 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. """ Master key that generates deterministic data keys and decrypts a pre-defined encrypted data key value to that deterministic data keys. diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py index d7ee4f6a9..32585c8bb 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py @@ -1,3 +1,15 @@ +# 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. """Master key that provides null data keys.""" from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig From 7d47a321e39297542c7027b9a751f672c780dd25 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 27 Sep 2018 10:47:43 -0700 Subject: [PATCH 28/37] add path for v0 API to readme --- decrypt_oracle/README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/decrypt_oracle/README.rst b/decrypt_oracle/README.rst index b419b857c..53dd871fa 100644 --- a/decrypt_oracle/README.rst +++ b/decrypt_oracle/README.rst @@ -11,6 +11,10 @@ The API is deployed on Amazon API Gateway and backed by AWS Lambda. API v0 ====== +**Path** + +``/v0/decrypt`` + **Request** * **Method**: POST From e30e19ccb416e52dd142a8a5c874f799ce0dfcfa Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 27 Sep 2018 10:49:56 -0700 Subject: [PATCH 29/37] more missed name conversions --- decrypt_oracle/tox.ini | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index 94e2773ad..7020f8f65 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -105,7 +105,7 @@ deps = {[testenv:mypy-common]deps} commands = python -m mypy \ --linecoverage-report build \ - src/aws_encryption_sdk_decryption_oracle/ \ + src/aws_encryption_sdk_decrypt_oracle/ \ {posargs} {[testenv:mypy-coverage]commands} @@ -116,7 +116,7 @@ commands = python -m mypy \ --py2 \ --linecoverage-report build \ - src/aws_encryption_sdk_decryption_oracle/ \ + src/aws_encryption_sdk_decrypt_oracle/ \ {posargs} {[testenv:mypy-coverage]commands} @@ -130,7 +130,7 @@ deps = flake8-print>=3.1.0 commands = flake8 \ - src/aws_encryption_sdk_decryption_oracle/ \ + src/aws_encryption_sdk_decrypt_oracle/ \ setup.py \ #doc/conf.py \ {posargs} @@ -157,7 +157,7 @@ deps = commands = pylint \ --rcfile=src/pylintrc \ - src/aws_encryption_sdk_decryption_oracle/ \ + src/aws_encryption_sdk_decrypt_oracle/ \ setup.py \ {posargs} @@ -178,7 +178,7 @@ deps = black commands = black --line-length 120 \ - src/aws_encryption_sdk_decryption_oracle/ \ + src/aws_encryption_sdk_decrypt_oracle/ \ setup.py \ #doc/conf.py \ test/ \ @@ -247,13 +247,13 @@ basepython = python3 deps = # Pull bandit from github because they haven't published 1.4.1 to pypi yet git+git://github.com/PyCQA/bandit.git@master -commands = bandit -r src/aws_encryption_sdk_decryption_oracle/ +commands = bandit -r src/aws_encryption_sdk_decrypt_oracle/ # Prone to false positives: only run independently [testenv:vulture] basepython = python3 deps = vulture -commands = vulture src/aws_encryption_sdk_decryption_oracle/ +commands = vulture src/aws_encryption_sdk_decrypt_oracle/ [testenv:linters] basepython = python3 From f1011a601f0cce4fd322dde6d38d92fc9caad5e7 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Thu, 27 Sep 2018 11:10:12 -0700 Subject: [PATCH 30/37] we only need to care about Python 3.6 for the decrypt oracle * remove import safeguards to allow 3.5.0 and 3.5.1 to not fail * convert from comment-style typehints to inline --- .travis.yml | 23 +------------ decrypt_oracle/setup.cfg | 2 +- .../aws_encryption_sdk_decrypt_oracle/app.py | 12 ++----- .../key_providers/counting.py | 28 +++++++-------- .../key_providers/null.py | 34 ++++++++----------- .../integration/integration_test_utils.py | 13 +++---- .../test/test_n_generate_test_vectors.py | 14 ++------ .../unit/key_providers/test_u_counting.py | 1 - .../test/unit/key_providers/test_u_null.py | 1 - 9 files changed, 40 insertions(+), 88 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74f9a3f44..4492fe59d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -206,33 +206,12 @@ matrix: ################## # Decrypt Oracle # ################## - # CPython 2.7 - - python: 2.7 - env: - DECRYPT_ORACLE=1 - TOXENV=py27-local - # CPython 3.4 - - python: 3.4 - env: - DECRYPT_ORACLE=1 - TOXENV=py34-local - # CPython 3.5 - - python: 3.5 - env: - DECRYPT_ORACLE=1 - TOXENV=py35-local # CPython 3.6 + # Because this build as Python 3.6 Lambda, this is the only runtime we are targetting. - python: 3.6 env: DECRYPT_ORACLE=1 TOXENV=py36-local - # CPython 3.7 - - python: 3.7 - env: - DECRYPT_ORACLE=1 - TOXENV=py37-local - dist: xenial - sudo: true # Linters - python: 3.6 env: diff --git a/decrypt_oracle/setup.cfg b/decrypt_oracle/setup.cfg index 118ff330d..fa524ca8b 100644 --- a/decrypt_oracle/setup.cfg +++ b/decrypt_oracle/setup.cfg @@ -39,4 +39,4 @@ force_grid_wrap = 0 combine_as_imports = True not_skip = __init__.py known_first_party = aws_encryption_sdk_decryption_oracle -known_third_party =aws_encryption_sdk,aws_encryption_sdk_decryption_oracle,chalice,pytest,requests,setuptools +known_third_party =aws_encryption_sdk,aws_encryption_sdk_decrypt_oracle,aws_encryption_sdk_decryption_oracle,chalice,pytest,requests,setuptools diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py index 45d490a24..85274ef7c 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -26,15 +26,8 @@ APP = Chalice(app_name="aws-encryption-sdk-decrypt-oracle", debug=CHALICE_DEBUG) APP.log.setLevel(logging.DEBUG) -try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Dict, Text, NoReturn, Union # noqa pylint: disable=unused-import -except ImportError: # pragma: no cover - # We only actually need these imports when running the mypy checks - pass - -def _master_key_provider(): - # type: () -> KMSMasterKeyProvider +def _master_key_provider() -> KMSMasterKeyProvider: """Build the V0 master key provider.""" master_key_provider = KMSMasterKeyProvider() master_key_provider.add_master_key_provider(NullMasterKey()) @@ -43,8 +36,7 @@ def _master_key_provider(): @APP.route("/v0/decrypt", methods=["POST"], content_types=["application/octet-stream"]) -def basic_decrypt(): - # type: () -> Response +def basic_decrypt() -> Response: """Basic decrypt handler for decrypt oracle v0. **Request** diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py index 172c44309..311924b89 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py @@ -14,18 +14,14 @@ Master key that generates deterministic data keys and decrypts a pre-defined encrypted data key value to that deterministic data keys. """ +from typing import Dict, NoReturn, Text + from aws_encryption_sdk.exceptions import DecryptKeyError -from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import +from aws_encryption_sdk.identifiers import AlgorithmSuite from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig -from aws_encryption_sdk.structures import EncryptedDataKey # noqa pylint: disable=unused-import +from aws_encryption_sdk.structures import EncryptedDataKey from aws_encryption_sdk.structures import DataKey -try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Dict, Text, NoReturn # noqa pylint: disable=unused-import -except ImportError: # pragma: no cover - # We only actually need these imports when running the mypy checks - pass - class CountingMasterKeyConfig(MasterKeyConfig): # pylint: disable=too-few-public-methods @@ -33,8 +29,7 @@ class CountingMasterKeyConfig(MasterKeyConfig): provider_id = "test_counting" - def __init__(self): - # type: () -> None + def __init__(self) -> None: """Set the key id to "test_counting_prov_info".""" super(CountingMasterKeyConfig, self).__init__(key_id=b"test_counting_prov_info") @@ -51,8 +46,7 @@ class CountingMasterKey(MasterKey): _config_class = CountingMasterKeyConfig _encrypted_data_key = b"\x40\x41\x42\x43\x44" - def _generate_data_key(self, algorithm, encryption_context): - # type: (AlgorithmSuite, Dict[Text, Text]) -> DataKey + def _generate_data_key(self, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text]) -> DataKey: """Perform the provider-specific data key generation task. :param algorithm: Algorithm on which to base data key @@ -64,8 +58,9 @@ def _generate_data_key(self, algorithm, encryption_context): data_key = b"".join([chr(i).encode("utf-8") for i in range(1, algorithm.data_key_len + 1)]) return DataKey(key_provider=self.key_provider, data_key=data_key, encrypted_data_key=self._encrypted_data_key) - def _encrypt_data_key(self, data_key, algorithm, encryption_context): - # type: (DataKey, AlgorithmSuite, Dict[Text, Text]) -> NoReturn + def _encrypt_data_key( + self, data_key: DataKey, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text] + ) -> NoReturn: """Encrypt a data key and return the ciphertext. :param data_key: Unencrypted data key @@ -78,8 +73,9 @@ def _encrypt_data_key(self, data_key, algorithm, encryption_context): """ raise NotImplementedError("CountingMasterKey does not support encrypt_data_key") - def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): - # type: (EncryptedDataKey, AlgorithmSuite, Dict[Text, Text]) -> DataKey + def _decrypt_data_key( + self, encrypted_data_key: EncryptedDataKey, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text] + ) -> DataKey: """Decrypt an encrypted data key and return the plaintext. :param data_key: Encrypted data key diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py index 32585c8bb..24a3b4470 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py @@ -11,17 +11,13 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Master key that provides null data keys.""" -from aws_encryption_sdk.identifiers import AlgorithmSuite # noqa pylint: disable=unused-import +from typing import Dict, NoReturn, Text + +from aws_encryption_sdk.identifiers import AlgorithmSuite from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig -from aws_encryption_sdk.structures import EncryptedDataKey # noqa pylint: disable=unused-import +from aws_encryption_sdk.structures import EncryptedDataKey from aws_encryption_sdk.structures import DataKey -try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Dict, Text, NoReturn # noqa pylint: disable=unused-import -except ImportError: # pragma: no cover - # We only actually need these imports when running the mypy checks - pass - class NullMasterKeyConfig(MasterKeyConfig): # pylint: disable=too-few-public-methods @@ -29,8 +25,7 @@ class NullMasterKeyConfig(MasterKeyConfig): provider_id = "null" - def __init__(self): - # type: () -> None + def __init__(self) -> None: """Set the key id to "null".""" super(NullMasterKeyConfig, self).__init__(key_id=b"null") @@ -43,8 +38,7 @@ class NullMasterKey(MasterKey): _allowed_provider_ids = (provider_id, "zero") _config_class = NullMasterKeyConfig - def owns_data_key(self, data_key): - # type: (DataKey) -> bool + def owns_data_key(self, data_key: DataKey) -> bool: """Determine whether the data key is owned by a ``null`` or ``zero`` provider. :param data_key: Data key to evaluate @@ -57,8 +51,7 @@ def owns_data_key(self, data_key): return data_key.key_provider.provider_id in self._allowed_provider_ids @staticmethod - def _null_plaintext_data_key(algorithm): - # type: (AlgorithmSuite) -> bytes + def _null_plaintext_data_key(algorithm: AlgorithmSuite) -> bytes: """Build the null data key of the correct length for the requested algorithm suite. :param algorithm: Algorithm on which to base data key @@ -68,8 +61,7 @@ def _null_plaintext_data_key(algorithm): """ return b"\x00" * algorithm.data_key_len - def _generate_data_key(self, algorithm, encryption_context): - # type: (AlgorithmSuite, Dict[Text, Text]) -> DataKey + def _generate_data_key(self, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text]) -> DataKey: """NullMasterKey does not support generate_data_key :param algorithm: Algorithm on which to base data key @@ -81,8 +73,9 @@ def _generate_data_key(self, algorithm, encryption_context): key_provider=self.key_provider, data_key=self._null_plaintext_data_key(algorithm), encrypted_data_key=b"" ) - def _encrypt_data_key(self, data_key, algorithm, encryption_context): - # type: (DataKey, AlgorithmSuite, Dict[Text, Text]) -> NoReturn + def _encrypt_data_key( + self, data_key: DataKey, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text] + ) -> NoReturn: """NullMasterKey does not support encrypt_data_key :param data_key: Unencrypted data key @@ -95,8 +88,9 @@ def _encrypt_data_key(self, data_key, algorithm, encryption_context): """ raise NotImplementedError("NullMasterKey does not support encrypt_data_key") - def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): - # type: (EncryptedDataKey, AlgorithmSuite, Dict[Text, Text]) -> DataKey + def _decrypt_data_key( + self, encrypted_data_key: EncryptedDataKey, algorithm: AlgorithmSuite, encryption_context: Dict[Text, Text] + ) -> DataKey: """Decrypt an encrypted data key and return the plaintext. :param data_key: Encrypted data key diff --git a/decrypt_oracle/test/integration/integration_test_utils.py b/decrypt_oracle/test/integration/integration_test_utils.py index 0d8fb5cae..6f37bd6d2 100644 --- a/decrypt_oracle/test/integration/integration_test_utils.py +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -15,6 +15,7 @@ import json import os from collections import namedtuple +from typing import Any, Callable, Iterable, Optional, Text import pytest from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider @@ -27,7 +28,7 @@ _ENDPOINT = None -def decrypt_endpoint(): +def decrypt_endpoint() -> Text: """Build the API endpoint based on environment variables.""" global _ENDPOINT # pylint: disable=global-statement @@ -51,7 +52,7 @@ def decrypt_endpoint(): return _ENDPOINT -def get_cmk_arn(): +def get_cmk_arn() -> Text: """Retrieve the target CMK ARN from environment variable.""" try: arn = os.environ[AWS_KMS_KEY_ID] @@ -68,7 +69,7 @@ def get_cmk_arn(): raise ValueError("KMS CMK ARN provided for integration tests must be a key not an alias") -def kms_master_key_provider(cache=True): +def kms_master_key_provider(cache: Optional[bool] = True): """Build the expected KMS Master Key Provider based on environment variables.""" global _KMS_MKP # pylint: disable=global-statement @@ -85,7 +86,7 @@ def kms_master_key_provider(cache=True): return _kms_master_key_provider -def test_vectors_filename(): +def test_vectors_filename() -> Text: """Provide the absolute path to the test vectors file.""" return os.path.join(HERE, "..", "vectors", "decrypt_vectors.json") @@ -93,7 +94,7 @@ def test_vectors_filename(): TestVector = namedtuple("TestVector", ["plaintext", "ciphertext", "key_type", "algorithm_suite"]) -def all_test_vectors(): +def all_test_vectors() -> Iterable[Any]: """Collect and iterate through all test vectors.""" with open(test_vectors_filename(), "r") as vectors_file: @@ -114,7 +115,7 @@ def all_test_vectors(): ) -def filtered_test_vectors(filter_function): +def filtered_test_vectors(filter_function: Callable) -> Iterable[Any]: """Collect and iterate through all test vectors that pass the filter function.""" for vector_param in all_test_vectors(): if filter_function(vector_param.values[0]): diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py index c39cf8241..deb3f7c4d 100644 --- a/decrypt_oracle/test/test_n_generate_test_vectors.py +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -15,31 +15,24 @@ import binascii import json import os +from typing import Dict, Iterable, Text import aws_encryption_sdk import pytest from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.key_providers.kms import KMSMasterKey - from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from .integration.integration_test_utils import test_vectors_filename -try: # Python 3.5.0 and 3.5.1 have incompatible typing modules - from typing import Dict, Text, Iterable # noqa pylint: disable=unused-import -except ImportError: # pragma: no cover - # We only actually need these imports when running the mypy checks - pass - HERE = os.path.abspath(os.path.dirname(__file__)) GENERATE_VECTORS = "AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_GENERATE_TEST_VECTORS" PUBLIC_CMK = "arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt" ENCRYPTION_CONTEXT = {"key1": "val1", "key2": "val2"} -def _key_providers(): - # type: () -> Iterable[MasterKeyProvider] +def _key_providers() -> Iterable[MasterKeyProvider]: """Generate all master key providers for test vector generation. Each will be used independently. """ @@ -48,8 +41,7 @@ def _key_providers(): yield KMSMasterKey(key_id=PUBLIC_CMK) -def _generate_vectors(key_provider, plaintext): - # type: (MasterKeyProvider, bytes) -> Iterable[Dict[Text, Text]] +def _generate_vectors(key_provider: MasterKeyProvider, plaintext: bytes) -> Iterable[Dict[Text, Text]]: """Generate all desired test vectors for a given key provider and plaintext.""" for algorithm_suite in aws_encryption_sdk.Algorithm: ciphertext, _header = aws_encryption_sdk.encrypt( diff --git a/decrypt_oracle/test/unit/key_providers/test_u_counting.py b/decrypt_oracle/test/unit/key_providers/test_u_counting.py index df3943a3a..f779b5169 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_counting.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_counting.py @@ -13,7 +13,6 @@ """Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.counting``.""" import aws_encryption_sdk import pytest - from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey from ...integration.integration_test_utils import filtered_test_vectors diff --git a/decrypt_oracle/test/unit/key_providers/test_u_null.py b/decrypt_oracle/test/unit/key_providers/test_u_null.py index f9a036be1..918ef0e5a 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_null.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_null.py @@ -13,7 +13,6 @@ """Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.null``.""" import aws_encryption_sdk import pytest - from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey from ...integration.integration_test_utils import filtered_test_vectors From 81e48edd8555cb8a0777a463e907ab04c9110742 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 16:26:45 -0700 Subject: [PATCH 31/37] remove non-3.6 Python version classifiers in setup.py --- decrypt_oracle/setup.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py index 58219a1fa..e5d9ec84f 100644 --- a/decrypt_oracle/setup.py +++ b/decrypt_oracle/setup.py @@ -45,13 +45,7 @@ def get_requirements(): "Natural Language :: English", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Security", "Topic :: Security :: Cryptography", From 528f01d6e789ca80625399ab42f9d943fb379e17 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 16:27:12 -0700 Subject: [PATCH 32/37] add warnings to not use null and counting master keys outside of testing --- .../key_providers/counting.py | 8 ++++++-- .../key_providers/null.py | 11 ++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py index 311924b89..365b71002 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py @@ -19,8 +19,7 @@ from aws_encryption_sdk.exceptions import DecryptKeyError from aws_encryption_sdk.identifiers import AlgorithmSuite from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig -from aws_encryption_sdk.structures import EncryptedDataKey -from aws_encryption_sdk.structures import DataKey +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey class CountingMasterKeyConfig(MasterKeyConfig): @@ -40,6 +39,11 @@ class CountingMasterKey(MasterKey): Generated/decrypted data keys are of the form: ``\01\02\03\04...`` counting bytes up from one to the data key length required for a given algorithm suite. + + .. warning:: + + This master key is NOT secure and should never be used for anything other than testing. + """ provider_id = "test_counting" diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py index 24a3b4470..37a8a208f 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py @@ -15,8 +15,7 @@ from aws_encryption_sdk.identifiers import AlgorithmSuite from aws_encryption_sdk.key_providers.base import MasterKey, MasterKeyConfig -from aws_encryption_sdk.structures import EncryptedDataKey -from aws_encryption_sdk.structures import DataKey +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey class NullMasterKeyConfig(MasterKeyConfig): @@ -32,7 +31,13 @@ def __init__(self) -> None: class NullMasterKey(MasterKey): """Master key that generates null data keys and decrypts any data key with provider id - "null" or "zero" as a null data key.""" + "null" or "zero" as a null data key. + + .. warning:: + + This master key is NOT secure and should never be used for anything other than testing. + + """ provider_id = "null" _allowed_provider_ids = (provider_id, "zero") From d7e2a7d7b8a7c72a61722740f6a06d09733f2af6 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 16:39:23 -0700 Subject: [PATCH 33/37] fix final missing decryption->decrypt rename --- decrypt_oracle/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decrypt_oracle/app.py b/decrypt_oracle/app.py index e206930b6..d79704041 100644 --- a/decrypt_oracle/app.py +++ b/decrypt_oracle/app.py @@ -11,4 +11,4 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Shim to pull decryption oracle app into expected location for Chalice.""" -from aws_encryption_sdk_decryption_oracle.app import APP as app +from aws_encryption_sdk_decrypt_oracle.app import APP as app From d58596bfcf992c166e6a535a42b3ea7efe337b77 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 16:40:30 -0700 Subject: [PATCH 34/37] split decrypt oracle Deny statement to deny invalid action OR invalid resource --- decrypt_oracle/.chalice/policy-dev.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json index 3bc879543..7eb2a1379 100644 --- a/decrypt_oracle/.chalice/policy-dev.json +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -34,7 +34,10 @@ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" - ], + ] + }, + { + "Effect": "Deny", "NotResource": [ "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", From e37232be9d4a0cc4e978059e35b642ccd6bf90d9 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 16:43:05 -0700 Subject: [PATCH 35/37] IAM policies require both action and resource --- decrypt_oracle/.chalice/policy-dev.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json index 7eb2a1379..eaac5470a 100644 --- a/decrypt_oracle/.chalice/policy-dev.json +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -34,10 +34,13 @@ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" - ] + ], + "Resource": "*" + }, { "Effect": "Deny", + "Action": "*", "NotResource": [ "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", From 84b376228ef4d15683627e31ff81102f2e2aa4dd Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 17:09:16 -0700 Subject: [PATCH 36/37] Revert "IAM policies require both action and resource" This reverts commit e37232be9d4a0cc4e978059e35b642ccd6bf90d9. --- decrypt_oracle/.chalice/policy-dev.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json index eaac5470a..7eb2a1379 100644 --- a/decrypt_oracle/.chalice/policy-dev.json +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -34,13 +34,10 @@ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" - ], - "Resource": "*" - + ] }, { "Effect": "Deny", - "Action": "*", "NotResource": [ "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", From 655ee698aa417835d1484c85d16a162ea0f8ed10 Mon Sep 17 00:00:00 2001 From: mattsb42-aws Date: Mon, 1 Oct 2018 17:09:23 -0700 Subject: [PATCH 37/37] Revert "split decrypt oracle Deny statement to deny invalid action OR invalid resource" This reverts commit d58596bfcf992c166e6a535a42b3ea7efe337b77. --- decrypt_oracle/.chalice/policy-dev.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json index 7eb2a1379..3bc879543 100644 --- a/decrypt_oracle/.chalice/policy-dev.json +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -34,10 +34,7 @@ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" - ] - }, - { - "Effect": "Deny", + ], "NotResource": [ "arn:aws:kms:us-west-2:658956600833:key/590fd781-ddde-4036-abec-3e1ab5a5d2ad", "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f",