├── doc ├── _static │ └── .gitignore ├── lib │ ├── tools │ │ ├── exceptions.rst │ │ ├── structures.rst │ │ ├── identifiers.rst │ │ ├── transform.rst │ │ └── index.rst │ ├── encrypted │ │ ├── item.rst │ │ ├── client.rst │ │ ├── config.rst │ │ ├── table.rst │ │ ├── resource.rst │ │ └── helpers.rst │ ├── materials_providers │ │ ├── metastore.rst │ │ ├── static.rst │ │ ├── aws_kms.rst │ │ ├── wrapped.rst │ │ ├── most_recent.rst │ │ ├── provider_stores.rst │ │ └── providers.rst │ ├── materials │ │ ├── raw.rst │ │ ├── wrapped.rst │ │ └── index.rst │ ├── delegated_keys │ │ ├── jce.rst │ │ └── index.rst │ └── internal.rst ├── index.rst └── conf.py ├── examples ├── requirements.txt ├── test │ ├── requirements.txt │ ├── __init__.py │ ├── pylintrc │ ├── examples_test_utils.py │ ├── test_wrapped_encrypted_examples.py │ ├── test_most_recent_provider_encrypted_examples.py │ └── test_aws_kms_encrypted_examples.py ├── src │ ├── pylintrc │ └── dynamodb_encryption_sdk_examples │ │ ├── __init__.py │ │ ├── aws_kms_encrypted_table.py │ │ ├── aws_kms_multi_region_key.py │ │ └── wrapped_symmetric_encrypted_table.py ├── __init__.py ├── tox.ini ├── setup.py └── README.rst ├── dev_requirements ├── ci-requirements.txt ├── doc-requirements.txt ├── release-requirements.txt ├── test-requirements.txt └── linter-requirements.txt ├── requirements.txt ├── NOTICE ├── codebuild ├── coverage │ └── coverage.yml ├── python3.10.yml ├── python3.11.yml ├── python3.7.yml ├── python3.8.yml ├── python3.9.yml ├── python3.12.yml └── release │ ├── prod-release.yml │ ├── test-release.yml │ └── validate.yml ├── MANIFEST.in ├── .github ├── CODEOWNERS ├── dependabot.yml ├── PULL_REQUEST_TEMPLATE.md ├── workflows │ ├── repo-sync.yml │ ├── ci_static-analysis.yaml │ └── ci_tests.yaml └── ISSUE_TEMPLATE │ └── amazon-dynamodb-encryption-client-issue.md ├── CODE_OF_CONDUCT.md ├── .gitignore ├── test ├── freeze-upstream-requirements.sh ├── upstream.md ├── vectors │ ├── material_description.json │ ├── encrypted_item │ │ └── ciphertext │ │ │ ├── java │ │ │ ├── metastore-aes-hmac-2.json │ │ │ ├── metastore-kms-3.json │ │ │ └── metastore-rsa-rsa-1.json │ │ │ └── python │ │ │ ├── metastore-aes-hmac-2.json │ │ │ ├── metastore-aws-kms-1.json │ │ │ └── metastore-rsa-rsa-1.json │ └── string_to_sign.json ├── unit │ ├── __init__.py │ ├── internal │ │ ├── __init__.py │ │ └── formatting │ │ │ ├── __init__.py │ │ │ └── test_attribute.py │ ├── encrypted │ │ ├── __init__.py │ │ └── test_encrypted.py │ ├── test_structures.py │ ├── material_providers │ │ ├── __init__.py │ │ ├── test_static.py │ │ └── test_wrapped.py │ ├── test_compatability.py │ └── unit_test_utils.py ├── __init__.py ├── acceptance │ ├── __init__.py │ └── encrypted │ │ ├── __init__.py │ │ ├── test_item.py │ │ └── test_table.py ├── functional │ ├── __init__.py │ ├── encrypted │ │ ├── __init__.py │ │ └── test_resource.py │ ├── internal │ │ ├── __init__.py │ │ ├── crypto │ │ │ ├── __init__.py │ │ │ └── test_authentication.py │ │ ├── formatting │ │ │ └── __init__.py │ │ └── test_str_ops.py │ ├── materials │ │ ├── __init__.py │ │ └── test_raw.py │ ├── delegated_keys │ │ ├── __init__.py │ │ └── test_jce.py │ ├── material_providers │ │ ├── __init__.py │ │ └── store │ │ │ ├── __init__.py │ │ │ └── test_meta.py │ ├── test_hypothesis_strategies.py │ ├── hypothesis_strategies.py │ └── test_identifiers.py ├── integration │ ├── __init__.py │ ├── encrypted │ │ ├── __init__.py │ │ ├── test_resource.py │ │ ├── test_table.py │ │ └── test_client.py │ ├── material_providers │ │ ├── __init__.py │ │ ├── store │ │ │ ├── __init__.py │ │ │ └── test_meta.py │ │ └── test_most_recent.py │ └── integration_test_utils.py ├── upstream-requirements-py311.txt ├── cloudformation │ └── tables.yaml ├── pylintrc ├── source-build-check.sh └── README.rst ├── .readthedocs.yaml ├── park.cfg ├── src ├── dynamodb_encryption_sdk │ ├── internal │ │ ├── __init__.py │ │ ├── crypto │ │ │ ├── __init__.py │ │ │ ├── jce_bridge │ │ │ │ └── __init__.py │ │ │ └── encryption.py │ │ ├── formatting │ │ │ ├── __init__.py │ │ │ ├── serialize │ │ │ │ └── __init__.py │ │ │ └── deserialize │ │ │ │ └── __init__.py │ │ ├── dynamodb_types.py │ │ ├── str_ops.py │ │ └── validators.py │ ├── __init__.py │ ├── transform.py │ ├── compatability.py │ ├── identifiers.py │ ├── material_providers │ │ ├── __init__.py │ │ ├── static.py │ │ └── store │ │ │ └── __init__.py │ ├── exceptions.py │ └── materials │ │ └── __init__.py └── pylintrc ├── VERSIONING.rst ├── SUPPORT_POLICY.rst ├── setup.py ├── setup.cfg └── CONTRIBUTING.md /doc/_static/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/requirements.txt: -------------------------------------------------------------------------------- 1 | dynamodb-encryption-sdk 2 | -------------------------------------------------------------------------------- /dev_requirements/ci-requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | tox==3.24.5 3 | -------------------------------------------------------------------------------- /examples/test/requirements.txt: -------------------------------------------------------------------------------- 1 | dynamodb-encryption-sdk 2 | pytest 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | boto3>=1.10.0 2 | cryptography>=3.4.6 3 | attrs>=17.4.0 4 | -------------------------------------------------------------------------------- /dev_requirements/doc-requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==4.4.0 2 | sphinx_rtd_theme==1.0.0 3 | -------------------------------------------------------------------------------- /doc/lib/tools/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ---------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.exceptions 5 | -------------------------------------------------------------------------------- /doc/lib/tools/structures.rst: -------------------------------------------------------------------------------- 1 | Structures 2 | ---------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.structures 5 | -------------------------------------------------------------------------------- /doc/lib/tools/identifiers.rst: -------------------------------------------------------------------------------- 1 | Identifiers 2 | ----------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.identifiers 5 | -------------------------------------------------------------------------------- /doc/lib/tools/transform.rst: -------------------------------------------------------------------------------- 1 | Transformers 2 | ------------ 3 | 4 | .. automodule:: dynamodb_encryption_sdk.transform 5 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | DynamoDB Encryption Client for Python 2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /doc/lib/encrypted/item.rst: -------------------------------------------------------------------------------- 1 | Item Encryptor 2 | -------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.encrypted.item 5 | -------------------------------------------------------------------------------- /doc/lib/encrypted/client.rst: -------------------------------------------------------------------------------- 1 | Encrypted Client 2 | ---------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.encrypted.client 5 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/metastore.rst: -------------------------------------------------------------------------------- 1 | MetaStore 2 | ========= 3 | 4 | .. automodule:: dynamodb_encryption_sdk.material_providers.store.meta 5 | -------------------------------------------------------------------------------- /doc/lib/encrypted/config.rst: -------------------------------------------------------------------------------- 1 | Cryptographic Configuration 2 | --------------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.encrypted 5 | -------------------------------------------------------------------------------- /doc/lib/encrypted/table.rst: -------------------------------------------------------------------------------- 1 | Encrypted Table Resource 2 | ------------------------ 3 | 4 | .. automodule:: dynamodb_encryption_sdk.encrypted.table 5 | -------------------------------------------------------------------------------- /doc/lib/materials/raw.rst: -------------------------------------------------------------------------------- 1 | Raw Cryptographic Materials 2 | --------------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.materials.raw 5 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/static.rst: -------------------------------------------------------------------------------- 1 | Static Provider 2 | --------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.material_providers.static 5 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/aws_kms.rst: -------------------------------------------------------------------------------- 1 | AWS KMS Provider 2 | ---------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.material_providers.aws_kms 5 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/wrapped.rst: -------------------------------------------------------------------------------- 1 | Wrapped Provider 2 | ---------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.material_providers.wrapped 5 | -------------------------------------------------------------------------------- /doc/lib/encrypted/resource.rst: -------------------------------------------------------------------------------- 1 | Encrypted Service Resource 2 | -------------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.encrypted.resource 5 | -------------------------------------------------------------------------------- /doc/lib/materials/wrapped.rst: -------------------------------------------------------------------------------- 1 | Wrapped Cryptographic Materials 2 | ------------------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.materials.wrapped 5 | -------------------------------------------------------------------------------- /dev_requirements/release-requirements.txt: -------------------------------------------------------------------------------- 1 | pypi-parker==0.1.2 2 | setuptools==66.1.1 3 | twine==3.8.0 4 | wheel==0.38.4 5 | #This is required for twine < 4.0 6 | packaging -------------------------------------------------------------------------------- /doc/lib/delegated_keys/jce.rst: -------------------------------------------------------------------------------- 1 | JCE Standard Name Delegated Key 2 | ------------------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.delegated_keys.jce 5 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/most_recent.rst: -------------------------------------------------------------------------------- 1 | Most Recent Provider 2 | -------------------- 3 | 4 | .. automodule:: dynamodb_encryption_sdk.material_providers.most_recent 5 | -------------------------------------------------------------------------------- /dev_requirements/test-requirements.txt: -------------------------------------------------------------------------------- 1 | hypothesis==6.31.6 2 | mock==4.0.3 3 | moto==3.0.2 4 | pytest==7.2.1 5 | pytest-cov==3.0.0 6 | pytest-mock==3.10.0 7 | pytest-xdist==3.2.0 8 | boto3==1.28.38 9 | botocore==1.31.38 10 | -------------------------------------------------------------------------------- /doc/lib/delegated_keys/index.rst: -------------------------------------------------------------------------------- 1 | Delegated Keys 2 | ============== 3 | 4 | .. toctree:: 5 | 6 | jce 7 | 8 | .. autosummary:: 9 | 10 | dynamodb_encryption_sdk.delegated_keys 11 | dynamodb_encryption_sdk.delegated_keys.jce 12 | -------------------------------------------------------------------------------- /codebuild/coverage/coverage.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "coverage" 6 | 7 | phases: 8 | install: 9 | runtime-versions: 10 | python: latest 11 | build: 12 | commands: 13 | - pip install "tox < 4.0" 14 | - tox 15 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/provider_stores.rst: -------------------------------------------------------------------------------- 1 | Provider Stores 2 | =============== 3 | 4 | .. toctree:: 5 | 6 | metastore 7 | 8 | .. autosummary:: 9 | 10 | dynamodb_encryption_sdk.material_providers.store 11 | dynamodb_encryption_sdk.material_providers.store.meta 12 | -------------------------------------------------------------------------------- /doc/lib/tools/index.rst: -------------------------------------------------------------------------------- 1 | Tools 2 | ===== 3 | 4 | .. toctree:: 5 | 6 | identifiers 7 | structures 8 | transform 9 | 10 | .. autosummary:: 11 | 12 | dynamodb_encryption_sdk.identifiers 13 | dynamodb_encryption_sdk.structures 14 | dynamodb_encryption_sdk.transform 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include CHANGELOG.rst 3 | include LICENSE 4 | include requirements.txt 5 | 6 | recursive-include doc * 7 | recursive-exclude doc .DS_Store *.pyc 8 | prune doc/build 9 | prune doc/lib/generated 10 | 11 | recursive-include test * 12 | recursive-exclude test .DS_Store *.pyc -------------------------------------------------------------------------------- /doc/lib/materials/index.rst: -------------------------------------------------------------------------------- 1 | Cryptographic Materials 2 | ----------------------- 3 | 4 | .. toctree:: 5 | 6 | wrapped 7 | raw 8 | 9 | .. autosummary:: 10 | 11 | dynamodb_encryption_sdk.materials 12 | dynamodb_encryption_sdk.materials.raw 13 | dynamodb_encryption_sdk.materials.wrapped 14 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Each line is a file pattern followed by one or more owners. 2 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 3 | 4 | # Default code owner for everything is our aws-crypto-tools group 5 | * @aws/aws-crypto-tools 6 | -------------------------------------------------------------------------------- /doc/lib/encrypted/helpers.rst: -------------------------------------------------------------------------------- 1 | Helper Clients 2 | ============== 3 | 4 | .. toctree:: 5 | 6 | table 7 | resource 8 | client 9 | 10 | .. autosummary:: 11 | 12 | dynamodb_encryption_sdk.encrypted.table 13 | dynamodb_encryption_sdk.encrypted.resource 14 | dynamodb_encryption_sdk.encrypted.client 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # master 4 | - package-ecosystem: "pip" 5 | directory: "/dev_requirements" 6 | schedule: 7 | interval: "daily" 8 | 9 | # Github Actions 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | *.pyo 4 | *~ 5 | .DS_Store 6 | .tox 7 | /.cache* 8 | /.coverage* 9 | /build 10 | /doc/generated/* 11 | /doc/lib/generated/* 12 | /runpy 13 | __pycache__ 14 | build 15 | dist 16 | docs/build 17 | .python-version 18 | .mypy_cache 19 | .hypothesis 20 | .pytest_cache 21 | 22 | #PyCharm 23 | .idea/ 24 | venv/ 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | 8 | # Check any applicable: 9 | - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. 10 | 11 | -------------------------------------------------------------------------------- /test/freeze-upstream-requirements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Minimal wrapper script for upstream requirements install and freeze. 3 | # We do this here rather than as tox commands because tox does not support output redirection. 4 | if [ -z ${1} ]; then 5 | exit 1 6 | fi 7 | 8 | pip install -r requirements.txt 9 | pip install -r dev_requirements/test-requirements.txt 10 | pip freeze > ${1} 11 | -------------------------------------------------------------------------------- /dev_requirements/linter-requirements.txt: -------------------------------------------------------------------------------- 1 | bandit==1.7.2 2 | black==22.3.0 3 | doc8==0.10.1 4 | flake8==4.0.1 5 | flake8-docstrings==1.7.0 6 | flake8-isort==4.1.1 7 | # https://github.com/JBKahn/flake8-print/pull/30 8 | flake8-print==5.0.0 9 | isort==5.12.0 10 | pylint==2.12.2 11 | pyflakes==2.4.0 12 | # https://github.com/PyCQA/pydocstyle/issues/375 13 | pydocstyle==3.0.0 14 | readme_renderer==34.0 15 | seed-isort-config==2.2.0 16 | vulture==2.3 17 | -------------------------------------------------------------------------------- /doc/lib/materials_providers/providers.rst: -------------------------------------------------------------------------------- 1 | Cryptographic Materials Providers 2 | ================================= 3 | 4 | .. toctree:: 5 | 6 | aws_kms 7 | wrapped 8 | most_recent 9 | static 10 | 11 | .. autosummary:: 12 | 13 | dynamodb_encryption_sdk.material_providers 14 | dynamodb_encryption_sdk.material_providers.wrapped 15 | dynamodb_encryption_sdk.material_providers.most_recent 16 | dynamodb_encryption_sdk.material_providers.static 17 | -------------------------------------------------------------------------------- /codebuild/python3.10.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py310-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: 3.10 15 | build: 16 | commands: 17 | - pip install "tox < 4.0" 18 | - tox 19 | -------------------------------------------------------------------------------- /codebuild/python3.11.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py311-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: 3.11 15 | build: 16 | commands: 17 | - pip install "tox < 4.0" 18 | - tox 19 | -------------------------------------------------------------------------------- /codebuild/python3.7.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py37-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: 3.7 15 | build: 16 | commands: 17 | - pip install "tox < 4.0" 18 | - tox 19 | -------------------------------------------------------------------------------- /codebuild/python3.8.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py38-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: 3.8 15 | build: 16 | commands: 17 | - pip install "tox < 4.0" 18 | - tox 19 | -------------------------------------------------------------------------------- /codebuild/python3.9.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py39-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: 3.9 15 | build: 16 | commands: 17 | - pip install "tox < 4.0" 18 | - tox 19 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the doc/ directory with Sphinx 9 | sphinx: 10 | configuration: doc/conf.py 11 | 12 | # Don't need to build documentation for test vectors or any other 13 | # sub modules 14 | submodules: 15 | exclude: all 16 | 17 | python: 18 | version: 3.8 19 | install: 20 | - requirements: dev_requirements/doc-requirements.txt 21 | - method: setuptools 22 | path: . 23 | -------------------------------------------------------------------------------- /test/upstream.md: -------------------------------------------------------------------------------- 1 | AWS Crypto Tools maintains `test/upstream-requirements-py.txt` in our Python products such that 2 | our Cryptographic Primitive Provider for Python ([pyca/cryptography](https://github.com/pyca/cryptography)) 3 | may execute downstream tests against AWS Crypto Tools Python products. 4 | These files allow pyca to install and test the Crypto Tools products. 5 | Additionally, Crypto Tools should maintain a test configuration that can be completed without using any AWS resources. 6 | If Crypto Tools needs to contact pyca about this expectation, 7 | they should cut a issue to the pyca/cryptography repo. 8 | -------------------------------------------------------------------------------- /test/vectors/material_description.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "material_description": { 4 | "A": "Field A", 5 | "B": "Field B", 6 | "C": "123" 7 | }, 8 | "serialized": "AAAAAAAAAAFBAAAAB0ZpZWxkIEEAAAABQgAAAAdGaWVsZCBCAAAAAUMAAAADMTIz" 9 | }, 10 | { 11 | "material_description": { 12 | "B": "Field B", 13 | "C": "123", 14 | "A": "Field A" 15 | }, 16 | "serialized": "AAAAAAAAAAFBAAAAB0ZpZWxkIEEAAAABQgAAAAdGaWVsZCBCAAAAAUMAAAADMTIz" 17 | }, 18 | { 19 | "material_description": {}, 20 | "serialized": "AAAAAA==" 21 | } 22 | ] -------------------------------------------------------------------------------- /test/unit/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /examples/src/pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | # Disabling messages that we either don't care about for tests or are necessary to break for tests. 3 | disable = 4 | duplicate-code, # these examples often feature similar code 5 | too-many-locals, # for these examples, we prioritize keeping everything together for simple readability 6 | consider-using-f-string, # Not supported in Python 3.5 7 | 8 | [BASIC] 9 | # Allow function names up to 50 characters 10 | function-rgx = [a-z_][a-z0-9_]{2,50}$ 11 | 12 | [DESIGN] 13 | max-args = 10 14 | 15 | [FORMAT] 16 | max-line-length = 120 17 | 18 | [REPORTS] 19 | msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 20 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Stub to allow relative imports between test groups.""" 14 | -------------------------------------------------------------------------------- /test/acceptance/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/integration/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/unit/internal/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Stub to allow relative imports of examples from tests.""" 14 | -------------------------------------------------------------------------------- /test/unit/encrypted/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/acceptance/encrypted/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/encrypted/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/internal/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/materials/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/integration/encrypted/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /examples/test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Stub module indicator to make linter configuration simpler.""" 14 | -------------------------------------------------------------------------------- /test/functional/delegated_keys/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/internal/crypto/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/unit/internal/formatting/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/internal/formatting/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/material_providers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/integration/material_providers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/functional/material_providers/store/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /test/integration/material_providers/store/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Dummy stub to make linters work better.""" 14 | -------------------------------------------------------------------------------- /examples/src/dynamodb_encryption_sdk_examples/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Stub module indicator to make linter configuration simpler.""" 14 | __version__ = "1.0.0" 15 | -------------------------------------------------------------------------------- /park.cfg: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | version: 0.0.2 3 | author: Amazon Web Services 4 | author_email: aws-cryptools@amazon.com 5 | url: https://aws-dynamodb-encryption-python.readthedocs.io/en/latest/ 6 | description: Did you mean to install dynamodb-encryption-sdk? 7 | long_description: 8 | This package has been parked by {author} to protect you against packages 9 | adopting names that might be common mistakes when looking for ours. You probably 10 | wanted to install dynamodb-encryption-sdk. For more information, see {url}. 11 | description_keys: 12 | author 13 | url 14 | 15 | [names] 16 | dynamodb-encryption: 17 | dynamodb-encyrption: 18 | dynamodb-encrpytion: 19 | dynamodb-encrytpion: 20 | dynamodbencryptionsdk: 21 | dynamodbencyrptionsdk: 22 | dynamodbencrpytionsdk: 23 | dynamodbencrytpionsdk: 24 | -------------------------------------------------------------------------------- /codebuild/python3.12.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | TOXENV: "py312-integ-slow" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- 9 | arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 10 | 11 | phases: 12 | install: 13 | runtime-versions: 14 | python: latest 15 | build: 16 | commands: 17 | - cd /root/.pyenv/plugins/python-build/../.. && git pull && cd - 18 | - pyenv install --skip-existing 3.12.0 19 | - pyenv local 3.12.0 20 | - pip install --upgrade pip 21 | - pip install setuptools 22 | - pip install "tox < 4.0" 23 | - tox 24 | -------------------------------------------------------------------------------- /test/upstream-requirements-py311.txt: -------------------------------------------------------------------------------- 1 | attrs==22.2.0 2 | boto3==1.20.51 3 | botocore==1.23.51 4 | certifi==2023.7.22 5 | cffi==1.15.1 6 | charset-normalizer==3.0.1 7 | coverage==7.1.0 8 | cryptography==42.0.4 9 | execnet==1.9.0 10 | hypothesis==6.31.6 11 | idna==3.4 12 | iniconfig==2.0.0 13 | Jinja2==3.1.3 14 | jmespath==0.10.0 15 | MarkupSafe==2.1.2 16 | mock==4.0.3 17 | moto==3.0.2 18 | packaging==23.0 19 | pluggy==1.0.0 20 | py==1.11.0 21 | pycparser==2.21 22 | pytest==7.2.1 23 | pytest-cov==3.0.0 24 | pytest-forked==1.6.0 25 | pytest-mock==3.10.0 26 | pytest-xdist==3.2.0 27 | python-dateutil==2.8.2 28 | pytz==2022.7.1 29 | requests==2.31.0 30 | responses==0.22.0 31 | s3transfer==0.5.2 32 | six==1.16.0 33 | sortedcontainers==2.4.0 34 | toml==0.10.2 35 | types-toml==0.10.8.5 36 | urllib3==1.26.18 37 | Werkzeug==2.3.8 38 | xmltodict==0.13.0 39 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | *** 4 | API 5 | *** 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | :caption: Encryption Helpers 10 | 11 | lib/encrypted/config.rst 12 | lib/encrypted/helpers 13 | lib/encrypted/item 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | :caption: Key Management 18 | 19 | lib/materials_providers/providers 20 | lib/materials_providers/provider_stores 21 | lib/materials/index 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | :caption: Cryptographic Operators 26 | 27 | lib/delegated_keys/index 28 | 29 | .. toctree:: 30 | :maxdepth: 2 31 | :caption: Additional Resources 32 | 33 | lib/tools/index 34 | lib/tools/exceptions 35 | 36 | .. toctree:: 37 | :maxdepth: 1 38 | :caption: Informational Only 39 | 40 | lib/internal 41 | 42 | .. include:: ../CHANGELOG.rst 43 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Internal implementation details. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | -------------------------------------------------------------------------------- /.github/workflows/repo-sync.yml: -------------------------------------------------------------------------------- 1 | name: Repo Sync 2 | 3 | on: 4 | workflow_dispatch: # allows triggering this manually through the Actions UI 5 | 6 | jobs: 7 | repo-sync: 8 | name: Repo Sync 9 | environment: repo-sync 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: repo-sync/github-sync@v2 14 | name: Sync repo to branch 15 | with: 16 | source_repo: ${{ secrets.SOURCE_REPO }} 17 | source_branch: master 18 | destination_branch: ${{ secrets.INTERMEDIATE_BRANCH }} 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | - uses: repo-sync/pull-request@v2 21 | name: Create pull request 22 | with: 23 | source_branch: ${{ secrets.INTERMEDIATE_BRANCH }} 24 | destination_branch: master 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/crypto/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Inner cryptographic components. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/formatting/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Data formatting utilities for the DynamoDB Encryption Client. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | -------------------------------------------------------------------------------- /src/pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | # Disabling messages that we either don't care about for tests or are necessary to break for tests. 3 | disable = 4 | bad-continuation, # we let black handle this 5 | ungrouped-imports, # we let isort handle this 6 | duplicate-code, # causes lots of problems with implementations of common interfaces 7 | # All below are disabled because we need to support Python 2 8 | useless-object-inheritance, 9 | raise-missing-from, 10 | super-with-arguments, 11 | consider-using-f-string, 12 | 13 | [BASIC] 14 | # Allow function names up to 50 characters 15 | function-rgx = [a-z_][a-z0-9_]{2,50}$ 16 | # Whitelist argument names: iv 17 | argument-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$) 18 | # Whitelist variable names: iv 19 | variable-rgx = ([a-z_][a-z0-9_]{2,30}$)|(^iv$) 20 | 21 | [DESIGN] 22 | max-args = 10 23 | 24 | [FORMAT] 25 | max-line-length = 120 26 | 27 | [REPORTS] 28 | msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 29 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/crypto/jce_bridge/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Components to provide cryptographic primitives based on JCE Standard Names. 14 | https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html 15 | 16 | .. warning:: 17 | No guarantee is provided on the modules and APIs within this 18 | namespace staying consistent. Directly reference at your own risk. 19 | """ 20 | -------------------------------------------------------------------------------- /examples/test/pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | # Disabling messages that we either don't care about for tests or are necessary to break for tests. 3 | disable = 4 | invalid-name, # we prefer long, descriptive, names for tests 5 | missing-docstring, # we generally don't write docstrings for tests 6 | wrong-import-position, # pylint does not appear to identify unknown modules as non-standard-library. 7 | # flake8 tests for this as well and does treat them properly 8 | import-error, # because the examples are not actually in a module, 9 | # sys.path is patched to find tests and test utils. 10 | # pylint does not recognize this 11 | duplicate-code, # tests for similar things tend to be similar 12 | redefined-outer-name, # raises false positives with fixtures 13 | consider-using-f-string, # Not supported in Python 3.5 14 | 15 | [DESIGN] 16 | max-args = 10 17 | 18 | [FORMAT] 19 | max-line-length = 120 20 | 21 | [REPORTS] 22 | msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 23 | -------------------------------------------------------------------------------- /examples/test/examples_test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper utilities for use while testing examples. 14 | 15 | isort:skip_file 16 | """ 17 | import os 18 | import sys 19 | 20 | os.environ["AWS_ENCRYPTION_SDK_EXAMPLES_TESTING"] = "yes" 21 | sys.path.extend([os.sep.join([os.path.dirname(__file__), "..", "..", "test", "integration"])]) 22 | 23 | # fmt: off 24 | from integration_test_utils import cmk_arn, cmk_mrk_arn, ddb_table_name, second_cmk_mrk_arn # noqa pylint: disable=unused-import 25 | # fmt: on 26 | -------------------------------------------------------------------------------- /test/cloudformation/tables.yaml: -------------------------------------------------------------------------------- 1 | Description: Create DynamoDB tables for use in the DynamoDB Encryption Client for Python integration tests. 2 | Resources: 3 | TestTable: 4 | Type: AWS::DynamoDB::Table 5 | Properties: 6 | AttributeDefinitions: 7 | - 8 | AttributeName: partition_attribute 9 | AttributeType: S 10 | - 11 | AttributeName: sort_attribute 12 | AttributeType: N 13 | KeySchema: 14 | - 15 | AttributeName: partition_attribute 16 | KeyType: HASH 17 | - 18 | AttributeName: sort_attribute 19 | KeyType: RANGE 20 | ProvisionedThroughput: 21 | ReadCapacityUnits: 100 22 | WriteCapacityUnits: 100 23 | Outputs: 24 | TestTableName: 25 | Description: Name of test table. 26 | Value: !Ref TestTable 27 | Export: 28 | Name: DynamoDB-Encryption-Client-Test-Table -------------------------------------------------------------------------------- /test/unit/test_structures.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit tests for ``dynamodb_encryption_sdk.structures``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.structures import EncryptionContext 17 | 18 | 19 | @pytest.mark.parametrize("operator", (repr, str)) 20 | def test_encryption_context_repr(operator): 21 | value = EncryptionContext(attributes={"unique_name": {"S": "unique_value"}}) 22 | 23 | test = operator(value) 24 | 25 | assert "unique_name" not in test 26 | assert "unique_value" not in test 27 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/dynamodb_types.py: -------------------------------------------------------------------------------- 1 | """Types used with mypy for DynamoDB items and attributes. 2 | 3 | .. warning:: 4 | No guarantee is provided on the modules and APIs within this 5 | namespace staying consistent. Directly reference at your own risk. 6 | """ 7 | # constant naming for types so pylint: disable=invalid-name 8 | from typing import Any, AnyStr, ByteString, Dict, List, Text 9 | 10 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 11 | ATTRIBUTE = Dict[Text, Any] # narrow this down 12 | ITEM = Dict[Text, ATTRIBUTE] 13 | RAW_ATTRIBUTE = ITEM 14 | NULL = bool # DynamoDB TypeSerializer converts none to {'NULL': True} 15 | BOOLEAN = bool 16 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 17 | NUMBER = int # This misses long on Python 2...figure out something for this 18 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 19 | STRING = AnyStr # can be unicode but should not be bytes 20 | BINARY = ByteString 21 | BINARY_ATTRIBUTE = Dict[Text, BINARY] 22 | SET = List # DynamoDB TypeSerializer converts sets into lists 23 | MAP = RAW_ATTRIBUTE 24 | LIST = List[RAW_ATTRIBUTE] 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/amazon-dynamodb-encryption-client-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Amazon DynamoDB Encryption Client Issue 3 | about: Amazon DynamoDB Encryption Client Issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Security issue notifications 11 | 12 | If you discover a potential security issue in the Amazon DynamoDB Encryption Client we ask that you notify AWS Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. 13 | 14 | ### Problem: 15 | 16 | A short description of what the problem is and why we need to fix it. Add reproduction steps if necessary. 17 | 18 | ### Solution: 19 | 20 | A description of the possible solution in terms of DynamoDB Encryption Client architecture. 21 | 22 | ### Out of scope: 23 | 24 | Is there anything the solution will intentionally NOT address? 25 | 26 | [//]: # (NOTE: If you believe this might be a security issue, please email aws-security@amazon.com instead of creating a GitHub issue. For more details, see the AWS Vulnerability Reporting Guide: https://aws.amazon.com/security/vulnerability-reporting/ ) 27 | -------------------------------------------------------------------------------- /test/pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | # Disabling messages that we either don't care about for tests or are necessary to break for tests. 3 | disable = 4 | invalid-name, # we prefer long, descriptive, names for tests 5 | missing-docstring, # we don't write docstrings for tests 6 | bad-continuation, # we let black handle this 7 | ungrouped-imports, # we let isort handle this 8 | no-member, # raised on patched objects with mock checks 9 | duplicate-code, # unit tests for similar things tend to be similar 10 | protected-access, # raised when calling _ methods 11 | redefined-outer-name, # raised when using pytest-mock 12 | unused-argument, # raised when patches and fixtures are needed but not called 13 | no-self-use, # raised on Classes in tests used for logically grouping tests 14 | # All below are disabled because we need to support Python 2 15 | useless-object-inheritance, 16 | raise-missing-from, 17 | super-with-arguments, 18 | consider-using-f-string, 19 | 20 | [DESIGN] 21 | max-args = 10 22 | 23 | [FORMAT] 24 | max-line-length = 120 25 | 26 | [REPORTS] 27 | msg-template = {path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 28 | -------------------------------------------------------------------------------- /codebuild/release/prod-release.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | BRANCH: "master" 6 | secrets-manager: 7 | TWINE_USERNAME: PyPiAdmin:username 8 | TWINE_PASSWORD: PyPiAdmin:password 9 | 10 | phases: 11 | install: 12 | commands: 13 | - pip install "tox < 4.0" 14 | - pip install --upgrade pip 15 | runtime-versions: 16 | python: latest 17 | pre_build: 18 | commands: 19 | - git checkout $COMMIT_ID 20 | - FOUND_VERSION=$(sed -n 's/__version__ = "\(.*\)"/\1/p' src/dynamodb_encryption_sdk/identifiers.py) 21 | - | 22 | if expr ${FOUND_VERSION} != ${VERSION}; then 23 | echo "identifiers.py version (${FOUND_VERSION}) does not match expected version (${VERSION}), stopping" 24 | exit 1; 25 | fi 26 | build: 27 | commands: 28 | - tox -e park 29 | - tox -e release 30 | 31 | batch: 32 | fast-fail: true 33 | build-graph: 34 | - identifier: release_to_prod 35 | - identifier: validate_prod_release 36 | depend-on: 37 | - release_to_prod 38 | buildspec: codebuild/release/validate.yml 39 | env: 40 | variables: 41 | PIP_INDEX_URL: https://pypi.python.org/simple/ 42 | -------------------------------------------------------------------------------- /.github/workflows/ci_static-analysis.yaml: -------------------------------------------------------------------------------- 1 | # This workflow runs static analysis checks on pull requests. 2 | name: static analysis 3 | 4 | on: 5 | pull_request: 6 | push: 7 | # Run once a day 8 | schedule: 9 | - cron: '0 0 * * *' 10 | 11 | jobs: 12 | analysis: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | category: 18 | # Disabled pending completion of integration 19 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 20 | # - mypy-py3 21 | - bandit 22 | - doc8 23 | - readme 24 | - docs 25 | - flake8 26 | - pylint 27 | - flake8-tests 28 | - flake8-examples 29 | - pylint-tests 30 | - pylint-examples 31 | - black-check 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: actions/setup-python@v5 35 | with: 36 | python-version: 3.8 37 | - run: | 38 | python -m pip install --upgrade pip 39 | pip install --upgrade -r dev_requirements/ci-requirements.txt 40 | - name: check 41 | env: 42 | TOXENV: ${{ matrix.category }} 43 | run: tox -- -vv 44 | -------------------------------------------------------------------------------- /test/integration/material_providers/test_most_recent.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Load testing using MostRecentProvider and MetaStore.""" 14 | import pytest 15 | 16 | from ..integration_test_utils import ddb_table_name # noqa=F401 pylint: disable=unused-import 17 | from ..integration_test_utils import temp_metastore # noqa=F401 pylint: disable=unused-import 18 | from ..integration_test_utils import functional_test_utils 19 | 20 | pytestmark = [pytest.mark.integ, pytest.mark.ddb_integ] 21 | 22 | 23 | def test_cache_use_encrypt(temp_metastore, ddb_table_name, caplog): 24 | functional_test_utils.check_metastore_cache_use_encrypt(temp_metastore, ddb_table_name, caplog) 25 | -------------------------------------------------------------------------------- /examples/tox.ini: -------------------------------------------------------------------------------- 1 | # Basic environments for running examples against various versions of Python 2 | 3 | [tox] 4 | envlist = 5 | py{3,37,38,39}-examples 6 | 7 | [testenv:base-command] 8 | commands = python -m pytest --basetemp={envtmpdir} -l {posargs} 9 | 10 | [testenv] 11 | passenv = 12 | # Identifies AWS KMS key id to use in integration tests 13 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID \ 14 | # Identifes AWS KMS Multi-Region key ids to use in examples \ 15 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID \ 16 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2 \ 17 | # DynamoDB Table to use in integration tests 18 | DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME \ 19 | # Pass through AWS credentials 20 | AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \ 21 | # AWS Role access in CodeBuild is via the container URI 22 | AWS_CONTAINER_CREDENTIALS_RELATIVE_URI \ 23 | # Pass through the default AWS region 24 | AWS_DEFAULT_REGION 25 | 26 | sitepackages = False 27 | deps = -rtest/requirements.txt 28 | # 'download' forces tox to always upgrade pip to the latest 29 | download = true 30 | commands = 31 | examples: {[testenv:base-command]commands} test/ -m "examples" 32 | -------------------------------------------------------------------------------- /test/source-build-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Verify that tests can be successfully run from the source build. 3 | # 4 | #NOTE: Humans should not run this file directly. If you want to run this check, use the 5 | # tox to run the "sourcebuildcheck" test environment. 6 | 7 | WORKINGDIR=${1} 8 | DISTDIR=${2} 9 | 10 | echo "Performing source build check" 11 | echo "Using working directory ${WORKINGDIR}" 12 | echo "Using dist directory ${DISTDIR}" 13 | 14 | echo "Locating the source build and copying it into the working directory." 15 | DISTFILE=$(ls ${DISTDIR}/dynamodb-encryption-sdk-*.tar.gz | tail -1) 16 | echo "Found source build at ${DISTFILE}" 17 | cp ${DISTFILE} ${WORKINGDIR} 18 | 19 | echo "Extracting the source build." 20 | cd ${WORKINGDIR} 21 | NEWDISTFILE=$(ls dynamodb-encryption-sdk-*.tar.gz | tail -1) 22 | echo "Using distfile ${NEWDISTFILE}" 23 | tar xzvf ${NEWDISTFILE} 24 | rm ${NEWDISTFILE} 25 | EXTRACTEDDIR=$(ls | tail -1) 26 | cd ${EXTRACTEDDIR} 27 | 28 | echo "Installing requirements from extracted source build." 29 | pip install -r dev_requirements/test-requirements.txt 30 | pip install -e . 31 | 32 | echo "Running tests from extracted source build." 33 | pytest --cov dynamodb_encryption_sdk -m "local and not slow and not veryslow and not nope" 34 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/java/metastore-aes-hmac-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "metastore": [ 3 | { 4 | "*amzn-ddb-map-desc*": { 5 | "B": "AAAAAAAAABVhbXpuLWRkYi1tYXAtc3ltLW1vZGUAAAARL0NCQy9QS0NTNVBhZGRpbmc=" 6 | }, 7 | "t": { 8 | "B": "eJcKzY3SHwBIhXdfxeYWd9UE5yX+RxaPJQ7L2TdgDxs=" 9 | }, 10 | "V": { 11 | "N": "0" 12 | }, 13 | "encAlg": { 14 | "B": "XJilRkdsIP0bqzvqutJc8AC8YhY1YApJCgTLXgAqtwU=" 15 | }, 16 | "enc": { 17 | "B": "DvDUW2Ao1YWp7uxxEL+mv5uqHCrSNIDR18CgBD8XHCuNlBPC6GXxk9YnFmv3kgVDlMdEo0wE79zRoETB7GmjcA==" 18 | }, 19 | "intAlg": { 20 | "B": "I//7G2LUrAQ2EwQGQr7ZIKyXl1AlGeB+kfvZGmCj6wShZpMKPXjyBF/9RvIz3clQ" 21 | }, 22 | "N": { 23 | "S": "materialName" 24 | }, 25 | "int": { 26 | "B": "zFha4J4gPaiwhjiQs47L0bTf4WSNemVAxKJJnBnujl7OajvO7ZW3zehGJlaai4tCLxTwoLPI+Ig/a+zCdau4iw==" 27 | }, 28 | "*amzn-ddb-map-sig*": { 29 | "B": "aklO+h7kSUjXEt6pBA03G4wiIU20XKT/sP+rKSeNAKc=" 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /test/functional/internal/crypto/test_authentication.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional tests for material description de/serialization.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.internal.crypto.authentication import _string_to_sign 17 | 18 | from ...functional_test_vector_generators import string_to_sign_test_vectors 19 | 20 | pytestmark = [pytest.mark.functional, pytest.mark.local] 21 | 22 | 23 | @pytest.mark.parametrize("item, table_name, attribute_actions, expected_result", string_to_sign_test_vectors()) 24 | def test_string_to_sign(item, table_name, attribute_actions, expected_result): 25 | generated_string = _string_to_sign(item, table_name, attribute_actions) 26 | assert generated_string == expected_result 27 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/python/metastore-aes-hmac-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "metastore": [ 3 | { 4 | "N": { 5 | "S": "materialName" 6 | }, 7 | "V": { 8 | "N": "0" 9 | }, 10 | "t": { 11 | "B": "u/m1JEvU36YBaRyqjUi/9XF6iaxBrmTW/Ta2vTCQD9c=" 12 | }, 13 | "encAlg": { 14 | "B": "CzmDPsshipn9Uj+D9w4gDYDE0TF175l+TEu5VmH8zk8=" 15 | }, 16 | "enc": { 17 | "B": "mnKFr2Ec4oJRSJdBOZfEd8lLVrC5KhkTgGiVT2hk+UDswPi5HV0l205l2lHialmvwpmFNKaUYEYo4Ceeg08W0A==" 18 | }, 19 | "intAlg": { 20 | "B": "V4kF2DsNjy4/1wbCo16sksDie5botukvkzJMqlJ3iO4ULeSBc9KjtC3xPBGZEzF/" 21 | }, 22 | "int": { 23 | "B": "qGNnZkEjDmMtQuu4fqnJ+GD+1+v53vzW4GCcS3lpbhWuOMGyKoqZ63pSSKX3u3C7FwvCqh279G43kY6lKRQivw==" 24 | }, 25 | "*amzn-ddb-map-sig*": { 26 | "B": "tXbeYVfF9NnJnenfnYzTOUd58LIBLY/kqmZt9Ra5xLk=" 27 | }, 28 | "*amzn-ddb-map-desc*": { 29 | "B": "AAAAAAAAABdhbXpuLWRkYi1tYXAtc2lnbmluZ0FsZwAAAApIbWFjU0hBMjU2AAAAFWFtem4tZGRiLW1hcC1zeW0tbW9kZQAAABEvQ0JDL1BLQ1M1UGFkZGluZw==" 30 | } 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /codebuild/release/test-release.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | BRANCH: "master" 6 | secrets-manager: 7 | TWINE_USERNAME: TestPyPiCryptoTools:username 8 | TWINE_PASSWORD: TestPyPiCryptoTools:password 9 | 10 | phases: 11 | install: 12 | commands: 13 | - pip install "tox < 4.0" 14 | - pip install --upgrade pip 15 | runtime-versions: 16 | python: latest 17 | pre_build: 18 | commands: 19 | - git checkout $COMMIT_ID 20 | - FOUND_VERSION=$(sed -n 's/__version__ = "\(.*\)"/\1/p' src/dynamodb_encryption_sdk/identifiers.py) 21 | - | 22 | if expr ${FOUND_VERSION} != ${VERSION}; then 23 | echo "identifiers.py version (${FOUND_VERSION}) does not match expected version (${VERSION}), stopping" 24 | exit 1; 25 | fi 26 | build: 27 | commands: 28 | - tox -e park 29 | - tox -e test-release 30 | 31 | 32 | batch: 33 | fast-fail: true 34 | build-graph: 35 | - identifier: release_to_staging 36 | - identifier: validate_staging_release 37 | depend-on: 38 | - release_to_staging 39 | buildspec: codebuild/release/validate.yml 40 | env: 41 | variables: 42 | PIP_INDEX_URL: https://test.pypi.org/simple/ 43 | PIP_EXTRA_INDEX_URL: https://pypi.python.org/simple/ 44 | -------------------------------------------------------------------------------- /doc/lib/internal.rst: -------------------------------------------------------------------------------- 1 | Internal Resources 2 | ================== 3 | 4 | .. warning:: 5 | 6 | These are provided for informational purposes only. No guarantee is provided on the modules 7 | and APIs described here remaining consistent. Directly reference at your own risk. 8 | 9 | .. autosummary:: 10 | :toctree: generated 11 | 12 | 13 | dynamodb_encryption_sdk.internal.identifiers 14 | dynamodb_encryption_sdk.internal.str_ops 15 | dynamodb_encryption_sdk.internal.utils 16 | dynamodb_encryption_sdk.internal.validators 17 | dynamodb_encryption_sdk.internal.crypto 18 | dynamodb_encryption_sdk.internal.crypto.authentication 19 | dynamodb_encryption_sdk.internal.crypto.encryption 20 | dynamodb_encryption_sdk.internal.crypto.jce_bridge 21 | dynamodb_encryption_sdk.internal.crypto.jce_bridge.authentication 22 | dynamodb_encryption_sdk.internal.crypto.jce_bridge.encryption 23 | dynamodb_encryption_sdk.internal.crypto.jce_bridge.primitives 24 | dynamodb_encryption_sdk.internal.formatting 25 | dynamodb_encryption_sdk.internal.formatting.material_description 26 | dynamodb_encryption_sdk.internal.formatting.deserialize 27 | dynamodb_encryption_sdk.internal.formatting.deserialize.attribute 28 | dynamodb_encryption_sdk.internal.formatting.serialize 29 | dynamodb_encryption_sdk.internal.formatting.serialize.attribute 30 | -------------------------------------------------------------------------------- /VERSIONING.rst: -------------------------------------------------------------------------------- 1 | ***************** 2 | Versioning Policy 3 | ***************** 4 | 5 | We use a three-part X.Y.Z (Major.Minor.Patch) versioning definition, as follows: 6 | 7 | * **X (Major)** version changes are significant and expected to break backwards compatibility. 8 | * **Y (Minor)** version changes are moderate changes. These include: 9 | 10 | * Significant non-breaking feature additions. 11 | * Any change to the version of a dependency. 12 | * Possible backwards-incompatible changes. These changes will be noted and explained in detail in the release notes. 13 | 14 | * **Z (Patch)** version changes are small changes. These changes will not break backwards compatibility. 15 | 16 | * Z releases will also include warning of upcoming breaking changes, whenever possible. 17 | 18 | What this means for you 19 | ======================= 20 | 21 | We recommend running the most recent version. Here are our suggestions for managing updates: 22 | 23 | * X changes will require some effort to incorporate. 24 | * Y changes will not require significant effort to incorporate. 25 | 26 | * If you have good unit and integration tests, these changes are generally safe to pick up automatically. 27 | 28 | * Z changes will not require any changes to your code. Z changes are intended to be picked up automatically. 29 | 30 | * Good unit and integration tests are always recommended. 31 | -------------------------------------------------------------------------------- /test/unit/material_providers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit test for ``dynamodb_encryption_sdk.material_providers``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider 17 | 18 | pytestmark = [pytest.mark.unit, pytest.mark.local] 19 | 20 | 21 | @pytest.mark.parametrize( 22 | "method, message", 23 | ( 24 | ("decryption_materials", "No decryption materials available"), 25 | ("encryption_materials", "No encryption materials available"), 26 | ), 27 | ) 28 | def test_no_materials(method, message): 29 | empty_cmp = CryptographicMaterialsProvider(decryption_materials=None, encryption_materials=None) 30 | 31 | with pytest.raises(AttributeError) as excinfo: 32 | getattr(empty_cmp, method)(None) 33 | 34 | excinfo.match(message) 35 | -------------------------------------------------------------------------------- /test/unit/encrypted/test_encrypted.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit tests for ``dynamodb_encryption_sdk.encrypted``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.encrypted import CryptoConfig 17 | from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext 18 | 19 | from ..unit_test_utils import wrapped_cmp # noqa pylint: disable=unused-import 20 | 21 | pytestmark = [pytest.mark.unit, pytest.mark.local] 22 | 23 | 24 | def test_with_item(wrapped_cmp): 25 | config = CryptoConfig( 26 | materials_provider=wrapped_cmp, 27 | encryption_context=EncryptionContext(attributes={}), 28 | attribute_actions=AttributeActions(), 29 | ) 30 | item = {"test": "item", "with": "some data"} 31 | new_config = config.with_item(item) 32 | 33 | assert config.encryption_context.attributes == {} 34 | assert new_config.encryption_context.attributes == item 35 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """DynamoDB Encryption Client.""" 14 | from dynamodb_encryption_sdk.compatability import _warn_deprecated_python 15 | from dynamodb_encryption_sdk.encrypted.client import EncryptedClient 16 | from dynamodb_encryption_sdk.encrypted.item import ( 17 | decrypt_dynamodb_item, 18 | decrypt_python_item, 19 | encrypt_dynamodb_item, 20 | encrypt_python_item, 21 | ) 22 | from dynamodb_encryption_sdk.encrypted.resource import EncryptedResource 23 | from dynamodb_encryption_sdk.encrypted.table import EncryptedTable 24 | from dynamodb_encryption_sdk.identifiers import __version__ 25 | 26 | _warn_deprecated_python() 27 | 28 | __all__ = ( 29 | "decrypt_dynamodb_item", 30 | "decrypt_python_item", 31 | "encrypt_dynamodb_item", 32 | "encrypt_python_item", 33 | "EncryptedClient", 34 | "EncryptedResource", 35 | "EncryptedTable", 36 | "__version__", 37 | ) 38 | -------------------------------------------------------------------------------- /test/unit/test_compatability.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit test suite for dynamodb_encryption_sdk.compatability.""" 14 | import sys 15 | 16 | import mock 17 | import pytest 18 | 19 | from dynamodb_encryption_sdk.compatability import _warn_deprecated_python 20 | 21 | pytestmark = [pytest.mark.unit, pytest.mark.local] 22 | 23 | 24 | class TestWarnDeprecatedPython: 25 | def test_happy_version(self): 26 | with mock.patch.object(sys, "version_info") as v_info: 27 | v_info.major = 3 28 | v_info.minor = 8 29 | with pytest.warns(None) as record: 30 | _warn_deprecated_python() 31 | assert len(record) == 0 32 | 33 | def test_below_warn(self): 34 | with mock.patch.object(sys, "version_info") as v_info: 35 | v_info.major = 2 36 | v_info.minor = 7 37 | with pytest.warns(DeprecationWarning): 38 | _warn_deprecated_python() 39 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/java/metastore-kms-3.json: -------------------------------------------------------------------------------- 1 | { 2 | "java-kms-metatable" : [ { 3 | "*amzn-ddb-map-desc*" : { 4 | "B" : "AAAAAAAAABBhbXpuLWRkYi1lbnYta2V5AAAA4EFRRUJBSGhBODR3blhqRUpkQmJCQnlsUlVGY1paSzJqN3h3aDZVeUxvTDI4blErMEZBQUFBSDR3ZkFZSktvWklodmNOQVFjR29HOHdiUUlCQURCb0Jna3Foa2lHOXcwQkJ3RXdIZ1lKWUlaSUFXVURCQUV1TUJFRUROeWZYRXBrdnRNOFJ4SjdpZ0lCRUlBN3RoNk9DemtMOGlWU2lLdFFucmJWZVZuRjdkTzdPemd2Y2NnMnJHNExDUlZUVzREYmtFelovaysyZU9hWDhRbWd1d3BKNmdmKytxQ21qYmM9AAAAD2F3cy1rbXMtZWMtYXR0cgAAAAYqa2V5cyoAAAAQYW16bi1kZGItZW52LWFsZwAAAAdBRVMvMjU2AAAAEGFtem4tZGRiLXNpZy1hbGcAAAAOSG1hY1NIQTI1Ni8yNTYAAAARYW16bi1kZGItd3JhcC1hbGcAAAADa21zAAAAFWFtem4tZGRiLW1hcC1zeW0tbW9kZQAAABEvQ0JDL1BLQ1M1UGFkZGluZw==" 5 | }, 6 | "t" : { 7 | "B" : "umnxJZ3TTBpM8+hy+2v7yz6SllE/FemGyyzPuqD5YI0=" 8 | }, 9 | "V" : { 10 | "N" : "0" 11 | }, 12 | "encAlg" : { 13 | "B" : "XiZ0+sYasuStPkGN7Z2KgiNr7/1uCQHVV6RTML1JeKk=" 14 | }, 15 | "enc" : { 16 | "B" : "FFo+FzNGNhOpk1YdLR8tZhPxCGjUru324LwTfWoyVaUBipRwlgHo75fJiHmgV9Yt3gfOKhLw+VWnnF9blrG5ag==" 17 | }, 18 | "intAlg" : { 19 | "B" : "hrie6pasAp0wKS8dlab/VaxS8xnpfwcvnbt6XSUSHmyRrozUtIf2LBBdoLTPASIi" 20 | }, 21 | "N" : { 22 | "S" : "java-kms-material-name" 23 | }, 24 | "int" : { 25 | "B" : "ii6lOKwLziK98SReZ8qLUJUaip0fKE2Mm4Knjb4F6qZfM49e4VcTtpnT9S/PFLqe1ZdU502UVxXR56PL9PqenA==" 26 | }, 27 | "*amzn-ddb-map-sig*" : { 28 | "B" : "w6DYQSzqyHzl7pHlv+oW4/H9+uCiJXwwQk1djzB0g1Q=" 29 | } 30 | } ] 31 | } -------------------------------------------------------------------------------- /codebuild/release/validate.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | env: 4 | variables: 5 | BRANCH: "master" 6 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- 7 | arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f 8 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID: >- 9 | arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 10 | AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- 11 | arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 12 | DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME: ddbec-release-validation 13 | 14 | 15 | phases: 16 | install: 17 | commands: 18 | - pip install "tox < 4.0" 19 | runtime-versions: 20 | python: latest 21 | pre_build: 22 | commands: 23 | - cd examples 24 | - sed -i "s/dynamodb-encryption-sdk/dynamodb-encryption-sdk==$VERSION/" test/requirements.txt 25 | build: 26 | commands: 27 | - NUM_RETRIES=3 28 | - | 29 | while [ $NUM_RETRIES -gt 0 ] 30 | do 31 | tox -re py3-examples 32 | if [ $? -eq 0 ]; then 33 | break 34 | fi 35 | NUM_RETRIES=$((NUM_RETRIES-1)) 36 | if [ $NUM_RETRIES -eq 0 ]; then 37 | echo "All validation attempts failed, stopping" 38 | exit 1; 39 | else 40 | echo "Validation failed, retrying in 60 seconds; will retry $NUM_RETRIES more times" && sleep 60 41 | fi 42 | done 43 | 44 | -------------------------------------------------------------------------------- /examples/test/test_wrapped_encrypted_examples.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Test ``wrapped_*_encrypted_*`` examples.""" 14 | import pytest 15 | from dynamodb_encryption_sdk_examples import wrapped_rsa_encrypted_table, wrapped_symmetric_encrypted_table 16 | 17 | from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey 18 | 19 | from .examples_test_utils import ddb_table_name # noqa pylint: disable=unused-import 20 | 21 | pytestmark = [pytest.mark.examples] 22 | 23 | 24 | def test_wrapped_rsa_encrypted_table(ddb_table_name): 25 | wrapping_key_bytes = JceNameLocalDelegatedKey.generate("RSA", 4096).key 26 | signing_key_bytes = JceNameLocalDelegatedKey.generate("SHA512withRSA", 4096).key 27 | wrapped_rsa_encrypted_table.encrypt_item(ddb_table_name, wrapping_key_bytes, signing_key_bytes) 28 | 29 | 30 | def test_wrapped_symmetric_encrypted_table(ddb_table_name): 31 | wrapping_key_bytes = JceNameLocalDelegatedKey.generate("AES", 256).key 32 | signing_key_bytes = JceNameLocalDelegatedKey.generate("HmacSHA512", 256).key 33 | wrapped_symmetric_encrypted_table.encrypt_item(ddb_table_name, wrapping_key_bytes, signing_key_bytes) 34 | -------------------------------------------------------------------------------- /test/unit/unit_test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper utilities for unit tests.""" 14 | import itertools 15 | 16 | import pytest 17 | 18 | from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey 19 | from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider 20 | 21 | 22 | @pytest.fixture 23 | def wrapped_cmp(): 24 | wrapping_key = JceNameLocalDelegatedKey.generate("AES", 256) 25 | signing_key = JceNameLocalDelegatedKey.generate("HmacSHA512", 256) 26 | cmp = WrappedCryptographicMaterialsProvider( 27 | signing_key=signing_key, wrapping_key=wrapping_key, unwrapping_key=wrapping_key 28 | ) 29 | return cmp 30 | 31 | 32 | def all_possible_combinations(*base_values): 33 | combinations = [itertools.combinations(base_values, i) for i in range(1, len(base_values) + 1)] 34 | return itertools.chain(*combinations) 35 | 36 | 37 | def all_possible_combinations_kwargs(*base_values): 38 | for combo in all_possible_combinations(*base_values): 39 | kwargs = {} 40 | for values in combo: 41 | kwargs.update(values) 42 | yield kwargs 43 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/formatting/serialize/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper functions for serializing values. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | import struct 20 | from typing import Sized 21 | 22 | __all__ = ("encode_length", "encode_value") 23 | 24 | 25 | def encode_length(attribute): 26 | # type: (Sized) -> bytes 27 | """Encodes the length of the attribute as an unsigned int. 28 | 29 | :param attribute: Attribute with length value 30 | :returns: Encoded value 31 | :rtype: bytes 32 | """ 33 | return struct.pack(">I", len(attribute)) 34 | 35 | 36 | def encode_value(value): 37 | # type: (bytes) -> bytes 38 | """Encodes the value in Length-Value format. 39 | 40 | :param value: Value to encode 41 | :type value: six.string_types or :class:`boto3.dynamodb_encryption_sdk.types.Binary` 42 | :returns: Length-Value encoded value 43 | :rtype: bytes 44 | """ 45 | return struct.pack(">I{attr_len:d}s".format(attr_len=len(value)), len(value), value) 46 | -------------------------------------------------------------------------------- /SUPPORT_POLICY.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | This page describes the support policy for the AWS DynamoDB Encryption Client. We regularly provide the AWS DynamoDB Encryption Client with updates that may contain support for new or updated APIs, new features, enhancements, bug fixes, security patches, or documentation updates. Updates may also address changes with dependencies, language runtimes, and operating systems. 4 | 5 | We recommend users to stay up-to-date with DynamoDB Encryption Client releases to keep up with the latest features, security updates, and underlying dependencies. Continued use of an unsupported SDK version is not recommended and is done at the user’s discretion. 6 | 7 | 8 | Major Version Lifecycle 9 | ======================== 10 | The AWS DynamoDB Encryption Client follows the same major version lifecycle as the AWS SDK. For details on this lifecycle, see `AWS SDKs and Tools Maintenance Policy`_. 11 | 12 | Version Support Matrix 13 | ====================== 14 | This table describes the current support status of each major version of the AWS DynamoDB Encryption Client for Python. It also shows the next status each major version will transition to, and the date at which that transition will happen. 15 | 16 | .. list-table:: 17 | :widths: 30 50 50 50 18 | :header-rows: 1 19 | 20 | * - Major version 21 | - Current status 22 | - Next status 23 | - Next status date 24 | * - 1.x 25 | - End of Support 26 | - 27 | - 28 | * - 2.x 29 | - End of Support 30 | - 31 | - 32 | * - 3.x 33 | - Generally Available 34 | - Maintenance 35 | - 2023-07-23 36 | 37 | .. _AWS SDKs and Tools Maintenance Policy: https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html#version-life-cycle 38 | -------------------------------------------------------------------------------- /test/README.rst: -------------------------------------------------------------------------------- 1 | ******************************************** 2 | dynamodb-encryption-client Integration Tests 3 | ******************************************** 4 | 5 | In order to run these integration tests successfully, these things which must be configured. 6 | 7 | #. These tests assume that AWS credentials are available in one of the 8 | `automatically discoverable credential locations`_. 9 | #. The ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID`` environment variable 10 | must be set to a valid `AWS KMS CMK ARN`_ that can be used by the available credentials. 11 | #. The ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID`` and ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2`` environment variables 12 | must be set to two related AWS KMS Multi-Region key ids in different regions. 13 | #. The ``DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME`` environment variable must be set to a valid 14 | DynamoDB table name, in the default region, to which the discoverable credentials have 15 | read, write, and describe permissions. 16 | 17 | .. _automatically discoverable credential locations: http://boto3.readthedocs.io/en/latest/guide/configuration.html 18 | .. _AWS KMS CMK ARN: http://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html 19 | 20 | Updating Upstream Requirements 21 | ============================== 22 | 23 | The purpose of the upstream requirements files is to provide a stable list of 24 | packages for dependencies to run downstream tests of the DynamoDB Encryption 25 | Client. In order to update the upstream requirements in `upstream-requirements-py37.txt`, 26 | run these commands:: 27 | 28 | $ tox -e freeze-upstream-requirements-py37 29 | 30 | Test them using:: 31 | 32 | $ tox -e test-upstream-requirements-py37 33 | 34 | -------------------------------------------------------------------------------- /test/unit/internal/formatting/test_attribute.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """De/serialization unit tests. 14 | 15 | Tests for ``dynamodb_encryption_sdk.internal.formatting.serialize.attribute`` 16 | and ``dynamodb_encryption_sdk.internal.formatting.deserialize.attribute``. 17 | """ 18 | import pytest 19 | 20 | from dynamodb_encryption_sdk.internal.formatting.serialize.attribute import _sorted_key_map 21 | from dynamodb_encryption_sdk.internal.str_ops import to_bytes 22 | 23 | pytestmark = [pytest.mark.unit, pytest.mark.local] 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "initial, expected, transform", 28 | ( 29 | ( 30 | {"test": "value", "zzz": "another", "aaa": "qqq", "?>?>?": 5, b"\x00\x00": None}, 31 | [ 32 | (b"\x00\x00", None, b"\x00\x00"), 33 | (b"?>?>?", 5, "?>?>?"), 34 | (b"aaa", "qqq", "aaa"), 35 | (b"test", "value", "test"), 36 | (b"zzz", "another", "zzz"), 37 | ], 38 | to_bytes, 39 | ), 40 | ), 41 | ) 42 | def test_sorted_key_map(initial, expected, transform): 43 | actual = _sorted_key_map(initial, transform) 44 | 45 | assert actual == expected 46 | -------------------------------------------------------------------------------- /test/functional/internal/test_str_ops.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"). You 5 | # may not use this file except in compliance with the License. A copy of 6 | # the License is located at 7 | # 8 | # http://aws.amazon.com/apache2.0/ 9 | # 10 | # or in the "license" file accompanying this file. This file is 11 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 12 | # ANY KIND, either express or implied. See the License for the specific 13 | # language governing permissions and limitations under the License. 14 | """Test suite for ``dynamodb_encryption_sdk.internal.str_ops``.""" 15 | import codecs 16 | 17 | import pytest 18 | 19 | from dynamodb_encryption_sdk.internal.str_ops import to_bytes, to_str 20 | 21 | pytestmark = [pytest.mark.functional, pytest.mark.local] 22 | 23 | 24 | @pytest.mark.parametrize( 25 | "data, expected_output", 26 | ( 27 | ("asdf", "asdf"), 28 | (b"asdf", "asdf"), 29 | (codecs.encode("Предисловие", "utf-8"), "Предисловие"), 30 | ("Предисловие", "Предисловие"), 31 | ), 32 | ) 33 | def test_to_str(data, expected_output): 34 | test = to_str(data) 35 | assert test == expected_output 36 | 37 | 38 | @pytest.mark.parametrize( 39 | "data, expected_output", 40 | ( 41 | ("asdf", b"asdf"), 42 | (b"asdf", b"asdf"), 43 | (b"\x3a\x00\x99", b"\x3a\x00\x99"), 44 | ("Предисловие", codecs.encode("Предисловие", "utf-8")), 45 | (codecs.encode("Предисловие", "utf-8"), codecs.encode("Предисловие", "utf-8")), 46 | ), 47 | ) 48 | def test_to_bytes(data, expected_output): 49 | test = to_bytes(data) 50 | assert test == expected_output 51 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/python/metastore-aws-kms-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "metastore": [ 3 | { 4 | "*amzn-ddb-map-desc*": { 5 | "B": "AAAAAAAAABBhbXpuLWRkYi1lbnYtYWxnAAAAB0FFUy8yNTYAAAAQYW16bi1kZGItZW52LWtleQAAAOBBUUVCQUhoQTg0d25YakVKZEJiQkJ5bFJVRmNaWksyajd4d2g2VXlMb0wyOG5RKzBGQUFBQUg0d2ZBWUpLb1pJaHZjTkFRY0dvRzh3YlFJQkFEQm9CZ2txaGtpRzl3MEJCd0V3SGdZSllJWklBV1VEQkFFdU1CRUVERnVzbFhTSEdSalYvZ0tVUkFJQkVJQTdqQmFFS3ZwUFg0R3JiMGZtREkxYkpHVXNtV2lwQVJVcGhNcjJhb2JtajhkcFF4cFpwcTh2S1FWaWFVVEovN0s3YlRkejIyeHpvOG16Y3lnPQAAABdhbXpuLWRkYi1tYXAtc2lnbmluZ0FsZwAAAApIbWFjU0hBMjU2AAAAFWFtem4tZGRiLW1hcC1zeW0tbW9kZQAAABEvQ0JDL1BLQ1M1UGFkZGluZwAAABBhbXpuLWRkYi1zaWctYWxnAAAADkhtYWNTSEEyNTYvMjU2AAAAEWFtem4tZGRiLXdyYXAtYWxnAAAAA2ttcwAAAA9hd3Mta21zLWVjLWF0dHIAAAAGKmtleXMq" 6 | }, 7 | "t": { 8 | "B": "xckQnYGs0m/hcMa8NXZm8b6ALjFwHnXUGW2MmSYUFgw=" 9 | }, 10 | "V": { 11 | "N": "0" 12 | }, 13 | "encAlg": { 14 | "B": "6gOxsW9IdpzvoDmqE9Q0HUbjJjVcNw+rwXZ0ewvw2Ac=" 15 | }, 16 | "enc": { 17 | "B": "EV+qvAvB3HlXRSmkLmrLkDtXysFPRjaSBneRWB68pjlzjlaTQgZcXT6ocro9xkcj5q/uByiy5+UZk1lfkLD5hQ==" 18 | }, 19 | "intAlg": { 20 | "B": "r7F5AKlagMQ1CxdTxtz3ACde3LO+JpzRDcYbS2aNEfoGXCU4XDwCADXmXx6i07sI" 21 | }, 22 | "*amzn-ddb-map-sig*": { 23 | "B": "LPOagzisLozwyUORpEmeyTjVHFCbyoiTftrq1UqTfcs=" 24 | }, 25 | "int": { 26 | "B": "0DmuUIf/X/zxawA3VrHtbK7FLh/4f63SOW/O/EGDqh8KSE51jLdggHuc3i9FWquxeTPEL5BVov8QF0vVRgVkaA==" 27 | }, 28 | "N": { 29 | "S": "materialName" 30 | } 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /test/functional/materials/test_raw.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional test suite for ``dynamodb_encryption_sdk.materials.raw``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey 17 | from dynamodb_encryption_sdk.materials.raw import RawDecryptionMaterials, RawEncryptionMaterials 18 | 19 | pytestmark = [pytest.mark.functional, pytest.mark.local] 20 | 21 | 22 | def test_no_encryption_key(): 23 | signing_key = JceNameLocalDelegatedKey.generate("HmacSHA512", 256) 24 | encryption_materials = RawEncryptionMaterials(signing_key=signing_key) 25 | 26 | with pytest.raises(AttributeError) as excinfo: 27 | encryption_materials.encryption_key # calls a property, so pylint: disable=pointless-statement 28 | 29 | excinfo.match("No encryption key available") 30 | 31 | 32 | def test_no_decryption_key(): 33 | verification_key = JceNameLocalDelegatedKey.generate("HmacSHA512", 256) 34 | decryption_materials = RawDecryptionMaterials(verification_key=verification_key) 35 | 36 | with pytest.raises(AttributeError) as excinfo: 37 | decryption_materials.decryption_key # calls a property, so pylint: disable=pointless-statement 38 | 39 | excinfo.match("No decryption key available") 40 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/transform.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper tools for translating between native and DynamoDB items.""" 14 | from typing import Any, Dict 15 | 16 | from boto3.dynamodb.types import TypeDeserializer, TypeSerializer 17 | 18 | __all__ = ("dict_to_ddb", "ddb_to_dict") 19 | 20 | 21 | def dict_to_ddb(item): 22 | # type: (Dict[str, Any]) -> Dict[str, Any] 23 | # narrow these types down 24 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 25 | """Converts a native Python dictionary to a raw DynamoDB item. 26 | 27 | :param dict item: Native item 28 | :returns: DynamoDB item 29 | :rtype: dict 30 | """ 31 | serializer = TypeSerializer() 32 | return {key: serializer.serialize(value) for key, value in item.items()} 33 | 34 | 35 | def ddb_to_dict(item): 36 | # type: (Dict[str, Any]) -> Dict[str, Any] 37 | # narrow these types down 38 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/66 39 | """Converts a raw DynamoDB item to a native Python dictionary. 40 | 41 | :param dict item: DynamoDB item 42 | :returns: Native item 43 | :rtype: dict 44 | """ 45 | deserializer = TypeDeserializer() 46 | return {key: deserializer.deserialize(value) for key, value in item.items()} 47 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/str_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper functions for consistently obtaining str and bytes objects in both Python2 and Python3. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | import codecs 20 | 21 | import six 22 | 23 | from dynamodb_encryption_sdk.internal.identifiers import TEXT_ENCODING 24 | 25 | __all__ = ("to_str", "to_bytes") 26 | 27 | 28 | def to_str(data): 29 | """Takes an input str or bytes object and returns an equivalent str object. 30 | 31 | :param data: Input data 32 | :type data: str or bytes 33 | :returns: Data normalized to str 34 | :rtype: str 35 | """ 36 | if isinstance(data, bytes): 37 | return codecs.decode(data, TEXT_ENCODING) 38 | return data 39 | 40 | 41 | def to_bytes(data): 42 | """Takes an input str or bytes object and returns an equivalent bytes object. 43 | 44 | :param data: Input data 45 | :type data: str or bytes 46 | :returns: Data normalized to bytes 47 | :rtype: bytes 48 | """ 49 | if isinstance(data, six.string_types) and not isinstance(data, bytes): 50 | return codecs.encode(data, TEXT_ENCODING) 51 | return data 52 | -------------------------------------------------------------------------------- /examples/test/test_most_recent_provider_encrypted_examples.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Test most recent provider examples.""" 14 | import uuid 15 | 16 | import boto3 17 | import pytest 18 | from dynamodb_encryption_sdk_examples import most_recent_provider_encrypted_table 19 | 20 | from dynamodb_encryption_sdk.material_providers.store.meta import MetaStore 21 | 22 | from .examples_test_utils import cmk_arn, ddb_table_name # noqa pylint: disable=unused-import 23 | 24 | pytestmark = [pytest.mark.examples] 25 | 26 | 27 | def test_most_recent_encrypted_table(ddb_table_name, cmk_arn): 28 | # define random new names for material and metastore table 29 | meta_table_name = "meta-table-{}".format(uuid.uuid4()) 30 | material_name = "material-{}".format(uuid.uuid4()) 31 | 32 | # create the metastore table 33 | client = boto3.client("dynamodb") 34 | MetaStore.create_table(client, meta_table_name, 10, 10) 35 | waiter = client.get_waiter("table_exists") 36 | waiter.wait(TableName=meta_table_name) 37 | 38 | # run the actual test 39 | most_recent_provider_encrypted_table.encrypt_item(ddb_table_name, cmk_arn, meta_table_name, material_name) 40 | 41 | # clean up the meta store table 42 | client.delete_table(TableName=meta_table_name) 43 | waiter = client.get_waiter("table_not_exists") 44 | waiter.wait(TableName=meta_table_name) 45 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/java/metastore-rsa-rsa-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "java-rsa-metatable" : [ { 3 | "*amzn-ddb-map-desc*" : { 4 | "B" : "AAAAAAAAABBhbXpuLWRkYi1lbnYta2V5AAABWG5kYjJ3T2l6UjR2QmdYWUdkWURLVGdDSzlXeG8wYUx3TGhuUWcrcHYxUkhQRVlvdWhWYktHeklaUDh6azlhK0UzSFRabkNNNU5QRUthdFRUbHlkUFVMbzkvdXAyYlZxMk5SajkwaS9xVmF6aWZWT1B3bEVwVEdiblpFZDFvN1F1d1JmZFZ6eTJRclk0RkJaR1lYUkUvd1g1dTdITWpDNHlaSmVDa1R6NXV5TEYvZnF4Y21IbzZwSnQ2eWNaRjIzNUE5WTdNb29UazdWeFp1aFdWakVNMTVnOGhqZ0VaamZwUExYQlp0cEl4LzVXYnVQcnl2MWp3V2lEeW1xTGRoZ1ROdkpUWHJjUUxEUHFXcnNwTlJWbDIxNEU2dWNkQTBaT25MUEowdUZGTW4yanRUVDJleUVGUXdtbytaSmQwd29rMmlTbm9oMzdJLzYyblZlcWM2eW1Ldz09AAAAEGFtem4tZGRiLWVudi1hbGcAAAADQUVTAAAAEWFtem4tZGRiLXdyYXAtYWxnAAAAJVJTQS9FQ0IvT0FFUFdpdGhTSEEtMjU2QW5kTUdGMVBhZGRpbmcAAAAXYW16bi1kZGItbWFwLXNpZ25pbmdBbGcAAAANU0hBMjU2d2l0aFJTQQAAABVhbXpuLWRkYi1tYXAtc3ltLW1vZGUAAAARL0NCQy9QS0NTNVBhZGRpbmc=" 5 | }, 6 | "t" : { 7 | "B" : "o5TWCbCjL3OvibBHwMFeUD5FoK7LOyRhaC/Qs28I71U=" 8 | }, 9 | "V" : { 10 | "N" : "0" 11 | }, 12 | "encAlg" : { 13 | "B" : "2qLiArFMS7ALoSgRyRymjCaMZ+Ep2qarlgBtJEAokAw=" 14 | }, 15 | "enc" : { 16 | "B" : "1SPFwKyp0mL+e7dQFdSIPpydQ/8T1KsUZMZRePuwq1tBeQhLzux1oCb8akQR4STmkt3fHz3Y9kzD//alEiksbA==" 17 | }, 18 | "intAlg" : { 19 | "B" : "CXMK6Ejt5OcLGK5744lgaDsZxsXYEQKfG6MEQYHQIIQOPfVVIa6Vau4BbVxDlaHr" 20 | }, 21 | "N" : { 22 | "S" : "java-rsa-material-name" 23 | }, 24 | "int" : { 25 | "B" : "rBbUkNVARki54hU4RXx6L6W4UaWabSTWlh6PKPA/DRWS9s93hnGE0AEqNVz6dGFRPNa9t7TgvrrDhIbi1ZIwBg==" 26 | }, 27 | "*amzn-ddb-map-sig*" : { 28 | "B" : "Cx9gASd4Sj+/7eGQUNquJi0tfDSRVCfK2KCfGw84sp60xGN0Ctwj9mM1v8ptPMevRT+VAT03ztX96hb58W/2l7FoHe+Twsz/lXCVKzuCA+0sYEqNd5Wh52qNr06eVgHUJTQ1SrwvNCETG3IxEt9pq5EemDSG9TJBd0yerEg1T8xkZ6DoGDqethE2YkQrHzqfW+DVZb0gVp96psQW3utBULIhI7fCMh2Prg7D87/G3lzp+4eB+ZfbM9tORV2EClCwnicjo9NCvbUhB9UlflUb2z6sIqE4YaBHOhDL/5m4I22QCeZj++5xcCnaymIHsZHFviUnQx2mWE09/rCtijCqbQ==" 29 | } 30 | } ] 31 | } -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/compatability.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Contains logic for checking the Python Version""" 14 | import sys 15 | import warnings 16 | 17 | DEPRECATION_DATE_MAP = {"1.x": "2022-07-08", "2.x": "2022-07-15"} 18 | 19 | 20 | def _warn_deprecated_python(): 21 | """Template for deprecation of Python warning.""" 22 | deprecated_versions = { 23 | (2, 7): {"date": DEPRECATION_DATE_MAP["2.x"]}, 24 | (3, 4): {"date": DEPRECATION_DATE_MAP["2.x"]}, 25 | (3, 5): {"date": "2021-11-10"}, 26 | (3, 6): {"date": "2021-12-19"}, 27 | (3, 7): {"date": "2024-03-04"}, 28 | } 29 | py_version = (sys.version_info.major, sys.version_info.minor) 30 | minimum_version = (3, 8) 31 | 32 | if py_version in deprecated_versions: 33 | params = deprecated_versions[py_version] 34 | warning = ( 35 | "aws-dynamodb-encryption will no longer support Python {}.{} " 36 | "starting {}. To continue receiving service updates, " 37 | "bug fixes, and security updates please upgrade to Python {}.{} or " 38 | "later. For more information, see SUPPORT_POLICY.rst: " 39 | "https://github.com/aws/aws-dynamodb-encryption-python/blob/master/SUPPORT_POLICY.rst" 40 | ).format(py_version[0], py_version[1], params["date"], minimum_version[0], minimum_version[1]) 41 | warnings.warn(warning, DeprecationWarning) 42 | -------------------------------------------------------------------------------- /test/integration/encrypted/test_resource.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Integration tests for ``dynamodb_encryption_sdk.encrypted.resource``.""" 14 | import pytest 15 | 16 | from ..integration_test_utils import ( # noqa pylint: disable=unused-import 17 | ddb_table_name, 18 | functional_test_utils, 19 | set_parameterized_kms_cmps, 20 | ) 21 | 22 | pytestmark = [pytest.mark.integ, pytest.mark.ddb_integ] 23 | 24 | 25 | def pytest_generate_tests(metafunc): 26 | functional_test_utils.set_parametrized_actions(metafunc) 27 | functional_test_utils.set_parametrized_cmp(metafunc) 28 | functional_test_utils.set_parametrized_item(metafunc) 29 | set_parameterized_kms_cmps(metafunc) 30 | 31 | 32 | def test_ephemeral_batch_item_cycle(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 33 | """Test a small number of curated CMPs against a small number of curated items.""" 34 | functional_test_utils.resource_cycle_batch_items_check( 35 | some_cmps, parametrized_actions, parametrized_item, ddb_table_name 36 | ) 37 | 38 | 39 | def test_ephemeral_batch_item_cycle_kms( 40 | ddb_table_name, all_aws_kms_cmp_builders, parametrized_actions, parametrized_item 41 | ): 42 | """Test the AWS KMS CMP against a small number of curated items.""" 43 | functional_test_utils.resource_cycle_batch_items_check( 44 | all_aws_kms_cmp_builders(), parametrized_actions, parametrized_item, ddb_table_name 45 | ) 46 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/identifiers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unique identifiers used by the DynamoDB Encryption Client.""" 14 | from enum import Enum 15 | 16 | __all__ = ("LOGGER_NAME", "CryptoAction", "EncryptionKeyType", "KeyEncodingType") 17 | __version__ = "3.2.0" 18 | 19 | LOGGER_NAME = "dynamodb_encryption_sdk" 20 | USER_AGENT_SUFFIX = "DynamodbEncryptionSdkPython/{}".format(__version__) 21 | 22 | 23 | class CryptoAction(Enum): 24 | """Possible actions to take on an item attribute.""" 25 | 26 | DO_NOTHING = 0 27 | SIGN_ONLY = 1 28 | ENCRYPT_AND_SIGN = 2 29 | 30 | def __gt__(self, other): 31 | # type: (CryptoAction) -> bool 32 | """Define CryptoAction equality.""" 33 | return not self.__lt__(other) and not self.__eq__(other) 34 | 35 | def __lt__(self, other): 36 | # type: (CryptoAction) -> bool 37 | """Define CryptoAction equality.""" 38 | return self.value < other.value # pylint: disable=comparison-with-callable 39 | 40 | def __eq__(self, other): 41 | # type: (CryptoAction) -> bool 42 | """Define CryptoAction equality.""" 43 | return self.value == other.value # pylint: disable=comparison-with-callable 44 | 45 | 46 | class EncryptionKeyType(Enum): 47 | """Supported types of encryption keys.""" 48 | 49 | SYMMETRIC = 0 50 | PRIVATE = 1 51 | PUBLIC = 2 52 | 53 | 54 | class KeyEncodingType(Enum): 55 | """Supported key encoding schemes.""" 56 | 57 | RAW = 0 58 | DER = 1 59 | PEM = 2 60 | -------------------------------------------------------------------------------- /examples/test/test_aws_kms_encrypted_examples.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Test ``aws_kms_encrypted_*`` examples.""" 14 | import pytest 15 | from dynamodb_encryption_sdk_examples import ( 16 | aws_kms_encrypted_client, 17 | aws_kms_encrypted_item, 18 | aws_kms_encrypted_resource, 19 | aws_kms_encrypted_table, 20 | aws_kms_multi_region_key, 21 | ) 22 | 23 | from .examples_test_utils import ( # noqa pylint: disable=unused-import 24 | cmk_arn, 25 | cmk_mrk_arn, 26 | ddb_table_name, 27 | second_cmk_mrk_arn, 28 | ) 29 | 30 | pytestmark = [pytest.mark.examples] 31 | 32 | 33 | def test_aws_kms_encrypted_table(ddb_table_name, cmk_arn): 34 | aws_kms_encrypted_table.encrypt_item(ddb_table_name, cmk_arn) 35 | 36 | 37 | def test_aws_kms_encrypted_client_item(ddb_table_name, cmk_arn): 38 | aws_kms_encrypted_client.encrypt_item(ddb_table_name, cmk_arn) 39 | 40 | 41 | def test_aws_kms_encrypted_client_batch_items(ddb_table_name, cmk_arn): 42 | aws_kms_encrypted_client.encrypt_batch_items(ddb_table_name, cmk_arn) 43 | 44 | 45 | def test_aws_kms_encrypted_item(ddb_table_name, cmk_arn): 46 | aws_kms_encrypted_item.encrypt_item(ddb_table_name, cmk_arn) 47 | 48 | 49 | def test_aws_kms_encrypted_resource(ddb_table_name, cmk_arn): 50 | aws_kms_encrypted_resource.encrypt_batch_items(ddb_table_name, cmk_arn) 51 | 52 | 53 | def test_aws_kms_mrk_client(ddb_table_name, cmk_mrk_arn, second_cmk_mrk_arn): 54 | aws_kms_multi_region_key.encrypt_item(ddb_table_name, cmk_mrk_arn, second_cmk_mrk_arn) 55 | -------------------------------------------------------------------------------- /test/vectors/encrypted_item/ciphertext/python/metastore-rsa-rsa-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "MetaTable": [ 3 | { 4 | "N": { 5 | "S": "my material name" 6 | }, 7 | "V": { 8 | "N": "5" 9 | }, 10 | "t": { 11 | "B": "WWJ3Vz3c9h3jh9mgm2SeCG50AKNjck17jOqh+Ck3YYk=" 12 | }, 13 | "encAlg": { 14 | "B": "+IzZN4S+D8IWe5owaeFdcIdg5/OLjcSlVXdMpPzKwKc=" 15 | }, 16 | "intAlg": { 17 | "B": "NN6CBzgosLeAualiFggqd1XMzwI9jBMFaLP/b/tytooUeTL3M0eHELRp57mQazDe" 18 | }, 19 | "enc": { 20 | "B": "MRP1Q6ptICi0stBWyjTpM5stvMw14it5ohM/zPgx9XbeDYVRoPgmTDkBVVA1dQDZ" 21 | }, 22 | "int": { 23 | "B": "q/PhRAvCb/0XXC7Z4XYy72fYrLIWdEjqtAQm6dZK0fg=" 24 | }, 25 | "*amzn-ddb-map-sig*": { 26 | "B": "oRo6E5S0vTY1ZjSPHBqB6JowBIjDKt7BTJt6xEjxgYykbpx+oq87YfDVy4P8sV/n09t8MqMHpKSxxJ8AhzKbkAe7QVLx7nGYrsBKPC3Q9y6MdWz2kpvTAFC2Q+fxj6PGyZFDeq2cip1bWmiP5Q+JkkgiS5lG6WpWgmuQTie1lklubcPGJqrnCsaG7SSPNMUc4KrhA54sv3TZXvJdPjgrjhMIiqDfYzRMle1IZcAJNyvSmJ03T9iSxFTPeqhtmFldn4DhWC3qwdlqaQQofiowiCpgcknNGBqlKwoL2ZDP6MuUKiW+Tll9nGzB2W00cmPyN/ZoX69fHLXxG2Gd/h3hgQ==" 27 | }, 28 | "*amzn-ddb-map-desc*": { 29 | "B": "AAAAAAAAABBhbXpuLWRkYi1lbnYtYWxnAAAAB0FFUy8yNTYAAAAQYW16bi1kZGItZW52LWtleQAAAVhEN2h3R1F3ckwxV1NVNmtTZTlvdXA3MEM3Sk0zNFZPaTlmMnlWVTJ4VE9kYWdrQ3Avczk2VFdxSmEvOWtVejEwRGcxdWxpVGtnY0llTEJNQ1JuK1hHRTg4SFovVVRCMzVMWkx5YklwY1JrL0hhelQ1U1ZQV1JaK2hWSjlhUWxUaHBocThoYWxPOTJ2WkN2RXA5YVcwRzRncXZObVhUenJoZm9MYU1LdCtPYXNCb0o1aWpaLzFYRDdGaVlMLzBCZ2hDdDlyL2pzU2NpdysweXdYNjZXZ3pVZDVGZmNIaVpYYUtENmVQd1pxOFlrc1lOS3V4NU1MNUZPcWxDd2ZjMUZiQUE0dXFZNDdrTWJzYjZ5SkVoMThzU3lTY3ZGOE9nZjNDQURGbzFRY1g2WjFxQ2Q0REl0SEhRRzZDcExiU0liSkFGaDJyOEJ4NVRKRklJR2ZXMmNxN1E9PQAAABdhbXpuLWRkYi1tYXAtc2lnbmluZ0FsZwAAAA1TSEEyNTZ3aXRoUlNBAAAAFWFtem4tZGRiLW1hcC1zeW0tbW9kZQAAABEvQ0JDL1BLQ1M1UGFkZGluZwAAABFhbXpuLWRkYi13cmFwLWFsZwAAACVSU0EvRUNCL09BRVBXaXRoU0hBLTI1NkFuZE1HRjFQYWRkaW5n" 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_tests.yaml: -------------------------------------------------------------------------------- 1 | # This workflow runs tests on pull requests. 2 | name: tests 3 | 4 | on: 5 | pull_request: 6 | push: 7 | # Run once a day 8 | schedule: 9 | - cron: '0 0 * * *' 10 | 11 | jobs: 12 | tests: 13 | runs-on: ${{ matrix.platform.os }} 14 | strategy: 15 | fail-fast: true 16 | matrix: 17 | platform: 18 | - os: ubuntu-latest 19 | architecture: x64 20 | - os: windows-latest 21 | architecture: x64 22 | # x86 builds are only meaningful for Windows 23 | - os: windows-latest 24 | architecture: x86 25 | - os: macos-latest 26 | architecture: x64 27 | python: 28 | - 3.8 29 | - 3.9 30 | - "3.10" 31 | - "3.11" 32 | - "3.12" 33 | - 3.x 34 | category: 35 | - local-slow 36 | # These require credentials. 37 | # Enable them once we sort how to provide them. 38 | # - integ-slow 39 | # - examples 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: actions/setup-python@v5 43 | with: 44 | python-version: ${{ matrix.python }} 45 | architecture: ${{ matrix.platform.architecture }} 46 | - run: | 47 | python -m pip install --upgrade pip 48 | pip install --upgrade -r dev_requirements/ci-requirements.txt 49 | - name: run test 50 | env: 51 | TOXENV: ${{ matrix.category }} 52 | run: tox -- -vv 53 | 54 | upstream-py311: 55 | runs-on: ubuntu-latest 56 | strategy: 57 | fail-fast: true 58 | matrix: 59 | category: 60 | - nocmk 61 | - sourcebuildcheck 62 | - test-upstream-requirements-py311 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: actions/setup-python@v5 66 | with: 67 | python-version: "3.11" 68 | - run: | 69 | python -m pip install --upgrade pip 70 | pip install --upgrade -r dev_requirements/ci-requirements.txt 71 | - name: run test 72 | env: 73 | TOXENV: ${{ matrix.category }} 74 | run: tox -- -vv 75 | -------------------------------------------------------------------------------- /examples/setup.py: -------------------------------------------------------------------------------- 1 | """DynamoDB Encryption Client for Python examples.""" 2 | import io 3 | import os 4 | import re 5 | 6 | from setuptools import find_packages, setup 7 | 8 | VERSION_RE = re.compile(r"""__version__ = ['"]([0-9.]+)['"]""") 9 | HERE = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def read(*args): 13 | """Reads complete file contents.""" 14 | return io.open(os.path.join(HERE, *args), encoding="utf-8").read() 15 | 16 | 17 | def get_version(): 18 | """Reads the version from this module.""" 19 | init = read("src", "dynamodb_encryption_sdk_examples", "__init__.py") 20 | return VERSION_RE.search(init).group(1) 21 | 22 | 23 | def get_requirements(): 24 | """Reads the requirements file.""" 25 | requirements = read("requirements.txt") 26 | return requirements.strip().splitlines() 27 | 28 | 29 | setup( 30 | name="dynamodb-encryption-sdk-examples", 31 | version=get_version(), 32 | packages=find_packages("src"), 33 | package_dir={"": "src"}, 34 | url="https://github.com/aws/aws-dynamodb-encryption-python", 35 | author="Amazon Web Services", 36 | author_email="aws-cryptools@amazon.com", 37 | maintainer="Amazon Web Services", 38 | description="DynamoDB Encryption Client for Python examples", 39 | long_description=read("README.rst"), 40 | keywords="dynamodb-encryption-sdk aws kms encryption dynamodb", 41 | data_files=["requirements.txt"], 42 | license="Apache License 2.0", 43 | install_requires=get_requirements(), 44 | classifiers=[ 45 | "Development Status :: 5 - Production/Stable", 46 | "Intended Audience :: Developers", 47 | "Natural Language :: English", 48 | "License :: OSI Approved :: Apache Software License", 49 | "Programming Language :: Python", 50 | "Programming Language :: Python :: 2", 51 | "Programming Language :: Python :: 2.7", 52 | "Programming Language :: Python :: 3", 53 | "Programming Language :: Python :: 3.7", 54 | "Programming Language :: Python :: 3.8", 55 | "Programming Language :: Python :: 3.9", 56 | "Programming Language :: Python :: Implementation :: CPython", 57 | "Topic :: Security", 58 | "Topic :: Security :: Cryptography", 59 | ], 60 | ) 61 | -------------------------------------------------------------------------------- /test/integration/encrypted/test_table.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Integration tests for ``dynamodb_encryption_sdk.encrypted.table``.""" 14 | import pytest 15 | 16 | from ..integration_test_utils import ( # noqa pylint: disable=unused-import 17 | ddb_table_name, 18 | functional_test_utils, 19 | set_parameterized_kms_cmps, 20 | ) 21 | 22 | pytestmark = [pytest.mark.integ, pytest.mark.ddb_integ] 23 | 24 | 25 | def pytest_generate_tests(metafunc): 26 | functional_test_utils.set_parametrized_actions(metafunc) 27 | functional_test_utils.set_parametrized_cmp(metafunc) 28 | functional_test_utils.set_parametrized_item(metafunc) 29 | set_parameterized_kms_cmps(metafunc) 30 | 31 | 32 | def test_ephemeral_item_cycle(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 33 | """Test a small number of curated CMPs against a small number of curated items.""" 34 | functional_test_utils.table_cycle_check(some_cmps, parametrized_actions, parametrized_item, ddb_table_name) 35 | 36 | 37 | def test_ephemeral_item_cycle_batch_writer(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 38 | """Test a small number of curated CMPs against a small number of curated items.""" 39 | functional_test_utils.table_cycle_batch_writer_check( 40 | some_cmps, parametrized_actions, parametrized_item, ddb_table_name 41 | ) 42 | 43 | 44 | def test_ephemeral_item_cycle_kms(ddb_table_name, all_aws_kms_cmp_builders, parametrized_actions, parametrized_item): 45 | """Test the AWS KMS CMP against a small number of curated items.""" 46 | functional_test_utils.table_cycle_check( 47 | all_aws_kms_cmp_builders(), parametrized_actions, parametrized_item, ddb_table_name 48 | ) 49 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """DynamoDB Encryption Client for Python.""" 2 | import io 3 | import os 4 | import re 5 | 6 | from setuptools import find_packages, setup 7 | 8 | VERSION_RE = re.compile(r"""__version__ = ['"]([0-9.]+)['"]""") 9 | HERE = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def read(*args): 13 | """Reads complete file contents.""" 14 | return io.open(os.path.join(HERE, *args), encoding="utf-8").read() # pylint: disable=consider-using-with 15 | 16 | 17 | def get_version(): 18 | """Reads the version from this module.""" 19 | init = read("src", "dynamodb_encryption_sdk", "identifiers.py") 20 | return VERSION_RE.search(init).group(1) 21 | 22 | 23 | def get_requirements(): 24 | """Reads the requirements file.""" 25 | requirements = read("requirements.txt") 26 | return requirements.strip().splitlines() 27 | 28 | 29 | setup( 30 | name="dynamodb-encryption-sdk", 31 | version=get_version(), 32 | packages=find_packages("src"), 33 | package_dir={"": "src"}, 34 | url="https://github.com/aws/aws-dynamodb-encryption-python", 35 | author="Amazon Web Services", 36 | author_email="aws-cryptools@amazon.com", 37 | maintainer="Amazon Web Services", 38 | description="DynamoDB Encryption Client for Python", 39 | long_description=read("README.rst"), 40 | keywords="dynamodb-encryption-sdk aws kms encryption dynamodb", 41 | data_files=["README.rst", "CHANGELOG.rst", "LICENSE", "requirements.txt"], 42 | license="Apache License 2.0", 43 | install_requires=get_requirements(), 44 | classifiers=[ 45 | "Development Status :: 5 - Production/Stable", 46 | "Intended Audience :: Developers", 47 | "Natural Language :: English", 48 | "License :: OSI Approved :: Apache Software License", 49 | "Programming Language :: Python", 50 | "Programming Language :: Python :: 3", 51 | "Programming Language :: Python :: 3.8", 52 | "Programming Language :: Python :: 3.9", 53 | "Programming Language :: Python :: 3.10", 54 | "Programming Language :: Python :: 3.11", 55 | "Programming Language :: Python :: 3.12", 56 | "Programming Language :: Python :: Implementation :: CPython", 57 | "Topic :: Security", 58 | "Topic :: Security :: Cryptography", 59 | ], 60 | ) 61 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/material_providers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Cryptographic materials providers.""" 14 | from dynamodb_encryption_sdk.materials import CryptographicMaterials # noqa pylint: disable=unused-import 15 | from dynamodb_encryption_sdk.structures import EncryptionContext # noqa pylint: disable=unused-import 16 | 17 | __all__ = ("CryptographicMaterialsProvider",) 18 | 19 | 20 | class CryptographicMaterialsProvider(object): 21 | """Base class for all cryptographic materials providers.""" 22 | 23 | def decryption_materials(self, encryption_context): 24 | # type: (EncryptionContext) -> CryptographicMaterials 25 | # pylint: disable=unused-argument,no-self-use 26 | """Return decryption materials. 27 | 28 | :param EncryptionContext encryption_context: Encryption context for request 29 | :raises AttributeError: if no decryption materials are available 30 | """ 31 | raise AttributeError("No decryption materials available") 32 | 33 | def encryption_materials(self, encryption_context): 34 | # type: (EncryptionContext) -> CryptographicMaterials 35 | # pylint: disable=unused-argument,no-self-use 36 | """Return encryption materials. 37 | 38 | :param EncryptionContext encryption_context: Encryption context for request 39 | :raises AttributeError: if no encryption materials are available 40 | """ 41 | raise AttributeError("No encryption materials available") 42 | 43 | def refresh(self): 44 | # type: () -> None 45 | # pylint: disable=unused-argument,no-self-use 46 | """Ask this instance to refresh the cryptographic materials. 47 | 48 | .. note:: 49 | 50 | Default behavior is to do nothing. 51 | """ 52 | -------------------------------------------------------------------------------- /test/unit/material_providers/test_static.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit tests for ``dynamodb_encryption_sdk.material_providers.static``.""" 14 | import pytest 15 | from mock import MagicMock 16 | 17 | from dynamodb_encryption_sdk.material_providers.static import StaticCryptographicMaterialsProvider 18 | from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials 19 | from dynamodb_encryption_sdk.structures import EncryptionContext 20 | 21 | pytestmark = [pytest.mark.unit, pytest.mark.local] 22 | 23 | 24 | @pytest.mark.parametrize( 25 | "method, message", 26 | ( 27 | ("decryption_materials", "No decryption materials available"), 28 | ("encryption_materials", "No encryption materials available"), 29 | ), 30 | ) 31 | def test_no_materials(method, message): 32 | empty_cmp = StaticCryptographicMaterialsProvider() 33 | 34 | with pytest.raises(AttributeError) as excinfo: 35 | getattr(empty_cmp, method)(EncryptionContext()) 36 | 37 | excinfo.match(message) 38 | 39 | 40 | @pytest.mark.parametrize( 41 | "invalid_kwargs", 42 | (dict(decryption_materials="not decryption materails"), dict(encryption_materials="not encryption materails")), 43 | ) 44 | def test_attrs_fail(invalid_kwargs): 45 | with pytest.raises(TypeError): 46 | StaticCryptographicMaterialsProvider(**invalid_kwargs) 47 | 48 | 49 | @pytest.mark.parametrize( 50 | "materials, method", 51 | ( 52 | (MagicMock(__class__=DecryptionMaterials), "decryption_materials"), 53 | (MagicMock(__class__=EncryptionMaterials), "encryption_materials"), 54 | ), 55 | ) 56 | def test_valid_materials(materials, method): 57 | kwargs = {method: materials} 58 | static_cmp = StaticCryptographicMaterialsProvider(**kwargs) 59 | 60 | assert getattr(static_cmp, method)(EncryptionContext()) is materials 61 | -------------------------------------------------------------------------------- /test/vectors/string_to_sign.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "table": "ExampleTableName", 4 | "item": { 5 | "a_number": { 6 | "value": {"N": "55"}, 7 | "action": "encrypt" 8 | } 9 | }, 10 | "string_to_sign": "9sgaD2yFEAmIplAUwJS+KesIRv1Xw0Q/DVnzVNZqp/U4+5C1DIfNg97Pu2hKprvFT1nzJNuMvsdX9zwUYqSJqjlBFaxOsJqklCk0iBayA4EnsPnjpSA3KuEAyhn7CP1QYD1mapH53y+ljTvr5TAcGBTbydPe4VTANZOIHuHviQ8=" 11 | }, 12 | { 13 | "table": "ExampleTableName", 14 | "item": { 15 | "a_number": { 16 | "value": {"N": "55"}, 17 | "action": "sign" 18 | } 19 | }, 20 | "string_to_sign": "9sgaD2yFEAmIplAUwJS+KesIRv1Xw0Q/DVnzVNZqp/U4+5C1DIfNg97Pu2hKprvFT1nzJNuMvsdX9zwUYqSJqstA59rchhYbl5jeSFEvMy0hwUH8weKKCG/eSjN1qrG1YD1mapH53y+ljTvr5TAcGBTbydPe4VTANZOIHuHviQ8=" 21 | }, 22 | { 23 | "table": "ExampleTableName", 24 | "item": { 25 | "a_number": { 26 | "value": {"N": "55"}, 27 | "action": "nothing" 28 | } 29 | }, 30 | "string_to_sign": "9sgaD2yFEAmIplAUwJS+KesIRv1Xw0Q/DVnzVNZqp/U=" 31 | }, 32 | { 33 | "table": "ExampleTableName", 34 | "item": { 35 | "a_number": { 36 | "value": {"N": "55"}, 37 | "action": "nothing" 38 | }, 39 | "some_string": { 40 | "value": {"S": "test ascii string"}, 41 | "action": "sign" 42 | }, 43 | "bytes": { 44 | "value": {"B": "YW4gYXNjaWkgYnl0ZSBzdHJpbmc="}, 45 | "action": "encrypt" 46 | }, 47 | "list_of_stuff": { 48 | "value": { 49 | "L": [ 50 | {"N": "55.34"}, 51 | {"B": "YW4gYXNjaWkgYnl0ZSBzdHJpbmc="}, 52 | {"S": "test ascii string"} 53 | ] 54 | }, 55 | "action": "encrypt" 56 | } 57 | }, 58 | "string_to_sign": "9sgaD2yFEAmIplAUwJS+KesIRv1Xw0Q/DVnzVNZqp/UncInZHAvfTy5oYrp+SgdgURlDH10T9ybdNSsG8bIGqTlBFaxOsJqklCk0iBayA4EnsPnjpSA3KuEAyhn7CP1QvuXxYe5x5afhZUv3xkXlofC611Cw5eAlbNdEaMf1xKGE96hSpUnIMULGeHZTXUI2ydB2gfFa9+W0FInbJtdwEjlBFaxOsJqklCk0iBayA4EnsPnjpSA3KuEAyhn7CP1QyIKWBxib2u7/xzb6bZmy+BHoUuSg5pgwcqIfCofBkyJTmjdP9D3OLolP1AYapUXm9/WXLUDumhZ2kB+5ISX/7stA59rchhYbl5jeSFEvMy0hwUH8weKKCG/eSjN1qrG1bSWEI5OScw844jVePhRaqST4raDsPSjQVLzgN1rEAl8=" 59 | } 60 | ] -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=invalid-name 2 | """Sphinx configuration.""" 3 | import io 4 | import os 5 | import re 6 | from datetime import datetime 7 | 8 | VERSION_RE = re.compile(r"""__version__ = ['"]([0-9.]+)['"]""") 9 | HERE = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def read(*args): 13 | """Reads complete file contents.""" 14 | return io.open(os.path.join(HERE, *args), encoding="utf-8").read() # pylint: disable=consider-using-with 15 | 16 | 17 | def get_release(): 18 | """Reads the release (full three-part version number) from this module.""" 19 | init = read("..", "src", "dynamodb_encryption_sdk", "identifiers.py") 20 | return VERSION_RE.search(init).group(1) 21 | 22 | 23 | def get_version(): 24 | """Reads the version (MAJOR.MINOR) from this module.""" 25 | _release = get_release() 26 | split_version = _release.split(".") 27 | if len(split_version) == 3: 28 | return ".".join(split_version[:2]) 29 | return _release 30 | 31 | 32 | project = "dynamodb-encryption-sdk-python" 33 | version = get_version() 34 | release = get_release() 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be extensions 37 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 38 | extensions = [ 39 | "sphinx.ext.autodoc", 40 | "sphinx.ext.doctest", 41 | "sphinx.ext.intersphinx", 42 | "sphinx.ext.todo", 43 | "sphinx.ext.coverage", 44 | "sphinx.ext.autosummary", 45 | "sphinx.ext.napoleon", 46 | "sphinx.ext.viewcode", 47 | ] 48 | napoleon_include_special_with_doc = False 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | templates_path = ["_templates"] 52 | 53 | source_suffix = ".rst" # The suffix of source filenames. 54 | master_doc = "index" # The master toctree document. 55 | 56 | copyright = "%s, Amazon" % datetime.now().year # pylint: disable=redefined-builtin 57 | 58 | # List of directories, relative to source directory, that shouldn't be searched 59 | # for source files. 60 | exclude_trees = ["_build"] 61 | 62 | pygments_style = "sphinx" 63 | 64 | autoclass_content = "both" 65 | autodoc_default_options = {"members": True, "show-inheritance": True} 66 | autodoc_member_order = "bysource" 67 | 68 | html_theme = "sphinx_rtd_theme" 69 | html_static_path = ["_static"] 70 | htmlhelp_basename = "%sdoc" % project 71 | 72 | # Example configuration for intersphinx: refer to the Python standard library. 73 | intersphinx_mapping = {"python": ("http://docs.python.org/", None)} 74 | 75 | # autosummary 76 | autosummary_generate = True 77 | -------------------------------------------------------------------------------- /test/functional/test_hypothesis_strategies.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Tests to verify that our advanced hypothesis strategies are behaving as expected.""" 14 | import hypothesis 15 | import pytest 16 | from boto3.dynamodb.types import DYNAMODB_CONTEXT 17 | 18 | from dynamodb_encryption_sdk.internal.formatting.deserialize.attribute import deserialize_attribute 19 | from dynamodb_encryption_sdk.internal.formatting.serialize.attribute import serialize_attribute 20 | from dynamodb_encryption_sdk.transform import ddb_to_dict, dict_to_ddb 21 | 22 | from .hypothesis_strategies import VERY_SLOW_SETTINGS, ddb_items, ddb_negative_numbers, ddb_number, ddb_positive_numbers 23 | 24 | pytestmark = [pytest.mark.functional, pytest.mark.slow, pytest.mark.local, pytest.mark.hypothesis_strategy] 25 | 26 | 27 | @VERY_SLOW_SETTINGS 28 | @hypothesis.given(item=ddb_items) 29 | def test_transformable_item(item): 30 | ddb_json = dict_to_ddb(item) 31 | serialized = {} 32 | for key, value in ddb_json.items(): 33 | serialized[key] = serialize_attribute(value) 34 | deserialized = {} 35 | for key, value in serialized.items(): 36 | deserialized[key] = deserialize_attribute(value) 37 | end_result = ddb_to_dict(deserialized) 38 | assert end_result == item 39 | 40 | 41 | @VERY_SLOW_SETTINGS 42 | @hypothesis.given(item=ddb_items) 43 | def test_serializable_item(item): 44 | ddb_json = dict_to_ddb(item) 45 | end_result = ddb_to_dict(ddb_json) 46 | assert end_result == item 47 | 48 | 49 | @VERY_SLOW_SETTINGS 50 | @hypothesis.given(value=ddb_number) 51 | def test_ddb_number(value): 52 | DYNAMODB_CONTEXT.create_decimal(value) 53 | 54 | 55 | @VERY_SLOW_SETTINGS 56 | @hypothesis.given(value=ddb_negative_numbers) 57 | def test_ddb_negative_numbers(value): 58 | assert value < 0 59 | DYNAMODB_CONTEXT.create_decimal(value) 60 | 61 | 62 | @VERY_SLOW_SETTINGS 63 | @hypothesis.given(value=ddb_positive_numbers) 64 | def test_ddb_positive_numbers(value): 65 | assert value > 0 66 | DYNAMODB_CONTEXT.create_decimal(value) 67 | -------------------------------------------------------------------------------- /test/functional/material_providers/store/test_meta.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional tests for ``dynamodb_encryption_sdk.material_providers.store.meta``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.exceptions import NoKnownVersionError 17 | from dynamodb_encryption_sdk.material_providers.store.meta import MetaStore, MetaStoreAttributeNames 18 | from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider 19 | 20 | from ...functional_test_utils import mock_metastore # noqa=F401 pylint: disable=unused-import 21 | 22 | pytestmark = [pytest.mark.functional, pytest.mark.local] 23 | 24 | 25 | def test_create_table(mock_metastore): 26 | # type: (MetaStore) -> None 27 | assert mock_metastore._table.key_schema == [ 28 | {"AttributeName": MetaStoreAttributeNames.PARTITION.value, "KeyType": "HASH"}, 29 | {"AttributeName": MetaStoreAttributeNames.SORT.value, "KeyType": "RANGE"}, 30 | ] 31 | assert mock_metastore._table.attribute_definitions == [ 32 | {"AttributeName": MetaStoreAttributeNames.PARTITION.value, "AttributeType": "S"}, 33 | {"AttributeName": MetaStoreAttributeNames.SORT.value, "AttributeType": "N"}, 34 | ] 35 | 36 | 37 | def test_max_version_empty(mock_metastore): 38 | # type: (MetaStore) -> None 39 | with pytest.raises(NoKnownVersionError) as excinfo: 40 | mock_metastore.max_version("example_name") 41 | 42 | excinfo.match(r"No known version for name: ") 43 | 44 | 45 | def test_max_version_exists(mock_metastore): 46 | # type: (MetaStore) -> None 47 | mock_metastore.get_or_create_provider("example_name", 2) 48 | mock_metastore.get_or_create_provider("example_name", 5) 49 | 50 | test = mock_metastore.max_version("example_name") 51 | 52 | assert test == 5 53 | 54 | 55 | @pytest.mark.xfail(strict=True) 56 | def test_version_from_material_description(): 57 | assert False 58 | 59 | 60 | def test_provider(mock_metastore): 61 | # type: (MetaStore) -> None 62 | test = mock_metastore.provider("example_name") 63 | 64 | assert isinstance(test, WrappedCryptographicMaterialsProvider) 65 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | 4 | [metadata] 5 | license_file = LICENSE 6 | 7 | [coverage:run] 8 | branch = True 9 | 10 | [coverage:report] 11 | show_missing = True 12 | fail_under = 90 13 | 14 | [mypy] 15 | ignore_missing_imports = True 16 | 17 | [tool:pytest] 18 | markers = 19 | local: superset of unit and functional (does not require network access) 20 | unit: mark test as a unit test (does not require network access) 21 | functional: mark test as a functional test (does not require network access) 22 | integ: mark a test as an integration test (requires network access) 23 | ddb_integ: mark a test as an integration test that requires a real DynamoDB table (requires network access and not used in CI) 24 | accept: mark a test as an acceptance test (requires network access) 25 | generate: mark a test as a generator of test vectors (requires network access) 26 | examples: mark a test as an examples test (requires network access) 27 | hypothesis: mark a test as using hypothesis (will run many times for each pytest call) 28 | hypothesis_strategy: mark a test as testing a hypothesis strategy 29 | slow: mark a test as being known to take a long time to complete (order 5s < t < 60s) 30 | veryslow: mark a test as being known to take a very long time to complete (order t > 60s) 31 | nope: mark a test as being so slow that it should only be very infrequently (order t > 30m) 32 | travis_isolation: mark a test that crashes Travis CI when run with other tests 33 | log_level=DEBUG 34 | 35 | # Flake8 Configuration 36 | [flake8] 37 | max_complexity = 11 38 | max_line_length = 120 39 | import_order_style = google 40 | application_import_names = dynamodb_encryption_sdk 41 | builtins = raw_input 42 | ignore = 43 | # Ignoring D205 and D400 because of false positives 44 | D205, D400, 45 | # Ignoring D401 pending discussion of imperative mood 46 | D401, 47 | # Ignoring D202 (no blank lines after function docstring) because mypy confuses flake8 48 | D202, 49 | # E203 is not PEP8 compliant https://github.com/ambv/black#slices 50 | E203, 51 | # W503 is not PEP8 compliant https://github.com/ambv/black#line-breaks--binary-operators 52 | W503 53 | 54 | 55 | # Doc8 Configuration 56 | [doc8] 57 | max-line-length = 120 58 | 59 | [isort] 60 | line_length = 120 61 | # https://github.com/timothycrosley/isort#multi-line-output-modes 62 | multi_line_output = 3 63 | include_trailing_comma = True 64 | force_grid_wrap = 0 65 | combine_as_imports = True 66 | known_first_party = dynamodb_encryption_sdk 67 | known_third_party =attr,aws_kms_encrypted_client,aws_kms_encrypted_item,aws_kms_encrypted_resource,aws_kms_encrypted_table,boto3,botocore,cryptography,dynamodb_encryption_sdk,functional_test_utils,functional_test_vector_generators,hypothesis,hypothesis_strategies,integration_test_utils,mock,most_recent_provider_encrypted_table,moto,mypy_extensions,pytest,pytest_mock,setuptools,six,wrapped_rsa_encrypted_table,wrapped_symmetric_encrypted_table 68 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/formatting/deserialize/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper functions for deserializing values. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | import struct 20 | 21 | from dynamodb_encryption_sdk.exceptions import DeserializationError 22 | 23 | __all__ = ("unpack_value", "decode_length", "decode_value", "decode_tag") 24 | 25 | 26 | def unpack_value(format_string, stream): 27 | """Helper function to unpack struct data from a stream and update the signature verifier. 28 | 29 | :param str format_string: Struct format string 30 | :param stream: Source data stream 31 | :type stream: io.BytesIO 32 | :returns: Unpacked values 33 | :rtype: tuple 34 | """ 35 | message_bytes = stream.read(struct.calcsize(format_string)) 36 | return struct.unpack(format_string, message_bytes) 37 | 38 | 39 | def decode_length(stream): 40 | """Decode the length of a value from a serialized stream. 41 | 42 | :param stream: Source data stream 43 | :type stream: io.BytesIO 44 | :returns: Decoded length 45 | :rtype: int 46 | """ 47 | (value,) = unpack_value(">I", stream) 48 | return value 49 | 50 | 51 | def decode_value(stream): 52 | """Decode the contents of a value from a serialized stream. 53 | 54 | :param stream: Source data stream 55 | :type stream: io.BytesIO 56 | :returns: Decoded value 57 | :rtype: bytes 58 | """ 59 | length = decode_length(stream) 60 | (value,) = unpack_value(">{:d}s".format(length), stream) 61 | return value 62 | 63 | 64 | def decode_byte(stream): 65 | """Decode a single raw byte from a serialized stream (used for deserialize bool). 66 | 67 | :param stream: Source data stream 68 | :type stream: io.BytesIO 69 | :returns: Decoded value 70 | :rtype: bytes 71 | """ 72 | (value,) = unpack_value(">1s", stream) 73 | return value 74 | 75 | 76 | def decode_tag(stream): 77 | """Decode a tag value from a serialized stream. 78 | 79 | :param stream: Source data stream 80 | :type stream: io.BytesIO 81 | :returns: Decoded tag 82 | :rtype: bytes 83 | """ 84 | (reserved, tag) = unpack_value(">cc", stream) 85 | 86 | if reserved != b"\x00": 87 | raise DeserializationError("Invalid tag: reserved byte is not null") 88 | 89 | return tag 90 | -------------------------------------------------------------------------------- /test/functional/encrypted/test_resource.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional tests for ``dynamodb_encryption_sdk.encrypted.resource``.""" 14 | import pytest 15 | 16 | from ..functional_test_utils import example_table # noqa=F401 pylint: disable=unused-import 17 | from ..functional_test_utils import mock_ddb_service # noqa=F401 pylint: disable=unused-import 18 | from ..functional_test_utils import ( 19 | TEST_REGION_NAME, 20 | TEST_TABLE_NAME, 21 | build_static_jce_cmp, 22 | resource_batch_items_unprocessed_check, 23 | resource_cycle_batch_items_check, 24 | set_parametrized_actions, 25 | set_parametrized_cmp, 26 | set_parametrized_item, 27 | ) 28 | 29 | pytestmark = [pytest.mark.functional, pytest.mark.local] 30 | 31 | 32 | def pytest_generate_tests(metafunc): 33 | set_parametrized_actions(metafunc) 34 | set_parametrized_cmp(metafunc) 35 | set_parametrized_item(metafunc) 36 | 37 | 38 | def _resource_cycle_batch_items_check(materials_provider, initial_actions, initial_item): 39 | resource_cycle_batch_items_check( 40 | materials_provider, initial_actions, initial_item, TEST_TABLE_NAME, TEST_REGION_NAME 41 | ) 42 | 43 | 44 | def _resource_batch_items_unprocessed_check(materials_provider, initial_actions, initial_item): 45 | resource_batch_items_unprocessed_check( 46 | materials_provider, initial_actions, initial_item, TEST_TABLE_NAME, TEST_REGION_NAME 47 | ) 48 | 49 | 50 | def test_ephemeral_batch_item_cycle(example_table, some_cmps, parametrized_actions, parametrized_item): 51 | """Test a small number of curated CMPs against a small number of curated items.""" 52 | _resource_cycle_batch_items_check(some_cmps, parametrized_actions, parametrized_item) 53 | 54 | 55 | def test_batch_item_unprocessed(example_table, parametrized_actions, parametrized_item): 56 | """Test Unprocessed Items handling with a single ephemeral static CMP against a small number of curated items.""" 57 | _resource_batch_items_unprocessed_check( 58 | build_static_jce_cmp("AES", 256, "HmacSHA256", 256), parametrized_actions, parametrized_item 59 | ) 60 | 61 | 62 | @pytest.mark.travis_isolation 63 | @pytest.mark.slow 64 | def test_ephemeral_batch_item_cycle_slow(example_table, all_the_cmps, parametrized_actions, parametrized_item): 65 | """Test ALL THE CMPS against a small number of curated items.""" 66 | _resource_cycle_batch_items_check(all_the_cmps, parametrized_actions, parametrized_item) 67 | -------------------------------------------------------------------------------- /test/integration/material_providers/store/test_meta.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Integration tests for ``dynamodb_encryption_sdk.material_providers.store.meta``.""" 14 | import pytest 15 | 16 | from dynamodb_encryption_sdk.exceptions import NoKnownVersionError 17 | from dynamodb_encryption_sdk.material_providers.store.meta import MetaStore, MetaStoreAttributeNames 18 | 19 | from ...integration_test_utils import temp_metastore # noqa=F401 pylint: disable=unused-import 20 | 21 | pytestmark = [pytest.mark.integ, pytest.mark.ddb_integ] 22 | 23 | 24 | def test_max_version_empty(temp_metastore): 25 | # type: (MetaStore) -> None 26 | with pytest.raises(NoKnownVersionError) as excinfo: 27 | temp_metastore.max_version("example_name") 28 | 29 | excinfo.match(r"No known version for name: ") 30 | 31 | 32 | def test_max_version_exists(temp_metastore): 33 | # type: (MetaStore) -> None 34 | temp_metastore.get_or_create_provider("example_name", 2) 35 | temp_metastore.get_or_create_provider("example_name", 5) 36 | 37 | test = temp_metastore.max_version("example_name") 38 | 39 | assert test == 5 40 | 41 | 42 | def test_get_or_create_provider_new_version(temp_metastore): 43 | # type: (MetaStore) -> None 44 | with pytest.raises(NoKnownVersionError): 45 | temp_metastore.max_version("example_name") 46 | 47 | temp_metastore.get_or_create_provider("example_name", 5) 48 | 49 | test = temp_metastore.max_version("example_name") 50 | 51 | assert test == 5 52 | 53 | 54 | def test_get_or_create_provider_existing_version(temp_metastore): 55 | # type: (MetaStore) -> None 56 | # create version 57 | temp_metastore.get_or_create_provider("example_name", 5) 58 | 59 | # retrieve version 60 | temp_metastore.get_or_create_provider("example_name", 5) 61 | 62 | test = temp_metastore.max_version("example_name") 63 | 64 | assert test == 5 65 | 66 | 67 | def test_get_or_create_provider_no_overwrite(temp_metastore): 68 | # type: (MetaStore) -> None 69 | # create version 70 | provider_1 = temp_metastore.get_or_create_provider("example_name", 5) 71 | 72 | initial_item = temp_metastore._table.get_item( 73 | Key={MetaStoreAttributeNames.PARTITION.value: "example_name", MetaStoreAttributeNames.SORT.value: 5} 74 | )["Item"] 75 | 76 | # retrieve version 77 | provider_2 = temp_metastore.get_or_create_provider("example_name", 5) 78 | 79 | assert provider_1 == provider_2 80 | 81 | second_item = temp_metastore._table.get_item( 82 | Key={MetaStoreAttributeNames.PARTITION.value: "example_name", MetaStoreAttributeNames.SORT.value: 5} 83 | )["Item"] 84 | 85 | assert initial_item == second_item 86 | -------------------------------------------------------------------------------- /examples/README.rst: -------------------------------------------------------------------------------- 1 | ######################################### 2 | AWS DynamoDB Encryption Client Examples 3 | ######################################### 4 | 5 | This section features examples that show you 6 | how to use the AWS DynamoDB Encryption Client. 7 | We demonstrate how to use the encryption and decryption APIs 8 | and how to set up some common configuration patterns. 9 | 10 | APIs 11 | ==== 12 | 13 | The AWS DynamoDB Encryption Client provides four high-level APIs: `EncryptedClient`, `EncryptedItem`, 14 | `EncryptedResource`, and `EncryptedTable`. 15 | 16 | You can find examples that demonstrate these APIs 17 | in the `examples/src/dynamodb_encryption_sdk_examples <./src/dynamodb_encryption_sdk_examples>`_ directory. 18 | Each of these examples uses AWS KMS as the materials provider. 19 | 20 | * `How to use the EncryptedClient API <./src/dynamodb_encryption_sdk_examples/aws_kms_encrypted_client.py>`_ 21 | * `How to use the EncryptedItem API <./src/dynamodb_encryption_sdk_examples/aws_kms_encrypted_item.py>`_ 22 | * `How to use the EncryptedResource API <./src/dynamodb_encryption_sdk_examples/aws_kms_encrypted_resource.py>`_ 23 | * `How to use the EncryptedTable API <./src/dynamodb_encryption_sdk_examples/aws_kms_encrypted_table.py>`_ 24 | 25 | Material Providers 26 | ================== 27 | 28 | To use the encryption and decryption APIs, you need to describe how you want the library to protect your data keys. 29 | You can do this by configuring material providers. AWS KMS is the most common material provider used with the AWS DynamoDB Encryption 30 | SDK, and each of the API examples above uses AWS KMS. This section describes the other providers that come bundled 31 | with this library. 32 | 33 | * `How to use the CachingMostRecentProvider <./src/dynamodb_encryption_sdk_examples/most_recent_provider_encrypted_table.py>`_ 34 | * `How to use raw symmetric wrapping keys <./src/dynamodb_encryption_sdk_examples/wrapped_symmetric_encrypted_table.py>`_ 35 | * `How to use raw asymmetric wrapping keys <./src/dynamodb_encryption_sdk_examples/wrapped_rsa_encrypted_table.py>`_ 36 | 37 | For more details on the different type of material providers, see `How to choose a cryptographic materials provider `_. 38 | 39 | Running the examples 40 | ==================== 41 | 42 | In order to run these examples, these things must be configured: 43 | 44 | #. Ensure that AWS credentials are available in one of the `automatically discoverable credential locations`_. 45 | #. The ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID`` environment variable 46 | must be set to a valid `AWS KMS CMK ARN`_ that can be used by the available credentials. 47 | #. The ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID`` and ``AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2`` environment variables 48 | must be set to two related AWS KMS Multi-Region key ids in different regions. 49 | #. The ``DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME`` environment variable must be set to a valid 50 | DynamoDB table name, in the default region, to which the discoverable credentials have 51 | read, write, and describe permissions. 52 | 53 | .. _automatically discoverable credential locations: http://boto3.readthedocs.io/en/latest/guide/configuration.html 54 | .. _AWS KMS CMK ARN: http://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html 55 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/crypto/encryption.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functions to handle encrypting and decrypting DynamoDB attributes. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | 20 | from typing import Text 21 | 22 | from dynamodb_encryption_sdk.delegated_keys import DelegatedKey # noqa pylint: disable=unused-import 23 | from dynamodb_encryption_sdk.internal import dynamodb_types 24 | from dynamodb_encryption_sdk.internal.formatting.deserialize.attribute import deserialize_attribute 25 | from dynamodb_encryption_sdk.internal.formatting.serialize.attribute import serialize_attribute 26 | from dynamodb_encryption_sdk.internal.identifiers import Tag 27 | 28 | __all__ = ("encrypt_attribute", "decrypt_attribute") 29 | 30 | 31 | def encrypt_attribute(attribute_name, attribute, encryption_key, algorithm): 32 | # type: (Text, dynamodb_types.RAW_ATTRIBUTE, DelegatedKey, Text) -> dynamodb_types.BINARY_ATTRIBUTE 33 | """Encrypt a single DynamoDB attribute. 34 | 35 | :param str attribute_name: DynamoDB attribute name 36 | :param dict attribute: Plaintext DynamoDB attribute 37 | :param DelegatedKey encryption_key: DelegatedKey to use to encrypt the attribute 38 | :param str algorithm: Encryption algorithm descriptor (passed to encryption_key as algorithm) 39 | :returns: Encrypted DynamoDB binary attribute 40 | :rtype: dict 41 | """ 42 | serialized_attribute = serialize_attribute(attribute) 43 | encrypted_attribute = encryption_key.encrypt( 44 | algorithm=algorithm, name=attribute_name, plaintext=serialized_attribute 45 | ) 46 | # for some reason pylint can't follow the Enum member attributes 47 | return {Tag.BINARY.dynamodb_tag: encrypted_attribute} # pylint: disable=no-member 48 | 49 | 50 | def decrypt_attribute(attribute_name, attribute, decryption_key, algorithm): 51 | # type: (Text, dynamodb_types.RAW_ATTRIBUTE, DelegatedKey, Text) -> dynamodb_types.RAW_ATTRIBUTE 52 | """Decrypt a single DynamoDB attribute. 53 | 54 | :param str attribute_name: DynamoDB attribute name 55 | :param dict attribute: Encrypted DynamoDB attribute 56 | :param DelegatedKey encryption_key: DelegatedKey to use to encrypt the attribute 57 | :param str algorithm: Decryption algorithm descriptor (passed to encryption_key as algorithm) 58 | :returns: Plaintext DynamoDB attribute 59 | :rtype: dict 60 | """ 61 | # for some reason pylint can't follow the Enum member attributes 62 | encrypted_attribute = attribute[Tag.BINARY.dynamodb_tag] # pylint: disable=no-member 63 | decrypted_attribute = decryption_key.decrypt( 64 | algorithm=algorithm, name=attribute_name, ciphertext=encrypted_attribute 65 | ) 66 | return deserialize_attribute(decrypted_attribute) 67 | -------------------------------------------------------------------------------- /test/acceptance/encrypted/test_item.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Acceptance tests for ``dynamodb_encryption_sdk.encrypted.item``.""" 14 | import pytest 15 | from moto import mock_dynamodb2 16 | 17 | from dynamodb_encryption_sdk.encrypted import CryptoConfig 18 | from dynamodb_encryption_sdk.encrypted.item import decrypt_dynamodb_item 19 | from dynamodb_encryption_sdk.structures import EncryptionContext 20 | 21 | from ..acceptance_test_utils import load_scenarios 22 | 23 | pytestmark = [pytest.mark.accept] 24 | 25 | 26 | def _item_check(materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions): 27 | cmp = materials_provider() # Some of the materials providers need to be constructed inside the test 28 | encryption_context = EncryptionContext( 29 | table_name=table_name, 30 | partition_key_name=table_index["partition"], 31 | sort_key_name=table_index.get("sort", None), 32 | attributes=ciphertext_item, 33 | ) 34 | crypto_config = CryptoConfig( 35 | materials_provider=cmp, encryption_context=encryption_context, attribute_actions=attribute_actions 36 | ) 37 | decrypted_item = decrypt_dynamodb_item(ciphertext_item.copy(), crypto_config) 38 | assert set(decrypted_item.keys()) == set(plaintext_item.keys()) 39 | for key in decrypted_item: 40 | if key == "version": 41 | continue 42 | assert decrypted_item[key] == plaintext_item[key] 43 | 44 | 45 | @mock_dynamodb2 46 | @pytest.mark.local 47 | @pytest.mark.parametrize( 48 | "materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep", 49 | load_scenarios(online=False), 50 | ) 51 | def test_item_encryptor_offline( 52 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 53 | ): 54 | metatable = None 55 | try: 56 | metatable = prep() 57 | return _item_check( 58 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions 59 | ) 60 | finally: 61 | if metatable: 62 | metatable.delete() 63 | metatable.wait_until_not_exists() 64 | 65 | 66 | @pytest.mark.ddb_integ 67 | @pytest.mark.parametrize( 68 | "materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep", 69 | load_scenarios(online=True), 70 | ) 71 | def test_item_encryptor_online( 72 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 73 | ): 74 | metatable = None 75 | try: 76 | metatable = prep() 77 | return _item_check( 78 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions 79 | ) 80 | finally: 81 | if metatable: 82 | metatable.delete() 83 | metatable.wait_until_not_exists() 84 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/internal/validators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Custom validators for ``attrs``. 14 | 15 | .. warning:: 16 | No guarantee is provided on the modules and APIs within this 17 | namespace staying consistent. Directly reference at your own risk. 18 | """ 19 | 20 | __all__ = ("dictionary_validator", "iterable_validator") 21 | 22 | 23 | def dictionary_validator(key_type, value_type): 24 | """Validator for ``attrs`` that performs deep type checking of dictionaries.""" 25 | 26 | def _validate_dictionary(instance, attribute, value): 27 | # pylint: disable=unused-argument 28 | """Validate that a dictionary is structured as expected. 29 | 30 | :raises TypeError: if ``value`` is not a dictionary 31 | :raises TypeError: if ``value`` keys are not all of ``key_type`` type 32 | :raises TypeError: if ``value`` values are not all of ``value_type`` type 33 | """ 34 | if not isinstance(value, dict): 35 | raise TypeError('"{}" must be a dictionary'.format(attribute.name)) 36 | 37 | for key, data in value.items(): 38 | if not isinstance(key, key_type): 39 | raise TypeError( 40 | '"{name}" dictionary keys must be of type "{type}"'.format(name=attribute.name, type=key_type) 41 | ) 42 | 43 | if not isinstance(data, value_type): 44 | raise TypeError( 45 | '"{name}" dictionary values must be of type "{type}"'.format(name=attribute.name, type=value_type) 46 | ) 47 | 48 | return _validate_dictionary 49 | 50 | 51 | def iterable_validator(iterable_type, member_type): 52 | """Validator for ``attrs`` that performs deep type checking of iterables.""" 53 | 54 | def _validate_tuple(instance, attribute, value): 55 | # pylint: disable=unused-argument 56 | """Validate that a dictionary is structured as expected. 57 | 58 | :raises TypeError: if ``value`` is not of ``iterable_type`` type 59 | :raises TypeError: if ``value`` members are not all of ``member_type`` type 60 | """ 61 | if not isinstance(value, iterable_type): 62 | raise TypeError('"{name}" must be a {type}'.format(name=attribute.name, type=iterable_type)) 63 | 64 | for member in value: 65 | if not isinstance(member, member_type): 66 | raise TypeError( 67 | '"{name}" members must all be of type "{type}"'.format(name=attribute.name, type=member_type) 68 | ) 69 | 70 | return _validate_tuple 71 | 72 | 73 | def callable_validator(instance, attribute, value): 74 | # pylint: disable=unused-argument 75 | """Validate that an attribute value is callable. 76 | 77 | :raises TypeError: if ``value`` is not callable 78 | """ 79 | if not callable(value): 80 | raise TypeError('"{name}" value "{value}" must be callable'.format(name=attribute.name, value=value)) 81 | -------------------------------------------------------------------------------- /test/integration/encrypted/test_client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Integration tests for ``dynamodb_encryption_sdk.encrypted.client``.""" 14 | import pytest 15 | 16 | from ..integration_test_utils import ( # noqa pylint: disable=unused-import 17 | ddb_table_name, 18 | functional_test_utils, 19 | set_parameterized_kms_cmps, 20 | ) 21 | 22 | pytestmark = [pytest.mark.integ, pytest.mark.ddb_integ] 23 | 24 | 25 | def pytest_generate_tests(metafunc): 26 | functional_test_utils.set_parametrized_actions(metafunc) 27 | functional_test_utils.set_parametrized_cmp(metafunc) 28 | functional_test_utils.set_parametrized_item(metafunc) 29 | set_parameterized_kms_cmps(metafunc) 30 | 31 | 32 | def test_ephemeral_item_cycle(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 33 | """Test a small number of curated CMPs against a small number of curated items.""" 34 | functional_test_utils.client_cycle_single_item_check( 35 | some_cmps, parametrized_actions, parametrized_item, ddb_table_name 36 | ) 37 | 38 | 39 | def test_ephemeral_item_cycle_kms(ddb_table_name, all_aws_kms_cmp_builders, parametrized_actions, parametrized_item): 40 | """Test the AWS KMS CMP against a small number of curated items.""" 41 | functional_test_utils.client_cycle_single_item_check( 42 | all_aws_kms_cmp_builders(), parametrized_actions, parametrized_item, ddb_table_name 43 | ) 44 | 45 | 46 | def test_ephemeral_batch_item_cycle(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 47 | """Test a small number of curated CMPs against a small number of curated items.""" 48 | functional_test_utils.client_cycle_batch_items_check( 49 | some_cmps, parametrized_actions, parametrized_item, ddb_table_name 50 | ) 51 | 52 | 53 | def test_ephemeral_batch_item_cycle_kms( 54 | ddb_table_name, all_aws_kms_cmp_builders, parametrized_actions, parametrized_item 55 | ): 56 | """Test the AWS KMS CMP against a small number of curated items.""" 57 | functional_test_utils.client_cycle_batch_items_check( 58 | all_aws_kms_cmp_builders(), parametrized_actions, parametrized_item, ddb_table_name 59 | ) 60 | 61 | 62 | def test_ephemeral_batch_item_cycle_scan_paginator(ddb_table_name, some_cmps, parametrized_actions, parametrized_item): 63 | """Test a small number of curated CMPs against a small number of curated items using the scan paginator.""" 64 | functional_test_utils.client_cycle_batch_items_check_scan_paginator( 65 | some_cmps, parametrized_actions, parametrized_item, ddb_table_name 66 | ) 67 | 68 | 69 | def test_ephemeral_batch_item_cycle_scan_paginator_kms( 70 | ddb_table_name, all_aws_kms_cmp_builders, parametrized_actions, parametrized_item 71 | ): 72 | """Test a the AWS KMS CMP against a small number of curated items using the scan paginator.""" 73 | functional_test_utils.client_cycle_batch_items_check_scan_paginator( 74 | all_aws_kms_cmp_builders(), parametrized_actions, parametrized_item, ddb_table_name 75 | ) 76 | -------------------------------------------------------------------------------- /examples/src/dynamodb_encryption_sdk_examples/aws_kms_encrypted_table.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Example showing use of AWS KMS CMP with EncryptedTable.""" 14 | import boto3 15 | from boto3.dynamodb.types import Binary 16 | 17 | from dynamodb_encryption_sdk.encrypted.table import EncryptedTable 18 | from dynamodb_encryption_sdk.identifiers import CryptoAction 19 | from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider 20 | from dynamodb_encryption_sdk.structures import AttributeActions 21 | 22 | 23 | def encrypt_item(table_name, aws_cmk_id): 24 | """Demonstrate use of EncryptedTable to transparently encrypt an item.""" 25 | index_key = {"partition_attribute": "is this", "sort_attribute": 55} 26 | plaintext_item = { 27 | "example": "data", 28 | "some numbers": 99, 29 | "and some binary": Binary(b"\x00\x01\x02"), 30 | "leave me": "alone", # We want to ignore this attribute 31 | } 32 | # Collect all of the attributes that will be encrypted (used later). 33 | encrypted_attributes = set(plaintext_item.keys()) 34 | encrypted_attributes.remove("leave me") 35 | # Collect all of the attributes that will not be encrypted (used later). 36 | unencrypted_attributes = set(index_key.keys()) 37 | unencrypted_attributes.add("leave me") 38 | # Add the index pairs to the item. 39 | plaintext_item.update(index_key) 40 | 41 | # Create a normal table resource. 42 | table = boto3.resource("dynamodb").Table(table_name) # generated code confuse pylint: disable=no-member 43 | # Create a crypto materials provider using the specified AWS KMS key. 44 | aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id) 45 | # Create attribute actions that tells the encrypted table to encrypt all attributes except one. 46 | actions = AttributeActions( 47 | default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} 48 | ) 49 | # Use these objects to create an encrypted table resource. 50 | encrypted_table = EncryptedTable(table=table, materials_provider=aws_kms_cmp, attribute_actions=actions) 51 | 52 | # Put the item to the table, using the encrypted table resource to transparently encrypt it. 53 | encrypted_table.put_item(Item=plaintext_item) 54 | 55 | # Get the encrypted item using the standard table resource. 56 | encrypted_item = table.get_item(Key=index_key)["Item"] 57 | 58 | # Get the item using the encrypted table resource, transparently decyrpting it. 59 | decrypted_item = encrypted_table.get_item(Key=index_key)["Item"] 60 | 61 | # Verify that all of the attributes are different in the encrypted item 62 | for name in encrypted_attributes: 63 | assert encrypted_item[name] != plaintext_item[name] 64 | assert decrypted_item[name] == plaintext_item[name] 65 | 66 | # Verify that all of the attributes that should not be encrypted were not. 67 | for name in unencrypted_attributes: 68 | assert decrypted_item[name] == encrypted_item[name] == plaintext_item[name] 69 | 70 | # Clean up the item 71 | encrypted_table.delete_item(Key=index_key) 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Security issue notifications 11 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 12 | 13 | 14 | ## Reporting Bugs/Feature Requests 15 | 16 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 17 | 18 | When filing an issue, please check [existing open](https://github.com/aws/aws-dynamodb-encryption-python/issues), or [recently closed](https://github.com/aws/aws-dynamodb-encryption-python/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 19 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 20 | 21 | * A reproducible test case or series of steps 22 | * The version of our code being used 23 | * Any modifications you've made relevant to the bug 24 | * Anything unusual about your environment or deployment 25 | 26 | 27 | ## Contributing via Pull Requests 28 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 29 | 30 | 1. You are working against the latest source on the *master* branch. 31 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 32 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 33 | 34 | To send us a pull request, please: 35 | 36 | 1. Fork the repository. 37 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 38 | 3. Ensure local tests pass. 39 | 4. Commit to your fork using clear commit messages. 40 | 5. Send us a pull request, answering any default questions in the pull request interface. 41 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 42 | 43 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 44 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 45 | 46 | 47 | ## Finding contributions to work on 48 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/aws-dynamodb-encryption-python/labels/help%20wanted) issues is a great place to start. 49 | 50 | 51 | ## Code of Conduct 52 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 53 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 54 | opensource-codeofconduct@amazon.com with any additional questions or comments. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws/aws-dynamodb-encryption-python/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /test/functional/hypothesis_strategies.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"). You 5 | # may not use this file except in compliance with the License. A copy of 6 | # the License is located at 7 | # 8 | # http://aws.amazon.com/apache2.0/ 9 | # 10 | # or in the "license" file accompanying this file. This file is 11 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 12 | # ANY KIND, either express or implied. See the License for the specific 13 | # language governing permissions and limitations under the License. 14 | """Hypothesis strategies for use in tests.""" 15 | from decimal import Decimal 16 | 17 | import hypothesis 18 | from boto3.dynamodb.types import Binary 19 | from hypothesis.strategies import binary, booleans, deferred, dictionaries, fractions, just, lists, none, sets, text 20 | 21 | SLOW_SETTINGS = hypothesis.settings( 22 | suppress_health_check=( 23 | hypothesis.HealthCheck.too_slow, 24 | hypothesis.HealthCheck.data_too_large, 25 | hypothesis.HealthCheck.large_base_example, 26 | # Hypothesis requires that we acknowledge that the example_table fixure 27 | # is not reset between examples generated by hypothesis.given. 28 | # This is the desired behavior for example_table, so supress this check 29 | hypothesis.HealthCheck.function_scoped_fixture, 30 | ), 31 | deadline=None, 32 | ) 33 | VERY_SLOW_SETTINGS = hypothesis.settings(SLOW_SETTINGS, max_examples=1000) 34 | MAX_ITEM_BYTES = 400 * 1024 * 1024 35 | 36 | # _MIN_NUMBER = Decimal('1E-128') # The DDB min is 1E-130, but DYNAMODB_CONTEXT Emin is -128 37 | # _MAX_NUMBER = Decimal('9.9999999999999999999999999999999999999E+125') 38 | # boto3 does not correctly handle conversion of large edge case values at this time 39 | _MIN_NUMBER = Decimal("1E-38") 40 | _MAX_NUMBER = Decimal("9.{}E37".format("9" * 37)) 41 | 42 | 43 | ddb_string = text(min_size=1, max_size=MAX_ITEM_BYTES) 44 | ddb_string_set = sets(ddb_string, min_size=1) 45 | 46 | 47 | def _ddb_fraction_to_decimal(val): 48 | """Hypothesis does not support providing a custom Context, so working around that.""" 49 | return Decimal(val.numerator) / Decimal(val.denominator) 50 | 51 | 52 | def _negative(val): 53 | return val * Decimal("-1") 54 | 55 | 56 | ddb_positive_numbers = fractions(min_value=_MIN_NUMBER, max_value=_MAX_NUMBER).map(_ddb_fraction_to_decimal) 57 | ddb_negative_numbers = ddb_positive_numbers.map(_negative) 58 | 59 | ddb_number = ddb_negative_numbers | just(Decimal("0")) | ddb_positive_numbers 60 | ddb_number_set = sets(ddb_number, min_size=1) 61 | 62 | ddb_binary = binary(min_size=1, max_size=MAX_ITEM_BYTES).map(Binary) 63 | ddb_binary_set = sets(ddb_binary, min_size=1) 64 | 65 | ddb_boolean = booleans() 66 | ddb_null = none() 67 | 68 | ddb_scalar_types = ddb_string | ddb_number | ddb_binary | ddb_boolean | ddb_null 69 | 70 | ddb_set_types = ddb_string_set | ddb_number_set | ddb_binary_set 71 | ddb_attribute_names = text(min_size=1, max_size=255) 72 | # List and Map types have a max depth of 32 73 | # https://github.com/aws/aws-dynamodb-encryption-python/issues/141 74 | ddb_map_type = deferred( 75 | lambda: dictionaries( 76 | keys=ddb_attribute_names, values=(ddb_scalar_types | ddb_set_types | ddb_list_type | ddb_map_type), min_size=1 77 | ) 78 | ) 79 | ddb_list_type = deferred(lambda: lists(ddb_scalar_types | ddb_set_types | ddb_list_type | ddb_map_type, min_size=1)) 80 | ddb_document_types = ddb_map_type | ddb_list_type 81 | 82 | ddb_attribute_values = ddb_scalar_types | ddb_set_types | ddb_list_type 83 | 84 | ddb_items = dictionaries(keys=ddb_attribute_names, values=ddb_attribute_values, min_size=1) 85 | 86 | 87 | material_descriptions = deferred(lambda: dictionaries(keys=text(), values=text(), min_size=1)) 88 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/material_providers/static.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Cryptographic materials provider for use with pre-configured encryption and decryption materials.""" 14 | from typing import Optional 15 | 16 | import attr 17 | 18 | from dynamodb_encryption_sdk.materials import CryptographicMaterials # noqa pylint: disable=unused-import 19 | from dynamodb_encryption_sdk.materials import DecryptionMaterials, EncryptionMaterials 20 | from dynamodb_encryption_sdk.structures import EncryptionContext # noqa pylint: disable=unused-import 21 | 22 | from . import CryptographicMaterialsProvider 23 | 24 | __all__ = ("StaticCryptographicMaterialsProvider",) 25 | 26 | 27 | @attr.s(init=False) 28 | class StaticCryptographicMaterialsProvider(CryptographicMaterialsProvider): 29 | """Manually combine encryption and decryption materials for use as a cryptographic materials provider. 30 | 31 | :param DecryptionMaterials decryption_materials: Decryption materials to provide (optional) 32 | :param EncryptionMaterials encryption_materials: Encryption materials to provide (optional) 33 | """ 34 | 35 | _decryption_materials = attr.ib( 36 | validator=attr.validators.optional(attr.validators.instance_of(DecryptionMaterials)), default=None 37 | ) 38 | _encryption_materials = attr.ib( 39 | validator=attr.validators.optional(attr.validators.instance_of(EncryptionMaterials)), default=None 40 | ) 41 | 42 | def __init__( 43 | self, 44 | decryption_materials=None, # type: Optional[DecryptionMaterials] 45 | encryption_materials=None, # type: Optional[EncryptionMaterials] 46 | ): # noqa=D107 47 | # type: (...) -> None 48 | # Workaround pending resolution of attrs/mypy interaction. 49 | # https://github.com/python/mypy/issues/2088 50 | # https://github.com/python-attrs/attrs/issues/215 51 | self._decryption_materials = decryption_materials 52 | self._encryption_materials = encryption_materials 53 | attr.validate(self) 54 | 55 | def decryption_materials(self, encryption_context): 56 | # type: (EncryptionContext) -> CryptographicMaterials 57 | """Return the static decryption materials. 58 | 59 | :param EncryptionContext encryption_context: Encryption context for request (not 60 | used by :class:`StaticCryptographicMaterialsProvider`) 61 | :raises AttributeError: if no decryption materials are available 62 | """ 63 | if self._decryption_materials is None: 64 | return super(StaticCryptographicMaterialsProvider, self).decryption_materials(encryption_context) 65 | 66 | return self._decryption_materials 67 | 68 | def encryption_materials(self, encryption_context): 69 | # type: (EncryptionContext) -> CryptographicMaterials 70 | """Return the static encryption materials. 71 | 72 | :param EncryptionContext encryption_context: Encryption context for request (not 73 | used by :class:`StaticCryptographicMaterialsProvider`) 74 | :raises AttributeError: if no encryption materials are available 75 | """ 76 | if self._encryption_materials is None: 77 | return super(StaticCryptographicMaterialsProvider, self).encryption_materials(encryption_context) 78 | 79 | return self._encryption_materials 80 | -------------------------------------------------------------------------------- /test/functional/delegated_keys/test_jce.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional test suite for ``dynamodb_encryption_sdk.delegated_keys.jce``.""" 14 | from __future__ import division 15 | 16 | import logging 17 | 18 | import pytest 19 | from cryptography.hazmat.backends import default_backend 20 | from cryptography.hazmat.primitives import serialization 21 | 22 | from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey 23 | from dynamodb_encryption_sdk.internal.crypto.jce_bridge.authentication import JAVA_AUTHENTICATOR 24 | from dynamodb_encryption_sdk.internal.identifiers import MinimumKeySizes 25 | 26 | pytestmark = [pytest.mark.functional, pytest.mark.local] 27 | 28 | 29 | def _find_aes_key_length(key): 30 | return len(key) * 8 31 | 32 | 33 | def _find_rsa_key_length(key): 34 | loaded_key = serialization.load_der_private_key(data=key, password=None, backend=default_backend()) 35 | return loaded_key.key_size 36 | 37 | 38 | @pytest.mark.parametrize( 39 | "algorithm, requested_bits, expected_bits, length_finder", 40 | ( 41 | ("AES", 256, 256, _find_aes_key_length), 42 | ("AESWrap", 256, 256, _find_aes_key_length), 43 | ("RSA", 4096, 4096, _find_rsa_key_length), 44 | ("HmacSHA512", 256, 256, _find_aes_key_length), 45 | ("HmacSHA256", 256, 256, _find_aes_key_length), 46 | ("HmacSHA384", 256, 256, _find_aes_key_length), 47 | ("HmacSHA224", 256, 256, _find_aes_key_length), 48 | ("SHA512withRSA", 4096, 4096, _find_rsa_key_length), 49 | ("SHA256withRSA", 4096, 4096, _find_rsa_key_length), 50 | ("SHA384withRSA", 4096, 4096, _find_rsa_key_length), 51 | ("SHA224withRSA", 4096, 4096, _find_rsa_key_length), 52 | ), 53 | ) 54 | def test_generate_correct_key_length(algorithm, requested_bits, expected_bits, length_finder): 55 | test = JceNameLocalDelegatedKey.generate(algorithm, requested_bits) 56 | 57 | assert length_finder(test.key) == expected_bits 58 | 59 | 60 | def build_short_key_cases(): 61 | for algorithm in JAVA_AUTHENTICATOR: 62 | if algorithm.upper().startswith("HMAC"): 63 | message = "HMAC keys smaller than {} bits are unsafe".format(MinimumKeySizes.HMAC.value) 64 | yield (algorithm, MinimumKeySizes.HMAC.value, False, message) 65 | yield (algorithm, MinimumKeySizes.HMAC.value - 1, True, message) 66 | 67 | elif algorithm.upper().endswith("RSA"): 68 | message = "RSA keys smaller than {} bits are unsafe".format(MinimumKeySizes.RSA.value) 69 | yield (algorithm, MinimumKeySizes.RSA.value, False, message) 70 | yield (algorithm, MinimumKeySizes.RSA.value // 2, True, message) 71 | 72 | message = "RSA keys smaller than {} bits are unsafe".format(MinimumKeySizes.RSA.value) 73 | yield ("RSA", MinimumKeySizes.RSA.value, False, message) 74 | yield ("RSA", MinimumKeySizes.RSA.value // 2, True, message) 75 | 76 | 77 | @pytest.mark.travis_isolation 78 | @pytest.mark.parametrize("algorithm, key_bits, too_short, error_message", build_short_key_cases()) 79 | def test_warn_on_short_keys(caplog, algorithm, key_bits, too_short, error_message): 80 | with caplog.at_level(logging.DEBUG): 81 | _test = JceNameLocalDelegatedKey.generate(algorithm, key_bits) # noqa=F401 82 | 83 | logging_results = caplog.text 84 | assert (too_short and error_message in logging_results) or (not too_short and error_message not in logging_results) 85 | -------------------------------------------------------------------------------- /test/integration/integration_test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Helper utilities for integration tests.""" 14 | import os 15 | from functools import partial 16 | 17 | import pytest 18 | 19 | from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider 20 | 21 | # convenience imports 22 | try: 23 | from ..functional import hypothesis_strategies # noqa=F401 pylint: disable=unused-import 24 | from ..functional import functional_test_utils 25 | except (ImportError, ValueError, SystemError): 26 | if "AWS_ENCRYPTION_SDK_EXAMPLES_TESTING" not in os.environ: 27 | raise 28 | 29 | AWS_KMS_KEY_ID = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID" 30 | AWS_KMS_MRK_KEY_ID = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID" 31 | AWS_KMS_MRK_KEY_ID_2 = "AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2" 32 | DDB_TABLE_NAME = "DDB_ENCRYPTION_CLIENT_TEST_TABLE_NAME" 33 | 34 | 35 | def cmk_arn_value(env_variable=AWS_KMS_KEY_ID): 36 | """Retrieve the target CMK ARN from environment variable.""" 37 | arn = os.environ.get(env_variable, None) 38 | if arn is None: 39 | raise ValueError( 40 | 'Environment variable "{}" must be set to a valid KMS CMK ARN for integration tests to run'.format( 41 | env_variable 42 | ) 43 | ) 44 | if arn.startswith("arn:") and ":alias/" not in arn: 45 | return arn 46 | raise ValueError("KMS CMK ARN provided for integration tests must be a key not an alias") 47 | 48 | 49 | @pytest.fixture 50 | def cmk_arn(): 51 | """As of Pytest 4.0.0, fixtures cannot be called directly.""" 52 | return cmk_arn_value(AWS_KMS_KEY_ID) 53 | 54 | 55 | @pytest.fixture 56 | def cmk_mrk_arn(): 57 | """As of Pytest 4.0.0, fixtures cannot be called directly.""" 58 | return cmk_arn_value(AWS_KMS_MRK_KEY_ID) 59 | 60 | 61 | @pytest.fixture 62 | def second_cmk_mrk_arn(): 63 | """As of Pytest 4.0.0, fixtures cannot be called directly.""" 64 | return cmk_arn_value(AWS_KMS_MRK_KEY_ID_2) 65 | 66 | 67 | def _build_kms_cmp(require_attributes): 68 | inner_cmp = AwsKmsCryptographicMaterialsProvider(key_id=cmk_arn_value()) 69 | if require_attributes: 70 | return functional_test_utils.PassThroughCryptographicMaterialsProviderThatRequiresAttributes(inner_cmp) 71 | 72 | return inner_cmp 73 | 74 | 75 | def set_parameterized_kms_cmps(metafunc, require_attributes=True): 76 | 77 | if "all_aws_kms_cmp_builders" in metafunc.fixturenames: 78 | metafunc.parametrize( 79 | "all_aws_kms_cmp_builders", 80 | (pytest.param(partial(_build_kms_cmp, require_attributes), id="Standard KMS CMP"),), 81 | ) 82 | 83 | 84 | @pytest.fixture 85 | def ddb_table_name(): 86 | """Retrieve the target DynamoDB table from environment variable.""" 87 | try: 88 | return os.environ[DDB_TABLE_NAME] 89 | except KeyError: 90 | raise ValueError( 91 | ( 92 | "Environment variable '{}' must be set to the correct DynamoDB table name" 93 | " for integration tests to run" 94 | ).format(DDB_TABLE_NAME) 95 | ) 96 | 97 | 98 | @pytest.fixture 99 | def temp_metastore(): 100 | metastore, table_name = functional_test_utils.build_metastore() 101 | yield metastore 102 | functional_test_utils.delete_metastore(table_name) 103 | -------------------------------------------------------------------------------- /test/acceptance/encrypted/test_table.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Acceptance tests for ``dynamodb_encryption_sdk.encrypted.table``.""" 14 | import pytest 15 | from boto3.resources.base import ServiceResource 16 | from mock import MagicMock 17 | from moto import mock_dynamodb2 18 | 19 | from dynamodb_encryption_sdk.encrypted.table import EncryptedTable 20 | from dynamodb_encryption_sdk.structures import TableIndex, TableInfo 21 | from dynamodb_encryption_sdk.transform import ddb_to_dict 22 | 23 | from ..acceptance_test_utils import load_scenarios 24 | 25 | pytestmark = [pytest.mark.accept] 26 | 27 | 28 | def fake_table(item): 29 | table = MagicMock(__class__=ServiceResource) 30 | table.get_item.return_value = {"Item": item} 31 | return table 32 | 33 | 34 | def _item_check(materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep): 35 | ciphertext_item = ddb_to_dict(ciphertext_item) 36 | plaintext_item = ddb_to_dict(plaintext_item) 37 | metatable = None 38 | try: 39 | metatable = prep() # Test scenario setup that needs to happen inside the test 40 | cmp = materials_provider() # Some of the materials providers need to be constructed inside the test 41 | table = fake_table(ciphertext_item) 42 | table_info = TableInfo( 43 | name=table_name, 44 | primary_index=TableIndex(partition=table_index["partition"], sort=table_index.get("sort", None)), 45 | ) 46 | item_key = {table_info.primary_index.partition: ciphertext_item[table_info.primary_index.partition]} 47 | if table_info.primary_index.sort is not None: 48 | item_key[table_info.primary_index.sort] = ciphertext_item[table_info.primary_index.sort] 49 | 50 | e_table = EncryptedTable( 51 | table=table, 52 | materials_provider=cmp, 53 | table_info=table_info, 54 | attribute_actions=attribute_actions, 55 | auto_refresh_table_indexes=False, 56 | ) 57 | decrypted_item = e_table.get_item(Key=item_key)["Item"] 58 | assert set(decrypted_item.keys()) == set(plaintext_item.keys()) 59 | for key in decrypted_item: 60 | if key == "version": 61 | continue 62 | assert decrypted_item[key] == plaintext_item[key] 63 | finally: 64 | if metatable: 65 | metatable.delete() 66 | metatable.wait_until_not_exists() 67 | 68 | 69 | @mock_dynamodb2 70 | @pytest.mark.local 71 | @pytest.mark.parametrize( 72 | "materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep", 73 | load_scenarios(online=False), 74 | ) 75 | def test_table_get_offline( 76 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 77 | ): 78 | return _item_check( 79 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 80 | ) 81 | 82 | 83 | @pytest.mark.ddb_integ 84 | @pytest.mark.parametrize( 85 | "materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep", 86 | load_scenarios(online=True), 87 | ) 88 | def test_table_get_online( 89 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 90 | ): 91 | return _item_check( 92 | materials_provider, table_name, table_index, ciphertext_item, plaintext_item, attribute_actions, prep 93 | ) 94 | -------------------------------------------------------------------------------- /test/functional/test_identifiers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Functional tests for ``dynamodb_encryption_sdk.identifiers``.""" 14 | import operator 15 | 16 | import pytest 17 | 18 | from dynamodb_encryption_sdk.identifiers import CryptoAction 19 | 20 | pytestmark = [pytest.mark.functional, pytest.mark.local] 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "left, right, expected", 25 | ( 26 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN), 27 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.SIGN_ONLY, CryptoAction.ENCRYPT_AND_SIGN), 28 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.DO_NOTHING, CryptoAction.ENCRYPT_AND_SIGN), 29 | (CryptoAction.SIGN_ONLY, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN), 30 | (CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY), 31 | (CryptoAction.SIGN_ONLY, CryptoAction.DO_NOTHING, CryptoAction.SIGN_ONLY), 32 | (CryptoAction.DO_NOTHING, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN), 33 | (CryptoAction.DO_NOTHING, CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY), 34 | (CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING), 35 | ), 36 | ) 37 | def test_item_action_max(left, right, expected): 38 | assert max(left, right) == expected 39 | 40 | 41 | @pytest.mark.parametrize( 42 | "left, right, expected", 43 | ( 44 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN), 45 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY), 46 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING), 47 | (CryptoAction.SIGN_ONLY, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.SIGN_ONLY), 48 | (CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY), 49 | (CryptoAction.SIGN_ONLY, CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING), 50 | (CryptoAction.DO_NOTHING, CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.DO_NOTHING), 51 | (CryptoAction.DO_NOTHING, CryptoAction.SIGN_ONLY, CryptoAction.DO_NOTHING), 52 | (CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING), 53 | ), 54 | ) 55 | def test_item_action_min(left, right, expected): 56 | assert min(left, right) == expected 57 | 58 | 59 | @pytest.mark.parametrize( 60 | "left, right, expected_comparison", 61 | ( 62 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.ENCRYPT_AND_SIGN, operator.eq), 63 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.SIGN_ONLY, operator.ne), 64 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.SIGN_ONLY, operator.gt), 65 | (CryptoAction.ENCRYPT_AND_SIGN, CryptoAction.DO_NOTHING, operator.gt), 66 | (CryptoAction.SIGN_ONLY, CryptoAction.ENCRYPT_AND_SIGN, operator.lt), 67 | (CryptoAction.SIGN_ONLY, CryptoAction.SIGN_ONLY, operator.eq), 68 | (CryptoAction.SIGN_ONLY, CryptoAction.DO_NOTHING, operator.ne), 69 | (CryptoAction.SIGN_ONLY, CryptoAction.DO_NOTHING, operator.gt), 70 | (CryptoAction.DO_NOTHING, CryptoAction.ENCRYPT_AND_SIGN, operator.lt), 71 | (CryptoAction.DO_NOTHING, CryptoAction.SIGN_ONLY, operator.lt), 72 | (CryptoAction.DO_NOTHING, CryptoAction.DO_NOTHING, operator.eq), 73 | (CryptoAction.DO_NOTHING, CryptoAction.ENCRYPT_AND_SIGN, operator.ne), 74 | ), 75 | ) 76 | def test_item_action_comp(left, right, expected_comparison): 77 | assert expected_comparison(left, right) 78 | -------------------------------------------------------------------------------- /test/unit/material_providers/test_wrapped.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Unit tests for ``dynamodb_encryption_sdk.material_providers.wrapped``.""" 14 | import pytest 15 | from mock import MagicMock 16 | from pytest_mock import mocker # noqa pylint: disable=unused-import 17 | 18 | import dynamodb_encryption_sdk.material_providers.wrapped 19 | from dynamodb_encryption_sdk.delegated_keys import DelegatedKey 20 | from dynamodb_encryption_sdk.exceptions import UnwrappingError, WrappingError 21 | from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider 22 | from dynamodb_encryption_sdk.structures import EncryptionContext 23 | 24 | pytestmark = [pytest.mark.unit, pytest.mark.local] 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "method, error_type, message", 29 | ( 30 | ("decryption_materials", UnwrappingError, "Decryption materials cannot be provided: no unwrapping key"), 31 | ("encryption_materials", WrappingError, "Encryption materials cannot be provided: no wrapping key"), 32 | ), 33 | ) 34 | def test_no_materials(method, error_type, message): 35 | empty_cmp = WrappedCryptographicMaterialsProvider(signing_key=MagicMock(__class__=DelegatedKey)) 36 | 37 | with pytest.raises(error_type) as excinfo: 38 | getattr(empty_cmp, method)(EncryptionContext()) 39 | 40 | excinfo.match(message) 41 | 42 | 43 | @pytest.mark.parametrize( 44 | "invalid_kwargs", 45 | (dict(signing_key=None), dict(wrapping_key="not a delegated key"), dict(unwrapping_key="not a delegated key")), 46 | ) 47 | def test_attrs_fail(invalid_kwargs): 48 | kwargs = dict(signing_key=MagicMock(__class__=DelegatedKey)) 49 | kwargs.update(invalid_kwargs) 50 | 51 | with pytest.raises(TypeError): 52 | WrappedCryptographicMaterialsProvider(**kwargs) 53 | 54 | 55 | @pytest.mark.parametrize("method", ("decryption_materials", "encryption_materials")) 56 | def test_valid_materials(mocker, method): 57 | mocker.patch.object(WrappedCryptographicMaterialsProvider, "_build_materials") 58 | 59 | cmp = WrappedCryptographicMaterialsProvider( 60 | signing_key=MagicMock(__class__=DelegatedKey), 61 | wrapping_key=MagicMock(__class__=DelegatedKey), 62 | unwrapping_key=MagicMock(__class__=DelegatedKey), 63 | ) 64 | 65 | context = EncryptionContext() 66 | test = getattr(cmp, method)(context) 67 | 68 | WrappedCryptographicMaterialsProvider._build_materials.assert_called_once_with(context) 69 | assert test is WrappedCryptographicMaterialsProvider._build_materials.return_value 70 | 71 | 72 | def test_build_materials(mocker): 73 | mocker.patch.object(dynamodb_encryption_sdk.material_providers.wrapped, "WrappedCryptographicMaterials") 74 | 75 | cmp = WrappedCryptographicMaterialsProvider( 76 | signing_key=MagicMock(__class__=DelegatedKey), 77 | wrapping_key=MagicMock(__class__=DelegatedKey), 78 | unwrapping_key=MagicMock(__class__=DelegatedKey), 79 | ) 80 | 81 | material_description = {"some": "data"} 82 | context = EncryptionContext(material_description=material_description) 83 | test = cmp._build_materials(context) 84 | 85 | dynamodb_encryption_sdk.material_providers.wrapped.WrappedCryptographicMaterials.assert_called_once_with( 86 | wrapping_key=cmp._wrapping_key, 87 | unwrapping_key=cmp._unwrapping_key, 88 | signing_key=cmp._signing_key, 89 | material_description=material_description, 90 | ) 91 | assert test is dynamodb_encryption_sdk.material_providers.wrapped.WrappedCryptographicMaterials.return_value 92 | -------------------------------------------------------------------------------- /examples/src/dynamodb_encryption_sdk_examples/aws_kms_multi_region_key.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | """Example showing use of AWS KMS CMP with a DynamoDB Global table and an AWS Multi-Region Key.""" 5 | 6 | import time 7 | 8 | import boto3 9 | 10 | from dynamodb_encryption_sdk.encrypted.client import EncryptedClient 11 | from dynamodb_encryption_sdk.identifiers import CryptoAction 12 | from dynamodb_encryption_sdk.material_providers.aws_kms import AwsKmsCryptographicMaterialsProvider 13 | from dynamodb_encryption_sdk.structures import AttributeActions 14 | 15 | SECOND_REGION = "eu-west-1" 16 | 17 | 18 | def encrypt_item(table_name, cmk_mrk_arn_first_region, cmk_mrk_arn_second_region): 19 | """Demonstrate use of Multi-Region Keys with DynamoDB Encryption Client. 20 | 21 | This example encrypts an item with a Multi-Region Key in one region and decrypts it in another region. It 22 | assumes that you have a Dynamo DB Global table in two regions, as well as a KMS 23 | Multi-Region Key replicated to these regions. 24 | """ 25 | index_key = {"partition_attribute": {"S": "is this"}, "sort_attribute": {"N": "55"}} 26 | plaintext_item = { 27 | "example": {"S": "data"}, 28 | "some numbers": {"N": "99"}, 29 | "and some binary": {"B": b"\x00\x01\x02"}, 30 | "leave me": {"S": "alone"}, # We want to ignore this attribute 31 | } 32 | # Collect all of the attributes that will be encrypted (used later). 33 | encrypted_attributes = set(plaintext_item.keys()) 34 | encrypted_attributes.remove("leave me") 35 | # Collect all of the attributes that will not be encrypted (used later). 36 | unencrypted_attributes = set(index_key.keys()) 37 | unencrypted_attributes.add("leave me") 38 | # Add the index pairs to the item. 39 | plaintext_item.update(index_key) 40 | 41 | # Create attribute actions that tells the encrypted client to encrypt all attributes except one. 42 | actions = AttributeActions( 43 | default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} 44 | ) 45 | 46 | # Create a DDB client and KMS crypto materials provider in the first region using the specified AWS KMS key. 47 | split_arn = cmk_mrk_arn_first_region.split(":") 48 | encryption_region = split_arn[3] 49 | ddb_client = boto3.client("dynamodb", region_name=encryption_region) 50 | encryption_cmp = AwsKmsCryptographicMaterialsProvider(key_id=cmk_mrk_arn_first_region) 51 | # Use these objects to create an encrypted client. 52 | encryption_client = EncryptedClient(client=ddb_client, materials_provider=encryption_cmp, attribute_actions=actions) 53 | 54 | # Put the item to the table, using the encrypted client to transparently encrypt it. 55 | encryption_client.put_item(TableName=table_name, Item=plaintext_item) 56 | 57 | # Create a DDB client and KMS crypto materials provider in the second region 58 | split_arn = cmk_mrk_arn_second_region.split(":") 59 | decryption_region = split_arn[3] 60 | decryption_cmp = AwsKmsCryptographicMaterialsProvider(key_id=cmk_mrk_arn_second_region) 61 | ddb_client = boto3.client("dynamodb", region_name=decryption_region) 62 | # Use these objects to create an encrypted client. 63 | decryption_client = EncryptedClient(client=ddb_client, materials_provider=decryption_cmp, attribute_actions=actions) 64 | 65 | # DDB Global Table replication takes some time. Sleep for a moment to give the item a chance to replicate to the 66 | # second region 67 | time.sleep(1) 68 | 69 | # Get the item from the second region, transparently decrypting it. This allows you to avoid a cross-region KMS 70 | # call to the first region if your application is running in the second region 71 | decrypted_item = decryption_client.get_item(TableName=table_name, Key=index_key)["Item"] 72 | 73 | # Verify that the decryption successfully retrieved the original plaintext 74 | for name in encrypted_attributes: 75 | assert plaintext_item[name] == decrypted_item[name] 76 | 77 | # Clean up the item 78 | encryption_client.delete_item(TableName=table_name, Key=index_key) 79 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Exception classed for use in the DynamoDB Encryption Client.""" 14 | 15 | 16 | class DynamodbEncryptionSdkError(Exception): 17 | """Base class for all custom exceptions.""" 18 | 19 | 20 | class InvalidArgumentError(DynamodbEncryptionSdkError): 21 | """Raised when a general invalid argument is provided.""" 22 | 23 | 24 | class SerializationError(DynamodbEncryptionSdkError): 25 | """Otherwise undifferentiated errors encountered while serializing data.""" 26 | 27 | 28 | class DeserializationError(DynamodbEncryptionSdkError): 29 | """Otherwise undifferentiated errors encountered while deserializing data.""" 30 | 31 | 32 | class InvalidMaterialDescriptionError(DeserializationError): 33 | """Raised when errors are encountered processing a material description.""" 34 | 35 | 36 | class InvalidMaterialDescriptionVersionError(DeserializationError): 37 | """Raised when a material description is encountered with an invalid version.""" 38 | 39 | 40 | class InvalidAlgorithmError(InvalidArgumentError): 41 | """Raised when an invalid algorithm identifier is encountered.""" 42 | 43 | 44 | class JceTransformationError(DynamodbEncryptionSdkError): 45 | """Otherwise undifferentiated errors encountered when attempting to read a JCE transformation.""" 46 | 47 | 48 | class DelegatedKeyError(DynamodbEncryptionSdkError): 49 | """Otherwise undifferentiated errors encountered by a DelegatedKey.""" 50 | 51 | 52 | class DelegatedKeyEncryptionError(DelegatedKeyError): 53 | """Raised when a DelegatedKey encounters an error during encryption.""" 54 | 55 | 56 | class DelegatedKeyDecryptionError(DelegatedKeyError): 57 | """Raised when a DelegatedKey encounters an error during decryption.""" 58 | 59 | 60 | class AwsKmsMaterialsProviderError(DynamodbEncryptionSdkError): 61 | """Otherwise undifferentiated errors encountered by the AwsKmsCryptographicMaterialsProvider.""" 62 | 63 | 64 | class UnknownRegionError(AwsKmsMaterialsProviderError): 65 | """Raised when the AwsKmsCryptographicMaterialsProvider is asked for an unknown region.""" 66 | 67 | 68 | class DecryptionError(DynamodbEncryptionSdkError): 69 | """Otherwise undifferentiated error encountered while decrypting data.""" 70 | 71 | 72 | class UnwrappingError(DynamodbEncryptionSdkError): 73 | """Otherwise undifferentiated error encountered while unwrapping a key.""" 74 | 75 | 76 | class EncryptionError(DynamodbEncryptionSdkError): 77 | """Otherwise undifferentiated error encountered while encrypting data.""" 78 | 79 | 80 | class WrappingError(DynamodbEncryptionSdkError): 81 | """Otherwise undifferentiated error encountered while wrapping a key.""" 82 | 83 | 84 | class SigningError(DynamodbEncryptionSdkError): 85 | """Otherwise undifferentiated error encountered while signing data.""" 86 | 87 | 88 | class SignatureVerificationError(DynamodbEncryptionSdkError): 89 | """Otherwise undifferentiated error encountered while verifying a signature.""" 90 | 91 | 92 | class ProviderStoreError(DynamodbEncryptionSdkError): 93 | """Otherwise undifferentiated error encountered by a provider store.""" 94 | 95 | 96 | class NoKnownVersionError(ProviderStoreError): 97 | """Raised if a provider store cannot locate any version of the requested material.""" 98 | 99 | 100 | class InvalidVersionError(ProviderStoreError): 101 | """Raised if an invalid version of a material is requested.""" 102 | 103 | 104 | class VersionAlreadyExistsError(ProviderStoreError): 105 | """Raised if a version that is being added to a provider store already exists.""" 106 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/materials/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Cryptographic materials are containers that provide delegated keys for cryptographic operations.""" 14 | import abc 15 | from typing import Dict, Text 16 | 17 | import six 18 | 19 | from dynamodb_encryption_sdk.delegated_keys import DelegatedKey # noqa pylint: disable=unused-import 20 | 21 | __all__ = ("CryptographicMaterials", "EncryptionMaterials", "DecryptionMaterials") 22 | 23 | 24 | @six.add_metaclass(abc.ABCMeta) 25 | class CryptographicMaterials(object): 26 | """Base class for all cryptographic materials.""" 27 | 28 | @property 29 | @abc.abstractmethod 30 | def material_description(self): 31 | # type: () -> Dict[Text, Text] 32 | """Material description to use with these cryptographic materials. 33 | 34 | :returns: Material description 35 | :rtype: dict 36 | """ 37 | 38 | @property 39 | @abc.abstractmethod 40 | def encryption_key(self): 41 | # type: () -> DelegatedKey 42 | """Delegated key used for encrypting attributes. 43 | 44 | :returns: Encryption key 45 | :rtype: DelegatedKey 46 | """ 47 | 48 | @property 49 | @abc.abstractmethod 50 | def decryption_key(self): 51 | # type: () -> DelegatedKey 52 | """Delegated key used for decrypting attributes. 53 | 54 | :returns: Decryption key 55 | :rtype: DelegatedKey 56 | """ 57 | 58 | @property 59 | @abc.abstractmethod 60 | def signing_key(self): 61 | # type: () -> DelegatedKey 62 | """Delegated key used for calculating digital signatures. 63 | 64 | :returns: Signing key 65 | :rtype: DelegatedKey 66 | """ 67 | 68 | @property 69 | @abc.abstractmethod 70 | def verification_key(self): 71 | # type: () -> DelegatedKey 72 | """Delegated key used for verifying digital signatures. 73 | 74 | :returns: Verification key 75 | :rtype: DelegatedKey 76 | """ 77 | 78 | 79 | class EncryptionMaterials(CryptographicMaterials): 80 | """Base class for all encryption materials.""" 81 | 82 | @property 83 | def decryption_key(self): 84 | """Encryption materials do not provide decryption keys. 85 | 86 | :raises NotImplementedError: because encryption materials do not contain decryption keys 87 | """ 88 | raise NotImplementedError("Encryption materials do not provide decryption keys.") 89 | 90 | @property 91 | def verification_key(self): 92 | """Encryption materials do not provide verification keys. 93 | 94 | :raises NotImplementedError: because encryption materials do not contain verification keys 95 | """ 96 | raise NotImplementedError("Encryption materials do not provide verification keys.") 97 | 98 | 99 | class DecryptionMaterials(CryptographicMaterials): 100 | """Base class for all decryption materials.""" 101 | 102 | @property 103 | def encryption_key(self): 104 | """Decryption materials do not provide encryption keys. 105 | 106 | :raises NotImplementedError: because decryption materials do not contain encryption keys 107 | """ 108 | raise NotImplementedError("Decryption materials do not provide encryption keys.") 109 | 110 | @property 111 | def signing_key(self): 112 | """Decryption materials do not provide signing keys. 113 | 114 | :raises NotImplementedError: because decryption materials do not contain signing keys 115 | """ 116 | raise NotImplementedError("Decryption materials do not provide signing keys.") 117 | -------------------------------------------------------------------------------- /src/dynamodb_encryption_sdk/material_providers/store/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Cryptographic materials provider stores.""" 14 | import abc 15 | from typing import Optional, Text 16 | 17 | import six 18 | 19 | from dynamodb_encryption_sdk.exceptions import NoKnownVersionError 20 | from dynamodb_encryption_sdk.material_providers import ( # noqa pylint: disable=unused-import 21 | CryptographicMaterialsProvider, 22 | ) 23 | 24 | __all__ = ("ProviderStore",) 25 | 26 | 27 | @six.add_metaclass(abc.ABCMeta) 28 | class ProviderStore(object): 29 | """Provide a standard way to retrieve and/or create cryptographic materials providers.""" 30 | 31 | @abc.abstractmethod 32 | def get_or_create_provider(self, material_name, version): 33 | # type: (Text, int) -> CryptographicMaterialsProvider 34 | """Obtain a cryptographic materials provider identified by a name and version. 35 | 36 | If the requested version does not exist, a new one might be created. 37 | 38 | :param str material_name: Material to locate 39 | :param int version: Version of material to locate (optional) 40 | :returns: cryptographic materials provider 41 | :rtype: CryptographicMaterialsProvider 42 | :raises InvalidVersionError: if the requested version is not available and cannot be created 43 | """ 44 | 45 | @abc.abstractmethod 46 | def version_from_material_description(self, material_description): 47 | # (Dict[Text, Text]) -> int 48 | """Determine the version from the provided material description. 49 | 50 | :param dict material_description: Material description to use with this request 51 | :returns: version to use 52 | :rtype: int 53 | """ 54 | 55 | def max_version(self, material_name): 56 | # (Text) -> int 57 | # pylint: disable=no-self-use 58 | """Find the maximum known version of the specified material. 59 | 60 | .. note:: 61 | 62 | Child classes should usually override this method. 63 | 64 | :param str material_name: Material to locate 65 | :returns: Maximum known version 66 | :rtype: int 67 | :raises NoKnownVersionError: if no version can be found 68 | """ 69 | raise NoKnownVersionError('No known version for name: "{}"'.format(material_name)) 70 | 71 | def provider(self, material_name, version=None): 72 | # type: (Text, Optional[int]) -> CryptographicMaterialsProvider 73 | """Obtain a cryptographic materials provider identified by a name and version. 74 | 75 | If the version is not provided, the maximum version will be used. 76 | 77 | :param str material_name: Material to locate 78 | :param int version: Version of material to locate (optional) 79 | :returns: cryptographic materials provider 80 | :rtype: CryptographicMaterialsProvider 81 | :raises InvalidVersionError: if the requested version is not found 82 | """ 83 | if version is None: 84 | try: 85 | version = self.max_version(material_name) 86 | except NoKnownVersionError: 87 | version = 0 88 | return self.get_or_create_provider(material_name, version) 89 | 90 | def new_provider(self, material_name): 91 | # type: (Text) -> CryptographicMaterialsProvider 92 | """Create a new provider with a version one greater than the current known maximum. 93 | 94 | :param str material_name: Material to locate 95 | :returns: cryptographic materials provider 96 | :rtype: CryptographicMaterialsProvider 97 | """ 98 | version = self.max_version(material_name) + 1 99 | return self.get_or_create_provider(material_name, version) 100 | -------------------------------------------------------------------------------- /examples/src/dynamodb_encryption_sdk_examples/wrapped_symmetric_encrypted_table.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"). You 4 | # may not use this file except in compliance with the License. A copy of 5 | # the License is located at 6 | # 7 | # http://aws.amazon.com/apache2.0/ 8 | # 9 | # or in the "license" file accompanying this file. This file is 10 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | # ANY KIND, either express or implied. See the License for the specific 12 | # language governing permissions and limitations under the License. 13 | """Example showing use of a symmetric wrapped CMP with EncryptedTable.""" 14 | import boto3 15 | from boto3.dynamodb.types import Binary 16 | 17 | from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey 18 | from dynamodb_encryption_sdk.encrypted.table import EncryptedTable 19 | from dynamodb_encryption_sdk.identifiers import CryptoAction, EncryptionKeyType, KeyEncodingType 20 | from dynamodb_encryption_sdk.material_providers.wrapped import WrappedCryptographicMaterialsProvider 21 | from dynamodb_encryption_sdk.structures import AttributeActions 22 | 23 | 24 | def encrypt_item(table_name, aes_wrapping_key_bytes, hmac_signing_key_bytes): 25 | """Demonstrate use of EncryptedTable to transparently encrypt an item.""" 26 | index_key = {"partition_attribute": "is this", "sort_attribute": 55} 27 | plaintext_item = { 28 | "example": "data", 29 | "some numbers": 99, 30 | "and some binary": Binary(b"\x00\x01\x02"), 31 | "leave me": "alone", # We want to ignore this attribute 32 | } 33 | # Collect all of the attributes that will be encrypted (used later). 34 | encrypted_attributes = set(plaintext_item.keys()) 35 | encrypted_attributes.remove("leave me") 36 | # Collect all of the attributes that will not be encrypted (used later). 37 | unencrypted_attributes = set(index_key.keys()) 38 | unencrypted_attributes.add("leave me") 39 | # Add the index pairs to the item. 40 | plaintext_item.update(index_key) 41 | 42 | # Create a normal table resource. 43 | table = boto3.resource("dynamodb").Table(table_name) # generated code confuse pylint: disable=no-member 44 | # Create a crypto materials provider using the provided wrapping and signing keys. 45 | wrapping_key = JceNameLocalDelegatedKey( 46 | key=aes_wrapping_key_bytes, 47 | algorithm="AES", 48 | key_type=EncryptionKeyType.SYMMETRIC, 49 | key_encoding=KeyEncodingType.RAW, 50 | ) 51 | signing_key = JceNameLocalDelegatedKey( 52 | key=hmac_signing_key_bytes, 53 | algorithm="HmacSHA512", 54 | key_type=EncryptionKeyType.SYMMETRIC, 55 | key_encoding=KeyEncodingType.RAW, 56 | ) 57 | wrapped_cmp = WrappedCryptographicMaterialsProvider( 58 | wrapping_key=wrapping_key, unwrapping_key=wrapping_key, signing_key=signing_key 59 | ) 60 | # Create attribute actions that tells the encrypted table to encrypt all attributes except one. 61 | actions = AttributeActions( 62 | default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING} 63 | ) 64 | # Use these objects to create an encrypted table resource. 65 | encrypted_table = EncryptedTable(table=table, materials_provider=wrapped_cmp, attribute_actions=actions) 66 | 67 | # Put the item to the table, using the encrypted table resource to transparently encrypt it. 68 | encrypted_table.put_item(Item=plaintext_item) 69 | 70 | # Get the encrypted item using the standard table resource. 71 | encrypted_item = table.get_item(Key=index_key)["Item"] 72 | 73 | # Get the item using the encrypted table resource, transparently decyrpting it. 74 | decrypted_item = encrypted_table.get_item(Key=index_key)["Item"] 75 | 76 | # Verify that all of the attributes are different in the encrypted item 77 | for name in encrypted_attributes: 78 | assert encrypted_item[name] != plaintext_item[name] 79 | assert decrypted_item[name] == plaintext_item[name] 80 | 81 | # Verify that all of the attributes that should not be encrypted were not. 82 | for name in unencrypted_attributes: 83 | assert decrypted_item[name] == encrypted_item[name] == plaintext_item[name] 84 | 85 | # Clean up the item 86 | encrypted_table.delete_item(Key=index_key) 87 | --------------------------------------------------------------------------------