Skip to content

Commit a98a5a8

Browse files
Merge branch 'mpl-reviewed' into cmmtest
2 parents a8c5b0d + ca7e159 commit a98a5a8

File tree

87 files changed

+4531
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+4531
-209
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ __pycache__
3333
.pytest_cache
3434
# Ignore key materials generated by examples or tests
3535
test_keyrings/
36+
# Ignore results of performance test
37+
performance_tests/results/*.csv
38+
performance_tests/results/*.pstats
39+
performance_tests/results/*.png
40+
# Ignore the memory profile logs
41+
mprofile_*
3642

3743
# PyCharm
3844
.idea/

README.rst

Lines changed: 108 additions & 176 deletions
Large diffs are not rendered by default.

buildspec.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ batch:
126126
buildspec: codebuild/py311/integ_mpl.yml
127127
env:
128128
image: aws/codebuild/standard:7.0
129+
- identifier: py311_performance_tests_mpl
130+
buildspec: codebuild/py311/performance_tests_mpl.yml
131+
env:
132+
image: aws/codebuild/standard:7.0
129133
- identifier: py311_examples
130134
buildspec: codebuild/py311/examples.yml
131135
env:
@@ -212,6 +216,10 @@ batch:
212216
buildspec: codebuild/py312/integ_mpl.yml
213217
env:
214218
image: aws/codebuild/standard:7.0
219+
- identifier: py312_performance_tests_mpl
220+
buildspec: codebuild/py312/performance_tests_mpl.yml
221+
env:
222+
image: aws/codebuild/standard:7.0
215223
- identifier: py312_examples
216224
buildspec: codebuild/py312/examples.yml
217225
env:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Runs the performance tests for the MPL in an environment with the MPL installed
2+
version: 0.2
3+
4+
env:
5+
variables:
6+
# No TOXENV. This runs multiple environments.
7+
REGION: "us-west-2"
8+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >-
9+
arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f
10+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >-
11+
arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2
12+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >-
13+
arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7
14+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >-
15+
arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7
16+
17+
phases:
18+
install:
19+
runtime-versions:
20+
python: 3.11
21+
build:
22+
commands:
23+
- cd /root/.pyenv/plugins/python-build/../.. && git pull && cd -
24+
- pyenv install --skip-existing 3.11.0
25+
- pyenv local 3.11.0
26+
- pip install --upgrade pip
27+
- pip install setuptools
28+
- pip install "tox < 4.0"
29+
# Assume special role to access keystore
30+
- TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py312ExamplesMpl")
31+
- export TMP_ROLE
32+
- export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId')
33+
- export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey')
34+
- export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken')
35+
- aws sts get-caller-identity
36+
# Run MPL-specific tests with special role
37+
- cd performance_tests/
38+
- tox -e py311-performance_tests-mpl
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Runs the performance tests for the MPL in an environment with the MPL installed
2+
version: 0.2
3+
4+
env:
5+
variables:
6+
# No TOXENV. This runs multiple environments.
7+
REGION: "us-west-2"
8+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >-
9+
arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f
10+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >-
11+
arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2
12+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >-
13+
arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7
14+
AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >-
15+
arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7
16+
17+
phases:
18+
install:
19+
runtime-versions:
20+
python: 3.12
21+
build:
22+
commands:
23+
- cd /root/.pyenv/plugins/python-build/../.. && git pull && cd -
24+
- pyenv install --skip-existing 3.12.0
25+
- pyenv local 3.12.0
26+
- pip install --upgrade pip
27+
- pip install setuptools
28+
- pip install "tox < 4.0"
29+
# Assume special role to access keystore
30+
- TMP_ROLE=$(aws sts assume-role --role-arn "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Python-Role-us-west-2" --role-session-name "CB-Py312ExamplesMpl")
31+
- export TMP_ROLE
32+
- export AWS_ACCESS_KEY_ID=$(echo "${TMP_ROLE}" | jq -r '.Credentials.AccessKeyId')
33+
- export AWS_SECRET_ACCESS_KEY=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SecretAccessKey')
34+
- export AWS_SESSION_TOKEN=$(echo "${TMP_ROLE}" | jq -r '.Credentials.SessionToken')
35+
- aws sts get-caller-identity
36+
# Run MPL-specific tests with special role
37+
- cd performance_tests/
38+
- tox -e py312-performance_tests-mpl

examples/src/branch_key_id_supplier_example.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ def get_branch_key_id(
2525
"""Returns branch key ID from the tenant ID in input's encryption context."""
2626
encryption_context: Dict[str, str] = param.encryption_context
2727

28-
if b"tenant" not in encryption_context:
28+
if "tenant" not in encryption_context:
2929
raise ValueError("EncryptionContext invalid, does not contain expected tenant key value pair.")
3030

31-
tenant_key_id: str = encryption_context.get(b"tenant")
31+
tenant_key_id: str = encryption_context.get("tenant")
3232
branch_key_id: str
3333

34-
if tenant_key_id == b"TenantA":
34+
if tenant_key_id == "TenantA":
3535
branch_key_id = self.branch_key_id_for_tenant_A
36-
elif tenant_key_id == b"TenantB":
36+
elif tenant_key_id == "TenantB":
3737
branch_key_id = self.branch_key_id_for_tenant_B
3838
else:
3939
raise ValueError(f"Item does not contain valid tenant ID: {tenant_key_id=}")
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Example to create a custom implementation of the MPL's ICryptographicMaterialsManager class and use it with the ESDK.
5+
6+
The cryptographic materials manager (CMM) assembles the cryptographic materials that are used
7+
to encrypt and decrypt data. The cryptographic materials include plaintext and encrypted data keys,
8+
and an optional message signing key.
9+
10+
Cryptographic Materials Managers (CMMs) are composable; if you just want to extend the behavior of
11+
the default CMM, you can do this as demonstrated in this example. This is the easiest approach if
12+
you are just adding a small check to the CMM methods, as in this example.
13+
14+
If your use case calls for fundamentally changing aspects of the default CMM, you can also write
15+
your own implementation without extending an existing CMM. The default CMM's implementation is a
16+
good reference to use if you need to write a custom CMM implementation from scratch.
17+
Custom implementations of CMMs must implement get_encryption_materials and decrypt_materials.
18+
19+
For more information on a default implementation of a CMM,
20+
please look at the default_cryptographic_materials_manager_example.py example.
21+
22+
For more information on Cryptographic Material Managers, see
23+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#crypt-materials-manager
24+
"""
25+
26+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
27+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
28+
from aws_cryptographic_materialproviders.mpl.models import (
29+
CreateDefaultCryptographicMaterialsManagerInput,
30+
SignatureAlgorithmNone,
31+
)
32+
from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager, IKeyring
33+
34+
import aws_encryption_sdk
35+
from aws_encryption_sdk import CommitmentPolicy
36+
37+
38+
# Custom CMM implementation using the MPL.
39+
# This CMM only allows encryption/decryption using signing algorithms.
40+
# It wraps an underlying CMM implementation and checks its materials
41+
# to ensure that it is only using signed encryption algorithms.
42+
class MPLCustomSigningSuiteOnlyCMM(ICryptographicMaterialsManager):
43+
"""Example custom crypto materials manager class."""
44+
45+
def __init__(self, keyring: IKeyring, cmm: ICryptographicMaterialsManager = None) -> None:
46+
"""Constructor for MPLCustomSigningSuiteOnlyCMM class."""
47+
if cmm is not None:
48+
self.underlying_cmm = cmm
49+
else:
50+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
51+
config=MaterialProvidersConfig()
52+
)
53+
54+
# Create a CryptographicMaterialsManager for encryption and decryption
55+
cmm_input: CreateDefaultCryptographicMaterialsManagerInput = \
56+
CreateDefaultCryptographicMaterialsManagerInput(
57+
keyring=keyring
58+
)
59+
60+
self.underlying_cmm: ICryptographicMaterialsManager = \
61+
mat_prov.create_default_cryptographic_materials_manager(
62+
input=cmm_input
63+
)
64+
65+
def get_encryption_materials(self, param):
66+
"""Provides encryption materials appropriate for the request for the custom CMM.
67+
68+
:param aws_cryptographic_materialproviders.mpl.models.GetEncryptionMaterialsInput param: Input object to
69+
provide to a crypto material manager's `get_encryption_materials` method.
70+
:returns: Encryption materials output
71+
:rtype: aws_cryptographic_materialproviders.mpl.models.GetEncryptionMaterialsOutput
72+
"""
73+
materials = self.underlying_cmm.get_encryption_materials(param)
74+
if isinstance(materials.encryption_materials.algorithm_suite.signature, SignatureAlgorithmNone):
75+
raise ValueError(
76+
"Algorithm provided to MPLCustomSigningSuiteOnlyCMM"
77+
+ " is not a supported signing algorithm: " + str(materials.encryption_materials.algorithm_suite)
78+
)
79+
return materials
80+
81+
def decrypt_materials(self, param):
82+
"""Provides decryption materials appropriate for the request for the custom CMM.
83+
84+
:param aws_cryptographic_materialproviders.mpl.models.DecryptMaterialsInput param: Input object to provide
85+
to a crypto material manager's `decrypt_materials` method.
86+
:returns: Decryption materials output
87+
:rtype: aws_cryptographic_materialproviders.mpl.models.GetDecryptionMaterialsOutput
88+
"""
89+
materials = self.underlying_cmm.decrypt_materials(param)
90+
if isinstance(materials.decryption_materials.algorithm_suite.signature, SignatureAlgorithmNone):
91+
raise ValueError(
92+
"Algorithm provided to MPLCustomSigningSuiteOnlyCMM"
93+
+ " is not a supported signing algorithm: " + str(materials.decryption_materials.algorithm_suite)
94+
)
95+
return materials
96+
97+
98+
EXAMPLE_DATA: bytes = b"Hello World"
99+
100+
101+
def encrypt_decrypt_with_cmm(
102+
cmm: ICryptographicMaterialsManager
103+
):
104+
"""Encrypts and decrypts a string using a custom CMM.
105+
106+
:param ICryptographicMaterialsManager cmm: CMM to use for encryption and decryption
107+
"""
108+
# Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a
109+
# commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default.
110+
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
111+
112+
# Encrypt the plaintext source data
113+
ciphertext, _ = client.encrypt(
114+
source=EXAMPLE_DATA,
115+
materials_manager=cmm
116+
)
117+
118+
# Decrypt the ciphertext
119+
cycled_plaintext, _ = client.decrypt(
120+
source=ciphertext,
121+
materials_manager=cmm
122+
)
123+
124+
# Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext
125+
assert cycled_plaintext == EXAMPLE_DATA
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example sets up the default Cryptographic Material Managers (CMM).
5+
6+
The default cryptographic materials manager (CMM) assembles the cryptographic materials
7+
that are used to encrypt and decrypt data. The cryptographic materials include
8+
plaintext and encrypted data keys, and an optional message signing key.
9+
This example creates a CMM and then encrypts a custom input EXAMPLE_DATA
10+
with an encryption context. Creating a CMM involves taking a keyring as input,
11+
and we use an AWS KMS Keyring for this example.
12+
This example also includes some sanity checks for demonstration:
13+
1. Ciphertext and plaintext data are not the same
14+
2. Encryption context is correct in the decrypted message header
15+
3. Decrypted plaintext value matches EXAMPLE_DATA
16+
These sanity checks are for demonstration in the example only. You do not need these in your code.
17+
18+
For more information on Cryptographic Material Managers, see
19+
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#crypt-materials-manager
20+
"""
21+
import boto3
22+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
23+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
24+
from aws_cryptographic_materialproviders.mpl.models import (
25+
CreateAwsKmsKeyringInput,
26+
CreateDefaultCryptographicMaterialsManagerInput,
27+
)
28+
from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager, IKeyring
29+
from typing import Dict # noqa pylint: disable=wrong-import-order
30+
31+
import aws_encryption_sdk
32+
from aws_encryption_sdk import CommitmentPolicy
33+
34+
EXAMPLE_DATA: bytes = b"Hello World"
35+
36+
37+
def encrypt_and_decrypt_with_default_cmm(
38+
kms_key_id: str
39+
):
40+
"""Demonstrate an encrypt/decrypt cycle using default Cryptographic Material Managers.
41+
42+
Usage: encrypt_and_decrypt_with_default_cmm(kms_key_id)
43+
:param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and
44+
decryption of your data keys.
45+
:type kms_key_id: string
46+
47+
For more information on KMS Key identifiers, see
48+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
49+
"""
50+
# 1. Instantiate the encryption SDK client.
51+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
52+
# which enforces that this client only encrypts using committing algorithm suites and enforces
53+
# that this client will only decrypt encrypted messages that were created with a committing
54+
# algorithm suite.
55+
# This is the default commitment policy if you were to build the client as
56+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
57+
client = aws_encryption_sdk.EncryptionSDKClient(
58+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
59+
)
60+
61+
# 2. Create encryption context.
62+
# Remember that your encryption context is NOT SECRET.
63+
# For more information, see
64+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
65+
encryption_context: Dict[str, str] = {
66+
"encryption": "context",
67+
"is not": "secret",
68+
"but adds": "useful metadata",
69+
"that can help you": "be confident that",
70+
"the data you are handling": "is what you think it is",
71+
}
72+
73+
# 3. Create a KMS keyring to use with the CryptographicMaterialsManager
74+
kms_client = boto3.client('kms', region_name="us-west-2")
75+
76+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
77+
config=MaterialProvidersConfig()
78+
)
79+
80+
keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
81+
kms_key_id=kms_key_id,
82+
kms_client=kms_client
83+
)
84+
85+
kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
86+
input=keyring_input
87+
)
88+
89+
# 4. Create a CryptographicMaterialsManager for encryption and decryption
90+
cmm_input: CreateDefaultCryptographicMaterialsManagerInput = \
91+
CreateDefaultCryptographicMaterialsManagerInput(
92+
keyring=kms_keyring
93+
)
94+
95+
cmm: ICryptographicMaterialsManager = mat_prov.create_default_cryptographic_materials_manager(
96+
input=cmm_input
97+
)
98+
99+
# 5. Encrypt the data with the encryptionContext.
100+
ciphertext, _ = client.encrypt(
101+
source=EXAMPLE_DATA,
102+
materials_manager=cmm,
103+
encryption_context=encryption_context
104+
)
105+
106+
# 6. Demonstrate that the ciphertext and plaintext are different.
107+
# (This is an example for demonstration; you do not need to do this in your own code.)
108+
assert ciphertext != EXAMPLE_DATA, \
109+
"Ciphertext and plaintext data are the same. Invalid encryption"
110+
111+
# 7. Decrypt your encrypted data using the same cmm you used on encrypt.
112+
plaintext_bytes, dec_header = client.decrypt(
113+
source=ciphertext,
114+
materials_manager=cmm
115+
)
116+
117+
# 8. Demonstrate that the encryption context is correct in the decrypted message header
118+
# (This is an example for demonstration; you do not need to do this in your own code.)
119+
for k, v in encryption_context.items():
120+
assert v == dec_header.encryption_context[k], \
121+
"Encryption context does not match expected values"
122+
123+
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
124+
# (This is an example for demonstration; you do not need to do this in your own code.)
125+
assert plaintext_bytes == EXAMPLE_DATA, \
126+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

0 commit comments

Comments
 (0)