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/.travis.yml b/.travis.yml index cb53b7989..4492fe59d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -203,7 +203,57 @@ matrix: # env: # TEST_VECTOR_HANDLERS=1 # TOXENV=pylint-tests + ################## + # Decrypt Oracle # + ################## + # 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 + # 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; + else if [[ -n $DECRYPT_ORACLE ]]; + then cd decrypt_oracle; + fi; + fi - tox diff --git a/decrypt_oracle/.chalice/build-requirements.py b/decrypt_oracle/.chalice/build-requirements.py new file mode 100644 index 000000000..a310953fa --- /dev/null +++ b/decrypt_oracle/.chalice/build-requirements.py @@ -0,0 +1,25 @@ +""" +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 + +BASE = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + + +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) + 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..71f70db7c --- /dev/null +++ b/decrypt_oracle/.chalice/config.json @@ -0,0 +1,13 @@ +{ + "version": "2.0", + "app_name": "aws-encryption-sdk-decryption-oracle", + "autogen_policy": false, + "stages": { + "dev": { + "api_gateway_stage": "api", + "environment_variables": { + "CHALICE_DEBUG": "yes" + } + } + } +} diff --git a/decrypt_oracle/.chalice/policy-dev.json b/decrypt_oracle/.chalice/policy-dev.json new file mode 100644 index 000000000..3bc879543 --- /dev/null +++ b/decrypt_oracle/.chalice/policy-dev.json @@ -0,0 +1,46 @@ +{ + "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": "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", + "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:logs:*:*:log-group", + "arn:aws:logs:*:*:log-group:*" + ] + } + ] +} \ No newline at end of file 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..53dd871fa --- /dev/null +++ b/decrypt_oracle/README.rst @@ -0,0 +1,49 @@ +#################################### +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 +====== + +**Path** + +``/v0/decrypt`` + +**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/app.py b/decrypt_oracle/app.py new file mode 100644 index 000000000..d79704041 --- /dev/null +++ b/decrypt_oracle/app.py @@ -0,0 +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_decrypt_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..fa524ca8b --- /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_decryption_oracle +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,aws_encryption_sdk_decrypt_oracle,aws_encryption_sdk_decryption_oracle,chalice,pytest,requests,setuptools diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py new file mode 100644 index 000000000..e5d9ec84f --- /dev/null +++ b/decrypt_oracle/setup.py @@ -0,0 +1,53 @@ +"""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_decrypt_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-decrypt-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 :: 3.6", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Security", + "Topic :: Security :: Cryptography", + ], +) diff --git a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py new file mode 100644 index 000000000..6a8068ecc --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py @@ -0,0 +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 new file mode 100644 index 000000000..85274ef7c --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -0,0 +1,72 @@ +# 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 +import os + +import aws_encryption_sdk +from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProvider +from chalice import Chalice, Response + +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-decrypt-oracle", debug=CHALICE_DEBUG) +APP.log.setLevel(logging.DEBUG) + + +def _master_key_provider() -> 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"], content_types=["application/octet-stream"]) +def basic_decrypt() -> Response: + """Basic decrypt handler for decrypt oracle 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 + """ + APP.log.debug("Request:") + APP.log.debug(json.dumps(APP.current_request.to_dict())) + 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) + + APP.log.debug("Response:") + APP.log.debug(json.dumps(response.to_dict(binary_types=["application/octet-stream"]))) + return response 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 new file mode 100644 index 000000000..98889fd63 --- /dev/null +++ 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 new file mode 100644 index 000000000..365b71002 --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/counting.py @@ -0,0 +1,99 @@ +# 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. +""" +from typing import Dict, NoReturn, Text + +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 DataKey, EncryptedDataKey + + +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) -> 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. + + .. warning:: + + This master key is NOT secure and should never be used for anything other than testing. + + """ + + provider_id = "test_counting" + _config_class = CountingMasterKeyConfig + _encrypted_data_key = b"\x40\x41\x42\x43\x44" + + 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 + :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: DataKey, algorithm: AlgorithmSuite, encryption_context: 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: EncryptedDataKey, algorithm: AlgorithmSuite, encryption_context: 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_decrypt_oracle/key_providers/null.py b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py new file mode 100644 index 000000000..37a8a208f --- /dev/null +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/key_providers/null.py @@ -0,0 +1,113 @@ +# 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 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 DataKey, EncryptedDataKey + + +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) -> 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. + + .. 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") + _config_class = NullMasterKeyConfig + + 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 + :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 + + @staticmethod + 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 + :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: AlgorithmSuite, encryption_context: Dict[Text, Text]) -> DataKey: + """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 + """ + 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: DataKey, algorithm: AlgorithmSuite, encryption_context: 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: EncryptedDataKey, algorithm: AlgorithmSuite, encryption_context: 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 + """ + return DataKey( + 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/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/__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..6f37bd6d2 --- /dev/null +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -0,0 +1,122 @@ +# 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 +from typing import Any, Callable, Iterable, Optional, Text + +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() -> Text: + """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() -> Text: + """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: Optional[bool] = 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() -> Text: + """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() -> Iterable[Any]: + """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: 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]): + 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 new file mode 100644 index 000000000..11be59be6 --- /dev/null +++ b/decrypt_oracle/test/requirements.txt @@ -0,0 +1,5 @@ +mock +pytest>=3.3.1 +pytest-cov +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..deb3f7c4d --- /dev/null +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -0,0 +1,71 @@ +# 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 decrypt oracle.""" +import base64 +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 + +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() -> 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: 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( + 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..f779b5169 --- /dev/null +++ b/decrypt_oracle/test/unit/key_providers/test_u_counting.py @@ -0,0 +1,40 @@ +# 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_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 + +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..918ef0e5a --- /dev/null +++ b/decrypt_oracle/test/unit/key_providers/test_u_null.py @@ -0,0 +1,40 @@ +# 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_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 + +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 new file mode 100644 index 000000000..7020f8f65 --- /dev/null +++ b/decrypt_oracle/tox.ini @@ -0,0 +1,335 @@ +[tox] +envlist = + py{27,34,35,36,37}-{local,integ}, + 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] +basepython = python3.6 +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] +basepython = python3.6 +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_decrypt_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 \ + # 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 +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] +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_decrypt_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_decrypt_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_decrypt_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_decrypt_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_decrypt_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-seed]deps} + {[testenv:isort]deps} +commands = + {[testenv:blacken]commands} + {[testenv:isort-seed]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_decrypt_oracle/ + +# Prone to false positives: only run independently +[testenv:vulture] +basepython = python3 +deps = vulture +commands = vulture src/aws_encryption_sdk_decrypt_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/*