diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..8957349aa --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +# .coveragerc file when running coverage WITHOUT coverage for the MPL +# This prevents the ESDK without the MPL from considering the MPL-specific modules as "missed" coverage +[run] +omit = */aws_encryption_sdk/materials_managers/mpl/* + +[report] +omit = */aws_encryption_sdk/materials_managers/mpl/* \ No newline at end of file diff --git a/.coveragercmpl b/.coveragercmpl new file mode 100644 index 000000000..31a7b4407 --- /dev/null +++ b/.coveragercmpl @@ -0,0 +1 @@ +# .coveragerc file when running coverage WITH coverage for the MPL diff --git a/.github/workflows/ci_codebuild-tests.yml b/.github/workflows/ci_codebuild-tests.yml new file mode 100644 index 000000000..79a5841e0 --- /dev/null +++ b/.github/workflows/ci_codebuild-tests.yml @@ -0,0 +1,48 @@ +name: AWS CodeBuild CI + +on: + pull_request: + push: + # Run once a day + schedule: + - cron: "0 0 * * *" + +permissions: + id-token: write + contents: read + +jobs: + codebuild-tests: + name: AWS CodeBuild CI + runs-on: ubuntu-latest + strategy: + matrix: + python: + - python_version: "38" + image: "aws/codebuild/standard:5.0" + - python_version: "39" + image: "aws/codebuild/standard:5.0" + - python_version: "310" + image: "aws/codebuild/standard:6.0" + - python_version: "311" + image: "aws/codebuild/standard:7.0" + - python_version: "312" + image: "aws/codebuild/standard:7.0" + codebuild_file_name: + - "awses_local.yml" + - "examples.yml" + - "integ.yml" + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} + aws-region: us-west-2 + role-duration-seconds: 4200 + - name: Run python-${{ matrix.python.python_version }} ${{ matrix.codebuild_file_name }} + uses: aws-actions/aws-codebuild-run-build@v1 + timeout-minutes: 70 + with: + project-name: python-esdk + buildspec-override: codebuild/py${{ matrix.python.python_version }}/${{ matrix.codebuild_file_name }} + image-override: ${{ matrix.python.image }} diff --git a/.github/workflows/ci_decrypt-oracle.yaml b/.github/workflows/ci_decrypt-oracle.yaml index 2819ad646..baf01c571 100644 --- a/.github/workflows/ci_decrypt-oracle.yaml +++ b/.github/workflows/ci_decrypt-oracle.yaml @@ -11,11 +11,11 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: - # The oracle runs in a Python 3.6 Lamba - python-version: 3.6 + # The oracle will run on a Python 3.9 Lamba + python-version: 3.9 - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt @@ -38,10 +38,10 @@ jobs: - flake8-tests - pylint-tests steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt diff --git a/.github/workflows/ci_static-analysis.yaml b/.github/workflows/ci_static-analysis.yaml index 3cdadf215..7f74e8fc3 100644 --- a/.github/workflows/ci_static-analysis.yaml +++ b/.github/workflows/ci_static-analysis.yaml @@ -27,8 +27,8 @@ jobs: - black-check - isort-check steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: 3.8 - run: | diff --git a/.github/workflows/ci_test-vector-handler.yaml b/.github/workflows/ci_test-vector-handler.yaml index f5b42cf86..90c87e502 100644 --- a/.github/workflows/ci_test-vector-handler.yaml +++ b/.github/workflows/ci_test-vector-handler.yaml @@ -19,11 +19,8 @@ jobs: os: - ubuntu-latest - windows-latest - - macos-latest + - macos-13 python: - - 2.7 - - 3.6 - - 3.7 - 3.8 - 3.x architecture: @@ -37,16 +34,16 @@ jobs: # x86 builds are only meaningful for Windows - os: ubuntu-latest architecture: x86 - - os: macos-latest + - os: macos-13 architecture: x86 steps: - - uses: aws-actions/configure-aws-credentials@v1 + - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.INTEG_AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.INTEG_AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.architecture }} @@ -72,10 +69,10 @@ jobs: - flake8-tests - pylint-tests steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt diff --git a/.github/workflows/ci_tests.yaml b/.github/workflows/ci_tests.yaml index f269246bc..256e49ce4 100644 --- a/.github/workflows/ci_tests.yaml +++ b/.github/workflows/ci_tests.yaml @@ -26,12 +26,13 @@ jobs: os: - ubuntu-latest - windows-latest - - macos-latest + - macos-13 python: - - 3.6 - - 3.7 - 3.8 - 3.9 + - "3.10" + - "3.11" + - "3.12" - 3.x architecture: - x64 @@ -39,19 +40,42 @@ jobs: category: - local - accept + - mpllocal # These require credentials. # Enable them once we sort how to provide them. # - integ # - examples + # Append '-mpl' to some test environments. + # This suffix signals to tox to install the MPL in the test environment. + optional_mpl_dependency: + - "" + - -mpl exclude: # x86 builds are only meaningful for Windows - os: ubuntu-latest architecture: x86 - - os: macos-latest + - os: macos-13 architecture: x86 + # MPL is not supported on <3.11 + - python: 3.7 + optional_mpl_dependency: -mpl + - python: 3.8 + optional_mpl_dependency: -mpl + - python: 3.9 + optional_mpl_dependency: -mpl + - python: 3.10 + optional_mpl_dependency: -mpl + # mpllocal requires the MPL to be installed + - category: mpllocal + optional_mpl_dependency: "" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + # Support long Dafny filenames (used in MPL and DBESDK repos) + - name: Support longpaths + run: | + git config --global core.longpaths true + + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.architecture }} @@ -60,25 +84,25 @@ jobs: pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }} + TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} run: tox -- -vv - upstream-py3: + upstream-py311: runs-on: ubuntu-latest strategy: fail-fast: true matrix: category: - nocmk - - test-upstream-requirements-py37 + - test-upstream-requirements-py311 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: "3.11" - run: | python -m pip install --upgrade pip pip install --upgrade -r dev_requirements/ci-requirements.txt - name: run test env: - TOXENV: ${{ matrix.category }} + TOXENV: ${{ matrix.category }}${{ matrix.optional_mpl_dependency }} run: tox -- -vv diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index f0e8190e4..308bfbda2 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v1.3.0 + uses: dependabot/fetch-metadata@v1.3.6 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index 6b1c6be3e..e3776d399 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -9,7 +9,7 @@ jobs: environment: repo-sync runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: repo-sync/github-sync@v2 name: Sync repo to branch with: diff --git a/.gitignore b/.gitignore index 63097dcba..2843404d0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,9 @@ docs/build __pycache__ *.egg-info -# Coverage.py -.coverage* +# Coverage.py, NOT .coveragerc nor .coveragercmpl +.coverage +.coverage.py # MyPy .mypy_cache @@ -30,6 +31,14 @@ __pycache__ # PyTest .pytest_cache +# Ignore key materials generated by examples or tests +test_keyrings/ +# Ignore results of performance test +performance_tests/results/*.csv +performance_tests/results/*.pstats +performance_tests/results/*.png +# Ignore the memory profile logs +mprofile_* # PyCharm .idea/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index a19ab5081..82c9c983c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,6 +5,12 @@ # Required version: 2 +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.8" + # Build documentation in the doc/ directory with Sphinx sphinx: configuration: doc/conf.py @@ -15,8 +21,7 @@ submodules: exclude: all python: - version: 3.8 install: - requirements: dev_requirements/doc-requirements.txt - - method: setuptools + - method: pip path: . diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f53472963..c9ba4ef56 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,125 @@ Changelog ********* +4.0.1 -- 2025-03-26 +=================== + +Fixes +----------- +* fix: Improve header serialization + `#747 `_ + + ESDK-Python <4.0.1 would truncate non-ASCII key provider IDs it wrote to message headers. + If a Raw or Custom MasterKeyProvider or Keyring supplied a non-ASCII key provider ID / key namespace, + ESDK-Python would truncate the the key provider ID it wrote to the message's header. + The message can be decrypted by replacing the truncated provider ID with the expected provider ID in decryption code. + Contact AWS for any questions about this approach. + +Maintenance +----------- +* deps: Extend supported `MPL`_ versions to include v1.10.0 + +4.0.0 -- 2024-10-29 +=================== + +Features +-------- +* Add support for constructs from the `AWS Cryptographic Material Providers Library (MPL) `_. + The MPL contains new constructs for encrypting and decrypting your data. + We highly recommend installing the MPL. See `Installing `_ for instructions. + +Breaking Changes +^^^^^^^^^^^^^^^^ +* The MPL introduces the Required Encryption Context Cryptographic Materials Manager + ("required EC CMM") as a new construct for protecting your data. + On encrypt, the required EC CMM will use specific configured + encryption context key-value pairs to calculate the message signature, + but will not store those pairs in the ESDK message. + On decrypt, decryptors must supply these same pairs that were used when encrypting the message. + All messages that have been encrypted with versions of the ESDK <4.0.0 are forward compatible with this change. + However, messages that are constructed with the required EC CMM are not backward compatible with ESDK <4.0.0, + as no version of ESDK <4.0.0 supports reading messages encrypted with the required EC CMM. + A message that is encrypted with the required EC CMM from the MPL must be decrypted with a CMM from the MPL. + For more information on using the required EC CMM, see `AWS Documentation `_. + +Fixes +----------- +* fix: MKPs attempt to decrypt with remaining keys if a preceding raw RSA key failed to decrypt + `#707 `_ + +3.3.0 -- 2024-05-20 +=================== + +Deprecation +----------- +The AWS Encryption SDK for Python no longer supports Python 3.7 +as of version 3.3; only Python 3.8+ is supported. + +Fixes +----------- +* fix: Handle errors when decrypting multiple EDKs with raw RSA MKPs (#672 (https://github.com/aws/aws-encryption-sdk-python/pull/672)) +* chore: Updated description of decrypt() usage in src/aws_encryption_sdk/__init__.py (#660 (https://github.com/aws/aws-encryption-sdk-python/pull/660)) +* fix(CI): removed appveyor.yml (#668 (https://github.com/aws/aws-encryption-sdk-python/pull/668)) +* fix(CI): updated ci_test-vector-handler.yaml and ci_tests.yaml (#665 (https://github.com/aws/aws-encryption-sdk-python/pull/665)) + +Maintenance +----------- +* feat: remove Python3.7 support (#648 (https://github.com/aws/aws-encryption-sdk-python/pull/648)) +* chore: Update copyright headers (#677 (https://github.com/aws/aws-encryption-sdk-python/pull/677)) +* chore(CFN): Changes for MPL TestVectors (#653 (https://github.com/aws/aws-encryption-sdk-python/pull/653)) + +3.2.0 -- 2024-03-18 +=================== + +Features +----------- +* test Python 3.12 in CI (#623 (https://github.com/josecorella/aws-encryption-sdk-python/issues/623)) (93a67d8 (https://github.com/josecorella/aws-encryption-sdk-python/commit/93a67d8a3806f560ead950e6d8898e53c4c4f9df)) +* update requirements and README (#638 (https://github.com/josecorella/aws-encryption-sdk-python/issues/638)) (bcead77 (https://github.com/josecorella/aws-encryption-sdk-python/commit/bcead776b022566ad8211a08e1a458375b23a356)) + +Fixes +----------- +* CI for Decrypt Oracle (#558 (https://github.com/josecorella/aws-encryption-sdk-python/issues/558)) (6c6b732 (https://github.com/josecorella/aws-encryption-sdk-python/commit/6c6b732379197e91d2137af9f018f670a1ce500a)) +* deprecate python36 from chalice (#539 (https://github.com/josecorella/aws-encryption-sdk-python/issues/539)) (f8aa29f (https://github.com/josecorella/aws-encryption-sdk-python/commit/f8aa29fe98d419dac916846d7ff207685ea95307)) +* test: correctly invoke ec.generate_private_key (#585 (https://github.com/josecorella/aws-encryption-sdk-python/issues/585)) (560e714 (https://github.com/josecorella/aws-encryption-sdk-python/commit/560e7143ac7caf98e190b17ce2af97b7eea6be16)) +* update pyca range (#507 (https://github.com/josecorella/aws-encryption-sdk-python/issues/507)) (aced92c (https://github.com/josecorella/aws-encryption-sdk-python/commit/aced92c3d87dddf3e0920b9dfad4cedd2473604a)) +* Use FORBID_ENCRYPT_ALLOW_DECRYPT policy for decrypt oracle (#538 (https://github.com/josecorella/aws-encryption-sdk-python/issues/538)) (e91838f (https://github.com/josecorella/aws-encryption-sdk-python/commit/e91838f65705867fc95506a4323054bca24e9521)) +* wrong formatting python warning (#546 (https://github.com/josecorella/aws-encryption-sdk-python/issues/546)) (9b618d3 (https://github.com/josecorella/aws-encryption-sdk-python/commit/9b618d3a5e517435304a891393fefcbbd89faf65)) + +Maintenance +----------- +* Add example for custom KMS client config (#440 (https://github.com/josecorella/aws-encryption-sdk-python/issues/440)) (08f305a (https://github.com/josecorella/aws-encryption-sdk-python/commit/08f305a9b7b5fc897d9cafac55fb98f3f2a6fe13)) +* Add Thread safety section to README (#562 (https://github.com/josecorella/aws-encryption-sdk-python/issues/562)) (7a07b16 (https://github.com/josecorella/aws-encryption-sdk-python/commit/7a07b161d51900066c131627f9f7330acb926d3b)) +* bump deps & document upstream test (#646 (https://github.com/josecorella/aws-encryption-sdk-python/issues/646)) (a93ffe7 (https://github.com/josecorella/aws-encryption-sdk-python/commit/a93ffe7a98f8913040f6a693701ba287dd1570fb)) +* CFN: Commit existing CFN (#636 (https://github.com/josecorella/aws-encryption-sdk-python/issues/636)) (c122076 (https://github.com/josecorella/aws-encryption-sdk-python/commit/c12207621d295b335fdfb500c2b02694cc6786d8)) +* ci: skip pyenv installation if already exists (#627 (https://github.com/josecorella/aws-encryption-sdk-python/issues/627)) (1006758 (https://github.com/josecorella/aws-encryption-sdk-python/commit/10067581cd3316fbb379929806db6867e4cb0feb)) +* deps: bump actions/checkout from 3 to 4 (#607 (https://github.com/josecorella/aws-encryption-sdk-python/issues/607)) (e5c331b (https://github.com/josecorella/aws-encryption-sdk-python/commit/e5c331b68590825b55b5300ffab6dc80fbd20818)) +* deps: bump actions/setup-python from 2 to 4.2.0 (#491 (https://github.com/josecorella/aws-encryption-sdk-python/issues/491)) (d064bf8 (https://github.com/josecorella/aws-encryption-sdk-python/commit/d064bf8813d25e1ba4a8cce7269b8ee48acfd79a)) +* deps: bump cryptography from 39.0.0 to 39.0.1 in /test (#559 (https://github.com/josecorella/aws-encryption-sdk-python/issues/559)) (6468137 (https://github.com/josecorella/aws-encryption-sdk-python/commit/646813786c6250a525afb67bebc486eda206edd8)) +* deps: bump cryptography from 39.0.1 to 41.0.2 in /test (#592 (https://github.com/josecorella/aws-encryption-sdk-python/issues/592)) (3ba8019 (https://github.com/josecorella/aws-encryption-sdk-python/commit/3ba8019681ed95c41bb9448f0c3897d1aecc7559)) +* deps: bump cryptography from 41.0.2 to 41.0.6 in /test (#626 (https://github.com/josecorella/aws-encryption-sdk-python/issues/626)) (c67e6bd (https://github.com/josecorella/aws-encryption-sdk-python/commit/c67e6bd471b30e13cc7f1b724ce7d19df2380c22)) +* deps: bump dependabot/fetch-metadata from 1.3.0 to 1.3.6 (#549 (https://github.com/josecorella/aws-encryption-sdk-python/issues/549)) (2a6bd9d (https://github.com/josecorella/aws-encryption-sdk-python/commit/2a6bd9d70c779655077985c544df3db6a3518443)) +* deps: bump flake8-bugbear in /dev_requirements (#512 (https://github.com/josecorella/aws-encryption-sdk-python/issues/512)) (93f01d6 (https://github.com/josecorella/aws-encryption-sdk-python/commit/93f01d655d6bce704bd8779cc9c4acb5f96b980c)) +* deps: bump flake8-docstrings in /dev_requirements (#555 (https://github.com/josecorella/aws-encryption-sdk-python/issues/555)) (bd8f270 (https://github.com/josecorella/aws-encryption-sdk-python/commit/bd8f270c8717e5d4a787d33bcfda8b53bbe7751e)) +* deps: bump flake8-print from 4.0.0 to 5.0.0 in /dev_requirements (#554 (https://github.com/josecorella/aws-encryption-sdk-python/issues/554)) (2326531 (https://github.com/josecorella/aws-encryption-sdk-python/commit/232653188558379bceeb884b3f74b56b07560f62)) +* deps: bump isort from 5.10.1 to 5.11.4 in /dev_requirements (#551 (https://github.com/josecorella/aws-encryption-sdk-python/issues/551)) (36a0ea2 (https://github.com/josecorella/aws-encryption-sdk-python/commit/36a0ea2199872d6590691b53fbea7aee2236a99e)) +* deps: bump pytest from 7.0.1 to 7.2.0 in /dev_requirements (#524 (https://github.com/josecorella/aws-encryption-sdk-python/issues/524)) (af98302 (https://github.com/josecorella/aws-encryption-sdk-python/commit/af983024fdd800e6b2c4ae41cdf1617c982e4916)) +* deps: bump pytest from 7.2.0 to 7.2.1 in /dev_requirements (#553 (https://github.com/josecorella/aws-encryption-sdk-python/issues/553)) (48f96d5 (https://github.com/josecorella/aws-encryption-sdk-python/commit/48f96d58eeb712a5faa631ce4f4930d5d23bb649)) +* deps: bump pytest-cov from 3.0.0 to 4.0.0 in /dev_requirements (#550 (https://github.com/josecorella/aws-encryption-sdk-python/issues/550)) (6e436e1 (https://github.com/josecorella/aws-encryption-sdk-python/commit/6e436e13ce250759a499c3d9c820384cfc26283c)) +* deps: bump readme-renderer from 34.0 to 37.3 in /dev_requirements (#526 (https://github.com/josecorella/aws-encryption-sdk-python/issues/526)) (38aa063 (https://github.com/josecorella/aws-encryption-sdk-python/commit/38aa06309ad8ad709044c86ac6b4951739fbf996)) +* deps: bump setuptools from 62.0.0 to 66.1.1 in /dev_requirements (#547 (https://github.com/josecorella/aws-encryption-sdk-python/issues/547)) (04e8c16 (https://github.com/josecorella/aws-encryption-sdk-python/commit/04e8c167273357a9548ff474c527805d8764a661)) +* deps: bump sphinx from 4.4.0 to 5.3.0 in /dev_requirements (#523 (https://github.com/josecorella/aws-encryption-sdk-python/issues/523)) (51cb2ce (https://github.com/josecorella/aws-encryption-sdk-python/commit/51cb2ce148bc7e048587b013337f2440b53c1387)) +* deps: bump tox from 3.24.5 to 3.27.1 in /dev_requirements (#528 (https://github.com/josecorella/aws-encryption-sdk-python/issues/528)) (e2c834a (https://github.com/josecorella/aws-encryption-sdk-python/commit/e2c834ac5c4a9ca65db2b225e794f7ddf4d89cc4)) +* deps: bump urllib3 from 1.26.14 to 1.26.18 in /test (#618 (https://github.com/josecorella/aws-encryption-sdk-python/issues/618)) (bbb2281 (https://github.com/josecorella/aws-encryption-sdk-python/commit/bbb2281ed61f8fc8700e31d9828753531c8e586f)) +* deps: bump vulture from 2.3 to 2.6 in /dev_requirements (#533 (https://github.com/josecorella/aws-encryption-sdk-python/issues/533)) (2822364 (https://github.com/josecorella/aws-encryption-sdk-python/commit/28223646b4c48b2508ca46e3084689988abd2d27)) +* deps: bump wheel from 0.37.1 to 0.38.4 in /dev_requirements (#536 (https://github.com/josecorella/aws-encryption-sdk-python/issues/536)) (1922650 (https://github.com/josecorella/aws-encryption-sdk-python/commit/19226506ad33f5b964fe6632604425923f6ba8c1)) +* drop py3.6 from Oracle & Test Vectors (#529 (https://github.com/josecorella/aws-encryption-sdk-python/issues/529)) (8b6a493 (https://github.com/josecorella/aws-encryption-sdk-python/commit/8b6a49388c85785a22d59430007b7873ac8acf96)) +* drop py36 support (#530 (https://github.com/josecorella/aws-encryption-sdk-python/issues/530)) (a753ff8 (https://github.com/josecorella/aws-encryption-sdk-python/commit/a753ff884fe3000881c7d3a2392a0b5d65cfa138)) +* release: add api token to prod release process (#503 (https://github.com/josecorella/aws-encryption-sdk-python/issues/503)) (333c85b (https://github.com/josecorella/aws-encryption-sdk-python/commit/333c85b40b8ee20ed6303b9775e7fb9a6c6d2c63)) +* release: add api token to staging release process (#502 (https://github.com/josecorella/aws-encryption-sdk-python/issues/502)) (78e43b3 (https://github.com/josecorella/aws-encryption-sdk-python/commit/78e43b38a5b9df9a925084242a230fccf91476f2)) +* rm upstream-py27 (#564 (https://github.com/josecorella/aws-encryption-sdk-python/issues/564)) (b378508 (https://github.com/josecorella/aws-encryption-sdk-python/commit/b3785085b7c00fef27a250abf78549d6e7928802)) +* SupportPolicy: Mark 1.x & 2.x End-of-Support (#501 (https://github.com/josecorella/aws-encryption-sdk-python/issues/501)) (ca58e5e (https://github.com/josecorella/aws-encryption-sdk-python/commit/ca58e5e0ce373e9ae5132bb5ce95b6886a0a37d3)) + + 3.1.1 -- 2022-06-20 =================== @@ -325,6 +444,7 @@ Minor =================== * Initial public release +.. _MPL: https://github.com/aws/aws-cryptographic-material-providers-library .. _breaking changes in attrs 17.1.0: https://attrs.readthedocs.io/en/stable/changelog.html .. _tox: https://tox.readthedocs.io/en/latest/ .. _pylint: https://www.pylint.org/ diff --git a/README.rst b/README.rst index 7f1b22824..ebc71fa85 100644 --- a/README.rst +++ b/README.rst @@ -34,11 +34,17 @@ Getting Started Required Prerequisites ====================== -* Python 3.6+ -* cryptography >= 2.5.0 +* Python 3.8+ +* cryptography >= 3.4.6 * boto3 >= 1.10.0 * attrs +Recommended Prerequisites +========================= + +* aws-cryptographic-material-providers: == 1.10.0 + * Requires Python 3.11+. + Installation ============ @@ -49,42 +55,71 @@ Installation .. code:: - $ pip install aws-encryption-sdk + $ pip install "aws-encryption-sdk[MPL]" +The `[MPL]` suffix also installs the `AWS Cryptographic Material Providers Library (MPL)`_. +This is a library that contains constructs for encrypting and decrypting your data. +We highly recommend installing the MPL. +However, if you do not wish to install the MPL, omit the `[MPL]` suffix. Concepts ======== -There are four main concepts that you need to understand to use this library: +There are three main concepts that you need to understand to use this library: + +Data Keys +--------- +Data keys are the encryption keys that are used to encrypt your data. If your algorithm suite +uses a key derivation function, the data key is used to generate the key that directly encrypts the data. + +Keyrings +-------- +Keyrings are resources that generate, encrypt, and decrypt data keys. +You specify a keyring when encrypting and the same or a different keyring when decrypting. + +Note: You must also install the `AWS Cryptographic Material Providers Library (MPL)`_ to create and use keyrings. + +For more information, see the `AWS Documentation for Keyrings`_. Cryptographic Materials Managers -------------------------------- Cryptographic materials managers (CMMs) are resources that collect cryptographic materials and prepare them for use by the Encryption SDK core logic. -An example of a CMM is the default CMM, which is automatically generated anywhere a caller provides a master -key provider. The default CMM collects encrypted data keys from all master keys referenced by the master key -provider. +An example of a CMM is the default CMM, +which is automatically generated anywhere a caller provides a keyring. -An example of a more advanced CMM is the caching CMM, which caches cryptographic materials provided by another CMM. +Note: You must also install the `AWS Cryptographic Material Providers Library (MPL)`_ +to create and use CMMs that use keyrings. +CMMs that use master key providers have been marked as legacy since v4 of this library. + +Legacy Concepts +=============== +This section describes legacy concepts introduced in earlier versions of this library. +These components have been superseded by new components in the `AWS Cryptographic Material Providers Library (MPL)`_. +Please avoid using these components, and instead use components in the MPL. Master Key Providers -------------------- Master key providers are resources that provide master keys. -An example of a master key provider is `AWS KMS`_. To encrypt data in this client, a ``MasterKeyProvider`` object must contain at least one ``MasterKey`` object. ``MasterKeyProvider`` objects can also contain other ``MasterKeyProvider`` objects. +NOTE: Master key providers are legacy components +and have been superseded by keyrings +provided by the `AWS Cryptographic Material Providers Library (MPL)`_. +Please install this library and migrate master key providers to keyring interfaces. + Master Keys ----------- Master keys generate, encrypt, and decrypt data keys. -An example of a master key is a `KMS customer master key (CMK)`_. +An example of a master key is an `AWS KMS key`_. -Data Keys ---------- -Data keys are the encryption keys that are used to encrypt your data. If your algorithm suite -uses a key derivation function, the data key is used to generate the key that directly encrypts the data. +NOTE: Master keys are legacy constructs +and have been superseded by keyrings +provided by the `AWS Cryptographic Material Providers Library (MPL)`_. +Please install this library and migrate master key providers to keyring interfaces. ***** Usage @@ -110,147 +145,71 @@ version of the AWS Encryption SDK, we recommend using the default value. ) -You must then create an instance of either a master key provider or a CMM. The examples in this -readme use the ``StrictAwsKmsMasterKeyProvider`` class. +You must then create an instance of either a keyring (with the MPL installed) or a CMM. +Note: You must also install the `AWS Cryptographic Material Providers Library (MPL)`_ to use keyrings. +(You may also provide an instance of a legacy master key provider, but this is not recommended.) + + +AwsKmsMultiKeyring +================== +An ``AwsKmsMultiKeyring`` is configured with a generator keyring and a list of +child keyrings of type ``AwsKmsKeyring``. The effect is like using several keyrings +in a series. When you use a multi-keyring to encrypt data, any of the wrapping keys +in any of its keyrings can decrypt that data. -StrictAwsKmsMasterKeyProvider -============================= -A ``StrictAwsKmsMasterKeyProvider`` is configured with an explicit list of AWS KMS -CMKs with which to encrypt and decrypt data. On encryption, it encrypts the plaintext with all -configured CMKs. On decryption, it only attempts to decrypt ciphertexts that have been wrapped -with a CMK that matches one of the configured CMK ARNs. +On encryption, the generator keyring generates and encrypts the plaintext data key. +Then, all of the wrapping keys in all of the child keyrings encrypt the same plaintext data key. +The final `encrypted message`_ will include a copy of the data key encrypted by each configured key. +On decryption, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted data keys. +The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. -To create a ``StrictAwsKmsMasterKeyProvider`` you must provide one or more CMKs. For providers that will only -be used for encryption, you can use any valid `KMS key identifier`_. For providers that will be used for decryption, you -must use the key ARN; key ids, alias names, and alias ARNs are not supported. +An individual ``AwsKmsKeyring`` in an ``AwsKmsMultiKeyring`` is configured with an +AWS KMS key ARN. +For keyrings that will only be used for encryption, +you can use any valid `KMS key identifier`_. +For providers that will be used for decryption, +you must use the key ARN. +Key ids, alias names, and alias ARNs are not supported for decryption. -Because the ``StrictAwsKmsMasterKeyProvider`` uses the `boto3 SDK`_ to interact with `AWS KMS`_, +Because the ``AwsKmsMultiKeyring`` uses the `boto3 SDK`_ to interact with `AWS KMS`_, it requires AWS Credentials. To provide these credentials, use the `standard means by which boto3 locates credentials`_ or provide a -pre-existing instance of a ``botocore session`` to the ``StrictAwsKmsMasterKeyProvider``. +pre-existing instance of a ``botocore session`` to the ``AwsKmsMultiKeyring``. This latter option can be useful if you have an alternate way to store your AWS credentials or you want to reuse an existing instance of a botocore session in order to decrease startup costs. +You can also add KMS keys from multiple regions to the ``AwsKmsMultiKeyring``. -If you configure the the ``StrictAwsKmsMasterKeyProvider`` with multiple CMKs, the `final message`_ -will include a copy of the data key encrypted by each configured CMK. - -.. code:: python +See `examples/src/aws_kms_multi_keyring_example.py`_ for a code example configuring and using +a ``AwsKmsMultiKeyring`` with the ``EncryptionSDKClient``. - import aws_encryption_sdk - - kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ]) - -You can add CMKs from multiple regions to the ``StrictAwsKmsMasterKeyProvider``. - -.. code:: python - - import aws_encryption_sdk - - kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - 'arn:aws:kms:us-west-2:3333333333333:key/33333333-3333-3333-3333-333333333333', - 'arn:aws:kms:ap-northeast-1:4444444444444:key/44444444-4444-4444-4444-444444444444' - ]) - - -DiscoveryAwsKmsMasterKeyProvider -================================ -We recommend using a ``StrictAwsKmsMasterKeyProvider`` in order to ensure that you can only -encrypt and decrypt data using the AWS KMS CMKs you expect. However, if you are unable to -explicitly identify the AWS KMS CMKs that should be used for decryption, you can instead -use a ``DiscoveryAwsKmsMasterKeyProvider`` for decryption operations. This provider +AwsKmsDiscoveryKeyring +====================== +We recommend using an ``AwsKmsMultiKeyring`` in order to ensure that you can only +encrypt and decrypt data using the AWS KMS key ARN you expect. However, if you are unable to +explicitly identify the AWS KMS key ARNs that should be used for decryption, you can instead +use an ``AwsKmsDiscoveryKeyring`` for decryption operations. This provider attempts decryption of any ciphertexts as long as they match a ``DiscoveryFilter`` that you configure. A ``DiscoveryFilter`` consists of a list of AWS account ids and an AWS partition. +If you do not want to filter the set of allowed accounts, you can also omit the ``discovery_filter`` argument. -.. code:: python - - import aws_encryption_sdk - from aws_encryption_sdk.key_providers.kms import DiscoveryFilter - - discovery_filter = DiscoveryFilter( - account_ids=['222222222222', '333333333333'], - partition='aws' - ) - kms_key_provider = aws_encryption_sdk.DiscoveryAwsKmsMasterKeyProvider( - discovery_filter=discovery_filter - ) +Note that an ``AwsKmsDiscoveryKeyring`` cannot be used for encryption operations. -If you do not want to filter the set of allowed accounts, you can also omit the ``discovery_filter`` argument. +See `examples/src/aws_kms_discovery_keyring_example.py`_ for a code example configuring and using +an ``AwsKmsDiscoveryKeyring`` with the ``EncryptionSDKClient``. -Note that a ``DiscoveryAwsKmsMasterKeyProvider`` cannot be used for encryption operations. Encryption and Decryption ========================= -After you create an instance of an ``EncryptionSDKClient`` and a ``MasterKeyProvider``, you can use either of -the client's two ``encrypt``/``decrypt`` functions to encrypt and decrypt your data. +After you create an instance of an ``EncryptionSDKClient`` and a ``Keyring``, you can use +the client's ``encrypt`` and ``decrypt`` functions to encrypt and decrypt your data. -.. code:: python - - import aws_encryption_sdk - from aws_encryption_sdk.identifiers import CommitmentPolicy - - client = aws_encryption_sdk.EncryptionSDKClient( - commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - ) - - kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ]) - my_plaintext = b'This is some super secret data! Yup, sure is!' - - my_ciphertext, encryptor_header = client.encrypt( - source=my_plaintext, - key_provider=kms_key_provider - ) - - decrypted_plaintext, decryptor_header = client.decrypt( - source=my_ciphertext, - key_provider=kms_key_provider - ) - - assert my_plaintext == decrypted_plaintext - assert encryptor_header.encryption_context == decryptor_header.encryption_context - -You can provide an `encryption context`_: a form of additional authenticating information. - -.. code:: python - - import aws_encryption_sdk - from aws_encryption_sdk.identifiers import CommitmentPolicy - - client = aws_encryption_sdk.EncryptionSDKClient( - commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - ) - - kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ]) - my_plaintext = b'This is some super secret data! Yup, sure is!' - - my_ciphertext, encryptor_header = client.encrypt( - source=my_plaintext, - key_provider=kms_key_provider, - encryption_context={ - 'not really': 'a secret', - 'but adds': 'some authentication' - } - ) - - decrypted_plaintext, decryptor_header = client.decrypt( - source=my_ciphertext, - key_provider=kms_key_provider - ) - - assert my_plaintext == decrypted_plaintext - assert encryptor_header.encryption_context == decryptor_header.encryption_context +You can also provide an `encryption context`_: a form of additional authenticating information. +See code in the `examples/src/`_ directory for code examples configuring and using +keyrings and encryption context with the ``EncryptionSDKClient``. Streaming ========= @@ -259,54 +218,36 @@ memory at once, you can use this library's streaming clients directly. The strea file-like objects, and behave exactly as you would expect a Python file object to behave, offering context manager and iteration support. -.. code:: python - - import aws_encryption_sdk - from aws_encryption_sdk.identifiers import CommitmentPolicy - import filecmp - - client = aws_encryption_sdk.EncryptionSDKClient( - commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT - ) - - kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ]) - plaintext_filename = 'my-secret-data.dat' - ciphertext_filename = 'my-encrypted-data.ct' - - with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file: - with client.stream( - mode='e', - source=pt_file, - key_provider=kms_key_provider - ) as encryptor: - for chunk in encryptor: - ct_file.write(chunk) - - new_plaintext_filename = 'my-decrypted-data.dat' - - with open(ciphertext_filename, 'rb') as ct_file, open(new_plaintext_filename, 'wb') as pt_file: - with client.stream( - mode='d', - source=ct_file, - key_provider=kms_key_provider - ) as decryptor: - for chunk in decryptor: - pt_file.write(chunk) - - assert filecmp.cmp(plaintext_filename, new_plaintext_filename) - assert encryptor.header.encryption_context == decryptor.header.encryption_context +See `examples/src/file_streaming_example.py`_ for a code example streaming data to and from files. Performance Considerations ========================== Adjusting the frame size can significantly improve the performance of encrypt/decrypt operations with this library. -Processing each frame in a framed message involves a certain amount of overhead. If you are encrypting a large file, -increasing the frame size can offer potentially significant performance gains. We recommend that you tune these values +Processing each frame in a framed message involves a certain amount of overhead. If you are encrypting a large file, +increasing the frame size can offer potentially significant performance gains. We recommend that you tune these values to your use-case in order to obtain peak performance. +Thread safety +========================== +The ``EncryptionSDKClient`` and all provided ``CryptoMaterialsManager`` in this library are thread safe. +But instances of ``BaseKMSMasterKeyProvider`` MUST not be shared between threads, +for the reasons outlined in `the boto3 docs `_. + +Because the ``BaseKMSMaterKeyProvider`` creates a `new boto3 sessions `_ per region, +users do not need to create a client for every region in every thread; +a new ``BaseKMSMasterKeyProvider`` per thread is sufficient. + +(The ``BaseKMSMasterKeyProvider`` is the internal parent class of all the KMS Providers.) + +Finally, while the ``CryptoMaterialsCache`` is thread safe, +sharing entries in that cache across threads needs to be done carefully +(see the !Note about partition name `in the API Docs `_). + +**Important:** Components from the `AWS Cryptographic Material Providers Library (MPL)`_ +have separate thread safety considerations. +For more information, see the note on thread safety in that project's `README `_. + .. _AWS Encryption SDK: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html .. _cryptography: https://cryptography.io/en/latest/ @@ -314,11 +255,17 @@ to your use-case in order to obtain peak performance. .. _Read the Docs: http://aws-encryption-sdk-python.readthedocs.io/en/latest/ .. _GitHub: https://github.com/aws/aws-encryption-sdk-python/ .. _AWS KMS: https://docs.aws.amazon.com/kms/latest/developerguide/overview.html -.. _KMS customer master key (CMK): https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#master_keys +.. _AWS KMS key: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#master_keys .. _KMS key identifier: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id .. _boto3 SDK: https://boto3.readthedocs.io/en/latest/ .. _standard means by which boto3 locates credentials: https://boto3.readthedocs.io/en/latest/guide/configuration.html -.. _final message: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html +.. _encrypted message: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html .. _encryption context: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context .. _Security issue notifications: ./CONTRIBUTING.md#security-issue-notifications .. _Support Policy: ./SUPPORT_POLICY.rst +.. _AWS Cryptographic Material Providers Library (MPL): https://github.com/aws/aws-cryptographic-material-providers-library +.. _AWS Documentation for Keyrings: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/choose-keyring.html +.. _examples/src/aws_kms_multi_keyring_example.py: https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/aws_kms_multi_keyring_example.py +.. _examples/src/aws_kms_discovery_keyring_example.py: https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/aws_kms_discovery_keyring_example.py +.. _examples/src/: https://github.com/aws/aws-encryption-sdk-python/tree/master/examples/src/ +.. _examples/src/file_streaming_example.py: https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/file_streaming_example.py diff --git a/SUPPORT_POLICY.rst b/SUPPORT_POLICY.rst index 9020b6a11..1e20c300f 100644 --- a/SUPPORT_POLICY.rst +++ b/SUPPORT_POLICY.rst @@ -22,14 +22,18 @@ This table describes the current support status of each major version of the AWS - Next status - Next status date * - 1.x - - Maintenance - End of Support - - 2022-06-30 + - + - * - 2.x - - Maintenance - End of Support - - 2022-07-01 + - + - * - 3.x + - General Availability + - Maintenance + - 2025-05-20 + * - 4.x - General Availability - - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index cfb4bdcdb..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,113 +0,0 @@ -# https://packaging.python.org/guides/supporting-windows-using-appveyor/ - -environment: - - matrix: - # The only test we perform on Windows are our actual code tests. All linting, static - # analysis, etc are only run on Linux (via Travis CI). - - # Python 2.7 - - PYTHON: "C:\\Python27" - TOXENV: "py27-local" - - PYTHON: "C:\\Python27" - TOXENV: "py27-integ" - - PYTHON: "C:\\Python27" - TOXENV: "py27-accept" - - PYTHON: "C:\\Python27" - TOXENV: "py27-examples" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-local" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-integ" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-accept" - - PYTHON: "C:\\Python27-x64" - TOXENV: "py27-examples" - - # Python 3.4 - - PYTHON: "C:\\Python34" - TOXENV: "py34-local" - - PYTHON: "C:\\Python34" - TOXENV: "py34-integ" - - PYTHON: "C:\\Python34" - TOXENV: "py34-accept" - - PYTHON: "C:\\Python34" - TOXENV: "py34-examples" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-local" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-integ" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-accept" - - PYTHON: "C:\\Python34-x64" - DISTUTILS_USE_SDK: "1" - TOXENV: "py34-examples" - - # Python 3.5 - - PYTHON: "C:\\Python35" - TOXENV: "py35-local" - - PYTHON: "C:\\Python35" - TOXENV: "py35-integ" - - PYTHON: "C:\\Python35" - TOXENV: "py35-accept" - - PYTHON: "C:\\Python35" - TOXENV: "py35-examples" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-local" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-integ" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-accept" - - PYTHON: "C:\\Python35-x64" - TOXENV: "py35-examples" - - # Python 3.6 - - PYTHON: "C:\\Python36" - TOXENV: "py36-local" - - PYTHON: "C:\\Python36" - TOXENV: "py36-integ" - - PYTHON: "C:\\Python36" - TOXENV: "py36-accept" - - PYTHON: "C:\\Python36" - TOXENV: "py36-examples" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-local" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-integ" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-accept" - - PYTHON: "C:\\Python36-x64" - TOXENV: "py36-examples" - - # Python 3.7 - - PYTHON: "C:\\Python37" - TOXENV: "py37-local" - - PYTHON: "C:\\Python37" - TOXENV: "py37-integ" - - PYTHON: "C:\\Python37" - TOXENV: "py37-accept" - - PYTHON: "C:\\Python37" - TOXENV: "py37-examples" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-local" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-integ" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-accept" - - PYTHON: "C:\\Python37-x64" - TOXENV: "py37-examples" - -install: - # Prepend newly installed Python to the PATH of this build - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - # Check the Python version to verify the correct version was installed - - "python --version" - - "python -m pip install wheel tox" - -build: off - -test_script: - - "tox" diff --git a/buildspec.yml b/buildspec.yml index bb1e0cd97..4065dc4e8 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -2,44 +2,345 @@ version: 0.2 batch: fast-fail: false - build-list: - - identifier: py36_integ - buildspec: codebuild/py36/integ.yml - - identifier: py36_examples - buildspec: codebuild/py36/examples.yml - - identifier: py36_awses_local - buildspec: codebuild/py36/awses_local.yml - - - identifier: py37_integ - buildspec: codebuild/py37/integ.yml - - identifier: py37_examples - buildspec: codebuild/py37/examples.yml - - identifier: py37_awses_local - buildspec: codebuild/py37/awses_local.yml + build-graph: + # 3.8 - identifier: py38_integ buildspec: codebuild/py38/integ.yml + env: + image: aws/codebuild/standard:5.0 - identifier: py38_examples buildspec: codebuild/py38/examples.yml - - identifier: py38_awses_local - buildspec: codebuild/py38/awses_local.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_decrypt_dafny_esdk_vectors + buildspec: codebuild/py38/decrypt_dafny_esdk_vectors.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_decrypt_net_401_vectors + buildspec: codebuild/py38/decrypt_net_401_vectors.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_encrypt_masterkey + buildspec: codebuild/py38/encrypt_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_generate_decrypt_vectors_masterkey + buildspec: codebuild/py38/generate_decrypt_vectors_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_decrypt_masterkey_with_masterkey + depend-on: + - py38_generate_decrypt_vectors_masterkey + buildspec: codebuild/py38/decrypt_masterkey_with_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py38_decrypt_masterkey_with_js + depend-on: + - py38_generate_decrypt_vectors_masterkey + buildspec: codebuild/py38/decrypt_masterkey_with_js.yml + env: + image: aws/codebuild/standard:5.0 + # 3.9 - identifier: py39_integ buildspec: codebuild/py39/integ.yml + env: + image: aws/codebuild/standard:5.0 - identifier: py39_examples buildspec: codebuild/py39/examples.yml - - identifier: py39_awses_latest + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_decrypt_dafny_esdk_vectors + buildspec: codebuild/py39/decrypt_dafny_esdk_vectors.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_decrypt_net_401_vectors + buildspec: codebuild/py39/decrypt_net_401_vectors.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_encrypt_masterkey + buildspec: codebuild/py39/encrypt_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_generate_decrypt_vectors_masterkey + buildspec: codebuild/py39/generate_decrypt_vectors_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_decrypt_masterkey_with_masterkey + depend-on: + - py39_generate_decrypt_vectors_masterkey + buildspec: codebuild/py39/decrypt_masterkey_with_masterkey.yml + env: + image: aws/codebuild/standard:5.0 + - identifier: py39_decrypt_masterkey_with_js + depend-on: + - py39_generate_decrypt_vectors_masterkey + buildspec: codebuild/py39/decrypt_masterkey_with_js.yml + env: + image: aws/codebuild/standard:5.0 + # 3.10 - identifier: py310_integ buildspec: codebuild/py310/integ.yml + env: + image: aws/codebuild/standard:6.0 - identifier: py310_examples buildspec: codebuild/py310/examples.yml - - identifier: py310_awses_latest - buildspec: codebuild/py310/awses_local.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_decrypt_dafny_esdk_vectors + buildspec: codebuild/py310/decrypt_dafny_esdk_vectors.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_decrypt_net_401_vectors + buildspec: codebuild/py310/decrypt_net_401_vectors.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_encrypt_masterkey + buildspec: codebuild/py310/encrypt_masterkey.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_generate_decrypt_vectors_masterkey + buildspec: codebuild/py310/generate_decrypt_vectors_masterkey.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_decrypt_masterkey_with_masterkey + depend-on: + - py310_generate_decrypt_vectors_masterkey + buildspec: codebuild/py310/decrypt_masterkey_with_masterkey.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py310_decrypt_masterkey_with_js + depend-on: + - py310_generate_decrypt_vectors_masterkey + buildspec: codebuild/py310/decrypt_masterkey_with_js.yml + env: + image: aws/codebuild/standard:6.0 + - identifier: py311_integ + buildspec: codebuild/py311/integ.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_integ_mpl + buildspec: codebuild/py311/integ_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_performance_tests_mpl + buildspec: codebuild/py311/performance_tests_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_examples + buildspec: codebuild/py311/examples.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_examples_mpl + buildspec: codebuild/py311/examples_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_dafny_esdk_vectors_masterkey + buildspec: codebuild/py311/decrypt_dafny_esdk_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_dafny_esdk_vectors_keyrings + buildspec: codebuild/py311/decrypt_dafny_esdk_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_net_401_vectors_masterkey + buildspec: codebuild/py311/decrypt_net_401_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_net_401_vectors_keyrings + buildspec: codebuild/py311/decrypt_net_401_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_encrypt_masterkey + buildspec: codebuild/py311/encrypt_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_encrypt_keyrings + buildspec: codebuild/py311/encrypt_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_generate_decrypt_vectors_masterkey + buildspec: codebuild/py311/generate_decrypt_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_masterkey_with_masterkey + depend-on: + - py311_generate_decrypt_vectors_masterkey + buildspec: codebuild/py311/decrypt_masterkey_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_masterkey_with_keyrings + depend-on: + - py311_generate_decrypt_vectors_masterkey + buildspec: codebuild/py311/decrypt_masterkey_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_masterkey_with_js + depend-on: + - py311_generate_decrypt_vectors_masterkey + buildspec: codebuild/py311/decrypt_masterkey_with_js.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_generate_decrypt_vectors_keyrings + buildspec: codebuild/py311/generate_decrypt_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_keyrings_with_masterkey + depend-on: + - py311_generate_decrypt_vectors_keyrings + buildspec: codebuild/py311/decrypt_keyrings_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_keyrings_with_keyrings + depend-on: + - py311_generate_decrypt_vectors_keyrings + buildspec: codebuild/py311/decrypt_keyrings_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_keyrings_with_js + depend-on: + - py311_generate_decrypt_vectors_keyrings + buildspec: codebuild/py311/decrypt_keyrings_with_js.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_golden_manifest_with_keyrings + buildspec: codebuild/py311/decrypt_golden_manifest_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py311_decrypt_golden_manifest_with_masterkey + buildspec: codebuild/py311/decrypt_golden_manifest_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + + - identifier: py312_integ + buildspec: codebuild/py312/integ.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_integ_mpl + buildspec: codebuild/py312/integ_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_performance_tests_mpl + buildspec: codebuild/py312/performance_tests_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_examples + buildspec: codebuild/py312/examples.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_examples_mpl + buildspec: codebuild/py312/examples_mpl.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_dafny_esdk_vectors_masterkey + buildspec: codebuild/py312/decrypt_dafny_esdk_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_dafny_esdk_vectors_keyrings + buildspec: codebuild/py312/decrypt_dafny_esdk_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_net_401_vectors_masterkey + buildspec: codebuild/py312/decrypt_net_401_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_net_401_vectors_keyrings + buildspec: codebuild/py312/decrypt_net_401_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_encrypt_masterkey + buildspec: codebuild/py312/encrypt_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_encrypt_keyrings + buildspec: codebuild/py312/encrypt_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_generate_decrypt_vectors_masterkey + buildspec: codebuild/py312/generate_decrypt_vectors_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_masterkey_with_masterkey + depend-on: + - py312_generate_decrypt_vectors_masterkey + buildspec: codebuild/py312/decrypt_masterkey_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_masterkey_with_keyrings + depend-on: + - py312_generate_decrypt_vectors_masterkey + buildspec: codebuild/py312/decrypt_masterkey_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_masterkey_with_js + depend-on: + - py312_generate_decrypt_vectors_masterkey + buildspec: codebuild/py312/decrypt_masterkey_with_js.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_generate_decrypt_vectors_keyrings + buildspec: codebuild/py312/generate_decrypt_vectors_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_keyrings_with_masterkey + depend-on: + - py312_generate_decrypt_vectors_keyrings + buildspec: codebuild/py312/decrypt_keyrings_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_keyrings_with_keyrings + depend-on: + - py312_generate_decrypt_vectors_keyrings + buildspec: codebuild/py312/decrypt_keyrings_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_keyrings_with_js + depend-on: + - py312_generate_decrypt_vectors_keyrings + buildspec: codebuild/py312/decrypt_keyrings_with_js.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/generate_hkeyring_decrypt_vectors.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_masterkey + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_keyrings + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_net + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_net.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_golden_manifest_with_keyrings + buildspec: codebuild/py312/decrypt_golden_manifest_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_golden_manifest_with_masterkey + buildspec: codebuild/py312/decrypt_golden_manifest_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: code_coverage buildspec: codebuild/coverage/coverage.yml + - identifier: code_coverage_mpl + buildspec: codebuild/coverage/coverage_mpl.yml + env: + image: aws/codebuild/standard:7.0 - identifier: compliance buildspec: codebuild/compliance/compliance.yml diff --git a/cfn/ESDK-Python.yml b/cfn/ESDK-Python.yml new file mode 100644 index 000000000..acf5689c5 --- /dev/null +++ b/cfn/ESDK-Python.yml @@ -0,0 +1,415 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Template to build a CodeBuild Project, assumes that GitHub credentials are already set up." +Parameters: + ProjectName: + Type: String + Description: The name of the CodeBuild Project + ProjectDescription: + Type: String + Description: The description for the CodeBuild Project + SourceLocation: + Type: String + Description: The https GitHub URL for the project + NumberOfBuildsInBatch: + Type: Number + MaxValue: 100 + MinValue: 1 + Default: 4 + Description: The number of builds you expect to run in a batch + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - + Label: + default: "Crypto Tools CodeBuild Project Template" + Parameters: + - ProjectName + - ProjectDescription + - SourceLocation + +Resources: + CodeBuildProject: + Type: "AWS::CodeBuild::Project" + Properties: + Name: !Ref ProjectName + Description: !Ref ProjectDescription + Source: + Location: !Ref SourceLocation + GitCloneDepth: 1 + GitSubmodulesConfig: + FetchSubmodules: true + InsecureSsl: false + ReportBuildStatus: false + Type: "GITHUB" + Artifacts: + Type: "NO_ARTIFACTS" + Cache: + Type: "NO_CACHE" + Environment: + ComputeType: "BUILD_GENERAL1_MEDIUM" + Image: "aws/codebuild/standard:3.0" + ImagePullCredentialsType: "CODEBUILD" + PrivilegedMode: false + Type: "LINUX_CONTAINER" + ServiceRole: !GetAtt CodeBuildCIServiceRole.Arn + TimeoutInMinutes: 60 + QueuedTimeoutInMinutes: 480 + EncryptionKey: !Sub "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3" + BadgeEnabled: false + BuildBatchConfig: + ServiceRole: !GetAtt CodeBuildCIServiceRole.Arn + Restrictions: + MaximumBuildsAllowed: !Ref NumberOfBuildsInBatch + ComputeTypesAllowed: + - BUILD_GENERAL1_SMALL + - BUILD_GENERAL1_MEDIUM + TimeoutInMins: 480 + LogsConfig: + CloudWatchLogs: + Status: "ENABLED" + S3Logs: + Status: "DISABLED" + EncryptionDisabled: false + + CodeBuildProjectTestRelease: + Type: "AWS::CodeBuild::Project" + Properties: + Name: !Sub "${ProjectName}-test-release" + Description: !Sub "CodeBuild project for ${ProjectName} to release to test PyPi." + Source: + Location: !Ref SourceLocation + BuildSpec: "codebuild/release/test-release.yml" + GitCloneDepth: 1 + GitSubmodulesConfig: + FetchSubmodules: false + InsecureSsl: false + ReportBuildStatus: false + Type: "GITHUB" + Artifacts: + Type: "NO_ARTIFACTS" + Cache: + Type: "NO_CACHE" + Environment: + ComputeType: "BUILD_GENERAL1_SMALL" + Image: "aws/codebuild/standard:3.0" + ImagePullCredentialsType: "CODEBUILD" + PrivilegedMode: false + Type: "LINUX_CONTAINER" + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + TimeoutInMinutes: 60 + QueuedTimeoutInMinutes: 480 + EncryptionKey: !Sub "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3" + BadgeEnabled: false + BuildBatchConfig: + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + Restrictions: + MaximumBuildsAllowed: !Ref NumberOfBuildsInBatch + ComputeTypesAllowed: + - BUILD_GENERAL1_SMALL + - BUILD_GENERAL1_MEDIUM + TimeoutInMins: 480 + LogsConfig: + CloudWatchLogs: + Status: "ENABLED" + S3Logs: + Status: "DISABLED" + EncryptionDisabled: false + + CodeBuildProjectProdRelease: + Type: "AWS::CodeBuild::Project" + Properties: + Name: !Sub "${ProjectName}-prod-release" + Description: !Sub "CodeBuild project for ${ProjectName} to release to prod PyPi." + Source: + Location: !Ref SourceLocation + BuildSpec: "codebuild/release/prod-release.yml" + GitCloneDepth: 1 + GitSubmodulesConfig: + FetchSubmodules: false + InsecureSsl: false + ReportBuildStatus: false + Type: "GITHUB" + Artifacts: + Type: "NO_ARTIFACTS" + Cache: + Type: "NO_CACHE" + Environment: + ComputeType: "BUILD_GENERAL1_SMALL" + Image: "aws/codebuild/standard:3.0" + ImagePullCredentialsType: "CODEBUILD" + PrivilegedMode: false + Type: "LINUX_CONTAINER" + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + TimeoutInMinutes: 60 + QueuedTimeoutInMinutes: 480 + EncryptionKey: !Sub "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3" + BadgeEnabled: false + BuildBatchConfig: + ServiceRole: !GetAtt CodeBuildServiceRole.Arn + Restrictions: + MaximumBuildsAllowed: !Ref NumberOfBuildsInBatch + ComputeTypesAllowed: + - BUILD_GENERAL1_SMALL + - BUILD_GENERAL1_MEDIUM + TimeoutInMins: 480 + LogsConfig: + CloudWatchLogs: + Status: "ENABLED" + S3Logs: + Status: "DISABLED" + EncryptionDisabled: false + + + + CodeBuildServiceRole: + Type: "AWS::IAM::Role" + Properties: + Path: "/service-role/" + RoleName: !Sub "codebuild-${ProjectName}-service-role" + AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"codebuild.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" + MaxSessionDuration: 3600 + ManagedPolicyArns: + # Ideally we would add GeneratedVectorsArtifactsS3BucketPolicy to run test vectors. + # However, this role would then have 11 managed policies. + # IAM has a limit of 10 managed policies per role. + # If we need to add more policies here, we should increase this limit. + - !Ref CryptoToolsKMS + - !Ref CodeBuildBatchPolicy + - !Ref CodeBuildBasePolicy + - !Ref SecretsManagerPolicy + - !Ref CodeBuildCISTSAllow + + CodeBuildCIServiceRole: + Type: "AWS::IAM::Role" + Properties: + Path: "/service-role/" + RoleName: !Sub "codebuild-${ProjectName}-CI-service-role" + AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"codebuild.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" + MaxSessionDuration: 3600 + ManagedPolicyArns: + - !Ref CryptoToolsKMS + - !Ref CodeBuildCIBatchPolicy + - !Ref CodeBuildBasePolicy + - !Ref SecretsManagerCIPolicy + - !Ref CodeBuildCISTSAllow + - !Ref GeneratedVectorsArtifactsS3BucketPolicy + + CodeBuildBatchPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CodeBuildBuildBatchPolicy-${ProjectName}-${AWS::Region}-codebuild-${ProjectName}-service-role" + Path: "/service-role/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${ProjectName}", + "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${ProjectName}-test-release", + "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${ProjectName}-prod-release" + ], + "Action": [ + "codebuild:StartBuild", + "codebuild:StopBuild", + "codebuild:RetryBuild" + ] + } + ] + } + + CodeBuildCIBatchPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CodeBuildBuildBatchPolicy-${ProjectName}-${AWS::Region}-codebuild-${ProjectName}-CI-service-role" + Path: "/service-role/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/${ProjectName}" + ], + "Action": [ + "codebuild:StartBuild", + "codebuild:StopBuild", + "codebuild:RetryBuild", + "codebuild:BatchGetBuilds" + ] + } + ] + } + + CodeBuildBasePolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CodeBuildBasePolicy-${ProjectName}-${AWS::Region}" + Path: "/service-role/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}", + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}:*", + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-test-release", + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-test-release:*", + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-prod-release", + "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-prod-release:*" + ], + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:GetLogEvents" + ] + }, + { + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::codepipeline-${AWS::Region}-*" + ], + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:GetObjectVersion", + "s3:GetBucketAcl", + "s3:GetBucketLocation" + ] + }, + { + "Effect": "Allow", + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Resource": [ + "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/${ProjectName}-*" + ] + } + ] + } + + SecretsManagerPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CryptoTools-SecretsManager-${ProjectName}-release" + Path: "/service-role/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:secretsmanager:us-west-2:587316601012:secret:TestPyPiCryptoTools-SxeLBh", + "arn:aws:secretsmanager:us-west-2:587316601012:secret:TestPyPiAPIToken-uERFjs", + "arn:aws:secretsmanager:us-west-2:587316601012:secret:PyPiAdmin-ZWyd1T", + "arn:aws:secretsmanager:us-west-2:587316601012:secret:PyPiAPIToken-nu1Gu6" + ], + "Action": "secretsmanager:GetSecretValue" + } + ] + } + + SecretsManagerCIPolicy: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CryptoTools-SecretsManagerCI-${ProjectName}-release" + Path: "/service-role/" + # Policy: Allow access to a Github fine-grained PAT that can read ESDK-Dafny "Daily CI" artifacts + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:secretsmanager:us-west-2:587316601012:secret:Github/lucasmcdonald3-fgpat-1aAsdO" + ], + "Action": "secretsmanager:GetSecretValue" + } + ] + } + + # There exist public AWS KMS CMKs that are used for testing + # Take care with these CMKs they are **ONLY** for testing!!! + CryptoToolsKMS: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub "CrypotToolsKMSPolicy-${ProjectName}-${AWS::Region}-codebuild-${ProjectName}-service-role" + Path: "/service-role/" + PolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + "arn:aws:kms:*:658956600833:key/*", + "arn:aws:kms:*:658956600833:alias/*", + "arn:aws:kms:*:370957321024:key/*", + "arn:aws:kms:*:370957321024:alias/*" + ], + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:Generate*", + "kms:GetPublicKey", + "kms:DescribeKey" + ] + } + ] + } + + CodeBuildCISTSAllow: + Type: "AWS::IAM::ManagedPolicy" + Properties: + ManagedPolicyName: !Sub CodeBuildCISTSAllow-${ProjectName} + Path: /service-role/ + PolicyDocument: | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Resource": "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" + } + ] + } + + GeneratedVectorsArtifactsS3Bucket: + Type: 'AWS::S3::Bucket' + Properties: + BucketName: generated-vectors-artifacts-bucket + LifecycleConfiguration: + Rules: + - Id: Expire artifacts in 14 days + Status: Enabled + ExpirationInDays: 14 + + GeneratedVectorsArtifactsS3BucketPolicy: + Type: 'AWS::IAM::ManagedPolicy' + Properties: + ManagedPolicyName: Generated-Vectors-Artifacts-S3-Bucket-Policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 's3:PutObject' + - 's3:GetObject' + - 's3:DeleteObject' + Resource: + - !Join [ "", [ !GetAtt GeneratedVectorsArtifactsS3Bucket.Arn, '/*'] ] diff --git a/cfn/Public-ESDK-Python-CI.yml b/cfn/Public-ESDK-Python-CI.yml new file mode 100644 index 000000000..6d40b8274 --- /dev/null +++ b/cfn/Public-ESDK-Python-CI.yml @@ -0,0 +1,59 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "IAM Managed Policies/Role for AWS KMS Hierarchical Keyring Testing" + +Parameters: + ProjectName: + Type: String + Description: A prefix that will be applied to any resource names + Default: Public-ESDK-Python + GitHubRepo: + Type: String + Description: GitHub Repo that invokes CI + Default: aws/aws-encryption-sdk-python + +Resources: + GitHubCIRole: + Type: 'AWS::IAM::Role' + Properties: + RoleName: !Sub "GitHub-CI-${ProjectName}-Role-${AWS::Region}" + Description: "Access DDB, KMS, Resources for CI from GitHub" + ManagedPolicyArns: + - "arn:aws:iam::370957321024:policy/ESDK-Dafny-DDB-ReadWriteDelete-us-west-2" + - "arn:aws:iam::370957321024:policy/Hierarchical-GitHub-KMS-Key-Policy" + - "arn:aws:iam::370957321024:policy/KMS-Public-CMK-EncryptDecrypt-Key-Access" + - "arn:aws:iam::370957321024:policy/RSA-GitHub-KMS-Key-Policy" + AssumeRolePolicyDocument: !Sub | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { "Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com" }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" + }, + "StringLike": { + "token.actions.githubusercontent.com:sub": "repo:${GitHubRepo}:*" + } + } + }, + { + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { + "aws:PrincipalArn": [ + "arn:aws:iam::587316601012:role/service-role/codebuild-python-esdk-CI-service-role", + "arn:aws:iam::587316601012:role/service-role/codebuild-python-esdk-service-role", + "arn:aws:iam::${AWS::AccountId}:role/ToolsDevelopment" + ] + } + } + } + ] + } \ No newline at end of file diff --git a/codebuild/coverage/coverage.yml b/codebuild/coverage/coverage.yml index f82a3a982..51d8b0a6f 100644 --- a/codebuild/coverage/coverage.yml +++ b/codebuild/coverage/coverage.yml @@ -10,5 +10,5 @@ phases: python: latest build: commands: - - pip install tox + - pip install "tox < 4.0" - tox diff --git a/codebuild/coverage/coverage_mpl.yml b/codebuild/coverage/coverage_mpl.yml new file mode 100644 index 000000000..922705569 --- /dev/null +++ b/codebuild/coverage/coverage_mpl.yml @@ -0,0 +1,14 @@ +version: 0.2 + +env: + variables: + TOXENV: "mplcoverage-mpl" + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py310/awses_local.yml b/codebuild/py310/awses_local.yml index a60eba6d2..df2fcf318 100644 --- a/codebuild/py310/awses_local.yml +++ b/codebuild/py310/awses_local.yml @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.10 build: commands: - - pyenv install 3.10.0 - - pyenv local 3.10.0 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py310/decrypt_dafny_esdk_vectors.yml b/codebuild/py310/decrypt_dafny_esdk_vectors.yml new file mode 100644 index 000000000..019a9adf6 --- /dev/null +++ b/codebuild/py310/decrypt_dafny_esdk_vectors.yml @@ -0,0 +1,58 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py310-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.10 + pre_build: + commands: + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json diff --git a/codebuild/py310/decrypt_masterkey_with_js.yml b/codebuild/py310/decrypt_masterkey_with_js.yml new file mode 100644 index 000000000..fdfb2363c --- /dev/null +++ b/codebuild/py310/decrypt_masterkey_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.10 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/310_masterkey.zip 310_masterkey.zip + # Repackage zip in expected format + - unzip 310_masterkey.zip + - cd 310_masterkey + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py310/decrypt_masterkey_with_masterkey.yml b/codebuild/py310/decrypt_masterkey_with_masterkey.yml new file mode 100644 index 000000000..577e81b9a --- /dev/null +++ b/codebuild/py310/decrypt_masterkey_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py310-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.10 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/310_masterkey.zip 310_masterkey.zip + - unzip 310_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../310_masterkey/manifest.json \ No newline at end of file diff --git a/codebuild/py310/decrypt_net_401_vectors.yml b/codebuild/py310/decrypt_net_401_vectors.yml new file mode 100644 index 000000000..0d81b349e --- /dev/null +++ b/codebuild/py310/decrypt_net_401_vectors.yml @@ -0,0 +1,35 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py310-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.10 + pre_build: + commands: + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json diff --git a/codebuild/py310/encrypt_masterkey.yml b/codebuild/py310/encrypt_masterkey.yml new file mode 100644 index 000000000..9cd89fb8f --- /dev/null +++ b/codebuild/py310/encrypt_masterkey.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py310-full_encrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.10 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json diff --git a/codebuild/py310/examples.yml b/codebuild/py310/examples.yml index 59bb42499..b495a327c 100644 --- a/codebuild/py310/examples.yml +++ b/codebuild/py310/examples.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.10 build: commands: - - pyenv install 3.10.0 - - pyenv local 3.10.0 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py310/generate_decrypt_vectors_masterkey.yml b/codebuild/py310/generate_decrypt_vectors_masterkey.yml new file mode 100644 index 000000000..640fb72d6 --- /dev/null +++ b/codebuild/py310/generate_decrypt_vectors_masterkey.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py310-full_decrypt_generate" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.10 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 310_masterkey + - zip -r 310_masterkey.zip 310_masterkey + - aws s3 cp 310_masterkey.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/310_masterkey.zip diff --git a/codebuild/py310/integ.yml b/codebuild/py310/integ.yml index 3346a06a2..6b557e709 100644 --- a/codebuild/py310/integ.yml +++ b/codebuild/py310/integ.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.10 build: commands: - - pyenv install 3.10.0 - - pyenv local 3.10.0 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py37/awses_local.yml b/codebuild/py311/awses_local.yml similarity index 86% rename from codebuild/py37/awses_local.yml rename to codebuild/py311/awses_local.yml index 9e77d43f7..1b00712d5 100644 --- a/codebuild/py37/awses_local.yml +++ b/codebuild/py311/awses_local.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py37-awses_local" + TOXENV: "py311-awses_local" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.11 build: commands: - - pyenv install 3.7.12 - - pyenv local 3.7.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py311/awses_local_mpl.yml b/codebuild/py311/awses_local_mpl.yml new file mode 100644 index 000000000..c6a975df8 --- /dev/null +++ b/codebuild/py311/awses_local_mpl.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-awses_local-mpl" + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers + - tox diff --git a/codebuild/py311/decrypt_dafny_esdk_vectors_keyrings.yml b/codebuild/py311/decrypt_dafny_esdk_vectors_keyrings.yml new file mode 100644 index 000000000..384f24fed --- /dev/null +++ b/codebuild/py311/decrypt_dafny_esdk_vectors_keyrings.yml @@ -0,0 +1,61 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py311-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json \ + --keyrings diff --git a/codebuild/py311/decrypt_dafny_esdk_vectors_masterkey.yml b/codebuild/py311/decrypt_dafny_esdk_vectors_masterkey.yml new file mode 100644 index 000000000..1d42953d9 --- /dev/null +++ b/codebuild/py311/decrypt_dafny_esdk_vectors_masterkey.yml @@ -0,0 +1,58 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py311-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json diff --git a/codebuild/py311/decrypt_golden_manifest_with_keyrings.yml b/codebuild/py311/decrypt_golden_manifest_with_keyrings.yml new file mode 100644 index 000000000..c65816d69 --- /dev/null +++ b/codebuild/py311/decrypt_golden_manifest_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download "golden manifest" + - curl -L -o python-2.3.0.zip https://github.com/awslabs/aws-encryption-sdk-test-vectors/raw/master/vectors/awses-decrypt/python-2.3.0.zip + - unzip python-2.3.0.zip -d python-2.3.0 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../python-2.3.0/manifest.json \ + --keyrings diff --git a/codebuild/py311/decrypt_golden_manifest_with_masterkey.yml b/codebuild/py311/decrypt_golden_manifest_with_masterkey.yml new file mode 100644 index 000000000..3b71759d3 --- /dev/null +++ b/codebuild/py311/decrypt_golden_manifest_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Download "golden manifest" + - curl -L -o python-2.3.0.zip https://github.com/awslabs/aws-encryption-sdk-test-vectors/raw/master/vectors/awses-decrypt/python-2.3.0.zip + - unzip python-2.3.0.zip -d python-2.3.0 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../python-2.3.0/manifest.json diff --git a/codebuild/py311/decrypt_keyrings_with_js.yml b/codebuild/py311/decrypt_keyrings_with_js.yml new file mode 100644 index 000000000..578b83cab --- /dev/null +++ b/codebuild/py311/decrypt_keyrings_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_keyrings.zip 311_keyrings.zip + # Repackage zip in expected format + - unzip 311_keyrings.zip + - cd 311_keyrings + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py311/decrypt_keyrings_with_keyrings.yml b/codebuild/py311/decrypt_keyrings_with_keyrings.yml new file mode 100644 index 000000000..f202c9c69 --- /dev/null +++ b/codebuild/py311/decrypt_keyrings_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_keyrings.zip 311_keyrings.zip + - unzip 311_keyrings.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../311_keyrings/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py311/decrypt_keyrings_with_masterkey.yml b/codebuild/py311/decrypt_keyrings_with_masterkey.yml new file mode 100644 index 000000000..714882c54 --- /dev/null +++ b/codebuild/py311/decrypt_keyrings_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_keyrings.zip 311_keyrings.zip + - unzip 311_keyrings.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../311_keyrings/manifest.json \ No newline at end of file diff --git a/codebuild/py311/decrypt_masterkey_with_js.yml b/codebuild/py311/decrypt_masterkey_with_js.yml new file mode 100644 index 000000000..a73e93580 --- /dev/null +++ b/codebuild/py311/decrypt_masterkey_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_masterkey.zip 311_masterkey.zip + # Repackage zip in expected format + - unzip 311_masterkey.zip + - cd 311_masterkey + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py311/decrypt_masterkey_with_keyrings.yml b/codebuild/py311/decrypt_masterkey_with_keyrings.yml new file mode 100644 index 000000000..1542b1acb --- /dev/null +++ b/codebuild/py311/decrypt_masterkey_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_masterkey.zip 311_masterkey.zip + - unzip 311_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../311_masterkey/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py311/decrypt_masterkey_with_masterkey.yml b/codebuild/py311/decrypt_masterkey_with_masterkey.yml new file mode 100644 index 000000000..dd64d2dff --- /dev/null +++ b/codebuild/py311/decrypt_masterkey_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_masterkey.zip 311_masterkey.zip + - unzip 311_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../311_masterkey/manifest.json \ No newline at end of file diff --git a/codebuild/py311/decrypt_net_401_vectors_keyrings.yml b/codebuild/py311/decrypt_net_401_vectors_keyrings.yml new file mode 100644 index 000000000..0cae98c4d --- /dev/null +++ b/codebuild/py311/decrypt_net_401_vectors_keyrings.yml @@ -0,0 +1,38 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py311-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json \ + --keyrings diff --git a/codebuild/py311/decrypt_net_401_vectors_masterkey.yml b/codebuild/py311/decrypt_net_401_vectors_masterkey.yml new file mode 100644 index 000000000..157e732f8 --- /dev/null +++ b/codebuild/py311/decrypt_net_401_vectors_masterkey.yml @@ -0,0 +1,45 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py311-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.11 + pre_build: + commands: + # Assume Role to access non-prod resources + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-TestVectorResources") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json \ No newline at end of file diff --git a/codebuild/py311/encrypt_keyrings.yml b/codebuild/py311/encrypt_keyrings.yml new file mode 100644 index 000000000..8e315ca9f --- /dev/null +++ b/codebuild/py311/encrypt_keyrings.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_encrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py311/encrypt_masterkey.yml b/codebuild/py311/encrypt_masterkey.yml new file mode 100644 index 000000000..226e1586d --- /dev/null +++ b/codebuild/py311/encrypt_masterkey.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_encrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json diff --git a/codebuild/py37/examples.yml b/codebuild/py311/examples.yml similarity index 83% rename from codebuild/py37/examples.yml rename to codebuild/py311/examples.yml index 57d1626df..6efcd26d2 100644 --- a/codebuild/py37/examples.yml +++ b/codebuild/py311/examples.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py37-examples" + TOXENV: "py311-examples" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.11 build: commands: - - pyenv install 3.7.12 - - pyenv local 3.7.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py311/examples_mpl.yml b/codebuild/py311/examples_mpl.yml new file mode 100644 index 000000000..19a5dec05 --- /dev/null +++ b/codebuild/py311/examples_mpl.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + # No TOXENV. This runs multiple environments. + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + # Run non-MPL-specific tests with the MPL installed + - tox -e py311-examples-mpl + # Assume special role to access keystore + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py311ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run MPL-specific tests with special role + - tox -e py311-mplexamples-mpl + diff --git a/codebuild/py311/generate_decrypt_vectors_keyrings.yml b/codebuild/py311/generate_decrypt_vectors_keyrings.yml new file mode 100644 index 000000000..0c9fc8333 --- /dev/null +++ b/codebuild/py311/generate_decrypt_vectors_keyrings.yml @@ -0,0 +1,31 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt_generate-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 311_keyrings \ + --keyrings + - zip -r 311_keyrings.zip 311_keyrings + - aws s3 cp 311_keyrings.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_keyrings.zip diff --git a/codebuild/py311/generate_decrypt_vectors_masterkey.yml b/codebuild/py311/generate_decrypt_vectors_masterkey.yml new file mode 100644 index 000000000..84db3f176 --- /dev/null +++ b/codebuild/py311/generate_decrypt_vectors_masterkey.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-full_decrypt_generate" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 311_masterkey + - zip -r 311_masterkey.zip 311_masterkey + - aws s3 cp 311_masterkey.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/311_masterkey.zip diff --git a/codebuild/py37/integ.yml b/codebuild/py311/integ.yml similarity index 84% rename from codebuild/py37/integ.yml rename to codebuild/py311/integ.yml index 04d24c26d..9606bee12 100644 --- a/codebuild/py37/integ.yml +++ b/codebuild/py311/integ.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py37-integ" + TOXENV: "py311-integ" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.11 build: commands: - - pyenv install 3.7.12 - - pyenv local 3.7.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py311/integ_mpl.yml b/codebuild/py311/integ_mpl.yml new file mode 100644 index 000000000..694bc0850 --- /dev/null +++ b/codebuild/py311/integ_mpl.yml @@ -0,0 +1,23 @@ +version: 0.2 + +env: + variables: + TOXENV: "py311-integ-mpl" + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py311/performance_tests_mpl.yml b/codebuild/py311/performance_tests_mpl.yml new file mode 100644 index 000000000..2debb1185 --- /dev/null +++ b/codebuild/py311/performance_tests_mpl.yml @@ -0,0 +1,38 @@ +# Runs the performance tests for the MPL in an environment with the MPL installed +version: 0.2 + +env: + variables: + # No TOXENV. This runs multiple environments. + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.11 + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install --skip-existing 3.11.0 + - pyenv local 3.11.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + # Assume special role to access keystore + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py312ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run MPL-specific tests with special role + - cd performance_tests/ + - tox -e py311-performance_tests-mpl diff --git a/codebuild/py36/awses_local.yml b/codebuild/py312/awses_local.yml similarity index 86% rename from codebuild/py36/awses_local.yml rename to codebuild/py312/awses_local.yml index b68a7e434..844cc7993 100644 --- a/codebuild/py36/awses_local.yml +++ b/codebuild/py312/awses_local.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py36-awses_local" + TOXENV: "py312-awses_local" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.12 build: commands: - - pyenv install 3.6.15 - - pyenv local 3.6.15 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py312/awses_local_mpl.yml b/codebuild/py312/awses_local_mpl.yml new file mode 100644 index 000000000..ea4d4f229 --- /dev/null +++ b/codebuild/py312/awses_local_mpl.yml @@ -0,0 +1,35 @@ +# Runs the same tests as awses_local in an environment with the MPL installed. +# This asserts existing tests continue to pass with the MPL installed. +version: 0.2 + +env: + variables: + TOXENV: "py312-awses_local-mpl" + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install --skip-existing 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + - cd test_vector_handlers + - tox diff --git a/codebuild/py312/decrypt_dafny_esdk_vectors_keyrings.yml b/codebuild/py312/decrypt_dafny_esdk_vectors_keyrings.yml new file mode 100644 index 000000000..e20277d94 --- /dev/null +++ b/codebuild/py312/decrypt_dafny_esdk_vectors_keyrings.yml @@ -0,0 +1,61 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json \ + --keyrings diff --git a/codebuild/py312/decrypt_dafny_esdk_vectors_masterkey.yml b/codebuild/py312/decrypt_dafny_esdk_vectors_masterkey.yml new file mode 100644 index 000000000..50948e31c --- /dev/null +++ b/codebuild/py312/decrypt_dafny_esdk_vectors_masterkey.yml @@ -0,0 +1,58 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json diff --git a/codebuild/py312/decrypt_golden_manifest_with_keyrings.yml b/codebuild/py312/decrypt_golden_manifest_with_keyrings.yml new file mode 100644 index 000000000..c0442a10b --- /dev/null +++ b/codebuild/py312/decrypt_golden_manifest_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download "golden manifest" + - curl -L -o python-2.3.0.zip https://github.com/awslabs/aws-encryption-sdk-test-vectors/raw/master/vectors/awses-decrypt/python-2.3.0.zip + - unzip python-2.3.0.zip -d python-2.3.0 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../python-2.3.0/manifest.json \ + --keyrings diff --git a/codebuild/py312/decrypt_golden_manifest_with_masterkey.yml b/codebuild/py312/decrypt_golden_manifest_with_masterkey.yml new file mode 100644 index 000000000..0e2ecb287 --- /dev/null +++ b/codebuild/py312/decrypt_golden_manifest_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download "golden manifest" + - curl -L -o python-2.3.0.zip https://github.com/awslabs/aws-encryption-sdk-test-vectors/raw/master/vectors/awses-decrypt/python-2.3.0.zip + - unzip python-2.3.0.zip -d python-2.3.0 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../python-2.3.0/manifest.json diff --git a/codebuild/py312/decrypt_hkeyring_with_keyrings.yml b/codebuild/py312/decrypt_hkeyring_with_keyrings.yml new file mode 100644 index 000000000..844f4c464 --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_keyrings.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_hkeyring_reccmm_manifest/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py312/decrypt_hkeyring_with_masterkey.yml b/codebuild/py312/decrypt_hkeyring_with_masterkey.yml new file mode 100644 index 000000000..04def1a98 --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_masterkey.yml @@ -0,0 +1,31 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_hkeyring_reccmm_manifest/manifest.json diff --git a/codebuild/py312/decrypt_hkeyring_with_net.yml b/codebuild/py312/decrypt_hkeyring_with_net.yml new file mode 100644 index 000000000..e1816282b --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_net.yml @@ -0,0 +1,49 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + - export DAFNY_AWS_ESDK_TEST_VECTOR_MANIFEST_PATH="${PWD}/312_hkeyring_reccmm_manifest/manifest.json" + + # Clone SDK-Dafny repo to get test vectors runner source code and the Dafny version to use + - git clone --recurse-submodules https://github.com/aws/aws-encryption-sdk.git + # Download Dafny to build the test vector runner; get Dafny version from ESDK's project.properties file + - export dafnyVersion=$(grep '^dafnyVersion=' aws-encryption-sdk/project.properties | cut -d '=' -f 2) + - curl https://github.com/dafny-lang/dafny/releases/download/v$dafnyVersion/dafny-$dafnyVersion-x64-ubuntu-20.04.zip -L -o dafny.zip + - unzip -qq dafny.zip && rm dafny.zip + - export PATH="$PWD/dafny:$PATH" + + # Build MPL test vector runner from source + - cd aws-encryption-sdk/mpl/TestVectorsAwsCryptographicMaterialProviders/ + - make transpile_net + + # Change ESDK TestVectors project to reference the published .NET ESDK + - cd ../../AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectorLib + # CodeBuild seems to want to use ESDK-NET 4.0.0, which is not the most recent version... + # Pin to at least 4.1.0; this is the most recent version at time of writing. + # Hopefully CodeBuild will find more recent versions in the future + - sed -i 's|||g' AWSEncryptionSDKTestVectorLib.csproj + - cd ../TestVectors + + build: + commands: + - dotnet test --framework net6.0 \ No newline at end of file diff --git a/codebuild/py312/decrypt_keyrings_with_js.yml b/codebuild/py312/decrypt_keyrings_with_js.yml new file mode 100644 index 000000000..9b1ebc270 --- /dev/null +++ b/codebuild/py312/decrypt_keyrings_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_keyrings.zip 312_keyrings.zip + # Repackage zip in expected format + - unzip 312_keyrings.zip + - cd 312_keyrings + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py312/decrypt_keyrings_with_keyrings.yml b/codebuild/py312/decrypt_keyrings_with_keyrings.yml new file mode 100644 index 000000000..27f9f1b5e --- /dev/null +++ b/codebuild/py312/decrypt_keyrings_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_keyrings.zip 312_keyrings.zip + - unzip 312_keyrings.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_keyrings/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py312/decrypt_keyrings_with_masterkey.yml b/codebuild/py312/decrypt_keyrings_with_masterkey.yml new file mode 100644 index 000000000..bb06ba4a2 --- /dev/null +++ b/codebuild/py312/decrypt_keyrings_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_keyrings.zip 312_keyrings.zip + - unzip 312_keyrings.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_keyrings/manifest.json \ No newline at end of file diff --git a/codebuild/py312/decrypt_masterkey_with_js.yml b/codebuild/py312/decrypt_masterkey_with_js.yml new file mode 100644 index 000000000..7c57c3111 --- /dev/null +++ b/codebuild/py312/decrypt_masterkey_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_masterkey.zip 312_masterkey.zip + # Repackage zip in expected format + - unzip 312_masterkey.zip + - cd 312_masterkey + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py312/decrypt_masterkey_with_keyrings.yml b/codebuild/py312/decrypt_masterkey_with_keyrings.yml new file mode 100644 index 000000000..e22bd2ace --- /dev/null +++ b/codebuild/py312/decrypt_masterkey_with_keyrings.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_masterkey.zip 312_masterkey.zip + - unzip 312_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_masterkey/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py312/decrypt_masterkey_with_masterkey.yml b/codebuild/py312/decrypt_masterkey_with_masterkey.yml new file mode 100644 index 000000000..0529fd894 --- /dev/null +++ b/codebuild/py312/decrypt_masterkey_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_masterkey.zip 312_masterkey.zip + - unzip 312_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_masterkey/manifest.json \ No newline at end of file diff --git a/codebuild/py312/decrypt_net_401_vectors_keyrings.yml b/codebuild/py312/decrypt_net_401_vectors_keyrings.yml new file mode 100644 index 000000000..281023f53 --- /dev/null +++ b/codebuild/py312/decrypt_net_401_vectors_keyrings.yml @@ -0,0 +1,38 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json \ + --keyrings diff --git a/codebuild/py312/decrypt_net_401_vectors_masterkey.yml b/codebuild/py312/decrypt_net_401_vectors_masterkey.yml new file mode 100644 index 000000000..81daba522 --- /dev/null +++ b/codebuild/py312/decrypt_net_401_vectors_masterkey.yml @@ -0,0 +1,45 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py312-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Assume Role to access non-prod resources + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-TestVectorResources") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json \ No newline at end of file diff --git a/codebuild/py312/encrypt_keyrings.yml b/codebuild/py312/encrypt_keyrings.yml new file mode 100644 index 000000000..db12d6c9d --- /dev/null +++ b/codebuild/py312/encrypt_keyrings.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_encrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py312/encrypt_masterkey.yml b/codebuild/py312/encrypt_masterkey.yml new file mode 100644 index 000000000..940f336a2 --- /dev/null +++ b/codebuild/py312/encrypt_masterkey.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_encrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json diff --git a/codebuild/py36/examples.yml b/codebuild/py312/examples.yml similarity index 83% rename from codebuild/py36/examples.yml rename to codebuild/py312/examples.yml index 46af14902..855a8fcdb 100644 --- a/codebuild/py36/examples.yml +++ b/codebuild/py312/examples.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py36-examples" + TOXENV: "py312-examples" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.12 build: commands: - - pyenv install 3.6.15 - - pyenv local 3.6.15 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py312/examples_mpl.yml b/codebuild/py312/examples_mpl.yml new file mode 100644 index 000000000..bca624742 --- /dev/null +++ b/codebuild/py312/examples_mpl.yml @@ -0,0 +1,41 @@ +# Runs the same tests as examples in an environment with the MPL installed +# to assert existing tests continue to pass with the MPL installed. +# Then, run MPL-specific tests. +version: 0.2 + +env: + variables: + # No TOXENV. This runs multiple environments. + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install --skip-existing 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + # Run non-MPL-specific tests with the MPL installed + - tox -e py312-examples-mpl + # Assume special role to access keystore + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py312ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run MPL-specific tests with special role + - tox -e py312-mplexamples-mpl diff --git a/codebuild/py312/generate_decrypt_vectors_keyrings.yml b/codebuild/py312/generate_decrypt_vectors_keyrings.yml new file mode 100644 index 000000000..1760333f0 --- /dev/null +++ b/codebuild/py312/generate_decrypt_vectors_keyrings.yml @@ -0,0 +1,31 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt_generate-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 312_keyrings \ + --keyrings + - zip -r 312_keyrings.zip 312_keyrings + - aws s3 cp 312_keyrings.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_keyrings.zip diff --git a/codebuild/py312/generate_decrypt_vectors_masterkey.yml b/codebuild/py312/generate_decrypt_vectors_masterkey.yml new file mode 100644 index 000000000..1fadba985 --- /dev/null +++ b/codebuild/py312/generate_decrypt_vectors_masterkey.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt_generate" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 312_masterkey + - zip -r 312_masterkey.zip 312_masterkey + - aws s3 cp 312_masterkey.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_masterkey.zip diff --git a/codebuild/py312/generate_hkeyring_decrypt_vectors.yml b/codebuild/py312/generate_hkeyring_decrypt_vectors.yml new file mode 100644 index 000000000..15b4f485e --- /dev/null +++ b/codebuild/py312/generate_hkeyring_decrypt_vectors.yml @@ -0,0 +1,35 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt_generate-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + # Build Python MPL TestVector runner from source + - sh test_vector_handlers/scripts/install_mpl_test_vector_runner.sh + - pip install "tox < 4.0" + - cd test_vector_handlers/test/aws-crypto-tools-test-vector-framework + # Checkout WIP branch with manifest containing HKeyring and required EC CMM test cases + - git checkout lucmcdon/hierarchy-test-vectors + - git pull + - cd ../.. + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0007-hkeyring-reccmm-generate-manifest.json \ + --output 312_hkeyring_reccmm_manifest \ + --keyrings + - zip -r 312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest + - aws s3 cp 312_hkeyring_reccmm_manifest.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip diff --git a/codebuild/py36/integ.yml b/codebuild/py312/integ.yml similarity index 84% rename from codebuild/py36/integ.yml rename to codebuild/py312/integ.yml index d55581c43..2ccad8913 100644 --- a/codebuild/py36/integ.yml +++ b/codebuild/py312/integ.yml @@ -2,7 +2,7 @@ version: 0.2 env: variables: - TOXENV: "py36-integ" + TOXENV: "py312-integ" AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.12 build: commands: - - pyenv install 3.6.15 - - pyenv local 3.6.15 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py312/integ_mpl.yml b/codebuild/py312/integ_mpl.yml new file mode 100644 index 000000000..7c6f046fe --- /dev/null +++ b/codebuild/py312/integ_mpl.yml @@ -0,0 +1,30 @@ +# Runs the same tests as integ in an environment with the MPL installed. +# This asserts existing tests continue to pass with the MPL installed. +version: 0.2 + +env: + variables: + TOXENV: "py312-integ-mpl" + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install --skip-existing 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + - tox diff --git a/codebuild/py312/performance_tests_mpl.yml b/codebuild/py312/performance_tests_mpl.yml new file mode 100644 index 000000000..97dbf359f --- /dev/null +++ b/codebuild/py312/performance_tests_mpl.yml @@ -0,0 +1,38 @@ +# Runs the performance tests for the MPL in an environment with the MPL installed +version: 0.2 + +env: + variables: + # No TOXENV. This runs multiple environments. + REGION: "us-west-2" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - + - pyenv install --skip-existing 3.12.0 + - pyenv local 3.12.0 + - pip install --upgrade pip + - pip install setuptools + - pip install "tox < 4.0" + # Assume special role to access keystore + - TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py312ExamplesMpl") + - export TMP_ROLE + - export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId') + - export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey') + - export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken') + - aws sts get-caller-identity + # Run MPL-specific tests with special role + - cd performance_tests/ + - tox -e py312-performance_tests-mpl diff --git a/codebuild/py38/awses_local.yml b/codebuild/py38/awses_local.yml index 4e20973d4..9876de247 100644 --- a/codebuild/py38/awses_local.yml +++ b/codebuild/py38/awses_local.yml @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.8 build: commands: - - pyenv install 3.8.12 - - pyenv local 3.8.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py38/decrypt_dafny_esdk_vectors.yml b/codebuild/py38/decrypt_dafny_esdk_vectors.yml new file mode 100644 index 000000000..3bc966126 --- /dev/null +++ b/codebuild/py38/decrypt_dafny_esdk_vectors.yml @@ -0,0 +1,58 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py38-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.8 + pre_build: + commands: + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json diff --git a/codebuild/py38/decrypt_masterkey_with_js.yml b/codebuild/py38/decrypt_masterkey_with_js.yml new file mode 100644 index 000000000..953e8818a --- /dev/null +++ b/codebuild/py38/decrypt_masterkey_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.8 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/38_masterkey.zip 38_masterkey.zip + # Repackage zip in expected format + - unzip 38_masterkey.zip + - cd 38_masterkey + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py38/decrypt_masterkey_with_masterkey.yml b/codebuild/py38/decrypt_masterkey_with_masterkey.yml new file mode 100644 index 000000000..6b32dcf15 --- /dev/null +++ b/codebuild/py38/decrypt_masterkey_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py38-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.8 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/38_masterkey.zip 38_masterkey.zip + - unzip 38_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../38_masterkey/manifest.json \ No newline at end of file diff --git a/codebuild/py38/decrypt_net_401_vectors.yml b/codebuild/py38/decrypt_net_401_vectors.yml new file mode 100644 index 000000000..91163f14d --- /dev/null +++ b/codebuild/py38/decrypt_net_401_vectors.yml @@ -0,0 +1,35 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py38-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.8 + pre_build: + commands: + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json diff --git a/codebuild/py38/encrypt_masterkey.yml b/codebuild/py38/encrypt_masterkey.yml new file mode 100644 index 000000000..b05396cc2 --- /dev/null +++ b/codebuild/py38/encrypt_masterkey.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py38-full_encrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.8 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json diff --git a/codebuild/py38/examples.yml b/codebuild/py38/examples.yml index 1a8f4f826..4dff71d65 100644 --- a/codebuild/py38/examples.yml +++ b/codebuild/py38/examples.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.8 build: commands: - - pyenv install 3.8.12 - - pyenv local 3.8.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py38/generate_decrypt_vectors_masterkey.yml b/codebuild/py38/generate_decrypt_vectors_masterkey.yml new file mode 100644 index 000000000..8705ef57c --- /dev/null +++ b/codebuild/py38/generate_decrypt_vectors_masterkey.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py38-full_decrypt_generate" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.8 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 38_masterkey + - zip -r 38_masterkey.zip 38_masterkey + - aws s3 cp 38_masterkey.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/38_masterkey.zip diff --git a/codebuild/py38/integ.yml b/codebuild/py38/integ.yml index 28eae25fc..8b7acafe1 100644 --- a/codebuild/py38/integ.yml +++ b/codebuild/py38/integ.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.8 build: commands: - - pyenv install 3.8.12 - - pyenv local 3.8.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py39/awses_1.7.1.yml b/codebuild/py39/awses_1.7.1.yml index e261b5e4d..22267ad8f 100644 --- a/codebuild/py39/awses_1.7.1.yml +++ b/codebuild/py39/awses_1.7.1.yml @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.9 build: commands: - - pyenv install 3.9.7 - - pyenv local 3.9.7 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py39/awses_2.0.0.yml b/codebuild/py39/awses_2.0.0.yml index b4a6654d5..d35e7e98c 100644 --- a/codebuild/py39/awses_2.0.0.yml +++ b/codebuild/py39/awses_2.0.0.yml @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.9 build: commands: - - pyenv install 3.9.7 - - pyenv local 3.9.7 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py39/awses_latest.yml b/codebuild/py39/awses_latest.yml index ac70cede8..719ab2238 100644 --- a/codebuild/py39/awses_latest.yml +++ b/codebuild/py39/awses_latest.yml @@ -17,11 +17,9 @@ env: phases: install: runtime-versions: - python: latest + python: 3.9 build: commands: - - pyenv install 3.9.7 - - pyenv local 3.9.7 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - cd test_vector_handlers - tox diff --git a/codebuild/py39/awses_local.yml b/codebuild/py39/awses_local.yml new file mode 100644 index 000000000..e56a9ff45 --- /dev/null +++ b/codebuild/py39/awses_local.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py39-awses_local" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_API_DEPLOYMENT_ID: "xi1mwx3ttb" + AWS_ENCRYPTION_SDK_PYTHON_DECRYPT_ORACLE_REGION: "us-west-2" + +phases: + install: + runtime-versions: + python: 3.9 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - tox diff --git a/codebuild/py39/decrypt_dafny_esdk_vectors.yml b/codebuild/py39/decrypt_dafny_esdk_vectors.yml new file mode 100644 index 000000000..a22c4d079 --- /dev/null +++ b/codebuild/py39/decrypt_dafny_esdk_vectors.yml @@ -0,0 +1,58 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py39-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + git-credential-helper: yes + secrets-manager: + GITHUB_TOKEN: Github/lucasmcdonald3-fgpat:actions read + +phases: + install: + runtime-versions: + python: 3.9 + pre_build: + commands: + # Fetch test vectors from Dafny ESDK's most recent run + # (Assuming the first result is most recent; seems to be correct...) + - | + MOST_RECENT_RUN_ID=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs?branch=mainline&status=completed&page=1&exclude_pull_requests=true" \ + | jq 'first(.workflow_runs[] | select(.name=="Daily CI") | .id)') + - | + echo "DEBUG: Fetching artifact from run $MOST_RECENT_RUN_ID" + - | + MOST_RECENT_RUN_DOWNLOAD_URL=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/aws/aws-encryption-sdk/actions/runs/$MOST_RECENT_RUN_ID/artifacts?name=ubuntu-22.04_vector_artifact" \ + | jq '.artifacts[0].archive_download_url') + - | + echo "DEBUG: Fetching artifact at $MOST_RECENT_RUN_DOWNLOAD_URL" + - | + curl -L -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + $(echo $MOST_RECENT_RUN_DOWNLOAD_URL | tr -d '"') -o ubuntu-22.04_vector_artifact.zip + # This unzips to `net41.zip`. + - unzip ubuntu-22.04_vector_artifact + # This unzips to `net41/`. + - unzip net41.zip -d net41 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../net41/manifest.json diff --git a/codebuild/py39/decrypt_masterkey_with_js.yml b/codebuild/py39/decrypt_masterkey_with_js.yml new file mode 100644 index 000000000..53f6433f8 --- /dev/null +++ b/codebuild/py39/decrypt_masterkey_with_js.yml @@ -0,0 +1,34 @@ +version: 0.2 + +env: + variables: + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.9 + commands: + - n 16 + # Install the Javascript ESDK run test vectors + - npm install -g @aws-crypto/integration-node + + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/39_masterkey.zip 39_masterkey.zip + # Repackage zip in expected format + - unzip 39_masterkey.zip + - cd 39_masterkey + - zip -r vectors.zip . + build: + commands: + # Decrypt generated vectors with Javascript ESDK + - integration-node decrypt -v vectors.zip \ No newline at end of file diff --git a/codebuild/py39/decrypt_masterkey_with_masterkey.yml b/codebuild/py39/decrypt_masterkey_with_masterkey.yml new file mode 100644 index 000000000..fcd9d3220 --- /dev/null +++ b/codebuild/py39/decrypt_masterkey_with_masterkey.yml @@ -0,0 +1,30 @@ +version: 0.2 + +env: + variables: + TOXENV: "py39-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.9 + pre_build: + commands: + # Download previously generated vectors + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/39_masterkey.zip 39_masterkey.zip + - unzip 39_masterkey.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../39_masterkey/manifest.json \ No newline at end of file diff --git a/codebuild/py39/decrypt_net_401_vectors.yml b/codebuild/py39/decrypt_net_401_vectors.yml new file mode 100644 index 000000000..d8a9ff453 --- /dev/null +++ b/codebuild/py39/decrypt_net_401_vectors.yml @@ -0,0 +1,35 @@ +version: 0.2 +# Runs Only the ESDK-NET v4.0.1 Decryption Vectors, testing Required EC CMM + +env: + variables: + TOXENV: "py39-full_decrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.9 + pre_build: + commands: + # Fetch ESDK .NET v4.0.1 Test Vectors + - VECTOR_ZIP=$CODEBUILD_SRC_DIR/v4-Net-4.0.1.zip + - VECTORS_URL=https://github.com/aws/aws-encryption-sdk/raw/mainline/AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors/resources/v4-Net-4.0.1.zip + - curl -s --output $VECTOR_ZIP --location $VECTORS_URL + - UNZIPPED_VECTORS_DIR=$CODEBUILD_SRC_DIR/test_vector_handlers/net_401_vectors + - unzip $VECTOR_ZIP -d $UNZIPPED_VECTORS_DIR + build: + commands: + # NOTE: We need to pass the absolute path of the vectors + - pip install "tox < 4.0" + - cd $CODEBUILD_SRC_DIR/test_vector_handlers + - | + tox -- \ + --input $UNZIPPED_VECTORS_DIR/manifest.json diff --git a/codebuild/py39/encrypt_masterkey.yml b/codebuild/py39/encrypt_masterkey.yml new file mode 100644 index 000000000..3bf18fbde --- /dev/null +++ b/codebuild/py39/encrypt_masterkey.yml @@ -0,0 +1,25 @@ +version: 0.2 + +env: + variables: + TOXENV: "py39-full_encrypt" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.9 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0003-awses-message-encryption.v2.json diff --git a/codebuild/py39/examples.yml b/codebuild/py39/examples.yml index 9b1911024..3d1399251 100644 --- a/codebuild/py39/examples.yml +++ b/codebuild/py39/examples.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.9 build: commands: - - pyenv install 3.9.7 - - pyenv local 3.9.7 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/py39/generate_decrypt_vectors_masterkey.yml b/codebuild/py39/generate_decrypt_vectors_masterkey.yml new file mode 100644 index 000000000..eb57d915a --- /dev/null +++ b/codebuild/py39/generate_decrypt_vectors_masterkey.yml @@ -0,0 +1,28 @@ +version: 0.2 + +env: + variables: + TOXENV: "py39-full_decrypt_generate" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.9 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0006-awses-message-decryption-generation.v2.json \ + --output 39_masterkey + - zip -r 39_masterkey.zip 39_masterkey + - aws s3 cp 39_masterkey.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/39_masterkey.zip diff --git a/codebuild/py39/integ.yml b/codebuild/py39/integ.yml index c7452e37e..6dec85b07 100644 --- a/codebuild/py39/integ.yml +++ b/codebuild/py39/integ.yml @@ -15,10 +15,8 @@ env: phases: install: runtime-versions: - python: latest + python: 3.9 build: commands: - - pyenv install 3.9.7 - - pyenv local 3.9.7 - - pip install tox tox-pyenv + - pip install "tox < 4.0" - tox diff --git a/codebuild/release/prod-release.yml b/codebuild/release/prod-release.yml index aa985e361..df5afb2fc 100644 --- a/codebuild/release/prod-release.yml +++ b/codebuild/release/prod-release.yml @@ -4,13 +4,13 @@ env: variables: BRANCH: "master" secrets-manager: - TWINE_USERNAME: PyPiAdmin:username - TWINE_PASSWORD: PyPiAdmin:password + TWINE_USERNAME: PyPiAPIToken:username + TWINE_PASSWORD: PyPiAPIToken:password phases: install: commands: - - pip install tox + - pip install "tox < 4.0" - pip install --upgrade pip runtime-versions: python: latest diff --git a/codebuild/release/test-release.yml b/codebuild/release/test-release.yml index 6c0ce85c9..cab56a9fd 100644 --- a/codebuild/release/test-release.yml +++ b/codebuild/release/test-release.yml @@ -4,13 +4,13 @@ env: variables: BRANCH: "master" secrets-manager: - TWINE_USERNAME: TestPyPiCryptoTools:username - TWINE_PASSWORD: TestPyPiCryptoTools:password + TWINE_USERNAME: TestPyPiAPIToken:username + TWINE_PASSWORD: TestPyPiAPIToken:password phases: install: commands: - - pip install tox + - pip install "tox < 4.0" - pip install --upgrade pip runtime-versions: python: latest diff --git a/codebuild/release/validate.yml b/codebuild/release/validate.yml index 95dfd5cb2..a929e9154 100644 --- a/codebuild/release/validate.yml +++ b/codebuild/release/validate.yml @@ -3,7 +3,7 @@ version: 0.2 phases: install: commands: - - pip install tox + - pip install "tox < 4.0" runtime-versions: python: latest pre_build: @@ -11,9 +11,9 @@ phases: - git clone https://github.com/aws-samples/busy-engineers-document-bucket.git - cd busy-engineers-document-bucket/exercises/python/encryption-context-complete - sed -i "s/aws_encryption_sdk/aws_encryption_sdk==$VERSION/" requirements-dev.txt - - pyenv install 3.8.12 + - pyenv install --skip-existing 3.8.12 - pyenv local 3.8.12 - - pip install tox tox-pyenv + - pip install "tox < 4.0" build: commands: - NUM_RETRIES=3 diff --git a/decrypt_oracle/.chalice/buildspec.yaml b/decrypt_oracle/.chalice/buildspec.yaml index 657c5a4be..d7c256698 100644 --- a/decrypt_oracle/.chalice/buildspec.yaml +++ b/decrypt_oracle/.chalice/buildspec.yaml @@ -2,7 +2,7 @@ version: 0.2 phases: install: commands: - - pip install tox + - pip install "tox < 4.0" build: commands: - cd decrypt_oracle diff --git a/decrypt_oracle/.chalice/pipeline.py b/decrypt_oracle/.chalice/pipeline.py index 45e050a2d..de8ee1b9c 100644 --- a/decrypt_oracle/.chalice/pipeline.py +++ b/decrypt_oracle/.chalice/pipeline.py @@ -23,10 +23,10 @@ APPLICATION_NAME = "AwsEncryptionSdkDecryptOraclePython" PIPELINE_STACK_NAME = "{}DeployPipeline".format(APPLICATION_NAME) -CODEBUILD_IMAGE = "aws/codebuild/python:3.6.5" +CODEBUILD_IMAGE = "aws/codebuild/standard:5.0" BUILDSPEC = "decrypt_oracle/.chalice/buildspec.yaml" GITHUB_REPO = "aws-encryption-sdk-python" -WAITER_CONFIG = dict(Delay=10) +WAITER_CONFIG = {"Delay": 10} _LOGGER = logging.getLogger("Decrypt Oracle Build Pipeline Deployer") @@ -35,7 +35,7 @@ class AllowEverywhere(AWS.Statement): def __init__(self, *args, **kwargs): """Set up override values.""" - my_kwargs = dict(Effect=AWS.Allow, Resource=["*"]) + my_kwargs = {"Effect": AWS.Allow, "Resource": ["*"]} my_kwargs.update(kwargs) super().__init__(*args, **my_kwargs) @@ -145,6 +145,7 @@ def _cloudformation_role() -> iam.Role: ) +# pylint: disable=too-many-positional-arguments def _pipeline( pipeline_role: iam.Role, cfn_role: iam.Role, @@ -167,13 +168,13 @@ def _pipeline( ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="ThirdParty", Version="1", Provider="GitHub" ), - Configuration=dict( - Owner=github_owner, - Repo=GITHUB_REPO, - OAuthToken=Ref(github_access_token), - Branch=github_branch, - PollForSourceChanges=True, - ), + Configuration={ + "Owner": github_owner, + "Repo": GITHUB_REPO, + "OAuthToken": Ref(github_access_token), + "Branch": github_branch, + "PollForSourceChanges": True, + }, ) ], ) @@ -191,7 +192,7 @@ def _pipeline( ActionTypeId=codepipeline.ActionTypeId( Category="Build", Owner="AWS", Version="1", Provider="CodeBuild" ), - Configuration=dict(ProjectName=Ref(codebuild_builder)), + Configuration={"ProjectName": Ref(codebuild_builder)}, ) ], ) @@ -200,25 +201,25 @@ def _pipeline( RunOrder="1", ActionTypeId=codepipeline.ActionTypeId(Category="Deploy", Owner="AWS", Version="1", Provider="CloudFormation"), InputArtifacts=[codepipeline.InputArtifacts(Name=_compiled_cfn_template)], - Configuration=dict( - ActionMode="CHANGE_SET_REPLACE", - ChangeSetName=_changeset_name, - RoleArn=GetAtt(cfn_role, "Arn"), - Capabilities="CAPABILITY_IAM", - StackName=_stack_name, - TemplatePath="{}::decrypt_oracle/transformed.yaml".format(_compiled_cfn_template), - ), + Configuration={ + "ActionMode": "CHANGE_SET_REPLACE", + "ChangeSetName": _changeset_name, + "RoleArn": GetAtt(cfn_role, "Arn"), + "Capabilities": "CAPABILITY_IAM", + "StackName": _stack_name, + "TemplatePath": "{}::decrypt_oracle/transformed.yaml".format(_compiled_cfn_template), + }, ) deploy_changeset = codepipeline.Actions( Name="Deploy", RunOrder="2", ActionTypeId=codepipeline.ActionTypeId(Category="Deploy", Owner="AWS", Version="1", Provider="CloudFormation"), - Configuration=dict( - ActionMode="CHANGE_SET_EXECUTE", - ChangeSetName=_changeset_name, - StackName=_stack_name, - OutputFileName="StackOutputs.json", - ), + Configuration={ + "ActionMode": "CHANGE_SET_EXECUTE", + "ChangeSetName": _changeset_name, + "StackName": _stack_name, + "OutputFileName": "StackOutputs.json", + }, OutputArtifacts=[codepipeline.OutputArtifacts(Name="AppDeploymentValues")], ) deploy = codepipeline.Stages(Name="Deploy", Actions=[stage_changeset, deploy_changeset]) @@ -272,8 +273,7 @@ def _stack_exists(cloudformation) -> bool: return False raise - else: - return True + return True def _update_existing_stack(cloudformation, template: Template, github_token: str) -> None: @@ -284,7 +284,7 @@ def _update_existing_stack(cloudformation, template: Template, github_token: str cloudformation.update_stack( StackName=PIPELINE_STACK_NAME, TemplateBody=template.to_json(), - Parameters=[dict(ParameterKey="GithubPersonalToken", ParameterValue=github_token)], + Parameters=[{"ParameterKey": "GithubPersonalToken", "ParameterValue": github_token}], Capabilities=["CAPABILITY_IAM"], ) _LOGGER.info("Waiting for stack update to complete...") @@ -301,7 +301,7 @@ def _deploy_new_stack(cloudformation, template: Template, github_token: str) -> cloudformation.create_stack( StackName=PIPELINE_STACK_NAME, TemplateBody=template.to_json(), - Parameters=[dict(ParameterKey="GithubPersonalToken", ParameterValue=github_token)], + Parameters=[{"ParameterKey": "GithubPersonalToken", "ParameterValue": github_token}], Capabilities=["CAPABILITY_IAM"], ) _LOGGER.info("Waiting for stack to deploy...") diff --git a/decrypt_oracle/app.py b/decrypt_oracle/app.py index 450fd25d0..a36b6db97 100644 --- a/decrypt_oracle/app.py +++ b/decrypt_oracle/app.py @@ -1,14 +1,4 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Shim to pull decryption oracle app into expected location for Chalice.""" from aws_encryption_sdk_decrypt_oracle.app import APP as app # noqa pylint: disable=unused-import diff --git a/decrypt_oracle/setup.py b/decrypt_oracle/setup.py index c56d64911..9908944ef 100644 --- a/decrypt_oracle/setup.py +++ b/decrypt_oracle/setup.py @@ -45,8 +45,6 @@ def get_requirements(): "Natural Language :: English", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", 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 6a8068ecc..1210013e3 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/__init__.py @@ -1,14 +1,4 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """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 b0d8a8d48..1294fe5d1 100644 --- a/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py +++ b/decrypt_oracle/src/aws_encryption_sdk_decrypt_oracle/app.py @@ -1,21 +1,12 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Decrypt Oracle powered by the AWS Encryption SDK for Python.""" import json import logging import os import aws_encryption_sdk +from aws_encryption_sdk.identifiers import CommitmentPolicy from aws_encryption_sdk.key_providers.kms import DiscoveryAwsKmsMasterKeyProvider from chalice import Chalice, Response @@ -59,7 +50,9 @@ def basic_decrypt() -> Response: APP.log.debug(APP.current_request.raw_body) try: - client = aws_encryption_sdk.EncryptionSDKClient() + # The decrypt oracle needs to be able to decrypt any message + # it does not encrypt messages for anyone. + client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) ciphertext = APP.current_request.raw_body plaintext, _header = client.decrypt(source=ciphertext, key_provider=_master_key_provider()) APP.log.debug("Plaintext:") 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 98889fd63..c384ec142 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 @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """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 da6c74cfc..cc2b5377f 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,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ 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 3eb90b3b9..2a90bc6e5 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,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Master key that provides null data keys.""" from typing import Dict, NoReturn, Text diff --git a/decrypt_oracle/src/pylintrc b/decrypt_oracle/src/pylintrc index 2e1e6336e..888ae1355 100644 --- a/decrypt_oracle/src/pylintrc +++ b/decrypt_oracle/src/pylintrc @@ -1,7 +1,6 @@ [MESSAGES CONTROL] # Disabling messages that we either don't care about for tests or are necessary to break for tests. disable = - bad-continuation, # we let black handle this ungrouped-imports, # we let isort handle this consider-using-f-string # disable until 2022-05-05; 6 months after 3.5 deprecation diff --git a/decrypt_oracle/test/__init__.py b/decrypt_oracle/test/__init__.py index 2add15ef3..d548f9b1f 100644 --- a/decrypt_oracle/test/__init__.py +++ b/decrypt_oracle/test/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Dummy stub to make linters work better.""" diff --git a/decrypt_oracle/test/integration/__init__.py b/decrypt_oracle/test/integration/__init__.py index 2add15ef3..d548f9b1f 100644 --- a/decrypt_oracle/test/integration/__init__.py +++ b/decrypt_oracle/test/integration/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """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 index c03b7f440..0989e5022 100644 --- a/decrypt_oracle/test/integration/integration_test_utils.py +++ b/decrypt_oracle/test/integration/integration_test_utils.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper utilities for use by integration tests.""" import base64 import json diff --git a/decrypt_oracle/test/integration/test_i_decrypt_oracle.py b/decrypt_oracle/test/integration/test_i_decrypt_oracle.py index 5863ecc0e..0ed733afb 100644 --- a/decrypt_oracle/test/integration/test_i_decrypt_oracle.py +++ b/decrypt_oracle/test/integration/test_i_decrypt_oracle.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Integration tests for deployed API.""" import pytest import requests diff --git a/decrypt_oracle/test/pylintrc b/decrypt_oracle/test/pylintrc index 3d4a895b2..7cbbfc2ff 100644 --- a/decrypt_oracle/test/pylintrc +++ b/decrypt_oracle/test/pylintrc @@ -5,7 +5,8 @@ disable = missing-docstring, # we don't write docstrings for tests bad-continuation, # we let black handle this ungrouped-imports, # we let isort handle this - consider-using-f-string # disable until 2022-05-05; 6 months after 3.5 deprecation + consider-using-f-string, # disable until 2022-05-05; 6 months after 3.5 deprecation + missing-timeout # disabling until we come up with a reasonable number [FORMAT] max-line-length = 120 diff --git a/decrypt_oracle/test/test_n_generate_test_vectors.py b/decrypt_oracle/test/test_n_generate_test_vectors.py index deb3f7c4d..6e35a0267 100644 --- a/decrypt_oracle/test/test_n_generate_test_vectors.py +++ b/decrypt_oracle/test/test_n_generate_test_vectors.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Generate test vectors for use in testing the decrypt oracle.""" import base64 import binascii diff --git a/decrypt_oracle/test/unit/__init__.py b/decrypt_oracle/test/unit/__init__.py index 2add15ef3..d548f9b1f 100644 --- a/decrypt_oracle/test/unit/__init__.py +++ b/decrypt_oracle/test/unit/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """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 index 2add15ef3..d548f9b1f 100644 --- a/decrypt_oracle/test/unit/key_providers/__init__.py +++ b/decrypt_oracle/test/unit/key_providers/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """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 index ebeaee198..161ef91a2 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_counting.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_counting.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.counting``.""" import pytest from aws_encryption_sdk_decrypt_oracle.key_providers.counting import CountingMasterKey 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 4c3a2e4d7..be3bb8b0c 100644 --- a/decrypt_oracle/test/unit/key_providers/test_u_null.py +++ b/decrypt_oracle/test/unit/key_providers/test_u_null.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test for ``aws_encryption_sdk_decrypt_oracle.key_providers.null``.""" import pytest from aws_encryption_sdk_decrypt_oracle.key_providers.null import NullMasterKey diff --git a/decrypt_oracle/tox.ini b/decrypt_oracle/tox.ini index b90d9f527..ce640bd32 100644 --- a/decrypt_oracle/tox.ini +++ b/decrypt_oracle/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{36,37,38,39}-{local,integ}, + py{39}-{local,integ}, bandit, doc8, readme, docs, {flake8,pylint}{,-tests}, # prone to false positives @@ -35,7 +35,7 @@ envlist = [testenv:generate-pipeline] -basepython = python3 +basepython = python3.9 skip_install = true deps = troposphere[policy] @@ -44,7 +44,7 @@ commands = python .chalice/pipeline.py {posargs} [testenv:chalice-prep] -basepython = python3.6 +basepython = python3.9 skip_install = true recreate = true deps = {[testenv:build]deps} @@ -59,7 +59,7 @@ commands = python {toxinidir}/.chalice/build-requirements.py [testenv:chalice] -basepython = python3.6 +basepython = python3.9 recreate = true deps = {[testenv:chalice-prep]deps} @@ -69,7 +69,7 @@ commands = chalice {posargs} [testenv:chalice-deploy] -basepython = python3.6 +basepython = python3.9 recreate = true deps = {[testenv:chalice]deps} @@ -141,17 +141,6 @@ commands = {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 @@ -167,7 +156,6 @@ commands = app.py \ setup.py \ .chalice/pipeline.py \ - #doc/conf.py \ {posargs} [testenv:flake8-tests] @@ -218,7 +206,6 @@ commands = src/aws_encryption_sdk_decrypt_oracle/ \ setup.py \ app.py \ - #doc/conf.py \ test/ \ .chalice/pipeline.py \ {posargs} @@ -276,7 +263,7 @@ basepython = python3 deps = sphinx doc8 -commands = doc8 doc/index.rst README.rst CHANGELOG.rst +commands = doc8 README.rst [testenv:readme] basepython = python3 @@ -319,21 +306,6 @@ 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 @@ -347,11 +319,9 @@ commands = python setup.py park basepython = python3 skip_install = true deps = - #{[testenv:docs]deps} wheel setuptools commands = - #{[testenv:docs]commands} python setup.py sdist bdist_wheel [testenv:test-release] diff --git a/dev_requirements/ci-requirements.txt b/dev_requirements/ci-requirements.txt index 9a41a70d3..4335988fd 100644 --- a/dev_requirements/ci-requirements.txt +++ b/dev_requirements/ci-requirements.txt @@ -1 +1,2 @@ -tox==3.24.5 +setuptools +tox==3.27.1 diff --git a/dev_requirements/doc-requirements.txt b/dev_requirements/doc-requirements.txt index 9364148e5..c1be99e9c 100644 --- a/dev_requirements/doc-requirements.txt +++ b/dev_requirements/doc-requirements.txt @@ -1,2 +1,2 @@ -sphinx==4.4.0 +sphinx==5.3.0 sphinx_rtd_theme==1.0.0 diff --git a/dev_requirements/linter-requirements.txt b/dev_requirements/linter-requirements.txt index 1ce748cb4..1295e522d 100644 --- a/dev_requirements/linter-requirements.txt +++ b/dev_requirements/linter-requirements.txt @@ -1,13 +1,13 @@ bandit==1.7.4 -black==22.3.0 +black==24.2.0 doc8==0.10.1 flake8==4.0.1 -flake8-bugbear==22.1.11 -flake8-docstrings==1.6.0 -flake8-print==4.0.0 -isort==5.10.1 +flake8-bugbear==22.9.11 +flake8-docstrings==1.7.0 +flake8-print==5.0.0 +isort==5.11.4 pyflakes==2.4.0 pylint==2.13.5 -readme_renderer==34.0 +readme_renderer==37.3 seed-isort-config==2.2.0 -vulture==2.3 +vulture==2.9.1 diff --git a/dev_requirements/release-requirements.txt b/dev_requirements/release-requirements.txt index 347169a9e..6dfce7f94 100644 --- a/dev_requirements/release-requirements.txt +++ b/dev_requirements/release-requirements.txt @@ -1,4 +1,4 @@ pypi-parker==0.1.2 -setuptools==62.0.0 -twine==4.0.1 -wheel==0.37.1 \ No newline at end of file +setuptools==70.0.0 +twine==5.1.1 +wheel==0.38.4 \ No newline at end of file diff --git a/dev_requirements/test-requirements.txt b/dev_requirements/test-requirements.txt index 0dbc5a42c..01d7a2e2b 100644 --- a/dev_requirements/test-requirements.txt +++ b/dev_requirements/test-requirements.txt @@ -1,4 +1,4 @@ mock==4.0.3 -pytest==7.0.1 -pytest-cov==3.0.0 +pytest==7.2.1 +pytest-cov==4.0.0 pytest-mock==3.6.1 diff --git a/examples/__init__.py b/examples/__init__.py index 9cef784c2..fa977e22f 100644 --- a/examples/__init__.py +++ b/examples/__init__.py @@ -1,13 +1,3 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Stub to allow relative imports of examples from tests.""" diff --git a/examples/src/__init__.py b/examples/src/__init__.py index e8fd618b1..120179eda 100644 --- a/examples/src/__init__.py +++ b/examples/src/__init__.py @@ -1,13 +1,3 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Stub module indicator to make linter configuration simpler.""" diff --git a/examples/src/aws_kms_discovery_keyring_example.py b/examples/src/aws_kms_discovery_keyring_example.py new file mode 100644 index 000000000..ba02b62a7 --- /dev/null +++ b/examples/src/aws_kms_discovery_keyring_example.py @@ -0,0 +1,199 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Discovery Keyring + +AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to + which the KMS key used for encryption belongs +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsDiscoveryKeyringInput, + CreateAwsKmsKeyringInput, + DiscoveryFilter, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str, + aws_account_id: str, + aws_region: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Keyring. + + Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for creating + the kms_keyring used for encryption + :type kms_key_id: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string + :param aws_region: AWS Region to use for the kms client + :type aws_region: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name=aws_region) + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + encrypt_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 5. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_kms_keyring, + encryption_context=encryption_context + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Now create a Discovery keyring to use for decryption. We'll add a discovery filter + # so that we limit the set of ciphertexts we are willing to decrypt to only ones + # created by KMS keys in our account and partition. + + discovery_keyring_input: CreateAwsKmsDiscoveryKeyringInput = CreateAwsKmsDiscoveryKeyringInput( + kms_client=kms_client, + discovery_filter=DiscoveryFilter( + account_ids=[aws_account_id], + partition="aws" + ) + ) + + discovery_keyring: IKeyring = mat_prov.create_aws_kms_discovery_keyring( + input=discovery_keyring_input + ) + + # 8. Decrypt your encrypted data using the discovery keyring. + # On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + # The header contains the Encrypted Data Keys (EDKs), which, if the EDK + # was encrypted by a KMS Keyring, includes the KMS Key ARN. + # The Discovery Keyring filters these EDKs for + # EDKs encrypted by Single Region OR Multi Region KMS Keys. + # If a Discovery Filter is present, these KMS Keys must belong + # to an AWS Account ID in the discovery filter's AccountIds and + # must be from the discovery filter's partition. + # Finally, KMS is called to decrypt each filtered EDK until an EDK is + # successfully decrypted. The resulting data key is used to decrypt the + # ciphertext's message. + # If all calls to KMS fail, the decryption fails. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=discovery_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # 10. Demonstrate that if a different discovery keyring (Bob's) doesn't have the correct + # AWS Account ID's, the decrypt will fail with an error message + # Note that this assumes Account ID used here ('888888888888') is different than the one used + # during encryption + discovery_keyring_input_bob: CreateAwsKmsDiscoveryKeyringInput = \ + CreateAwsKmsDiscoveryKeyringInput( + kms_client=kms_client, + discovery_filter=DiscoveryFilter( + account_ids=["888888888888"], + partition="aws" + ) + ) + + discovery_keyring_bob: IKeyring = mat_prov.create_aws_kms_discovery_keyring( + input=discovery_keyring_input_bob + ) + + # Decrypt the ciphertext using Bob's discovery keyring which doesn't contain the required + # Account ID's for the KMS keyring used for encryption. + # This should throw an AWSEncryptionSDKClientError exception + try: + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=discovery_keyring_bob, + # Verify that the encryption context in the result contains the + # encryption context supplied to the encrypt method + encryption_context=encryption_context, + ) + + raise AssertionError("Decrypt using discovery keyring with wrong AWS Account ID should" + + "raise AWSEncryptionSDKClientError") + except AWSEncryptionSDKClientError: + pass diff --git a/examples/src/aws_kms_discovery_multi_keyring_example.py b/examples/src/aws_kms_discovery_multi_keyring_example.py new file mode 100644 index 000000000..5dd85f99c --- /dev/null +++ b/examples/src/aws_kms_discovery_multi_keyring_example.py @@ -0,0 +1,163 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption +using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi +keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsDiscoveryMultiKeyringInput, + CreateAwsKmsKeyringInput, + DiscoveryFilter, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str, + aws_account_id: str, + aws_regions: list +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS Discovery Multi Keyring. + + Usage: encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_regions) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for creating + the kms_keyring used for encryption + :type kms_key_id: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string + :param aws_regions: List of AWS Regions to use for creating the discovery multi keyring + :type aws_regions: list[string] + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + encrypt_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 5. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_kms_keyring, + encryption_context=encryption_context + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter + # so that we limit the set of ciphertexts we are willing to decrypt to only ones + # created by KMS keys in our account and partition. + discovery_multi_keyring_input: CreateAwsKmsDiscoveryMultiKeyringInput = \ + CreateAwsKmsDiscoveryMultiKeyringInput( + regions=aws_regions, + discovery_filter=DiscoveryFilter( + account_ids=[aws_account_id], + partition="aws" + ) + ) + + # This is a Multi Keyring composed of Discovery Keyrings. + # There is a keyring for every region in `regions`. + # All the keyrings have the same Discovery Filter. + # Each keyring has its own KMS Client, which is created for the keyring's region. + discovery_multi_keyring: IKeyring = mat_prov.create_aws_kms_discovery_multi_keyring( + input=discovery_multi_keyring_input + ) + + # 8. On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + # The header contains the Encrypted Data Keys (EDKs), which, if the EDK + # was encrypted by a KMS Keyring, includes the KMS Key ARN. + # For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + # is successful. + # Since every member of the Multi Keyring is a Discovery Keyring: + # Each Keyring will filter the EDKs by the Discovery Filter + # For the filtered EDKs, the keyring will try to decrypt it with the keyring's client. + # All of this is done serially, until a success occurs or all keyrings have + # failed all (filtered) EDKs. + # KMS Discovery Keyrings will attempt to decrypt Multi Region Keys (MRKs) and regular KMS Keys. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=discovery_multi_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/aws_kms_keyring_example.py b/examples/src/aws_kms_keyring_example.py new file mode 100644 index 000000000..8590b35ef --- /dev/null +++ b/examples/src/aws_kms_keyring_example.py @@ -0,0 +1,109 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the KMS Keyring + +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS keyring. + + Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + # 5. Encrypt the data with the encryptionContext. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_keyring, + encryption_context=encryption_context + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/aws_kms_mrk_discovery_keyring_example.py b/examples/src/aws_kms_mrk_discovery_keyring_example.py new file mode 100644 index 000000000..a88e1ae20 --- /dev/null +++ b/examples/src/aws_kms_mrk_discovery_keyring_example.py @@ -0,0 +1,174 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS MRK (multi-region key) Discovery Keyring + +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsMrkDiscoveryKeyringInput, + CreateAwsKmsMrkKeyringInput, + DiscoveryFilter, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + mrk_key_id_encrypt: str, + aws_account_id: str, + mrk_encrypt_region: str, + mrk_replica_decrypt_region: str +): + """Demonstrate decryption using an AWS KMS MRK Discovery keyring. + + Since discovery keyrings cannot be used to encrypt, we use KMS MRK keyring for encryption + Usage: encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + aws_account_id, + mrk_encrypt_region, + mrk_replica_decrypt_region) + :param mrk_key_id_encrypt: KMS Key identifier for the KMS key located in your + default region, which you want to use for encryption of your data keys + :type mrk_key_id_encrypt: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string + :param mrk_encrypt_region: AWS Region for encryption of your data keys. This should + be the region of the mrk_key_id_encrypt. + :type mrk_encrypt_region: string + :param mrk_replica_decrypt_region: AWS Region for decryption of your data keys. + This example assumes you have already replicated your mrk_key_id_encrypt to the + region mrk_replica_decrypt_region. Therefore, this mrk_replica_decrypt_region should + be the region of the MRK replica. However, since we are using a discovery keyring, + we don't need to provide the replica MRK ID. + :type mrk_replica_decrypt_region: string + + For more information on KMS Key identifiers for multi-region keys, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create an MRK keyring without discovery mode. + + # Create a keyring that will encrypt your data, using a KMS MRK in the first region. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # Create a boto3 client for KMS in the first region. + encrypt_kms_client = boto3.client('kms', region_name=mrk_encrypt_region) + + encrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput( + kms_key_id=mrk_key_id_encrypt, + kms_client=encrypt_kms_client + ) + + encrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring( + input=encrypt_keyring_input + ) + + # 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6. Now create a Discovery keyring to use for decryption. + # In order to illustrate the MRK behavior of this keyring, we configure + # the keyring to use the second KMS region where the MRK (mrk_key_id_encrypt) is replicated to. + # This example assumes you have already replicated your key, but since we + # are using a discovery keyring, we don't need to provide the mrk replica key id + + # Create a boto3 client for KMS in the second region. + decrypt_kms_client = boto3.client('kms', region_name=mrk_replica_decrypt_region) + + decrypt_discovery_keyring_input: CreateAwsKmsMrkDiscoveryKeyringInput = \ + CreateAwsKmsMrkDiscoveryKeyringInput( + kms_client=decrypt_kms_client, + region=mrk_replica_decrypt_region, + discovery_filter=DiscoveryFilter( + account_ids=[aws_account_id], + partition="aws" + ) + ) + + decrypt_discovery_keyring: IKeyring = mat_prov.create_aws_kms_mrk_discovery_keyring( + input=decrypt_discovery_keyring_input + ) + + # 7. Decrypt your encrypted data using the discovery keyring. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=decrypt_discovery_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA diff --git a/examples/src/aws_kms_mrk_discovery_multi_keyring_example.py b/examples/src/aws_kms_mrk_discovery_multi_keyring_example.py new file mode 100644 index 000000000..26412e2b2 --- /dev/null +++ b/examples/src/aws_kms_mrk_discovery_multi_keyring_example.py @@ -0,0 +1,183 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS MRK (multi-region key) Discovery Multi Keyring + +AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings. + +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsMrkDiscoveryMultiKeyringInput, + CreateAwsKmsMrkKeyringInput, + DiscoveryFilter, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + mrk_key_id_encrypt: str, + mrk_encrypt_region: str, + aws_account_id: str, + aws_regions: list +): + """Demonstrate decryption using an AWS KMS MRK Discovery Multi keyring. + + Since discovery keyrings cannot be used to encrypt, we use KMS MRK keyring for encryption + Usage: encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + mrk_encrypt_region, + aws_account_id, + aws_regions) + :param mrk_key_id_encrypt: KMS Key identifier for the KMS key located in your + default region, which you want to use for encryption of your data keys + :type mrk_key_id_encrypt: string + :param mrk_encrypt_region: AWS Region for encryption of your data keys. This should + be the region of the mrk_key_id_encrypt + :type mrk_encrypt_region: string + :param aws_account_id: AWS Account ID to use in the discovery filter + :type aws_account_id: string + :param aws_regions: AWS Regions to use in the the discovery filter + :type aws_regions: list[string] + + For more information on KMS Key identifiers for multi-region keys, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create the keyring that determines how your data keys are protected. + # Although this example highlights Discovery keyrings, Discovery keyrings cannot + # be used to encrypt, so for encryption we create an MRK keyring without discovery mode. + + # Create a keyring that will encrypt your data, using a KMS MRK in the first region. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # Create a boto3 client for KMS in the first region. + encrypt_kms_client = boto3.client('kms', region_name=mrk_encrypt_region) + + encrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput( + kms_key_id=mrk_key_id_encrypt, + kms_client=encrypt_kms_client + ) + + encrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring( + input=encrypt_keyring_input + ) + + # 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6. Now create a MRK Discovery Multi Keyring to use for decryption. + # We'll add a discovery filter to limit the set of encrypted data keys + # we are willing to decrypt to only ones created by KMS keys in select + # accounts and the partition `aws`. + # MRK Discovery keyrings also filter encrypted data keys by the region + # the keyring is created with. + decrypt_discovery_multi_keyring_input: CreateAwsKmsMrkDiscoveryMultiKeyringInput = \ + CreateAwsKmsMrkDiscoveryMultiKeyringInput( + regions=aws_regions, + discovery_filter=DiscoveryFilter( + account_ids=[aws_account_id], + partition="aws" + ) + ) + + # This is a Multi Keyring composed of Discovery Keyrings. + # There is a keyring for every region in `regions`. + # All the keyrings have the same Discovery Filter. + # Each keyring has its own KMS Client, which is created for the keyring's region. + decrypt_discovery_keyring: IKeyring = mat_prov.create_aws_kms_mrk_discovery_multi_keyring( + input=decrypt_discovery_multi_keyring_input + ) + + # 7. Decrypt your encrypted data using the discovery multi keyring. + # On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + # The header contains the Encrypted Data Keys (EDKs), which, if the EDK + # was encrypted by a KMS Keyring, includes the KMS Key ARN. + # For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + # is successful. + # Since every member of the Multi Keyring is a Discovery Keyring: + # Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. + # For each filtered EDK, the keyring will attempt decryption with the keyring's client. + # All of this is done serially, until a success occurs or all keyrings have failed + # all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt + # Multi Region Keys (MRKs) and regular KMS Keys. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=decrypt_discovery_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA diff --git a/examples/src/aws_kms_mrk_keyring_example.py b/examples/src/aws_kms_mrk_keyring_example.py new file mode 100644 index 000000000..1cadfec45 --- /dev/null +++ b/examples/src/aws_kms_mrk_keyring_example.py @@ -0,0 +1,144 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS MRK (multi-region key) Keyring + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsMrkKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + mrk_key_id_encrypt: str, + mrk_replica_key_id_decrypt: str, + mrk_encrypt_region: str, + mrk_replica_decrypt_region: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS MRK keyring. + + Usage: encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + mrk_replica_key_id_decrypt, + mrk_encrypt_region, + mrk_replica_decrypt_region) + :param mrk_key_id_encrypt: KMS Key identifier for the KMS key located in your + default region, which you want to use for encryption of your data keys + :type mrk_key_id_encrypt: string + :param mrk_replica_key_id_decrypt: KMS Key identifier for the KMS key + that is a replica of the `mrk_key_id_encrypt` in a second region, which you + want to use for decryption of your data keys + :type mrk_replica_key_id_decrypt: string + :param mrk_encrypt_region: AWS Region for encryption of your data keys. This should + be the region of the mrk_key_id_encrypt. + :type mrk_encrypt_region: string + :param mrk_replica_decrypt_region: AWS Region for decryption of your data keys. This should + be the region of the mrk_replica_key_id_decrypt. + :type mrk_replica_decrypt_region: string + + For more information on KMS Key identifiers for multi-region keys, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create a keyring that will encrypt your data, using a KMS MRK in the first region. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # Create a boto3 client for KMS in the first region. + encrypt_kms_client = boto3.client('kms', region_name=mrk_encrypt_region) + + encrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput( + kms_key_id=mrk_key_id_encrypt, + kms_client=encrypt_kms_client + ) + + encrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring( + input=encrypt_keyring_input + ) + + # 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=encrypt_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6. Create a keyring that will decrypt your data, using the same KMS MRK replicated + # to the second region. This example assumes you have already replicated your key + + # Create a boto3 client for KMS in the second region. + decrypt_kms_client = boto3.client('kms', region_name=mrk_replica_decrypt_region) + + decrypt_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput( + kms_key_id=mrk_replica_key_id_decrypt, + kms_client=decrypt_kms_client + ) + + decrypt_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring( + input=decrypt_keyring_input + ) + + # 7. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=decrypt_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/aws_kms_mrk_multi_keyring_example.py b/examples/src/aws_kms_mrk_multi_keyring_example.py new file mode 100644 index 000000000..d9514d961 --- /dev/null +++ b/examples/src/aws_kms_mrk_multi_keyring_example.py @@ -0,0 +1,172 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS MRK Multi Keyring + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with AWS KMS MRK keys. +The KMS MRK multi-keyring consists of one or more individual keyrings of the +same or different type. The keys can either be regular KMS keys or MRKs. +The effect is like using several keyrings in a series. + +This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id +as a child key, and then encrypts a custom input EXAMPLE_DATA with an encryption context. +Either KMS Key individually is capable of decrypting data encrypted under this keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the + MRK (from the multi-keyring used for encryption) copied from the first region into + the second region +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsMrkKeyringInput, + CreateAwsKmsMrkMultiKeyringInput, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + mrk_key_id: str, + kms_key_id: str, + mrk_replica_key_id: str, + mrk_replica_decrypt_region: str +): + """Demonstrate an encrypt/decrypt cycle using a Multi-Keyring made + up of multiple AWS KMS MRK Keyrings + + Usage: encrypt_and_decrypt_with_keyring(mrk_key_id, + kms_key_id, + mrk_replica_key_id, + mrk_replica_decrypt_region) + :param mrk_key_id: KMS Key identifier for an AWS KMS multi-region key (MRK) located in your + default region + :type mrk_key_id: string + :param kms_key_id: KMS Key identifier for a KMS key, possibly located in a different region + than the MRK + :type kms_key_id: string + :param mrk_replica_key_id: KMS Key identifier for an MRK that is a replica of the + `mrk_key_id` in a second region. + :type mrk_replica_key_id: string + :param mrk_replica_decrypt_region: The second region where the MRK replica is located + :type mrk_replica_decrypt_region: string + + For more information on KMS Key identifiers for multi-region keys, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create an AwsKmsMrkMultiKeyring that protects your data under two different KMS Keys. + # The Keys can either be regular KMS keys or MRKs. + # Either KMS Key individually is capable of decrypting data encrypted under this keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_mrk_multi_keyring_input: CreateAwsKmsMrkMultiKeyringInput =\ + CreateAwsKmsMrkMultiKeyringInput( + generator=mrk_key_id, + kms_key_ids=[kms_key_id] + ) + + kms_mrk_multi_keyring: IKeyring = mat_prov.create_aws_kms_mrk_multi_keyring( + input=kms_mrk_multi_keyring_input + ) + + # 4. Encrypt the data with the encryptionContext using the kms_mrk_multi_keyring. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_mrk_multi_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6. Decrypt your encrypted data using the same AwsKmsMrkMultiKeyring you used on encrypt. + # It will decrypt the data using the generator key (in this case, the MRK), since that is + # the first available KMS key on the keyring that is capable of decrypting the data. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=kms_mrk_multi_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 7. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # Demonstrate that a single AwsKmsMrkKeyring configured with a replica of the MRK from the + # multi-keyring used to encrypt the data is also capable of decrypting the data. + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 8. Create a single AwsKmsMrkKeyring with the replica KMS MRK from the second region. + + # Create a boto3 client for KMS in the second region which is the region for mrk_replica_key_id. + second_region_kms_client = boto3.client('kms', region_name=mrk_replica_decrypt_region) + + second_region_mrk_keyring_input: CreateAwsKmsMrkKeyringInput = CreateAwsKmsMrkKeyringInput( + kms_key_id=mrk_replica_key_id, + kms_client=second_region_kms_client + ) + + second_region_mrk_keyring: IKeyring = mat_prov.create_aws_kms_mrk_keyring( + input=second_region_mrk_keyring_input + ) + + # 9. Decrypt your encrypted data using the second region AwsKmsMrkKeyring + plaintext_bytes_second_region, _ = client.decrypt( + source=ciphertext, + keyring=second_region_mrk_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_second_region == EXAMPLE_DATA + + # Not shown in this example: A KMS Keyring created with `kms_key_id` could also + # decrypt this message. diff --git a/examples/src/aws_kms_multi_keyring_example.py b/examples/src/aws_kms_multi_keyring_example.py new file mode 100644 index 000000000..69928ccd7 --- /dev/null +++ b/examples/src/aws_kms_multi_keyring_example.py @@ -0,0 +1,207 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, + and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and +another KMS keyring as a child keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput, CreateAwsKmsMultiKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + default_region_kms_key_id: str, + second_region_kms_key_id: str, + default_region: str, + second_region: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS Multi keyring. + The multi_keyring is created using a KMS keyring as generator keyring and another KMS keyring + as a child keyring. For this example, `default_region_kms_key_id` is the generator key id + for a KMS key located in your default region, and `second_region_kms_key_id` is the KMS key id + for a KMS Key located in some second region. + + Usage: encrypt_and_decrypt_with_keyring(default_region_kms_key_id, + second_region_kms_key_id, + default_region, + second_region) + :param default_region_kms_key_id: KMS Key identifier for the default region KMS key you want to + use as a generator keyring + :type default_region_kms_key_id: string + :param second_region_kms_key_id: KMS Key identifier for the second region KMS key you want to + use as a child keyring + :type second_region_kms_key_id: string + :param default_region: AWS Region for the default region KMS key + :type default_region: string + :param second_region: AWS Region for the second region KMS key + :type second_region: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. + # Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + kms_multi_keyring_input: CreateAwsKmsMultiKeyringInput = CreateAwsKmsMultiKeyringInput( + generator=default_region_kms_key_id, + kms_key_ids=[second_region_kms_key_id] + ) + + kms_multi_keyring: IKeyring = mat_prov.create_aws_kms_multi_keyring( + input=kms_multi_keyring_input + ) + + # 4. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_multi_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + plaintext_bytes_multi_keyring, _ = client.decrypt( + source=ciphertext, + keyring=kms_multi_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 6b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_multi_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # Because you used a multi_keyring on Encrypt, you can use either of the two + # kms keyrings individually to decrypt the data. + + # 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the + # `default_region_kms_key_id` directly. + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 7a. Create a boto3 client for KMS for the default region. + default_region_kms_client = boto3.client('kms', region_name=default_region) + + # 7b. Create KMS keyring + default_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=default_region_kms_key_id, + kms_client=default_region_kms_client + ) + + default_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=default_region_kms_keyring_input + ) + + # 7c. Decrypt your encrypted data using the default_region_kms_keyring. + plaintext_bytes_default_region_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=default_region_kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 7d. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_default_region_kms_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the + # `second_region_kms_key_id` directly. + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 8a. Create a boto3 client for KMS for the second region. + second_region_kms_client = boto3.client('kms', region_name=second_region) + + # 8b. Create KMS keyring + second_region_kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=second_region_kms_key_id, + kms_client=second_region_kms_client + ) + + second_region_kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=second_region_kms_keyring_input + ) + + # 8c. Decrypt your encrypted data using the second_region_kms_keyring. + plaintext_bytes_second_region_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=second_region_kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8d. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_second_region_kms_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/aws_kms_rsa_keyring_example.py b/examples/src/aws_kms_rsa_keyring_example.py new file mode 100644 index 000000000..4750660eb --- /dev/null +++ b/examples/src/aws_kms_rsa_keyring_example.py @@ -0,0 +1,115 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the AWS KMS RSA Keyring + +This example creates a KMS RSA Keyring and then encrypts a custom input +EXAMPLE_DATA with an encryption context. This example also includes some sanity checks for +demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +# For more information on how to use KMS keyrings, see +# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsRsaKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.identifiers import AlgorithmSuite + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_rsa_key_id: str, + public_key: str +): + """Demonstrate an encrypt/decrypt cycle using an AWS KMS RSA keyring. + + Usage: encrypt_and_decrypt_with_keyring(kms_rsa_key_id, public_key) + :param kms_rsa_key_id: KMS RSA Key identifier for the KMS RSA key you want to use + :type kms_rsa_key_id: string + :param public_key: public key you want to use + :type public_key: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create a KMS RSA keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # Create the AWS KMS RSA keyring input + # For more information on the allowed encryption algorithms, please see + # https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html#key-spec-rsa + keyring_input: CreateAwsKmsRsaKeyringInput = CreateAwsKmsRsaKeyringInput( + public_key=public_key, + kms_key_id=kms_rsa_key_id, + encryption_algorithm="RSAES_OAEP_SHA_256", + kms_client=kms_client + ) + + kms_rsa_keyring: IKeyring = mat_prov.create_aws_kms_rsa_keyring( + input=keyring_input + ) + + # 5. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_rsa_keyring, + encryption_context=encryption_context, + algorithm=AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=kms_rsa_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/branch_key_id_supplier_example.py b/examples/src/branch_key_id_supplier_example.py new file mode 100644 index 000000000..a14e8eae3 --- /dev/null +++ b/examples/src/branch_key_id_supplier_example.py @@ -0,0 +1,41 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Example implementation of a branch key ID supplier.""" + +from aws_cryptographic_material_providers.mpl.models import GetBranchKeyIdInput, GetBranchKeyIdOutput +from aws_cryptographic_material_providers.mpl.references import IBranchKeyIdSupplier +from typing import Dict # noqa pylint: disable=wrong-import-order + + +class ExampleBranchKeyIdSupplier(IBranchKeyIdSupplier): + """Example implementation of a branch key ID supplier.""" + + branch_key_id_for_tenant_A: str + branch_key_id_for_tenant_B: str + + def __init__(self, tenant_1_id, tenant_2_id): + """Example constructor for a branch key ID supplier.""" + self.branch_key_id_for_tenant_A = tenant_1_id + self.branch_key_id_for_tenant_B = tenant_2_id + + def get_branch_key_id( + self, + param: GetBranchKeyIdInput + ) -> GetBranchKeyIdOutput: + """Returns branch key ID from the tenant ID in input's encryption context.""" + encryption_context: Dict[str, str] = param.encryption_context + + if "tenant" not in encryption_context: + raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.") + + tenant_key_id: str = encryption_context.get("tenant") + branch_key_id: str + + if tenant_key_id == "TenantA": + branch_key_id = self.branch_key_id_for_tenant_A + elif tenant_key_id == "TenantB": + branch_key_id = self.branch_key_id_for_tenant_B + else: + raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}") + + return GetBranchKeyIdOutput(branch_key_id=branch_key_id) diff --git a/examples/src/custom_mpl_cmm_example.py b/examples/src/custom_mpl_cmm_example.py new file mode 100644 index 000000000..b9a8f043f --- /dev/null +++ b/examples/src/custom_mpl_cmm_example.py @@ -0,0 +1,125 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Example to create a custom implementation of the MPL's ICryptographicMaterialsManager class and use it with the ESDK. + +The cryptographic materials manager (CMM) assembles the cryptographic materials that are used +to encrypt and decrypt data. The cryptographic materials include plaintext and encrypted data keys, +and an optional message signing key. + +Cryptographic Materials Managers (CMMs) are composable; if you just want to extend the behavior of +the default CMM, you can do this as demonstrated in this example. This is the easiest approach if +you are just adding a small check to the CMM methods, as in this example. + +If your use case calls for fundamentally changing aspects of the default CMM, you can also write +your own implementation without extending an existing CMM. The default CMM's implementation is a +good reference to use if you need to write a custom CMM implementation from scratch. +Custom implementations of CMMs must implement get_encryption_materials and decrypt_materials. + +For more information on a default implementation of a CMM, +please look at the default_cryptographic_materials_manager_example.py example. + +For more information on Cryptographic Material Managers, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#crypt-materials-manager +""" + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, + SignatureAlgorithmNone, +) +from aws_cryptographic_material_providers.mpl.references import ICryptographicMaterialsManager, IKeyring + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + + +# Custom CMM implementation using the MPL. +# This CMM only allows encryption/decryption using signing algorithms. +# It wraps an underlying CMM implementation and checks its materials +# to ensure that it is only using signed encryption algorithms. +class MPLCustomSigningSuiteOnlyCMM(ICryptographicMaterialsManager): + """Example custom crypto materials manager class.""" + + def __init__(self, keyring: IKeyring, cmm: ICryptographicMaterialsManager = None) -> None: + """Constructor for MPLCustomSigningSuiteOnlyCMM class.""" + if cmm is not None: + self.underlying_cmm = cmm + else: + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # Create a CryptographicMaterialsManager for encryption and decryption + cmm_input: CreateDefaultCryptographicMaterialsManagerInput = \ + CreateDefaultCryptographicMaterialsManagerInput( + keyring=keyring + ) + + self.underlying_cmm: ICryptographicMaterialsManager = \ + mat_prov.create_default_cryptographic_materials_manager( + input=cmm_input + ) + + def get_encryption_materials(self, param): + """Provides encryption materials appropriate for the request for the custom CMM. + + :param aws_cryptographic_material_providers.mpl.models.GetEncryptionMaterialsInput param: Input object to + provide to a crypto material manager's `get_encryption_materials` method. + :returns: Encryption materials output + :rtype: aws_cryptographic_material_providers.mpl.models.GetEncryptionMaterialsOutput + """ + materials = self.underlying_cmm.get_encryption_materials(param) + if isinstance(materials.encryption_materials.algorithm_suite.signature, SignatureAlgorithmNone): + raise ValueError( + "Algorithm provided to MPLCustomSigningSuiteOnlyCMM" + + " is not a supported signing algorithm: " + str(materials.encryption_materials.algorithm_suite) + ) + return materials + + def decrypt_materials(self, param): + """Provides decryption materials appropriate for the request for the custom CMM. + + :param aws_cryptographic_material_providers.mpl.models.DecryptMaterialsInput param: Input object to provide + to a crypto material manager's `decrypt_materials` method. + :returns: Decryption materials output + :rtype: aws_cryptographic_material_providers.mpl.models.GetDecryptionMaterialsOutput + """ + materials = self.underlying_cmm.decrypt_materials(param) + if isinstance(materials.decryption_materials.algorithm_suite.signature, SignatureAlgorithmNone): + raise ValueError( + "Algorithm provided to MPLCustomSigningSuiteOnlyCMM" + + " is not a supported signing algorithm: " + str(materials.decryption_materials.algorithm_suite) + ) + return materials + + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_decrypt_with_cmm( + cmm: ICryptographicMaterialsManager +): + """Encrypts and decrypts a string using a custom CMM. + + :param ICryptographicMaterialsManager cmm: CMM to use for encryption and decryption + """ + # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a + # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default. + client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + + # Encrypt the plaintext source data + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + materials_manager=cmm + ) + + # Decrypt the ciphertext + cycled_plaintext, _ = client.decrypt( + source=ciphertext, + materials_manager=cmm + ) + + # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext + assert cycled_plaintext == EXAMPLE_DATA diff --git a/examples/src/default_cryptographic_materials_manager_example.py b/examples/src/default_cryptographic_materials_manager_example.py new file mode 100644 index 000000000..e60b1cea7 --- /dev/null +++ b/examples/src/default_cryptographic_materials_manager_example.py @@ -0,0 +1,121 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the default Cryptographic Material Managers (CMM). + +The default cryptographic materials manager (CMM) assembles the cryptographic materials +that are used to encrypt and decrypt data. The cryptographic materials include +plaintext and encrypted data keys, and an optional message signing key. +This example creates a CMM and then encrypts a custom input EXAMPLE_DATA +with an encryption context. Creating a CMM involves taking a keyring as input, +and we use an AWS KMS Keyring for this example. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on Cryptographic Material Managers, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#crypt-materials-manager +""" +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsKeyringInput, + CreateDefaultCryptographicMaterialsManagerInput, +) +from aws_cryptographic_material_providers.mpl.references import ICryptographicMaterialsManager, IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_default_cmm( + kms_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using default Cryptographic Material Managers. + + Usage: encrypt_and_decrypt_with_default_cmm(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create a KMS keyring to use with the CryptographicMaterialsManager + kms_client = boto3.client('kms', region_name="us-west-2") + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + # 4. Create a CryptographicMaterialsManager for encryption and decryption + cmm_input: CreateDefaultCryptographicMaterialsManagerInput = \ + CreateDefaultCryptographicMaterialsManagerInput( + keyring=kms_keyring + ) + + cmm: ICryptographicMaterialsManager = mat_prov.create_default_cryptographic_materials_manager( + input=cmm_input + ) + + # 5. Encrypt the data with the encryptionContext. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + materials_manager=cmm, + encryption_context=encryption_context + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Decrypt your encrypted data using the same cmm you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + materials_manager=cmm, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/file_streaming_example.py b/examples/src/file_streaming_example.py new file mode 100644 index 000000000..56953f667 --- /dev/null +++ b/examples/src/file_streaming_example.py @@ -0,0 +1,139 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example demonstrates file streaming for encryption and decryption. + +File streaming is useful when the plaintext or ciphertext file/data is too large to load into +memory. Therefore, the AWS Encryption SDK allows users to stream the data, instead of loading it +all at once in memory. In this example, we demonstrate file streaming for encryption and decryption +using a Raw AES keyring. However, you can use any keyring with streaming. + +This example creates a Raw AES Keyring and then encrypts an input stream from the file +`plaintext_filename` with an encryption context to an output (encrypted) file `ciphertext_filename`. +It then decrypts the ciphertext from `ciphertext_filename` to a new file `decrypted_filename`. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + +See raw_aes_keyring_example.py in the same directory for another raw AES keyring example +in the AWS Encryption SDK for Python. +""" +import filecmp +import secrets + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + + +def encrypt_and_decrypt_with_keyring( + plaintext_filename: str, + ciphertext_filename: str, + decrypted_filename: str +): + """Demonstrate a streaming encrypt/decrypt cycle. + + Usage: encrypt_and_decrypt_with_keyring(plaintext_filename + ciphertext_filename + decrypted_filename) + :param plaintext_filename: filename of the plaintext data + :type plaintext_filename: string + :param ciphertext_filename: filename of the ciphertext data + :type ciphertext_filename: string + :param decrypted_filename: filename of the decrypted data + :type decrypted_filename: string + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. The key namespace and key name are defined by you. + # and are used by the Raw AES keyring to determine + # whether it should attempt to decrypt an encrypted data key. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Generate a 256-bit AES key to use with your keyring. + # In practice, you should get this key from a secure key management system such as an HSM. + + # Here, the input to secrets.token_bytes() = 32 bytes = 256 bits + static_key = secrets.token_bytes(32) + + # 5. Create a Raw AES keyring + # We choose to use a raw AES keyring, but any keyring can be used with streaming. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + # 6. Encrypt the data stream with the encryptionContext + with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file: + with client.stream( + mode='e', + source=pt_file, + keyring=raw_aes_keyring, + encryption_context=encryption_context + ) as encryptor: + for chunk in encryptor: + ct_file.write(chunk) + + # 7. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert not filecmp.cmp(plaintext_filename, ciphertext_filename), \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 8. Decrypt your encrypted data stream using the same keyring you used on encrypt. + with open(ciphertext_filename, 'rb') as ct_file, open(decrypted_filename, 'wb') as pt_file: + with client.stream( + mode='d', + source=ct_file, + keyring=raw_aes_keyring, + encryption_context=encryption_context + ) as decryptor: + for chunk in decryptor: + pt_file.write(chunk) + + # 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert filecmp.cmp(plaintext_filename, decrypted_filename), \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/hierarchical_keyring_example.py b/examples/src/hierarchical_keyring_example.py new file mode 100644 index 000000000..a325491e3 --- /dev/null +++ b/examples/src/hierarchical_keyring_example.py @@ -0,0 +1,242 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" +keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these +branch keys are themselves protected by a KMS Key. + +Establishing a key hierarchy like this has two benefits: +First, by caching the branch key material, and only calling KMS to re-establish authentication +regularly according to your configured TTL, you limit how often you need to call KMS to protect +your data. This is a performance security tradeoff, where your authentication, audit, and logging +from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail +cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has +Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, +the benefit is that you no longer have to make a network call to KMS for every encrypt or +decrypt. + +Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a +multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect +the tenant's data. You can either statically configure a single branch key to ensure you are +restricting access to a single tenant, or you can implement an interface that selects the Branch +Key based on the Encryption Context. + +This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to +encrypt and decrypt data for two separate tenants. + +This example requires access to the DDB Table where you are storing the Branch Keys. This +table must be configured with the following primary key configuration: - Partition key is named +"partition_key" with type (S) - Sort key is named "sort_key" with type (S). + +This example also requires using a KMS Key. You need the following access on this key: - +GenerateDataKeyWithoutPlaintext - Decrypt +""" + +import boto3 +# Ignore missing MPL for pylint, but the MPL is required for this example +# noqa pylint: disable=import-error +from aws_cryptographic_material_providers.keystore import KeyStore +from aws_cryptographic_material_providers.keystore.config import KeyStoreConfig +from aws_cryptographic_material_providers.keystore.models import CreateKeyInput, KMSConfigurationKmsKeyArn +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, + DefaultCache, +) +from aws_cryptographic_material_providers.mpl.references import IBranchKeyIdSupplier, IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +from .branch_key_id_supplier_example import ExampleBranchKeyIdSupplier + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + key_store_table_name: str, + logical_key_store_name: str, + kms_key_id: str +): + """Creates a hierarchical keyring using the provided resources, then encrypts and decrypts a string with it.""" + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create boto3 clients for DynamoDB and KMS. + ddb_client = boto3.client('dynamodb', region_name="us-west-2") + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Configure your KeyStore resource. + # This SHOULD be the same configuration that you used + # to initially create and populate your KeyStore. + keystore: KeyStore = KeyStore( + config=KeyStoreConfig( + ddb_client=ddb_client, + ddb_table_name=key_store_table_name, + logical_key_store_name=logical_key_store_name, + kms_client=kms_client, + kms_configuration=KMSConfigurationKmsKeyArn( + value=kms_key_id + ), + ) + ) + + # 4. Call CreateKey to create two new active branch keys + branch_key_id_a: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier + branch_key_id_b: str = keystore.create_key(input=CreateKeyInput()).branch_key_identifier + + # 5. Create a branch key supplier that maps the branch key id to a more readable format + branch_key_id_supplier: IBranchKeyIdSupplier = ExampleBranchKeyIdSupplier( + tenant_1_id=branch_key_id_a, + tenant_2_id=branch_key_id_b, + ) + + # 6. Create the Hierarchical Keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id_supplier=branch_key_id_supplier, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input + ) + + # 7. Create encryption context for both tenants. + # The Branch Key Id supplier uses the encryption context to determine which branch key id will + # be used to encrypt data. + + # Create encryption context for TenantA + encryption_context_a: Dict[str, str] = { + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # Create encryption context for TenantB + encryption_context_b: Dict[str, str] = { + "tenant": "TenantB", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 8. Encrypt the data with encryptionContextA & encryptionContextB + ciphertext_a, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=hierarchical_keyring, + encryption_context=encryption_context_a + ) + ciphertext_b, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=hierarchical_keyring, + encryption_context=encryption_context_b + ) + + # 9. To attest that TenantKeyB cannot decrypt a message written by TenantKeyA, + # let's construct more restrictive hierarchical keyrings. + keyring_input_a: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=branch_key_id_a, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring_a: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input_a + ) + + keyring_input_b: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=branch_key_id_b, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring_b: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input_b + ) + + # 10. Demonstrate that data encrypted by one tenant's key + # cannot be decrypted with by a keyring specific to another tenant. + + # Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key + # This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes. + try: + client.decrypt( + source=ciphertext_a, + keyring=hierarchical_keyring_b, + # Verify that the encryption context in the result contains the + # encryption context supplied to the encrypt method + encryption_context=encryption_context_a, + ) + except AWSEncryptionSDKClientError: + pass + + # Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. + # This will fail and raise a AWSEncryptionSDKClientError, which we swallow ONLY for demonstration purposes. + try: + client.decrypt( + source=ciphertext_b, + keyring=hierarchical_keyring_a, + # Verify that the encryption context in the result contains the + # encryption context supplied to the encrypt method + encryption_context=encryption_context_b, + ) + except AWSEncryptionSDKClientError: + pass + + # 11. Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, + # and that the decrypted data matches the input data. + plaintext_bytes_a, _ = client.decrypt( + source=ciphertext_a, + keyring=hierarchical_keyring_a, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context_a, + ) + assert plaintext_bytes_a == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + plaintext_bytes_b, _ = client.decrypt( + source=ciphertext_b, + keyring=hierarchical_keyring_b, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context_b, + ) + assert plaintext_bytes_b == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/legacy/__init__.py b/examples/src/legacy/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/examples/src/legacy/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/src/basic_encryption.py b/examples/src/legacy/basic_encryption.py similarity index 79% rename from examples/src/basic_encryption.py rename to examples/src/legacy/basic_encryption.py index cfe8ac791..24c667db9 100644 --- a/examples/src/basic_encryption.py +++ b/examples/src/legacy/basic_encryption.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy diff --git a/examples/src/basic_file_encryption_with_multiple_providers.py b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py similarity index 91% rename from examples/src/basic_file_encryption_with_multiple_providers.py rename to examples/src/legacy/basic_file_encryption_with_multiple_providers.py index 6a1b7ccd1..f5eb743e5 100644 --- a/examples/src/basic_file_encryption_with_multiple_providers.py +++ b/examples/src/legacy/basic_file_encryption_with_multiple_providers.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing creation of a RawMasterKeyProvider, how to use multiple master key providers to encrypt, and demonstrating that each master key provider can then be used independently to decrypt the same encrypted message. diff --git a/examples/src/basic_file_encryption_with_raw_key_provider.py b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py similarity index 87% rename from examples/src/basic_file_encryption_with_raw_key_provider.py rename to examples/src/legacy/basic_file_encryption_with_raw_key_provider.py index 2d964c7b4..28b097f8b 100644 --- a/examples/src/basic_file_encryption_with_raw_key_provider.py +++ b/examples/src/legacy/basic_file_encryption_with_raw_key_provider.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing creation and use of a RawMasterKeyProvider.""" import filecmp import os diff --git a/examples/src/legacy/custom_cmm_example.py b/examples/src/legacy/custom_cmm_example.py new file mode 100644 index 000000000..07e8ca50b --- /dev/null +++ b/examples/src/legacy/custom_cmm_example.py @@ -0,0 +1,87 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Example to create a custom implementation of the native ESDK CryptoMaterialsManager class.""" + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy, StrictAwsKmsMasterKeyProvider +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager + + +# Custom CMM implementation. +# This CMM only allows encryption/decryption using signing algorithms. +# It wraps an underlying CMM implementation and checks its materials +# to ensure that it is only using signed encryption algorithms. +class CustomSigningSuiteOnlyCMM(CryptoMaterialsManager): + """Example custom crypto materials manager class.""" + + def __init__(self, master_key_provider: StrictAwsKmsMasterKeyProvider) -> None: + """Constructor for CustomSigningSuiteOnlyCMM class.""" + self.underlying_cmm = DefaultCryptoMaterialsManager(master_key_provider) + + def get_encryption_materials(self, request): + """Provides encryption materials appropriate for the request for the custom CMM. + + :param EncryptionMaterialsRequest request: Request object to provide to a + crypto material manager's `get_encryption_materials` method. + :returns: Encryption materials + :rtype: EncryptionMaterials + """ + materials = self.underlying_cmm.get_encryption_materials(request) + if not materials.algorithm.is_signing(): + raise ValueError( + "Algorithm provided to CustomSigningSuiteOnlyCMM" + + " is not a supported signing algorithm: " + materials.algorithm + ) + return materials + + def decrypt_materials(self, request): + """Provides decryption materials appropriate for the request for the custom CMM. + + :param DecryptionMaterialsRequest request: Request object to provide to a + crypto material manager's `decrypt_materials` method. + """ + if not request.algorithm.is_signing(): + raise ValueError( + "Algorithm provided to CustomSigningSuiteOnlyCMM" + + " is not a supported signing algorithm: " + request.algorithm + ) + return self.underlying_cmm.decrypt_materials(request) + + +def encrypt_decrypt_with_cmm( + cmm: CryptoMaterialsManager, + source_plaintext: str +): + """Encrypts and decrypts a string using a custom CMM. + + :param CryptoMaterialsManager cmm: CMM to use for encryption and decryption + :param bytes source_plaintext: Data to encrypt + """ + # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a + # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default. + client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + + # Encrypt the plaintext source data + ciphertext, encryptor_header = client.encrypt( + source=source_plaintext, + materials_manager=cmm + ) + + # Decrypt the ciphertext + cycled_plaintext, decrypted_header = client.decrypt( + source=ciphertext, + materials_manager=cmm + ) + + # Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext + assert cycled_plaintext == source_plaintext + + # Verify that the encryption context used in the decrypt operation includes all key pairs from + # the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + # + # In production, always use a meaningful encryption context. In this sample, we omit the + # encryption context (no key pairs). + assert all( + pair in decrypted_header.encryption_context.items() for pair in encryptor_header.encryption_context.items() + ) diff --git a/examples/src/legacy/custom_kms_client_for_kms_provider.py b/examples/src/legacy/custom_kms_client_for_kms_provider.py new file mode 100644 index 000000000..07107af1a --- /dev/null +++ b/examples/src/legacy/custom_kms_client_for_kms_provider.py @@ -0,0 +1,67 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Example showing how to customize the AWS KMS Client.""" +import boto3 +from botocore.config import Config + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + + +# Create a new class that extends the AWS KMS Provider you need to use +class CustomKMSClientMasterKeyProvider(aws_encryption_sdk.StrictAwsKmsMasterKeyProvider): + """Custom region-specific client which extends the StrictAwsKmsMasterKeyProvider""" + + # Override `add_regional_client` to use whatever configuration you need + def add_regional_client(self, region_name): + """Adds a regional client for the specified region if it does not already exist. + :param str region_name: AWS Region ID (ex: us-east-1) + """ + if region_name not in self._regional_clients: + session = boto3.session.Session(botocore_session=self.config.botocore_session) + client = session.client( + 'kms', + region_name=region_name, + # Add additional custom client configuration here + config=Config(connection_timeout=10).merge(self._user_agent_adding_config) + ) + self._register_client(client, region_name) + self._regional_clients[region_name] = client + + +# This is just an example of using the above master key provider +def encrypt_decrypt(key_arn, source_plaintext, botocore_session=None): + """Encrypts and then decrypts a string under one KMS customer master key (CMK). + + :param str key_arn: Amazon Resource Name (ARN) of the KMS CMK + :param bytes source_plaintext: Data to encrypt + :param botocore_session: existing botocore session instance + :type botocore_session: botocore.session.Session + """ + kwargs = dict(key_ids=[key_arn]) + + if botocore_session is not None: + kwargs["botocore_session"] = botocore_session + + # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a + # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default. + client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + + # Create the custom master key provider using the ARN of the key and the session (botocore_session) + kms_key_provider = CustomKMSClientMasterKeyProvider(**kwargs) + + # Encrypt the plaintext using the AWS Encryption SDK. It returns the encrypted message and the header. Note: in + # order for decrypt to succeed, the key_ids value must be the key ARN of the CMK. + ciphertext, encrypted_message_header = client.encrypt(source=source_plaintext, key_provider=kms_key_provider) + + # Decrypt the encrypted message using the AWS Encryption SDK. It returns the decrypted message and the header + plaintext, decrypted_message_header = client.decrypt(source=ciphertext, key_provider=kms_key_provider) + + # Check if the original message and the decrypted message are the same + assert source_plaintext == plaintext + + # Check if the headers of the encrypted message and decrypted message match + assert all( + pair in encrypted_message_header.encryption_context.items() + for pair in decrypted_message_header.encryption_context.items() + ) diff --git a/examples/src/data_key_caching_basic.py b/examples/src/legacy/data_key_caching_basic.py similarity index 77% rename from examples/src/data_key_caching_basic.py rename to examples/src/legacy/data_key_caching_basic.py index e67b35d0a..50567aa58 100644 --- a/examples/src/data_key_caching_basic.py +++ b/examples/src/legacy/data_key_caching_basic.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example of encryption with data key caching.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy diff --git a/examples/src/discovery_kms_provider.py b/examples/src/legacy/discovery_kms_provider.py similarity index 83% rename from examples/src/discovery_kms_provider.py rename to examples/src/legacy/discovery_kms_provider.py index 659d76ea6..e5b1683e0 100644 --- a/examples/src/discovery_kms_provider.py +++ b/examples/src/legacy/discovery_kms_provider.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing encryption of a value already in memory using one KMS CMK, then decryption of the ciphertext using a DiscoveryAwsKmsMasterKeyProvider. """ diff --git a/examples/src/mrk_aware_kms_provider.py b/examples/src/legacy/mrk_aware_kms_provider.py similarity index 87% rename from examples/src/mrk_aware_kms_provider.py rename to examples/src/legacy/mrk_aware_kms_provider.py index da04c7d62..8010872e1 100644 --- a/examples/src/mrk_aware_kms_provider.py +++ b/examples/src/legacy/mrk_aware_kms_provider.py @@ -1,15 +1,5 @@ -# Copyright 2021 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing encryption of a value already in memory using one KMS CMK, then decryption of the ciphertext using a DiscoveryAwsKmsMasterKeyProvider. """ diff --git a/examples/src/multiple_kms_cmk.py b/examples/src/legacy/multiple_kms_cmk.py similarity index 82% rename from examples/src/multiple_kms_cmk.py rename to examples/src/legacy/multiple_kms_cmk.py index 03eef88ae..ee6198819 100644 --- a/examples/src/multiple_kms_cmk.py +++ b/examples/src/legacy/multiple_kms_cmk.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory using one KMS CMK.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy diff --git a/examples/src/one_kms_cmk.py b/examples/src/legacy/one_kms_cmk.py similarity index 78% rename from examples/src/one_kms_cmk.py rename to examples/src/legacy/one_kms_cmk.py index cbd28c10a..c7268c6e2 100644 --- a/examples/src/one_kms_cmk.py +++ b/examples/src/legacy/one_kms_cmk.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory using one KMS CMK.""" import aws_encryption_sdk from aws_encryption_sdk import CommitmentPolicy diff --git a/examples/src/one_kms_cmk_streaming_data.py b/examples/src/legacy/one_kms_cmk_streaming_data.py similarity index 82% rename from examples/src/one_kms_cmk_streaming_data.py rename to examples/src/legacy/one_kms_cmk_streaming_data.py index f4e5ce476..c29b7c2a6 100644 --- a/examples/src/one_kms_cmk_streaming_data.py +++ b/examples/src/legacy/one_kms_cmk_streaming_data.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of streaming data in memory using one KMS CMK.""" import filecmp diff --git a/examples/src/one_kms_cmk_unsigned.py b/examples/src/legacy/one_kms_cmk_unsigned.py similarity index 80% rename from examples/src/one_kms_cmk_unsigned.py rename to examples/src/legacy/one_kms_cmk_unsigned.py index 9794202e6..7854fea30 100644 --- a/examples/src/one_kms_cmk_unsigned.py +++ b/examples/src/legacy/one_kms_cmk_unsigned.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing basic encryption and decryption of a value already in memory using one AWS KMS CMK with an unsigned algorithm. diff --git a/examples/src/legacy/pylintrc b/examples/src/legacy/pylintrc new file mode 100644 index 000000000..858fa4d96 --- /dev/null +++ b/examples/src/legacy/pylintrc @@ -0,0 +1,44 @@ +[MESSAGE CONTROL] +# Disabling messages that either we don't care about we intentionally break. +disable = + invalid-name, # we prefer long, descriptive, names for examples + bad-continuation, # we let black handle this + ungrouped-imports, # we let isort handle this + no-member, # breaks with attrs + no-self-use, # interesting to keep in mind for later refactoring, but not blocking + useless-object-inheritance, # we need to support Python 2, so no, not useless + duplicate-code, # some examples may be similar + too-few-public-methods, # does not allow value stores + too-many-locals, # examples may sometimes have more locals defined for clarity than would be appropriate in code + no-else-return, # we omit this on purpose for brevity where it would add no value + attribute-defined-outside-init, # breaks with attrs_post_init + abstract-method, # throws false positives on io.BaseIO grandchildren + redefined-outer-name, # we do this on purpose in multiple places + consider-using-f-string # disable until 2022-05-05; 6 months after 3.5 deprecation + +[BASIC] +# Allow function names up to 50 characters +function-rgx = [a-z_][a-z0-9_]{2,50}$ +# Allow method names up to 50 characters +method-rgx = [a-z_][a-z0-9_]{2,50}$ +# Allow class attribute names up to 50 characters +# Whitelist class attribute names: iv +class-attribute-rgx = (([A-Za-z_][A-Za-z0-9_]{2,50}|(__.*__))$)|(^iv$) +# Whitelist attribute names: iv +attr-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$) +# Whitelist argument names: iv, b +argument-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)|(^b$) +# Whitelist variable names: iv, b, _b, x, y, r, s +variable-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)|(^b$)|(^_b$)|(^x$)|(^y$)|(^r$)|(^s$) + +[VARIABLES] +additional-builtins = raw_input + +[DESIGN] +max-args = 10 + +[FORMAT] +max-line-length = 120 + +[REPORTS] +msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} diff --git a/examples/src/set_commitment.py b/examples/src/legacy/set_commitment.py similarity index 79% rename from examples/src/set_commitment.py rename to examples/src/legacy/set_commitment.py index 7d58461c9..d989f1a31 100644 --- a/examples/src/set_commitment.py +++ b/examples/src/legacy/set_commitment.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Example showing how to disable commitment. Note: This configuration should only be used as part of a migration from version 1.x to 2.x, or for advanced users diff --git a/examples/src/migration/README.rst b/examples/src/migration/README.rst new file mode 100644 index 000000000..41d3f5515 --- /dev/null +++ b/examples/src/migration/README.rst @@ -0,0 +1,19 @@ +################## +Migration Examples +################## + +The `Encryption SDK for Python`_ now uses the `AWS Cryptographic Material Providers Library`_. The MPL abstracts lower +level cryptographic materials management of encryption and decryption materials. + +This directory contains migration examples for: + +#. Moving to Keyrings from Master Key Providers: + * Migration example to AWS KMS keyring from AWS KMS Master Key Provider. + * Migration example to Raw AES keyring from Raw AES Master Key Provider. + * Migration example to Raw RSA keyring from Raw RSA Master Key Provider. + +#. Migration to newer versions of the ESDK (4.x+) from 1.x versions: + * Setting a 'CommitmentPolicy' during migration - If you have messages encrypted with 1.x versions of the ESDK (i.e. not using key commitment) and want to migrate to encrypt with key commitment using the keyring providers introduced in ESDK 4.x, this example will guide you on how to decrypt those messages using the new version of the ESDK. + +.. _AWS Cryptographic Material Providers Library: https://github.com/aws/aws-cryptographic-material-providers-library +.. _Encryption SDK for Python: https://github.com/aws/aws-encryption-sdk-python/tree/9c34aad60fc918c1a9186ec5215a451e8bfd0f65 \ No newline at end of file diff --git a/examples/src/migration/migration_aws_kms_key_example.py b/examples/src/migration/migration_aws_kms_key_example.py new file mode 100644 index 000000000..c46c6f1c9 --- /dev/null +++ b/examples/src/migration/migration_aws_kms_key_example.py @@ -0,0 +1,188 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the AWS KMS Keyring from AWS KMS master key provider (MKP) + +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and KMS MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both KMS keyring and KMS MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP should give the same plaintext result. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html +""" +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", +} + + +def create_keyring( + kms_key_id: str, + aws_region="us-west-2" +): + """Demonstrate how to create an AWS KMS keyring. + + Usage: create_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name=aws_region) + + # Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + return keyring + + +def create_key_provider( + kms_key_id: str +): + """Demonstrate how to create an AWS KMS master key provider. + + Usage: create_key_provider(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a KMS master key provider. + key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ + kms_key_id, + ]) + + return key_provider + + +def migration_aws_kms_key( + kms_key_id: str +): + """Demonstrate a migration example for moving to an AWS KMS keyring from AWS KMS MKP. + + Usage: migration_aws_kms_key(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a AWS KMS Keyring + aws_kms_keyring = create_keyring(kms_key_id=kms_key_id) + + # 1b. Create a AWS KMS Master Key Provider + aws_kms_master_key_provider = create_key_provider(kms_key_id=kms_key_id) + + # 2a. Encrypt EXAMPLE_DATA using AWS KMS Keyring + ciphertext_keyring, encrypted_header_keyring = client.encrypt( + source=EXAMPLE_DATA, + keyring=aws_kms_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using AWS KMS Master Key Provider + ciphertext_mkp, encrypted_header_mkp = client.encrypt( + source=EXAMPLE_DATA, + key_provider=aws_kms_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP should give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=aws_kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_keyring_using_mkp, decrypted_header_keyring_using_mkp = client.decrypt( + source=ciphertext_keyring, + key_provider=aws_kms_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_keyring_using_mkp.encryption_context.items() + for pair in encrypted_header_keyring.encryption_context.items() + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=aws_kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_mkp_using_mkp, decrypted_header_mkp_using_mkp = client.decrypt( + source=ciphertext_mkp, + key_provider=aws_kms_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_mkp_using_mkp.encryption_context.items() + for pair in encrypted_header_mkp.encryption_context.items() + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration/migration_raw_aes_key_example.py b/examples/src/migration/migration_raw_aes_key_example.py new file mode 100644 index 000000000..c00ec489e --- /dev/null +++ b/examples/src/migration/migration_raw_aes_key_example.py @@ -0,0 +1,229 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the Raw AES Keyring from Raw AES master key provider (MKP) + +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. + +This example creates a Raw AES Keyring and Raw AES MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both Raw AES keyring and Raw AES MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +""" +import secrets + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", +} + +DEFAULT_AES_256_STATIC_KEY = secrets.token_bytes(32) + +# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME_SPACE = "Some managed raw keys" + +# The key name in the Raw keyrings is equivalent to the Key ID field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME = "My 256-bit AES wrapping key" + + +def create_keyring(): + """Demonstrate how to create a Raw AES keyring. + + Usage: create_keyring() + """ + # We fix the static key in order to make the test deterministic + static_key = DEFAULT_AES_256_STATIC_KEY + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=DEFAULT_KEY_NAME_SPACE, + key_name=DEFAULT_KEY_NAME, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + return keyring + + +# This is a helper class necessary for the Raw AES master key provider +# In the StaticMasterKeyProvider, we fix the static key to +# DEFAULT_AES_256_STATIC_KEY in order to make the test deterministic. +# Thus, both the Raw AES keyring and Raw AES MKP have the same key +# and we are able to encrypt data using keyrings and decrypt using MKP and vice versa +# In practice, users should generate a new random key for each key id. +class StaticMasterKeyProvider(RawMasterKeyProvider): + """Generates 256-bit keys for each unique key ID.""" + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + provider_id = DEFAULT_KEY_NAME_SPACE + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Returns a static, symmetric key for the specified key ID. + + :param str key_id: Key ID + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + static_key = DEFAULT_AES_256_STATIC_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.SYMMETRIC, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw AES master key provider. + + Usage: create_key_provider() + """ + # Create a Raw AES master key provider. + + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + key_id = DEFAULT_KEY_NAME + key_provider = StaticMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def migration_raw_aes_key(): + """Demonstrate a migration example for moving to a Raw AES keyring from Raw AES MKP. + + Usage: migration_raw_aes_key() + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a Raw AES Keyring + raw_aes_keyring = create_keyring() + + # 1b. Create a Raw AES Master Key Provider + raw_aes_master_key_provider = create_key_provider() + + # 2a. Encrypt EXAMPLE_DATA using Raw AES Keyring + ciphertext_keyring, encrypted_header_keyring = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_aes_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using Raw AES Master Key Provider + ciphertext_mkp, encrypted_header_mkp = client.encrypt( + source=EXAMPLE_DATA, + key_provider=raw_aes_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=raw_aes_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_keyring_using_mkp, decrypted_header_keyring_using_mkp = client.decrypt( + source=ciphertext_keyring, + key_provider=raw_aes_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_keyring_using_mkp.encryption_context.items() + for pair in encrypted_header_keyring.encryption_context.items() + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=raw_aes_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_mkp_using_mkp, decrypted_header_mkp_using_mkp = client.decrypt( + source=ciphertext_mkp, + key_provider=raw_aes_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_mkp_using_mkp.encryption_context.items() + for pair in encrypted_header_mkp.encryption_context.items() + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration/migration_raw_rsa_key_example.py b/examples/src/migration/migration_raw_rsa_key_example.py new file mode 100644 index 000000000..8d4d3f322 --- /dev/null +++ b/examples/src/migration/migration_raw_rsa_key_example.py @@ -0,0 +1,281 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This is a migration example for moving to the Raw RSA Keyring from Raw RSA master key provider (MKP) + +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. In this example, we define the RSA keys to +encrypt and decrypt the data keys. + +You need to generate, store, and protect the private key, preferably in a +hardware security module (HSM) or key management system. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. + +This example creates a Raw RSA Keyring and Raw RSA MKP and +then encrypts a custom input EXAMPLE_DATA with the same encryption context using both +the keyring and MKP. The example then decrypts the ciphertexts using both keyring and MKPs. +This example also includes some sanity checks for demonstration: +1. Decryption of these ciphertexts encrypted using keyring and MKP + is possible using both Raw RSA keyring and Raw RSA MKP +2. Both decrypted plaintexts are same and match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP are not +the same because the ESDK generates different data keys each time for encryption of the data. +But both ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +""" +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateRawRsaKeyringInput, PaddingScheme +from aws_cryptographic_material_providers.mpl.references import IKeyring +from cryptography.hazmat.backends import default_backend as crypto_default_backend +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +EXAMPLE_DATA: bytes = b"Hello World" + +DEFAULT_ENCRYPTION_CONTEXT : Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", +} + +# The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME_SPACE = "Some managed raw keys" + +# The key name in the Raw keyrings is equivalent to the Key ID field +# in the Raw Master Key Providers +DEFAULT_KEY_NAME = "My 4096-bit RSA wrapping key" + + +def generate_rsa_keys_helper(): + """Generates a 4096-bit RSA public and private key pair + + Usage: generate_rsa_keys_helper() + """ + ssh_rsa_exponent = 65537 + bit_strength = 4096 + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=ssh_rsa_exponent, + key_size=bit_strength + ) + + # This example choses a particular type of encoding, format and encryption_algorithm + # Users can choose the PublicFormat, PrivateFormat and encryption_algorithm that align most + # with their use-cases + public_key = key.public_key().public_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key = key.private_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=crypto_serialization.NoEncryption() + ) + + return public_key, private_key + + +DEFAULT_RSA_PUBLIC_KEY, DEFAULT_RSA_PRIVATE_KEY = generate_rsa_keys_helper() + + +def create_keyring(public_key, private_key): + """Demonstrate how to create a Raw RSA keyring using the key pair. + + Usage: create_keyring(public_key, private_key) + """ + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=DEFAULT_KEY_NAME_SPACE, + key_name=DEFAULT_KEY_NAME, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + keyring: IKeyring = mat_prov.create_raw_rsa_keyring( + input=keyring_input + ) + + return keyring + + +# This is a helper class necessary for the Raw RSA master key provider. +# In the StaticMasterKeyProvider, we fix the static key to +# DEFAULT_RSA_PRIVATE_KEY in order to make the test deterministic. +# Thus, both the Raw RSA keyring and Raw RSA MKP have the same private_key +# and we are able to encrypt data using keyrings and decrypt using MKP and vice versa +# In practice, users should generate a new random key pair for each key id. +class StaticMasterKeyProvider(RawMasterKeyProvider): + """Provides 4096-bit RSA keys consistently per unique key id.""" + + # The key namespace in the Raw keyrings is equivalent to Provider ID (or Provider) field + # in the Raw Master Key Providers + provider_id = DEFAULT_KEY_NAME_SPACE + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Retrieves a static, RSA key for the specified key id. + + :param str key_id: User-defined ID for the static key + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + # Also, in practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + static_key = DEFAULT_RSA_PRIVATE_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.PRIVATE, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw RSA master key provider. + + Usage: create_key_provider() + """ + # Create a Raw RSA master key provider. + + # The key name in the Raw keyrings is equivalent to the Key ID field + # in the Raw Master Key Providers + key_id = DEFAULT_KEY_NAME + + # In this example, we fix the static key to DEFAULT_RSA_PRIVATE_KEY in both the keyring + # and MKP (for MKP, we fix the static key in StaticMasterKeyProvider) in order to make + # the test deterministic. Thus, both the Raw RSA keyring and Raw RSA MKP have the same + # private_key and we are able to encrypt data using keyrings and decrypt using MKP + # and vice versa. In practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + key_provider = StaticMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def migration_raw_rsa_key( + public_key=DEFAULT_RSA_PUBLIC_KEY, + private_key=DEFAULT_RSA_PRIVATE_KEY +): + """Demonstrate a migration example for moving to a Raw RSA keyring from Raw RSA MKP. + + Usage: migration_raw_rsa_key(public_key, private_key) + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + # 1a. Create a Raw RSA Keyring + raw_rsa_keyring = create_keyring(public_key=public_key, private_key=private_key) + + # 1b. Create a Raw RSA Master Key Provider + + # In this example, we fix the static key to DEFAULT_RSA_PRIVATE_KEY in both the keyring + # and MKP (for MKP, we fix the static key in StaticMasterKeyProvider) in order to make + # the test deterministic. Thus, both the Raw RSA keyring and Raw RSA MKP have the same + # private_key and we are able to encrypt data using keyrings and decrypt using MKP + # and vice versa. In practice, users should generate a new key pair for each key id in + # the StaticMasterKeyProvider. + raw_rsa_master_key_provider = create_key_provider() + + # 2a. Encrypt EXAMPLE_DATA using Raw RSA Keyring + ciphertext_keyring, encrypted_header_keyring = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_rsa_keyring, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # 2b. Encrypt EXAMPLE_DATA using Raw RSA Master Key Provider + ciphertext_mkp, encrypted_header_mkp = client.encrypt( + source=EXAMPLE_DATA, + key_provider=raw_rsa_master_key_provider, + encryption_context=DEFAULT_ENCRYPTION_CONTEXT + ) + + # Note: The ciphertexts obtained by encrypting EXAMPLE_DATA using keyring and MKP + # (that is ciphertext_keyring and ciphertext_mkp) are not the same because the ESDK + # generates different data keys each time for encryption of the data. But both + # ciphertexts when decrypted using keyring and MKP will give the same plaintext result. + + # 3. Decrypt the ciphertext_keyring using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_keyring_using_keyring, _ = client.decrypt( + source=ciphertext_keyring, + keyring=raw_rsa_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_keyring_using_mkp, decrypted_header_keyring_using_mkp = client.decrypt( + source=ciphertext_keyring, + key_provider=raw_rsa_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_keyring_using_mkp.encryption_context.items() + for pair in encrypted_header_keyring.encryption_context.items() + ) + + assert decrypted_ciphertext_keyring_using_keyring == decrypted_ciphertext_keyring_using_mkp \ + and decrypted_ciphertext_keyring_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" + + # 4. Decrypt the ciphertext_mkp using both the keyring and MKP and ensure the + # resulting plaintext is the same and also equal to EXAMPLE_DATA + decrypted_ciphertext_mkp_using_keyring, _ = client.decrypt( + source=ciphertext_mkp, + keyring=raw_rsa_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=DEFAULT_ENCRYPTION_CONTEXT, + ) + + decrypted_ciphertext_mkp_using_mkp, decrypted_header_mkp_using_mkp = client.decrypt( + source=ciphertext_mkp, + key_provider=raw_rsa_master_key_provider + ) + + # Legacy MasterKeyProviders do not support providing encryption context on decrypt. + # If decrypting with a legacy MasterKeyProvider, you should manually verify + # that the encryption context used in the decrypt operation + # includes all key pairs from the encrypt operation. (The SDK can add pairs, so don't require an exact match.) + assert all( + pair in decrypted_header_mkp_using_mkp.encryption_context.items() + for pair in encrypted_header_mkp.encryption_context.items() + ) + + assert decrypted_ciphertext_mkp_using_keyring == decrypted_ciphertext_mkp_using_mkp \ + and decrypted_ciphertext_mkp_using_keyring == EXAMPLE_DATA, \ + "Decrypted outputs using keyring and master key provider are not the same" diff --git a/examples/src/migration/migration_set_commitment_policy_example.py b/examples/src/migration/migration_set_commitment_policy_example.py new file mode 100644 index 000000000..b698fd3ab --- /dev/null +++ b/examples/src/migration/migration_set_commitment_policy_example.py @@ -0,0 +1,117 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example configures a client with a specific commitment policy for the +AWS Encryption SDK client, then encrypts and decrypts data using an AWS KMS Keyring. + +The commitment policy in this example (FORBID_ENCRYPT_ALLOW_DECRYPT) should only be +used as part of a migration from version 1.x to 2.x, or for advanced users with +specialized requirements. Most AWS Encryption SDK users should use the default +commitment policy (REQUIRE_ENCRYPT_REQUIRE_DECRYPT). + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context for the commitment policy FORBID_ENCRYPT_ALLOW_DECRYPT. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on setting your commitment policy, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#commitment-policy +""" + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Demonstrate how to set your commitment policy for migration. + + Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This example builds the client with the FORBID_ENCRYPT_ALLOW_DECRYPT commitment policy, + # which enforces that this client cannot encrypt with key commitment + # and it can decrypt ciphertexts encrypted with or without key commitment. + # The default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()` is REQUIRE_ENCRYPT_REQUIRE_DECRYPT. + # We recommend that AWS Encryption SDK users use the default commitment policy + # (REQUIRE_ENCRYPT_REQUIRE_DECRYPT) whenever possible. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + # 5. Encrypt the data with the encryptionContext. Make sure you use a non-committing algorithm + # with the commitment policy FORBID_ENCRYPT_ALLOW_DECRYPT. Otherwise client.encrypt() will throw + # aws_encryption_sdk.exceptions.ActionNotAllowedError. By default for + # FORBID_ENCRYPT_ALLOW_DECRYPT, the algorithm used is + # AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384 which is a non-committing algorithm. + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=kms_keyring, + encryption_context=encryption_context + ) + + # 6. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 7. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/multi_keyring_example.py b/examples/src/multi_keyring_example.py new file mode 100644 index 000000000..2465c7c04 --- /dev/null +++ b/examples/src/multi_keyring_example.py @@ -0,0 +1,212 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the Multi Keyring + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring +as a child keyring. You can use different combinations of keyrings in the multi_keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html +""" +import secrets + +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + AesWrappingAlg, + CreateAwsKmsKeyringInput, + CreateMultiKeyringInput, + CreateRawAesKeyringInput, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Demonstrate an encrypt/decrypt cycle using a Multi keyring. + The multi_keyring is created using a KMS keyring as generator keyring and a raw AES keyring + as a child keyring. Therefore, we take a kms_key_id as input + + Usage: encrypt_and_decrypt_with_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and + decryption of your data keys in the kms_keyring, that is in-turn used in the multi_keyring + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name="us-west-2") + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Initialize the material providers library + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # 5. Create a KMS keyring + kms_keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=kms_keyring_input + ) + + # 6. Create a raw AES keyring to additionally encrypt under as child_keyring + + # The key namespace and key name are defined by you. + # and are used by the Raw AES keyring to determine + # whether it should attempt to decrypt an encrypted data key. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # Generate a 256-bit AES key to use with your raw AES keyring. + # Here, the input to secrets.token_bytes() = 32 bytes = 256 bits + static_key = secrets.token_bytes(32) + + raw_aes_keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=raw_aes_keyring_input + ) + + # 7. Create a multi_keyring that consists of the previously created keyrings. + # When using this multi_keyring to encrypt data, either `kms_keyring` or + # `raw_aes_keyring` (or a multi_keyring containing either) may be used to decrypt the data. + multi_keyring_input: CreateMultiKeyringInput = CreateMultiKeyringInput( + generator=kms_keyring, + child_keyrings=[raw_aes_keyring] + ) + + multi_keyring: IKeyring = mat_prov.create_multi_keyring( + input=multi_keyring_input + ) + + # 8. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=multi_keyring, + encryption_context=encryption_context + ) + + # 9. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 10a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + plaintext_bytes_multi_keyring, _ = client.decrypt( + source=ciphertext, + keyring=multi_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 10b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_multi_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # Because you used a multi_keyring on Encrypt, you can use either the + # `kms_keyring` or `raw_aes_keyring` individually to decrypt the data. + + # 11. Demonstrate that you can successfully decrypt data using just the `kms_keyring` + # directly. + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 11a. Decrypt your encrypted data using the kms_keyring. + plaintext_bytes_kms_keyring, _ = client.decrypt( + source=ciphertext, + keyring=kms_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 11b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_kms_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # 12. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring` + # directly. + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 12a. Decrypt your encrypted data using the raw_aes_keyring. + plaintext_bytes_raw_aes_keyring, _ = client.decrypt( + source=ciphertext, + keyring=raw_aes_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 12b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes_raw_aes_keyring == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/multithreading/__init__.py b/examples/src/multithreading/__init__.py new file mode 100644 index 000000000..6ac3d9a33 --- /dev/null +++ b/examples/src/multithreading/__init__.py @@ -0,0 +1,65 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""init file for multi-threading examples.""" +import time + +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk + + +def encrypt_and_decrypt_with_keyring( + plaintext_data: bytes, + keyring: IKeyring, + client: aws_encryption_sdk.EncryptionSDKClient +): + """Demonstrate how to encrypt and decrypt plaintext data using a keyring. + + Usage: encrypt_and_decrypt_with_keyring(plaintext_data, keyring, client) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param keyring: Keyring to use for encryption. + :type keyring: IKeyring + :param client: The Encryption SDK client to use for encryption. + :type client: aws_encryption_sdk.EncryptionSDKClient + :return: encrypted and decrypted (cycled) plaintext data + :rtype: bytes + """ + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + keyring=keyring, + encryption_context=encryption_context + ) + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + keyring=keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + return decrypted_plaintext_data + + +def run_encrypt_and_decrypt_with_keyring_for_duration_seconds( + plaintext_data: bytes, + keyring: IKeyring, + client: aws_encryption_sdk.EncryptionSDKClient, + duration: int = 2 +): + """Helper function to repeatedly run an encrypt and decrypt cycle for 'duration' seconds.""" + time_end = time.time() + duration + + while time.time() < time_end: + decrypted_plaintext_data = encrypt_and_decrypt_with_keyring(plaintext_data, keyring, client) + assert decrypted_plaintext_data == plaintext_data, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/multithreading/raw_aes_keyring.py b/examples/src/multithreading/raw_aes_keyring.py new file mode 100644 index 000000000..65e4627b1 --- /dev/null +++ b/examples/src/multithreading/raw_aes_keyring.py @@ -0,0 +1,38 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This file contains methods to use for testing multi-threading for Raw AES keyring.""" + +import secrets + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring + + +def create_keyring(): + """Demonstrate how to create a Raw AES keyring. + + Usage: create_keyring() + """ + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + static_key = secrets.token_bytes(32) + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + return keyring diff --git a/examples/src/multithreading/raw_rsa_keyring.py b/examples/src/multithreading/raw_rsa_keyring.py new file mode 100644 index 000000000..06d2fe679 --- /dev/null +++ b/examples/src/multithreading/raw_rsa_keyring.py @@ -0,0 +1,66 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This file contains methods to use for testing multi-threading for Raw RSA keyring.""" +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateRawRsaKeyringInput, PaddingScheme +from aws_cryptographic_material_providers.mpl.references import IKeyring +from cryptography.hazmat.backends import default_backend as crypto_default_backend +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa + + +def generate_rsa_keys(): + """Generates a 4096-bit RSA public and private key pair + + Usage: generate_rsa_keys() + """ + ssh_rsa_exponent = 65537 + bit_strength = 4096 + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=ssh_rsa_exponent, + key_size=bit_strength + ) + + # This example choses a particular type of encoding, format and encryption_algorithm + # Users can choose the PublicFormat, PrivateFormat and encryption_algorithm that align most + # with their use-cases + public_key = key.public_key().public_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key = key.private_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=crypto_serialization.NoEncryption() + ) + + return public_key, private_key + + +def create_keyring(public_key, private_key): + """Demonstrate how to create a Raw RSA keyring using the key pair. + + Usage: create_keyring(public_key, private_key) + """ + key_name_space = "Some managed raw keys" + key_name = "My 4096-bit RSA wrapping key" + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + keyring: IKeyring = mat_prov.create_raw_rsa_keyring( + input=keyring_input + ) + + return keyring diff --git a/examples/src/pylintrc b/examples/src/pylintrc index 858fa4d96..8ed5cb105 100644 --- a/examples/src/pylintrc +++ b/examples/src/pylintrc @@ -1,6 +1,7 @@ [MESSAGE CONTROL] # Disabling messages that either we don't care about we intentionally break. disable = + import-error, # ignore mpl import errors invalid-name, # we prefer long, descriptive, names for examples bad-continuation, # we let black handle this ungrouped-imports, # we let isort handle this diff --git a/examples/src/raw_aes_keyring_example.py b/examples/src/raw_aes_keyring_example.py new file mode 100644 index 000000000..0e0b53491 --- /dev/null +++ b/examples/src/raw_aes_keyring_example.py @@ -0,0 +1,119 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the Raw AES Keyring + +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. + +This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that +you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, +but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +""" +import secrets + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring(): + """Demonstrate an encrypt/decrypt cycle using a Raw AES keyring. + + Usage: encrypt_and_decrypt_with_keyring() + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. The key namespace and key name are defined by you. + # and are used by the Raw AES keyring to determine + # whether it should attempt to decrypt an encrypted data key. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Generate a 256-bit AES key to use with your keyring. + # In practice, you should get this key from a secure key management system such as an HSM. + + # Here, the input to secrets.token_bytes() = 32 bytes = 256 bits + static_key = secrets.token_bytes(32) + + # 5. Create a Raw AES keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + # 6. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_aes_keyring, + encryption_context=encryption_context + ) + + # 7. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 8. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=raw_aes_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/src/raw_rsa_keyring_example.py b/examples/src/raw_rsa_keyring_example.py new file mode 100644 index 000000000..e59a9581c --- /dev/null +++ b/examples/src/raw_rsa_keyring_example.py @@ -0,0 +1,242 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example sets up the Raw RSA Keyring + +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. In this example, we define the RSA keys to +encrypt and decrypt the data keys. + +You need to generate, store, and protect the private key, preferably in a +hardware security module (HSM) or key management system. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. + +This example creates a Raw RSA Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. The original ciphertext is not decryptable using a keyring with a different RSA key pair +These sanity checks are for demonstration in the example only. You do not need these in your code. + +A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private +key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, +and you can decrypt data with a Raw RSA keyring that has only a private key. This example requires +the user to either provide both private and public keys, or not provide any keys and the example +generates both to test encryption and decryption. If you configure a Raw RSA keyring with a +public and private key, be sure that they are part of the same key pair. Some language +implementations of the AWS Encryption SDK will not construct a Raw RSA keyring with keys +from different pairs. Others rely on you to verify that your keys are from the same key pair. +You can include any Raw RSA keyring in a multi-keyring. + +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +""" + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateRawRsaKeyringInput, PaddingScheme +from aws_cryptographic_material_providers.mpl.references import IKeyring +from cryptography.hazmat.backends import default_backend as crypto_default_backend +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +EXAMPLE_DATA: bytes = b"Hello World" + + +def should_generate_new_rsa_key_pair(public_key_file_name, private_key_file_name): + """Returns True if user doesn't provide keys, and we need to generate them; + Returns False if the user has already provided both public and private keys + Raises a ValueError if the user only provides one of private_key and public_key + + Usage: should_generate_new_rsa_key_pair(public_key_file_name, private_key_file_name) + """ + # If only one of public_key and private_key files is provided, raise a ValueError + if (public_key_file_name and not private_key_file_name)\ + or (not public_key_file_name and private_key_file_name): + raise ValueError("Either both public and private keys should be provided! Or no keys \ + should be provided and the example can create the keys for you!") + + # If no keys are provided, we should generate a new rsa key pair, so return True + if not public_key_file_name and not private_key_file_name: + return True + + # If both keys are already provided, return False + return False + + +def generate_rsa_keys(): + """Generates a 4096-bit RSA public and private key pair + + Usage: generate_rsa_keys() + """ + ssh_rsa_exponent = 65537 + bit_strength = 4096 + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=ssh_rsa_exponent, + key_size=bit_strength + ) + + # This example choses a particular type of encoding, format and encryption_algorithm + # Users can choose the PublicFormat, PrivateFormat and encryption_algorithm that align most + # with their use-cases + public_key = key.public_key().public_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key = key.private_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=crypto_serialization.NoEncryption() + ) + + return public_key, private_key + + +def create_rsa_keyring(public_key, private_key): + """Create a Raw RSA keyring using the key pair + + Usage: create_rsa_keyring(public_key, private_key) + """ + # 1. The key namespace and key name are defined by you. + # and are used by the Raw RSA keyring + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 4096-bit RSA wrapping key" + + # 2. Create a Raw RSA keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + raw_rsa_keyring: IKeyring = mat_prov.create_raw_rsa_keyring( + input=keyring_input + ) + + return raw_rsa_keyring + + +def encrypt_and_decrypt_with_keyring(public_key_file_name=None, private_key_file_name=None): + """Demonstrate an encrypt/decrypt cycle using a Raw RSA keyring + with user defined keys. If no keys are present, generate new RSA + public and private keys and use them to create a Raw RSA keyring + + Usage: encrypt_and_decrypt_with_keyring(public_key_file_name, private_key_file_name) + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 3. Create a Raw RSA keyring. + # If you have provided keys in a PEM file, they will be loaded into the keyring. + # Otherwise, a key pair will be randomly generated for you. + + # Check if we need to generate an RSA key pair + should_generate_new_rsa_key_pair_bool = \ + should_generate_new_rsa_key_pair(public_key_file_name=public_key_file_name, + private_key_file_name=private_key_file_name) + + # If user doesn't provide the keys, that is, if should_generate_new_rsa_key_pair_bool is True + # generate a new RSA public and private key pair + if should_generate_new_rsa_key_pair_bool: + public_key, private_key = generate_rsa_keys() + else: + # If user provides the keys, read the keys from the files + with open(public_key_file_name, "r", encoding='utf-8') as f: + public_key = f.read() + + # Convert the public key from a string to bytes + public_key = bytes(public_key, 'utf-8') + + with open(private_key_file_name, "r", encoding='utf-8') as f: + private_key = f.read() + + # Convert the private key from a string to bytes + private_key = bytes(private_key, 'utf-8') + + # Create the keyring + raw_rsa_keyring = create_rsa_keyring(public_key=public_key, private_key=private_key) + + # 4. Encrypt the data with the encryptionContext + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_rsa_keyring, + encryption_context=encryption_context + ) + + # 5. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 6. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=raw_rsa_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 7. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # The next part of the example creates a new RSA keyring (for Bob) to demonstrate that + # decryption of the original ciphertext is not possible with a different keyring (Bob's). + # (This is an example for demonstration; you do not need to do this in your own code.) + + # 8. Create a new Raw RSA keyring for Bob + # Generate new keys + public_key_bob, private_key_bob = generate_rsa_keys() + + # Create the keyring + raw_rsa_keyring_bob = create_rsa_keyring(public_key=public_key_bob, private_key=private_key_bob) + + # 9. Test decrypt for the original ciphertext using raw_rsa_keyring_bob + try: + plaintext_bytes_bob, _ = client.decrypt( # pylint: disable=unused-variable + source=ciphertext, + keyring=raw_rsa_keyring_bob, + # Verify that the encryption context in the result contains the + # encryption context supplied to the encrypt method + encryption_context=encryption_context, + ) + + raise AssertionError("client.decrypt should throw an error of type AWSEncryptionSDKClientError!") + except AWSEncryptionSDKClientError: + pass diff --git a/examples/src/required_encryption_context_cmm.py b/examples/src/required_encryption_context_cmm.py new file mode 100644 index 000000000..8edf7545e --- /dev/null +++ b/examples/src/required_encryption_context_cmm.py @@ -0,0 +1,155 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. +A required encryption context CMM asks for required keys in the encryption context field +on encrypt such that they will not be stored on the message, but WILL be included in the header signature. +On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. +""" + +import boto3 +# Ignore missing MPL for pylint, but the MPL is required for this example +# noqa pylint: disable=import-error +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateAwsKmsKeyringInput, + CreateDefaultCryptographicMaterialsManagerInput, + CreateRequiredEncryptionContextCMMInput, +) +from aws_cryptographic_material_providers.mpl.references import ICryptographicMaterialsManager, IKeyring +from typing import Dict, List # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring( + kms_key_id: str +): + """Creates a hierarchical keyring using the provided resources, then encrypts and decrypts a string with it.""" + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. Create an encryption context. + # Most encrypted data should have an associated encryption context + # to protect integrity. This sample uses placeholder values. + # For more information see: + # pylint: disable=line-too-long + # blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management + encryption_context: Dict[str, str] = { + "key1": "value1", + "key2": "value2", + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + + # 3. Create list of required encryption context keys. + # This is a list of keys that must be present in the encryption context. + required_encryption_context_keys: List[str] = ["requiredKey1", "requiredKey2"] + + # 4. Create the AWS KMS keyring. + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=boto3.client('kms', region_name="us-west-2") + ) + kms_keyring: IKeyring = mpl.create_aws_kms_keyring(keyring_input) + + # 5. Create the required encryption context CMM. + underlying_cmm: ICryptographicMaterialsManager = \ + mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=kms_keyring + ) + ) + + required_ec_cmm: ICryptographicMaterialsManager = \ + mpl.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + required_encryption_context_keys=required_encryption_context_keys, + underlying_cmm=underlying_cmm, + ) + ) + + # 6. Encrypt the data + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + materials_manager=required_ec_cmm, + encryption_context=encryption_context + ) + + # 7. Reproduce the encryption context. + # The reproduced encryption context MUST contain a value for + # every key in the configured required encryption context keys during encryption with + # Required Encryption Context CMM. + reproduced_encryption_context: Dict[str, str] = { + "requiredKey1": "requiredValue1", + "requiredKey2": "requiredValue2", + } + + # 8. Decrypt the data + plaintext_bytes_a, _ = client.decrypt( + source=ciphertext, + materials_manager=required_ec_cmm, + encryption_context=reproduced_encryption_context + ) + assert plaintext_bytes_a == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # We can also decrypt using the underlying CMM, + # but must also provide the reproduced encryption context + plaintext_bytes_a, _ = client.decrypt( + source=ciphertext, + materials_manager=underlying_cmm, + encryption_context=reproduced_encryption_context + ) + assert plaintext_bytes_a == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" + + # 9. Extra: Demonstrate that if we don't provide the reproduced encryption context, + # decryption will fail. + try: + plaintext_bytes_a, _ = client.decrypt( + source=ciphertext, + materials_manager=required_ec_cmm, + # No reproduced encryption context for required EC CMM-produced message makes decryption fail. + ) + raise Exception("If this exception is raised, decryption somehow succeeded!") + except AWSEncryptionSDKClientError: + # Swallow specific expected exception. + # We expect decryption to fail with an AWSEncryptionSDKClientError + # since we did not provide reproduced encryption context when decrypting + # a message encrypted with the requried encryption context CMM. + pass + + # Same for the default CMM; + # If we don't provide the reproduced encryption context, decryption will fail. + try: + plaintext_bytes_a, _ = client.decrypt( + source=ciphertext, + materials_manager=underlying_cmm, + # No reproduced encryption context for required EC CMM-produced message makes decryption fail. + ) + raise Exception("If this exception is raised, decryption somehow succeeded!") + except AWSEncryptionSDKClientError: + # Swallow specific expected exception. + # We expect decryption to fail with an AWSEncryptionSDKClientError + # since we did not provide reproduced encryption context when decrypting + # a message encrypted with the requried encryption context CMM, + # even though we are using a default CMM on decrypt. + pass diff --git a/examples/src/set_encryption_algorithm_suite_example.py b/examples/src/set_encryption_algorithm_suite_example.py new file mode 100644 index 000000000..f6df411aa --- /dev/null +++ b/examples/src/set_encryption_algorithm_suite_example.py @@ -0,0 +1,140 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +This example demonstrates how to set an algorithm suite while using the Raw AES Keyring +in the AWS Encryption SDK. + +The algorithm suite used in the encrypt() method is the algorithm used to protect your +data using the data key. By setting this algorithm, you can configure the algorithm used +to encrypt and decrypt your data. + +Algorithm suites can be set in a similar manner in other keyrings as well. However, +please make sure that you're using a logical algorithm suite that is compatible with your +keyring. For more information on algorithm suites supported by the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html + +The AES wrapping algorithm (AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16) protects your data key using +the user-provided wrapping key. In contrast, the algorithm suite used in the encrypt() method +is the algorithm used to protect your data using the data key. This example demonstrates setting the +latter, which is the algorithm suite for protecting your data. When the commitment policy is +REQUIRE_ENCRYPT_REQUIRE_DECRYPT, the default algorithm used in the encrypt method is +AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384, which is a committing and signing algorithm. +Signature verification ensures the integrity of a digital message as it goes across trust +boundaries. However, signature verification adds a significant performance cost to encryption +and decryption. If encryptors and decryptors are equally trusted, we can consider using an algorithm +suite that does not include signing. This example sets the algorithm suite as +AES_256_GCM_HKDF_SHA512_COMMIT_KEY, which is a committing but non-signing algorithm. +For more information on digital signatures, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + +This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context and the algorithm suite AES_256_GCM_HKDF_SHA512_COMMIT_KEY. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +""" +import secrets + +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring +from typing import Dict # noqa pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy +from aws_encryption_sdk.identifiers import AlgorithmSuite + +EXAMPLE_DATA: bytes = b"Hello World" + + +def encrypt_and_decrypt_with_keyring(): + """Demonstrate an encrypt/decrypt cycle using a Raw AES keyring. + + Usage: encrypt_and_decrypt_with_keyring() + """ + # 1. Instantiate the encryption SDK client. + # This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + # which enforces that this client only encrypts using committing algorithm suites and enforces + # that this client will only decrypt encrypted messages that were created with a committing + # algorithm suite. + # This is the default commitment policy if you were to build the client as + # `client = aws_encryption_sdk.EncryptionSDKClient()`. + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + # 2. The key namespace and key name are defined by you. + # and are used by the Raw AES keyring to determine + # whether it should attempt to decrypt an encrypted data key. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # 3. Create encryption context. + # Remember that your encryption context is NOT SECRET. + # For more information, see + # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + encryption_context: Dict[str, str] = { + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + # 4. Generate a 256-bit AES wrapping key to use with your keyring. + # In practice, you should get this key from a secure key management system such as an HSM. + + # Here, the input to secrets.token_bytes() = 32 bytes = 256 bits + static_key = secrets.token_bytes(32) + + # 5. Create a Raw AES keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + # The wrapping algorithm here is NOT the algorithm suite we set in this example. + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + # 6. Encrypt the data with the encryptionContext. + # This is the important step in this example where we specify the algorithm suite + # you want to use for encrypting your data + ciphertext, _ = client.encrypt( + source=EXAMPLE_DATA, + keyring=raw_aes_keyring, + encryption_context=encryption_context, + algorithm=AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY + ) + + # 7. Demonstrate that the ciphertext and plaintext are different. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert ciphertext != EXAMPLE_DATA, \ + "Ciphertext and plaintext data are the same. Invalid encryption" + + # 8. Decrypt your encrypted data using the same keyring you used on encrypt. + plaintext_bytes, _ = client.decrypt( + source=ciphertext, + keyring=raw_aes_keyring, + # Provide the encryption context that was supplied to the encrypt method + encryption_context=encryption_context, + ) + + # 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + # (This is an example for demonstration; you do not need to do this in your own code.) + assert plaintext_bytes == EXAMPLE_DATA, \ + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption" diff --git a/examples/test/__init__.py b/examples/test/__init__.py index e8fd618b1..120179eda 100644 --- a/examples/test/__init__.py +++ b/examples/test/__init__.py @@ -1,13 +1,3 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/legacy/__init__.py b/examples/test/legacy/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/examples/test/legacy/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/examples_test_utils.py b/examples/test/legacy/examples_test_utils.py similarity index 82% rename from examples/test/examples_test_utils.py rename to examples/test/legacy/examples_test_utils.py index 8a51f21c8..8787e0f38 100644 --- a/examples/test/examples_test_utils.py +++ b/examples/test/legacy/examples_test_utils.py @@ -1,21 +1,11 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper utilities for use while testing examples.""" import os import sys os.environ["AWS_ENCRYPTION_SDK_EXAMPLES_TESTING"] = "yes" -sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "test", "integration"])]) +sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "..", "test", "integration"])]) static_plaintext = ( b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. " @@ -49,7 +39,7 @@ from integration_test_utils import ( # noqa pylint: disable=unused-import,import-error get_cmk_arn, - get_second_cmk_arn, get_mrk_arn, + get_second_cmk_arn, get_second_mrk_arn, ) diff --git a/examples/test/legacy/pylintrc b/examples/test/legacy/pylintrc new file mode 100644 index 000000000..67090eedf --- /dev/null +++ b/examples/test/legacy/pylintrc @@ -0,0 +1,22 @@ +[MESSAGES CONTROL] +# Disabling messages that we either don't care about for tests or are necessary to break for tests. +disable = + invalid-name, # we prefer long, descriptive, names for tests + missing-docstring, # we don't write docstrings for tests + wrong-import-position, # similar to E0401, pylint does not appear to identify + # unknown modules as non-standard-library. flake8 tests for this as well + # and does treat them properly + duplicate-code, # tests for similar things tend to be similar + consider-using-f-string # disable until 2022-05-05; 6 months after 3.5 deprecation + +[VARIABLES] +additional-builtins = raw_input + +[DESIGN] +max-args = 10 + +[FORMAT] +max-line-length = 120 + +[REPORTS] +msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} diff --git a/examples/test/legacy/test_i_basic_encryption.py b/examples/test/legacy/test_i_basic_encryption.py new file mode 100644 index 000000000..5ef8c5bbd --- /dev/null +++ b/examples/test/legacy/test_i_basic_encryption.py @@ -0,0 +1,16 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the Strings examples in the AWS-hosted documentation.""" +import botocore.session +import pytest + +from ...src.legacy.basic_encryption import cycle_string +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_cycle_string(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + cycle_string(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/legacy/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/legacy/test_i_basic_file_encryption_with_multiple_providers.py new file mode 100644 index 000000000..08bc5ba59 --- /dev/null +++ b/examples/test/legacy/test_i_basic_file_encryption_with_multiple_providers.py @@ -0,0 +1,29 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the Bytes Streams Multiple Providers examples in the AWS-hosted documentation.""" +import os +import tempfile + +import botocore.session +import pytest + +from ...src.legacy.basic_file_encryption_with_multiple_providers import cycle_file +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_cycle_file(): + cmk_arn = get_cmk_arn() + handle, filename = tempfile.mkstemp() + with open(filename, "wb") as f: + f.write(static_plaintext) + try: + new_files = cycle_file( + key_arn=cmk_arn, source_plaintext_filename=filename, botocore_session=botocore.session.Session() + ) + for f in new_files: + os.remove(f) + finally: + os.close(handle) + os.remove(filename) diff --git a/examples/test/legacy/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/legacy/test_i_basic_file_encryption_with_raw_key_provider.py new file mode 100644 index 000000000..26ceaeb08 --- /dev/null +++ b/examples/test/legacy/test_i_basic_file_encryption_with_raw_key_provider.py @@ -0,0 +1,25 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the Bytes Streams examples in the AWS-hosted documentation.""" +import os +import tempfile + +import pytest + +from ...src.legacy.basic_file_encryption_with_raw_key_provider import cycle_file +from .examples_test_utils import static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_cycle_file(): + handle, filename = tempfile.mkstemp() + with open(filename, "wb") as f: + f.write(static_plaintext) + try: + new_files = cycle_file(source_plaintext_filename=filename) + for f in new_files: + os.remove(f) + finally: + os.close(handle) + os.remove(filename) diff --git a/examples/test/legacy/test_i_custom_cmm_example.py b/examples/test/legacy/test_i_custom_cmm_example.py new file mode 100644 index 000000000..397230090 --- /dev/null +++ b/examples/test/legacy/test_i_custom_cmm_example.py @@ -0,0 +1,51 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for encryption and decryption using custom CMM.""" +import botocore.session +import pytest + +import aws_encryption_sdk + +from ...src.legacy.custom_cmm_example import CustomSigningSuiteOnlyCMM, encrypt_decrypt_with_cmm +from .examples_test_utils import get_cmk_arn, static_plaintext +from .v3_default_cmm import V3DefaultCryptoMaterialsManager + +pytestmark = [pytest.mark.examples] + + +def test_custom_cmm_example(): + """Test method for encryption and decryption using V3 default CMM.""" + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + botocore_session = botocore.session.Session() + + # Create a KMS master key provider. + kms_kwargs = dict(key_ids=[cmk_arn]) + if botocore_session is not None: + kms_kwargs["botocore_session"] = botocore_session + master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) + + # Create the custom signing CMM using the master_key_provider + cmm = CustomSigningSuiteOnlyCMM(master_key_provider=master_key_provider) + + encrypt_decrypt_with_cmm(cmm=cmm, + source_plaintext=plaintext) + + +def test_v3_default_cmm(): + """Test method for encryption and decryption using V3 default CMM.""" + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + botocore_session = botocore.session.Session() + + # Create a KMS master key provider. + kms_kwargs = dict(key_ids=[cmk_arn]) + if botocore_session is not None: + kms_kwargs["botocore_session"] = botocore_session + master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs) + + # Create the V3 default CMM (V3DefaultCryptoMaterialsManager) using the master_key_provider + cmm = V3DefaultCryptoMaterialsManager(master_key_provider=master_key_provider) + + encrypt_decrypt_with_cmm(cmm=cmm, + source_plaintext=plaintext) diff --git a/examples/test/legacy/test_i_data_key_caching_basic.py b/examples/test/legacy/test_i_data_key_caching_basic.py new file mode 100644 index 000000000..4ff15156f --- /dev/null +++ b/examples/test/legacy/test_i_data_key_caching_basic.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the basic data key caching example in the AWS-hosted documentation.""" +import pytest + +from ...src.legacy.data_key_caching_basic import encrypt_with_caching +from .examples_test_utils import get_cmk_arn + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_with_caching(): + cmk_arn = get_cmk_arn() + encrypt_with_caching(kms_cmk_arn=cmk_arn, max_age_in_cache=10.0, cache_capacity=10) diff --git a/examples/test/legacy/test_i_discovery_kms_provider.py b/examples/test/legacy/test_i_discovery_kms_provider.py new file mode 100644 index 000000000..4664613dc --- /dev/null +++ b/examples/test/legacy/test_i_discovery_kms_provider.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK example.""" + +import botocore.session +import pytest + +from ...src.legacy.discovery_kms_provider import encrypt_decrypt +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_discovery_kms_provider(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/legacy/test_i_mrk_aware_kms_provider.py b/examples/test/legacy/test_i_mrk_aware_kms_provider.py new file mode 100644 index 000000000..6317edca9 --- /dev/null +++ b/examples/test/legacy/test_i_mrk_aware_kms_provider.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK example.""" + +import pytest + +from ...src.legacy.mrk_aware_kms_provider import encrypt_decrypt +from .examples_test_utils import get_mrk_arn, get_second_mrk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_discovery_kms_provider(): + plaintext = static_plaintext + cmk_arn_1 = get_mrk_arn() + cmk_arn_2 = get_second_mrk_arn() + encrypt_decrypt(mrk_arn=cmk_arn_1, mrk_arn_second_region=cmk_arn_2, source_plaintext=plaintext) diff --git a/examples/test/legacy/test_i_multiple_kms_cmk.py b/examples/test/legacy/test_i_multiple_kms_cmk.py new file mode 100644 index 000000000..19cb3234c --- /dev/null +++ b/examples/test/legacy/test_i_multiple_kms_cmk.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK example.""" + +import botocore.session +import pytest + +from ...src.legacy.multiple_kms_cmk import encrypt_decrypt +from .examples_test_utils import get_cmk_arn, get_second_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_one_kms_cmk(): + plaintext = static_plaintext + cmk_arns = [get_cmk_arn(), get_second_cmk_arn()] + encrypt_decrypt(key_arns=cmk_arns, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/legacy/test_i_one_kms_cmk.py b/examples/test/legacy/test_i_one_kms_cmk.py new file mode 100644 index 000000000..669ab8c0b --- /dev/null +++ b/examples/test/legacy/test_i_one_kms_cmk.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK example.""" + +import botocore.session +import pytest + +from ...src.legacy.one_kms_cmk import encrypt_decrypt +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_one_kms_cmk(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_one_kms_cmk_streaming_data.py b/examples/test/legacy/test_i_one_kms_cmk_streaming_data.py similarity index 53% rename from examples/test/test_i_one_kms_cmk_streaming_data.py rename to examples/test/legacy/test_i_one_kms_cmk_streaming_data.py index b22fa4232..d6098aab1 100644 --- a/examples/test/test_i_one_kms_cmk_streaming_data.py +++ b/examples/test/legacy/test_i_one_kms_cmk_streaming_data.py @@ -1,15 +1,5 @@ -# Copyright 2017-2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for the encryption and decryption of streaming data using one KMS CMK example.""" import os import tempfile @@ -17,10 +7,9 @@ import botocore.session import pytest -from ..src.one_kms_cmk_streaming_data import encrypt_decrypt_stream +from ...src.legacy.one_kms_cmk_streaming_data import encrypt_decrypt_stream from .examples_test_utils import get_cmk_arn, static_plaintext - pytestmark = [pytest.mark.examples] diff --git a/examples/test/legacy/test_i_one_kms_cmk_unsigned.py b/examples/test/legacy/test_i_one_kms_cmk_unsigned.py new file mode 100644 index 000000000..5b219cfeb --- /dev/null +++ b/examples/test/legacy/test_i_one_kms_cmk_unsigned.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK with an unsigned algorithm example.""" + +import botocore.session +import pytest + +from ...src.legacy.one_kms_cmk_unsigned import encrypt_decrypt +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_one_kms_cmk_unsigned(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/legacy/test_i_set_commitment.py b/examples/test/legacy/test_i_set_commitment.py new file mode 100644 index 000000000..5e3585b0a --- /dev/null +++ b/examples/test/legacy/test_i_set_commitment.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite for the encryption and decryption using one KMS CMK example.""" + +import botocore.session +import pytest + +from ...src.legacy.set_commitment import encrypt_decrypt +from .examples_test_utils import get_cmk_arn, static_plaintext + +pytestmark = [pytest.mark.examples] + + +def test_disable_commitment(): + plaintext = static_plaintext + cmk_arn = get_cmk_arn() + encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/legacy/v3_default_cmm.py b/examples/test/legacy/v3_default_cmm.py new file mode 100644 index 000000000..f077e26c9 --- /dev/null +++ b/examples/test/legacy/v3_default_cmm.py @@ -0,0 +1,159 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Copy-paste of the V3 default CMM with small changes to pass linters.""" +import logging + +import attr + +from aws_encryption_sdk.exceptions import MasterKeyProviderError, SerializationError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.internal.crypto.authentication import Signer, Verifier +from aws_encryption_sdk.internal.crypto.elliptic_curve import generate_ecc_signing_key +from aws_encryption_sdk.internal.defaults import ALGORITHM, ALGORITHM_COMMIT_KEY, ENCODED_SIGNER_KEY +from aws_encryption_sdk.internal.str_ops import to_str +from aws_encryption_sdk.internal.utils import prepare_data_keys +from aws_encryption_sdk.internal.utils.commitment import ( + validate_commitment_policy_on_decrypt, + validate_commitment_policy_on_encrypt, +) +from aws_encryption_sdk.key_providers.base import MasterKeyProvider +from aws_encryption_sdk.materials_managers import DecryptionMaterials, EncryptionMaterials +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager + +_LOGGER = logging.getLogger(__name__) + + +@attr.s(hash=False) +class V3DefaultCryptoMaterialsManager(CryptoMaterialsManager): + """Copy of the default crypto material manager for ESDK V3. + + This is a copy-paste of the DefaultCryptoMaterialsManager implementation + from the V3 ESDK commit: 98b5eb7c2bd7d1b2a3380aacfa508e8721c4d8a9 + This CMM is used to explicitly assert that the V3 implementation of + the DefaultCMM is compatible with future version's logic, + which implicitly asserts that custom implementations of V3-compatible CMMs + are also compatible with future version's logic. + + :param master_key_provider: Master key provider to use + :type master_key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + + master_key_provider = attr.ib(validator=attr.validators.instance_of(MasterKeyProvider)) + +# pylint: disable=no-self-use + def _generate_signing_key_and_update_encryption_context(self, algorithm, encryption_context): + """Generates a signing key based on the provided algorithm. + + :param algorithm: Algorithm for which to generate signing key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context from request + :returns: Signing key bytes + :rtype: bytes or None + """ + _LOGGER.debug("Generating signing key") + if algorithm.signing_algorithm_info is None: + return None + + signer = Signer(algorithm=algorithm, key=generate_ecc_signing_key(algorithm=algorithm)) + encryption_context[ENCODED_SIGNER_KEY] = to_str(signer.encoded_public_key()) + return signer.key_bytes() + + def get_encryption_materials(self, request): + """Creates encryption materials using underlying master key provider. + + :param request: encryption materials request + :type request: aws_encryption_sdk.materials_managers.EncryptionMaterialsRequest + :returns: encryption materials + :rtype: aws_encryption_sdk.materials_managers.EncryptionMaterials + :raises MasterKeyProviderError: if no master keys are available from the underlying master key provider + :raises MasterKeyProviderError: if the primary master key provided by the underlying master key provider + is not included in the full set of master keys provided by that provider + :raises ActionNotAllowedError: if the commitment policy in the request is violated by the algorithm being + used + """ + default_algorithm = ALGORITHM + if request.commitment_policy in ( + CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT, + CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT, + ): + default_algorithm = ALGORITHM_COMMIT_KEY + algorithm = request.algorithm if request.algorithm is not None else default_algorithm + + validate_commitment_policy_on_encrypt(request.commitment_policy, request.algorithm) + + encryption_context = request.encryption_context.copy() + + signing_key = self._generate_signing_key_and_update_encryption_context(algorithm, encryption_context) + + primary_master_key, master_keys = self.master_key_provider.master_keys_for_encryption( + encryption_context=encryption_context, + plaintext_rostream=request.plaintext_rostream, + plaintext_length=request.plaintext_length, + ) + if not master_keys: + raise MasterKeyProviderError("No Master Keys available from Master Key Provider") + if primary_master_key not in master_keys: + raise MasterKeyProviderError("Primary Master Key not in provided Master Keys") + + data_encryption_key, encrypted_data_keys = prepare_data_keys( + primary_master_key=primary_master_key, + master_keys=master_keys, + algorithm=algorithm, + encryption_context=encryption_context, + ) + + _LOGGER.debug("Post-encrypt encryption context: %s", encryption_context) + + return EncryptionMaterials( + algorithm=algorithm, + data_encryption_key=data_encryption_key, + encrypted_data_keys=encrypted_data_keys, + encryption_context=encryption_context, + signing_key=signing_key, + ) + +# pylint: disable=no-self-use + def _load_verification_key_from_encryption_context(self, algorithm, encryption_context): + """Loads the verification key from the encryption context if used by algorithm suite. + + :param algorithm: Algorithm for which to generate signing key + :type algorithm: aws_encryption_sdk.identifiers.Algorithm + :param dict encryption_context: Encryption context from request + :returns: Raw verification key + :rtype: bytes + :raises SerializationError: if algorithm suite requires message signing and no verification key is found + """ + encoded_verification_key = encryption_context.get(ENCODED_SIGNER_KEY, None) + + if algorithm.signing_algorithm_info is not None and encoded_verification_key is None: + raise SerializationError("No signature verification key found in header for signed algorithm.") + + if algorithm.signing_algorithm_info is None: + if encoded_verification_key is not None: + raise SerializationError("Signature verification key found in header for non-signed algorithm.") + return None + + verifier = Verifier.from_encoded_point(algorithm=algorithm, encoded_point=encoded_verification_key) + return verifier.key_bytes() + + def decrypt_materials(self, request): + """Obtains a plaintext data key from one or more encrypted data keys + using underlying master key provider. + + :param request: decrypt materials request + :type request: aws_encryption_sdk.materials_managers.DecryptionMaterialsRequest + :returns: decryption materials + :rtype: aws_encryption_sdk.materials_managers.DecryptionMaterials + """ + validate_commitment_policy_on_decrypt(request.commitment_policy, request.algorithm) + + data_key = self.master_key_provider.decrypt_data_key_from_list( + encrypted_data_keys=request.encrypted_data_keys, + algorithm=request.algorithm, + encryption_context=request.encryption_context, + ) + verification_key = self._load_verification_key_from_encryption_context( + algorithm=request.algorithm, encryption_context=request.encryption_context + ) + + return DecryptionMaterials(data_key=data_key, verification_key=verification_key) diff --git a/examples/test/migration/__init__.py b/examples/test/migration/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/examples/test/migration/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/migration/test_i_migration_aws_kms_key_example.py b/examples/test/migration/test_i_migration_aws_kms_key_example.py new file mode 100644 index 000000000..612a896ba --- /dev/null +++ b/examples/test/migration/test_i_migration_aws_kms_key_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_aws_kms_key_example.""" +import pytest + +from ...src.migration.migration_aws_kms_key_example import migration_aws_kms_key + +pytestmark = [pytest.mark.examples] + + +def test_migration_aws_kms_key(): + """Test function for migration of AWS KMS Keys.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + migration_aws_kms_key(kms_key_id) diff --git a/examples/test/migration/test_i_migration_raw_aes_key_example.py b/examples/test/migration/test_i_migration_raw_aes_key_example.py new file mode 100644 index 000000000..d5e4f7789 --- /dev/null +++ b/examples/test/migration/test_i_migration_raw_aes_key_example.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_raw_aes_key_example.""" +import pytest + +from ...src.migration.migration_raw_aes_key_example import migration_raw_aes_key + +pytestmark = [pytest.mark.examples] + + +def test_migration_raw_aes_key(): + """Test function for migration of Raw AES keys.""" + migration_raw_aes_key() diff --git a/examples/test/migration/test_i_migration_raw_rsa_key_example.py b/examples/test/migration/test_i_migration_raw_rsa_key_example.py new file mode 100644 index 000000000..238dcbaab --- /dev/null +++ b/examples/test/migration/test_i_migration_raw_rsa_key_example.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_raw_rsa_key_example.""" +import pytest + +from ...src.migration.migration_raw_rsa_key_example import migration_raw_rsa_key + +pytestmark = [pytest.mark.examples] + + +def test_migration_raw_rsa_key(): + """Test function for migration of Raw RSA keys.""" + migration_raw_rsa_key() diff --git a/examples/test/migration/test_i_migration_set_commitment_policy_example.py b/examples/test/migration/test_i_migration_set_commitment_policy_example.py new file mode 100644 index 000000000..4620d64df --- /dev/null +++ b/examples/test/migration/test_i_migration_set_commitment_policy_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the migration_set_commitment_policy_example.""" +import pytest + +from ...src.migration.migration_set_commitment_policy_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for setting commitment policy using the AWS KMS Keyring example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_key_id) diff --git a/examples/test/multithreading/__init__.py b/examples/test/multithreading/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/examples/test/multithreading/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/examples/test/multithreading/test_i_raw_aes_keyring_multithreaded_example.py b/examples/test/multithreading/test_i_raw_aes_keyring_multithreaded_example.py new file mode 100644 index 000000000..225e3c411 --- /dev/null +++ b/examples/test/multithreading/test_i_raw_aes_keyring_multithreaded_example.py @@ -0,0 +1,50 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the Raw AES keyring example with multi-threading.""" +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +# pylint and isort disagree about where this goes; listen to isort +from typing import Optional # pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +from ...src.multithreading import run_encrypt_and_decrypt_with_keyring_for_duration_seconds +from ...src.multithreading.raw_aes_keyring import create_keyring + +pytestmark = [pytest.mark.examples] + + +def encrypt_and_decrypt_with_keyring_multithreaded_helper(n_threads=64, duration=60): + """Encrypt and decrypt using a keyring for fixed n_threads and duration.""" + keyring = create_keyring() + plaintext_data = b"Hello World" + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + with ThreadPoolExecutor(max_workers=n_threads) as executor: + thread_futures = {executor.submit(run_encrypt_and_decrypt_with_keyring_for_duration_seconds, + plaintext_data=plaintext_data, + keyring=keyring, + client=client, + duration=duration): i for i in range(n_threads)} + + for future in as_completed(thread_futures): + future.result() + + +def test_encrypt_and_decrypt_with_keyring_multithreaded( + n_threads_list: Optional[list] = None, + duration_list: Optional[list] = None, +): + """Test function for multi-threaded encrypt and decrypt using a keyring for different n_threads and duration.""" + # Set defaults if no value is provided + if n_threads_list is None: + n_threads_list = [1, 4, 16, 64] + if duration_list is None: + duration_list = [2, 10, 60] + for n in n_threads_list: + for d in duration_list: + encrypt_and_decrypt_with_keyring_multithreaded_helper(n_threads=n, duration=d) diff --git a/examples/test/multithreading/test_i_raw_rsa_keyring_multithreaded_example.py b/examples/test/multithreading/test_i_raw_rsa_keyring_multithreaded_example.py new file mode 100644 index 000000000..c1ebdbe24 --- /dev/null +++ b/examples/test/multithreading/test_i_raw_rsa_keyring_multithreaded_example.py @@ -0,0 +1,51 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the Raw RSA keyring example with multi-threading.""" +from concurrent.futures import ThreadPoolExecutor, as_completed + +import pytest +# pylint and isort disagree about where this goes; listen to isort +from typing import Optional # pylint: disable=wrong-import-order + +import aws_encryption_sdk +from aws_encryption_sdk import CommitmentPolicy + +from ...src.multithreading import run_encrypt_and_decrypt_with_keyring_for_duration_seconds +from ...src.multithreading.raw_rsa_keyring import create_keyring, generate_rsa_keys + +pytestmark = [pytest.mark.examples] + + +def encrypt_and_decrypt_with_keyring_multithreaded_helper(n_threads=64, duration=60): + """Encrypt and decrypt using a keyring for fixed n_threads and duration.""" + public_key, private_key = generate_rsa_keys() + keyring = create_keyring(public_key=public_key, private_key=private_key) + plaintext_data = b"Hello World" + client = aws_encryption_sdk.EncryptionSDKClient( + commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + ) + + with ThreadPoolExecutor(max_workers=n_threads) as executor: + thread_futures = {executor.submit(run_encrypt_and_decrypt_with_keyring_for_duration_seconds, + plaintext_data=plaintext_data, + keyring=keyring, + client=client, + duration=duration): i for i in range(n_threads)} + + for future in as_completed(thread_futures): + future.result() + + +def test_encrypt_and_decrypt_with_keyring_multithreaded( + n_threads_list: Optional[list] = None, + duration_list: Optional[list] = None, +): + """Test function for multi-threaded encrypt and decrypt using a keyring for different n_threads and duration.""" + # Set defaults if no value is provided + if n_threads_list is None: + n_threads_list = [1, 4, 16, 64] + if duration_list is None: + duration_list = [2, 10, 60] + for n in n_threads_list: + for d in duration_list: + encrypt_and_decrypt_with_keyring_multithreaded_helper(n_threads=n, duration=d) diff --git a/examples/test/pylintrc b/examples/test/pylintrc index 67090eedf..9792ea592 100644 --- a/examples/test/pylintrc +++ b/examples/test/pylintrc @@ -1,6 +1,7 @@ [MESSAGES CONTROL] # Disabling messages that we either don't care about for tests or are necessary to break for tests. disable = + import-error, # ignore mpl import errors invalid-name, # we prefer long, descriptive, names for tests missing-docstring, # we don't write docstrings for tests wrong-import-position, # similar to E0401, pylint does not appear to identify diff --git a/examples/test/test_i_aws_kms_discovery_keyring_example.py b/examples/test/test_i_aws_kms_discovery_keyring_example.py new file mode 100644 index 000000000..06b141712 --- /dev/null +++ b/examples/test/test_i_aws_kms_discovery_keyring_example.py @@ -0,0 +1,16 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS Discovery keyring example.""" +import pytest + +from ..src.aws_kms_discovery_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Discovery Keyring example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + aws_account_id = "658956600833" + aws_region = "us-west-2" + encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_region) diff --git a/examples/test/test_i_aws_kms_discovery_multi_keyring_example.py b/examples/test/test_i_aws_kms_discovery_multi_keyring_example.py new file mode 100644 index 000000000..f8cd13550 --- /dev/null +++ b/examples/test/test_i_aws_kms_discovery_multi_keyring_example.py @@ -0,0 +1,16 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS Discovery Multi keyring example.""" +import pytest + +from ..src.aws_kms_discovery_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Discovery Multi Keyring example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + aws_account_id = "658956600833" + aws_regions = ["us-east-1", "us-west-2"] + encrypt_and_decrypt_with_keyring(kms_key_id, aws_account_id, aws_regions) diff --git a/examples/test/test_i_aws_kms_keyring_example.py b/examples/test/test_i_aws_kms_keyring_example.py new file mode 100644 index 000000000..ef7b1b643 --- /dev/null +++ b/examples/test/test_i_aws_kms_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS keyring example.""" +import pytest + +from ..src.aws_kms_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Keyring example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_key_id) diff --git a/examples/test/test_i_aws_kms_mrk_discovery_keyring_example.py b/examples/test/test_i_aws_kms_mrk_discovery_keyring_example.py new file mode 100644 index 000000000..0a49709a3 --- /dev/null +++ b/examples/test/test_i_aws_kms_mrk_discovery_keyring_example.py @@ -0,0 +1,21 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS MRK Discovery keyring example.""" +import pytest + +from ..src.aws_kms_mrk_discovery_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS MRK Discovery Keyring example.""" + mrk_key_id_encrypt = \ + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + aws_account_id = "658956600833" + mrk_encrypt_region = "us-east-1" + mrk_replica_decrypt_region = "eu-west-1" + encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + aws_account_id, + mrk_encrypt_region, + mrk_replica_decrypt_region) diff --git a/examples/test/test_i_aws_kms_mrk_discovery_multi_keyring_example.py b/examples/test/test_i_aws_kms_mrk_discovery_multi_keyring_example.py new file mode 100644 index 000000000..ded9a78e8 --- /dev/null +++ b/examples/test/test_i_aws_kms_mrk_discovery_multi_keyring_example.py @@ -0,0 +1,21 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS MRK Discovery Multi keyring example.""" +import pytest + +from ..src.aws_kms_mrk_discovery_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using AWS KMS MRK Discovery Multi Keyring example.""" + mrk_key_id_encrypt = \ + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + mrk_encrypt_region = "us-east-1" + aws_account_id = "658956600833" + aws_regions = ["us-west-2", "us-east-1"] + encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + mrk_encrypt_region, + aws_account_id, + aws_regions) diff --git a/examples/test/test_i_aws_kms_mrk_keyring_example.py b/examples/test/test_i_aws_kms_mrk_keyring_example.py new file mode 100644 index 000000000..3bc977e26 --- /dev/null +++ b/examples/test/test_i_aws_kms_mrk_keyring_example.py @@ -0,0 +1,22 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS MRK keyring example.""" +import pytest + +from ..src.aws_kms_mrk_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS MRK Keyring example.""" + mrk_key_id_encrypt = \ + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + mrk_replica_key_id_decrypt = \ + "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + mrk_encrypt_region = "us-east-1" + mrk_replica_decrypt_region = "eu-west-1" + encrypt_and_decrypt_with_keyring(mrk_key_id_encrypt, + mrk_replica_key_id_decrypt, + mrk_encrypt_region, + mrk_replica_decrypt_region) diff --git a/examples/test/test_i_aws_kms_mrk_multi_keyring_example.py b/examples/test/test_i_aws_kms_mrk_multi_keyring_example.py new file mode 100644 index 000000000..960a8f3cb --- /dev/null +++ b/examples/test/test_i_aws_kms_mrk_multi_keyring_example.py @@ -0,0 +1,23 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS MRK Multi keyring example.""" +import pytest + +from ..src.aws_kms_mrk_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS MRK Multi Keyring example.""" + mrk_key_id = \ + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + kms_key_id = \ + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + mrk_replica_key_id = \ + "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7" + mrk_replica_decrypt_region = "eu-west-1" + encrypt_and_decrypt_with_keyring(mrk_key_id, + kms_key_id, + mrk_replica_key_id, + mrk_replica_decrypt_region) diff --git a/examples/test/test_i_aws_kms_multi_keyring_example.py b/examples/test/test_i_aws_kms_multi_keyring_example.py new file mode 100644 index 000000000..28cf0c497 --- /dev/null +++ b/examples/test/test_i_aws_kms_multi_keyring_example.py @@ -0,0 +1,22 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS multi keyring example.""" +import pytest + +from ..src.aws_kms_multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Multi Keyring example.""" + default_region_kms_key_id = \ + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + second_region_kms_key_id = \ + "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2" + default_region = "us-west-2" + second_region = "eu-central-1" + encrypt_and_decrypt_with_keyring(default_region_kms_key_id, + second_region_kms_key_id, + default_region, + second_region) diff --git a/examples/test/test_i_aws_kms_rsa_keyring_example.py b/examples/test/test_i_aws_kms_rsa_keyring_example.py new file mode 100644 index 000000000..6f41da1cb --- /dev/null +++ b/examples/test/test_i_aws_kms_rsa_keyring_example.py @@ -0,0 +1,25 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the AWS KMS RSA keyring example.""" +import pytest + +from ..src.aws_kms_rsa_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS RSA Keyring example.""" + kms_rsa_key_id = "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297" + + # THIS IS A PUBLIC RESOURCE AND SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT + public_key = bytes("-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ" + + "oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN" + + "/GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK" + + "fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2" + + "r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9" + + "yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou" + + "twIDAQAB" + + "\n-----END PUBLIC KEY-----", 'utf-8') + encrypt_and_decrypt_with_keyring(kms_rsa_key_id, public_key) diff --git a/examples/test/test_i_basic_encryption.py b/examples/test/test_i_basic_encryption.py deleted file mode 100644 index f2a4fab51..000000000 --- a/examples/test/test_i_basic_encryption.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2017-2019 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 suite for the Strings examples in the AWS-hosted documentation.""" -import botocore.session -import pytest - -from ..src.basic_encryption import cycle_string -from .examples_test_utils import get_cmk_arn, static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_string(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - cycle_string(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py b/examples/test/test_i_basic_file_encryption_with_multiple_providers.py deleted file mode 100644 index 282a272ab..000000000 --- a/examples/test/test_i_basic_file_encryption_with_multiple_providers.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2017-2019 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 suite for the Bytes Streams Multiple Providers examples in the AWS-hosted documentation.""" -import os -import tempfile - -import botocore.session -import pytest - -from ..src.basic_file_encryption_with_multiple_providers import cycle_file -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_file(): - cmk_arn = get_cmk_arn() - handle, filename = tempfile.mkstemp() - with open(filename, "wb") as f: - f.write(static_plaintext) - try: - new_files = cycle_file( - key_arn=cmk_arn, source_plaintext_filename=filename, botocore_session=botocore.session.Session() - ) - for f in new_files: - os.remove(f) - finally: - os.close(handle) - os.remove(filename) diff --git a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py b/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py deleted file mode 100644 index 710c0ccac..000000000 --- a/examples/test/test_i_basic_file_encryption_with_raw_key_provider.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2017-2019 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 suite for the Bytes Streams examples in the AWS-hosted documentation.""" -import os -import tempfile - -import pytest - -from ..src.basic_file_encryption_with_raw_key_provider import cycle_file -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_cycle_file(): - handle, filename = tempfile.mkstemp() - with open(filename, "wb") as f: - f.write(static_plaintext) - try: - new_files = cycle_file(source_plaintext_filename=filename) - for f in new_files: - os.remove(f) - finally: - os.close(handle) - os.remove(filename) diff --git a/examples/test/test_i_custom_mpl_cmm_example.py b/examples/test/test_i_custom_mpl_cmm_example.py new file mode 100644 index 000000000..6791914c4 --- /dev/null +++ b/examples/test/test_i_custom_mpl_cmm_example.py @@ -0,0 +1,39 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for encryption and decryption using custom CMM.""" +import boto3 +import pytest +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring + +from ..src.custom_mpl_cmm_example import MPLCustomSigningSuiteOnlyCMM, encrypt_decrypt_with_cmm + +pytestmark = [pytest.mark.examples] + + +def test_custom_cmm_example(): + """Test method for encryption and decryption using V3 default CMM.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + + # Create KMS keyring to use with the CMM + kms_client = boto3.client('kms', region_name="us-west-2") + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + # Create the custom MPL signing CMM using the keyring + cmm = MPLCustomSigningSuiteOnlyCMM(keyring=kms_keyring) + + encrypt_decrypt_with_cmm(cmm=cmm) diff --git a/examples/test/test_i_data_key_caching_basic.py b/examples/test/test_i_data_key_caching_basic.py deleted file mode 100644 index 734c35692..000000000 --- a/examples/test/test_i_data_key_caching_basic.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2017-2019 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 suite for the basic data key caching example in the AWS-hosted documentation.""" -import pytest - -from ..src.data_key_caching_basic import encrypt_with_caching -from .examples_test_utils import get_cmk_arn - - -pytestmark = [pytest.mark.examples] - - -def test_encrypt_with_caching(): - cmk_arn = get_cmk_arn() - encrypt_with_caching(kms_cmk_arn=cmk_arn, max_age_in_cache=10.0, cache_capacity=10) diff --git a/examples/test/test_i_default_cryptographic_materials_manager_example.py b/examples/test/test_i_default_cryptographic_materials_manager_example.py new file mode 100644 index 000000000..8a18f655d --- /dev/null +++ b/examples/test/test_i_default_cryptographic_materials_manager_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the default Cryptographic Materials Manager example.""" +import pytest + +from ..src.default_cryptographic_materials_manager_example import encrypt_and_decrypt_with_default_cmm + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_default_cmm(): + """Test function for encrypt and decrypt using the default Cryptographic Materials Manager example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_default_cmm(kms_key_id) diff --git a/examples/test/test_i_discovery_kms_provider.py b/examples/test/test_i_discovery_kms_provider.py deleted file mode 100644 index e9a1c6e71..000000000 --- a/examples/test/test_i_discovery_kms_provider.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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 suite for the encryption and decryption using one KMS CMK example.""" - -import botocore.session -import pytest - -from ..src.discovery_kms_provider import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_discovery_kms_provider(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_file_streaming_example.py b/examples/test/test_i_file_streaming_example.py new file mode 100644 index 000000000..67cd2b34c --- /dev/null +++ b/examples/test/test_i_file_streaming_example.py @@ -0,0 +1,62 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the file streaming keyring example.""" +import os + +import pytest + +from ..src.file_streaming_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt for file streaming example using Raw AES keyring.""" + test_keyrings_directory = 'test_keyrings' + if not os.path.exists(test_keyrings_directory): + os.makedirs(test_keyrings_directory) + + # Define the filename of the input plaintext data. + plaintext_filename = test_keyrings_directory + '/my-secret-data.dat' + + # Define the plaintext data to be encrypted and decrypted. + plaintext_data = '''Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Praesent non feugiat leo. Aenean iaculis tellus ut velit consectetur, +quis convallis orci eleifend. Sed eu dictum sapien. Nulla facilisi. Suspendisse potenti. +Proin vehicula vehicula maximus. Donec varius et elit vel rutrum. Nulla lacinia neque turpis +quis consequat orci pharetra et. Etiam consequat ullamcorper mauris. Vivamus molestie mollis +mauris a gravida. Curabitur sed bibendum nisl. Cras varius tortor non erat sodales, quis congu +tellus laoreet. Etiam fermentum purus eu diam sagittis, vitae commodo est vehicula. +Nulla feugiat viverra orci vel interdum. Quisque pulvinar elit eget nulla facilisis varius. +Mauris at suscipit sem. Aliquam in purus ut velit fringilla volutpat id non mi. +Curabitur quis nunc eleifend, ornare lectus non, fringilla quam. Nam maximus volutpat placerat. +Nulla ullamcorper lorem velit, nec sagittis ex tristique posuere. Aliquam fringilla magna commod +libero faucibus tempor. Vestibulum non ligula tincidunt, finibus sapien in, sollicitudin +ex. Pellentesque congue laoreet mi in condimentum. Cras convallis nisi ac nunc tincidunt +venenatis. Suspendisse urna elit, cursus eu lacus a, aliquet porttitor mi. +Nulla vel congue nibh, sed condimentum dui. Ut ante ligula, blandit eu finibus nec, +scelerisque quis eros. Maecenas gravida odio eget nibh dictum, dictum varius lacus interdum. +Integer quis nulla vulputate, rhoncus diam vitae, mollis mauris. Sed ut porttitor dolor. +Fusce ut justo a ex bibendum imperdiet nec sit amet magna. Sed ullamcorper luctus augue, +tempor viverra elit interdum sed. Cras sit amet arcu eu turpis molestie sollicitudin. +Curabitur fermentum varius nibh, ut aliquet nisi. Aliquam id tempus tellus. +Nulla porttitor nulla at nibh interdum, quis sollicitudin erat egestas. +Ut blandit mauris quis efficitur efficitur. Morbi neque sapien, posuere ut aliquam eget, +aliquam at velit. Morbi sit amet rhoncus felis, et hendrerit sem. Nulla porta dictum ligula +eget iaculis. Cras lacinia ligula quis risus ultrices, sed consectetur metus imperdiet. +Nullam id enim vestibulum nibh ultricies auctor. Morbi neque lacus, faucibus vitae commodo quis, +malesuada sed velit.''' + + # Write plaintext data to plaintext_filename file + with open(plaintext_filename, "w", encoding="utf-8") as f: + f.write(plaintext_data) + + # Define the filename of the encrypted data. + ciphertext_filename = test_keyrings_directory + '/my-encrypted-data.ct' + + # Define the filename of the decrypted data. + decrypted_filename = test_keyrings_directory + '/my-decrypted-data.dat' + + encrypt_and_decrypt_with_keyring(plaintext_filename, + ciphertext_filename, + decrypted_filename) diff --git a/examples/test/test_i_hierarchical_keyring_example.py b/examples/test/test_i_hierarchical_keyring_example.py new file mode 100644 index 000000000..72f2d48df --- /dev/null +++ b/examples/test/test_i_hierarchical_keyring_example.py @@ -0,0 +1,15 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the hierarchical keyring example.""" +import pytest + +from ..src.hierarchical_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the AWS KMS Hierarchical Keyring example.""" + key_store_table_name = "KeyStoreDdbTable" + kms_key_id = "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126" + encrypt_and_decrypt_with_keyring(key_store_table_name, key_store_table_name, kms_key_id) diff --git a/examples/test/test_i_mrk_aware_kms_provider.py b/examples/test/test_i_mrk_aware_kms_provider.py deleted file mode 100644 index 8e7a003f8..000000000 --- a/examples/test/test_i_mrk_aware_kms_provider.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2021 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 suite for the encryption and decryption using one KMS CMK example.""" - -import pytest - -from ..src.mrk_aware_kms_provider import encrypt_decrypt -from .examples_test_utils import get_mrk_arn, get_second_mrk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_discovery_kms_provider(): - plaintext = static_plaintext - cmk_arn_1 = get_mrk_arn() - cmk_arn_2 = get_second_mrk_arn() - encrypt_decrypt(mrk_arn=cmk_arn_1, mrk_arn_second_region=cmk_arn_2, source_plaintext=plaintext) diff --git a/examples/test/test_i_multi_keyring_example.py b/examples/test/test_i_multi_keyring_example.py new file mode 100644 index 000000000..485789c8b --- /dev/null +++ b/examples/test/test_i_multi_keyring_example.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the multi keyring example.""" +import pytest + +from ..src.multi_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the Multi Keyring example.""" + kms_key_id = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(kms_key_id) diff --git a/examples/test/test_i_multiple_kms_cmk.py b/examples/test/test_i_multiple_kms_cmk.py deleted file mode 100644 index 39369cbc6..000000000 --- a/examples/test/test_i_multiple_kms_cmk.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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 suite for the encryption and decryption using one KMS CMK example.""" - -import botocore.session -import pytest - -from ..src.multiple_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn, get_second_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk(): - plaintext = static_plaintext - cmk_arns = [get_cmk_arn(), get_second_cmk_arn()] - encrypt_decrypt(key_arns=cmk_arns, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_one_kms_cmk.py b/examples/test/test_i_one_kms_cmk.py deleted file mode 100644 index 71ce74d3d..000000000 --- a/examples/test/test_i_one_kms_cmk.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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 suite for the encryption and decryption using one KMS CMK example.""" - -import botocore.session -import pytest - -from ..src.one_kms_cmk import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_one_kms_cmk_unsigned.py b/examples/test/test_i_one_kms_cmk_unsigned.py deleted file mode 100644 index 8a2758c96..000000000 --- a/examples/test/test_i_one_kms_cmk_unsigned.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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 suite for the encryption and decryption using one KMS CMK with an unsigned algorithm example.""" - -import botocore.session -import pytest - -from ..src.one_kms_cmk_unsigned import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_one_kms_cmk_unsigned(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_raw_aes_keyring_example.py b/examples/test/test_i_raw_aes_keyring_example.py new file mode 100644 index 000000000..5ac2b6012 --- /dev/null +++ b/examples/test/test_i_raw_aes_keyring_example.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the Raw AES keyring example.""" +import pytest + +from ..src.raw_aes_keyring_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for encrypt and decrypt using the Raw AES Keyring example.""" + encrypt_and_decrypt_with_keyring() diff --git a/examples/test/test_i_raw_rsa_keyring_example.py b/examples/test/test_i_raw_rsa_keyring_example.py new file mode 100644 index 000000000..8cdf628f2 --- /dev/null +++ b/examples/test/test_i_raw_rsa_keyring_example.py @@ -0,0 +1,116 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the Raw RSA keyring example.""" +import os + +import pytest + +from ..src.raw_rsa_keyring_example import encrypt_and_decrypt_with_keyring, generate_rsa_keys + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring_without_user_defined_keys(): + """Test function for encrypt and decrypt using the Raw RSA Keyring example. + + Here user doesn't provide the public and private keys + """ + encrypt_and_decrypt_with_keyring() + + +def test_encrypt_and_decrypt_with_keyring_with_user_defined_keys(): + """Test function for encrypt and decrypt using the Raw RSA Keyring example. + + Here user provides the public and private keys. To test this, we create the + keys using the generate_rsa_keys function and write them to the file. + Then we call the encrypt_and_decrypt_with_keyring function and pass them + """ + # Generate the user keys for testing + user_public_key, user_private_key = generate_rsa_keys() + + # Convert the keys to strings + user_public_key = user_public_key.decode('utf-8') + user_private_key = user_private_key.decode('utf-8') + + test_keyrings_directory = 'test_keyrings' + if not os.path.exists(test_keyrings_directory): + os.makedirs(test_keyrings_directory) + + # Define the file names for the keys + user_public_key_file_name = test_keyrings_directory + '/user_public_key_file_name.pem' + user_private_key_file_name = test_keyrings_directory + '/user_private_key_file_name.pem' + + # Write the public key to the file + with open(user_public_key_file_name, "w", encoding="utf-8") as f: + f.write(user_public_key) + + # Write the private key to the file + with open(user_private_key_file_name, "w", encoding="utf-8") as f: + f.write(user_private_key) + + encrypt_and_decrypt_with_keyring(public_key_file_name=user_public_key_file_name, + private_key_file_name=user_private_key_file_name) + + +def test_encrypt_and_decrypt_fails_if_user_provides_only_public_key(): + """Test function for encrypt and decrypt using the Raw RSA Keyring example. + + Here user provides only the public key. The program should throw an Value error + as this example requires the user to either provide both private and public keys to + test both encryption and decryption, or not provide any keys and the example generates both + """ + # Generate the user keys for testing + user_public_key, user_private_key = generate_rsa_keys() # pylint: disable=unused-variable + + # Convert the public key to string + user_public_key = user_public_key.decode('utf-8') + + test_keyrings_directory = 'test_keyrings' + if not os.path.exists(test_keyrings_directory): + os.makedirs(test_keyrings_directory) + + # Define the file name for the public key + user_public_key_file_name = test_keyrings_directory + '/user_public_key_file_name.pem' + + # Write the public key to the file + with open(user_public_key_file_name, "w", encoding="utf-8") as f: + f.write(user_public_key) + + try: + encrypt_and_decrypt_with_keyring(public_key_file_name=user_public_key_file_name) + + raise AssertionError("encrypt_and_decrypt_with_keyring should raise an error") + except ValueError: + pass + + +def test_encrypt_and_decrypt_fails_if_user_provides_only_private_key(): + """Test function for encrypt and decrypt using the Raw RSA Keyring example. + + Here user provides only the private key. The program should throw an Value error + as this example requires the user to either provide both private and public keys to + test both encryption and decryption, or not provide any keys and the example generates both + """ + # Generate the user keys for testing + user_public_key, user_private_key = generate_rsa_keys() # pylint: disable=unused-variable + + # Convert the private key to string + user_private_key = user_private_key.decode('utf-8') + + test_keyrings_directory = 'test_keyrings' + if not os.path.exists(test_keyrings_directory): + os.makedirs(test_keyrings_directory) + + # Define the file name for the private key + user_private_key_file_name = test_keyrings_directory + '/user_private_key_file_name.pem' + + # Write the private key to the file + with open(user_private_key_file_name, "w", encoding="utf-8") as f: + f.write(user_private_key) + + try: + encrypt_and_decrypt_with_keyring(private_key_file_name=user_private_key_file_name) + + raise AssertionError("encrypt_and_decrypt_with_keyring should raise an error") + except ValueError: + pass diff --git a/examples/test/test_i_required_encryption_context_cmm.py b/examples/test/test_i_required_encryption_context_cmm.py new file mode 100644 index 000000000..2aa702b37 --- /dev/null +++ b/examples/test/test_i_required_encryption_context_cmm.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the required encryption context CMM example.""" +import pytest + +from ..src.required_encryption_context_cmm import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + key_arn = "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f" + encrypt_and_decrypt_with_keyring(key_arn) diff --git a/examples/test/test_i_set_commitment.py b/examples/test/test_i_set_commitment.py deleted file mode 100644 index 96247334b..000000000 --- a/examples/test/test_i_set_commitment.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 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 suite for the encryption and decryption using one KMS CMK example.""" - -import botocore.session -import pytest - -from ..src.set_commitment import encrypt_decrypt -from .examples_test_utils import get_cmk_arn -from .examples_test_utils import static_plaintext - - -pytestmark = [pytest.mark.examples] - - -def test_disable_commitment(): - plaintext = static_plaintext - cmk_arn = get_cmk_arn() - encrypt_decrypt(key_arn=cmk_arn, source_plaintext=plaintext, botocore_session=botocore.session.Session()) diff --git a/examples/test/test_i_set_encryption_algorithm_suite_example.py b/examples/test/test_i_set_encryption_algorithm_suite_example.py new file mode 100644 index 000000000..9069cfe44 --- /dev/null +++ b/examples/test/test_i_set_encryption_algorithm_suite_example.py @@ -0,0 +1,13 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Test suite for the Set Algorithm Suite example for a Raw AES keyring.""" +import pytest + +from ..src.set_encryption_algorithm_suite_example import encrypt_and_decrypt_with_keyring + +pytestmark = [pytest.mark.examples] + + +def test_encrypt_and_decrypt_with_keyring(): + """Test function for setting an algorithm suite in a Raw AES Keyring.""" + encrypt_and_decrypt_with_keyring() diff --git a/performance_tests/README.rst b/performance_tests/README.rst new file mode 100644 index 000000000..1e23548dd --- /dev/null +++ b/performance_tests/README.rst @@ -0,0 +1,219 @@ +##################################### +aws-encryption-sdk performance tests +##################################### + +This module runs performance tests for the `AWS Encryption SDK Python`_. + +******** +Overview +******** + +This module tests the following keyrings / master key providers: + +1. KMS Keyring / KMS Master Key Provider +2. Raw AES Keyring / AES Master Key Provider +3. Raw RSA Keyring / RSA Master Key Provider +4. Hierarchy Keyring +5. Caching CMM + +For each test on the above keyrings / master key providers, this package measures: + +1. Execution time +2. Total memory consumption + +For each keyring / master key provider, the execution time and memory consumption +is measured for three operations: + +1. Create keyring / master key provider +2. Encrypt +3. Decrypt + +The usage of the performance tests is demonstrated through an `AWS KMS Keyring`_. +However, the procedure is the same for any keyring / master key provider, with slight +changes in the input arguments. + +The results for the performance test will be available in the results folder in the +performance_tests directory. + +********************** +Required Prerequisites +********************** + +* Python 3.8+ +* aws-encryption-sdk +* boto3 >= 1.10.0 +* click +* tqdm +* pytest + +Recommended Prerequisites +========================= + +* aws-cryptographic-material-providers: == 1.10.0 + * Requires Python 3.11+. + +***** +Usage +***** + +Execution Time +============== + +Create Keyring +-------------- +To run the performance test for execution time, please use the +following commands in the performance_tests directory. + +.. code:: + + usage: python test/keyrings/test_aws_kms_keyring.py create + + Create a keyring to use for encryption and decryption. + + optional arguments: + -h, --help show this help message and exit. + --kms_key_id KMS_KEY_ID The KMS key ID you want to use. + --n_iters N_ITERS Number of iterations you want to + run the test for. For instance, + if n_iters = 100, this performance + test script will run the create_keyring + method 100 times and report the + execution time of each of the calls. + --output_file OUTPUT_FILE The output file for execution times + for each function call, + default='kms_keyring_create' in the + results folder. + +Encrypt +------- + +To run the performance test for execution time, please use the following +commands in the performance_tests directory: + +.. code:: + + usage: python test/keyrings/test_aws_kms_keyring.py encrypt + + optional arguments: + -h, --help show this help message and exit. + --plaintext_data_filename PLAINTEXT_DATA_FILENAME Filename containing plaintext data + you want to encrypt. + default='test/resources/plaintext/plaintext-data-medium.dat'. + You can choose to use any other plaintext + file as well. Some example plaintext + data files are present in the + 'test/resources' directory. + --kms_key_id KMS_KEY_ID The KMS key ID you want to use. + --n_iters N_ITERS Number of iterations you want to + run the test for. For instance, + if n_iters = 100, this performance + test script will run the create_keyring + method 100 times and report the + execution time of each of the calls. + --output_file OUTPUT_FILE The output file for execution times + for each function call, + default='kms_keyring_create' in the + results folder. + +Decrypt +------- + +To run the performance test for execution time, please use the +following commands in the performance_tests directory + +.. code:: + + usage: python test/keyrings/test_aws_kms_keyring.py decrypt + + optional arguments: + -h, --help show this help message and exit. + --ciphertext_data_filename CIPHERTEXT_DATA_FILENAME Filename containing ciphertext data + you want to decrypt. + default='test/resources/ciphertext/kms/ciphertext-data-medium.ct'. + You can choose to use any other + ciphertext file as well. Some example + ciphertext data files are present in + the 'test/resources' directory. + --kms_key_id KMS_KEY_ID The KMS key ID you want to use. + --n_iters N_ITERS Number of iterations you want to + run the test for. For instance, + if n_iters = 100, this performance + test script will run the create_keyring + method 100 times and report the + execution time of each of the calls. + --output_file OUTPUT_FILE The output file for execution times + for each function call, + default='kms_keyring_create' in the + results folder. + +Consolidate Time Results +======================== + +In order to find the minimum, maximum, average, 99th percentile and bottom +99th percentile trimmed average times from the n_iters runs, please use the +following script from the performance_tests directory with the csv file +containing times for each of the n_iters runs generated in the previous +"Execution Time" section: + +.. code:: + + usage: python consolidate_results.py results/kms_keyring_decrypt.csv + +Memory Consumption +================== + +To get the memory consumption, simply replace 'python' +with 'mprof run' in the previously mentioned commands. + +For example, if you want to calculate the memory consumption +of the encrypt function of a AWS KMS Keyring, simply write: + +.. code:: + + usage: mprof run test/keyrings/test_aws_kms_keyring.py encrypt + + +This should generate an mprofile log file in your current directory. +This mprofile log file contains the total memory consumed by the program +with respect to time elapsed. +To plot the memory consumption with respect to time, please use the following +command from the same directory + +.. code:: + + usage: mprof plot + + +This 'mprof plot' command will plot the most recent mprofile log file. + + +Performance Graph +================= + +To generate a performance graph, please use the following command +to generate the pstats log file by specifying the output pstats file +path. Here, 'results/kms_keyring_create.pstats' is set as the default +output file. + +.. code:: + + usage: python -m cProfile -o results/kms_keyring_create.pstats test/keyrings/test_aws_kms_keyring.py create + + +After generating the pstats file, please run the following command +to generate the performance graph. The output performance graph will +be a .png file that you specify. Here, 'results/kms_keyring_create.png' +is set as the default output file. + +.. code:: + + usage: gprof2dot -f pstats results/kms_keyring_create.pstats | dot -Tpng -o results/kms_keyring_create.png && eog results/kms_keyring_create.png + + +Note: This project does not adhere to semantic versioning; as such it +makes no guarantees that functionality will persist across major, +minor, or patch versions. +**DO NOT** take a standalone dependency on this library. + +.. _AWS Encryption SDK Python: https://github.com/aws/aws-encryption-sdk-python/ +.. _AWS KMS Keyring: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html diff --git a/performance_tests/__init__.py b/performance_tests/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/consolidate_results.py b/performance_tests/consolidate_results.py new file mode 100644 index 000000000..2601417cc --- /dev/null +++ b/performance_tests/consolidate_results.py @@ -0,0 +1,49 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Script for consolidating results for execution times""" + +import argparse +import csv + +import numpy as np + + +def calculate_statistics(_csv_file): + """Calculate average, trimmed average, minimum, maximum and p99 statistics for execution times in a CSV file.""" + with open(_csv_file, 'r', encoding='utf-8') as file: + reader = csv.reader(file) + data = [float(row[0]) for row in reader] + + output_stats = {} + + # Calculate statistics + if data: + data = np.sort(data) + output_stats['total_entries'] = len(data) + output_stats['average'] = np.mean(data) + output_stats['trimmed_average_99_bottom'] = np.mean(data[0:int(0.99 * len(data))]) + output_stats['minimum'] = min(data) + output_stats['maximum'] = max(data) + output_stats['perc_99'] = np.percentile(data, 99) + return output_stats + + return None + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('csv_file', + help='csv file containing the outputs of execution times for n_iter iterations') + args = parser.parse_args() + + statistics = calculate_statistics(args.csv_file) + if statistics: + print("CSV File:", args.csv_file) + print("Total Entries:", statistics['total_entries']) + print("Average:", statistics['average']) + print("Bottom 99th percentile trimmed average:", statistics['trimmed_average_99_bottom']) + print("Minimum:", statistics['minimum']) + print("Maximum:", statistics['maximum']) + print("99th percentile:", statistics['perc_99']) + else: + print("No data found in the CSV file.") diff --git a/performance_tests/pylintrc b/performance_tests/pylintrc new file mode 100644 index 000000000..8ed5cb105 --- /dev/null +++ b/performance_tests/pylintrc @@ -0,0 +1,45 @@ +[MESSAGE CONTROL] +# Disabling messages that either we don't care about we intentionally break. +disable = + import-error, # ignore mpl import errors + invalid-name, # we prefer long, descriptive, names for examples + bad-continuation, # we let black handle this + ungrouped-imports, # we let isort handle this + no-member, # breaks with attrs + no-self-use, # interesting to keep in mind for later refactoring, but not blocking + useless-object-inheritance, # we need to support Python 2, so no, not useless + duplicate-code, # some examples may be similar + too-few-public-methods, # does not allow value stores + too-many-locals, # examples may sometimes have more locals defined for clarity than would be appropriate in code + no-else-return, # we omit this on purpose for brevity where it would add no value + attribute-defined-outside-init, # breaks with attrs_post_init + abstract-method, # throws false positives on io.BaseIO grandchildren + redefined-outer-name, # we do this on purpose in multiple places + consider-using-f-string # disable until 2022-05-05; 6 months after 3.5 deprecation + +[BASIC] +# Allow function names up to 50 characters +function-rgx = [a-z_][a-z0-9_]{2,50}$ +# Allow method names up to 50 characters +method-rgx = [a-z_][a-z0-9_]{2,50}$ +# Allow class attribute names up to 50 characters +# Whitelist class attribute names: iv +class-attribute-rgx = (([A-Za-z_][A-Za-z0-9_]{2,50}|(__.*__))$)|(^iv$) +# Whitelist attribute names: iv +attr-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$) +# Whitelist argument names: iv, b +argument-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)|(^b$) +# Whitelist variable names: iv, b, _b, x, y, r, s +variable-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$)|(^b$)|(^_b$)|(^x$)|(^y$)|(^r$)|(^s$) + +[VARIABLES] +additional-builtins = raw_input + +[DESIGN] +max-args = 10 + +[FORMAT] +max-line-length = 120 + +[REPORTS] +msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} diff --git a/performance_tests/requirements.txt b/performance_tests/requirements.txt new file mode 100644 index 000000000..0b879647f --- /dev/null +++ b/performance_tests/requirements.txt @@ -0,0 +1,5 @@ +attrs >= 17.4.0 +aws-encryption-sdk>=2.3.0 +pytest>=3.3.1 +tqdm +click diff --git a/performance_tests/requirements_mpl.txt b/performance_tests/requirements_mpl.txt new file mode 100644 index 000000000..be77ea2da --- /dev/null +++ b/performance_tests/requirements_mpl.txt @@ -0,0 +1 @@ +aws-cryptographic-material-providers>=1.7.4,<=1.10.0 \ No newline at end of file diff --git a/performance_tests/results/.gitkeep b/performance_tests/results/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/performance_tests/setup.cfg b/performance_tests/setup.cfg new file mode 100644 index 000000000..c584a25bd --- /dev/null +++ b/performance_tests/setup.cfg @@ -0,0 +1,41 @@ +[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_third_party = attr,aws_encryption_sdk,pytest,setuptools,six diff --git a/performance_tests/setup.py b/performance_tests/setup.py new file mode 100644 index 000000000..702813509 --- /dev/null +++ b/performance_tests/setup.py @@ -0,0 +1,34 @@ +"""Performance test for 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), encoding="utf-8").read() # pylint: disable=consider-using-with + + +def get_version(): + """Read the version from this module.""" + init = read("src", "aws_encryption_sdk_performance_tests", "__init__.py") + return VERSION_RE.search(init).group(1) + + +setup( + name="aws-encryption-sdk-performance-tests", + packages=find_packages("src"), + package_dir={"": "src"}, + author="Amazon Web Services", + maintainer="Amazon Web Services", + author_email="aws-cryptools@amazon.com", + url="https://github.com/awslabs/aws-encryption-sdk-python", + description="Performance tests for the AWS Encryption SDK for Python", + keywords="aws-encryption-sdk aws kms encryption", + license="Apache License 2.0", + version=get_version(), +) diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/__init__.py b/performance_tests/src/aws_encryption_sdk_performance_tests/__init__.py new file mode 100644 index 000000000..cf1bf0fb4 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" +__version__ = "0.1.0" diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/__init__.py b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/aws_kms_keyring.py b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/aws_kms_keyring.py new file mode 100644 index 000000000..c05409ca4 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/aws_kms_keyring.py @@ -0,0 +1,131 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the AWS KMS keyring.""" + +import aws_encryption_sdk +import boto3 +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring + + +def create_keyring( + kms_key_id: str +): + """Demonstrate how to create an AWS KMS keyring. + + Usage: create_keyring(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a boto3 client for KMS. + kms_client = create_kms_client() + + # Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + return keyring + + +def create_kms_client(aws_region="us-west-2"): + """Create an AWS KMS client. + + Usage: create_kms_client(aws_region) + :param aws_region: AWS region to use for KMS client. + :type aws_region: string + """ + # Create a boto3 client for KMS. + kms_client = boto3.client('kms', region_name=aws_region) + + return kms_client + + +def create_keyring_given_kms_client( + kms_key_id: str, + kms_client: boto3.client, +): + """Demonstrate how to create an AWS KMS keyring with given KMS client. + + Usage: create_keyring(kms_key_id, kms_client) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + :param kms_client: boto3 client for KMS. + :type kms_client: boto3.client + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a KMS keyring + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id=kms_key_id, + kms_client=kms_client + ) + + keyring: IKeyring = mat_prov.create_aws_kms_keyring( + input=keyring_input + ) + + return keyring + + +def encrypt_using_keyring( + plaintext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to encrypt plaintext data using an AWS KMS keyring. + + Usage: encrypt_using_keyring(plaintext_data, keyring) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param keyring: Keyring to use for encryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + keyring=keyring + ) + + return ciphertext_data + + +def decrypt_using_keyring( + ciphertext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to decrypt ciphertext data using an AWS KMS keyring. + + Usage: decrypt_using_keyring(ciphertext_data, keyring) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param keyring: Keyring to use for decryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + keyring=keyring + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/hierarchy_keyring.py b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/hierarchy_keyring.py new file mode 100644 index 000000000..8ee7b8992 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/hierarchy_keyring.py @@ -0,0 +1,128 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the hierarchy keyring.""" + +import aws_encryption_sdk +import boto3 +from aws_cryptographic_material_providers.keystore import KeyStore +from aws_cryptographic_material_providers.keystore.config import KeyStoreConfig +from aws_cryptographic_material_providers.keystore.models import KMSConfigurationKmsKeyArn +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, + DefaultCache, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring + +from ..utils.util import PerfTestUtils + + +def create_keyring( + key_store_table_name: str, + logical_key_store_name: str, + kms_key_id: str, + branch_key_id: str = PerfTestUtils.DEFAULT_BRANCH_KEY_ID +): + """Demonstrate how to create a hierarchy keyring. + + Usage: create_keyring(key_store_table_name, logical_key_store_name, kms_key_id, branch_key_id) + :param key_store_table_name: Name of the KeyStore DynamoDB table. + :type key_store_table_name: string + :param logical_key_store_name: Logical name of the KeyStore. + :type logical_key_store_name: string + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + :param branch_key_id: Branch key you want to use for the hierarchy keyring. + :type branch_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create boto3 clients for DynamoDB and KMS. + ddb_client = boto3.client('dynamodb', region_name="us-west-2") + kms_client = boto3.client('kms', region_name="us-west-2") + + # Configure your KeyStore resource. + # This SHOULD be the same configuration that you used + # to initially create and populate your KeyStore. + keystore: KeyStore = KeyStore( + config=KeyStoreConfig( + ddb_client=ddb_client, + ddb_table_name=key_store_table_name, + logical_key_store_name=logical_key_store_name, + kms_client=kms_client, + kms_configuration=KMSConfigurationKmsKeyArn( + value=kms_key_id + ), + ) + ) + + # Create the Hierarchical Keyring. + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id=branch_key_id, + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + keyring: IKeyring = mat_prov.create_aws_kms_hierarchical_keyring( + input=keyring_input + ) + + return keyring + + +def encrypt_using_keyring( + plaintext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to encrypt plaintext data using a hierarchy keyring. + + Usage: encrypt_using_keyring(plaintext_data, keyring) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param keyring: Keyring to use for encryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + keyring=keyring, + encryption_context=PerfTestUtils.DEFAULT_ENCRYPTION_CONTEXT + ) + + return ciphertext_data + + +def decrypt_using_keyring( + ciphertext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to decrypt ciphertext data using a hierarchy keyring. + + Usage: decrypt_using_keyring(ciphertext_data, keyring) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param keyring: Keyring to use for decryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + keyring=keyring, + encryption_context=PerfTestUtils.DEFAULT_ENCRYPTION_CONTEXT + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_aes_keyring.py b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_aes_keyring.py new file mode 100644 index 000000000..08be9d216 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_aes_keyring.py @@ -0,0 +1,84 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the Raw AES keyring.""" + +import aws_encryption_sdk +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import AesWrappingAlg, CreateRawAesKeyringInput +from aws_cryptographic_material_providers.mpl.references import IKeyring + +from ..utils.util import PerfTestUtils + + +def create_keyring(): + """Demonstrate how to create a Raw AES keyring. + + Usage: create_keyring() + """ + key_name_space = "Some managed raw keys" + key_name = "My 256-bit AES wrapping key" + + # We fix the static key in order to make the test deterministic + static_key = PerfTestUtils.DEFAULT_AES_256_STATIC_KEY + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + keyring: IKeyring = mat_prov.create_raw_aes_keyring( + input=keyring_input + ) + + return keyring + + +def encrypt_using_keyring( + plaintext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to encrypt plaintext data using a Raw AES keyring. + + Usage: encrypt_using_keyring(plaintext_data, keyring) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param keyring: Keyring to use for encryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + keyring=keyring + ) + + return ciphertext_data + + +def decrypt_using_keyring( + ciphertext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to decrypt ciphertext data using a Raw AES keyring. + + Usage: decrypt_using_keyring(ciphertext_data, keyring) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param keyring: Keyring to use for decryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + keyring=keyring + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_rsa_keyring.py b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_rsa_keyring.py new file mode 100644 index 000000000..48894330e --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/keyrings/raw_rsa_keyring.py @@ -0,0 +1,79 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the Raw RSA keyring.""" +import aws_encryption_sdk +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import CreateRawRsaKeyringInput, PaddingScheme +from aws_cryptographic_material_providers.mpl.references import IKeyring + + +def create_keyring(public_key, private_key): + """Demonstrate how to create a Raw RSA keyring using the key pair. + + Usage: create_keyring(public_key, private_key) + """ + key_name_space = "Some managed raw keys" + key_name = "My 4096-bit RSA wrapping key" + + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + keyring: IKeyring = mat_prov.create_raw_rsa_keyring( + input=keyring_input + ) + + return keyring + + +def encrypt_using_keyring( + plaintext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to encrypt plaintext data using a Raw RSA keyring. + + Usage: encrypt_using_keyring(plaintext_data, keyring) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param keyring: Keyring to use for encryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + keyring=keyring + ) + + return ciphertext_data + + +def decrypt_using_keyring( + ciphertext_data: bytes, + keyring: IKeyring +): + """Demonstrate how to decrypt ciphertext data using a Raw RSA keyring. + + Usage: decrypt_using_keyring(ciphertext_data, keyring) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param keyring: Keyring to use for decryption. + :type keyring: IKeyring + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + keyring=keyring + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/__init__.py b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/aws_kms_master_key_provider.py b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/aws_kms_master_key_provider.py new file mode 100644 index 000000000..023cd5942 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/aws_kms_master_key_provider.py @@ -0,0 +1,69 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the AWS KMS master key provider.""" + +import aws_encryption_sdk + + +def create_key_provider( + kms_key_id: str +): + """Demonstrate how to create an AWS KMS master key provider. + + Usage: create_key_provider(kms_key_id) + :param kms_key_id: KMS Key identifier for the KMS key you want to use. + :type kms_key_id: string + + For more information on KMS Key identifiers, see + https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id + """ + # Create a KMS master key provider. + key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ + kms_key_id, + ]) + + return key_provider + + +def encrypt_using_key_provider( + plaintext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to encrypt plaintext data using an AWS KMS master key provider. + + Usage: encrypt_using_key_provider(plaintext_data, key_provider) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param key_provider: Master key provider to use for encryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + key_provider=key_provider + ) + + return ciphertext_data + + +def decrypt_using_key_provider( + ciphertext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to decrypt ciphertext data using an AWS KMS master key provider. + + Usage: decrypt_using_key_provider(ciphertext_data, key_provider) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param key_provider: Master key provider to use for decryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + key_provider=key_provider + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/caching_cmm.py b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/caching_cmm.py new file mode 100644 index 000000000..7199c50bf --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/caching_cmm.py @@ -0,0 +1,85 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the Caching Cryptographic Materials Manager (CMM) with KMS Master Key Provider.""" + +import aws_encryption_sdk + + +def create_cmm( + kms_key_id: str, + max_age_in_cache: float, + cache_capacity: int +): + """Demonstrate how to create a Caching CMM. + + Usage: create_cmm(kms_key_id, max_age_in_cache, cache_capacity) + :param kms_key_id: Amazon Resource Name (ARN) of the KMS customer master key + :type kms_key_id: str + :param max_age_in_cache: Maximum time in seconds that a cached entry can be used + :type max_age_in_cache: float + :param cache_capacity: Maximum number of entries to retain in cache at once + :type cache_capacity: int + """ + # Security thresholds + # Max messages (or max bytes per) data key are optional + max_messages_encrypted = 100 + + # Create a master key provider for the KMS customer master key (CMK) + key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[kms_key_id]) + + # Create a local cache + cache = aws_encryption_sdk.LocalCryptoMaterialsCache(cache_capacity) + + # Create a caching CMM + caching_cmm = aws_encryption_sdk.CachingCryptoMaterialsManager( + master_key_provider=key_provider, + cache=cache, + max_age=max_age_in_cache, + max_messages_encrypted=max_messages_encrypted, + ) + + return caching_cmm + + +def encrypt_using_cmm( + plaintext_data: bytes, + caching_cmm: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager +): + """Demonstrate how to encrypt plaintext data using a Caching CMM. + + Usage: encrypt_using_cmm(plaintext_data, caching_cmm) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param caching_cmm: Crypto Materials Manager to use for encryption. + :type caching_cmm: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + materials_manager=caching_cmm + ) + + return ciphertext_data + + +def decrypt_using_cmm( + ciphertext_data: bytes, + caching_cmm: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager +): + """Demonstrate how to decrypt ciphertext data using a Caching CMM. + + Usage: decrypt_using_cmm(ciphertext_data, caching_cmm) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param caching_cmm: Crypto Materials Manager to use for encryption. + :type caching_cmm: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + materials_manager=caching_cmm + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_aes_master_key_provider.py b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_aes_master_key_provider.py new file mode 100644 index 000000000..de0188c5e --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_aes_master_key_provider.py @@ -0,0 +1,101 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the Raw AES master key provider.""" + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +from ..utils.util import PerfTestUtils + + +class StaticRandomMasterKeyProvider(RawMasterKeyProvider): + """Generates 256-bit keys for each unique key ID.""" + + # The Provider ID (or Provider) field in the JceMasterKey and RawMasterKey is + # equivalent to key namespace in the Raw keyrings + provider_id = "Some managed raw keys" + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Returns a static, randomly-generated symmetric key for the specified key ID. + + :param str key_id: Key ID + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + static_key = PerfTestUtils.DEFAULT_AES_256_STATIC_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.SYMMETRIC, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw AES master key provider. + + Usage: create_key_provider() + """ + # Create a Raw AES master key provider. + + # The Key ID field in the JceMasterKey and RawMasterKey is equivalent to key name in the Raw keyrings + key_id = "My 256-bit AES wrapping key" + key_provider = StaticRandomMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def encrypt_using_key_provider( + plaintext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to encrypt plaintext data using a Raw AES master key provider. + + Usage: encrypt_using_key_provider(plaintext_data, key_provider) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param key_provider: Master key provider to use for encryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + key_provider=key_provider + ) + + return ciphertext_data + + +def decrypt_using_key_provider( + ciphertext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to decrypt ciphertext data using a Raw AES master key provider. + + Usage: decrypt_using_key_provider(ciphertext_data, key_provider) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param key_provider: Master key provider to use for decryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + key_provider=key_provider + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_rsa_master_key_provider.py b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_rsa_master_key_provider.py new file mode 100644 index 000000000..cdbe24110 --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/master_key_providers/raw_rsa_master_key_provider.py @@ -0,0 +1,101 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Performance tests for the Raw RSA master key provider.""" + +import aws_encryption_sdk +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm +from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey +from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider + +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + + +class StaticRandomMasterKeyProvider(RawMasterKeyProvider): + """Randomly generates and provides 4096-bit RSA keys consistently per unique key id.""" + + # The Provider ID (or Provider) field in the JceMasterKey and RawMasterKey is + # equivalent to key namespace in the Raw keyrings + provider_id = "Some managed raw keys" + + def __init__(self, **kwargs): # pylint: disable=unused-argument + """Initialize empty map of keys.""" + self._static_keys = {} + + def _get_raw_key(self, key_id): + """Retrieves a static, randomly generated, RSA key for the specified key id. + + :param str key_id: User-defined ID for the static key + :returns: Wrapping key that contains the specified static key + :rtype: :class:`aws_encryption_sdk.internal.crypto.WrappingKey` + """ + try: + static_key = self._static_keys[key_id] + except KeyError: + # We fix the static key in order to make the test deterministic + # In practice, you should get this key from a secure key management system such as an HSM. + static_key = PerfTestUtils.DEFAULT_RSA_PRIVATE_KEY + self._static_keys[key_id] = static_key + return WrappingKey( + wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1, + wrapping_key=static_key, + wrapping_key_type=EncryptionKeyType.PRIVATE, + ) + + +def create_key_provider(): + """Demonstrate how to create a Raw RSA master key provider. + + Usage: create_key_provider() + """ + # Create a Raw RSA master key provider. + + # The Key ID field in the JceMasterKey and RawMasterKey is equivalent to key name in the Raw keyrings + key_id = "My 4096-bit RSA wrapping key" + key_provider = StaticRandomMasterKeyProvider() + key_provider.add_master_key(key_id) + + return key_provider + + +def encrypt_using_key_provider( + plaintext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to encrypt plaintext data using a Raw RSA master key provider. + + Usage: encrypt_using_key_provider(plaintext_data, key_provider) + :param plaintext_data: plaintext data you want to encrypt + :type: bytes + :param key_provider: Master key provider to use for encryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + ciphertext_data, _ = client.encrypt( + source=plaintext_data, + key_provider=key_provider + ) + + return ciphertext_data + + +def decrypt_using_key_provider( + ciphertext_data: bytes, + key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider +): + """Demonstrate how to decrypt ciphertext data using a Raw RSA master key provider. + + Usage: decrypt_using_key_provider(ciphertext_data, key_provider) + :param ciphertext_data: ciphertext data you want to decrypt + :type: bytes + :param key_provider: Master key provider to use for decryption. + :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + """ + client = aws_encryption_sdk.EncryptionSDKClient() + + decrypted_plaintext_data, _ = client.decrypt( + source=ciphertext_data, + key_provider=key_provider + ) + + return decrypted_plaintext_data diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/utils/__init__.py b/performance_tests/src/aws_encryption_sdk_performance_tests/utils/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/utils/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/src/aws_encryption_sdk_performance_tests/utils/util.py b/performance_tests/src/aws_encryption_sdk_performance_tests/utils/util.py new file mode 100644 index 000000000..9100ef12e --- /dev/null +++ b/performance_tests/src/aws_encryption_sdk_performance_tests/utils/util.py @@ -0,0 +1,114 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Utility functions for AWS Encryption SDK performance tests.""" + + +class PerfTestUtils: + """Utility functions for AWS Encryption SDK performance tests.""" + DEFAULT_N_ITERS = 100 + DEFAULT_TESTING_N_ITERS = 1 + DEFAULT_FILE_SIZE = 'medium' + DEFAULT_AES_256_STATIC_KEY = \ + b'_\xcf"\x82\x03\x12\x9d\x00\x8a\xed\xaf\xe4\x80\x1d\x00t\xa6P\xac\xb6\xfe\xc5\xf6/{\xe7\xaaO\x01\x13W\x85' + DEFAULT_RSA_PUBLIC_KEY = bytes("-----BEGIN PUBLIC KEY-----\n" + + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxwWEEtEofjwaoo3WO79D\n" + + "hntoPf2APlY5yzlqm6ZvMyaazlwetkAzLSn5GB4hjKZaf043BfADJEdwXMHn8/UN\n" + + "up0BfUj8PfGn/b8cL78CTnvFZd/7WxQh6tUnfLX7BMiccHMb9OHhRy5PrTSuj6Um\n" + + "wwhBadL+Lc23DGl2cyN9SjGuYWWQ1IHGFA4/2EQr+Ez4LpebZqwXgv0iLuApte1q\n" + + "vGl6zOhByxi1N/ORVEscLT82+L+F3STgeTYA1CaoLFQ0y9ybx+7UUfEfKxhGoGEO\n" + + "XEOTuRBdLE2Jm8xaBODLqfiXr0z62VhTpRs4CYYTGHTLFCJHqeH7R2fwvwoG1nIg\n" + + "QzWSyyapK7d5MLn3rF3ManjZhvlyHK1wqa7nWVpo+jq1Py+HWLAtU8FY0br6wnOR\n" + + "3jjPGk0N4//iDnxNN+kpDxFnHEvxe3eJKWnbw0GR9+BGj32O+wRMtGyfRTzkoD/E\n" + + "EqIRlDzdtYCAtFW0HUsdQwL+ssDjEQ0+lqvEQrwTU1WBZiBQhEmzksAowHAcNIT+\n" + + "Fz7mvIlpEETNOQbsJkoXdEkhJXljh5UYmH1cB5al1MJf/5ea5Xb2HfH5WkMy4+eS\n" + + "V68V+tXv3ZthTe2bCk9rQTH9FWKLIYJyZfv8WAIxSWEEsyk5b+7WUGmvtm/nPJ4Z\n" + + "RfzkXoBJqJiSiPYCM0+jG4sCAwEAAQ==\n" + + "-----END PUBLIC KEY-----\n", 'utf-8') + + DEFAULT_RSA_PRIVATE_KEY = bytes("-----BEGIN PRIVATE KEY-----\n" + + "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDHBYQS0Sh+PBqi\n" + + "jdY7v0OGe2g9/YA+VjnLOWqbpm8zJprOXB62QDMtKfkYHiGMplp/TjcF8AMkR3Bc\n" + + "wefz9Q26nQF9SPw98af9vxwvvwJOe8Vl3/tbFCHq1Sd8tfsEyJxwcxv04eFHLk+t\n" + + "NK6PpSbDCEFp0v4tzbcMaXZzI31KMa5hZZDUgcYUDj/YRCv4TPgul5tmrBeC/SIu\n" + + "4Cm17Wq8aXrM6EHLGLU385FUSxwtPzb4v4XdJOB5NgDUJqgsVDTL3JvH7tRR8R8r\n" + + "GEagYQ5cQ5O5EF0sTYmbzFoE4Mup+JevTPrZWFOlGzgJhhMYdMsUIkep4ftHZ/C/\n" + + "CgbWciBDNZLLJqkrt3kwufesXcxqeNmG+XIcrXCprudZWmj6OrU/L4dYsC1TwVjR\n" + + "uvrCc5HeOM8aTQ3j/+IOfE036SkPEWccS/F7d4kpadvDQZH34EaPfY77BEy0bJ9F\n" + + "POSgP8QSohGUPN21gIC0VbQdSx1DAv6ywOMRDT6Wq8RCvBNTVYFmIFCESbOSwCjA\n" + + "cBw0hP4XPua8iWkQRM05BuwmShd0SSEleWOHlRiYfVwHlqXUwl//l5rldvYd8fla\n" + + "QzLj55JXrxX61e/dm2FN7ZsKT2tBMf0VYoshgnJl+/xYAjFJYQSzKTlv7tZQaa+2\n" + + "b+c8nhlF/ORegEmomJKI9gIzT6MbiwIDAQABAoICAAi9ysfCzQsCW88g+LRmGbKp\n" + + "7/GtFTlnsyEkc/TDMiYmf20p6aVqm3TT3596D1IsqlPmHQ+TM6gfxSUl1SjHbiNw\n" + + "qvSURJP57b186+GC+7hzwj9Pv6wH7ddxJktZeN2EbC6aN7OhSjJEq/Y5FqOzhsjR\n" + + "L4JU5Joha3VNmojDGcks9nJLsjlLO+Z8m7xFfkLpKottWEOBsoSr1pkFen+FnocJ\n" + + "AP5IAz/G5YrAFXWE2Qd5u9HgI6KLcJqSTyYCTqenySvdFDCLYmL4+rv7VHrN2IIf\n" + + "67iYqeb8vtsLdja5ouhjxVHLSUdLlFzvnZ35eBQ+aP8I5GnnRZCk1ZOmfpdjqtwE\n" + + "4mQRJU44DtGH/aySgQEAjn5BAxjrflSBpgAJs8HxTIoGXEEtGgJQeJcvSxv/1fTy\n" + + "EJSmwzepxDT1kAK0BPEllSHNLlHTEeJ8FMCGaEofDXPvJsJP/UvWxGmyRQXtG68m\n" + + "WAy27OsAQ2z6Iqn2829lUnJERjtFUHJDu5ZlJHRPz6d7FTbmI5jFOGGTDWKtHqFI\n" + + "88JZTwby55KyYLwDyxbqcDrRSOtzZ4N0rV2tLIMRoMDpjhJ8CopuxuQyxeuP3/7V\n" + + "tcW4IbNTqEDKL4TFZkZhb+govAvFAkRFjBWu7kZpSEGNVvR+O1pTgXxWsfaAb+3K\n" + + "EZ0lXelzaGCMCbwysAhxAoIBAQD+AfzgIva7GelSKujRO8rlhtPxoVNTMDbRo9QX\n" + + "NtztLHvrxyaZqM5nqf4rMjrbU7vPdT5Fn/3/iupaBkZk8IqqdKpdmgi+Pr+aFvOB\n" + + "LU2blEY8zWZCOwYerrwEPbQKblLdkIhDvOGpx1g4JuAlqIqJWW/RvMODf9Makwyq\n" + + "fxkG+y2Cr8TIsM3jKXprOkgeE7sB97r2OvkSuL/xP2cedCt0dI1vnk2QvUWw6af4\n" + + "Fs4xzqntS3KG3PHM9Jhljadm6da3cFnQxTIYpS0qT+Dv07NnTn1Ysjb2iCXEjvW2\n" + + "vZEjrcLO4dWfZXVIXAjKhG+e/MbCcjEmbhd480SvDjImzk47AoIBAQDIlR+afYw+\n" + + "UHaaQJiqnkY8E/ju4emgwVDZN3QJGEQS1q+HrCM0QAD410cwEBiyhuciYN27lEfU\n" + + "3NXNb4TKYLN9u/Alj0Em+UFN/cPdUEvgrqQXS5E5GWOX3ehG3LYI/a4n6nlo/zdu\n" + + "GSqHU93i8PoKweQFS23oCqnCkH5xBRcyvC3J/T4G/fl8FrnoVn9HLs3vM0gYMZSl\n" + + "Ej2XZJXbitpqS3QyK51ULePVwaC3Zjot3YxsAzpdcSG1/6VNj1QWr9KAr8YdXTu7\n" + + "VcStCElDksVbfMgYahpBYlU4xipPA101ll1KPom1ECI/F6ku5b2H2vnewy2TNzsY\n" + + "QX0R4NFofQLxAoIBAG2af/pbO+naMXKSL2nxighmmFfATAsuV8k4DxGBS+1Pb516\n" + + "jq5pR781fAY5o2n2hKjtJ1S1x80XrS3xXTi7Dqqkssq256TnwJeF5cbMvJswbOpZ\n" + + "mxFjFK3yqhCOa3zAxCL09cd83kb7TJbWN4woYLcJj5WKBTdd1cK2xxVeyHbZtXaZ\n" + + "z6jlmcG2qStRt8K6sswTkGolYkpwy+oWeLGMYR/cFxed0ExvT34aJK+Jb6nQSkSp\n" + + "dJ67Ad91f7j6WcyvhEYdRbQvEwHNbGLAmwgBan1eQfoe1Famwt1A7sfOnq0tkkzg\n" + + "5+PizKvPgr+YS+3nlwBac9joUlqPZgi/cGaMSPcCggEBALbTLZ4sJyM5RhFtJXoG\n" + + "j6/86F4cbk1HRwDmSY5snsepBQ8duGzMldY6qrlFQq2expgQQKrUCfEcZIg+yIOK\n" + + "RrApGEez3ke+02ZaEifsI20k4Y4WI8UuvhdTfX7xd76UMyRQ1N7+GTDyIVB+AfXz\n" + + "fYVGmya0TPY+meMsvwMXB8EHwpikid/nqHoRYNxD0vk30R7g2CqtLnaTPK58URdt\n" + + "5Y0TP1LnbBypQ0y3k1z3AbqCgJaHDrDTCE4SOUKLjLKtCaqgDG0BaQtkrsKkldrQ\n" + + "sbCk+OE//LRyA4mfHjssrs3EQz4D6JKvpPdrApsrbmihEDWaIzVXFzcRogUkrNqX\n" + + "b5ECggEBAKGW7doJEm0MjyvrJj/Tj4Zx3S8UjMgheBEIUZtMjewtNL0pn70O2AxN\n" + + "aEa4zHaNS0yTgMdbObImzYgat+asJbmFcv0UJy/e4CN+rrZlCHW2D9v9U+O0wKLB\n" + + "e5AmmFwaT/vVIy4gmBTcKGxV90ZF799gmKSoHAlrgjPFSRB/WcJsMwsGEyXl/C4Z\n" + + "4/xCqJgr0VJvuwrCiWf1QKn9AHuytit27E2R52n4FjU5nJ+CJEQqU1XDgF0x+txw\n" + + "PXUuRjOxKO6MzldzqJSUrTir8uqCwBIR9x9GOrGDp//ZbRw2TK4EbkyjNYO7KtOF\n" + + "A/DHJmMI5bKETJyj1GhBE9LqypAI1Bo=\n" + + "-----END PRIVATE KEY-----\n", "utf-8") + + DEFAULT_ENCRYPTION_CONTEXT = { + "tenant": "TenantA", + "encryption": "context", + "is not": "secret", + "but adds": "useful metadata", + "that can help you": "be confident that", + "the data you are handling": "is what you think it is", + } + + DEFAULT_BRANCH_KEY_ID = 'a52dfaad-7dbd-4430-a1fd-abaa5299da07' + + @staticmethod + def read_file(filename): + """Returns the contents of the file.""" + with open(filename, 'rb') as file: + return file.read() + + @staticmethod + def get_rsa_key_from_file(filename): + """Returns the RSA key""" + with open(filename, "r", encoding='utf-8') as f: + key = f.read() + + # Convert the key from a string to bytes + key = bytes(key, 'utf-8') + + return key + + @staticmethod + def write_time_list_to_csv(time_list, filename): + """Writes the time list to a CSV file.""" + with open(filename + '.csv', 'w', encoding='utf-8') as myfile: + for time in time_list: + myfile.write(str(time) + '\n') diff --git a/performance_tests/test/keyrings/__init__.py b/performance_tests/test/keyrings/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/test/keyrings/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/test/keyrings/test_aws_kms_keyring.py b/performance_tests/test/keyrings/test_aws_kms_keyring.py new file mode 100644 index 000000000..950a2a82e --- /dev/null +++ b/performance_tests/test/keyrings/test_aws_kms_keyring.py @@ -0,0 +1,206 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the AWS KMS keyring.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.keyrings.aws_kms_keyring import ( + create_keyring, + create_keyring_given_kms_client, + create_kms_client, + decrypt_using_keyring, + encrypt_using_keyring, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_kms_keyring(): + """Click group helper function""" + + +@create_kms_keyring.command() +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_keyring_create') +def create( + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the create_keyring function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_keyring(kms_key_id) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def create_kms_keyring_given_kms_client(): + """Click group helper function""" + + +@create_kms_keyring_given_kms_client.command() +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_keyring_create_given_kms_client') +def create_given_kms_client( + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the create_keyring function.""" + kms_client = create_kms_client() + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_keyring_given_kms_client(kms_key_id, kms_client) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_kms_keyring(): + """Click group helper function""" + + +@encrypt_kms_keyring.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_keyring_encrypt') +def encrypt( + plaintext_data_filename: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_keyring function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + keyring = create_keyring(kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_keyring(plaintext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_kms_keyring(): + """Click group helper function""" + + +@decrypt_kms_keyring.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/kms/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_keyring_decrypt') +def decrypt( + ciphertext_data_filename: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_keyring function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + keyring = create_keyring(kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_keyring(ciphertext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +kms_keyring_test = click.CommandCollection(sources=[create_kms_keyring, + create_kms_keyring_given_kms_client, + encrypt_kms_keyring, + decrypt_kms_keyring]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_keyring function""" + result = runner.invoke(create_kms_keyring.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_create_given_kms_client(runner): + """Test the create_keyring_given_kms_client function""" + result = runner.invoke(create_kms_keyring_given_kms_client.commands['create-given-kms-client'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_keyring function""" + result = runner.invoke(encrypt_kms_keyring.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_keyring function""" + result = runner.invoke(decrypt_kms_keyring.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + kms_keyring_test() diff --git a/performance_tests/test/keyrings/test_hierarchy_keyring.py b/performance_tests/test/keyrings/test_hierarchy_keyring.py new file mode 100644 index 000000000..e890c2a18 --- /dev/null +++ b/performance_tests/test/keyrings/test_hierarchy_keyring.py @@ -0,0 +1,174 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the hierarchy keyring.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.keyrings.hierarchy_keyring import ( + create_keyring, + decrypt_using_keyring, + encrypt_using_keyring, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_hierarchy_keyring(): + """Click group helper function""" + + +@create_hierarchy_keyring.command() +@click.option('--key_store_table_name', + default='KeyStoreDdbTable') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/hierarchy_keyring_create') +def create( + key_store_table_name: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the create_keyring function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_keyring(key_store_table_name, key_store_table_name, kms_key_id) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_hierarchy_keyring(): + """Click group helper function""" + + +@encrypt_hierarchy_keyring.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--key_store_table_name', + default='KeyStoreDdbTable') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/hierarchy_keyring_encrypt') +def encrypt( + plaintext_data_filename: str, + key_store_table_name: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_keyring function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + keyring = create_keyring(key_store_table_name, key_store_table_name, kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_keyring(plaintext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_hierarchy_keyring(): + """Click group helper function""" + + +@decrypt_hierarchy_keyring.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/hierarchy/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--key_store_table_name', + default='KeyStoreDdbTable') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/hierarchy_keyring_decrypt') +def decrypt( + ciphertext_data_filename: str, + key_store_table_name: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_keyring function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + keyring = create_keyring(key_store_table_name, key_store_table_name, kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_keyring(ciphertext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +hierarchy_keyring_test = click.CommandCollection(sources=[create_hierarchy_keyring, + encrypt_hierarchy_keyring, + decrypt_hierarchy_keyring]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_keyring function""" + result = runner.invoke(create_hierarchy_keyring.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_keyring function""" + result = runner.invoke(encrypt_hierarchy_keyring.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_keyring function""" + result = runner.invoke(decrypt_hierarchy_keyring.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + hierarchy_keyring_test() diff --git a/performance_tests/test/keyrings/test_raw_aes_keyring.py b/performance_tests/test/keyrings/test_raw_aes_keyring.py new file mode 100644 index 000000000..1e1da428a --- /dev/null +++ b/performance_tests/test/keyrings/test_raw_aes_keyring.py @@ -0,0 +1,156 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the Raw AES keyring.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.keyrings.raw_aes_keyring import ( + create_keyring, + decrypt_using_keyring, + encrypt_using_keyring, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_raw_aes_keyring(): + """Click group helper function""" + + +@create_raw_aes_keyring.command() +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_keyring_create') +def create( + n_iters: int, + output_file: str +): + """Performance test for the create_keyring function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_keyring() + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_raw_aes_keyring(): + """Click group helper function""" + + +@encrypt_raw_aes_keyring.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_keyring_encrypt') +def encrypt( + plaintext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_keyring function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + keyring = create_keyring() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_keyring(plaintext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_raw_aes_keyring(): + """Click group helper function""" + + +@decrypt_raw_aes_keyring.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/raw_aes/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_keyring_decrypt') +def decrypt( + ciphertext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_keyring function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + keyring = create_keyring() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_keyring(ciphertext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +raw_aes_keyring_test = click.CommandCollection(sources=[create_raw_aes_keyring, + encrypt_raw_aes_keyring, + decrypt_raw_aes_keyring]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_keyring function""" + result = runner.invoke(create_raw_aes_keyring.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_keyring function""" + result = runner.invoke(encrypt_raw_aes_keyring.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_keyring function""" + result = runner.invoke(decrypt_raw_aes_keyring.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + raw_aes_keyring_test() diff --git a/performance_tests/test/keyrings/test_raw_rsa_keyring.py b/performance_tests/test/keyrings/test_raw_rsa_keyring.py new file mode 100644 index 000000000..476701ac0 --- /dev/null +++ b/performance_tests/test/keyrings/test_raw_rsa_keyring.py @@ -0,0 +1,163 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the Raw RSA keyring.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.keyrings.raw_rsa_keyring import ( + create_keyring, + decrypt_using_keyring, + encrypt_using_keyring, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_raw_rsa_keyring(): + """Click group helper function""" + + +@create_raw_rsa_keyring.command() +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_keyring_create') +def create( + n_iters: int, + output_file: str +): + """Performance test for the create_keyring function.""" + public_key = PerfTestUtils.DEFAULT_RSA_PUBLIC_KEY + private_key = PerfTestUtils.DEFAULT_RSA_PRIVATE_KEY + + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_keyring(public_key, private_key) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_raw_rsa_keyring(): + """Click group helper function""" + + +@encrypt_raw_rsa_keyring.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_keyring_encrypt') +def encrypt( + plaintext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_keyring function.""" + public_key = PerfTestUtils.DEFAULT_RSA_PUBLIC_KEY + private_key = PerfTestUtils.DEFAULT_RSA_PRIVATE_KEY + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + keyring = create_keyring(public_key, private_key) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_keyring(plaintext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_raw_rsa_keyring(): + """Click group helper function""" + + +@decrypt_raw_rsa_keyring.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/raw_rsa/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_keyring_decrypt') +def decrypt( + ciphertext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_keyring function.""" + public_key = PerfTestUtils.DEFAULT_RSA_PUBLIC_KEY + private_key = PerfTestUtils.DEFAULT_RSA_PRIVATE_KEY + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + keyring = create_keyring(public_key, private_key) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_keyring(ciphertext_data, keyring) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +raw_rsa_keyring_test = click.CommandCollection(sources=[create_raw_rsa_keyring, + encrypt_raw_rsa_keyring, + decrypt_raw_rsa_keyring]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_keyring function""" + result = runner.invoke(create_raw_rsa_keyring.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_keyring function""" + result = runner.invoke(encrypt_raw_rsa_keyring.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_keyring function""" + result = runner.invoke(decrypt_raw_rsa_keyring.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + raw_rsa_keyring_test() diff --git a/performance_tests/test/master_key_providers/__init__.py b/performance_tests/test/master_key_providers/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/test/master_key_providers/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/test/master_key_providers/test_aws_kms_master_key_provider.py b/performance_tests/test/master_key_providers/test_aws_kms_master_key_provider.py new file mode 100644 index 000000000..c7b665857 --- /dev/null +++ b/performance_tests/test/master_key_providers/test_aws_kms_master_key_provider.py @@ -0,0 +1,165 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the AWS KMS Master key provider.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.master_key_providers.aws_kms_master_key_provider import ( + create_key_provider, + decrypt_using_key_provider, + encrypt_using_key_provider, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_kms_key_provider(): + """Click group helper function""" + + +@create_kms_key_provider.command() +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_key_provider_create') +def create( + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the create_key_provider function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_key_provider(kms_key_id) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_kms_key_provider(): + """Click group helper function""" + + +@encrypt_kms_key_provider.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_key_provider_encrypt') +def encrypt( + plaintext_data_filename: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_key_provider function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + key_provider = create_key_provider(kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_key_provider(plaintext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_kms_key_provider(): + """Click group helper function""" + + +@decrypt_kms_key_provider.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/kms/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/kms_key_provider_decrypt') +def decrypt( + ciphertext_data_filename: str, + kms_key_id: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_key_provider function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + key_provider = create_key_provider(kms_key_id) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_key_provider(ciphertext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +kms_key_provider_test = click.CommandCollection(sources=[create_kms_key_provider, + encrypt_kms_key_provider, + decrypt_kms_key_provider]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_key_provider function""" + result = runner.invoke(create_kms_key_provider.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_key_provider function""" + result = runner.invoke(encrypt_kms_key_provider.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_key_provider function""" + result = runner.invoke(decrypt_kms_key_provider.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + kms_key_provider_test() diff --git a/performance_tests/test/master_key_providers/test_caching_cmm.py b/performance_tests/test/master_key_providers/test_caching_cmm.py new file mode 100644 index 000000000..f8552f96e --- /dev/null +++ b/performance_tests/test/master_key_providers/test_caching_cmm.py @@ -0,0 +1,183 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating a Caching CMM.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.master_key_providers.caching_cmm import ( + create_cmm, + decrypt_using_cmm, + encrypt_using_cmm, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_caching_cmm(): + """Click group helper function""" + + +@create_caching_cmm.command() +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--max_age_in_cache', + default=10.0) +@click.option('--cache_capacity', + default=10) +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/caching_cmm_create') +def create( + kms_key_id: str, + max_age_in_cache: float, + cache_capacity: int, + n_iters: int, + output_file: str +): + """Performance test for the create_cmm function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_cmm(kms_key_id, max_age_in_cache, cache_capacity) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_caching_cmm(): + """Click group helper function""" + + +@encrypt_caching_cmm.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--max_age_in_cache', + default=10.0) +@click.option('--cache_capacity', + default=10) +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/caching_cmm_encrypt') +def encrypt( + plaintext_data_filename: str, + kms_key_id: str, + max_age_in_cache: float, + cache_capacity: int, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_cmm function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + caching_cmm = create_cmm(kms_key_id, max_age_in_cache, cache_capacity) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_cmm(plaintext_data, caching_cmm) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_caching_cmm(): + """Click group helper function""" + + +@decrypt_caching_cmm.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/caching_cmm/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--kms_key_id', + default='arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f') +@click.option('--max_age_in_cache', + default=10.0) +@click.option('--cache_capacity', + default=10) +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/caching_cmm_decrypt') +def decrypt( + ciphertext_data_filename: str, + kms_key_id: str, + max_age_in_cache: float, + cache_capacity: int, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_cmm function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + caching_cmm = create_cmm(kms_key_id, max_age_in_cache, cache_capacity) + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_cmm(ciphertext_data, caching_cmm) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +caching_cmm_test = click.CommandCollection(sources=[create_caching_cmm, + encrypt_caching_cmm, + decrypt_caching_cmm]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_cmm function""" + result = runner.invoke(create_caching_cmm.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_cmm function""" + result = runner.invoke(encrypt_caching_cmm.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_cmm function""" + result = runner.invoke(decrypt_caching_cmm.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + caching_cmm_test() diff --git a/performance_tests/test/master_key_providers/test_raw_aes_master_key_provider.py b/performance_tests/test/master_key_providers/test_raw_aes_master_key_provider.py new file mode 100644 index 000000000..cf39963bf --- /dev/null +++ b/performance_tests/test/master_key_providers/test_raw_aes_master_key_provider.py @@ -0,0 +1,156 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the Raw AES Master key provider.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.master_key_providers.raw_aes_master_key_provider import ( + create_key_provider, + decrypt_using_key_provider, + encrypt_using_key_provider, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_raw_aes_key_provider(): + """Click group helper function""" + + +@create_raw_aes_key_provider.command() +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_key_provider_create') +def create( + n_iters: int, + output_file: str +): + """Performance test for the create_key_provider function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_key_provider() + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_raw_aes_key_provider(): + """Click group helper function""" + + +@encrypt_raw_aes_key_provider.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_key_provider_encrypt') +def encrypt( + plaintext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_key_provider function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + key_provider = create_key_provider() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_key_provider(plaintext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_raw_aes_key_provider(): + """Click group helper function""" + + +@decrypt_raw_aes_key_provider.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/raw_aes/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_aes_key_provider_decrypt') +def decrypt( + ciphertext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_key_provider function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + key_provider = create_key_provider() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_key_provider(ciphertext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +raw_aes_key_provider_test = click.CommandCollection(sources=[create_raw_aes_key_provider, + encrypt_raw_aes_key_provider, + decrypt_raw_aes_key_provider]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_key_provider function""" + result = runner.invoke(create_raw_aes_key_provider.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_key_provider function""" + result = runner.invoke(encrypt_raw_aes_key_provider.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_key_provider function""" + result = runner.invoke(decrypt_raw_aes_key_provider.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + raw_aes_key_provider_test() diff --git a/performance_tests/test/master_key_providers/test_raw_rsa_master_key_provider.py b/performance_tests/test/master_key_providers/test_raw_rsa_master_key_provider.py new file mode 100644 index 000000000..63b00a0e3 --- /dev/null +++ b/performance_tests/test/master_key_providers/test_raw_rsa_master_key_provider.py @@ -0,0 +1,156 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""This is a performance test for creating the Raw RSA Master key provider.""" + +import os +import time + +import click +import click.testing +import pytest +from tqdm import tqdm + +from aws_encryption_sdk_performance_tests.master_key_providers.raw_rsa_master_key_provider import ( + create_key_provider, + decrypt_using_key_provider, + encrypt_using_key_provider, +) +from aws_encryption_sdk_performance_tests.utils.util import PerfTestUtils + +MODULE_ABS_PATH = os.path.abspath(__file__) + + +@click.group() +def create_raw_rsa_key_provider(): + """Click group helper function""" + + +@create_raw_rsa_key_provider.command() +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_key_provider_create') +def create( + n_iters: int, + output_file: str +): + """Performance test for the create_key_provider function.""" + time_list = [] + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + create_key_provider() + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def encrypt_raw_rsa_key_provider(): + """Click group helper function""" + + +@encrypt_raw_rsa_key_provider.command() +@click.option('--plaintext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/plaintext/plaintext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.dat') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_key_provider_encrypt') +def encrypt( + plaintext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the encrypt_using_key_provider function.""" + plaintext_data = PerfTestUtils.read_file(plaintext_data_filename) + + key_provider = create_key_provider() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + encrypt_using_key_provider(plaintext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +@click.group() +def decrypt_raw_rsa_key_provider(): + """Click group helper function""" + + +@decrypt_raw_rsa_key_provider.command() +@click.option('--ciphertext_data_filename', + default='/'.join(MODULE_ABS_PATH.split("/")[:-2]) + '/resources/ciphertext/raw_rsa/ciphertext-data-' + + PerfTestUtils.DEFAULT_FILE_SIZE + '.ct') +@click.option('--n_iters', + default=PerfTestUtils.DEFAULT_N_ITERS) +@click.option('--output_file', + default='/'.join(MODULE_ABS_PATH.split("/")[:-3]) + '/results/raw_rsa_key_provider_decrypt') +def decrypt( + ciphertext_data_filename: str, + n_iters: int, + output_file: str +): + """Performance test for the decrypt_using_key_provider function.""" + ciphertext_data = PerfTestUtils.read_file(ciphertext_data_filename) + + key_provider = create_key_provider() + time_list = [] + + for _ in tqdm(range(n_iters)): + curr_time = time.time() + + decrypt_using_key_provider(ciphertext_data, key_provider) + + # calculate elapsed time in milliseconds + elapsed_time = (time.time() - curr_time) * 1000 + time_list.append(elapsed_time) + + PerfTestUtils.write_time_list_to_csv(time_list, output_file) + + +raw_rsa_key_provider_test = click.CommandCollection(sources=[create_raw_rsa_key_provider, + encrypt_raw_rsa_key_provider, + decrypt_raw_rsa_key_provider]) + + +@pytest.fixture +def runner(): + """Click runner""" + return click.testing.CliRunner() + + +def test_create(runner): + """Test the create_key_provider function""" + result = runner.invoke(create_raw_rsa_key_provider.commands['create'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_encrypt(runner): + """Test the encrypt_using_key_provider function""" + result = runner.invoke(encrypt_raw_rsa_key_provider.commands['encrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +def test_decrypt(runner): + """Test the decrypt_using_key_provider function""" + result = runner.invoke(decrypt_raw_rsa_key_provider.commands['decrypt'], + ['--n_iters', PerfTestUtils.DEFAULT_TESTING_N_ITERS]) + assert result.exit_code == 0 + + +if __name__ == "__main__": + raw_rsa_key_provider_test() diff --git a/performance_tests/test/resources/__init__.py b/performance_tests/test/resources/__init__.py new file mode 100644 index 000000000..120179eda --- /dev/null +++ b/performance_tests/test/resources/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Stub module indicator to make linter configuration simpler.""" diff --git a/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-empty.ct b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-empty.ct new file mode 100644 index 000000000..5bfb39a4e Binary files /dev/null and b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-empty.ct differ diff --git a/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-large.ct b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-large.ct new file mode 100644 index 000000000..a9204fba3 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-large.ct differ diff --git a/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-medium.ct b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-medium.ct new file mode 100644 index 000000000..bf8ce38e8 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-medium.ct differ diff --git a/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-small.ct b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-small.ct new file mode 100644 index 000000000..63dac90b7 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/caching_cmm/ciphertext-data-small.ct differ diff --git a/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-empty.ct b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-empty.ct new file mode 100644 index 000000000..df4279720 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-empty.ct differ diff --git a/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-large.ct b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-large.ct new file mode 100644 index 000000000..97f71fca7 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-large.ct differ diff --git a/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-medium.ct b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-medium.ct new file mode 100644 index 000000000..dbb9ea9c0 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-medium.ct differ diff --git a/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-small.ct b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-small.ct new file mode 100644 index 000000000..ae53e0787 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/hierarchy/ciphertext-data-small.ct differ diff --git a/performance_tests/test/resources/ciphertext/kms/ciphertext-data-empty.ct b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-empty.ct new file mode 100644 index 000000000..18f169687 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-empty.ct differ diff --git a/performance_tests/test/resources/ciphertext/kms/ciphertext-data-large.ct b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-large.ct new file mode 100644 index 000000000..076926bfb Binary files /dev/null and b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-large.ct differ diff --git a/performance_tests/test/resources/ciphertext/kms/ciphertext-data-medium.ct b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-medium.ct new file mode 100644 index 000000000..a954c7134 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-medium.ct differ diff --git a/performance_tests/test/resources/ciphertext/kms/ciphertext-data-small.ct b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-small.ct new file mode 100644 index 000000000..473e914e6 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/kms/ciphertext-data-small.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-empty.ct b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-empty.ct new file mode 100644 index 000000000..3eb401da7 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-empty.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-large.ct b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-large.ct new file mode 100644 index 000000000..ea9e7166d Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-large.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-medium.ct b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-medium.ct new file mode 100644 index 000000000..e62387249 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-medium.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-small.ct b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-small.ct new file mode 100644 index 000000000..61c056b37 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_aes/ciphertext-data-small.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-empty.ct b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-empty.ct new file mode 100644 index 000000000..202643e9a Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-empty.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-large.ct b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-large.ct new file mode 100644 index 000000000..259d83ea5 Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-large.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-medium.ct b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-medium.ct new file mode 100644 index 000000000..a249cfbaf Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-medium.ct differ diff --git a/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-small.ct b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-small.ct new file mode 100644 index 000000000..390a36a4b Binary files /dev/null and b/performance_tests/test/resources/ciphertext/raw_rsa/ciphertext-data-small.ct differ diff --git a/performance_tests/test/resources/plaintext/plaintext-data-empty.dat b/performance_tests/test/resources/plaintext/plaintext-data-empty.dat new file mode 100644 index 000000000..e69de29bb diff --git a/performance_tests/test/resources/plaintext/plaintext-data-large.dat b/performance_tests/test/resources/plaintext/plaintext-data-large.dat new file mode 100644 index 000000000..22bad9f3f --- /dev/null +++ b/performance_tests/test/resources/plaintext/plaintext-data-large.dat @@ -0,0 +1,21 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Et netus et malesuada fames. Bibendum enim facilisis gravida neque convallis. Tortor consequat id porta nibh venenatis cras. Lacus sed viverra tellus in hac habitasse platea dictumst. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Risus pretium quam vulputate dignissim suspendisse in. Ante in nibh mauris cursus mattis molestie a iaculis at. Nulla porttitor massa id neque aliquam vestibulum morbi blandit. Urna et pharetra pharetra massa massa ultricies mi quis. Leo duis ut diam quam nulla porttitor massa id. Vitae suscipit tellus mauris a diam maecenas sed enim ut. + +Non nisi est sit amet facilisis magna etiam tempor. Mi bibendum neque egestas congue quisque egestas diam in arcu. Volutpat est velit egestas dui id ornare arcu odio ut. Amet tellus cras adipiscing enim eu turpis egestas. Tortor dignissim convallis aenean et tortor at risus viverra. Interdum consectetur libero id faucibus nisl tincidunt eget. In eu mi bibendum neque egestas congue quisque egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Leo vel orci porta non pulvinar. Amet massa vitae tortor condimentum. Malesuada fames ac turpis egestas maecenas pharetra. Feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Quam elementum pulvinar etiam non quam lacus. Accumsan in nisl nisi scelerisque eu ultrices. Pretium aenean pharetra magna ac placerat vestibulum. Dui faucibus in ornare quam viverra orci sagittis eu volutpat. Amet venenatis urna cursus eget nunc scelerisque viverra mauris in. Penatibus et magnis dis parturient montes nascetur ridiculus mus mauris. Quis commodo odio aenean sed adipiscing diam donec adipiscing tristique. + +Leo a diam sollicitudin tempor id. Tempor orci eu lobortis elementum nibh. Sit amet commodo nulla facilisi nullam vehicula. Hendrerit gravida rutrum quisque non tellus. Diam vulputate ut pharetra sit. Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Tempus iaculis urna id volutpat lacus laoreet non curabitur gravida. Porttitor lacus luctus accumsan tortor posuere ac ut consequat semper. Tortor vitae purus faucibus ornare. Risus viverra adipiscing at in tellus integer. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse. Facilisis sed odio morbi quis commodo odio aenean sed. At auctor urna nunc id. Ac tortor vitae purus faucibus ornare suspendisse sed nisi. Suspendisse interdum consectetur libero id faucibus nisl. Tellus id interdum velit laoreet id. Mus mauris vitae ultricies leo integer. Metus vulputate eu scelerisque felis imperdiet proin. + +Venenatis cras sed felis eget velit aliquet sagittis id. Turpis cursus in hac habitasse. Magna fringilla urna porttitor rhoncus dolor purus non. In tellus integer feugiat scelerisque varius morbi. Tortor consequat id porta nibh venenatis cras sed. Ut sem viverra aliquet eget sit amet tellus cras. Semper risus in hendrerit gravida. Libero enim sed faucibus turpis in. Ultricies leo integer malesuada nunc vel risus commodo. Ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant. Varius duis at consectetur lorem donec massa sapien faucibus et. Ornare arcu dui vivamus arcu felis bibendum ut tristique et. + +Diam quis enim lobortis scelerisque fermentum dui faucibus in. Cursus mattis molestie a iaculis at erat. Diam sit amet nisl suscipit adipiscing. Ultrices dui sapien eget mi proin sed libero. Purus ut faucibus pulvinar elementum integer enim neque. Ultricies mi quis hendrerit dolor magna eget. Morbi tincidunt ornare massa eget. Mauris cursus mattis molestie a iaculis at erat pellentesque. Phasellus vestibulum lorem sed risus. Sodales ut etiam sit amet nisl purus in. Habitant morbi tristique senectus et netus et. Consectetur lorem donec massa sapien faucibus et molestie. + +Montes nascetur ridiculus mus mauris vitae ultricies. Lectus urna duis convallis convallis. Pulvinar proin gravida hendrerit lectus. Semper auctor neque vitae tempus quam pellentesque nec. Donec pretium vulputate sapien nec. Id aliquet lectus proin nibh nisl. Enim ut sem viverra aliquet eget sit amet tellus. Vel orci porta non pulvinar neque laoreet suspendisse interdum consectetur. Feugiat vivamus at augue eget arcu dictum varius. Venenatis tellus in metus vulputate eu. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Id venenatis a condimentum vitae. Lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis. + +Nisi scelerisque eu ultrices vitae. Eu feugiat pretium nibh ipsum consequat nisl vel pretium. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Sit amet cursus sit amet dictum. Fermentum iaculis eu non diam. Quis imperdiet massa tincidunt nunc pulvinar. Tempor commodo ullamcorper a lacus vestibulum sed. Venenatis urna cursus eget nunc scelerisque viverra mauris in. Vulputate mi sit amet mauris commodo quis imperdiet massa. Non nisi est sit amet facilisis magna etiam tempor orci. Consectetur libero id faucibus nisl tincidunt eget nullam. Sit amet risus nullam eget felis eget nunc. Aliquet porttitor lacus luctus accumsan. Vitae congue eu consequat ac felis donec. Vehicula ipsum a arcu cursus vitae congue mauris rhoncus. Fringilla phasellus faucibus scelerisque eleifend donec pretium vulputate. + +Massa eget egestas purus viverra. Nunc sed augue lacus viverra vitae congue eu consequat. Lectus quam id leo in. Augue eget arcu dictum varius duis. Nulla facilisi cras fermentum odio eu feugiat pretium. Adipiscing diam donec adipiscing tristique risus. Imperdiet dui accumsan sit amet. Volutpat commodo sed egestas egestas fringilla phasellus faucibus scelerisque. Dolor sit amet consectetur adipiscing elit duis. In fermentum et sollicitudin ac orci phasellus egestas tellus rutrum. Ridiculus mus mauris vitae ultricies leo integer malesuada. Nulla pharetra diam sit amet nisl. Nec dui nunc mattis enim ut tellus. Morbi non arcu risus quis varius quam quisque. Ac auctor augue mauris augue neque gravida in fermentum et. Morbi tincidunt augue interdum velit euismod. Sem viverra aliquet eget sit amet tellus cras adipiscing enim. Volutpat commodo sed egestas egestas fringilla phasellus faucibus scelerisque. Odio facilisis mauris sit amet. Amet mattis vulputate enim nulla aliquet. + +Nisi vitae suscipit tellus mauris a diam maecenas sed enim. Venenatis urna cursus eget nunc scelerisque viverra. Neque egestas congue quisque egestas diam in. Adipiscing vitae proin sagittis nisl. Sodales neque sodales ut etiam sit. Non consectetur a erat nam at. Ac felis donec et odio. Adipiscing tristique risus nec feugiat in fermentum posuere urna. Ultrices in iaculis nunc sed augue lacus viverra vitae. Enim sit amet venenatis urna. Amet consectetur adipiscing elit pellentesque. + +Venenatis a condimentum vitae sapien pellentesque. Ut faucibus pulvinar elementum integer enim neque. Nisl nunc mi ipsum faucibus vitae aliquet. Netus et malesuada fames ac. Et odio pellentesque diam volutpat commodo sed egestas egestas fringilla. Ut diam quam nulla porttitor massa id. Id donec ultrices tincidunt arcu non sodales neque sodales ut. Viverra ipsum nunc aliquet bibendum enim. Lacus vestibulum sed arcu non odio. Lobortis mattis aliquam faucibus purus in massa tempor. Tortor at auctor urna nunc id cursus metus aliquam eleifend. Ornare suspendisse sed nisi lacus sed viverra tellus in. Tristique magna sit amet purus gravida quis. At ultrices mi tempus imperdiet nulla malesuada. Erat imperdiet sed euismod nisi. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada. Fermentum dui faucibus in ornare quam viverra orci sagittis. Nec dui nunc mattis enim ut tellus elementum sagittis. + +Suspendisse interdum consectetur libero id faucibus nisl. Bibendum enim facilisis gravida neque convallis. Nisi vitae suscipit tellus mauris a. Massa ultricies mi quis hendreri. \ No newline at end of file diff --git a/performance_tests/test/resources/plaintext/plaintext-data-medium.dat b/performance_tests/test/resources/plaintext/plaintext-data-medium.dat new file mode 100644 index 000000000..3313ba519 --- /dev/null +++ b/performance_tests/test/resources/plaintext/plaintext-data-medium.dat @@ -0,0 +1,11 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Et netus et malesuada fames. Bibendum enim facilisis gravida neque convallis. Tortor consequat id porta nibh venenatis cras. Lacus sed viverra tellus in hac habitasse platea dictumst. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Risus pretium quam vulputate dignissim suspendisse in. Ante in nibh mauris cursus mattis molestie a iaculis at. Nulla porttitor massa id neque aliquam vestibulum morbi blandit. Urna et pharetra pharetra massa massa ultricies mi quis. Leo duis ut diam quam nulla porttitor massa id. Vitae suscipit tellus mauris a diam maecenas sed enim ut. + +Non nisi est sit amet facilisis magna etiam tempor. Mi bibendum neque egestas congue quisque egestas diam in arcu. Volutpat est velit egestas dui id ornare arcu odio ut. Amet tellus cras adipiscing enim eu turpis egestas. Tortor dignissim convallis aenean et tortor at risus viverra. Interdum consectetur libero id faucibus nisl tincidunt eget. In eu mi bibendum neque egestas congue quisque egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Leo vel orci porta non pulvinar. Amet massa vitae tortor condimentum. Malesuada fames ac turpis egestas maecenas pharetra. Feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Quam elementum pulvinar etiam non quam lacus. Accumsan in nisl nisi scelerisque eu ultrices. Pretium aenean pharetra magna ac placerat vestibulum. Dui faucibus in ornare quam viverra orci sagittis eu volutpat. Amet venenatis urna cursus eget nunc scelerisque viverra mauris in. Penatibus et magnis dis parturient montes nascetur ridiculus mus mauris. Quis commodo odio aenean sed adipiscing diam donec adipiscing tristique. + +Leo a diam sollicitudin tempor id. Tempor orci eu lobortis elementum nibh. Sit amet commodo nulla facilisi nullam vehicula. Hendrerit gravida rutrum quisque non tellus. Diam vulputate ut pharetra sit. Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Tempus iaculis urna id volutpat lacus laoreet non curabitur gravida. Porttitor lacus luctus accumsan tortor posuere ac ut consequat semper. Tortor vitae purus faucibus ornare. Risus viverra adipiscing at in tellus integer. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse. Facilisis sed odio morbi quis commodo odio aenean sed. At auctor urna nunc id. Ac tortor vitae purus faucibus ornare suspendisse sed nisi. Suspendisse interdum consectetur libero id faucibus nisl. Tellus id interdum velit laoreet id. Mus mauris vitae ultricies leo integer. Metus vulputate eu scelerisque felis imperdiet proin. + +Venenatis cras sed felis eget velit aliquet sagittis id. Turpis cursus in hac habitasse. Magna fringilla urna porttitor rhoncus dolor purus non. In tellus integer feugiat scelerisque varius morbi. Tortor consequat id porta nibh venenatis cras sed. Ut sem viverra aliquet eget sit amet tellus cras. Semper risus in hendrerit gravida. Libero enim sed faucibus turpis in. Ultricies leo integer malesuada nunc vel risus commodo. Ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant. Varius duis at consectetur lorem donec massa sapien faucibus et. Ornare arcu dui vivamus arcu felis bibendum ut tristique et. + +Diam quis enim lobortis scelerisque fermentum dui faucibus in. Cursus mattis molestie a iaculis at erat. Diam sit amet nisl suscipit adipiscing. Ultrices dui sapien eget mi proin sed libero. Purus ut faucibus pulvinar elementum integer enim neque. Ultricies mi quis hendrerit dolor magna eget. Morbi tincidunt ornare massa eget. Mauris cursus mattis molestie a iaculis at erat pellentesque. Phasellus vestibulum lorem sed risus. Sodales ut etiam sit amet nisl purus in. Habitant morbi tristique senectus et netus et. Consectetur lorem donec massa sapien faucibus et molestie. + +Montes nascetur ridiculus mus mauris vitae ultricies. Lectus urna duis convallis convallis. Pulvinar pr. \ No newline at end of file diff --git a/performance_tests/test/resources/plaintext/plaintext-data-small.dat b/performance_tests/test/resources/plaintext/plaintext-data-small.dat new file mode 100644 index 000000000..b8475e61f --- /dev/null +++ b/performance_tests/test/resources/plaintext/plaintext-data-small.dat @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consect. \ No newline at end of file diff --git a/performance_tests/tox.ini b/performance_tests/tox.ini new file mode 100644 index 000000000..1b7d073aa --- /dev/null +++ b/performance_tests/tox.ini @@ -0,0 +1,215 @@ +[tox] +envlist = + # The performance tests only work for python 3.11 and 3.12 + py{311,312}-performance_tests-mpl + bandit, doc8 + ; {flake8, pylint}{,-tests}, + isort-check, black-check, + # prone to false positives + vulture + +# Additional test environments: +# +# 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: +# +# 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:base-command] +commands = pytest test/ +deps = + click + + +[testenv] +passenv = + # Pass through AWS credentials + AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ + # AWS Role access in CodeBuild is via the contaner URI + AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ + # Pass through AWS profile name (useful for local testing) + AWS_PROFILE +sitepackages = False +deps = + -rrequirements.txt + # Install the MPL requirements if the `-mpl` suffix is present + mpl: -rrequirements_mpl.txt + .. +commands = + performance_tests: {[testenv:base-command]commands} + +[testenv:blacken-src] +basepython = python3 +deps = -r../dev_requirements/linter-requirements.txt +commands = + black --line-length 120 \ + src/aws_encryption_sdk_performance_tests/ \ + setup.py \ + test/ \ + {posargs} + +# Linters +[testenv:flake8] +basepython = python3 +deps = -r../dev_requirements/linter-requirements.txt +commands = + flake8 \ + src/aws_encryption_sdk_performance_tests/ \ + setup.py \ + {posargs} + +[testenv:flake8-tests] +basepython = {[testenv:flake8]basepython} +deps = -r../dev_requirements/linter-requirements.txt +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,D \ + test/ + +[testenv:pylint] +basepython = python3 +deps = + -r../dev_requirements/linter-requirements.txt +commands = + pylint \ + --rcfile=pylintrc \ + src/aws_encryption_sdk_performance_tests/ \ + setup.py \ + {posargs} + +[testenv:pylint-tests] +basepython = {[testenv:pylint]basepython} +deps = {[testenv:pylint]deps} +commands = + pylint \ + --rcfile=pylintrc \ + 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 = -r../dev_requirements/linter-requirements.txt +commands = seed-isort-config + +[testenv:isort] +basepython = python3 +deps = -r../dev_requirements/linter-requirements.txt +commands = isort -rc \ + src \ + test \ + 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 = -r../dev_requirements/linter-requirements.txt +commands = doc8 README.rst + +[testenv:readme] +basepython = python3 +deps = -r../dev_requirements/linter-requirements.txt +commands = python setup.py check -r -s + +[testenv:bandit] +basepython = python3 +deps = -r../dev_requirements/linter-requirements.txt +commands = bandit -r src/aws_encryption_sdk_performance_tests/ + +[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} + +# Release tooling +[testenv:park] +basepython = python3 +skip_install = true +deps = -r../dev_requirements/release-requirements.txt +commands = python setup.py park + +[testenv:build] +basepython = python3 +skip_install = true +deps = + -r../dev_requirements/release-requirements.txt +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/* diff --git a/requirements.txt b/requirements.txt index fab293c05..506801993 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.10.0 -cryptography>=2.5.0 +cryptography>=3.4.6 attrs>=17.4.0 wrapt>=1.10.11 diff --git a/requirements_mpl.txt b/requirements_mpl.txt new file mode 100644 index 000000000..8912641d0 --- /dev/null +++ b/requirements_mpl.txt @@ -0,0 +1 @@ +aws-cryptographic-material-providers>=1.7.4,<=1.10.0 diff --git a/setup.py b/setup.py index 2db856d89..587495fd4 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,9 @@ def get_requirements(): keywords="aws-encryption-sdk aws kms encryption", license="Apache License 2.0", install_requires=get_requirements(), + extras_require={ + "MPL": ["aws-cryptographic-material-providers>=1.7.4,<=1.10.0"], + }, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -46,10 +49,11 @@ def get_requirements(): "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Security", "Topic :: Security :: Cryptography", diff --git a/src/aws_encryption_sdk/__init__.py b/src/aws_encryption_sdk/__init__.py index 661d41ee6..326dacc13 100644 --- a/src/aws_encryption_sdk/__init__.py +++ b/src/aws_encryption_sdk/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """High level AWS Encryption SDK client functions.""" # Below are imported for ease of use by implementors import warnings @@ -45,7 +35,8 @@ class EncryptionSDKClientConfig(object): :param commitment_policy: The commitment policy to apply to encryption and decryption requests :type commitment_policy: aws_encryption_sdk.materials_manager.identifiers.CommitmentPolicy - :param max_encrypted_data_keys: The maximum number of encrypted data keys to allow during encryption and decryption + :param max_encrypted_data_keys: The maximum number of encrypted data keys to allow during + encryption and decryption :type max_encrypted_data_keys: None or positive int """ @@ -104,15 +95,26 @@ def encrypt(self, **kwargs): .. code:: python + >>> import boto3 + >>> from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + >>> from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + >>> from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput + >>> from aws_cryptographic_material_providers.mpl.references import IKeyring >>> import aws_encryption_sdk >>> client = aws_encryption_sdk.EncryptionSDKClient() - >>> kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - ... 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - ... 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ... ]) + >>> mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + ... config=MaterialProvidersConfig() + ... ) + >>> keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + ... kms_key_id='arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', + ... kms_client=boto3.client('kms', region_name="us-west-2") + ... ) + >>> kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + ... input=keyring_input + ... ) >>> my_ciphertext, encryptor_header = client.encrypt( ... source=my_plaintext, - ... key_provider=kms_key_provider + ... keyring=kms_keyring ... ) :param config: Client configuration object (config or individual parameters required) @@ -120,11 +122,14 @@ def encrypt(self, **kwargs): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param materials_manager: `CryptoMaterialsManager` that returns cryptographic materials - (requires either `materials_manager` or `key_provider`) + (requires either `materials_manager` or `keyring`) :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager :param key_provider: `MasterKeyProvider` that returns data keys for encryption (requires either `materials_manager` or `key_provider`) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` that returns keyring for encryption + (requires either `materials_manager` or `keyring`) + :type keyring: aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -134,7 +139,7 @@ def encrypt(self, **kwargs): .. note:: If `source_length` and `materials_manager` are both provided, the total plaintext bytes encrypted will not be allowed to exceed `source_length`. To maintain backwards compatibility, - this is not enforced if a `key_provider` is provided. + this is not enforced if a `keyring` is provided. :param dict encryption_context: Dictionary defining encryption context :param algorithm: Algorithm to use for encryption @@ -158,15 +163,26 @@ def decrypt(self, **kwargs): .. code:: python + >>> import boto3 + >>> from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + >>> from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + >>> from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput + >>> from aws_cryptographic_material_providers.mpl.references import IKeyring >>> import aws_encryption_sdk >>> client = aws_encryption_sdk.EncryptionSDKClient() - >>> kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - ... 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - ... 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ... ]) - >>> my_ciphertext, encryptor_header = client.decrypt( + >>> mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + ... config=MaterialProvidersConfig() + ... ) + >>> keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + ... kms_key_id='arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', + ... kms_client=boto3.client('kms', region_name="us-west-2") + ... ) + >>> kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + ... input=keyring_input + ... ) + >>> my_plaintext, decryptor_header = client.decrypt( ... source=my_ciphertext, - ... key_provider=kms_key_provider + ... keyring=kms_keyring ... ) :param config: Client configuration object (config or individual parameters required) @@ -174,17 +190,23 @@ def decrypt(self, **kwargs): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param materials_manager: `CryptoMaterialsManager` that returns cryptographic materials - (requires either `materials_manager` or `key_provider`) + (requires either `materials_manager` or `keyring`) :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager :param key_provider: `MasterKeyProvider` that returns data keys for decryption (requires either `materials_manager` or `key_provider`) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` that returns keyring for encryption + (requires either `materials_manager` or `keyring`) + :type keyring: aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: If source_length is not provided and read() is called, will attempt to seek() to the end of the stream and tell() to find the length of source data. + :param dict encryption_context: Dictionary defining encryption context to validate + on decrypt. This is ONLY validated on decrypt if using a CMM from the + aws-cryptographic-material-providers library. :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. :returns: Tuple containing the decrypted plaintext and the message header object @@ -215,28 +237,39 @@ def stream(self, **kwargs): .. code:: python + >>> import boto3 + >>> from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + >>> from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + >>> from aws_cryptographic_material_providers.mpl.models import CreateAwsKmsKeyringInput + >>> from aws_cryptographic_material_providers.mpl.references import IKeyring >>> import aws_encryption_sdk >>> client = aws_encryption_sdk.EncryptionSDKClient() - >>> kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[ - ... 'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', - ... 'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333' - ... ]) + >>> mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + ... config=MaterialProvidersConfig() + ... ) + >>> keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + ... kms_key_id='arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222', + ... kms_client=boto3.client('kms', region_name="us-west-2") + ... ) + >>> kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring( + ... input=keyring_input + ... ) >>> plaintext_filename = 'my-secret-data.dat' >>> ciphertext_filename = 'my-encrypted-data.ct' >>> with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file: - ... with client.stream( + ... with client.stream( ... mode='e', ... source=pt_file, - ... key_provider=kms_key_provider + ... keyring=kms_keyring ... ) as encryptor: ... for chunk in encryptor: - ... ct_file.write(chunk) - >>> new_plaintext_filename = 'my-decrypted-data.dat' - >>> with open(ciphertext_filename, 'rb') as ct_file, open(new_plaintext_filename, 'wb') as pt_file: + ... ct_file.write(chunk) + >>> decrypted_filename = 'my-decrypted-data.dat' + >>> with open(ciphertext_filename, 'rb') as ct_file, open(decrypted_filename, 'wb') as pt_file: ... with client.stream( ... mode='d', ... source=ct_file, - ... key_provider=kms_key_provider + ... keyring=kms_keyring ... ) as decryptor: ... for chunk in decryptor: ... pt_file.write(chunk) diff --git a/src/aws_encryption_sdk/caches/__init__.py b/src/aws_encryption_sdk/caches/__init__.py index 9d2afa485..a7877f381 100644 --- a/src/aws_encryption_sdk/caches/__init__.py +++ b/src/aws_encryption_sdk/caches/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Common functions and structures for use in cryptographic materials caches. .. versionadded:: 1.3.0 diff --git a/src/aws_encryption_sdk/caches/base.py b/src/aws_encryption_sdk/caches/base.py index 1d923ab2b..9166e79d7 100644 --- a/src/aws_encryption_sdk/caches/base.py +++ b/src/aws_encryption_sdk/caches/base.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Base class interface for caches for use with caching crypto material managers.""" import abc diff --git a/src/aws_encryption_sdk/caches/local.py b/src/aws_encryption_sdk/caches/local.py index 90a961aa7..9832447b7 100644 --- a/src/aws_encryption_sdk/caches/local.py +++ b/src/aws_encryption_sdk/caches/local.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Local, in-memory, LRU, cryptographic materials cache for use with caching cryptographic materials providers.""" import logging import weakref diff --git a/src/aws_encryption_sdk/caches/null.py b/src/aws_encryption_sdk/caches/null.py index f26d3dd54..376de92f3 100644 --- a/src/aws_encryption_sdk/caches/null.py +++ b/src/aws_encryption_sdk/caches/null.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Null cache: a cache which does not cache.""" from ..exceptions import CacheKeyError from . import CryptoMaterialsCacheEntry diff --git a/src/aws_encryption_sdk/compatability.py b/src/aws_encryption_sdk/compatability.py index 4dbc022d0..9900a8893 100644 --- a/src/aws_encryption_sdk/compatability.py +++ b/src/aws_encryption_sdk/compatability.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains logic for checking ESDK and Python Version""" import sys import warnings @@ -23,9 +13,10 @@ def _warn_deprecated_python(): (2, 7): {"date": DEPRECATION_DATE_MAP["2.x"]}, (3, 4): {"date": DEPRECATION_DATE_MAP["2.x"]}, (3, 5): {"date": "2021-11-10"}, + (3, 7): {"date": "2024-03-04"}, } py_version = (sys.version_info.major, sys.version_info.minor) - minimum_version = (3, 6) + minimum_version = (3, 8) if py_version in deprecated_versions: params = deprecated_versions[py_version] @@ -35,5 +26,5 @@ def _warn_deprecated_python(): "bug fixes, and security updates please upgrade to Python {}.{} or " "later. For more information, see SUPPORT_POLICY.rst: " "https://github.com/aws/aws-encryption-sdk-python/blob/master/SUPPORT_POLICY.rst" - ).format(py_version[0], py_version[1], minimum_version[0], minimum_version[1], params["date"]) + ).format(py_version[0], py_version[1], params["date"], minimum_version[0], minimum_version[1]) warnings.warn(warning, DeprecationWarning) diff --git a/src/aws_encryption_sdk/exceptions.py b/src/aws_encryption_sdk/exceptions.py index ed4fea744..f8d6b8cf5 100644 --- a/src/aws_encryption_sdk/exceptions.py +++ b/src/aws_encryption_sdk/exceptions.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains exception classes for AWS Encryption SDK.""" diff --git a/src/aws_encryption_sdk/identifiers.py b/src/aws_encryption_sdk/identifiers.py index d4397335c..8a5a2c8a1 100644 --- a/src/aws_encryption_sdk/identifiers.py +++ b/src/aws_encryption_sdk/identifiers.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """AWS Encryption SDK native data structures for defining implementation-specific characteristics.""" import struct from enum import Enum @@ -27,7 +17,7 @@ # We only actually need these imports when running the mypy checks pass -__version__ = "3.1.1" +__version__ = "4.0.1" USER_AGENT_SUFFIX = "AwsEncryptionSdkPython/{}".format(__version__) diff --git a/src/aws_encryption_sdk/internal/__init__.py b/src/aws_encryption_sdk/internal/__init__.py index 7a78f48ea..e6f21eae2 100644 --- a/src/aws_encryption_sdk/internal/__init__.py +++ b/src/aws_encryption_sdk/internal/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Internal Implementation Details .. warning:: diff --git a/src/aws_encryption_sdk/internal/arn.py b/src/aws_encryption_sdk/internal/arn.py index 4efe6b7d5..eb0edf5f0 100644 --- a/src/aws_encryption_sdk/internal/arn.py +++ b/src/aws_encryption_sdk/internal/arn.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Utility class for processing Amazon Resource Names (ARNs)""" from aws_encryption_sdk.exceptions import MalformedArnError diff --git a/src/aws_encryption_sdk/internal/crypto/__init__.py b/src/aws_encryption_sdk/internal/crypto/__init__.py index 9717f65bd..26d4f6a4e 100644 --- a/src/aws_encryption_sdk/internal/crypto/__init__.py +++ b/src/aws_encryption_sdk/internal/crypto/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Cryptographic modules.""" # Backwards compatible import for use by RawMasterKeyProvider implementations. from .wrapping_keys import WrappingKey # noqa diff --git a/src/aws_encryption_sdk/internal/crypto/authentication.py b/src/aws_encryption_sdk/internal/crypto/authentication.py index f90ac77e0..f59903b2a 100644 --- a/src/aws_encryption_sdk/internal/crypto/authentication.py +++ b/src/aws_encryption_sdk/internal/crypto/authentication.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains authentication primitives.""" import base64 import logging @@ -68,7 +58,7 @@ class Signer(_PrehashingAuthenticator): """ @classmethod - def from_key_bytes(cls, algorithm, key_bytes): + def from_key_bytes(cls, algorithm, key_bytes, encoding=serialization.Encoding.DER): """Builds a `Signer` from an algorithm suite and a raw signing key. :param algorithm: Algorithm on which to base signer @@ -76,7 +66,12 @@ def from_key_bytes(cls, algorithm, key_bytes): :param bytes key_bytes: Raw signing key :rtype: aws_encryption_sdk.internal.crypto.Signer """ - key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + if encoding == serialization.Encoding.DER: + key = serialization.load_der_private_key(data=key_bytes, password=None, backend=default_backend()) + elif encoding == serialization.Encoding.PEM: + key = serialization.load_pem_private_key(data=key_bytes, password=None, backend=default_backend()) + else: + raise ValueError(f"Unsupported encoding for Signer: {encoding}") return cls(algorithm, key) def key_bytes(self): diff --git a/src/aws_encryption_sdk/internal/crypto/data_keys.py b/src/aws_encryption_sdk/internal/crypto/data_keys.py index 13a3bc2b7..518b239a1 100644 --- a/src/aws_encryption_sdk/internal/crypto/data_keys.py +++ b/src/aws_encryption_sdk/internal/crypto/data_keys.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains data key helper functions.""" import logging import struct diff --git a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py index d2edde317..a7f1d1446 100644 --- a/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py +++ b/src/aws_encryption_sdk/internal/crypto/elliptic_curve.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains elliptic curve functionality.""" import logging from collections import namedtuple diff --git a/src/aws_encryption_sdk/internal/crypto/encryption.py b/src/aws_encryption_sdk/internal/crypto/encryption.py index 1e5523826..4766e22e8 100644 --- a/src/aws_encryption_sdk/internal/crypto/encryption.py +++ b/src/aws_encryption_sdk/internal/crypto/encryption.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains encryption primitives and helper functions.""" import logging diff --git a/src/aws_encryption_sdk/internal/crypto/iv.py b/src/aws_encryption_sdk/internal/crypto/iv.py index e5424057b..e0c7f975e 100644 --- a/src/aws_encryption_sdk/internal/crypto/iv.py +++ b/src/aws_encryption_sdk/internal/crypto/iv.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Helper functions used for generating deterministic initialization vectors (IVs). diff --git a/src/aws_encryption_sdk/internal/crypto/wrapping_keys.py b/src/aws_encryption_sdk/internal/crypto/wrapping_keys.py index 91f9fd834..903e6c863 100644 --- a/src/aws_encryption_sdk/internal/crypto/wrapping_keys.py +++ b/src/aws_encryption_sdk/internal/crypto/wrapping_keys.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Contains wrapping key primitives.""" import logging import os @@ -98,9 +88,12 @@ def decrypt(self, encrypted_wrapped_data_key, encryption_context): if self.wrapping_key_type is EncryptionKeyType.PUBLIC: raise IncorrectMasterKeyError("Public key cannot decrypt") if self.wrapping_key_type is EncryptionKeyType.PRIVATE: - return self._wrapping_key.decrypt( - ciphertext=encrypted_wrapped_data_key.ciphertext, padding=self.wrapping_algorithm.padding - ) + try: + return self._wrapping_key.decrypt( + ciphertext=encrypted_wrapped_data_key.ciphertext, padding=self.wrapping_algorithm.padding + ) + except ValueError: + raise IncorrectMasterKeyError("_wrapping_key cannot decrypt provided ciphertext") serialized_encryption_context = serialize_encryption_context(encryption_context=encryption_context) return decrypt( algorithm=self.wrapping_algorithm.algorithm, diff --git a/src/aws_encryption_sdk/internal/defaults.py b/src/aws_encryption_sdk/internal/defaults.py index 184e36bbc..833deec83 100644 --- a/src/aws_encryption_sdk/internal/defaults.py +++ b/src/aws_encryption_sdk/internal/defaults.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Default values for AWS Encryption SDK.""" import io diff --git a/src/aws_encryption_sdk/internal/deprecation.py b/src/aws_encryption_sdk/internal/deprecation.py new file mode 100644 index 000000000..18e587237 --- /dev/null +++ b/src/aws_encryption_sdk/internal/deprecation.py @@ -0,0 +1,32 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Module containing utilities for deprecated components.""" +import functools +import warnings + + +def deprecated(reason): + """Decorator to apply to classes to emit deprecation warnings.""" + def decorator(cls): + # If class does not define init, + # its default init it Python's object.__init__, + # which does nothing, but cannot be wrapped. + if cls.__init__ is object.__init__: + # Make a new init that just emits this deprecation warning. + def new_init(self, *args, **kwargs): # pylint: disable=unused-argument + warnings.warn(f"{cls.__name__} is deprecated: {reason}", + category=DeprecationWarning, stacklevel=2) + else: + original_init = cls.__init__ + + # Wrap the original init method with a deprecation warning. + @functools.wraps(cls.__init__) + def new_init(self, *args, **kwargs): + warnings.warn(f"{cls.__name__} is deprecated: {reason}", + category=DeprecationWarning, stacklevel=2) + original_init(self, *args, **kwargs) + + cls.__init__ = new_init + return cls + + return decorator diff --git a/src/aws_encryption_sdk/internal/formatting/__init__.py b/src/aws_encryption_sdk/internal/formatting/__init__.py index 9dd59a29f..8760f7855 100644 --- a/src/aws_encryption_sdk/internal/formatting/__init__.py +++ b/src/aws_encryption_sdk/internal/formatting/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Formatting functions for aws_encryption_sdk.""" from .serialize import serialize_header diff --git a/src/aws_encryption_sdk/internal/formatting/deserialize.py b/src/aws_encryption_sdk/internal/formatting/deserialize.py index b06b5ba11..c90dc9124 100644 --- a/src/aws_encryption_sdk/internal/formatting/deserialize.py +++ b/src/aws_encryption_sdk/internal/formatting/deserialize.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Components for handling AWS Encryption SDK message deserialization.""" from __future__ import division @@ -155,6 +145,17 @@ def deserialize_encrypted_data_keys(stream, max_encrypted_data_keys=None): (key_provider_information,) = unpack_values(">{}s".format(key_provider_information_length), stream) (encrypted_data_key_length,) = unpack_values(">H", stream) encrypted_data_key = stream.read(encrypted_data_key_length) + # ESDK-Python <4.0.1 incorrectly computed the key provider length for non-ASCII key provider IDs. + # The length in the header was computed as the length of the key provider ID as a string instead of + # the length of the key provider ID as UTF-8 bytes. + # If a non-ASCII key provider ID were supplied, the key provider ID's UTF-8 bytes written to the header + # would be truncated, and attempting to decrypt the message would result in a deserialization error. + # That error would be raised when calling `to_str(key_provider_identifier)` below. + # An impacted message can be decrypted by replacing the truncated provider ID with the expected provider ID + # in decryption code. + # Contact AWS for any questions about this approach. + # ESDK-Python >=4.0.1 corrects the serialization logic and writes the correct length and expected bytes + # to the message header. encrypted_data_keys.add( EncryptedDataKey( key_provider=MasterKeyInfo( @@ -475,9 +476,9 @@ def deserialize_frame(stream, header, verifier=None): frame_data["iv"] = frame_iv if final_frame is True: (content_length,) = unpack_values(">I", stream, verifier) - if content_length >= header.frame_length: + if content_length > header.frame_length: raise SerializationError( - "Invalid final frame length: {final} >= {normal}".format( + "Invalid final frame length: {final} > {normal}".format( final=content_length, normal=header.frame_length ) ) diff --git a/src/aws_encryption_sdk/internal/formatting/encryption_context.py b/src/aws_encryption_sdk/internal/formatting/encryption_context.py index 5f8b39327..949ebe6f2 100644 --- a/src/aws_encryption_sdk/internal/formatting/encryption_context.py +++ b/src/aws_encryption_sdk/internal/formatting/encryption_context.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Components for handling serialization and deserialization of encryption context data in AWS Encryption SDK messages. diff --git a/src/aws_encryption_sdk/internal/formatting/serialize.py b/src/aws_encryption_sdk/internal/formatting/serialize.py index b4d866099..27e98c399 100644 --- a/src/aws_encryption_sdk/internal/formatting/serialize.py +++ b/src/aws_encryption_sdk/internal/formatting/serialize.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Components for handling AWS Encryption SDK message serialization.""" import logging import struct @@ -45,16 +35,30 @@ def serialize_encrypted_data_key(encrypted_data_key): "H" # encrypted data key length "{enc_data_key_len}s" # encrypted data key ) + # ESDK-Python <4.0.1 incorrectly computed len_key_provider_id_bytes for non-ASCII key provider IDs. + # len_key_provider_id_bytes was computed as the length of the key provider ID as a string instead of + # the length of the key provider ID as UTF-8 bytes. + # If a non-ASCII key provider ID were supplied, the key provider ID as UTF-8 bytes written to the header + # would be truncated, and attempting to decrypt the message would result in a deserialization error. + # The message can be decrypted by replacing the truncated provider ID with the expected provider ID + # in decryption code. + # Contact AWS for any questions about this approach. + # ESDK-Python >=4.0.1 corrects the serialization logic and writes the correct length and expected bytes + # to the message header. + key_provider_id_bytes = to_bytes(encrypted_data_key.key_provider.provider_id) + len_key_provider_id_bytes = len(key_provider_id_bytes) + key_info_bytes = to_bytes(encrypted_data_key.key_provider.key_info) + len_key_info_bytes = len(key_info_bytes) return struct.pack( encrypted_data_key_format.format( - provider_id_len=len(encrypted_data_key.key_provider.provider_id), - provider_info_len=len(encrypted_data_key.key_provider.key_info), + provider_id_len=len_key_provider_id_bytes, + provider_info_len=len_key_info_bytes, enc_data_key_len=len(encrypted_data_key.encrypted_data_key), ), - len(encrypted_data_key.key_provider.provider_id), - to_bytes(encrypted_data_key.key_provider.provider_id), - len(encrypted_data_key.key_provider.key_info), - to_bytes(encrypted_data_key.key_provider.key_info), + len_key_provider_id_bytes, + key_provider_id_bytes, + len_key_info_bytes, + key_info_bytes, len(encrypted_data_key.encrypted_data_key), encrypted_data_key.encrypted_data_key, ) @@ -189,7 +193,13 @@ def serialize_header(header, signer=None): raise SerializationError("Unrecognized message format version: {}".format(header.version)) -def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=None): +def _serialize_header_auth_v1( + algorithm, + header, + data_encryption_key, + signer=None, + required_ec_bytes=None +): """Creates serialized header authentication data for messages in serialization version V1. :param algorithm: Algorithm to use for encryption @@ -198,16 +208,35 @@ def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=Non :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-material-providers library + AND its required encryption context CMM. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ - header_auth = encrypt( - algorithm=algorithm, - key=data_encryption_key, - plaintext=b"", - associated_data=header, - iv=header_auth_iv(algorithm), - ) + if required_ec_bytes is None: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + associated_data=header, + iv=header_auth_iv(algorithm), + ) + else: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + # The AAD MUST be the concatenation of the serialized message header body and the serialization + # of encryption context to only authenticate. The encryption context to only authenticate MUST + # be the encryption context in the encryption materials filtered to only contain key value + # pairs listed in the encryption material's required encryption context keys serialized + # according to the encryption context serialization specification. + associated_data=header + required_ec_bytes, + iv=header_auth_iv(algorithm), + ) output = struct.pack( ">{iv_len}s{tag_len}s".format(iv_len=algorithm.iv_len, tag_len=algorithm.tag_len), header_auth.iv, @@ -218,7 +247,13 @@ def _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer=Non return output -def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=None): +def _serialize_header_auth_v2( + algorithm, + header, + data_encryption_key, + signer=None, + required_ec_bytes=None +): """Creates serialized header authentication data for messages in serialization version V2. :param algorithm: Algorithm to use for encryption @@ -227,16 +262,35 @@ def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=Non :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-material-providers library + AND its required encryption context CMM. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ - header_auth = encrypt( - algorithm=algorithm, - key=data_encryption_key, - plaintext=b"", - associated_data=header, - iv=header_auth_iv(algorithm), - ) + if required_ec_bytes is None: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + associated_data=header, + iv=header_auth_iv(algorithm), + ) + else: + header_auth = encrypt( + algorithm=algorithm, + key=data_encryption_key, + plaintext=b"", + # The AAD MUST be the concatenation of the serialized message header body and the serialization + # of encryption context to only authenticate. The encryption context to only authenticate MUST + # be the encryption context in the encryption materials filtered to only contain key value + # pairs listed in the encryption material's required encryption context keys serialized + # according to the encryption context serialization specification. + associated_data=header + required_ec_bytes, + iv=header_auth_iv(algorithm), + ) output = struct.pack( ">{tag_len}s".format(tag_len=algorithm.tag_len), header_auth.tag, @@ -246,7 +300,14 @@ def _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer=Non return output -def serialize_header_auth(version, algorithm, header, data_encryption_key, signer=None): +def serialize_header_auth( + version, + algorithm, + header, + data_encryption_key, + signer=None, + required_ec_bytes=None +): """Creates serialized header authentication data. :param version: The serialization version of the message @@ -257,13 +318,22 @@ def serialize_header_auth(version, algorithm, header, data_encryption_key, signe :param bytes data_encryption_key: Data key with which to encrypt message :param signer: Cryptographic signer object (optional) :type signer: aws_encryption_sdk.Signer + :param required_encryption_context_bytes: Serialized encryption context items + for all items whose keys are in the required_encryption_context list. + This is ONLY processed if using the aws-cryptographic-material-providers library + AND its required encryption context CMM. (optional) + :type required_encryption_context_bytes: bytes :returns: Serialized header authentication data :rtype: bytes """ if version == SerializationVersion.V1: - return _serialize_header_auth_v1(algorithm, header, data_encryption_key, signer) + return _serialize_header_auth_v1( + algorithm, header, data_encryption_key, signer, required_ec_bytes + ) elif version == SerializationVersion.V2: - return _serialize_header_auth_v2(algorithm, header, data_encryption_key, signer) + return _serialize_header_auth_v2( + algorithm, header, data_encryption_key, signer, required_ec_bytes + ) else: raise SerializationError("Unrecognized message format version: {}".format(version)) diff --git a/src/aws_encryption_sdk/internal/str_ops.py b/src/aws_encryption_sdk/internal/str_ops.py index 75b70be92..f92769245 100644 --- a/src/aws_encryption_sdk/internal/str_ops.py +++ b/src/aws_encryption_sdk/internal/str_ops.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper functions for consistently obtaining str and bytes objects in both Python2 and Python3.""" import codecs diff --git a/src/aws_encryption_sdk/internal/structures.py b/src/aws_encryption_sdk/internal/structures.py index 2730bd19a..152e021b0 100644 --- a/src/aws_encryption_sdk/internal/structures.py +++ b/src/aws_encryption_sdk/internal/structures.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Public data structures for aws_encryption_sdk.""" import attr import six diff --git a/src/aws_encryption_sdk/internal/utils/__init__.py b/src/aws_encryption_sdk/internal/utils/__init__.py index dac38ac73..7ce1dcdb2 100644 --- a/src/aws_encryption_sdk/internal/utils/__init__.py +++ b/src/aws_encryption_sdk/internal/utils/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper utility functions for AWS Encryption SDK.""" import io import logging @@ -163,3 +153,25 @@ def source_data_key_length_check(source_data_key, algorithm): actual=len(source_data_key.data_key), required=algorithm.kdf_input_len ) ) + + +def exactly_one_arg_is_not_none(*args): + """ + Helper function for internal ESDK logic. + Returns `True` if exactly one item in the provided arguments is not `None`. + Returns `False` otherwise. + + :param args: Input arguments to check + :returns: `True` if exactly one item in the provided arguments is not `None`; `False` otherwise + """ + # Have not found any `not None` + found_one = False + for arg in args: + if arg is not None: + if found_one is False: + # Have not already found a `not None`, found a `not None` => only one `not None` (so far) + found_one = True + else: + # Already found a `not None`, found another `not None` => not exactly one `not None` + return False + return found_one diff --git a/src/aws_encryption_sdk/internal/utils/commitment.py b/src/aws_encryption_sdk/internal/utils/commitment.py index 1af27dcd0..c6d5950d7 100644 --- a/src/aws_encryption_sdk/internal/utils/commitment.py +++ b/src/aws_encryption_sdk/internal/utils/commitment.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper functions for validating commitment policies and algorithms for the AWS Encryption SDK.""" from aws_encryption_sdk.exceptions import ActionNotAllowedError from aws_encryption_sdk.identifiers import CommitmentPolicy diff --git a/src/aws_encryption_sdk/internal/utils/signature.py b/src/aws_encryption_sdk/internal/utils/signature.py index 74b481eb6..7a3558005 100644 --- a/src/aws_encryption_sdk/internal/utils/signature.py +++ b/src/aws_encryption_sdk/internal/utils/signature.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper functions for validating signature policies and algorithms for the AWS Encryption SDK.""" from enum import Enum diff --git a/src/aws_encryption_sdk/internal/utils/streams.py b/src/aws_encryption_sdk/internal/utils/streams.py index 0ab9a9524..5b787c275 100644 --- a/src/aws_encryption_sdk/internal/utils/streams.py +++ b/src/aws_encryption_sdk/internal/utils/streams.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper stream utility objects for AWS Encryption SDK.""" import io diff --git a/src/aws_encryption_sdk/key_providers/__init__.py b/src/aws_encryption_sdk/key_providers/__init__.py index 867d1e0ca..fdf6acfbc 100644 --- a/src/aws_encryption_sdk/key_providers/__init__.py +++ b/src/aws_encryption_sdk/key_providers/__init__.py @@ -1,13 +1,3 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """All provided master key provider and master keys.""" diff --git a/src/aws_encryption_sdk/key_providers/base.py b/src/aws_encryption_sdk/key_providers/base.py index d0871e802..5fa99cc1c 100644 --- a/src/aws_encryption_sdk/key_providers/base.py +++ b/src/aws_encryption_sdk/key_providers/base.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Base class interface for Master Key Providers.""" import abc import logging @@ -22,6 +12,7 @@ ConfigMismatchError, DecryptKeyError, IncorrectMasterKeyError, + InvalidDataKeyError, InvalidKeyIdError, MasterKeyProviderError, ) @@ -266,7 +257,7 @@ def decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): # //# input encryption context. data_key = master_key.decrypt_data_key(encrypted_data_key, algorithm, encryption_context) - except (IncorrectMasterKeyError, DecryptKeyError) as error: + except (IncorrectMasterKeyError, DecryptKeyError, InvalidDataKeyError) as error: _LOGGER.debug( "%s raised when attempting to decrypt data key with master key %s", repr(error), @@ -314,8 +305,8 @@ def decrypt_data_key_from_list(self, encrypted_data_keys, algorithm, encryption_ try: data_key = self.decrypt_data_key(encrypted_data_key, algorithm, encryption_context) # MasterKeyProvider.decrypt_data_key throws DecryptKeyError - # but MasterKey.decrypt_data_key throws IncorrectMasterKeyError - except (DecryptKeyError, IncorrectMasterKeyError): + # but MasterKey.decrypt_data_key throws IncorrectMasterKeyError and InvalidDataKeyError + except (DecryptKeyError, IncorrectMasterKeyError, InvalidDataKeyError): continue else: break diff --git a/src/aws_encryption_sdk/key_providers/kms.py b/src/aws_encryption_sdk/key_providers/kms.py index ab18c62ed..45402077d 100644 --- a/src/aws_encryption_sdk/key_providers/kms.py +++ b/src/aws_encryption_sdk/key_providers/kms.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Master Key Providers for use with AWS KMS""" import abc import functools diff --git a/src/aws_encryption_sdk/key_providers/raw.py b/src/aws_encryption_sdk/key_providers/raw.py index 57a1d5edf..f3d8ec478 100644 --- a/src/aws_encryption_sdk/key_providers/raw.py +++ b/src/aws_encryption_sdk/key_providers/raw.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Resources required for Raw Master Keys.""" import abc import logging diff --git a/src/aws_encryption_sdk/materials_managers/__init__.py b/src/aws_encryption_sdk/materials_managers/__init__.py index 9db1dafae..3d03e1f30 100644 --- a/src/aws_encryption_sdk/materials_managers/__init__.py +++ b/src/aws_encryption_sdk/materials_managers/__init__.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Primitive structures for use when interacting with crypto material managers. .. versionadded:: 1.3.0 @@ -89,11 +79,17 @@ class DecryptionMaterialsRequest(object): :param encrypted_data_keys: Set of encrypted data keys :type encrypted_data_keys: set of `aws_encryption_sdk.structures.EncryptedDataKey` :param dict encryption_context: Encryption context to provide to master keys for underlying decrypt requests + :param dict reproduced_encryption_context: Encryption context to provide on decrypt. + This is ONLY processed if using a CMM from the aws-cryptographic-material-providers library. """ algorithm = attr.ib(validator=attr.validators.instance_of(Algorithm)) encrypted_data_keys = attr.ib(validator=attr.validators.instance_of(set)) encryption_context = attr.ib(validator=attr.validators.instance_of(dict)) + reproduced_encryption_context = attr.ib( + default=None, + validator=attr.validators.optional(attr.validators.instance_of(dict)) + ) commitment_policy = attr.ib( default=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT, validator=attr.validators.optional(attr.validators.instance_of(CommitmentPolicy)), diff --git a/src/aws_encryption_sdk/materials_managers/base.py b/src/aws_encryption_sdk/materials_managers/base.py index e8ffe8a5e..7820d9042 100644 --- a/src/aws_encryption_sdk/materials_managers/base.py +++ b/src/aws_encryption_sdk/materials_managers/base.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Base class interface for crypto material managers.""" import abc diff --git a/src/aws_encryption_sdk/materials_managers/caching.py b/src/aws_encryption_sdk/materials_managers/caching.py index 826c3be79..b1a0ecab5 100644 --- a/src/aws_encryption_sdk/materials_managers/caching.py +++ b/src/aws_encryption_sdk/materials_managers/caching.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Caching crypto material manager.""" import logging import uuid diff --git a/src/aws_encryption_sdk/materials_managers/default.py b/src/aws_encryption_sdk/materials_managers/default.py index e708f25e2..4a7119792 100644 --- a/src/aws_encryption_sdk/materials_managers/default.py +++ b/src/aws_encryption_sdk/materials_managers/default.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Default crypto material manager class.""" import logging diff --git a/src/aws_encryption_sdk/materials_managers/mpl/__init__.py b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py new file mode 100644 index 000000000..be75f3566 --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/__init__.py @@ -0,0 +1,6 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Modules related to the MPL's materials managers interfaces. + +The aws-cryptographic-materials-library MUST be installed to use these modules. +""" diff --git a/src/aws_encryption_sdk/materials_managers/mpl/cmm.py b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py new file mode 100644 index 000000000..430417c0d --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/cmm.py @@ -0,0 +1,157 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Retrieves encryption/decryption materials from the MPL and interfaces them to EDK components. + +The aws-cryptographic-materials-library MUST be installed to use this module. +""" +# pylint should pass even if the MPL isn't installed +# Also thinks these imports aren't used if it can't import them +# noqa pylint: disable=import-error,unused-import +from aws_cryptographic_material_providers.mpl.errors import ( + AwsCryptographicMaterialProvidersException, + CollectionOfErrors, +) +from aws_cryptographic_material_providers.mpl.models import ( + AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, + CommitmentPolicyESDK as MPL_CommitmentPolicyESDK, + DecryptMaterialsInput as MPL_DecryptMaterialsInput, + DecryptMaterialsOutput as MPL_DecryptMaterialsOutput, + EncryptedDataKey as MPL_EncryptedDataKey, + GetEncryptionMaterialsInput as MPL_GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, +) +from aws_cryptographic_material_providers.mpl.references import ( + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, +) +# noqa pylint: enable=import-error,unused-import +# pylint and isort disagree on where this should go. Choose isort and disable pylint for this. +from typing import List # noqa pylint: disable=wrong-import-order + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey + + +class CryptoMaterialsManagerFromMPL(CryptoMaterialsManager): + """ + In instances where encryption materials are provided by an implementation of the MPL's + `aws_cryptographic_material_providers.mpl.references.MPL_ICryptographicMaterialsManager`, + this maps the ESDK-Python CMM interfaces to the MPL CMM. + """ + + mpl_cmm: 'MPL_ICryptographicMaterialsManager' + + def __init__( + self, + mpl_cmm: 'MPL_ICryptographicMaterialsManager' + ): + """ + Create CryptoMaterialsManagerFromMPL. + :param mpl_cmm: Underlying MPL cryptographic materials manager + """ + if isinstance(mpl_cmm, MPL_ICryptographicMaterialsManager): + self.mpl_cmm = mpl_cmm + else: + raise ValueError(f"Invalid CMM passed to CryptoMaterialsManagerFromMPL. cmm: {mpl_cmm}") + + def get_encryption_materials( + self, + request: EncryptionMaterialsRequest + ) -> EncryptionMaterialsFromMPL: + """ + Returns an EncryptionMaterialsHandler for the configured CMM. + :param request: Request for encryption materials + """ + try: + mpl_input: MPL_GetEncryptionMaterialsInput = \ + CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + request + ) + mpl_output: MPL_GetEncryptionMaterialsOutput = self.mpl_cmm.get_encryption_materials(mpl_input) + return EncryptionMaterialsFromMPL(mpl_output.encryption_materials) + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_to_mpl_get_encryption_materials( + request: EncryptionMaterialsRequest + ) -> 'MPL_GetEncryptionMaterialsInput': + output_kwargs = { + "encryption_context": request.encryption_context, + "max_plaintext_length": request.plaintext_length, + "commitment_policy": CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( + request.commitment_policy + ) + } + + if request.algorithm is not None: + output_kwargs["algorithm_suite_id"] = \ + CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ) + + return MPL_GetEncryptionMaterialsInput(**output_kwargs) + + @staticmethod + def _native_to_mpl_commitment_policy( + native_commitment_policy: CommitmentPolicy + ) -> 'MPL_CommitmentPolicyESDK': + if native_commitment_policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT: + return MPL_CommitmentPolicyESDK(value="FORBID_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT: + return MPL_CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_ALLOW_DECRYPT") + elif native_commitment_policy == CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT: + return MPL_CommitmentPolicyESDK(value="REQUIRE_ENCRYPT_REQUIRE_DECRYPT") + else: + raise ValueError(f"Invalid native_commitment_policy: {native_commitment_policy}") + + def decrypt_materials( + self, + request: DecryptionMaterialsRequest + ) -> DecryptionMaterialsFromMPL: + """ + Returns a DecryptionMaterialsFromMPL for the configured CMM. + :param request: Request for decryption materials + """ + try: + mpl_input: 'MPL_DecryptMaterialsInput' = \ + CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request(request) + mpl_output: 'MPL_DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(mpl_input) + return DecryptionMaterialsFromMPL(mpl_output.decryption_materials) + except (AwsCryptographicMaterialProvidersException, CollectionOfErrors) as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + @staticmethod + def _native_algorithm_id_to_mpl_algorithm_id(native_algorithm_id: str) -> 'MPL_AlgorithmSuiteIdESDK': + # MPL algorithm suite ID = hexstr(native_algorithm_id) padded to 4 digits post-`x`. + return MPL_AlgorithmSuiteIdESDK(f"{native_algorithm_id:#0{6}x}") + + @staticmethod + def _create_mpl_decrypt_materials_input_from_request( + request: DecryptionMaterialsRequest + ) -> 'MPL_DecryptMaterialsInput': + key_blob_list: List[Native_EncryptedDataKey] = request.encrypted_data_keys + list_edks = [MPL_EncryptedDataKey( + key_provider_id=key_blob.key_provider.provider_id, + key_provider_info=key_blob.key_provider.key_info, + ciphertext=key_blob.encrypted_data_key, + ) for key_blob in key_blob_list] + output: MPL_DecryptMaterialsInput = MPL_DecryptMaterialsInput( + algorithm_suite_id=CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + request.algorithm.algorithm_id + ), + commitment_policy=CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy( + request.commitment_policy + ), + encrypted_data_keys=list_edks, + encryption_context=request.encryption_context, + reproduced_encryption_context=request.reproduced_encryption_context, + ) + return output diff --git a/src/aws_encryption_sdk/materials_managers/mpl/materials.py b/src/aws_encryption_sdk/materials_managers/mpl/materials.py new file mode 100644 index 000000000..c2ae305ed --- /dev/null +++ b/src/aws_encryption_sdk/materials_managers/mpl/materials.py @@ -0,0 +1,157 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Provides encryption/decryption materials from an underlying materials provider from the MPL. + +The aws-cryptographic-materials-library MUST be installed to use this module. +""" +# pylint should pass even if the MPL isn't installed +# noqa pylint: disable=import-error +from aws_cryptographic_material_providers.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, +) +# pylint and isort disagree on where this should go. Choose isort and disable pylint for this. +from typing import Dict, List, Set # noqa pylint: disable=wrong-import-order + +from aws_encryption_sdk.identifiers import Algorithm, AlgorithmSuite +from aws_encryption_sdk.materials_managers import ( + DecryptionMaterials as Native_DecryptionMaterials, + EncryptionMaterials as Native_EncryptionMaterials, +) +from aws_encryption_sdk.structures import DataKey, EncryptedDataKey as Native_EncryptedDataKey, MasterKeyInfo + + +def _mpl_algorithm_id_to_native_algorithm_id(mpl_algorithm_id: str) -> int: + # MPL algorithm suite ID == hex(native algorithm suite ID) + return int(mpl_algorithm_id, 16) + + +class EncryptionMaterialsFromMPL(Native_EncryptionMaterials): + """ + In instances where encryption materials are be provided by + the MPL's `aws_cryptographic_material_providers.mpl.models.EncryptionMaterials`, + this maps the ESDK interfaces to the underlying MPL materials. + """ + + mpl_materials: 'MPL_EncryptionMaterials' + + def __init__( + self, + mpl_materials: 'MPL_EncryptionMaterials' + ): + """ + Create EncryptionMaterialsFromMPL. + :param materials: Underlying encryption materials + """ + if isinstance(mpl_materials, MPL_EncryptionMaterials): + self.mpl_materials = mpl_materials + else: + raise ValueError("Invalid EncryptionMaterials passed to EncryptionMaterialsFromMPL. " + f"materials: {mpl_materials}") + + @property + def algorithm(self) -> Algorithm: + """Materials' native Algorithm.""" + return AlgorithmSuite.get_by_id( + _mpl_algorithm_id_to_native_algorithm_id( + self.mpl_materials.algorithm_suite.id.value + ) + ) + + @property + def encryption_context(self) -> Dict[str, str]: + """Materials' encryption context.""" + return self.mpl_materials.encryption_context + + @property + def encrypted_data_keys(self) -> List[Native_EncryptedDataKey]: + """Materials' encrypted data keys.""" + mpl_edk_list: List[MPL_EncryptedDataKey] = self.mpl_materials.encrypted_data_keys + key_blob_list: Set[Native_EncryptedDataKey] = {Native_EncryptedDataKey( + key_provider=MasterKeyInfo( + provider_id=mpl_edk.key_provider_id, + key_info=mpl_edk.key_provider_info, + ), + encrypted_data_key=mpl_edk.ciphertext, + ) for mpl_edk in mpl_edk_list} + return key_blob_list + + @property + def data_encryption_key(self) -> DataKey: + """Materials' data encryption key.""" + mpl_dek = self.mpl_materials.plaintext_data_key + return DataKey( + # key_provider is unused, but the return type is DataKey + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=mpl_dek, + encrypted_data_key=b'', # No encrypted DEK + ) + + @property + def signing_key(self) -> bytes: + """Materials' signing key.""" + return self.mpl_materials.signing_key + + @property + # Pylint thinks this name is too long, but it's the best descriptor for this... + # pylint: disable=invalid-name + def required_encryption_context_keys(self) -> bytes: + """Materials' required encryption context keys.""" + return self.mpl_materials.required_encryption_context_keys + + +class DecryptionMaterialsFromMPL(Native_DecryptionMaterials): + """ + In instances where decryption materials are be provided by + the MPL's `aws_cryptographic_material_providers.mpl.models.DecryptionMaterials`, + this maps the ESDK interfaces to the underlying MPL materials. + """ + + mpl_materials: 'MPL_DecryptionMaterials' + + def __init__( + self, + mpl_materials: 'MPL_DecryptionMaterials' + ): + """ + Create DecryptionMaterialsFromMPL. + :param materials: Underlying decryption materials + """ + if isinstance(mpl_materials, MPL_DecryptionMaterials): + self.mpl_materials = mpl_materials + else: + raise ValueError(f"Invalid DecryptionMaterials passed to DecryptionMaterialsFromMPL.\ + materials: {mpl_materials}") + + @property + def data_key(self) -> DataKey: + """Materials' data key.""" + return DataKey( + key_provider=MasterKeyInfo( + provider_id="", + key_info=b'' + ), + data_key=self.mpl_materials.plaintext_data_key, + encrypted_data_key=b'', + ) + + @property + def verification_key(self) -> bytes: + """Materials' verification key.""" + return self.mpl_materials.verification_key + + @property + def encryption_context(self) -> Dict[str, str]: + """Materials' encryption context.""" + return self.mpl_materials.encryption_context + + @property + # Pylint thinks this name is too long, but it's the best descriptor for this... + # pylint: disable=invalid-name + def required_encryption_context_keys(self) -> bytes: + """Materials' required encryption context keys.""" + return self.mpl_materials.required_encryption_context_keys diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index 54eb046d2..0eb5670b4 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -1,19 +1,10 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """High level AWS Encryption SDK client for streaming objects.""" from __future__ import division import abc +import base64 import hmac import io import logging @@ -21,6 +12,7 @@ import attr import six +from cryptography.hazmat.primitives import serialization import aws_encryption_sdk.internal.utils from aws_encryption_sdk.exceptions import ( @@ -56,6 +48,7 @@ serialize_non_framed_close, serialize_non_framed_open, ) +from aws_encryption_sdk.internal.utils import exactly_one_arg_is_not_none from aws_encryption_sdk.internal.utils.commitment import ( validate_commitment_policy_on_decrypt, validate_commitment_policy_on_encrypt, @@ -67,6 +60,25 @@ from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager from aws_encryption_sdk.structures import MessageHeader +try: + # pylint should pass even if the MPL isn't installed + # noqa pylint: disable=import-error + from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + from aws_cryptographic_material_providers.mpl.errors import AwsCryptographicMaterialProvidersException + from aws_cryptographic_material_providers.mpl.models import CreateDefaultCryptographicMaterialsManagerInput + from aws_cryptographic_material_providers.mpl.references import ( + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, + IKeyring as MPL_IKeyring, + ) + _HAS_MPL = True + + # Import internal ESDK modules that depend on the MPL + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL + +except ImportError: + _HAS_MPL = False + _LOGGER = logging.getLogger(__name__) @@ -107,12 +119,38 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes max_encrypted_data_keys = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(int)) ) - materials_manager = attr.ib( - hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(CryptoMaterialsManager)) - ) + if _HAS_MPL: + # With the MPL, the provided materials_manager can be an instance of + # either the native interface or an MPL interface. + # If it implements the MPL interface, this constructor will + # internally wrap it in a native interface. + materials_manager = attr.ib( + hash=True, + default=None, + validator=attr.validators.optional( + attr.validators.instance_of( + (CryptoMaterialsManager, MPL_ICryptographicMaterialsManager) + ) + ) + ) + else: + materials_manager = attr.ib( + hash=True, + default=None, + validator=attr.validators.optional( + attr.validators.instance_of( + CryptoMaterialsManager + ) + ) + ) key_provider = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider)) ) + if _HAS_MPL: + # Keyrings are only available if the MPL is installed in the runtime + keyring = attr.ib( + hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(MPL_IKeyring)) + ) source_length = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) ) @@ -120,8 +158,45 @@ class _ClientConfig(object): # pylint: disable=too-many-instance-attributes hash=True, default=LINE_LENGTH, validator=attr.validators.instance_of(six.integer_types) ) # DEPRECATED: Value is no longer configurable here. Parameter left here to avoid breaking consumers. - def __attrs_post_init__(self): - """Normalize inputs to crypto material manager.""" + def _has_mpl_attrs_post_init(self): + """If the MPL is present in the runtime, perform MPL-specific post-init logic + to validate the new object has a valid state. + """ + if not exactly_one_arg_is_not_none(self.materials_manager, self.key_provider, self.keyring): + raise TypeError("Exactly one of keyring, materials_manager, or key_provider must be provided") + if self.materials_manager is None: + if self.key_provider is not None: + # No CMM, provided legacy native `key_provider` => create legacy native DefaultCryptoMaterialsManager + self.materials_manager = DefaultCryptoMaterialsManager( + master_key_provider=self.key_provider + ) + elif self.keyring is not None: + try: + mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + cmm = mat_prov.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.keyring + ) + ) + cmm_handler: CryptoMaterialsManager = CryptoMaterialsManagerFromMPL(cmm) + self.materials_manager = cmm_handler + except AwsCryptographicMaterialProvidersException as mpl_exception: + # Wrap MPL error into the ESDK error type + # so customers only have to catch ESDK error types. + raise AWSEncryptionSDKClientError(mpl_exception) + + # If the provided materials_manager is directly from the MPL, wrap it in a native interface + # for internal use. + elif (self.materials_manager is not None + and isinstance(self.materials_manager, MPL_ICryptographicMaterialsManager)): + self.materials_manager = CryptoMaterialsManagerFromMPL(self.materials_manager) + + def _no_mpl_attrs_post_init(self): + """If the MPL is NOT present in the runtime, perform post-init logic + to validate the new object has a valid state. + """ both_cmm_and_mkp_defined = self.materials_manager is not None and self.key_provider is not None neither_cmm_nor_mkp_defined = self.materials_manager is None and self.key_provider is None @@ -130,6 +205,13 @@ def __attrs_post_init__(self): if self.materials_manager is None: self.materials_manager = DefaultCryptoMaterialsManager(master_key_provider=self.key_provider) + def __attrs_post_init__(self): + """Normalize inputs to crypto material manager.""" + if _HAS_MPL: + self._has_mpl_attrs_post_init() + else: + self._no_mpl_attrs_post_init() + class _EncryptionStream(io.IOBase): """Parent class for StreamEncryptor and StreamDecryptor classes. @@ -299,10 +381,10 @@ def seek(self, offset, whence=0): def readline(self): """Read a chunk of the output""" - _LOGGER.info("reading line") + _LOGGER.debug("reading line") line = self.read(self.line_length) if len(line) < self.line_length: - _LOGGER.info("all lines read") + _LOGGER.debug("all lines read") return line def readlines(self): @@ -343,6 +425,10 @@ class EncryptorConfig(_ClientConfig): :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` from the aws_cryptographic_material_providers library + which handles encryption and decryption + :type keyring: + aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -394,6 +480,10 @@ class StreamEncryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for encryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` from the aws_cryptographic_material_providers library + which handles encryption and decryption + :type keyring: + aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -480,9 +570,18 @@ def _prep_message(self): if self._encryption_materials.signing_key is None: self.signer = None else: - self.signer = Signer.from_key_bytes( - algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key - ) + # MPL verification key is PEM bytes, not DER bytes. + # If the underlying CMM is from the MPL, load PEM bytes. + if (_HAS_MPL + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + self.signer = Signer.from_key_bytes( + algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key, + encoding=serialization.Encoding.PEM, + ) + else: + self.signer = Signer.from_key_bytes( + algorithm=self._encryption_materials.algorithm, key_bytes=self._encryption_materials.signing_key + ) aws_encryption_sdk.internal.utils.validate_frame_length( frame_length=self.config.frame_length, algorithm=self._encryption_materials.algorithm ) @@ -514,11 +613,27 @@ def generate_header(self, message_id): if self._encryption_materials.algorithm.message_format_version == 0x02: version = SerializationVersion.V2 + # If the underlying materials_provider provided required_encryption_context_keys + # (ex. if the materials_provider is a required encryption context CMM), + # then partition the encryption context based on those keys. + if hasattr(self._encryption_materials, "required_encryption_context_keys"): + self._required_encryption_context = {} + self._stored_encryption_context = {} + for (key, value) in self._encryption_materials.encryption_context.items(): + if key in self._encryption_materials.required_encryption_context_keys: + self._required_encryption_context[key] = value + else: + self._stored_encryption_context[key] = value + # Otherwise, store all encryption context with the message. + else: + self._stored_encryption_context = self._encryption_materials.encryption_context + self._required_encryption_context = None + kwargs = dict( version=version, algorithm=self._encryption_materials.algorithm, message_id=message_id, - encryption_context=self._encryption_materials.encryption_context, + encryption_context=self._stored_encryption_context, encrypted_data_keys=self._encryption_materials.encrypted_data_keys, content_type=self.content_type, frame_length=self.config.frame_length, @@ -542,13 +657,31 @@ def generate_header(self, message_id): def _write_header(self): """Builds the message header and writes it to the output stream.""" self.output_buffer += serialize_header(header=self._header, signer=self.signer) - self.output_buffer += serialize_header_auth( - version=self._header.version, - algorithm=self._encryption_materials.algorithm, - header=self.output_buffer, - data_encryption_key=self._derived_data_key, - signer=self.signer, - ) + + # If there is _required_encryption_context, + # serialize it, then authenticate it + if hasattr(self, "_required_encryption_context"): + required_ec_serialized = \ + aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) + self.output_buffer += serialize_header_auth( + version=self._header.version, + algorithm=self._encryption_materials.algorithm, + header=self.output_buffer, + data_encryption_key=self._derived_data_key, + signer=self.signer, + required_ec_bytes=required_ec_serialized, + ) + # Otherwise, do not pass in any required encryption context + else: + self.output_buffer += serialize_header_auth( + version=self._header.version, + algorithm=self._encryption_materials.algorithm, + header=self.output_buffer, + data_encryption_key=self._derived_data_key, + signer=self.signer, + ) def _prep_non_framed(self): """Prepare the opening data for a non-framed message.""" @@ -729,11 +862,15 @@ class DecryptorConfig(_ClientConfig): :param source: Source data to encrypt or decrypt :type source: str, bytes, io.IOBase, or file :param materials_manager: `CryptoMaterialsManager` from which to obtain cryptographic materials - (either `materials_manager` or `key_provider` required) + (either `keyring`, `materials_manager` or `key_provider` required) :type materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption - (either `materials_manager` or `key_provider` required) + (either `keyring`, `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` from the aws_cryptographic_material_providers library + which handles encryption and decryption + :type keyring: + aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -742,11 +879,19 @@ class DecryptorConfig(_ClientConfig): :param int max_body_length: Maximum frame size (or content length for non-framed messages) in bytes to read from ciphertext message. + :param dict encryption_context: Dictionary defining encryption context to validate + on decrypt. This is ONLY validated on decrypt if using a CMM + from the aws-cryptographic-material-providers library. """ max_body_length = attr.ib( hash=True, default=None, validator=attr.validators.optional(attr.validators.instance_of(six.integer_types)) ) + encryption_context = attr.ib( + hash=False, # dictionaries are not hashable + default=None, + validator=attr.validators.optional(attr.validators.instance_of(dict)), + ) class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-attributes @@ -770,6 +915,10 @@ class StreamDecryptor(_EncryptionStream): # pylint: disable=too-many-instance-a :param key_provider: `MasterKeyProvider` from which to obtain data keys for decryption (either `materials_manager` or `key_provider` required) :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider + :param keyring: `IKeyring` from the aws_cryptographic_material_providers library + which handles encryption and decryption + :type keyring: + aws_cryptographic_material_providers.mpl.references.IKeyring :param int source_length: Length of source data (optional) .. note:: @@ -795,6 +944,77 @@ def _prep_message(self): self._prep_non_framed() self._message_prepped = True + def _create_decrypt_materials_request(self, header): + """ + Create a DecryptionMaterialsRequest based on whether + the StreamDecryptor was provided encryption_context on decrypt + (i.e. expects to use a CMM from the MPL). + """ + # If encryption_context is provided on decrypt, + # pass it to the DecryptionMaterialsRequest as reproduced_encryption_context + if hasattr(self.config, "encryption_context") \ + and self.config.encryption_context is not None: + if (_HAS_MPL + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + return DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + reproduced_encryption_context=self.config.encryption_context + ) + else: + raise TypeError("encryption_context on decrypt is only supported for CMMs and keyrings " + "from the aws-cryptographic-material-providers library.") + return DecryptionMaterialsRequest( + encrypted_data_keys=header.encrypted_data_keys, + algorithm=header.algorithm, + encryption_context=header.encryption_context, + commitment_policy=self.config.commitment_policy, + ) + + def _validate_parsed_header( + self, + header, + header_auth, + raw_header, + ): + """ + Pass arguments from this StreamDecryptor to validate_header based on whether + the StreamDecryptor has the _required_encryption_context attribute + (i.e. is using the required encryption context CMM from the MPL). + """ + # If _required_encryption_context is present, + # serialize it and pass it to validate_header. + if hasattr(self, "_required_encryption_context") \ + and self._required_encryption_context is not None: + # The authenticated only encryption context is all encryption context key-value pairs where the + # key exists in Required Encryption Context Keys. It is then serialized according to the + # message header Key Value Pairs. + required_ec_serialized = \ + aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context( + self._required_encryption_context + ) + + validate_header( + header=header, + header_auth=header_auth, + # When verifying the header, the AAD input to the authenticated encryption algorithm + # specified by the algorithm suite is the message header body and the serialized + # authenticated only encryption context. + raw_header=raw_header + required_ec_serialized, + data_key=self._derived_data_key + ) + else: + validate_header( + header=header, + header_auth=header_auth, + raw_header=raw_header, + data_key=self._derived_data_key + ) + + return header, header_auth + def _read_header(self): """Reads the message header from the input stream. @@ -821,19 +1041,35 @@ def _read_header(self): ) ) - decrypt_materials_request = DecryptionMaterialsRequest( - encrypted_data_keys=header.encrypted_data_keys, - algorithm=header.algorithm, - encryption_context=header.encryption_context, - commitment_policy=self.config.commitment_policy, - ) + decrypt_materials_request = self._create_decrypt_materials_request(header) decryption_materials = self.config.materials_manager.decrypt_materials(request=decrypt_materials_request) + + # If the materials_manager passed required_encryption_context_keys, + # get the items out of the encryption_context with the keys. + # The items are used in header validation. + if hasattr(decryption_materials, "required_encryption_context_keys"): + self._required_encryption_context = {} + for (key, value) in decryption_materials.encryption_context.items(): + if key in decryption_materials.required_encryption_context_keys: + self._required_encryption_context[key] = value + else: + self._required_encryption_context = None + if decryption_materials.verification_key is None: self.verifier = None else: - self.verifier = Verifier.from_key_bytes( - algorithm=header.algorithm, key_bytes=decryption_materials.verification_key - ) + # MPL verification key is NOT key bytes; it is bytes of the compressed point. + # If the underlying CMM is from the MPL, load bytes from encoded point. + if (_HAS_MPL + and isinstance(self.config.materials_manager, CryptoMaterialsManagerFromMPL)): + self.verifier = Verifier.from_encoded_point( + algorithm=header.algorithm, + encoded_point=base64.b64encode(decryption_materials.verification_key) + ) + else: + self.verifier = Verifier.from_key_bytes( + algorithm=header.algorithm, key_bytes=decryption_materials.verification_key + ) if self.verifier is not None: self.verifier.update(raw_header) @@ -857,9 +1093,11 @@ def _read_header(self): "message. Halting processing of this message." ) - validate_header(header=header, header_auth=header_auth, raw_header=raw_header, data_key=self._derived_data_key) - - return header, header_auth + return self._validate_parsed_header( + header=header, + header_auth=header_auth, + raw_header=raw_header, + ) def _prep_non_framed(self): """Prepare the opening data for a non-framed message.""" diff --git a/src/aws_encryption_sdk/structures.py b/src/aws_encryption_sdk/structures.py index 52d16dd4c..a09391d5b 100644 --- a/src/aws_encryption_sdk/structures.py +++ b/src/aws_encryption_sdk/structures.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Public data structures for aws_encryption_sdk.""" import attr import six diff --git a/test/__init__.py b/test/__init__.py index 53a960891..f94fd12a2 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,12 +1,2 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/functional/__init__.py b/test/functional/__init__.py index 53a960891..f94fd12a2 100644 --- a/test/functional/__init__.py +++ b/test/functional/__init__.py @@ -1,12 +1,2 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/functional/key_providers/__init__.py b/test/functional/key_providers/__init__.py index cf81c339a..f94fd12a2 100644 --- a/test/functional/key_providers/__init__.py +++ b/test/functional/key_providers/__init__.py @@ -1,12 +1,2 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/functional/key_providers/test_base.py b/test/functional/key_providers/test_base.py index c85f65711..ebec95558 100644 --- a/test/functional/key_providers/test_base.py +++ b/test/functional/key_providers/test_base.py @@ -1,15 +1,5 @@ -# Copyright 2019 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional tests for ``aws_encryption_sdk.key_providers.base``.""" import itertools diff --git a/test/functional/test_f_aws_encryption_sdk_client.py b/test/functional/test_f_aws_encryption_sdk_client.py index 7310e580f..244f8f7d7 100644 --- a/test/functional/test_f_aws_encryption_sdk_client.py +++ b/test/functional/test_f_aws_encryption_sdk_client.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional test suite for aws_encryption_sdk.kms_thick_client""" from __future__ import division diff --git a/test/functional/test_f_commitment.py b/test/functional/test_f_commitment.py index 0d4c3249d..fdfe281ae 100644 --- a/test/functional/test_f_commitment.py +++ b/test/functional/test_f_commitment.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional test suite for functionality related to key commitment.""" from test.functional.test_f_aws_encryption_sdk_client import fake_kms_key_provider diff --git a/test/functional/test_f_crypto.py b/test/functional/test_f_crypto.py index 9242deedd..518139603 100644 --- a/test/functional/test_f_crypto.py +++ b/test/functional/test_f_crypto.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional test suite for Elliptic Curve static length signature calculation.""" import pytest from cryptography.hazmat.backends import default_backend @@ -43,7 +33,7 @@ def test_ecc_static_length_signature(algorithm): def test_signer_key_bytes_cycle(): - key = ec.generate_private_key(curve=ec.SECP384R1, backend=default_backend()) + key = ec.generate_private_key(curve=ec.SECP384R1(), backend=default_backend()) signer = Signer(algorithm=aws_encryption_sdk.Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, key=key) key_bytes = signer.key_bytes() new_signer = Signer.from_key_bytes( diff --git a/test/functional/test_f_crypto_iv.py b/test/functional/test_f_crypto_iv.py index 815f025ac..a58c503cc 100644 --- a/test/functional/test_f_crypto_iv.py +++ b/test/functional/test_f_crypto_iv.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for IV generation helper functions.""" import pytest diff --git a/test/functional/test_f_xcompat.py b/test/functional/test_f_xcompat.py index 790724367..8b511ceca 100644 --- a/test/functional/test_f_xcompat.py +++ b/test/functional/test_f_xcompat.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Functional test suite testing decryption of known good test files encrypted using static RawMasterKeyProvider.""" import base64 import json diff --git a/test/integration/__init__.py b/test/integration/__init__.py index 53a960891..f94fd12a2 100644 --- a/test/integration/__init__.py +++ b/test/integration/__init__.py @@ -1,12 +1,2 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/integration/integration_test_utils.py b/test/integration/integration_test_utils.py index 5d40f8124..fbd3774e5 100644 --- a/test/integration/integration_test_utils.py +++ b/test/integration/integration_test_utils.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Utility functions to handle configuration and credentials setup for integration tests.""" import os diff --git a/test/integration/test_i_aws_encrytion_sdk_client.py b/test/integration/test_i_aws_encrytion_sdk_client.py index 5dd13e4c6..51f5aa492 100644 --- a/test/integration/test_i_aws_encrytion_sdk_client.py +++ b/test/integration/test_i_aws_encrytion_sdk_client.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Integration test suite for `aws_encryption_sdk`.""" import io import logging diff --git a/test/integration/test_i_thread_safety.py b/test/integration/test_i_thread_safety.py index 77407b243..3ddb0ecd1 100644 --- a/test/integration/test_i_thread_safety.py +++ b/test/integration/test_i_thread_safety.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Basic sanity check for ``aws_encryption_sdk`` client behavior when threading.""" from __future__ import division diff --git a/test/integration/test_i_xcompat_kms.py b/test/integration/test_i_xcompat_kms.py index 7cfeee111..1caff028c 100644 --- a/test/integration/test_i_xcompat_kms.py +++ b/test/integration/test_i_xcompat_kms.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Integration test suite testing decryption of known good test files encrypted using KMSMasterKeyProvider.""" import json import os diff --git a/test/integration/test_kat_commitment.py b/test/integration/test_kat_commitment.py index e72277787..78891941c 100644 --- a/test/integration/test_kat_commitment.py +++ b/test/integration/test_kat_commitment.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Known answer test suite for functionality related to key commitment.""" import base64 import json diff --git a/test/mpl/README.md b/test/mpl/README.md new file mode 100644 index 000000000..7ae7134d0 --- /dev/null +++ b/test/mpl/README.md @@ -0,0 +1 @@ +Tests in this directory REQUIRE the [aws-cryptographic-material-providers](https://github.com/aws/aws-cryptographic-material-providers-library) library to execute. \ No newline at end of file diff --git a/test/mpl/__init__.py b/test/mpl/__init__.py new file mode 100644 index 000000000..79522d342 --- /dev/null +++ b/test/mpl/__init__.py @@ -0,0 +1,6 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Module testing components that use the MPL. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" diff --git a/test/mpl/integ/__init__.py b/test/mpl/integ/__init__.py new file mode 100644 index 000000000..f94fd12a2 --- /dev/null +++ b/test/mpl/integ/__init__.py @@ -0,0 +1,2 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/mpl/integ/test_required_ec_cmm.py b/test/mpl/integ/test_required_ec_cmm.py new file mode 100644 index 000000000..ab308ed54 --- /dev/null +++ b/test/mpl/integ/test_required_ec_cmm.py @@ -0,0 +1,560 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import copy + +import pytest +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, + CreateRequiredEncryptionContextCMMInput, +) + +import aws_encryption_sdk +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError + +from ..utils import TestEncryptionContexts, TestKeyringCreator + +pytestmark = [pytest.mark.integ] + +# ESDK client. Used to encrypt/decrypt in each test. +client = aws_encryption_sdk.EncryptionSDKClient() + +# MPL client. Used to create CMMs. +mpl_client: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() +) + + +# Keyrings under test +SOME_RSA_KEYRING = TestKeyringCreator._create_raw_rsa_keyring() +SOME_AES_KEYRING = TestKeyringCreator._create_raw_aes_keyring() +SOME_KMS_KEYRING = TestKeyringCreator._create_kms_keyring() +SOME_HIERARCHICAL_KEYRING = TestKeyringCreator._create_hierarchical_keyring() +TEST_KEYRINGS_LIST = [ + SOME_AES_KEYRING, + SOME_KMS_KEYRING, + SOME_RSA_KEYRING, + SOME_HIERARCHICAL_KEYRING, +] +# Multi-keyring composed of individual keyrings. +# In lots of tests in this file, +# this multi keyring encrypts one message, +# then the test attempts decryption with all of its component keyrings +# ("component" = generator + child keyrings). +SOME_MULTI_KEYRING = TestKeyringCreator._create_multi_keyring(TEST_KEYRINGS_LIST) + + +SOME_PLAINTEXT = b"Hello World" + + +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.ALL_ENCRYPTION_CONTEXTS) +# HAPPY CASE 1 +# Test supply same encryption context on encrypt and decrypt NO filtering +def test_GIVEN_same_EC_on_encrypt_and_decrypt_WHEN_encrypt_decrypt_cycle_THEN_decrypt_matches_plaintext( + encryption_context +): + # When: encrypt/decrypt cycle + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + keyring=SOME_MULTI_KEYRING, + # Given: same encryption context on encrypt and decrypt + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + pt, _ = client.decrypt( + source=ct, + # Given: same encryption context on encrypt and decrypt + encryption_context=encryption_context, + keyring=decrypt_keyring + ) + + # Then: decrypted plaintext matches original plaintext + assert pt == SOME_PLAINTEXT + + +# This test needs >=1 item to supply as required encryption context +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.NONEMPTY_ENCRYPTION_CONTEXTS) +# HAPPY CASE 2 +# On Encrypt we will only write one encryption context key value to the header +# we will then supply only what we didn't write wth no required ec cmm, +# This test case is checking that the default cmm is doing the correct filtering +def test_GIVEN_RECCMM_with_one_REC_key_on_encrypt_AND_default_CMM_with_valid_reproduced_EC_on_decrypt_WHEN_supply_reproduced_EC_with_REC_key_on_decrypt_THEN_decrypt_matches_plaintext( # noqa pylint: disable=line-too-long + encryption_context +): + # Grab one item from encryption_context to supply as reproduced EC + one_k, one_v = next(iter(encryption_context.items())) + reproduced_ec = {one_k: one_v} + # Given: one required encryption context (REC) key + required_ec_keys = [one_k] + + default_cmm = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm, + # Given: one required encryption context (REC) key + required_encryption_context_keys=required_ec_keys + ) + ) + + # When: encrypt/decrypt cycle + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + # Given: required encryption context CMM (RECCMM) on encrypt + materials_manager=required_ec_cmm, + # Given: encryption context with REC key on encrypt + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + pt, _ = client.decrypt( + source=ct, + # Given: default CMM on decrypt (ESDK auto-creates default CMM) + keyring=decrypt_keyring, + # When: supply valid reproduced EC (containing REC key) on decrypt + encryption_context=reproduced_ec, + ) + + # Then: decrypted plaintext matches original plaintext, no errors + assert pt == SOME_PLAINTEXT + + +# This test needs >=1 item to supply as required encryption context +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.NONEMPTY_ENCRYPTION_CONTEXTS) +# HAPPY CASE 3 +# On Encrypt we will only write one encryption context key value to the header +# we will then supply only what we didn't write but included in the signature while we +# are configured with the required encryption context cmm +def test_GIVEN_RECCMM_with_one_REC_key_on_encrypt_AND_RECCMM_with_valid_reproduced_EC_on_decrypt_WHEN_supply_reproduced_EC_with_REC_key_on_decrypt_THEN_decrypt_matches_plaintext( # noqa pylint: disable=line-too-long + encryption_context +): + # Grab one item from encryption_context to supply as reproduced EC + one_k, one_v = next(iter(encryption_context.items())) + reproduced_ec = {one_k: one_v} + # Given: one required encryption context (REC) key + required_ec_keys = [one_k] + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm_encrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_encrypt, + # Given: one required encryption context (REC) key + required_encryption_context_keys=required_ec_keys + ) + ) + + # When: encrypt/decrypt cycle + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + # Given: required encryption context CMM (RECCMM) on encrypt + materials_manager=required_ec_cmm_encrypt, + # Given: encryption context with REC key on encrypt + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + default_cmm_decrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=decrypt_keyring, + ) + ) + + required_ec_cmm_decrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + # Given: required encryption context CMM (RECCMM) on decrypt + underlying_cmm=default_cmm_decrypt, + # Given: correct required encryption context (REC) keys on decrypt + required_encryption_context_keys=required_ec_keys + ) + ) + + pt, _ = client.decrypt( + source=ct, + # Given: required encryption context CMM (RECCMM) on decrypt + materials_manager=required_ec_cmm_decrypt, + # When: supply reproduced EC on decrypt + encryption_context=reproduced_ec, + ) + + # Then: decrypted plaintext matches original plaintext + assert pt == SOME_PLAINTEXT + + +# This test needs >=1 item to supply as required encryption context +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.NONEMPTY_ENCRYPTION_CONTEXTS) +# HAPPY CASE 4 +# On Encrypt we write all encryption context +# as if the message was encrypted before the feature existed. +# We will then have a required encryption context cmm +# that will require us to supply the encryption context on decrypt. +def test_GIVEN_default_CMM_on_encrypt_AND_default_CMM_with_valid_reproduced_EC_on_decrypt_WHEN_supply_reproduced_EC_with_REC_key_on_decrypt_THEN_decrypt_matches_plaintext( # noqa pylint: disable=line-too-long + encryption_context +): + # Grab one item from encryption_context to supply as reproduced EC + one_k, one_v = next(iter(encryption_context.items())) + reproduced_ec = {one_k: one_v} + # Given: one required encryption context (REC) key + required_ec_keys = [one_k] + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + # When: encrypt/decrypt cycle + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + # Given: default CMM on encrypt + materials_manager=default_cmm_encrypt, + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + default_cmm_decrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=decrypt_keyring, + ) + ) + + required_ec_cmm_decrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_decrypt, + # Given: one required encryption context (REC) key + required_encryption_context_keys=required_ec_keys + ) + ) + + pt, _ = client.decrypt( + source=ct, + # Given: required encryption context CMM (RECCMM) on decrypt + materials_manager=required_ec_cmm_decrypt, + # When: supply reproduced EC on decrypt + encryption_context=reproduced_ec, + ) + + # Then: decrypted plaintext matches original plaintext + assert pt == SOME_PLAINTEXT + + +# This test needs >=2 items in EC so it can swap their key/value pairs +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.AT_LEAST_TWO_ITEMS_ENCRYPTION_CONTEXTS) +# FAILURE CASE 1 +# Encrypt with and store all encryption context in header +# On Decrypt supply additional encryption context not stored in the header; this MUST fail +# On Decrypt supply mismatched encryption context key values; this MUST fail +def test_GIVEN_default_CMM_with_EC_on_encrypt_AND_default_CMM_with_different_EC_on_decrypt_WHEN_decrypt_THEN_raise_AWSEncryptionSDKClientError( # noqa pylint: disable=line-too-long + encryption_context, +): + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + keyring=SOME_MULTI_KEYRING, + encryption_context=encryption_context, + ) + + # Create some different ECs to test failure on decrypt + + # Swap one key/value pair to create a "mismatched" EC + ec_iter = iter(encryption_context.items()) + one_k, one_v = next(ec_iter) + two_k, two_v = next(ec_iter) + some_mismatched_ec = copy.deepcopy(encryption_context) + some_mismatched_ec[one_k] = two_v + some_mismatched_ec[two_k] = one_v + + # Some other encryption context where its key/value pair is not in the encryption context on encrypt + some_reproduced_ec_not_in_ec = {"this is not in": "the original encryption context"} + + for decrypt_keyring in TEST_KEYRINGS_LIST: + # Then: decrypting with mismatched EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=decrypt_keyring, + # Given: different encryption context on decrypt + encryption_context=some_mismatched_ec, + ) + + # Then: decrypting with some other EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=decrypt_keyring, + # Given: different encryption context on decrypt + encryption_context=some_reproduced_ec_not_in_ec, + ) + + +# This test needs >=1 item to supply as required encryption context +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.NONEMPTY_ENCRYPTION_CONTEXTS) +# FAILURE CASE 2 +# Encrypt will not store all Encryption Context, we will drop one entry but it will still get +# included in the +# header signture. +# Decrypt will not supply any reproduced Encryption Context; this MUST fail. +def test_GIVEN_RECCCMM_with_one_REC_key_on_encrypt_AND_default_CMM_with_no_EC_on_decrypt_WHEN_decrypt_THEN_raise_AWSEncryptionSDKClientError( # noqa pylint: disable=line-too-long + encryption_context, +): + # Grab one item from encryption_context to supply as reproduced EC + one_k, _ = next(iter(encryption_context.items())) + # Given: one required encryption context (REC) key + required_ec_keys = [one_k] + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm_encrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_encrypt, + # Given: one required encryption context (REC) key + required_encryption_context_keys=required_ec_keys + ) + ) + + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + materials_manager=required_ec_cmm_encrypt, + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + # Then: decrypting with no EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=decrypt_keyring, + # Given: no encryption context on decrypt + ) + + +# This test needs >=1 item to supply as required encryption context +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.NONEMPTY_ENCRYPTION_CONTEXTS) +# FAILURE CASE 3 +# Encrypt will not store all Encryption Context, we will drop one entry but it will still get +# included in the +# header signture. +# Decrypt will supply the correct key but incorrect value; this MUST fail. +def test_GIVEN_RECCMM_on_encrypt_AND_EC_with_wrong_value_for_key_on_decrypt_WHEN_decrypt_THEN_raise_AWSEncryptionSDKClientError( # noqa pylint: disable=line-too-long + encryption_context, +): + # Grab one item from encryption_context to supply as reproduced EC + one_k, _ = next(iter(encryption_context.items())) + # Given: one required encryption context (REC) key + required_ec_keys = [one_k] + # Create mismatched EC + some_mismatched_ec = copy.deepcopy(encryption_context) + some_mismatched_ec[one_k] = "some incorrect value NOT in the original encryption context" + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm_encrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_encrypt, + required_encryption_context_keys=required_ec_keys + ) + ) + + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + materials_manager=required_ec_cmm_encrypt, + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + # Then: decrypting with mismatched EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=decrypt_keyring, + # Given: encryption context with wrong value for required key on decrypt + encryption_context=some_mismatched_ec, + ) + + +# This test needs >=2 items in EC so it create incorrect reproduced EC scenarios +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.AT_LEAST_TWO_ITEMS_ENCRYPTION_CONTEXTS) +# FAILURE CASE 4 +# Encrypt will not store all Encryption Context, we will drop one entry but it will still get +# included in the +# header signture. +# Decrypt will supply the correct key but incorrect value; this MUST fail. +def test_GIVEN_RECCMM_on_encrypt_AND_reproduced_EC_missing_REC_key_on_decrypt_WHEN_decrypt_THEN_raise_AWSEncryptionSDKClientError( # noqa pylint: disable=line-too-long + encryption_context, +): + # Grab one item from encryption_context to supply as reproduced EC + ec_iter = iter(encryption_context.items()) + required_k, _ = next(ec_iter) + required_ec_keys = [required_k] + # Grab another item to use as the reproduced EC + # where the key in reproduced EC is not in required encryption context keys + some_other_k, some_other_v = next(ec_iter) + incorrect_reproduced_ec = {some_other_k : some_other_v} + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm_encrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_encrypt, + required_encryption_context_keys=required_ec_keys + ) + ) + + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + materials_manager=required_ec_cmm_encrypt, + encryption_context=encryption_context, + ) + + for decrypt_keyring in TEST_KEYRINGS_LIST: + # Then: decrypting with invalid EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=decrypt_keyring, + # Given: encryption context on decrypt does not have required EC key + encryption_context=incorrect_reproduced_ec, + ) + + +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.ALL_ENCRYPTION_CONTEXTS) +# FAILURE CASE 5 +# Although we are requesting that we remove a RESERVED key word from the encryption context +# The CMM instantiation will still succeed because the CMM is meant to work with different +# higher level +# encryption libraries who may have different reserved keys. Encryption will ultimately fail. +def test_GIVEN_RECCMM_with_reserved_key_on_encrypt_WHEN_encrypt_THEN_raise_AWSEncryptionSDKClientError( + encryption_context +): + invalid_encryption_context = copy.deepcopy(encryption_context) + # Add reserved EC item to encryption context to make it invalid + reserved_ec_keyword = "aws-crypto-public-key" + invalid_encryption_context[reserved_ec_keyword] = "some value in reserved key" + required_ec_keys = [reserved_ec_keyword] + + default_cmm_encrypt = mpl_client.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=SOME_MULTI_KEYRING, + ) + ) + + required_ec_cmm_encrypt = mpl_client.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + underlying_cmm=default_cmm_encrypt, + # Given: required encryption context keys has a reserved value + required_encryption_context_keys=required_ec_keys + ) + ) + + # Then: encrypting with reserved key in encryption context raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: encrypt + client.encrypt( + source=SOME_PLAINTEXT, + materials_manager=required_ec_cmm_encrypt, + encryption_context=invalid_encryption_context, + ) + + +# This test needs >=2 items in EC so it create invalid reproduced EC scenarios +@pytest.mark.parametrize("encryption_context", TestEncryptionContexts.AT_LEAST_TWO_ITEMS_ENCRYPTION_CONTEXTS) +@pytest.mark.parametrize("keyring", TEST_KEYRINGS_LIST) +def test_GIVEN_some_keyring_AND_some_EC_WHEN_decrypt_valid_message_with_mutated_EC_THEN_decryption_matches_expected_result( # noqa pylint: disable=line-too-long + keyring, + encryption_context, +): + # Additional EC + some_additional_ec = copy.deepcopy(encryption_context) + some_additional_ec["some extra key to add"] = "some extra value added" + + # Mismatched EC. Swap key/value pair to create a mismatched EC + ec_iter = iter(encryption_context.items()) + one_k, one_v = next(ec_iter) + two_k, two_v = next(ec_iter) + some_mismatched_ec = copy.deepcopy(encryption_context) + some_mismatched_ec[one_k] = two_v + some_mismatched_ec[two_k] = one_v + + ct, _ = client.encrypt( + source=SOME_PLAINTEXT, + # Given: some keyring + keyring=keyring, + # Given: some encryption context + encryption_context=encryption_context, + ) + + # Expected failure: incorrect EC + + # Then: decrypting with incorrect EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=keyring, + # Given: incorrect encryption context with an extra item + encryption_context=some_additional_ec, + ) + + # Expected failure: Mismatched EC (swapped value for 2 keys) + + # Then: decrypting with mismatched EC raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # When: decrypt + client.decrypt( + source=ct, + keyring=keyring, + # Given: mismatched encryption context on decrypt + encryption_context=some_mismatched_ec, + ) + + # Expected success: No encryption context supplied on decrypt + # (Success because the message was not encrypted with required EC CMM) + + # When: decrypt + pt, _ = client.decrypt( + source=ct, + keyring=keyring, + # Given: no encryption context on decrypt + ) + + # Then: decrypted plaintext matches original plaintext + assert pt == SOME_PLAINTEXT + + # Expected success: Correct encryption context supplied on decrypt + + # When: decrypt + pt, _ = client.decrypt( + source=ct, + keyring=keyring, + # Given: no encryption context on decrypt + encryption_context=encryption_context, + ) + + # Then: decrypted plaintext matches original plaintext + assert pt == SOME_PLAINTEXT diff --git a/test/mpl/unit/test_material_managers_mpl_cmm.py b/test/mpl/unit/test_material_managers_mpl_cmm.py new file mode 100644 index 000000000..e8d6ceb8b --- /dev/null +++ b/test/mpl/unit/test_material_managers_mpl_cmm.py @@ -0,0 +1,301 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.cmm logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" + +import pytest +from aws_cryptographic_material_providers.mpl.errors import AwsCryptographicMaterialProvidersException +from aws_cryptographic_material_providers.mpl.models import ( + AlgorithmSuiteIdESDK as MPL_AlgorithmSuiteIdESDK, + CommitmentPolicyESDK as MPL_CommitmentPolicyESDK, + DecryptionMaterials as MPL_DecryptionMaterials, + DecryptMaterialsInput as MPL_DecryptMaterialsInput, + EncryptionMaterials as MPL_EncryptionMaterials, + GetEncryptionMaterialsInput as MPL_GetEncryptionMaterialsInput, + GetEncryptionMaterialsOutput as MPL_GetEncryptionMaterialsOutput, +) +from aws_cryptographic_material_providers.mpl.references import ( + ICryptographicMaterialsManager as MPL_ICryptographicMaterialsManager, +) +from mock import MagicMock, patch + +from aws_encryption_sdk.exceptions import AWSEncryptionSDKClientError +from aws_encryption_sdk.identifiers import CommitmentPolicy +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL +from aws_encryption_sdk.structures import EncryptedDataKey as Native_EncryptedDataKey + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + + +mock_mpl_cmm = MagicMock(__class__=MPL_ICryptographicMaterialsManager) +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) +mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) +mock_reproduced_encryption_context = MagicMock(__class_=dict) + + +mock_edk = MagicMock(__class__=Native_EncryptedDataKey) +mock_mpl_key_provider_id = MagicMock(__class__=str) +mock_edk.key_provider.provider_id = mock_mpl_key_provider_id +mock_mpl_key_provider_info = MagicMock(__class__=bytes) +mock_edk.key_provider.key_info = mock_mpl_key_provider_info +mock_mpl_encrypted_data_key = MagicMock(__class__=bytes) +mock_edk.encrypted_data_key = mock_mpl_encrypted_data_key + + +def test_GIVEN_valid_mpl_cmm_WHEN_create_CryptoMaterialsManagerFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): + # Given: valid mpl_cmm + # When: create new CryptoMaterialsManagerFromMPL + mpl_cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + # Then: CryptoMaterialsManagerFromMPL is valid + assert mpl_cmm.mpl_cmm == mock_mpl_cmm + + +def test_GIVEN_invalid_mpl_cmm_WHEN_create_CryptoMaterialsManagerFromMPL_THEN_raise_ValueError(): + # Then: raises ValueError + with pytest.raises(ValueError): + # Given: invalid mpl_cmm + # When: create new CryptoMaterialsManagerFromMPL + CryptoMaterialsManagerFromMPL(mpl_cmm="not a valid mpl_cmm") + + +@patch.object(mock_mpl_cmm, "get_encryption_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_get_encryption_materials") +def test_GIVEN_valid_request_WHEN_get_encryption_materials_THEN_return_EncryptionMaterialsFromMPL( + mock_native_to_mpl_get_encryption_materials, + mock_get_encryption_materials, +): + + # Given: _native_to_mpl_get_encryption_materials creates a MPL_GetEncryptionMaterialsInput + mock_get_encryption_materials_input = MagicMock(__class__=MPL_GetEncryptionMaterialsInput) + mock_native_to_mpl_get_encryption_materials.return_value = mock_get_encryption_materials_input + + # Given: mpl_cmm.get_encryption_materials returns mock MPL encryption materials + mock_get_encryption_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) + mock_get_encryption_materials_output.encryption_materials = mock_mpl_encryption_materials + mock_get_encryption_materials.return_value = mock_get_encryption_materials_output + + # When: get_encryption_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + output = cmm.get_encryption_materials(mock_encryption_materials_request) + + # Then: + # Verify cmm returns EncryptionMaterialsFromMPL + assert isinstance(output, EncryptionMaterialsFromMPL) + # Verify returned EncryptionMaterialsHandler uses the output of `get_encryption_materials` + assert output.mpl_materials == mock_mpl_encryption_materials + # Verify we actually called `get_encryption_materials` + mock_mpl_cmm.get_encryption_materials.assert_called_once_with(mock_get_encryption_materials_input) + + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commitment_policy") +def test_GIVEN_mpl_cmm_raises_MPLException_WHEN_get_encryption_materials_THEN_raise_ESDKException( + _, + mock_mpl_algorithm_id, +): + # Given: _native_algorithm_id_to_mpl_algorithm_id returns a valid MPL algorithm ID + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + + # Then: Raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # Given: mpl_cmm.get_encryption_materials raises MPL exception + with patch.object(mock_mpl_cmm, "get_encryption_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + # When: get_encryption_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + cmm.get_encryption_materials(mock_encryption_materials_request) + + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commitment_policy") +def test_GIVEN_valid_mpl_commitment_policy_WHEN_native_to_mpl_get_encryption_materials_THEN_returns_MPL_GetEncryptionMaterialsInput( # noqa: E501 + mock_mpl_commitment_policy, + mock_mpl_algorithm_id, +): + # Given: _native_algorithm_id_to_mpl_algorithm_id returns a valid MPL algorithm ID + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + + # Given: commitment policy is some MPL ESDK commitment policy + mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + # When: _native_to_mpl_get_encryption_materials + output = CryptoMaterialsManagerFromMPL._native_to_mpl_get_encryption_materials( + mock_encryption_materials_request + ) + + # Then: returned MPL_GetEncryptionMaterialsInput is correct + assert isinstance(output, MPL_GetEncryptionMaterialsInput) + assert output.encryption_context == mock_encryption_materials_request.encryption_context + assert output.commitment_policy == mock_commitment_policy + assert output.max_plaintext_length == mock_encryption_materials_request.plaintext_length + + +def test_GIVEN_CommitmentPolicy_FORBID_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_FORBID_ENCRYPT_ALLOW_DECRYPT(): # noqa: E501 + # Given: native FORBID_ENCRYPT_ALLOW_DECRYPT + native_commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT + + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) + + # Then: Returns MPL FORBID_ENCRYPT_ALLOW_DECRYPT + assert isinstance(output, MPL_CommitmentPolicyESDK) + assert output.value == "FORBID_ENCRYPT_ALLOW_DECRYPT" + + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_ALLOW_DECRYPT_WHEN_native_to_mpl_commitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_ALLOW_DECRYPT(): # noqa: E501 + # Given: native REQUIRE_ENCRYPT_ALLOW_DECRYPT + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT + + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) + + # Then: Returns MPL REQUIRE_ENCRYPT_ALLOW_DECRYPT + assert isinstance(output, MPL_CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_ALLOW_DECRYPT" + + +def test_GIVEN_CommitmentPolicy_REQUIRE_ENCRYPT_REQUIRE_DECRYPT_WHEN_native_to_mpl_commitment_policy_THEN_returns_MPL_CommitmentPolicyESDK_REQUIRE_ENCRYPT_REQUIRE_DECRYPT(): # noqa: E501 + # Given: native REQUIRE_ENCRYPT_REQUIRE_DECRYPT + native_commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + # When: _native_to_mpl_commitment_policy + output = CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) + + # Then: Returns MPL REQUIRE_ENCRYPT_REQUIRE_DECRYPT + assert isinstance(output, MPL_CommitmentPolicyESDK) + assert output.value == "REQUIRE_ENCRYPT_REQUIRE_DECRYPT" + + +def test_GIVEN_CommitmentPolicy_unrecognized_WHEN_native_to_mpl_commitment_policy_THEN_raise_ValueError(): + # Given: invalid native commitment policy + native_commitment_policy = "not a commitment policy" + + # Then: Raises ValueError + with pytest.raises(ValueError): + # When: _native_to_mpl_commitment_policy + CryptoMaterialsManagerFromMPL._native_to_mpl_commitment_policy(native_commitment_policy) + + +@patch.object(mock_mpl_cmm, "decrypt_materials") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_valid_request_WHEN_decrypt_materials_THEN_return_DecryptionMaterialsFromMPL( + mock_native_to_mpl_decrypt_materials, + mock_get_encryption_materials, +): + # Given: mpl_cmm.get_decryption_materials returns mock MPL decryption materials + mock_decrypt_materials_output = MagicMock(__class__=MPL_GetEncryptionMaterialsOutput) + mock_decrypt_materials_output.decryption_materials = mock_mpl_decrypt_materials + mock_get_encryption_materials.return_value = mock_decrypt_materials_output + + # Given: CMMHandler._create_mpl_decrypt_materials_input_from_request creates a MPL_DecryptMaterialsInput + mock_decrypt_materials_input = MagicMock(__class__=MPL_GetEncryptionMaterialsInput) + mock_native_to_mpl_decrypt_materials.return_value = mock_decrypt_materials_input + + # When: decrypt_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + output = cmm.decrypt_materials(mock_decryption_materials_request) + + # Then: + # Verify cmm returns DecryptionMaterialsFromMPL + assert isinstance(output, DecryptionMaterialsFromMPL) + # Verify returned DecryptionMaterialsFromMPL uses the output of `decrypt_materials` + assert output.mpl_materials == mock_mpl_decrypt_materials + # Verify we actually called `decrypt_materials` + mock_mpl_cmm.decrypt_materials.assert_called_once_with(mock_decrypt_materials_input) + + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._create_mpl_decrypt_materials_input_from_request") +def test_GIVEN_decrypt_materials_raises_MPL_Exception_WHEN_call_decrypt_materials_THEN_raise_ESDK_Exception( + _ +): + # Then: Raises AWSEncryptionSDKClientError + with pytest.raises(AWSEncryptionSDKClientError): + # Given: mpl_cmm.decrypt_materials raises MPL exception + with patch.object(mock_mpl_cmm, "decrypt_materials", + side_effect=AwsCryptographicMaterialProvidersException("any")): + # When: decrypt_materials + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mock_mpl_cmm) + cmm.decrypt_materials(mock_decryption_materials_request) + + +def test_GIVEN_valid_native_algorithm_id_WHEN_native_algorithm_id_to_mpl_algorithm_id_THEN_returns_valid_MPL_AlgorithmSuiteIdESDK(): # noqa: E501 + # Given: any native algorithm ID + some_native_algorithm_id = 0x1234 # Not a real algorithm ID, but fits the format + + # When: _native_algorithm_id_to_mpl_algorithm_id + mpl_output = CryptoMaterialsManagerFromMPL._native_algorithm_id_to_mpl_algorithm_id( + some_native_algorithm_id + ) + + # Then: returns valid MPL algorithm ID + assert isinstance(mpl_output, MPL_AlgorithmSuiteIdESDK) + assert mpl_output.value == "0x1234" + + +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_algorithm_id_to_mpl_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.cmm.CryptoMaterialsManagerFromMPL" + "._native_to_mpl_commitment_policy") +def test_GIVEN_valid_request_WHEN_create_mpl_decrypt_materials_input_from_request_THEN_returns_MPL_MPL_DecryptMaterialsInput( # noqa: E501 + mock_mpl_commitment_policy, + mock_mpl_algorithm_id, +): + # Given: _native_algorithm_id_to_mpl_algorithm_id returns a valid MPL algorithm ID + mock_algorithm_id = "0x1234" # Some fake algorithm ID that fits the format + mock_mpl_algorithm_id.return_value = mock_algorithm_id + + # Given: _native_to_mpl_commitment_policy returns some MPL commitment policy + mock_commitment_policy = MagicMock(__class__=MPL_CommitmentPolicyESDK) + mock_mpl_commitment_policy.return_value = mock_commitment_policy + + no_mock_edks = [mock_edk] + one_mock_edk = [mock_edk] + two_mock_edks = [mock_edk, mock_edk] + + # Given: ESK lists of various lengths + for mock_edks in [no_mock_edks, one_mock_edk, two_mock_edks]: + + mock_decryption_materials_request.encrypted_data_keys = mock_edks + mock_decryption_materials_request.reproduced_encryption_context = mock_reproduced_encryption_context + + # When: _create_mpl_decrypt_materials_input_from_request + output = CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input_from_request( + mock_decryption_materials_request + ) + + # Then: + # Verify general correctness of output structure + assert isinstance(output, MPL_DecryptMaterialsInput) + assert output.algorithm_suite_id == mock_algorithm_id + assert output.commitment_policy == mock_commitment_policy + assert output.encryption_context == mock_decryption_materials_request.encryption_context + assert output.reproduced_encryption_context == mock_reproduced_encryption_context + + assert len(output.encrypted_data_keys) == len(mock_edks) + for i in range(len(output.encrypted_data_keys)): + # Assume input[i] == output[i] to make validation easier + # This is how the src is implemented but is not a requirement. + # If this assumption breaks, we should enhance this test. + output_edk = output.encrypted_data_keys[i] + input_edk = mock_edks[i] + assert output_edk.key_provider_id == input_edk.key_provider.provider_id + assert output_edk.key_provider_info == input_edk.key_provider.key_info + assert output_edk.ciphertext == input_edk.encrypted_data_key diff --git a/test/mpl/unit/test_material_managers_mpl_materials.py b/test/mpl/unit/test_material_managers_mpl_materials.py new file mode 100644 index 000000000..0466a6424 --- /dev/null +++ b/test/mpl/unit/test_material_managers_mpl_materials.py @@ -0,0 +1,228 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite to validate aws_encryption_sdk.materials_managers.mpl.materials logic. + +The aws-cryptographic-materials-library MUST be installed to run tests in this module. +""" + +import pytest +from aws_cryptographic_material_providers.mpl.models import ( + DecryptionMaterials as MPL_DecryptionMaterials, + EncryptedDataKey as MPL_EncryptedDataKey, + EncryptionMaterials as MPL_EncryptionMaterials, +) +from mock import MagicMock, patch +from typing import Dict + +import aws_encryption_sdk.materials_managers.mpl.materials +from aws_encryption_sdk.identifiers import AlgorithmSuite +from aws_encryption_sdk.materials_managers import DecryptionMaterialsRequest, EncryptionMaterialsRequest +from aws_encryption_sdk.materials_managers.mpl.materials import DecryptionMaterialsFromMPL, EncryptionMaterialsFromMPL + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +mock_mpl_encryption_materials = MagicMock(__class__=MPL_EncryptionMaterials) +mock_mpl_decrypt_materials = MagicMock(__class__=MPL_DecryptionMaterials) + +mock_encryption_materials_request = MagicMock(__class__=EncryptionMaterialsRequest) +mock_encryption_materials_handler = MagicMock(__class__=EncryptionMaterialsFromMPL) +mock_decryption_materials_request = MagicMock(__class__=DecryptionMaterialsRequest) + +mock_edk = MagicMock(__class__=MPL_EncryptedDataKey) +mock_mpl_key_provider_id = MagicMock(__class__=str) +mock_edk.key_provider_id = mock_mpl_key_provider_id +mock_mpl_key_provider_info = MagicMock(__class__=bytes) +mock_edk.key_provider_info = mock_mpl_key_provider_info +mock_mpl_ciphertext = MagicMock(__class__=bytes) +mock_edk.ciphertext = mock_mpl_ciphertext + + +def test_GIVEN_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_return_new_CryptoMaterialsManagerFromMPL(): + # Given: valid mpl_materials + # When: create EncryptionMaterialsFromMPL + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + + # Then: EncryptionMaterialsFromMPL is valid + assert mpl_encryption_materials.mpl_materials == mock_mpl_encryption_materials + + +def test_GIVEN_invalid_mpl_materials_WHEN_create_EncryptionMaterialsFromMPL_THEN_raise_ValueError(): + # Then: Raise ValueError + with pytest.raises(ValueError): + # Given: invalid mpl_materials + # When: create EncryptionMaterialsFromMPL + EncryptionMaterialsFromMPL(mpl_materials="not a valid mpl_materials") + + +def test_GIVEN_valid_mpl_algorithm_id_WHEN_mpl_algorithm_id_to_native_algorithm_id_THEN_valid_native_output(): + # Given: any valid MPL algorithm ID + some_mpl_algorithm_id = "0x1234" # Not a real algorithm ID, but fits the format + + # When: _mpl_algorithm_id_to_native_algorithm_id + native_output = aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id( + some_mpl_algorithm_id + ) + + # Then: valid native algorithm ID + assert native_output == 0x1234 + + +@patch("aws_encryption_sdk.materials_managers.mpl.materials._mpl_algorithm_id_to_native_algorithm_id") +@patch("aws_encryption_sdk.materials_managers.mpl.materials.AlgorithmSuite.get_by_id") +def test_GIVEN_valid_mpl_algorithm_id_WHEN_EncryptionMaterials_get_algorithm_THEN_valid_native_algorithm_id( + mock_algorithm, + mock_native_algorithm_id, +): + # Given: _mpl_algorithm_id_to_native_algorithm_id returns a valid native algorithm ID + mock_native_algorithm_id.return_value = 0x1234 + + # Given: get_by_id returns a valid native AlgorithmSuite by looking up an ID + mock_algorithm.return_value = MagicMock(__class__=AlgorithmSuite) + + # When: Get algorithm + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.algorithm + + # Then: output is valid + assert output == mock_algorithm() # property calls automatically, we need to call the mock + + +def test_GIVEN_valid_encryption_context_WHEN_EncryptionMaterials_get_encryption_context_THEN_valid_encryption_context(): + # Given: valid encryption context + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_encryption_materials.encryption_context = mock_encryption_context + + # When: get encryption context + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encryption_context + + # Then: returns valid encryption context + assert output == mock_encryption_context + + +def test_GIVEN_valid_edks_WHEN_EncryptionMaterials_get_edks_THEN_returns_edks(): + + # Given: lists of mocked EDKs of various lengths + no_mock_edks = [] + one_mock_edk = [mock_edk] + two_mocked_edks = [mock_edk, mock_edk] + for mock_edks in [no_mock_edks, one_mock_edk, two_mocked_edks]: + mock_mpl_encryption_materials.encrypted_data_keys = mock_edks + + # When: get EDKs + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.encrypted_data_keys + + # Then: returns EDKs + output_as_list = list(output) + # Native ESDK Python types the EDKs as a set; + # Ensure the MPL's list is collapsed into a set correctly + assert len(output_as_list) == len(set(mock_edks)) + for i in range(len(output_as_list)): + # Assume input[i] == output[i] to make validation easier + # This is how the src is implemented but is not a requirement. + # If this assumption breaks, we should enhance this test. + native_edk = output_as_list[i] + mpl_edk = mock_edks[i] + + assert native_edk.encrypted_data_key == mpl_edk.ciphertext + assert native_edk.key_provider.provider_id == mpl_edk.key_provider_id + assert native_edk.key_provider.key_info == mpl_edk.key_provider_info + + +def test_GIVEN_valid_data_key_WHEN_EncryptionMaterials_get_data_key_THEN_returns_data_key(): + # Given: Valid MPL data key + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.plaintext_data_key = mock_data_key + + # When: get data key + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.data_encryption_key + + # Then: Returns native data key + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +def test_GIVEN_valid_signing_key_WHEN_EncryptionMaterials_get_signing_key_THEN_returns_signing_key(): + # Given: valid signing key + mock_signing_key = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.signing_key = mock_signing_key + + # When: get signing key + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.signing_key + + # Then: returns signing key + assert output == mock_signing_key + + +def test_GIVEN_valid_required_encryption_context_keys_WHEN_EncryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): # noqa pylint: disable=line-too-long + # Given: valid required encryption context keys + mock_required_encryption_context_keys = MagicMock(__class__=bytes) + mock_mpl_encryption_materials.required_encryption_context_keys = mock_required_encryption_context_keys + + # When: get required encryption context keys + mpl_encryption_materials = EncryptionMaterialsFromMPL(mpl_materials=mock_mpl_encryption_materials) + output = mpl_encryption_materials.required_encryption_context_keys + + # Then: returns required encryption context keys + assert output == mock_required_encryption_context_keys + + +def test_GIVEN_valid_data_key_WHEN_DecryptionMaterials_get_data_key_THEN_returns_data_key(): + # Given: valid MPL data key + mock_data_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.plaintext_data_key = mock_data_key + + # When: get data key + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.data_key + + # Then: returns valid native data key + assert output.key_provider.provider_id == "" + assert output.key_provider.key_info == b"" + assert output.data_key == mock_data_key + assert output.encrypted_data_key == b"" + + +def test_GIVEN_valid_verification_key_WHEN_DecryptionMaterials_get_verification_key_THEN_returns_verification_key(): + # Given: valid verification key + mock_verification_key = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.verification_key = mock_verification_key + + # When: get verification key + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.verification_key + + # Then: returns verification key + assert output == mock_verification_key + + +def test_GIVEN_valid_encryption_context_WHEN_DecryptionMaterials_get_encryption_context_THEN_returns_encryption_context(): # noqa pylint: disable=line-too-long + # Given: valid encryption context + mock_encryption_context = MagicMock(__class__=Dict[str, str]) + mock_mpl_decrypt_materials.encryption_context = mock_encryption_context + + # When: get encryption context + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.encryption_context + + # Then: returns valid encryption context + assert output == mock_encryption_context + + +def test_GIVEN_valid_required_encryption_context_keys_WHEN_DecryptionMaterials_get_required_encryption_context_keys_THEN_returns_required_encryption_context_keys(): # noqa pylint: disable=line-too-long + # Given: valid required encryption context keys + mock_required_encryption_context_keys = MagicMock(__class__=bytes) + mock_mpl_decrypt_materials.required_encryption_context_keys = mock_required_encryption_context_keys + + # When: get required encryption context keys + mpl_decryption_materials = DecryptionMaterialsFromMPL(mpl_materials=mock_mpl_decrypt_materials) + output = mpl_decryption_materials.required_encryption_context_keys + + # Then: returns required encryption context keys + assert output == mock_required_encryption_context_keys diff --git a/test/mpl/utils.py b/test/mpl/utils.py new file mode 100644 index 000000000..a3168c02c --- /dev/null +++ b/test/mpl/utils.py @@ -0,0 +1,183 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import random +import secrets +import string + +import boto3 +from aws_cryptographic_material_providers.keystore import KeyStore +from aws_cryptographic_material_providers.keystore.config import KeyStoreConfig +from aws_cryptographic_material_providers.keystore.models import KMSConfigurationKmsKeyArn +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + AesWrappingAlg, + CacheTypeDefault, + CreateAwsKmsHierarchicalKeyringInput, + CreateAwsKmsKeyringInput, + CreateMultiKeyringInput, + CreateRawAesKeyringInput, + CreateRawRsaKeyringInput, + DefaultCache, + PaddingScheme, +) +from aws_cryptographic_material_providers.mpl.references import IKeyring +from cryptography.hazmat.backends import default_backend as crypto_default_backend +from cryptography.hazmat.primitives import serialization as crypto_serialization +from cryptography.hazmat.primitives.asymmetric import rsa + +# MPL client. Used to create keyrings. +mpl_client: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() +) + + +class TestKeyringCreator: + + # Private raw AES keyring creator. + # Lifted from raw AES keyring example. + @staticmethod + def _create_raw_aes_keyring(): + static_key = secrets.token_bytes(32) + + keyring_input: CreateRawAesKeyringInput = CreateRawAesKeyringInput( + key_namespace="some_key_namespace", + key_name="some_key_name", + wrapping_key=static_key, + wrapping_alg=AesWrappingAlg.ALG_AES256_GCM_IV12_TAG16 + ) + + raw_aes_keyring: IKeyring = mpl_client.create_raw_aes_keyring( + input=keyring_input + ) + + return raw_aes_keyring + + # Private raw RSA keyring creator. + # Lifted from raw RSA keyring example. + @staticmethod + def _create_raw_rsa_keyring(): + ssh_rsa_exponent = 65537 + bit_strength = 4096 + key = rsa.generate_private_key( + backend=crypto_default_backend(), + public_exponent=ssh_rsa_exponent, + key_size=bit_strength + ) + + public_key = key.public_key().public_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PublicFormat.SubjectPublicKeyInfo + ) + private_key = key.private_bytes( + encoding=crypto_serialization.Encoding.PEM, + format=crypto_serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=crypto_serialization.NoEncryption() + ) + + key_name_space = "some_key_name_space" + key_name = "some_key_name" + + keyring_input: CreateRawRsaKeyringInput = CreateRawRsaKeyringInput( + key_namespace=key_name_space, + key_name=key_name, + padding_scheme=PaddingScheme.OAEP_SHA256_MGF1, + public_key=public_key, + private_key=private_key + ) + + raw_rsa_keyring: IKeyring = mpl_client.create_raw_rsa_keyring( + input=keyring_input + ) + + return raw_rsa_keyring + + # Private KMS keyring creator. + # Lifted KMS keyring example. + @staticmethod + def _create_kms_keyring(): + kms_client = boto3.client('kms', region_name="us-west-2") + keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput( + kms_key_id="arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f", + kms_client=kms_client + ) + + kms_keyring: IKeyring = mpl_client.create_aws_kms_keyring( + input=keyring_input + ) + + return kms_keyring + + # Private hierarchical keyring creator. + # Lifted hierarchical keyring example. + @staticmethod + def _create_hierarchical_keyring(): + kms_client = boto3.client('kms', region_name="us-west-2") + ddb_client = boto3.client('dynamodb', region_name="us-west-2") + + keystore: KeyStore = KeyStore( + config=KeyStoreConfig( + ddb_client=ddb_client, + ddb_table_name="KeyStoreDdbTable", + logical_key_store_name="KeyStoreDdbTable", + kms_client=kms_client, + kms_configuration=KMSConfigurationKmsKeyArn( + value='arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126' + ), + ) + ) + + keyring_input: CreateAwsKmsHierarchicalKeyringInput = CreateAwsKmsHierarchicalKeyringInput( + key_store=keystore, + branch_key_id='a52dfaad-7dbd-4430-a1fd-abaa5299da07', + ttl_seconds=600, + cache=CacheTypeDefault( + value=DefaultCache( + entry_capacity=100 + ) + ), + ) + + hierarchical_keyring: IKeyring = mpl_client.create_aws_kms_hierarchical_keyring( + input=keyring_input + ) + + return hierarchical_keyring + + # Private multi-keyring creator. + @staticmethod + def _create_multi_keyring(keyrings): + a = mpl_client.create_multi_keyring(CreateMultiKeyringInput( + generator=keyrings[0], + child_keyrings=keyrings[1:] + )) + return a + + +class TestEncryptionContexts: + + # Encryption contexts under test + SOME_EMPTY_ENCRYPTION_CONTEXT = {} + SOME_SINGLE_ITEM_ENCRYPTION_CONTEXT = {"some_key": "some_value"} + SOME_DOUBLE_ITEM_ENCRYPTION_CONTEXT = {"some_key": "some_value", "some_other_key": "some_other_value"} + SOME_MANY_ITEM_ENCRYPTION_CONTEXT = { + ''.join(random.choices(string.ascii_letters, k=6)) + : ''.join(random.choices(string.ascii_letters, k=6)) for _ in range(20) + } + ALL_ENCRYPTION_CONTEXTS = [ + SOME_EMPTY_ENCRYPTION_CONTEXT, + SOME_SINGLE_ITEM_ENCRYPTION_CONTEXT, + SOME_DOUBLE_ITEM_ENCRYPTION_CONTEXT, + SOME_MANY_ITEM_ENCRYPTION_CONTEXT, + ] + + NONEMPTY_ENCRYPTION_CONTEXTS = [ + SOME_SINGLE_ITEM_ENCRYPTION_CONTEXT, + SOME_DOUBLE_ITEM_ENCRYPTION_CONTEXT, + SOME_MANY_ITEM_ENCRYPTION_CONTEXT, + ] + + AT_LEAST_TWO_ITEMS_ENCRYPTION_CONTEXTS = [ + SOME_DOUBLE_ITEM_ENCRYPTION_CONTEXT, + SOME_MANY_ITEM_ENCRYPTION_CONTEXT, + ] diff --git a/test/unit/__init__.py b/test/unit/__init__.py index 53a960891..f94fd12a2 100644 --- a/test/unit/__init__.py +++ b/test/unit/__init__.py @@ -1,12 +1,2 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/test/unit/test_algorithm_suite.py b/test/unit/test_algorithm_suite.py index 0176a9f8f..9735088c0 100644 --- a/test/unit/test_algorithm_suite.py +++ b/test/unit/test_algorithm_suite.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.identifiers.AlgorithmSuite.""" import pytest diff --git a/test/unit/test_arn.py b/test/unit/test_arn.py index 141be7322..42a3a7f7e 100644 --- a/test/unit/test_arn.py +++ b/test/unit/test_arn.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.arn functions.""" import pytest diff --git a/test/unit/test_caches.py b/test/unit/test_caches.py index 8f7d32280..465325f97 100644 --- a/test/unit/test_caches.py +++ b/test/unit/test_caches.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.caches common functions.""" import struct from base64 import b64decode diff --git a/test/unit/test_caches_base.py b/test/unit/test_caches_base.py index c3aa2369b..f62d81dbb 100644 --- a/test/unit/test_caches_base.py +++ b/test/unit/test_caches_base.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for CryptoMaterialsCache""" import pytest @@ -19,18 +9,16 @@ def test_abstracts(): - with pytest.raises(TypeError) as excinfo: + with pytest.raises(TypeError, match='instantiate abstract class CryptoMaterialsCache') as excinfo: CryptoMaterialsCache() - excinfo.match( - r"Can't instantiate abstract class CryptoMaterialsCache with abstract methods {}".format( - ", ".join( - [ - "get_decryption_materials", - "get_encryption_materials", - "put_decryption_materials", - "put_encryption_materials", - ] - ) - ) - ) + exception = str(excinfo.value) + method_names = [ + "get_decryption_materials", + "get_encryption_materials", + "put_decryption_materials", + "put_encryption_materials" + ] + for name in method_names: + if exception.rfind(name) == -1: + raise AssertionError("{} missing from Exception Message".format(name)) diff --git a/test/unit/test_caches_crypto_cache_entry.py b/test/unit/test_caches_crypto_cache_entry.py index 15c946ee4..cc487427f 100644 --- a/test/unit/test_caches_crypto_cache_entry.py +++ b/test/unit/test_caches_crypto_cache_entry.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for CryptoMaterialsCacheEntry and CryptoMaterialsCacheEntryHints""" import pytest from mock import MagicMock diff --git a/test/unit/test_caches_local.py b/test/unit/test_caches_local.py index 6ced05d8b..e54482ddb 100644 --- a/test/unit/test_caches_local.py +++ b/test/unit/test_caches_local.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit testing suite for LocalCryptoMaterialsCache""" import weakref from collections import OrderedDict, deque diff --git a/test/unit/test_caches_null.py b/test/unit/test_caches_null.py index d593b2332..89fb36864 100644 --- a/test/unit/test_caches_null.py +++ b/test/unit/test_caches_null.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit testing suite for NullCryptoMaterialsCache""" import pytest from mock import MagicMock diff --git a/test/unit/test_commitment.py b/test/unit/test_commitment.py index 4545530e3..18fb4bf08 100644 --- a/test/unit/test_commitment.py +++ b/test/unit/test_commitment.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit testing suite for commitment utility functions""" import pytest from mock import MagicMock diff --git a/test/unit/test_compatability.py b/test/unit/test_compatability.py index bf444052d..bd602c7cd 100644 --- a/test/unit/test_compatability.py +++ b/test/unit/test_compatability.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.compatability""" import sys diff --git a/test/unit/test_crypto.py b/test/unit/test_crypto.py index 23a5c4975..6b0389d11 100644 --- a/test/unit/test_crypto.py +++ b/test/unit/test_crypto.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.crypto""" from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 11271abfb..4d2623c62 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -1,18 +1,9 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Signer``.""" +import cryptography.hazmat.primitives.serialization import pytest -from mock import MagicMock, sentinel +from mock import MagicMock, patch, sentinel from pytest_mock import mocker # noqa pylint: disable=unused-import import aws_encryption_sdk.internal.crypto.authentication @@ -77,18 +68,82 @@ def test_f_signer_key_bytes(): assert test.key_bytes() == VALUES["ecc_private_key_prime_private_bytes"] -def test_signer_from_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): +def test_GIVEN_no_encoding_WHEN_signer_from_key_bytes_THEN_load_der_private_key( + patch_default_backend, + patch_build_hasher, + patch_ec +): mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) - signer = Signer.from_key_bytes(algorithm=_algorithm, key_bytes=sentinel.key_bytes) + # Make a new patched serialization module for this test. + # The default patch introduces serialization as `serialization.Encoding.DER` + # from within the src, but is `Encoding.DER` in the test. + # This namespace change causes the src's `isinstance` checks to fail. + # Mock the `serialization.Encoding.DER` + with patch.object(cryptography.hazmat.primitives, "serialization"): + # Mock the `serialization.load_der_private_key` + with patch.object( + aws_encryption_sdk.internal.crypto.authentication.serialization, + "load_der_private_key" + ) as mock_der: + # When: from_key_bytes + Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: No encoding provided => default arg + ) + + # Then: calls load_der_private_key + mock_der.assert_called_once_with( + data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value + ) + + +def test_GIVEN_PEM_encoding_WHEN_signer_from_key_bytes_THEN_load_pem_private_key( + patch_default_backend, + patch_serialization, + patch_build_hasher, + patch_ec +): + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) - patch_serialization.load_der_private_key.assert_called_once_with( + # When: from_key_bytes + signer = Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: PEM encoding + encoding=patch_serialization.Encoding.PEM + ) + + # Then: calls load_pem_private_key + patch_serialization.load_pem_private_key.assert_called_once_with( data=sentinel.key_bytes, password=None, backend=patch_default_backend.return_value ) assert isinstance(signer, Signer) assert signer.algorithm is _algorithm - assert signer.key is patch_serialization.load_der_private_key.return_value + assert signer.key is patch_serialization.load_pem_private_key.return_value + + +def test_GIVEN_unrecognized_encoding_WHEN_signer_from_key_bytes_THEN_raise_ValueError( + patch_default_backend, + patch_serialization, + patch_build_hasher, + patch_ec +): + mock_algorithm_info = MagicMock(return_value=sentinel.algorithm_info, spec=patch_ec.EllipticCurve) + _algorithm = MagicMock(signing_algorithm_info=mock_algorithm_info) + + # Then: Raises ValueError + with pytest.raises(ValueError): + # When: from_key_bytes + Signer.from_key_bytes( + algorithm=_algorithm, + key_bytes=sentinel.key_bytes, + # Given: Invalid encoding + encoding="not an encoding" + ) def test_signer_key_bytes(patch_default_backend, patch_serialization, patch_build_hasher, patch_ec): diff --git a/test/unit/test_crypto_authentication_verifier.py b/test/unit/test_crypto_authentication_verifier.py index 05af581eb..7562fa762 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.authentication.Verifier``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_crypto_data_keys.py b/test/unit/test_crypto_data_keys.py index 6d00f5e8c..5dd2366db 100644 --- a/test/unit/test_crypto_data_keys.py +++ b/test/unit/test_crypto_data_keys.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.data_keys``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_crypto_elliptic_curve.py b/test/unit/test_crypto_elliptic_curve.py index 02497b31c..1e04d6e3c 100644 --- a/test/unit/test_crypto_elliptic_curve.py +++ b/test/unit/test_crypto_elliptic_curve.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.elliptic_curve``.""" import sys diff --git a/test/unit/test_crypto_encryption_decryptor.py b/test/unit/test_crypto_encryption_decryptor.py index f709bea1e..57ea90383 100644 --- a/test/unit/test_crypto_encryption_decryptor.py +++ b/test/unit/test_crypto_encryption_decryptor.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.encryption.Decryptor``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_crypto_encryption_encryptor.py b/test/unit/test_crypto_encryption_encryptor.py index 207aa36dd..717f95e7c 100644 --- a/test/unit/test_crypto_encryption_encryptor.py +++ b/test/unit/test_crypto_encryption_encryptor.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.encryption.Encryptor``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_crypto_prehashing_authenticator.py b/test/unit/test_crypto_prehashing_authenticator.py index 104133da0..631391f53 100644 --- a/test/unit/test_crypto_prehashing_authenticator.py +++ b/test/unit/test_crypto_prehashing_authenticator.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto._PrehashingAuthenticater``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_crypto_wrapping_keys.py b/test/unit/test_crypto_wrapping_keys.py index 459d2cc40..1971b2934 100644 --- a/test/unit/test_crypto_wrapping_keys.py +++ b/test/unit/test_crypto_wrapping_keys.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for ``aws_encryption_sdk.internal.crypto.wrapping_keys``.""" import pytest from mock import MagicMock, sentinel diff --git a/test/unit/test_defaults.py b/test/unit/test_defaults.py index 37965768b..cf1157f1e 100644 --- a/test/unit/test_defaults.py +++ b/test/unit/test_defaults.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite to verify calculated values in aws_encryption_sdk.internal.defaults""" import pytest diff --git a/test/unit/test_deserialize.py b/test/unit/test_deserialize.py index f5d257971..f0273c428 100644 --- a/test/unit/test_deserialize.py +++ b/test/unit/test_deserialize.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.deserialize""" import io import struct @@ -275,6 +265,16 @@ def test_deserialize_body_frame_final(self): assert test_body == VALUES["deserialized_body_final_frame_single"] assert test_final + def test_GIVEN_final_frame_content_length_equals_header_frame_length_WHEN_deserialize_header_THEN_no_error(self): + """Validate that the deserialize_body_frame function + behaves as expected for a valid final body frame + where the final frame length equals the header frame length. + """ + stream = io.BytesIO(VALUES["serialized_final_frame_512_length"]) + aws_encryption_sdk.internal.formatting.deserialize.deserialize_frame( + stream=stream, header=VALUES["deserialized_header_frame_512_frame"] + ) + def test_deserialize_body_frame_final_invalid_final_frame_length(self): """Validate that the deserialize_body_frame function behaves as expected for a valid final body frame. diff --git a/test/unit/test_encryption_client.py b/test/unit/test_encryption_client.py index db2ca1c4c..06cfe16e8 100644 --- a/test/unit/test_encryption_client.py +++ b/test/unit/test_encryption_client.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.EncryptionSDKClient""" import warnings diff --git a/test/unit/test_encryption_context.py b/test/unit/test_encryption_context.py index ccc18c219..bd717fbd8 100644 --- a/test/unit/test_encryption_context.py +++ b/test/unit/test_encryption_context.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.formatting.encryption_context""" import pytest diff --git a/test/unit/test_identifiers.py b/test/unit/test_identifiers.py index 3fd421b65..f475fe329 100644 --- a/test/unit/test_identifiers.py +++ b/test/unit/test_identifiers.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.identifiers""" import pytest from mock import Mock diff --git a/test/unit/test_internal_structures.py b/test/unit/test_internal_structures.py index d57166982..29b780b3a 100644 --- a/test/unit/test_internal_structures.py +++ b/test/unit/test_internal_structures.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.structures""" import pytest diff --git a/test/unit/test_material_managers.py b/test/unit/test_material_managers.py index 2d892bd36..4de2030a4 100644 --- a/test/unit/test_material_managers.py +++ b/test/unit/test_material_managers.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.materials_managers""" import pytest from mock import MagicMock diff --git a/test/unit/test_material_managers_base.py b/test/unit/test_material_managers_base.py index 792118979..8d55a290b 100644 --- a/test/unit/test_material_managers_base.py +++ b/test/unit/test_material_managers_base.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.materials_managers.base""" import pytest @@ -19,11 +9,10 @@ def test_abstracts(): - with pytest.raises(TypeError) as excinfo: + with pytest.raises(TypeError, match='instantiate abstract class CryptoMaterialsManager') as excinfo: CryptoMaterialsManager() - - excinfo.match( - r"Can't instantiate abstract class CryptoMaterialsManager with abstract methods {}".format( - ", ".join(["decrypt_materials", "get_encryption_materials"]) - ) - ) + method_names = ["decrypt_materials", "get_encryption_materials"] + exception = str(excinfo.value) + for name in method_names: + if exception.rfind(name) == -1: + raise AssertionError("{} missing from Exception Message".format(name)) diff --git a/test/unit/test_material_managers_caching.py b/test/unit/test_material_managers_caching.py index 04540e7bd..7f186becc 100644 --- a/test/unit/test_material_managers_caching.py +++ b/test/unit/test_material_managers_caching.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for CachingCryptoMaterialsManager""" import pytest diff --git a/test/unit/test_material_managers_default.py b/test/unit/test_material_managers_default.py index 4d35d072b..bd872ddfd 100644 --- a/test/unit/test_material_managers_default.py +++ b/test/unit/test_material_managers_default.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.materials_managers.default""" import pytest diff --git a/test/unit/test_providers_base_master_key.py b/test/unit/test_providers_base_master_key.py index 86c290196..6514e87c2 100644 --- a/test/unit/test_providers_base_master_key.py +++ b/test/unit/test_providers_base_master_key.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.key_providers.base.MasterKey""" import attr import pytest diff --git a/test/unit/test_providers_base_master_key_config.py b/test/unit/test_providers_base_master_key_config.py index 8b6c8731a..994fff637 100644 --- a/test/unit/test_providers_base_master_key_config.py +++ b/test/unit/test_providers_base_master_key_config.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.key_providers.base.MasterKeyConfig""" import pytest diff --git a/test/unit/test_providers_base_master_key_provider.py b/test/unit/test_providers_base_master_key_provider.py index a4b4e3832..fc18769a6 100644 --- a/test/unit/test_providers_base_master_key_provider.py +++ b/test/unit/test_providers_base_master_key_provider.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.key_providers.base.MasterKeyProvider""" import attr import pytest diff --git a/test/unit/test_providers_base_master_key_provider_config.py b/test/unit/test_providers_base_master_key_provider_config.py index 0e21ded80..56ea6fe45 100644 --- a/test/unit/test_providers_base_master_key_provider_config.py +++ b/test/unit/test_providers_base_master_key_provider_config.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.key_providers.base.MasterKeyProviderConfig""" from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig # noqa pylint: disable=unused-import diff --git a/test/unit/test_providers_kms_master_key.py b/test/unit/test_providers_kms_master_key.py index be91c96c7..01a87d4fd 100644 --- a/test/unit/test_providers_kms_master_key.py +++ b/test/unit/test_providers_kms_master_key.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.key_providers.kms.KMSMasterKey""" import botocore.client import pytest diff --git a/test/unit/test_providers_kms_master_key_config.py b/test/unit/test_providers_kms_master_key_config.py index 1501c951f..baaf0573d 100644 --- a/test/unit/test_providers_kms_master_key_config.py +++ b/test/unit/test_providers_kms_master_key_config.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.key_providers.kms.KMSMasterKeyConfig""" import boto3 import pytest diff --git a/test/unit/test_providers_kms_master_key_provider.py b/test/unit/test_providers_kms_master_key_provider.py index 6e142bb58..6cc6c81e4 100644 --- a/test/unit/test_providers_kms_master_key_provider.py +++ b/test/unit/test_providers_kms_master_key_provider.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite from aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider""" import botocore.client import botocore.session diff --git a/test/unit/test_providers_kms_master_key_provider_config.py b/test/unit/test_providers_kms_master_key_provider_config.py index affa74102..bc7092c5f 100644 --- a/test/unit/test_providers_kms_master_key_provider_config.py +++ b/test/unit/test_providers_kms_master_key_provider_config.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.key_providers.kms.KMSMasterKeyProviderConfig""" import botocore.session import pytest diff --git a/test/unit/test_providers_raw_master_key.py b/test/unit/test_providers_raw_master_key.py index 9abcd14c6..93f40ff22 100644 --- a/test/unit/test_providers_raw_master_key.py +++ b/test/unit/test_providers_raw_master_key.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.key_providers.raw.RawMasterKey""" import pytest from mock import MagicMock, patch, sentinel diff --git a/test/unit/test_providers_raw_master_key_config.py b/test/unit/test_providers_raw_master_key_config.py index d06feae87..e61e7bf1e 100644 --- a/test/unit/test_providers_raw_master_key_config.py +++ b/test/unit/test_providers_raw_master_key_config.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.key_providers.raw.RawMasterKeyConfig""" import pytest import six diff --git a/test/unit/test_providers_raw_master_key_provider.py b/test/unit/test_providers_raw_master_key_provider.py index 5128b1e22..aeb23887c 100644 --- a/test/unit/test_providers_raw_master_key_provider.py +++ b/test/unit/test_providers_raw_master_key_provider.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.key_providers.raw.RawMasterKeyProvider""" import attr import pytest diff --git a/test/unit/test_serialize.py b/test/unit/test_serialize.py index 56da114b4..1df4d1bd7 100644 --- a/test/unit/test_serialize.py +++ b/test/unit/test_serialize.py @@ -1,19 +1,13 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.formatting.serialize""" +import io +import struct + import pytest from mock import MagicMock, patch, sentinel +import aws_encryption_sdk.internal.formatting.deserialize import aws_encryption_sdk.internal.formatting.serialize from aws_encryption_sdk.exceptions import SerializationError from aws_encryption_sdk.identifiers import ContentAADString, SerializationVersion @@ -25,6 +19,8 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +provider_input_strings = ["", "abc", "𐀂", "abc𐀂", "𐀂abc", "秘密代码", "abc秘密代码", "秘密代码abc", "秘密代码abc𐀂", "𐀂abc秘密代码123𐀂"] + @pytest.mark.parametrize( "sequence_number, error_message", @@ -79,6 +75,7 @@ def apply_fixtures(self): "aws_encryption_sdk.internal.formatting.serialize.aws_encryption_sdk.internal.utils.validate_frame_length" ) self.mock_valid_frame_length = self.mock_valid_frame_length_patcher.start() + self.mock_required_ec_bytes = MagicMock() # Set up mock signer self.mock_signer = MagicMock() self.mock_signer.update.return_value = None @@ -89,6 +86,146 @@ def apply_fixtures(self): self.mock_encrypt_patcher.stop() self.mock_valid_frame_length_patcher.stop() + @pytest.mark.parametrize("provider_id", provider_input_strings) + @pytest.mark.parametrize("provider_info", provider_input_strings) + def test_GIVEN_valid_encrypted_data_key_WHEN_serialize_encrypted_data_key_THEN_deserialize_equals_input( + self, + provider_id, + provider_info, + ): + # Given: Some valid encrypted data key + key_provider = MasterKeyInfo(provider_id=provider_id, key_info=provider_info) + encrypted_data_key = EncryptedDataKey( + key_provider=key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + # When: serialize_encrypted_data_key + serialized_edk = aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key + ) + + # Then: Can deserialize the value + serialized_edks = bytes() + # Hardcode to have only 1 EDK + serialized_edks += struct.pack(">H", 1) + serialized_edks += serialized_edk + # Deserialization must not raise exception + deserialized = aws_encryption_sdk.internal.formatting.deserialize.deserialize_encrypted_data_keys( + stream=io.BytesIO(serialized_edks) + ) + assert deserialized == {encrypted_data_key} + assert len(deserialized) == 1 + deserialized_edk = list(deserialized)[0] + assert deserialized_edk.key_provider == encrypted_data_key.key_provider + assert deserialized_edk.key_provider.provider_id == encrypted_data_key.key_provider.provider_id + assert deserialized_edk.key_provider.key_info == encrypted_data_key.key_provider.key_info + assert deserialized_edk.encrypted_data_key == encrypted_data_key.encrypted_data_key + + @pytest.mark.parametrize("edk_1_provider_id", provider_input_strings) + @pytest.mark.parametrize("edk_1_provider_info", provider_input_strings) + @pytest.mark.parametrize("edk_2_provider_id", provider_input_strings) + @pytest.mark.parametrize("edk_2_provider_info", provider_input_strings) + def test_GIVEN_two_distinct_valid_encrypted_data_keys_WHEN_serialize_encrypted_data_keys_THEN_deserialize_equals_inputs( # noqa pylint: disable=line-too-long + self, + edk_1_provider_id, + edk_1_provider_info, + edk_2_provider_id, + edk_2_provider_info, + ): + # pylint: disable=too-many-locals + # Given: Two distinct valid encrypted data keys + edk_1_key_provider = MasterKeyInfo(provider_id=edk_1_provider_id, key_info=edk_1_provider_info) + encrypted_data_key_1 = EncryptedDataKey( + key_provider=edk_1_key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + edk_2_key_provider = MasterKeyInfo(provider_id=edk_2_provider_id, key_info=edk_2_provider_info) + encrypted_data_key_2 = EncryptedDataKey( + key_provider=edk_2_key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + # Must be distinct + if encrypted_data_key_1 == encrypted_data_key_2: + return + + # When: serialize_encrypted_data_key + serialized_edk_1 = aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key_1 + ) + serialized_edk_2 = aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key_2 + ) + + # Then: Can deserialize the value + serialized_edks = bytes() + # Hardcode to have only 2 EDKs + serialized_edks += struct.pack(">H", 2) + serialized_edks += serialized_edk_1 + serialized_edks += serialized_edk_2 + # Deserialization must not raise exception + deserialized = aws_encryption_sdk.internal.formatting.deserialize.deserialize_encrypted_data_keys( + stream=io.BytesIO(serialized_edks) + ) + assert deserialized == {encrypted_data_key_1, encrypted_data_key_2} + assert len(deserialized) == 2 + deserialized_edk_list = list(deserialized) + + deserialized_edk_some = deserialized_edk_list[0] + deserialized_edk_other = deserialized_edk_list[1] + + assert ( + (deserialized_edk_some == encrypted_data_key_1 and deserialized_edk_other == encrypted_data_key_2) + or (deserialized_edk_some == encrypted_data_key_2 and deserialized_edk_other == encrypted_data_key_1) + ) + + def test_GIVEN_invalid_encrypted_data_key_WHEN_serialize_THEN_raises_UnicodeEncodeError( + self, + ): + # Given: Some invalid encrypted data key + + # This is invalid because "\ud800\udc02" cannot be encoded to UTF-8. + # This value MUST be able to be encoded to UTF-8, or serialization will fail. + invalid_provider_string = "\ud800\udc02" + + # Then: raises UnicodeEncodeError + with pytest.raises(UnicodeEncodeError): + key_provider = MasterKeyInfo(provider_id=invalid_provider_string, key_info=invalid_provider_string) + + encrypted_data_key = EncryptedDataKey( + key_provider=key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + # When: serialize_encrypted_data_key + aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key + ) + + # Then: raises UnicodeEncodeError + with pytest.raises(UnicodeEncodeError): + key_provider = MasterKeyInfo(provider_id=invalid_provider_string, key_info="abc") + + encrypted_data_key = EncryptedDataKey( + key_provider=key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + # When: serialize_encrypted_data_key + aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key + ) + + # Then: raises UnicodeEncodeError + with pytest.raises(UnicodeEncodeError): + key_provider = MasterKeyInfo(provider_id="abc", key_info=invalid_provider_string) + + encrypted_data_key = EncryptedDataKey( + key_provider=key_provider, encrypted_data_key=VALUES["encrypted_data_key"] + ) + + # When: serialize_encrypted_data_key + aws_encryption_sdk.internal.formatting.serialize.serialize_encrypted_data_key( + encrypted_data_key=encrypted_data_key + ) + def test_serialize_header_v1(self): """Validate that the _serialize_header function behaves as expected. @@ -167,6 +304,34 @@ def test_serialize_header_auth_v1_no_signer(self): data_encryption_key=VALUES["data_key_obj"], ) + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v1_THEN_aad_has_required_ec_bytes( + self, + mock_header_auth_iv, + ): + """Validate that the _create_header_auth function + behaves as expected for SerializationVersion.V1 + when required_ec_bytes are provided. + """ + self.mock_encrypt.return_value = VALUES["header_auth_base"] + test = aws_encryption_sdk.internal.formatting.serialize.serialize_header_auth( + version=SerializationVersion.V1, + algorithm=self.mock_algorithm, + header=VALUES["serialized_header"], + data_encryption_key=sentinel.encryption_key, + signer=self.mock_signer, + required_ec_bytes=self.mock_required_ec_bytes, + ) + self.mock_encrypt.assert_called_once_with( + algorithm=self.mock_algorithm, + key=sentinel.encryption_key, + plaintext=b"", + associated_data=VALUES["serialized_header"] + self.mock_required_ec_bytes, + iv=mock_header_auth_iv.return_value, + ) + self.mock_signer.update.assert_called_once_with(VALUES["serialized_header_auth"]) + assert test == VALUES["serialized_header_auth"] + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") def test_serialize_header_auth_v2(self, mock_header_auth_iv): """Validate that the _create_header_auth function @@ -203,6 +368,33 @@ def test_serialize_header_auth_v2_no_signer(self): data_encryption_key=VALUES["data_key_obj"], ) + @patch("aws_encryption_sdk.internal.formatting.serialize.header_auth_iv") + def test_GIVEN_required_ec_bytes_WHEN_serialize_header_auth_v2_THEN_aad_has_required_ec_bytes( + self, + mock_header_auth_iv, + ): + """Validate that the _create_header_auth function + behaves as expected for SerializationVersion.V2. + """ + self.mock_encrypt.return_value = VALUES["header_auth_base"] + test = aws_encryption_sdk.internal.formatting.serialize.serialize_header_auth( + version=SerializationVersion.V2, + algorithm=self.mock_algorithm, + header=VALUES["serialized_header_v2_committing"], + data_encryption_key=sentinel.encryption_key, + signer=self.mock_signer, + required_ec_bytes=self.mock_required_ec_bytes, + ) + self.mock_encrypt.assert_called_once_with( + algorithm=self.mock_algorithm, + key=sentinel.encryption_key, + plaintext=b"", + associated_data=VALUES["serialized_header_v2_committing"] + self.mock_required_ec_bytes, + iv=mock_header_auth_iv.return_value, + ) + self.mock_signer.update.assert_called_once_with(VALUES["serialized_header_auth_v2"]) + assert test == VALUES["serialized_header_auth_v2"] + def test_serialize_non_framed_open(self): """Validate that the serialize_non_framed_open function behaves as expected. diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py index 426f8f85f..d0ecefcf4 100644 --- a/test/unit/test_streaming_client_configs.py +++ b/test/unit/test_streaming_client_configs.py @@ -1,20 +1,11 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite to validate aws_encryption_sdk.streaming_client config classes.""" import io import pytest import six +from mock import MagicMock, patch from aws_encryption_sdk import CommitmentPolicy from aws_encryption_sdk.internal.defaults import ALGORITHM, FRAME_LENGTH, LINE_LENGTH @@ -28,6 +19,18 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_cryptographic_material_providers.mpl.references import ICryptographicMaterialsManager, IKeyring + HAS_MPL = True + + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL +except ImportError: + HAS_MPL = False + + class FakeCryptoMaterialsManager(CryptoMaterialsManager): def get_encryption_materials(self, request): return @@ -44,6 +47,15 @@ def _new_master_key(self, key_id): return +if HAS_MPL: + class FakeKeyring(IKeyring): + def on_encrypt(self, param): + return + + def on_decrypt(self, param): + return + + BASE_KWARGS = dict( source=b"", materials_manager=FakeCryptoMaterialsManager(), @@ -126,6 +138,18 @@ def test_client_config_defaults(): assert test.max_encrypted_data_keys is None +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_client_config_with_mpl_attr(): + test = _ClientConfig(**BASE_KWARGS) + assert hasattr(test, "keyring") + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_client_config_no_mpl(): + test = _ClientConfig(**BASE_KWARGS) + assert not hasattr(test, "keyring") + + def test_encryptor_config_defaults(): test = EncryptorConfig(**BASE_KWARGS) assert test.encryption_context == {} @@ -154,3 +178,111 @@ def test_client_config_converts(kwargs, stream_type): assert isinstance(test.source, stream_type) if test.key_provider is not None: assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + + +# Given: no MPL +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +@patch.object(_ClientConfig, "_no_mpl_attrs_post_init") +def test_GIVEN_no_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( + mock_no_mpl_attrs_post_init, +): + # When: attrs_post_init + _ClientConfig(**BASE_KWARGS) + # Then: calls _no_mpl_attrs_post_init + mock_no_mpl_attrs_post_init.assert_called_once_with() + + +# Given: has MPL +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@patch.object(_ClientConfig, "_has_mpl_attrs_post_init") +def test_GIVEN_has_mpl_WHEN_attrs_post_init_THEN_calls_no_mpl_method( + mock_has_mpl_attrs_post_init, +): + # When: attrs_post_init + _ClientConfig(**BASE_KWARGS) + # Then: calls _has_mpl_attrs_post_init + mock_has_mpl_attrs_post_init.assert_called_once_with() + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +@pytest.mark.parametrize( + "kwargs", + ( + (dict(source=b"", materials_manager=FakeCryptoMaterialsManager())), + (dict(source=b"", key_provider=FakeMasterKeyProvider())), + (dict(source="", materials_manager=FakeCryptoMaterialsManager())), + (dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager())), + (dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager())), + ), +) +def test_client_configs_with_mpl( + kwargs, +): + kwargs["commitment_policy"] = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + + test = _ClientConfig(**kwargs) + + # In all cases, config should have a materials manager + assert test.materials_manager is not None + + # If materials manager was provided, it should be directly used + if "materials_manager" in kwargs: + assert kwargs["materials_manager"] == test.materials_manager + + # If native key_provider was provided, it should be wrapped in native materials manager + elif "key_provider" in kwargs: + assert test.key_provider is not None + assert test.key_provider == kwargs["key_provider"] + assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) + + # If MPL keyring was provided, it should be wrapped in MPL materials manager + elif "keyring" in kwargs: + assert test.keyring is not None + assert test.keyring == kwargs["keyring"] + assert isinstance(test.keyring, IKeyring) + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + + else: + raise ValueError(f"Test did not find materials_manager or key_provider. {kwargs}") + + +# This is an addition to test_client_configs_with_mpl; +# This needs its own test; pytest's parametrize cannot use a conditionally-loaded type (IKeyring) +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_keyring_client_config_with_mpl( +): + kwargs = { + "source": b"", + "keyring": FakeKeyring(), + "commitment_policy": CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + } + + test = _ClientConfig(**kwargs) + + assert test.materials_manager is not None + + assert test.keyring is not None + assert test.keyring == kwargs["keyring"] + assert isinstance(test.keyring, IKeyring) + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + + +# This is an addition to test_client_configs_with_mpl; +# This needs its own test; pytest's parametrize cannot use a conditionally-loaded type (MPL CMM) +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_mpl_cmm_client_config_with_mpl( +): + mock_mpl_cmm = MagicMock(__class__=ICryptographicMaterialsManager) + kwargs = { + "source": b"", + "materials_manager": mock_mpl_cmm, + "commitment_policy": CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT + } + + test = _ClientConfig(**kwargs) + + assert test.materials_manager is not None + # Assert that the MPL CMM is wrapped in the native interface + assert isinstance(test.materials_manager, CryptoMaterialsManagerFromMPL) + # Assert the MPL CMM is used by the native interface + assert test.materials_manager.mpl_cmm == mock_mpl_cmm diff --git a/test/unit/test_streaming_client_encryption_stream.py b/test/unit/test_streaming_client_encryption_stream.py index c54455654..30497d0c8 100644 --- a/test/unit/test_streaming_client_encryption_stream.py +++ b/test/unit/test_streaming_client_encryption_stream.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.streaming_client._EncryptionStream""" import copy import io diff --git a/test/unit/test_streaming_client_mpl_import.py b/test/unit/test_streaming_client_mpl_import.py new file mode 100644 index 000000000..0dcd8a981 --- /dev/null +++ b/test/unit/test_streaming_client_mpl_import.py @@ -0,0 +1,37 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Unit test suite to validate aws_encryption_sdk.streaming_client MPL import logic.""" + +import pytest + +import aws_encryption_sdk.streaming_client + +pytestmark = [pytest.mark.unit, pytest.mark.local] + + +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + import aws_cryptographic_material_providers # noqa pylint: disable=unused-import + HAS_MPL = True +except ImportError: + HAS_MPL = False + + +@pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") +def test_GIVEN_test_has_mpl_is_True_THEN_streaming_client_has_mpl_is_True(): + """If the MPL IS installed in the runtime environment, + assert the streaming client has _HAS_MPL set to True""" + + assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") + assert aws_encryption_sdk.streaming_client._HAS_MPL is True + + +@pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") +def test_GIVEN_test_has_mpl_is_False_THEN_streaming_client_has_mpl_is_False(): + """If the MPL IS NOT installed in the runtime environment, + assert the streaming client has _HAS_MPL set to False""" + + assert hasattr(aws_encryption_sdk.streaming_client, "_HAS_MPL") + assert aws_encryption_sdk.streaming_client._HAS_MPL is False diff --git a/test/unit/test_streaming_client_stream_decryptor.py b/test/unit/test_streaming_client_stream_decryptor.py index 157755094..adcb7215b 100644 --- a/test/unit/test_streaming_client_stream_decryptor.py +++ b/test/unit/test_streaming_client_stream_decryptor.py @@ -1,16 +1,7 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.streaming_client.StreamDecryptor""" +# noqa pylint: disable=too-many-lines import io import pytest @@ -33,14 +24,31 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL + HAS_MPL = True + +except ImportError: + HAS_MPL = False + + class TestStreamDecryptor(object): @pytest.fixture(autouse=True) def apply_fixtures(self): self.mock_key_provider = MagicMock(__class__=MasterKeyProvider) self.mock_materials_manager = MagicMock(__class__=CryptoMaterialsManager) - self.mock_materials_manager.decrypt_materials.return_value = MagicMock( + self.mock_decrypt_materials = MagicMock( data_key=VALUES["data_key_obj"], verification_key=sentinel.verification_key ) + self.mock_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials + + if HAS_MPL: + self.mock_mpl_materials_manager = MagicMock(__class__=CryptoMaterialsManagerFromMPL) + self.mock_mpl_materials_manager.decrypt_materials.return_value = self.mock_decrypt_materials + self.mock_header = MagicMock() self.mock_header.version = SerializationVersion.V1 self.mock_header.algorithm = MagicMock( @@ -176,6 +184,10 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, test_decryptor.source_stream = ct_stream test_decryptor._stream_length = len(VALUES["data_128"]) + # Mock: hasattr(self.config, "encryption_context") returns False + if hasattr(test_decryptor.config, "encryption_context"): + del test_decryptor.config.encryption_context + test_header, test_header_auth = test_decryptor._read_header() self.mock_deserialize_header.assert_called_once_with(ct_stream, None) @@ -213,6 +225,136 @@ def test_read_header(self, mock_derive_datakey, mock_decrypt_materials_request, assert test_header is self.mock_header assert test_header_auth is sentinel.header_auth + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: no MPL + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_verification_key_AND_no_mpl_WHEN_read_header_THEN_calls_from_key_bytes( + self, + mock_verifier, + *_, + ): + # Given: verification key + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + # When: read header + test_decryptor._read_header() + + # Then: calls from_key_bytes + mock_verifier.from_key_bytes.assert_called_once_with( + algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key + ) + + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_verification_key_AND_has_mpl_AND_not_MPLCMM_WHEN_read_header_THEN_calls_from_key_bytes( + self, + mock_verifier, + *_, + ): + # Given: verification key + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + # Given: native CMM + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + # When: read_header + test_decryptor._read_header() + + # Then: calls from_key_bytess + mock_verifier.from_key_bytes.assert_called_once_with( + algorithm=self.mock_header.algorithm, key_bytes=sentinel.verification_key + ) + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: no MPL + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_decrypt_config_has_ec_AND_no_mpl_WHEN_read_header_THEN_raise_TypeError( + self, + mock_verifier, + mock_decrypt_materials_request, + *_, + ): + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + # Given: self.config has "encryption_context" + # (i.e. encryption context provided on decrypt) + any_reproduced_ec = {"some": "ec"} + test_decryptor.config.encryption_context = any_reproduced_ec + + # Then: raise TypeError + with pytest.raises(TypeError): + # When: read header + test_decryptor._read_header() + + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.Verifier") + @patch("base64.b64encode") + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_verification_key_AND_has_mpl_AND_has_MPLCMM_WHEN_read_header_THEN_calls_from_encoded_point( + self, + mock_b64encoding, + mock_verifier, + *_, + ): + # Given: Verification key + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + ct_stream = io.BytesIO(VALUES["data_128"]) + mock_commitment_policy = MagicMock(__class__=CommitmentPolicy) + test_decryptor = StreamDecryptor( + # Given: MPL CMM + materials_manager=self.mock_mpl_materials_manager, + source=ct_stream, + commitment_policy=mock_commitment_policy, + ) + test_decryptor.source_stream = ct_stream + test_decryptor._stream_length = len(VALUES["data_128"]) + + # When: read header + test_decryptor._read_header() + + # Then: calls from_encoded_point + mock_verifier.from_encoded_point.assert_called_once_with( + algorithm=self.mock_header.algorithm, encoded_point=mock_b64encoding() + ) + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") def test_read_header_frame_too_large(self, mock_derive_datakey): self.mock_header.content_type = ContentType.FRAMED_DATA @@ -768,3 +910,219 @@ def test_close_no_footer(self, mock_close): with pytest.raises(SerializationError) as excinfo: test_decryptor.close() excinfo.match("Footer not read") + + @patch("aws_encryption_sdk.streaming_client.validate_header") + def test_GIVEN_does_not_have_required_EC_WHEN_validate_parsed_header_THEN_validate_header( + self, + mock_validate_header + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + test_decryptor._derived_data_key = sentinel.derived_data_key + # Given: test_decryptor does not have _required_encryption_context attribute + # When: _validate_parsed_header + test_decryptor._validate_parsed_header( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + ) + # Then: validate_header + mock_validate_header.assert_called_once_with( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header, + data_key=sentinel.derived_data_key, + ) + + @patch("aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context") + @patch("aws_encryption_sdk.streaming_client.validate_header") + def test_GIVEN_has_required_EC_WHEN_validate_parsed_header_THEN_validate_header_with_serialized_required_EC( + self, + mock_validate_header, + mock_serialize_encryption_context, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + test_decryptor._derived_data_key = sentinel.derived_data_key + # Given: test_decryptor has _required_encryption_context attribute + mock_required_ec = MagicMock(__class__=dict) + test_decryptor._required_encryption_context = mock_required_ec + mock_serialized_required_ec = MagicMock(__class__=bytes) + mock_serialize_encryption_context.return_value = mock_serialized_required_ec + # When: _validate_parsed_header + test_decryptor._validate_parsed_header( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + ) + # Then: call validate_header with serialized required EC + mock_validate_header.assert_called_once_with( + header=self.mock_header, + header_auth=sentinel.header_auth, + raw_header=self.mock_raw_header + mock_serialized_required_ec, + data_key=sentinel.derived_data_key, + ) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_MPL_AND_config_has_EC_WHEN_create_decrypt_materials_request_THEN_provide_reproduced_EC( + self, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_mpl_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: StreamDecryptor.config has encryption_context attribute + mock_reproduced_encryption_context = MagicMock(__class__=dict) + test_decryptor.config.encryption_context = mock_reproduced_encryption_context + # Type checking on header encryption context seems to require concrete instance, + # neither MagicMock nor sentinel value work + self.mock_header.encryption_context = {"some_key_to_pass_type_validation": "some_value"} + + # When: _create_decrypt_materials_request + output = test_decryptor._create_decrypt_materials_request( + header=self.mock_header, + ) + + # Then: decrypt_materials_request has reproduced_encryption_context attribute + assert hasattr(output, "reproduced_encryption_context") + assert output.reproduced_encryption_context == mock_reproduced_encryption_context + + def test_GIVEN_config_does_not_have_EC_WHEN_create_decrypt_materials_request_THEN_request_does_not_have_reproduced_EC( # noqa pylint: disable=line-too-long + self, + ): + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: StreamDecryptor.config does not have an encryption_context attribute + del test_decryptor.config.encryption_context + # Type checking on header encryption context seems to require concrete instance, + # neither MagicMock nor sentinel value work + self.mock_header.encryption_context = {"some_key_to_pass_type_validation": "some_value"} + + # When: _create_decrypt_materials_request + output = test_decryptor._create_decrypt_materials_request( + header=self.mock_header, + ) + + # Then: decrypt_materials_request.reproduced_encryption_context is None + assert output.reproduced_encryption_context is None + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_materials_has_no_required_encryption_context_keys_attr_WHEN_read_header_THEN_required_EC_is_None( + self, + mock_verifier, + *_ + ): + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # Given: decryption_materials does not have a required_encryption_context_keys attribute + del self.mock_decrypt_materials.required_encryption_context_keys + + # When: _read_header + test_decryptor._read_header() + + # Then: StreamDecryptor._required_encryption_context is None + assert test_decryptor._required_encryption_context is None + + @patch("aws_encryption_sdk.streaming_client.derive_data_encryption_key") + @patch("aws_encryption_sdk.streaming_client.DecryptionMaterialsRequest") + @patch("aws_encryption_sdk.streaming_client.Verifier") + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_materials_has_required_encryption_context_keys_attr_WHEN_read_header_THEN_creates_correct_required_EC( # noqa pylint: disable=line-too-long + self, + mock_verifier, + *_ + ): + required_encryption_context_keys_values = [ + # Case of empty encryption context list is not allowed; + # if a list is provided, it must be non-empty. + # The MPL enforces this behavior on construction. + ["one_key"], + ["one_key", "two_key"], + ["one_key", "two_key", "red_key"], + ["one_key", "two_key", "red_key", "blue_key"], + ] + + encryption_context_values = [ + {}, + {"one_key": "some_value"}, + { + "one_key": "some_value", + "two_key": "some_other_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + "blue_key": "some_blue_value", + } + ] + + for required_encryption_context_keys in required_encryption_context_keys_values: + + # Given: decryption_materials has required_encryption_context_keys + self.mock_decrypt_materials.required_encryption_context_keys = \ + required_encryption_context_keys + + for encryption_context in encryption_context_values: + + self.mock_decrypt_materials.encryption_context = encryption_context + + mock_verifier_instance = MagicMock() + mock_verifier.from_key_bytes.return_value = mock_verifier_instance + + self.mock_header.content_type = ContentType.FRAMED_DATA + test_decryptor = StreamDecryptor( + materials_manager=self.mock_materials_manager, + source=self.mock_input_stream, + commitment_policy=self.mock_commitment_policy, + ) + + # When: _read_header + test_decryptor._read_header() + + # Then: Assert correctness of partitioned EC + for k, v in encryption_context.items(): + # If a key is in required_encryption_context_keys, then ... + if k in required_encryption_context_keys: + # ... its EC is in the StreamEncryptor._required_encryption_context + assert k in test_decryptor._required_encryption_context + assert test_decryptor._required_encryption_context[k] == v + # If a key is NOT in required_encryption_context_keys, then ... + else: + # ... its EC is NOT in the StreamEncryptor._required_encryption_context + assert k not in test_decryptor._required_encryption_context diff --git a/test/unit/test_streaming_client_stream_encryptor.py b/test/unit/test_streaming_client_stream_encryptor.py index 5bfd0c903..283f60fb0 100644 --- a/test/unit/test_streaming_client_stream_encryptor.py +++ b/test/unit/test_streaming_client_stream_encryptor.py @@ -1,20 +1,12 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.streaming_client.StreamEncryptor""" +# noqa pylint: disable=too-many-lines import io import pytest import six +from cryptography.hazmat.primitives import serialization from mock import MagicMock, call, patch, sentinel import aws_encryption_sdk.internal.defaults @@ -37,6 +29,17 @@ pytestmark = [pytest.mark.unit, pytest.mark.local] +# Check if MPL is installed, and skip tests based on its installation status +# Ideally, this logic would be based on mocking imports and testing logic, +# but doing that introduces errors that cause other tests to fail. +try: + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL + HAS_MPL = True + +except ImportError: + HAS_MPL = False + + class TestStreamEncryptor(object): @pytest.fixture(autouse=True) def apply_fixtures(self): @@ -60,6 +63,10 @@ def apply_fixtures(self): self.mock_master_keys_set, ) + if HAS_MPL: + self.mock_mpl_materials_manager = MagicMock(__class__=CryptoMaterialsManagerFromMPL) + self.mock_mpl_materials_manager.get_encryption_materials.return_value = self.mock_encryption_materials + self.mock_master_key = MagicMock(__class__=MasterKey) self.mock_frame_length = MagicMock(__class__=int) @@ -366,6 +373,188 @@ def test_prep_message_non_framed_message(self, mock_write_header, mock_prep_non_ test_encryptor._prep_message() mock_prep_non_framed.assert_called_once_with() + # Given: no MPL + @pytest.mark.skipif(HAS_MPL, reason="Test should only be executed without MPL in installation") + def test_GIVEN_no_mpl_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep message + test_encryptor._prep_message() + # Then: calls from_key_bytes with default encoding + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key + ) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_not_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_default_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + # Given: native CMM + materials_manager=self.mock_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep_message + test_encryptor._prep_message() + # Then: calls from_key_bytes with default encoding + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key + ) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_has_MPLCMM_AND_uses_signer_WHEN_prep_message_THEN_signer_uses_PEM_encoding(self): + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + # Given: MPL CMM + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + with patch.object(self.mock_signer, "from_key_bytes"): + # When: prep_message + test_encryptor._prep_message() + self.mock_signer.from_key_bytes.assert_called_once_with( + algorithm=self.mock_encryption_materials.algorithm, + key_bytes=self.mock_encryption_materials.signing_key, + # Then: calls from_key_bytes with PEM encoding + encoding=serialization.Encoding.PEM + ) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_encryption_materials_has_required_EC_keys_WHEN_prep_message_THEN_paritions_stored_and_required_EC( # noqa pylint: disable=line-too-long + self + ): + # Create explicit values to explicitly test logic in smaller cases + required_encryption_context_keys_values = [ + # Case of empty encryption context list is not allowed; + # if a list is provided, it must be non-empty. + # The MPL enforces this behavior on construction. + ["one_key"], + ["one_key", "two_key"], + ["one_key", "two_key", "red_key"], + ["one_key", "two_key", "red_key", "blue_key"], + ] + + encryption_context_values = [ + {}, + {"one_key": "some_value"}, + { + "one_key": "some_value", + "two_key": "some_other_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + }, + { + "one_key": "some_value", + "two_key": "some_other_value", + "red_key": "some_red_value", + "blue_key": "some_blue_value", + } + ] + + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + + for required_encryption_context_keys in required_encryption_context_keys_values: + + # Given: encryption context has required_encryption_context_keys + self.mock_encryption_materials.required_encryption_context_keys = \ + required_encryption_context_keys + + for encryption_context in encryption_context_values: + self.mock_encryption_materials.encryption_context = encryption_context + + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + # When: prep_message + test_encryptor._prep_message() + + # Then: Assert correctness of partitioned EC + for k, v in encryption_context.items(): + # If a key is in required_encryption_context_keys, then + if k in required_encryption_context_keys: + # 1) Its EC is in the StreamEncryptor._required_encryption_context + assert k in test_encryptor._required_encryption_context + assert test_encryptor._required_encryption_context[k] == v + # 2) Its EC is NOT in the StreamEncryptor._stored_encryption_context + assert k not in test_encryptor._stored_encryption_context + # If a key is NOT in required_encryption_context_keys, then + else: + # 1) Its EC is NOT in the StreamEncryptor._required_encryption_context + assert k not in test_encryptor._required_encryption_context + # 2) Its EC is in the StreamEncryptor._stored_encryption_context + assert k in test_encryptor._stored_encryption_context + assert test_encryptor._stored_encryption_context[k] == v + + # Assert size(stored_EC) + size(required_EC) == size(EC) + # (i.e. every EC was sorted into one or the other) + assert len(test_encryptor._required_encryption_context) \ + + len(test_encryptor._stored_encryption_context) \ + == len(encryption_context) + + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_encryption_materials_does_not_have_required_EC_keys_WHEN_prep_message_THEN_stored_EC_is_EC( # noqa pylint: disable=line-too-long + self + ): + + self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 + + mock_encryption_context = MagicMock(__class__=dict) + self.mock_encryption_materials.encryption_context = mock_encryption_context + # Given: encryption materials does not have required encryption context keys + # (MagicMock default is to "make up" "Some" value here; this deletes that value) + del self.mock_encryption_materials.required_encryption_context_keys + + test_encryptor = StreamEncryptor( + source=VALUES["data_128"], + materials_manager=self.mock_mpl_materials_manager, + frame_length=self.mock_frame_length, + algorithm=Algorithm.AES_128_GCM_IV12_TAG16, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.content_type = ContentType.FRAMED_DATA + # When: prep_message + test_encryptor._prep_message() + + # Then: _stored_encryption_context is the provided encryption_context + assert test_encryptor._stored_encryption_context == mock_encryption_context + # Then: _required_encryption_context is None + assert test_encryptor._required_encryption_context is None + def test_prep_message_no_signer(self): self.mock_encryption_materials.algorithm = Algorithm.AES_128_GCM_IV12_TAG16 test_encryptor = StreamEncryptor( @@ -490,6 +679,53 @@ def test_write_header(self): ) assert test_encryptor.output_buffer == b"1234567890" + @patch("aws_encryption_sdk.internal.formatting.encryption_context.serialize_encryption_context") + # Given: has MPL + @pytest.mark.skipif(not HAS_MPL, reason="Test should only be executed with MPL in installation") + def test_GIVEN_has_mpl_AND_has_required_EC_WHEN_write_header_THEN_adds_serialized_required_ec_to_header_auth( + self, + serialize_encryption_context + ): + self.mock_serialize_header.return_value = b"12345" + self.mock_serialize_header_auth.return_value = b"67890" + pt_stream = io.BytesIO(self.plaintext) + test_encryptor = StreamEncryptor( + source=pt_stream, + materials_manager=self.mock_materials_manager, + algorithm=aws_encryption_sdk.internal.defaults.ALGORITHM, + frame_length=self.mock_frame_length, + commitment_policy=self.mock_commitment_policy, + signature_policy=self.mock_signature_policy, + ) + test_encryptor.signer = sentinel.signer + test_encryptor.content_type = sentinel.content_type + test_encryptor._header = sentinel.header + sentinel.header.version = SerializationVersion.V1 + test_encryptor.output_buffer = b"" + test_encryptor._encryption_materials = self.mock_encryption_materials + test_encryptor._derived_data_key = sentinel.derived_data_key + + # Given: StreamEncryptor has _required_encryption_context + mock_required_ec = MagicMock(__class__=dict) + test_encryptor._required_encryption_context = mock_required_ec + mock_serialized_required_ec = MagicMock(__class__=bytes) + serialize_encryption_context.return_value = mock_serialized_required_ec + + # When: _write_header() + test_encryptor._write_header() + + self.mock_serialize_header.assert_called_once_with(header=test_encryptor._header, signer=sentinel.signer) + self.mock_serialize_header_auth.assert_called_once_with( + version=sentinel.header.version, + algorithm=self.mock_encryption_materials.algorithm, + header=b"12345", + data_encryption_key=sentinel.derived_data_key, + signer=sentinel.signer, + # Then: Pass serialized required EC to serialize_header_auth + required_ec_bytes=mock_serialized_required_ec, + ) + assert test_encryptor.output_buffer == b"1234567890" + @patch("aws_encryption_sdk.streaming_client.non_framed_body_iv") def test_prep_non_framed(self, mock_non_framed_iv): self.mock_serialize_non_framed_open.return_value = b"1234567890" diff --git a/test/unit/test_structures.py b/test/unit/test_structures.py index 416777505..28db3eaf2 100644 --- a/test/unit/test_structures.py +++ b/test/unit/test_structures.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.structures""" import pytest diff --git a/test/unit/test_util_str_ops.py b/test/unit/test_util_str_ops.py index 5a6ef2546..b43fcda4b 100644 --- a/test/unit/test_util_str_ops.py +++ b/test/unit/test_util_str_ops.py @@ -1,16 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.internal.str_ops""" import codecs diff --git a/test/unit/test_util_streams.py b/test/unit/test_util_streams.py index ab7b05152..147f60c65 100644 --- a/test/unit/test_util_streams.py +++ b/test/unit/test_util_streams.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Unit test suite for aws_encryption_sdk.internal.utils.streams""" import io diff --git a/test/unit/test_utils.py b/test/unit/test_utils.py index c6d565108..82ee317f1 100644 --- a/test/unit/test_utils.py +++ b/test/unit/test_utils.py @@ -1,16 +1,6 @@ # coding: utf-8 -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test suite for aws_encryption_sdk.internal.utils""" import io @@ -265,3 +255,28 @@ def test_source_data_key_length_check_invalid(self): source_data_key=mock_data_key, algorithm=mock_algorithm ) excinfo.match("Invalid Source Data Key length 4 for algorithm required: 5") + + def test_exactly_one_arg_is_not_none(self): + # No args => no args are not None + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none() is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None" + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", "also not None" + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", None + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + "not None", "also not None" + ) is False + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None, "not None" + ) is True + assert aws_encryption_sdk.internal.utils.exactly_one_arg_is_not_none( + None, None + ) is False diff --git a/test/unit/test_values.py b/test/unit/test_values.py index cb48c8c74..6e5883127 100644 --- a/test/unit/test_values.py +++ b/test/unit/test_values.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Values to use in various unit test suites.""" import copy import struct @@ -240,6 +230,49 @@ def array_byte(source): VALUES["final_frame_base"].tag, ] ) +# This is a valid frame from a ESDK-.NET-encrypted message. +# ESDK Python versions before v4.0.0 would raise a SerializationError when deserializing this frame +# because its frame length (512; the b"\x00\x00\x02\x00" string) +# equals the configured frame length. +# In other ESDK implementations, the final frame length would never equal the frame length +# because they would append an empty final frame. +# Both are valid implementations of the ESDK specification, +# and the ESDK-Python must support this case. +VALUES["serialized_final_frame_512_length"] = b"".join( + [ + b"\xff\xff\xff\xff", + b"\x00\x00\x00\x14", + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14', + b"\x00\x00\x02\x00", + b'''\x87r+k7 \xc7\xc3\xbf)T.8,}\xc5a.H]\x16/08k2 + )\xb5QB\xccP\xc2\xc6\xeanf\x06Z7\xbb\xcd\x87L\xa6 + ~~\xdc\xab~\x0e\xf6\x05\n\xa9\x94X[\xb8En?x$\x11 + \x10\x84g0i\xeai\xf9\x8c\xe6}\xc3\xa1Gig\xbdA\x1an + \x1b\x9d\xf1\rW\xc8\xad|\x04hSt\x10\xc7\x0e\'\x8f + \xe8\x94\x9d\xdb\x82\xdb"\x95\xbc\xf5\xc5\xd0\xddQ + \xba\xaa\xbf6\x1e\xd8\xffB\xed\xee\xda1\x15\xf6=x + \xe14\xe7\xf5\xb7t\x10\x11\xa4!,!\xfa\xc7\xf1\t\xf7 + \xc3X?eI\xcdk\xf3\xb5\x80b\xdd;*\xe9\x9c\xd5\x83[\xc4c + \xe4[mA\x87\xd9\x94g\xd6\\<\xd1\xff\xcc<\xef\xe2\xbc\xda> + \xda|\xa1L\xd1\xf4u\x07Y\x13\xa3\xd4\x15\x1fS\x98\x00^ + \x1d^\xcdu\x17\xc8.\xfb\x9d\xaaU\xbf\x8f\xa96%YPX\xe6 + \xf5\\\x141\xe5\xdd\x9a,\xc7d\xca\xffQ\x02:\xd87s:\x9a + \xdf\xd5\'\xf0!\x13\xafuU\xf7w\x15\xbd\xecS \xf2h\xa4 + \xdd\xfb9\xbb\xb3\xd7?3\xc0\xeed\x0e\x17\x1b\xccN\xf9)s + \xd1\x97\x84\xb6\xce5\xca\x9b\xde\xa9\x0e$>x\xd9\x9cD= + \xd5\xa3\xa1qb#\x8c\xc1\x81Nv\x8dA0\'{~\x1c\xf1?\n\x7fAX\x9f, + \xe1\xe6d\xc5\xed\x9e\xa9o\x1bpp\xac\x1b\x03P\xd8\xae\xd6\xf6 + \xaca;N\xd6C\x08\x99!\x0bU8\x85(g\xe6\x8fD\xf7\x19\xb0]4 + \x19hB\x15\xa7\xee\xd8\xc0\xe9D\x850\xb6\x05\xd1\xa3`%\xcb + \xfb\x88&"\xdfnm\xa6\xf1X\xc4\x84\x1c\xc3\xe8]\x05mh$\xff]= + \xab\xa2p\x8e\x82:U\xef\xf3\x86X\xe16\x1f\xc7\x7f\x8dv\x1a + \xe4\r5\x8a\xea\x90\xb2\x1cA(\x9b\xedyT0\xd4h\tJ\xa4<\x07C9 + \xa3a]\x7f\x17Ak\x1d\xb9gA\x04\xbaq\xe5(y-\xc4!\x87\xa83 + \xdd\xf3\xea\xa7\x12X\xb6l\x98\xdf,\xc8\xe6\x9f7\xb0\xcd + \xb3\x9a\xf4\xe7a"H\xd9L\xd7.\x0f\x7f1W''', + b'XK#8\xb3\xab\x07\x11\x94\xf7\xac\xea\xd0g\x9b#', + ] +) VALUES["serialized_final_frame_bad_length"] = b"".join( [ b"\xff\xff\xff\xff", @@ -371,6 +404,25 @@ def array_byte(source): header_iv_length=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384.iv_len, frame_length=2 ** 16, ) +VALUES["deserialized_header_frame_512_frame"] = MessageHeader( + version=SerializationVersion.V1, + type=ObjectType.CUSTOMER_AE_DATA, + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + message_id=VALUES["message_id"], + encryption_context=VALUES["updated_encryption_context"], + encrypted_data_keys=set( + [ + EncryptedDataKey( + key_provider=VALUES["data_keys"][0].key_provider, + encrypted_data_key=VALUES["data_keys"][0].encrypted_data_key, + ) + ] + ), + content_type=ContentType.FRAMED_DATA, + content_aad_length=0, + header_iv_length=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384.iv_len, + frame_length=512, +) VALUES["deserialized_header_small_frame"] = MessageHeader( version=SerializationVersion.V1, type=ObjectType.CUSTOMER_AE_DATA, diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py index e43240c8a..ecff483ef 100644 --- a/test/unit/unit_test_utils.py +++ b/test/unit/unit_test_utils.py @@ -1,15 +1,5 @@ -# Copyright 2017 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Utility functions to handle common test framework functions.""" import copy import io diff --git a/test/upstream-requirements-py27.txt b/test/upstream-requirements-py27.txt deleted file mode 100644 index a5a48c9ab..000000000 --- a/test/upstream-requirements-py27.txt +++ /dev/null @@ -1,36 +0,0 @@ -atomicwrites==1.4.0 -attrs==21.2.0 -backports.functools-lru-cache==1.6.4 -boto3==1.17.92 -botocore==1.20.92 -cffi==1.14.5 -configparser==4.0.2 -contextlib2==0.6.0.post1 -coverage==5.5 -cryptography==3.3.2 -enum34==1.1.10 -funcsigs==1.0.2 -futures==3.3.0 -importlib-metadata==2.1.1 -ipaddress==1.0.23 -jmespath==0.10.0 -mock==3.0.5 -more-itertools==5.0.0 -packaging==20.9 -pathlib2==2.3.5 -pluggy==0.13.1 -py==1.10.0 -pycparser==2.20 -pyparsing==2.4.7 -pytest==4.6.11 -pytest-cov==2.12.1 -pytest-mock==2.0.0 -python-dateutil==2.8.1 -s3transfer==0.4.2 -scandir==1.10.0 -six==1.16.0 -toml==0.10.2 -urllib3==1.26.5 -wcwidth==0.2.5 -wrapt==1.12.1 -zipp==1.2.0 diff --git a/test/upstream-requirements-py311.txt b/test/upstream-requirements-py311.txt new file mode 100644 index 000000000..16ae4654a --- /dev/null +++ b/test/upstream-requirements-py311.txt @@ -0,0 +1,20 @@ +attrs==22.2.0 +boto3==1.26.54 +botocore==1.29.54 +cffi==1.15.1 +coverage==7.0.5 +cryptography==42.0.4 +iniconfig==2.0.0 +jmespath==1.0.1 +mock==4.0.3 +packaging==23.0 +pluggy==1.0.0 +pycparser==2.21 +pytest==7.2.0 +pytest-cov==3.0.0 +pytest-mock==3.6.1 +python-dateutil==2.8.2 +s3transfer==0.6.0 +six==1.16.0 +urllib3==1.26.18 +wrapt==1.14.1 diff --git a/test/upstream-requirements-py37.txt b/test/upstream-requirements-py37.txt deleted file mode 100644 index a4603ddf8..000000000 --- a/test/upstream-requirements-py37.txt +++ /dev/null @@ -1,26 +0,0 @@ -attrs==21.2.0 -boto3==1.17.92 -botocore==1.20.92 -cffi==1.14.5 -coverage==5.5 -cryptography==3.4.7 -importlib-metadata==4.5.0 -iniconfig==1.1.1 -jmespath==0.10.0 -mock==4.0.3 -packaging==20.9 -pluggy==0.13.1 -py==1.10.0 -pycparser==2.20 -pyparsing==2.4.7 -pytest==6.2.4 -pytest-cov==2.12.1 -pytest-mock==3.6.1 -python-dateutil==2.8.1 -s3transfer==0.4.2 -six==1.16.0 -toml==0.10.2 -typing-extensions==3.10.0.0 -urllib3==1.26.5 -wrapt==1.12.1 -zipp==3.4.1 diff --git a/test/upstream.md b/test/upstream.md new file mode 100644 index 000000000..aeb3ed638 --- /dev/null +++ b/test/upstream.md @@ -0,0 +1,7 @@ +AWS Crypto Tools maintains `test/upstream-requirements-py.txt` in our Python products such that +our Cryptographic Primitive Provider for Python ([pyca/cryptography](https://github.com/pyca/cryptography)) +may execute downstream tests against AWS Crypto Tools Python products. +These files allow pyca to install and test the Crypto Tools products. +Additionally, Crypto Tools should maintain a test configuration that can be completed without using any AWS resources. +If Crypto Tools needs to contact pyca about this expectation, +they should cut a issue to the pyca/cryptography repo. diff --git a/test_vector_handlers/LICENSE b/test_vector_handlers/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/test_vector_handlers/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/test_vector_handlers/MANIFEST.in b/test_vector_handlers/MANIFEST.in index 132bd9406..71b01728a 100644 --- a/test_vector_handlers/MANIFEST.in +++ b/test_vector_handlers/MANIFEST.in @@ -1,9 +1,5 @@ include README.rst -include CHANGELOG.rst -include CONTRIBUTING.rst include LICENSE include requirements.txt -recursive-include doc * recursive-include test *.py -recursive-include examples *.py \ No newline at end of file diff --git a/test_vector_handlers/README.rst b/test_vector_handlers/README.rst index 382352736..9bf6e5ce5 100644 --- a/test_vector_handlers/README.rst +++ b/test_vector_handlers/README.rst @@ -12,7 +12,7 @@ Getting Started Required Prerequisites ====================== -* Python 2.7 or 3.4+ +* Python 3.8+ * aws-encryption-sdk Use @@ -75,4 +75,4 @@ decrypt and verify all referenced ciphertexts. -h, --help show this help message and exit --input INPUT Existing full message decrypt manifest -.. _shared test vectors repository: https://github.com/awslabs/aws-encryption-sdk-test-vectors \ No newline at end of file +.. _shared test vectors repository: https://github.com/awslabs/aws-encryption-sdk-test-vectors diff --git a/test_vector_handlers/requirements_mpl.txt b/test_vector_handlers/requirements_mpl.txt new file mode 100644 index 000000000..8912641d0 --- /dev/null +++ b/test_vector_handlers/requirements_mpl.txt @@ -0,0 +1 @@ +aws-cryptographic-material-providers>=1.7.4,<=1.10.0 diff --git a/test_vector_handlers/scripts/install_mpl_test_vector_runner.sh b/test_vector_handlers/scripts/install_mpl_test_vector_runner.sh new file mode 100644 index 000000000..aecff1a26 --- /dev/null +++ b/test_vector_handlers/scripts/install_mpl_test_vector_runner.sh @@ -0,0 +1,24 @@ +# Builds the Python MPL TestVector runner from source. +# This package is used by the ESDK-Python test vectors for testing with the MPL. + +# This script is intended to be used by ESDK-Python's integration tests. +# You may need or want to make local changes to get this work on your machine. + +# Change to the directory of the script +cd "$(dirname "$0")" + +# Get highest MPL version specified in requirements.txt +export mplVersion=$(grep 'aws-cryptographic-material-providers' ../requirements_mpl.txt | tr ',' '\n' | grep '<=' | sed -E 's/[^0-9]*//') + +# Clone MPL repo to get test vectors runner source code and the Dafny version to use +git clone --branch v$mplVersion --recurse-submodules https://github.com/aws/aws-cryptographic-material-providers-library.git + +# Download Dafny to build the test vector runner; get Dafny version from ESDK's project.properties file +export dafnyVersion=$(grep '^dafnyVersion=' aws-cryptographic-material-providers-library/project.properties | cut -d '=' -f 2) +curl https://github.com/dafny-lang/dafny/releases/download/v$dafnyVersion/dafny-$dafnyVersion-x64-ubuntu-20.04.zip -L -o dafny.zip +unzip -qq dafny.zip && rm dafny.zip +export PATH="$PWD/dafny:$PATH" + +# Build MPL test vector runner from source +cd aws-cryptographic-material-providers-library/TestVectorsAwsCryptographicMaterialProviders/ +make transpile_python diff --git a/test_vector_handlers/setup.py b/test_vector_handlers/setup.py index 9a89fb698..f09126929 100644 --- a/test_vector_handlers/setup.py +++ b/test_vector_handlers/setup.py @@ -46,9 +46,6 @@ def get_requirements(): "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", diff --git a/test_vector_handlers/src/awses_test_vectors/__init__.py b/test_vector_handlers/src/awses_test_vectors/__init__.py index 5870f4318..0a9c2d8c7 100644 --- a/test_vector_handlers/src/awses_test_vectors/__init__.py +++ b/test_vector_handlers/src/awses_test_vectors/__init__.py @@ -1,14 +1,4 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Static test vector handling logic for the AWS Encyrption SDK.""" __version__ = "2.0.0" diff --git a/test_vector_handlers/src/awses_test_vectors/commands/__init__.py b/test_vector_handlers/src/awses_test_vectors/commands/__init__.py index 17b493032..52ecb14a1 100644 --- a/test_vector_handlers/src/awses_test_vectors/commands/__init__.py +++ b/test_vector_handlers/src/awses_test_vectors/commands/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """CLI commands.""" diff --git a/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt.py b/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt.py index baf1d1f03..91b68287a 100644 --- a/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt.py @@ -1,20 +1,17 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Command to test AWS Encryption SDK full message decryption vectors.""" import argparse from awses_test_vectors.manifests.full_message.decrypt import MessageDecryptionManifest +try: + import aws_cryptographic_material_providers # noqa pylint: disable=unused-import,import-error + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + + try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover @@ -29,9 +26,19 @@ def cli(args=None): parser.add_argument( "--input", required=True, type=argparse.FileType("r"), help="Existing full message decrypt manifest" ) + parser.add_argument( + "--keyrings", + action="store_true", + required=False, + default=False, + help="Use keyring interfaces to encrypt", + ) parsed = parser.parse_args(args) - decrypt_manifest = MessageDecryptionManifest.from_file(parsed.input) + if parsed.keyrings and not _HAS_MPL: + raise ImportError("The --keyrings flag requires the aws-cryptographic-material-providers library.") + + decrypt_manifest = MessageDecryptionManifest.from_file(parsed.input, parsed.keyrings) decrypt_manifest.run() diff --git a/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt_generate.py b/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt_generate.py index 5d8b94893..6383f7229 100644 --- a/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt_generate.py +++ b/test_vector_handlers/src/awses_test_vectors/commands/full_message_decrypt_generate.py @@ -1,20 +1,16 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Command to generate AWS Encryption SDK full message decryption vectors.""" import argparse from awses_test_vectors.manifests.full_message.decrypt_generation import MessageDecryptionGenerationManifest +try: + import aws_cryptographic_material_providers # noqa pylint: disable=unused-import,import-error + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover @@ -39,9 +35,19 @@ def cli(args=None): dest="json_indent", help="Output human-readable JSON", ) + parser.add_argument( + "--keyrings", + action="store_true", + required=False, + default=False, + help="Use keyring interfaces to encrypt", + ) parsed = parser.parse_args(args) - encrypt_manifest = MessageDecryptionGenerationManifest.from_file(parsed.input) + if parsed.keyrings and not _HAS_MPL: + raise ImportError("The --keyrings flag requires the aws-cryptographic-material-providers library.") + + encrypt_manifest = MessageDecryptionGenerationManifest.from_file(parsed.input, parsed.keyrings) encrypt_manifest.run_and_write_to_dir(target_directory=parsed.output, json_indent=parsed.json_indent) diff --git a/test_vector_handlers/src/awses_test_vectors/commands/full_message_encrypt.py b/test_vector_handlers/src/awses_test_vectors/commands/full_message_encrypt.py index 2b8b92f3c..1025b9426 100644 --- a/test_vector_handlers/src/awses_test_vectors/commands/full_message_encrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/commands/full_message_encrypt.py @@ -1,20 +1,17 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Command to test AWS Encryption SDK full message encryption vectors.""" import argparse from awses_test_vectors.manifests.full_message.encrypt import MessageEncryptionManifest +try: + import aws_cryptographic_material_providers # noqa pylint: disable=unused-import,import-error + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + + try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import Iterable, Optional # noqa pylint: disable=unused-import except ImportError: # pragma: no cover @@ -29,9 +26,19 @@ def cli(args=None): parser.add_argument( "--input", required=True, type=argparse.FileType("r"), help="Existing full message encrypt manifest" ) + parser.add_argument( + "--keyrings", + action="store_true", + required=False, + default=False, + help="Use keyring interfaces to encrypt", + ) parsed = parser.parse_args(args) - encrypt_manifest = MessageEncryptionManifest.from_file(parsed.input) + if parsed.keyrings and not _HAS_MPL: + raise ImportError("The --keyrings flag requires the aws-cryptographic-material-providers library.") + + encrypt_manifest = MessageEncryptionManifest.from_file(parsed.input, parsed.keyrings) encrypt_manifest.run() diff --git a/test_vector_handlers/src/awses_test_vectors/internal/__init__.py b/test_vector_handlers/src/awses_test_vectors/internal/__init__.py index 34bfd8a1d..9bad12089 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/__init__.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/__init__.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Internal implementation details. .. warning:: diff --git a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py index 14c109e7d..16425d719 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/aws_kms.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Helper utilities for interacting with AWS KMS.""" try: from aws_encryption_sdk.identifiers import AlgorithmSuite diff --git a/test_vector_handlers/src/awses_test_vectors/internal/defaults.py b/test_vector_handlers/src/awses_test_vectors/internal/defaults.py index d72a9287d..fcc8a655a 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/defaults.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/defaults.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Default values for use in AWS Encryption SDK test vector handlers.""" ENCODING = "utf-8" diff --git a/test_vector_handlers/src/awses_test_vectors/internal/mpl/__init__.py b/test_vector_handlers/src/awses_test_vectors/internal/mpl/__init__.py new file mode 100644 index 000000000..11e9569d9 --- /dev/null +++ b/test_vector_handlers/src/awses_test_vectors/internal/mpl/__init__.py @@ -0,0 +1,3 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Internal modules that require the aws-cryptographic-material-providers library.""" diff --git a/test_vector_handlers/src/awses_test_vectors/internal/mpl/keyvectors_provider.py b/test_vector_handlers/src/awses_test_vectors/internal/mpl/keyvectors_provider.py new file mode 100644 index 000000000..90581182c --- /dev/null +++ b/test_vector_handlers/src/awses_test_vectors/internal/mpl/keyvectors_provider.py @@ -0,0 +1,27 @@ +"""Singleton provider for the KeyVectors client.""" +# # Ignore missing MPL TestVectors for pylint, but the MPL TestVectors is required for this file +# pylint: disable=import-error +from aws_cryptography_materialproviders_test_vectors.smithygenerated.\ + aws_cryptography_materialproviderstestvectorkeys.client import ( + KeyVectors, + ) +from aws_cryptography_materialproviders_test_vectors.smithygenerated.\ + aws_cryptography_materialproviderstestvectorkeys.config import ( + KeyVectorsConfig + ) + +keyvectors_instances = {} + + +# pylint: disable=too-few-public-methods +class KeyVectorsProvider: + """Singleton manager for the KeyVectors client.""" + + instance: KeyVectors + + @classmethod + def get_keyvectors(cls, keys_path): + """Return the singleton KeyVectors client.""" + if keys_path not in keyvectors_instances: + keyvectors_instances[keys_path] = KeyVectors(KeyVectorsConfig(key_manifest_path=keys_path)) + return keyvectors_instances[keys_path] diff --git a/test_vector_handlers/src/awses_test_vectors/internal/mpl/tampering_mpl_materials.py b/test_vector_handlers/src/awses_test_vectors/internal/mpl/tampering_mpl_materials.py new file mode 100644 index 000000000..4caf85635 --- /dev/null +++ b/test_vector_handlers/src/awses_test_vectors/internal/mpl/tampering_mpl_materials.py @@ -0,0 +1,179 @@ +"""Allows using ESDK-MPL interfaces with the tampering tests. +These must ONLY be used in testing and NOT in production. +""" +from copy import copy +import attr +import six + + +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager + +# Ignore missing MPL for pylint, but the MPL is required for this class +# pylint: disable=import-error,no-name-in-module +from aws_encryption_sdk.materials_managers.mpl.materials import ( + EncryptionMaterialsFromMPL +) +from aws_encryption_sdk.materials_managers.mpl.cmm import ( + CryptoMaterialsManagerFromMPL +) +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, +) + +try: + from aws_encryption_sdk.identifiers import AlgorithmSuite +except ImportError: + from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite + + +class HalfSigningCryptoMaterialsManagerFromMPL(CryptoMaterialsManagerFromMPL): + """ + Custom CMM that uses HalfSigningEncryptionMaterialsFromMPL. + This extends CryptoMaterialsManagerFromMPL so ESDK-internal checks + follow MPL logic. + + THIS IS ONLY USED TO CREATE INVALID MESSAGES and should never be used in + production! + """ + + wrapped_default_cmm = attr.ib(validator=attr.validators.instance_of(CryptoMaterialsManagerFromMPL)) + + def __init__(self, master_key_provider): # pylint: disable=super-init-not-called + """Create a new CMM that wraps a the given CMM.""" + mpl = AwsCryptographicMaterialProviders(MaterialProvidersConfig()) + mpl_cmm = mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=master_key_provider + ) + ) + self.wrapped_default_cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mpl_cmm) + + def get_encryption_materials(self, request): + """ + Generate half-signing materials by requesting signing materials + from the wrapped default CMM, and then changing the algorithm suite + and removing the signing key from teh result. + """ + if request.algorithm == AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY: + signing_request = copy(request) + signing_request.algorithm = AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384 + + result = HalfSigningEncryptionMaterialsFromMPL( + self.wrapped_default_cmm.get_encryption_materials(signing_request) + ) + + result.algorithm = request.algorithm + result.signing_key = None + + return result + + raise NotImplementedError( + "The half-sign tampering method is only supported on the " + "AES_256_GCM_HKDF_SHA512_COMMIT_KEY algorithm suite." + ) + + def decrypt_materials(self, request): + """Thunks to the wrapped default CMM""" + return self.wrapped_default_cmm.decrypt_materials(request) + + +class HalfSigningEncryptionMaterialsFromMPL(EncryptionMaterialsFromMPL): + """Allows overriding properties inside the EncryptionMaterialsFromMPL. + The test vectors to this to "tamper" with the messages + and ensure they fail with expected errors. + This must ONLY be used in testing and NOT in production. + This is used in testing malicious message modification (HalfSigningTampering). + """ + + _underlying_materials: EncryptionMaterialsFromMPL + + def __init__(self, underlying_materials): # pylint: disable=super-init-not-called + """Create a HalfSigningEncryptionMaterialsFromMPL wrapper + around underlying_materials. + """ + self._underlying_materials = underlying_materials + + # pylint thinks EncryptionMaterialsFromMPL.algorithm is a method + # pylint: disable=invalid-overridden-method + @property + def algorithm(self): + """Return any previously-provided overriden algorithm; + if none was provided, returns underlying algorithm from encryption materials. + """ + if hasattr(self, "set_algorithm"): + return self.set_algorithm + return self._underlying_materials.algorithm + + @algorithm.setter + def algorithm(self, algorithm): + self.set_algorithm = algorithm + + # pylint thinks EncryptionMaterialsFromMPL.signing_key is a method + # pylint: disable=invalid-overridden-method + @property + def signing_key(self): + """Return any previously-provided overriden signing_key; + if none was provided, returns underlying signing_key from encryption materials. + """ + if hasattr(self, "set_signing_key"): + return self.set_signing_key + return self._underlying_materials.algorithm + + @signing_key.setter + def signing_key(self, signing_key): + self.set_signing_key = signing_key + + @property + def encryption_context(self): + """Get encryption_context from _underlying_materials.""" + return self._underlying_materials.encryption_context + + @property + def encrypted_data_keys(self): + """Get encrypted_data_keys from _underlying_materials.""" + return self._underlying_materials.encrypted_data_keys + + @property + def data_encryption_key(self): + """Get data_encryption_key from _underlying_materials.""" + return self._underlying_materials.data_encryption_key + + @property + def required_encryption_context_keys(self): + """Get required_encryption_context_keys from _underlying_materials.""" + return self._underlying_materials.required_encryption_context_keys + + +class ProviderInfoChangingCryptoMaterialsManagerFromMPL(CryptoMaterialsManagerFromMPL): + """ + Custom CMM that modifies the provider info field on EDKs. + This extends CryptoMaterialsManagerFromMPL so ESDK-internal checks + follow MPL logic. + + THIS IS ONLY USED TO CREATE INVALID MESSAGES and should never be used in + production! + """ + + wrapped_cmm = attr.ib(validator=attr.validators.instance_of(CryptoMaterialsManager)) + new_provider_info = attr.ib(validator=attr.validators.instance_of(six.string_types)) + + def __init__(self, materials_manager, new_provider_info): # pylint: disable=super-init-not-called + """Create a new CMM that wraps a the given CMM.""" + self.wrapped_cmm = materials_manager + self.new_provider_info = new_provider_info + + def get_encryption_materials(self, request): + """ + Request materials from the wrapped CMM, and then change the provider info + on each EDK. + """ + result = self.wrapped_cmm.get_encryption_materials(request) + for encrypted_data_key in result.encrypted_data_keys: + encrypted_data_key.key_provider.key_info = self.new_provider_info + return result + + def decrypt_materials(self, request): + """Thunks to the wrapped CMM""" + return self.wrapped_cmm.decrypt_materials(request) diff --git a/test_vector_handlers/src/awses_test_vectors/internal/mypy_types.py b/test_vector_handlers/src/awses_test_vectors/internal/mypy_types.py index 3712643e8..482b303a0 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/mypy_types.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/mypy_types.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """MyPy types for use in AWS Encryption SDK test vector handlers.""" # mypy types confuse pylint: disable=invalid-name diff --git a/test_vector_handlers/src/awses_test_vectors/internal/util.py b/test_vector_handlers/src/awses_test_vectors/internal/util.py index da5552f13..002e36689 100644 --- a/test_vector_handlers/src/awses_test_vectors/internal/util.py +++ b/test_vector_handlers/src/awses_test_vectors/internal/util.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Utility functions for use in AWS Encryption SDK test vector handlers.""" import os import struct diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/__init__.py b/test_vector_handlers/src/awses_test_vectors/manifests/__init__.py index e2472c3bb..c8ba0a088 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/__init__.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Test vector manifest handlers.""" diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/__init__.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/__init__.py index 090bd995e..0a47b9fcc 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/__init__.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/__init__.py @@ -1,13 +1,3 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """Full-message test vector manifest handlers.""" diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py index c94fd1452..a938106bd 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ AWS Encryption SDK Decrypt Message manifest handler. @@ -18,6 +8,8 @@ import json import os from enum import Enum +import contextlib +import io import attr import aws_encryption_sdk @@ -35,6 +27,21 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs +try: + from awses_test_vectors.manifests.mpl_keyring import KeyringSpec, keyring_from_master_key_specs + from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + from aws_cryptographic_material_providers.mpl.references import ICryptographicMaterialsManager + from aws_cryptographic_material_providers.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, + CreateRequiredEncryptionContextCMMInput, + ) + + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + + try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import @@ -49,7 +56,7 @@ CLIENT_NAME = "aws/aws-encryption-sdk-python" CURRENT_VERSION = 2 -SUPPORTED_VERSIONS = (2,) +SUPPORTED_VERSIONS = (2, 4,) @attr.s(init=False) @@ -121,8 +128,19 @@ def match(self, name, decrypt_fn): # The ESDK implementations are not consistent in the types of errors they produce # or the exact error messages they use. The most important thing to test is that decryption # fails in some way, and hence the overly-broad implicit try/catch here. + with pytest.raises(Exception): - decrypt_fn() + # Here, an exception is expected. + # However, when the expected exception is raised, + # the Python environment will write stderrs to console. + # Redirect stderr to null-like sink + # so local and CI build logs are cleaner, + # and any actual issues are easier to see. + # If an exception is not raised as expected, + # `pytest.raises` will fail. + tmp_file = io.StringIO() + with contextlib.redirect_stderr(tmp_file): + decrypt_fn() except BaseException: # Translate the exception just to attach context. raise RuntimeError( @@ -181,7 +199,7 @@ class DecryptionMethod(Enum): @attr.s(init=False) class MessageDecryptionTestScenario(object): - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-instance-attributes """Data class for a single full message decrypt test scenario. Handles serialization and deserialization to and from manifest specs. @@ -192,6 +210,10 @@ class MessageDecryptionTestScenario(object): :param master_key_specs: Iterable of master key specifications :type master_key_specs: iterable of :class:`MasterKeySpec` :param Callable master_key_provider_fn: + :param bool keyrings: True if should decrypt with keyring interfaces; False otherwise + :param str cmm_type: `cmm` from test vector manifest; "Default" if not specified + :param str encryption_context: Any encryption context to validate on decrypt if using + keyrings AND the required encryption context CMM :param str description: Description of test scenario (optional) """ @@ -202,6 +224,8 @@ class MessageDecryptionTestScenario(object): master_key_specs = attr.ib(validator=iterable_validator(list, MasterKeySpec)) master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) result = attr.ib(validator=attr.validators.instance_of(MessageDecryptionTestResult)) + keyrings = attr.ib(validator=attr.validators.instance_of(bool)) + cmm_type = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(str))) decryption_method = attr.ib( default=None, validator=attr.validators.optional(attr.validators.instance_of(DecryptionMethod)) ) @@ -216,6 +240,9 @@ def __init__( result, # type: MessageDecryptionTestResult master_key_specs, # type: Iterable[MasterKeySpec] master_key_provider_fn, # type: Callable + keyrings, # type: bool + cmm_type, # type: str + encryption_context, # type: Dict[str, str] decryption_method=None, # type: Optional[DecryptionMethod] description=None, # type: Optional[str] ): # noqa=D107 @@ -229,18 +256,24 @@ def __init__( self.result = result self.master_key_specs = master_key_specs self.master_key_provider_fn = master_key_provider_fn + self.keyrings = keyrings + self.cmm_type = cmm_type + self.encryption_context = encryption_context self.decryption_method = decryption_method self.description = description attr.validate(self) @classmethod - def from_scenario( + def from_scenario( # noqa: C901 cls, scenario, # type: DECRYPT_SCENARIO_SPEC plaintext_reader, # type: Callable[[str], bytes] ciphertext_reader, # type: Callable[[str], bytes] keys, # type: KeysManifest + keyrings, # type: bool + keys_uri, # type: str ): + # pylint: disable=too-many-locals,too-many-branches # type: (...) -> MessageDecryptionTestScenario """Load from a scenario specification. @@ -252,9 +285,19 @@ def from_scenario( :rtype: MessageDecryptionTestScenario """ raw_master_key_specs = scenario["master-keys"] # type: Iterable[MASTER_KEY_SPEC] - master_key_specs = [MasterKeySpec.from_scenario(spec) for spec in raw_master_key_specs] + if keyrings: + master_key_specs = [ + KeyringSpec.from_scenario(spec) for spec in raw_master_key_specs + ] + else: + master_key_specs = [ + MasterKeySpec.from_scenario(spec) for spec in raw_master_key_specs + if spec["type"] != "aws-kms-hierarchy" + ] def master_key_provider_fn(): + if keyrings: + return keyring_from_master_key_specs(keys_uri, master_key_specs, "decrypt") return master_key_provider_from_master_key_specs(keys, master_key_specs) decryption_method_spec = scenario.get("decryption-method") @@ -262,12 +305,51 @@ def master_key_provider_fn(): result_spec = scenario["result"] result = MessageDecryptionTestResult.from_result_spec(result_spec, plaintext_reader) + if "encryption-context" in scenario: + encryption_context = scenario["encryption-context"] + else: + encryption_context = {} + + # MPL test vectors add CMM types to the test vectors manifests + if "cmm" in scenario \ + and scenario["cmm"] is not None: + if scenario["cmm"] == "Default": + # Master keys and keyrings can handle default CMM + cmm_type = scenario["cmm"] + elif scenario["cmm"] == "RequiredEncryptionContext": + # Skip RequiredEncryptionContext CMM for master keys; + # RequiredEncryptionContext is unsupported for master keys. + # Caller logic should expect `None` to mean "no scenario". + if keyrings: + cmm_type = scenario["cmm"] + else: + return None + else: + raise ValueError("Unrecognized cmm_type: " + scenario["cmm"]) + else: + # If unspecified, set "Default" as the default + cmm_type = "Default" + + try: + # If this scenario does not have any key providers, + # do not create a scenario. + # Caller logic should expect `None` to mean "no scenario". + if master_key_provider_fn() is None: + return None + except Exception: # nosec,pylint: disable=broad-except + # If there is any exception when loading the key, continue to create the test scenario. + # Some test scenarios have bad keys that should fail during the test execution. + pass + return cls( ciphertext_uri=scenario["ciphertext"], ciphertext=ciphertext_reader(scenario["ciphertext"]), master_key_specs=master_key_specs, master_key_provider_fn=master_key_provider_fn, result=result, + keyrings=keyrings, + encryption_context=encryption_context, + cmm_type=cmm_type, decryption_method=decryption_method, description=scenario.get("description"), ) @@ -289,16 +371,105 @@ def scenario_spec(self): spec["decryption-method"] = self.decryption_method.value if self.description is not None: spec["description"] = self.description + spec["cmm"] = self.cmm_type + spec["encryption-context"] = self.encryption_context + return spec def _one_shot_decrypt(self): client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - return client.decrypt(source=self.ciphertext, key_provider=self.master_key_provider_fn()) + if self.cmm_type == "Default": + if self.keyrings: + return client.decrypt(source=self.ciphertext, keyring=self.master_key_provider_fn()) + return client.decrypt(source=self.ciphertext, key_provider=self.master_key_provider_fn()) + if self.cmm_type == "RequiredEncryptionContext": + # We need to make a custom CMM and pass it into the client + if not self.keyrings: + raise ValueError("Must provide keyrings arg to use RequiredEncryptionContext") + if not _HAS_MPL: + raise ValueError("Must install the aws-cryptographic-material-providers library" + "to use RequiredEncryptionContext") + + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + underlying_cmm: ICryptographicMaterialsManager = \ + mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.master_key_provider_fn() + ) + ) + + required_ec_cmm: ICryptographicMaterialsManager = \ + mpl.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + # Currently, the test vector manifest assumes these + # are the only required encryption context keys for any message. + # If this assumption changes, this logic must be augmented. + required_encryption_context_keys=["key1", "key2"], + underlying_cmm=underlying_cmm, + ) + ) + + return client.decrypt( + source=self.ciphertext, + materials_manager=required_ec_cmm, + encryption_context=self.encryption_context, + ) + + # If the cmm type was not in if/elif above, raise error + raise ValueError(f"Unrecognized cmm_type: {self.cmm_type}") def _streaming_decrypt(self): result = bytearray() client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - with client.stream(source=self.ciphertext, mode="d", key_provider=self.master_key_provider_fn()) as decryptor: + + kwargs = { + "source": self.ciphertext, + "mode": "d" + } + if self.cmm_type == "Default": + if self.keyrings: + kwargs["keyring"] = self.master_key_provider_fn() + else: + kwargs["key_provider"] = self.master_key_provider_fn() + elif self.cmm_type == "RequiredEncryptionContext": + # We need to make a custom CMM and pass it into the client + if not self.keyrings: + raise ValueError("Must provide keyrings arg to use RequiredEncryptionContext") + if not _HAS_MPL: + raise ValueError("Must install the aws-cryptographic-material-providers library" + "to use RequiredEncryptionContext") + + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + underlying_cmm: ICryptographicMaterialsManager = \ + mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.master_key_provider_fn() + ) + ) + + required_ec_cmm: ICryptographicMaterialsManager = \ + mpl.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + # Currently, the test vector manifest assumes these + # are the only required encryption context keys for any message. + # If this assumption changes, this logic must be augmented. + required_encryption_context_keys=["key1", "key2"], + underlying_cmm=underlying_cmm, + ) + ) + + kwargs["materials_manager"] = required_ec_cmm + kwargs["encryption_context"] = self.encryption_context + else: + raise ValueError(f"Unrecognized cmm_type: {self.cmm_type}") + + with client.stream(**kwargs) as decryptor: for chunk in decryptor: result.extend(chunk) return result, decryptor.header @@ -306,9 +477,53 @@ def _streaming_decrypt(self): def _streaming_decrypt_unsigned(self): result = bytearray() client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - with client.stream( - source=self.ciphertext, mode="decrypt-unsigned", key_provider=self.master_key_provider_fn() - ) as decryptor: + + stream_kwargs = { + "source": self.ciphertext, + "mode": "decrypt-unsigned", + } + + if self.cmm_type == "Default": + if self.keyrings: + stream_kwargs["keyring"] = self.master_key_provider_fn() + else: + stream_kwargs["key_provider"] = self.master_key_provider_fn() + elif self.cmm_type == "RequiredEncryptionContext": + # We need to make a custom CMM and pass it into the client + if not self.keyrings: + raise ValueError("Must provide keyrings arg to use RequiredEncryptionContext") + if not _HAS_MPL: + raise ValueError("Must install the aws-cryptographic-material-providers library" + "to use RequiredEncryptionContext") + + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + config=MaterialProvidersConfig() + ) + + underlying_cmm: ICryptographicMaterialsManager = \ + mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=self.master_key_provider_fn() + ) + ) + + required_ec_cmm: ICryptographicMaterialsManager = \ + mpl.create_required_encryption_context_cmm( + CreateRequiredEncryptionContextCMMInput( + # Currently, the test vector manifest assumes these + # are the only required encryption context keys for any message. + # If this assumption changes, this logic must be augmented. + required_encryption_context_keys=["key1", "key2"], + underlying_cmm=underlying_cmm, + ) + ) + + stream_kwargs["materials_manager"] = required_ec_cmm + stream_kwargs["encryption_context"] = self.encryption_context + else: + raise ValueError(f"Unrecognized cmm_type: {self.cmm_type}") + + with client.stream(**stream_kwargs) as decryptor: for chunk in decryptor: result.extend(chunk) return result, decryptor.header @@ -388,11 +603,13 @@ def manifest_spec(self): return {"manifest": manifest_spec, "client": client_spec, "keys": self.keys_uri, "tests": test_specs} @classmethod - def from_file(cls, input_file): + def from_file(cls, input_file, keyrings): + # pylint: disable=too-many-locals # type: (IO) -> MessageDecryptionManifest """Load from a file containing a full message decrypt manifest. :param file input_file: File object for file containing JSON manifest + :param bool keyrings: True if should decrypt with keyring interfaces; False otherwise :return: Loaded manifest :rtype: MessageDecryptionManifest """ @@ -407,18 +624,53 @@ def from_file(cls, input_file): version = raw_manifest["manifest"]["version"] # type: int keys_uri = raw_manifest["keys"] # type: str + # MPL TestVector keyring needs to know the path to the keys file + keys_uri = raw_manifest["keys"] + keys_filename = keys_uri.replace("file://", "") + keys_abs_path = os.path.join(parent_dir, keys_filename) + raw_keys_manifest = json.loads(root_reader(keys_uri).decode(ENCODING)) keys = KeysManifest.from_manifest_spec(raw_keys_manifest) client_name = raw_manifest["client"]["name"] # type: str client_version = raw_manifest["client"]["version"] # type: str raw_scenarios = raw_manifest["tests"] # type: Dict[str, DECRYPT_SCENARIO_SPEC] - test_scenarios = { - name: MessageDecryptionTestScenario.from_scenario( - scenario=scenario, plaintext_reader=root_reader, ciphertext_reader=root_reader, keys=keys - ) - for name, scenario in raw_scenarios.items() - } + + # If optional keyrings argument is specified, + # decrypt with keyrings + if keyrings: + test_scenarios = { + name + "-keyring": MessageDecryptionTestScenario.from_scenario( + scenario=scenario, + plaintext_reader=root_reader, + ciphertext_reader=root_reader, + keys=keys, + keyrings=True, + keys_uri=keys_abs_path, + ) + for name, scenario in raw_scenarios.items() + } + # If optional keyrings argument is not specified, + # decrypt with master key providers. + else: + test_scenarios = { + name: MessageDecryptionTestScenario.from_scenario( + scenario=scenario, + plaintext_reader=root_reader, + ciphertext_reader=root_reader, + keys=keys, + keyrings=False, + keys_uri=keys_abs_path, + ) + for name, scenario in raw_scenarios.items() + } + + # Remove any `None` scenarios from test scenarios. + # `None` scenarios indicate the loader determined the scenario is invalid. + # e.g. cmm_type = "RequiredEncryptionContext" with master keys + for name in list(test_scenarios.keys()): + if test_scenarios[name] is None: + del test_scenarios[name] return cls( keys_uri=keys_uri, diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py index e407a1b65..2e94dc6a7 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ AWS Encryption SDK Decrypt Message Generation manifest handler. @@ -23,10 +13,38 @@ import attr import six from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache +from aws_encryption_sdk.key_providers.base import MasterKeyProvider from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager +try: + from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders + from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig + from aws_cryptographic_material_providers.mpl.references import ( + IKeyring, + ) + from aws_cryptographic_material_providers.mpl.models import ( + CreateDefaultCryptographicMaterialsManagerInput, + ) + from aws_encryption_sdk.materials_managers.mpl.cmm import CryptoMaterialsManagerFromMPL + + from awses_test_vectors.manifests.mpl_keyring import KeyringSpec, keyring_from_master_key_specs + + from aws_encryption_sdk.materials_managers.mpl.materials import ( + EncryptionMaterialsFromMPL + ) + from awses_test_vectors.internal.mpl.tampering_mpl_materials import ( + HalfSigningEncryptionMaterialsFromMPL, + ProviderInfoChangingCryptoMaterialsManagerFromMPL, + HalfSigningCryptoMaterialsManagerFromMPL, + ) + + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + + from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( dictionary_validator, @@ -63,7 +81,7 @@ # We only actually need these imports when running the mypy checks pass -SUPPORTED_VERSIONS = (2,) +SUPPORTED_VERSIONS = (2, 4, ) class TamperingMethod: @@ -92,9 +110,23 @@ def run_scenario_with_tampering(self, ciphertext_writer, generation_scenario, pl return: a list of (ciphertext, result) pairs """ - materials_manager = DefaultCryptoMaterialsManager( - generation_scenario.encryption_scenario.master_key_provider_fn() - ) + key_provider = generation_scenario.encryption_scenario.master_key_provider_fn() + if isinstance(key_provider, MasterKeyProvider): + materials_manager = DefaultCryptoMaterialsManager( + key_provider + ) + elif _HAS_MPL and isinstance(key_provider, IKeyring): + mpl = AwsCryptographicMaterialProviders(MaterialProvidersConfig()) + mpl_cmm = mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=key_provider + ) + ) + materials_manager = CryptoMaterialsManagerFromMPL( + mpl_cmm=mpl_cmm + ) + else: + raise ValueError(f"Unrecognized master_key_provider_fn return type: {str(key_provider)}") ciphertext_to_decrypt = generation_scenario.encryption_scenario.run(materials_manager) if generation_scenario.result: expected_result = generation_scenario.result @@ -131,16 +163,31 @@ def run_scenario_with_tampering(self, ciphertext_writer, generation_scenario, _p master_key_provider = generation_scenario.encryption_scenario.master_key_provider_fn() # Use a caching CMM to avoid generating a new data key every time. - cache = LocalCryptoMaterialsCache(10) - caching_cmm = CachingCryptoMaterialsManager( - master_key_provider=master_key_provider, - cache=cache, - max_age=60.0, - max_messages_encrypted=100, - ) + if isinstance(master_key_provider, MasterKeyProvider): + cache = LocalCryptoMaterialsCache(10) + caching_cmm = CachingCryptoMaterialsManager( + master_key_provider=master_key_provider, + cache=cache, + max_age=60.0, + max_messages_encrypted=100, + ) + cmm = caching_cmm + # No caching CMM in MPL :( + # Use default CMM + elif _HAS_MPL and isinstance(master_key_provider, IKeyring): + mpl = AwsCryptographicMaterialProviders(MaterialProvidersConfig()) + mpl_cmm = mpl.create_default_cryptographic_materials_manager( + CreateDefaultCryptographicMaterialsManagerInput( + keyring=master_key_provider + ) + ) + cmm = CryptoMaterialsManagerFromMPL(mpl_cmm=mpl_cmm) + else: + raise TypeError(f"Unrecognized master_key_provider type: {master_key_provider}") + return [ self.run_scenario_with_new_provider_info( - ciphertext_writer, generation_scenario, caching_cmm, new_provider_info + ciphertext_writer, generation_scenario, cmm, new_provider_info ) for new_provider_info in self.new_provider_infos ] @@ -149,7 +196,18 @@ def run_scenario_with_new_provider_info( self, ciphertext_writer, generation_scenario, materials_manager, new_provider_info ): """Run with tampering for a specific new provider info value""" - tampering_materials_manager = ProviderInfoChangingCryptoMaterialsManager(materials_manager, new_provider_info) + if _HAS_MPL and isinstance(materials_manager, CryptoMaterialsManagerFromMPL): + tampering_materials_manager = ProviderInfoChangingCryptoMaterialsManagerFromMPL( + materials_manager, + new_provider_info + ) + elif isinstance(materials_manager, CryptoMaterialsManager): + tampering_materials_manager = ProviderInfoChangingCryptoMaterialsManager( + materials_manager, + new_provider_info + ) + else: + raise TypeError(f"Unrecognized materials_manager type: {materials_manager}") ciphertext_to_decrypt = generation_scenario.encryption_scenario.run(tampering_materials_manager) expected_result = MessageDecryptionTestResult.expect_error( "Incorrect encrypted data key provider info: " + new_provider_info @@ -253,9 +311,20 @@ def run_scenario_with_tampering(self, ciphertext_writer, generation_scenario, _p return: a list of (ciphertext, result) pairs. """ - tampering_materials_manager = HalfSigningCryptoMaterialsManager( - generation_scenario.encryption_scenario.master_key_provider_fn() - ) + if isinstance( + generation_scenario.encryption_scenario.master_key_provider_fn(), + MasterKeyProvider + ): + tampering_materials_manager = HalfSigningCryptoMaterialsManager( + generation_scenario.encryption_scenario.master_key_provider_fn() + ) + elif _HAS_MPL and isinstance( + generation_scenario.encryption_scenario.master_key_provider_fn(), + IKeyring + ): + tampering_materials_manager = HalfSigningCryptoMaterialsManagerFromMPL( + generation_scenario.encryption_scenario.master_key_provider_fn() + ) ciphertext_to_decrypt = generation_scenario.encryption_scenario.run(tampering_materials_manager) expected_result = MessageDecryptionTestResult.expect_error( "Unsigned message using a data key with a public key" @@ -296,6 +365,11 @@ def get_encryption_materials(self, request): signing_request.algorithm = AlgorithmSuite.AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384 result = self.wrapped_default_cmm.get_encryption_materials(signing_request) + + if _HAS_MPL: + if isinstance(result, EncryptionMaterialsFromMPL): + result = HalfSigningEncryptionMaterialsFromMPL(result) + result.algorithm = request.algorithm result.signing_key = None @@ -326,6 +400,7 @@ class MessageDecryptionTestScenarioGenerator(object): :type decryption_master_key_specs: iterable of :class:`MasterKeySpec` :param Callable decryption_master_key_provider_fn: :param result: + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise """ encryption_scenario = attr.ib(validator=attr.validators.instance_of(MessageEncryptionTestScenario)) @@ -334,29 +409,52 @@ class MessageDecryptionTestScenarioGenerator(object): decryption_master_key_specs = attr.ib(validator=iterable_validator(list, MasterKeySpec)) decryption_master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) result = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(MessageDecryptionTestResult))) + keyrings = attr.ib(validator=attr.validators.instance_of(bool)) + cmm_type = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(str))) + encryption_context = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(dict))) @classmethod - def from_scenario(cls, scenario, keys, plaintexts): + def from_scenario(cls, scenario, keys, plaintexts, keyrings, keys_uri): + # pylint: disable=too-many-arguments,too-many-locals """Load from a scenario specification. :param dict scenario: Scenario specification JSON :param KeysManifest keys: Loaded keys :param dict plaintexts: Mapping of plaintext names to plaintext values + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise + :param string keys_uri: Filepath to keys manifest. Used by MPL TestVector keyring constructor. :return: Loaded test scenario :rtype: MessageDecryptionTestScenarioGenerator """ encryption_scenario_spec = scenario["encryption-scenario"] - encryption_scenario = MessageEncryptionTestScenario.from_scenario(encryption_scenario_spec, keys, plaintexts) + encryption_scenario = MessageEncryptionTestScenario.from_scenario( + encryption_scenario_spec, + keys, + plaintexts, + keyrings, + keys_uri, + ) + + if encryption_scenario is None: + return None + tampering = scenario.get("tampering") tampering_method = TamperingMethod.from_tampering_spec(tampering) decryption_method_spec = scenario.get("decryption-method") decryption_method = DecryptionMethod(decryption_method_spec) if decryption_method_spec else None if "decryption-master-keys" in scenario: - decryption_master_key_specs = [ - MasterKeySpec.from_scenario(spec) for spec in scenario["decryption-master-keys"] - ] + if keyrings: + decryption_master_key_specs = [ + KeyringSpec.from_scenario(spec) for spec in scenario["decryption-master-keys"] + ] + else: + decryption_master_key_specs = [ + MasterKeySpec.from_scenario(spec) for spec in scenario["decryption-master-keys"] + ] def decryption_master_key_provider_fn(): + if keyrings: + return keyring_from_master_key_specs(keys_uri, decryption_master_key_specs, "decrypt-generation") return master_key_provider_from_master_key_specs(keys, decryption_master_key_specs) else: @@ -365,6 +463,16 @@ def decryption_master_key_provider_fn(): result_spec = scenario.get("result") result = MessageDecryptionTestResult.from_result_spec(result_spec, None) if result_spec else None + try: + encryption_context = encryption_scenario_spec["encryption-context"] + except KeyError: + encryption_context = None + + try: + cmm_type = encryption_scenario_spec["cmm"] + except KeyError: + cmm_type = None + return cls( encryption_scenario=encryption_scenario, tampering_method=tampering_method, @@ -372,6 +480,9 @@ def decryption_master_key_provider_fn(): decryption_master_key_specs=decryption_master_key_specs, decryption_master_key_provider_fn=decryption_master_key_provider_fn, result=result, + keyrings=keyrings, + cmm_type=cmm_type, + encryption_context=encryption_context, ) def run(self, ciphertext_writer, plaintext_uri): @@ -400,6 +511,9 @@ def decryption_test_scenario_pair(self, ciphertext_writer, ciphertext_to_decrypt master_key_provider_fn=self.decryption_master_key_provider_fn, decryption_method=self.decryption_method, result=expected_result, + keyrings=self.keyrings, + cmm_type=self.cmm_type, + encryption_context=self.encryption_context, ), ) @@ -414,12 +528,14 @@ class MessageDecryptionGenerationManifest(object): :param KeysManifest keys: Loaded keys :param dict plaintexts: Mapping of plaintext names to plaintext values :param dict tests: Mapping of test scenario names to :class:`MessageDecryptionGenerationManifest`s + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise """ version = attr.ib(validator=membership_validator(SUPPORTED_VERSIONS)) keys = attr.ib(validator=attr.validators.instance_of(KeysManifest)) plaintexts = attr.ib(validator=dictionary_validator(six.string_types, six.binary_type)) tests = attr.ib(validator=dictionary_validator(six.string_types, MessageDecryptionTestScenarioGenerator)) + keyrings = attr.ib(validator=attr.validators.instance_of(bool)) type_name = "awses-decrypt-generate" @staticmethod @@ -434,11 +550,13 @@ def _generate_plaintexts(plaintexts_specs): return {name: os.urandom(size) for name, size in plaintexts_specs.items()} @classmethod - def from_file(cls, input_file): + def from_file(cls, input_file, keyrings): + # pylint: disable=too-many-locals # type: (IO) -> MessageDecryptionGenerationManifest """Load from a file containing a full message encrypt manifest. :param file input_file: File object for file containing JSON manifest + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise :return: Loaded manifest :rtype: MessageEncryptionManifest """ @@ -449,18 +567,30 @@ def from_file(cls, input_file): parent_dir = os.path.abspath(os.path.dirname(input_file.name)) reader = file_reader(parent_dir) - raw_keys_manifest = json.loads(reader(raw_manifest["keys"]).decode(ENCODING)) + + # MPL TestVector keyring needs to know the path to the keys file + keys_uri = raw_manifest["keys"] + keys_filename = keys_uri.replace("file://", "") + keys_abs_path = os.path.join(parent_dir, keys_filename) + + raw_keys_manifest = json.loads(reader(keys_uri).decode(ENCODING)) keys = KeysManifest.from_manifest_spec(raw_keys_manifest) plaintexts = cls._generate_plaintexts(raw_manifest["plaintexts"]) tests = {} for name, scenario in raw_manifest["tests"].items(): try: tests[name] = MessageDecryptionTestScenarioGenerator.from_scenario( - scenario=scenario, keys=keys, plaintexts=plaintexts + scenario=scenario, keys=keys, plaintexts=plaintexts, keyrings=keyrings, keys_uri=keys_abs_path, ) except NotImplementedError: continue - return cls(version=raw_manifest["manifest"]["version"], keys=keys, plaintexts=plaintexts, tests=tests) + return cls( + version=raw_manifest["manifest"]["version"], + keys=keys, + plaintexts=plaintexts, + tests=tests, + keyrings=keyrings, + ) def run_and_write_to_dir(self, target_directory, json_indent=None): # type: (str, Optional[int]) -> None diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py index c77fed1ce..2a6795902 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ AWS Encryption SDK Encrypt Message manifest handler. @@ -22,6 +12,8 @@ import aws_encryption_sdk import six +from aws_encryption_sdk.key_providers.base import MasterKeyProvider + from awses_test_vectors.internal.defaults import ENCODING from awses_test_vectors.internal.util import ( algorithm_suite_from_string_id, @@ -34,11 +26,23 @@ from awses_test_vectors.manifests.keys import KeysManifest from awses_test_vectors.manifests.master_key import MasterKeySpec, master_key_provider_from_master_key_specs + try: from aws_encryption_sdk.identifiers import AlgorithmSuite, CommitmentPolicy except ImportError: from aws_encryption_sdk.identifiers import Algorithm as AlgorithmSuite +try: + from aws_cryptographic_material_providers.mpl.references import ( + IKeyring, + ) + + from awses_test_vectors.manifests.mpl_keyring import KeyringSpec, keyring_from_master_key_specs + + _HAS_MPL = True +except ImportError: + _HAS_MPL = False + try: # Python 3.5.0 and 3.5.1 have incompatible typing modules from typing import IO, Callable, Dict, Iterable, Optional # noqa pylint: disable=unused-import @@ -78,24 +82,58 @@ class MessageEncryptionTestScenario(object): encryption_context = attr.ib(validator=dictionary_validator(six.string_types, six.string_types)) master_key_specs = attr.ib(validator=iterable_validator(list, MasterKeySpec)) master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) + keyrings = attr.ib(validator=attr.validators.instance_of(bool)) + cmm = attr.ib(validator=attr.validators.instance_of(str)) @classmethod - def from_scenario(cls, scenario, keys, plaintexts): - # type: (ENCRYPT_SCENARIO_SPEC, KeysManifest, Dict[str, bytes]) -> MessageEncryptionTestScenario + def from_scenario(cls, scenario, keys, plaintexts, keyrings, keys_uri): + # pylint: disable=too-many-arguments + # type: (ENCRYPT_SCENARIO_SPEC, KeysManifest, Dict[str, bytes], bool, str) -> MessageEncryptionTestScenario """Load from a scenario specification. :param dict scenario: Scenario specification JSON :param KeysManifest keys: Loaded keys :param dict plaintexts: Mapping of plaintext names to plaintext values + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise + :param str keys_uri: Path to the keys manifest :return: Loaded test scenario :rtype: MessageEncryptionTestScenario """ algorithm = algorithm_suite_from_string_id(scenario["algorithm"]) - master_key_specs = [MasterKeySpec.from_scenario(spec) for spec in scenario["master-keys"]] + + if keyrings: + master_key_specs = [ + KeyringSpec.from_scenario(spec) for spec in scenario["master-keys"] + ] + else: + master_key_specs = [ + MasterKeySpec.from_scenario(spec) for spec in scenario["master-keys"] + ] def master_key_provider_fn(): + if keyrings: + return keyring_from_master_key_specs(keys_uri, master_key_specs, "encrypt") return master_key_provider_from_master_key_specs(keys, master_key_specs) + # MPL test vectors add CMM types to the test vectors manifests + if "cmm" in scenario: + if scenario["cmm"] == "Default": + # Master keys and keyrings can handle default CMM + cmm_type = scenario["cmm"] + elif scenario["cmm"] == "RequiredEncryptionContext": + # Skip RequiredEncryptionContext CMM for master keys; + # RequiredEncryptionContext is unsupported for master keys. + # Caller logic should expect `None` to mean "no scenario". + if keyrings: + cmm_type = scenario["cmm"] + else: + return None + else: + raise ValueError("Unrecognized cmm_type: " + cmm_type) + else: + # If unspecified, set "Default" as the default + cmm_type = "Default" + return cls( plaintext_name=scenario["plaintext"], plaintext=plaintexts[scenario["plaintext"]], @@ -104,6 +142,8 @@ def master_key_provider_fn(): encryption_context=scenario["encryption-context"], master_key_specs=master_key_specs, master_key_provider_fn=master_key_provider_fn, + keyrings=keyrings, + cmm=cmm_type, ) def run(self, materials_manager=None): @@ -129,8 +169,12 @@ def run(self, materials_manager=None): ) if materials_manager: encrypt_kwargs["materials_manager"] = materials_manager - else: + elif isinstance(self.master_key_provider_fn(), MasterKeyProvider): encrypt_kwargs["key_provider"] = self.master_key_provider_fn() + elif _HAS_MPL and isinstance(self.master_key_provider_fn(), IKeyring): + encrypt_kwargs["keyring"] = self.master_key_provider_fn() + else: + raise TypeError(f"Unrecognized master_key_provider_fn return type: {self.master_key_provider_fn()}") ciphertext, _header = client.encrypt(**encrypt_kwargs) return ciphertext @@ -165,11 +209,12 @@ def _generate_plaintexts(plaintexts_specs): return {name: os.urandom(size) for name, size in plaintexts_specs.items()} @classmethod - def from_file(cls, input_file): + def from_file(cls, input_file, keyrings): # type: (IO) -> MessageEncryptionManifest """Load frome a file containing a full message encrypt manifest. :param file input_file: File object for file containing JSON manifest + :param bool keyrings: True if should encrypt with keyring interfaces; False otherwise :return: Loaded manifest :rtype: MessageEncryptionManifest """ @@ -180,14 +225,20 @@ def from_file(cls, input_file): parent_dir = os.path.abspath(os.path.dirname(input_file.name)) reader = file_reader(parent_dir) - raw_keys_manifest = json.loads(reader(raw_manifest["keys"]).decode(ENCODING)) + + # MPL TestVector keyring needs to know the path to the keys file + keys_uri = raw_manifest["keys"] + keys_filename = keys_uri.replace("file://", "") + keys_abs_path = os.path.join(parent_dir, keys_filename) + + raw_keys_manifest = json.loads(reader(keys_uri).decode(ENCODING)) keys = KeysManifest.from_manifest_spec(raw_keys_manifest) plaintexts = cls._generate_plaintexts(raw_manifest["plaintexts"]) tests = {} for name, scenario in raw_manifest["tests"].items(): try: tests[name] = MessageEncryptionTestScenario.from_scenario( - scenario=scenario, keys=keys, plaintexts=plaintexts + scenario=scenario, keys=keys, plaintexts=plaintexts, keyrings=keyrings, keys_uri=keys_abs_path ) except NotImplementedError: continue diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py index cba6b7e25..ce8bf756f 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Keys Manifest handler. @@ -29,6 +19,7 @@ from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import AWS_KMS_KEY_SPEC, + AWS_KMS_HIERARCHY_KEY_SPEC, KEY_SPEC, KEYS_MANIFEST, MANIFEST_VERSION, @@ -110,6 +101,51 @@ def manifest_spec(self): } +@attr.s(init=False) +class AwsKmsHierarchyKeySpec(KeySpec): + """AWS KMS hierarchy key specification. + + :param bool encrypt: Key can be used to encrypt + :param bool decrypt: Key can be used to decrypt + :param str type_name: Master key type name (must be "static-branch-key") + :param str key_id: Branch key ID + """ + + # pylint: disable=too-few-public-methods + + type_name = attr.ib(validator=membership_validator(("static-branch-key",))) + + # noqa pylint: disable=line-too-long,too-many-arguments + def __init__(self, encrypt, decrypt, type_name, key_id, branch_key_version, branch_key, beacon_key): # noqa=D107 + # type: (bool, bool, str, str) -> None + # Workaround pending resolution of attrs/mypy interaction. + # https://github.com/python/mypy/issues/2088 + # https://github.com/python-attrs/attrs/issues/215 + self.type_name = type_name + self.branch_key_version = branch_key_version + self.branch_key = branch_key + self.beacon_key = beacon_key + super(AwsKmsHierarchyKeySpec, self).__init__(encrypt, decrypt, key_id) + + @property + def manifest_spec(self): + # type: () -> AWS_KMS_HIERARCHY_KEY_SPEC + """Build a key specification describing this key specification. + + :return: Key specification JSON + :rtype: dict + """ + return { + "encrypt": self.encrypt, + "decrypt": self.decrypt, + "type": self.type_name, + "key-id": self.key_id, + "branchKeyVersion": self.branch_key_version, + "branchKey": self.branch_key, + "beaconKey": self.beacon_key, + } + + @attr.s(init=False) class ManualKeySpec(KeySpec): # pylint: disable=too-many-arguments @@ -206,8 +242,22 @@ def key_from_manifest_spec(key_spec): key_id = key_spec["key-id"] # type: str return AwsKmsKeySpec(encrypt=encrypt, decrypt=decrypt, type_name=type_name, key_id=key_id) - algorithm = key_spec["algorithm"] # type: str + if key_spec["type"] == "static-branch-key": + branch_key_version = key_spec["branchKeyVersion"] # type: str + branch_key = key_spec["branchKey"] # type: str + beacon_key = key_spec["beaconKey"] # type: str + return AwsKmsHierarchyKeySpec( + encrypt=encrypt, + decrypt=decrypt, + type_name=type_name, + key_id=key_id, + branch_key_version=branch_key_version, + branch_key=branch_key, + beacon_key=beacon_key, + ) + bits = key_spec["bits"] # type: int + algorithm = key_spec["algorithm"] encoding = key_spec["encoding"] # type: str material = key_spec["material"] # type: str return ManualKeySpec( diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py index a1a7ae4af..96af030d7 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ AWS Encryption SDK master key specification utilities. @@ -17,8 +7,9 @@ """ import attr import six +from aws_encryption_sdk.exceptions import InvalidKeyIdError from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm -from aws_encryption_sdk.key_providers.base import MasterKeyProvider # noqa pylint: disable=unused-import +from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig from aws_encryption_sdk.key_providers.kms import ( # noqa pylint: disable=unused-import DiscoveryFilter, KMSMasterKey, @@ -44,7 +35,7 @@ # We only actually need these imports when running the mypy checks pass -KNOWN_TYPES = ("aws-kms", "aws-kms-mrk-aware", "aws-kms-mrk-aware-discovery", "raw") +KNOWN_TYPES = ("aws-kms", "aws-kms-mrk-aware", "aws-kms-mrk-aware-discovery", "raw", ) KNOWN_ALGORITHMS = ("aes", "rsa") KNOWN_PADDING = ("pkcs1", "oaep-mgf1") KNOWN_PADDING_HASH = ("sha1", "sha256", "sha384", "sha512") @@ -300,6 +291,44 @@ def scenario_spec(self): return spec +class TestVectorsMultiMasterKeyProvider(MasterKeyProvider): + """ + Provider for other MasterKeyProviders. + Acts as a "multi" MasterKeyProvider for use in test vectors. + + There is some disagreement between the spec + and how Python ESDK implements MasterKey; + this class fills that gap. + + In the ESDK-Python, MasterKey extends MasterKeyProvider; + i.e. MasterKey "is a" MasterKeyProvider; isinstance(some_master_key, MasterKeyProvider) == True. + + From AWS ESDK specification: + "A master key MUST supply itself and MUST NOT supply any other master keys." + https://github.com/awslabs/aws-encryption-sdk-specification/blob/master/framework/master-key-interface.md#get-master-key + + The MasterKey class overrides MasterKeyProvider's `decrypt_data_key` method to correct this gap. + However, this modification suggests that this "is a" relationship is not entirely true. + + master_key_provider_from_master_key_specs expects to return a MasterKeyProvider, not a MasterKey. + master_key_provider_from_master_key_specs uses this class to always return a MasterKeyProvider + that wraps any MasterKeyProvider or MasterKey loaded from a spec. + """ + + _config_class = MasterKeyProviderConfig + provider_id = "aws-test-vectors-multi-master-key-provider" + _members = [] + + def add_key(self, key_provider): + """Add a MKP to the list of configured MKPs.""" + self._members.append(key_provider) + + def _new_master_key(self, key_id): + # This MKP does not have a key associated with it. + # ESDK-Python will find keys in _members. + raise InvalidKeyIdError() + + def master_key_provider_from_master_key_specs(keys, master_key_specs): # type: (KeysManifest, Iterable[MasterKeySpec]) -> MasterKeyProvider """Build and combine all master key providers identified by the provided specs and @@ -311,9 +340,18 @@ def master_key_provider_from_master_key_specs(keys, master_key_specs): :return: Master key provider combining all loaded master keys :rtype: MasterKeyProvider """ - master_keys = [spec.master_key(keys) for spec in master_key_specs] - primary = master_keys[0] - others = master_keys[1:] - for master_key in others: - primary.add_master_key_provider(master_key) - return primary + master_keys = [] + for spec in master_key_specs: + try: + master_keys.append(spec.master_key(keys)) + # If spec is not a valid master key + # (e.g. hierarchical keyring) + # do not make a master key + except KeyError: + pass + if len(master_keys) == 0: + return None + mkp = TestVectorsMultiMasterKeyProvider() + for master_key in master_keys: + mkp.add_key(master_key) + return mkp diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py b/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py new file mode 100644 index 000000000..3ed7913d2 --- /dev/null +++ b/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py @@ -0,0 +1,225 @@ +# 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. +"""Keyring Manifest handler. + +This REQUIRES the aws-cryptographic-material-providers library. +""" +import json +import attr + +# Ignore missing MPL for pylint, but the MPL is required for this example +# noqa pylint: disable=import-error +from aws_cryptography_materialproviders_test_vectors.smithygenerated.\ + aws_cryptography_materialproviderstestvectorkeys.models import ( + GetKeyDescriptionInput, + GetKeyDescriptionOutput, + TestVectorKeyringInput, + ) +from aws_cryptographic_material_providers.mpl import AwsCryptographicMaterialProviders +from aws_cryptographic_material_providers.mpl.config import MaterialProvidersConfig +from aws_cryptographic_material_providers.mpl.references import IKeyring +from aws_cryptographic_material_providers.mpl.models import CreateMultiKeyringInput + +import _dafny +from smithy_dafny_standard_library.internaldafny.generated import UTF8 + +# Ignore pylint not being able to read a module that requires the MPL +# pylint: disable=no-name-in-module +from awses_test_vectors.internal.mpl.keyvectors_provider import KeyVectorsProvider +from awses_test_vectors.internal.util import membership_validator +from awses_test_vectors.manifests.keys import KeysManifest # noqa: disable=F401 + +from .master_key import KNOWN_TYPES as MASTER_KEY_KNOWN_TYPES, MasterKeySpec + +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Iterable # noqa pylint: disable=unused-import + + from awses_test_vectors.internal.mypy_types import MASTER_KEY_SPEC # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + +KEYRING_ONLY_KNOWN_TYPES = ("aws-kms-hierarchy", ) + + +@attr.s +class KeyringSpec(MasterKeySpec): # pylint: disable=too-many-instance-attributes + """AWS Encryption SDK master key specification utilities. + + Described in AWS Crypto Tools Test Vector Framework features #0003 and #0004. + + :param str type_name: Master key type name + :param str key_name: Name of key in keys spec + :param str provider_id: Master key provider ID + :param str encryption_algorithm: Wrapping key encryption algorithm (required for raw master keys) + :param str padding_algorithm: Wrapping key padding algorithm (required for raw master keys) + :param str padding_hash: Wrapping key padding hash (required for raw master keys) + """ + + type_name = attr.ib(validator=membership_validator( + set(MASTER_KEY_KNOWN_TYPES).union(set(KEYRING_ONLY_KNOWN_TYPES)) + )) + + @classmethod + def from_scenario(cls, spec): + # type: (MASTER_KEY_SPEC) -> KeyringSpec + """Load from a keyring specification. + + :param dict spec: Master key specification JSON + :return: Loaded master key specification + :rtype: MasterKeySpec + """ + return cls( + type_name=spec["type"], + key_name=spec.get("key"), + default_mrk_region=spec.get("default-mrk-region"), + discovery_filter=cls._discovery_filter_from_spec(spec.get("aws-kms-discovery-filter")), + provider_id=spec.get("provider-id"), + encryption_algorithm=spec.get("encryption-algorithm"), + padding_algorithm=spec.get("padding-algorithm"), + padding_hash=spec.get("padding-hash"), + ) + + def keyring(self, keys_uri, mode): # noqa: C901 + # pylint: disable=too-many-branches + # type: (KeysManifest) -> IKeyring + """Build a keyring using this specification. + :param str keys_uri: Path to the keys manifest + """ + keyvectors = KeyVectorsProvider.get_keyvectors(keys_path=keys_uri) + + # Variable to flag whether we changed anything in weird hack #1. + # Signals to weird hack #2 whether it should execute. + changed_key_name_from_private_to_public = False + + # Construct the input to KeyVectorsConfig + input_kwargs = { + "type": self.type_name, + "key": self.key_name, + "provider-id": self.provider_id, + "encryption-algorithm": self.encryption_algorithm, + } + + if self.padding_algorithm is not None and self.padding_algorithm != "": + input_kwargs["padding-algorithm"] = self.padding_algorithm + if self.padding_hash is not None: + input_kwargs["padding-hash"] = self.padding_hash + if self.default_mrk_region is not None: + input_kwargs["default-mrk-region"] = self.default_mrk_region + if self.discovery_filter is not None: + input_kwargs["aws-kms-discovery-filter"] = {} + if self.discovery_filter.partition is not None: + input_kwargs["aws-kms-discovery-filter"]["partition"] = self.discovery_filter.partition + if self.discovery_filter.account_ids is not None: + input_kwargs["aws-kms-discovery-filter"]["account-ids"] = self.discovery_filter.account_ids + + if input_kwargs["type"] == "raw" \ + and input_kwargs["encryption-algorithm"] == "rsa": + # Weird hack #1: + # Gets public key for encryption instead of private key. + # + # If generating decrypt vectors (i.e. encrypting) + # and the manifest specified an RSA private key, + # change the input to KeyVectors to a public key. + # KeyVectors requires a public key to encrypt. + # If this is not done, then keyring.OnEncrypt fails with + # "A RawRSAKeyring without a public key cannot provide OnEncrypt" + if input_kwargs["key"] == "rsa-4096-private" \ + and mode in ("decrypt-generate", "encrypt"): + changed_key_name_from_private_to_public = True + input_kwargs["key"] = "rsa-4096-public" + # Specify default padding-hash + if "padding-hash" not in input_kwargs: + input_kwargs["padding-hash"] = "sha1" + + # stringify the dict + input_as_string = json.dumps(input_kwargs) + # convert to unicode code point (expected representation) + encoded_json = [ord(c) for c in input_as_string] + + output: GetKeyDescriptionOutput = keyvectors.get_key_description( + GetKeyDescriptionInput(json=encoded_json) + ) + + keyring: IKeyring = keyvectors.create_test_vector_keyring( + TestVectorKeyringInput( + key_description=output.key_description + ) + ) + + # Weird hack #2: + # Sets keyProviderInfo to "private" even though the material is "public". + # + # Weird hack #1 allows the encrypting keyring to be created with a public key. + # However, it also changes the keyName of the encrypting keyring. + # This hack changes it back. + # + # If this is not done, then decryption fails + # (for BOTH native master keys and MPL keyrings) + # with error + # native master keys: "Unable to decrypt any data key" + # MPL: "Raw RSA Key was unable to decrypt any encrypted data key" + # + # Digging, the keyring is unable to decrypt in the MPL + # because the EDK keyProviderInfo differs from the keyring keyName, + # and this check fails: + # https://github.com/aws/aws-cryptographic-material-providers-library/blob/bd549c88cefc93ba8a2d204bd23134b3b12c69fb/AwsCryptographicMaterialProviders/dafny/AwsCryptographicMaterialProviders/src/Keyrings/RawRSAKeyring.dfy#L382 + # due to the two variables not being equal: + # edk.keyProviderInfo='rsa-4096-public' + # keyring.keyName='rsa-4096-private' + # + # Changing the encrypting keyring's keyName back to 'rsa-4096-private' + # sets any EDKs this keyring encrypts to now have + # keyName="rsa-4096-private". + # However, keyvectors has still retrieved the public key material to encrypt with. + # So it any EDKs it encrypts will use the public material, but have keyName="rsa-4096-private". + # + # This configuration seems to be correct, because + # all of the test vectors (master keys and MPL) pass with these two hacks. + # But this seems weird, and we didn't have to do this in Java. + if hasattr(keyring, "_impl"): # pylint: disable=protected-access + if hasattr(keyring._impl, "_keyName"): # pylint: disable=protected-access + if keyring._impl._keyName == UTF8.default__.Encode(_dafny.Seq("rsa-4096-public")).value \ + and mode in ("decrypt-generate", "encrypt"): # pylint: disable=protected-access + if changed_key_name_from_private_to_public: + # pylint: disable=protected-access + keyring._impl._keyName = UTF8.default__.Encode(_dafny.Seq("rsa-4096-private")).value + + return keyring + + +def keyring_from_master_key_specs(keys_uri, master_key_specs, mode): + # type: (str, list[KeyringSpec]) -> IKeyring + """Build and combine all keyrings identified by the provided specs and + using the provided keys. + + :param str keys_uri: Path to the keys manifest + :param master_key_specs: Master key specs from which to load master keys + :type master_key_specs: iterable of MasterKeySpec + :return: Master key provider combining all loaded master keys + :rtype: IKeyring + """ + keyrings = [spec.keyring(keys_uri, mode) for spec in master_key_specs] + primary = keyrings[0] + others = keyrings[1:] + + mpl: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders( + MaterialProvidersConfig() + ) + multi_keyring: IKeyring = mpl.create_multi_keyring( + CreateMultiKeyringInput( + generator=primary, + child_keyrings=others + ) + ) + return multi_keyring diff --git a/test_vector_handlers/test/aws-crypto-tools-test-vector-framework b/test_vector_handlers/test/aws-crypto-tools-test-vector-framework index 1779b438f..fc793e257 160000 --- a/test_vector_handlers/test/aws-crypto-tools-test-vector-framework +++ b/test_vector_handlers/test/aws-crypto-tools-test-vector-framework @@ -1 +1 @@ -Subproject commit 1779b438f23cb356d5cab7ca40068dcb827b4cb1 +Subproject commit fc793e257f4a58ae49b92f95a519ba2c31ccff12 diff --git a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py index 6305a15da..58c0de4a4 100644 --- a/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py +++ b/test_vector_handlers/test/integration/commands/test_i_full_message_encrypt.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Integration tests for ``awses_test_vectors.commands``. """ diff --git a/test_vector_handlers/test/integration/integration_test_utils.py b/test_vector_handlers/test/integration/integration_test_utils.py index fbe6cf7b7..ef8d0fb88 100644 --- a/test_vector_handlers/test/integration/integration_test_utils.py +++ b/test_vector_handlers/test/integration/integration_test_utils.py @@ -1,15 +1,5 @@ -# 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. +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 """ Integration test utilities. """ diff --git a/test_vector_handlers/tox.ini b/test_vector_handlers/tox.ini index 10be9652d..7fe77ff50 100644 --- a/test_vector_handlers/tox.ini +++ b/test_vector_handlers/tox.ini @@ -2,9 +2,10 @@ envlist = # The test vectors depend on new features now, # so until release we can only effectively test the local version of the ESDK. - py{35,36,37,38,39,310}-awses_local, + py{37,38,39,310}-awses_local + py{311,312}-awses_local{,-mpl} # 1.2.0 and 1.2.max are being difficult because of attrs - bandit, doc8, readme, docs, + bandit, doc8, readme, {flake8,pylint}{,-tests}, # prone to false positives vulture @@ -30,14 +31,12 @@ envlist = # 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:base-command] -commands = pytest --basetemp={envtmpdir} -l --cov awses_test_vectors test/ {posargs} +commands = pytest --basetemp={envtmpdir} -l --cov awses_test_vectors test/ --ignore test/mpl {posargs} [testenv] passenv = @@ -50,9 +49,17 @@ passenv = sitepackages = False deps = -rtest/requirements.txt + # Install the MPL requirements if the `-mpl` suffix is present + mpl: -rrequirements_mpl.txt + # This is the filepath used in ESDK-Python's integration tests. + # You may need to change this to run this project locally. + mpl: scripts/aws-cryptographic-material-providers-library/TestVectorsAwsCryptographicMaterialProviders/runtimes/python .. commands = - {[testenv:base-command]commands} + awses_local: {[testenv:base-command]commands} + full_decrypt_generate: awses-full-message-decrypt-generate {posargs} + full_decrypt: awses-full-message-decrypt {posargs} + full_encrypt: awses-full-message-encrypt {posargs} [testenv:full-encrypt] basepython = python3 @@ -110,7 +117,6 @@ commands = flake8 \ src/awses_test_vectors/ \ setup.py \ - #doc/conf.py \ {posargs} [testenv:flake8-tests] @@ -152,7 +158,6 @@ commands = black --line-length 120 \ src/awses_test_vectors/ \ setup.py \ - #doc/conf.py \ test/ \ {posargs} @@ -182,7 +187,6 @@ deps = -r../dev_requirements/linter-requirements.txt commands = isort -rc \ src \ test \ - #doc \ setup.py \ {posargs} @@ -204,7 +208,7 @@ commands = [testenv:doc8] basepython = python3 deps = -r../dev_requirements/linter-requirements.txt -commands = doc8 doc/index.rst README.rst CHANGELOG.rst +commands = doc8 README.rst [testenv:readme] basepython = python3 @@ -246,21 +250,6 @@ 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 @@ -272,10 +261,8 @@ commands = python setup.py park basepython = python3 skip_install = true deps = - {[testenv:docs]deps} -r../dev_requirements/release-requirements.txt commands = - {[testenv:docs]commands} python setup.py sdist bdist_wheel [testenv:test-release] diff --git a/tox.ini b/tox.ini index d234dcd1f..28be94a63 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,24 @@ [tox] envlist = - py{36,37,38,39,310}-{local,integ,accept,examples}, nocmk, + # <3.11: run all non-MPL tests + py{38,39,310}-{local,integ,accept,examples}, + # >=3.11: run all tests with MPL installed and without MPL installed + # The `-mpl` suffix tells tox to install the MPL. + # In the case where the suffix IS NOT appended, + # this runs tests for the target version WITHOUT the MPL installed. + # In the case where the suffix IS appended, + # this runs tests for the target version WITH the MPL installed. + # This does not run any MPL-specific tests; it only runs non-MPL-specific + # tests in a test environment that also has the MPL. + py{311,312}-{local,integ,accept,examples}{,-mpl}, + # >=3.11: Run ONLY the MPL-specific tests. + # These must be separate from the above target, since + # these require the `-mpl` suffix. + # The `mpl` prefix specifies a separate target, + # i.e. `mpllocal` instead of `local`. + # `mplXXX` contains tests using MPL components. + py{311,312}-mpl{local,examples}-mpl + nocmk, bandit, doc8, readme, docs, {flake8,pylint}{,-tests,-examples}, isort-check, black-check, @@ -61,18 +79,28 @@ passenv = # Pass through custom pip config file settings PIP_CONFIG_FILE sitepackages = False -deps = -rdev_requirements/test-requirements.txt +deps = + -rdev_requirements/test-requirements.txt + # Install the MPL requirements if the `-mpl` suffix is present + mpl: -rrequirements_mpl.txt commands = - local: {[testenv:base-command]commands} test/ -m local - integ: {[testenv:base-command]commands} test/ -m integ - accept: {[testenv:base-command]commands} test/ -m accept - examples: {[testenv:base-command]commands} examples/test/ -m examples - all: {[testenv:base-command]commands} test/ examples/test/ + local: {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ + # MPL unit tests require the MPL to be installed + mpllocal: {[testenv:base-command]commands} test/ -m local + integ: {[testenv:base-command]commands} test/ -m integ --ignore test/mpl/ + accept: {[testenv:base-command]commands} test/ -m accept --ignore test/mpl/ + examples: {[testenv:base-command]commands} examples/test/legacy/ -m examples + # MPL keyring examples require a special IAM role; run these separately under a separate set of permissions + mplexamples: {[testenv:base-command]commands} examples/test/ -m examples --ignore examples/test/legacy/ + all: {[testenv:base-command]commands} test/ examples/test/legacy/ --ignore test/mpl/ + mplall: {[testenv:base-command]commands} test/ examples/test/ manual: {[testenv:base-command]commands} # Run code coverage on the unit tests [testenv:coverage] -commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local +commands = {[testenv:base-command]commands} --cov aws_encryption_sdk test/ -m local --ignore test/mpl/ +[testenv:mplcoverage-mpl] +commands = {[testenv:base-command]commands} --cov-config=.coveragercmpl --cov aws_encryption_sdk test/ -m local # Verify that local tests work without environment variables present [testenv:nocmk] @@ -84,7 +112,7 @@ passenv = setenv = ######################################################### deps = -rdev_requirements/test-requirements.txt -commands = {[testenv:base-command]commands} test/ -m local +commands = {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ # Collect requirements for use in upstream tests [testenv:freeze-upstream-requirements-base] @@ -94,25 +122,25 @@ recreate = True deps = commands = {toxinidir}/test/freeze-upstream-requirements.sh -# Freeze for Python 3.7 -[testenv:freeze-upstream-requirements-py37] -basepython = python3.7 +# Freeze for Python 3.11 +[testenv:freeze-upstream-requirements-py311] +basepython = python3.11 sitepackages = {[testenv:freeze-upstream-requirements-base]sitepackages} skip_install = {[testenv:freeze-upstream-requirements-base]skip_install} recreate = {[testenv:freeze-upstream-requirements-base]recreate} deps = {[testenv:freeze-upstream-requirements-base]deps} -commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-requirements-py37.txt +commands = {[testenv:freeze-upstream-requirements-base]commands} test/upstream-requirements-py311.txt # Test frozen upstream requirements [testenv:test-upstream-requirements-base] sitepackages = False recreate = True -commands = {[testenv:base-command]commands} test/ -m local +commands = {[testenv:base-command]commands} test/ -m local --ignore test/mpl/ -# Test frozen upstream requirements for Python 3.7 -[testenv:test-upstream-requirements-py37] -basepython = python3.7 -deps = -rtest/upstream-requirements-py37.txt +# Test frozen upstream requirements for Python 3.11 +[testenv:test-upstream-requirements-py311] +basepython = python3.11 +deps = -rtest/upstream-requirements-py311.txt sitepackages = {[testenv:test-upstream-requirements-base]sitepackages} recreate = {[testenv:test-upstream-requirements-base]recreate} commands = {[testenv:test-upstream-requirements-base]commands} @@ -141,10 +169,12 @@ deps = {[testenv:flake8]deps} commands = flake8 examples/src/ flake8 \ - # Ingore D103 missing docstring errors in tests (test names should be self-documenting) + # Ignore D103 missing docstring errors in tests (test names should be self-documenting) # 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 D103,E203,W503 \ + # copy-paste test for v3_default_cmm; intentionally not changing code + --per-file-ignores 'examples/test/legacy/v3_default_cmm.py: D205,D400,D401' \ examples/test/ [testenv:pylint] @@ -158,6 +188,7 @@ commands = --max-module-lines=1500 \ src/aws_encryption_sdk/ \ setup.py + --ignore-paths=src/aws_encryption_sdk/internal/mpl/ [testenv:pylint-examples] basepython = {[testenv:pylint]basepython}