Skip to content

Commit f492de9

Browse files
Merge branch 'mpl-reviewed' into lucmcdon/mpl-v2
2 parents b450538 + fceb2b0 commit f492de9

File tree

84 files changed

+4519
-203
lines changed

Some content is hidden

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

84 files changed

+4519
-203
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
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: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 sys
22+
23+
import boto3
24+
from aws_cryptographic_materialproviders.mpl import AwsCryptographicMaterialProviders
25+
from aws_cryptographic_materialproviders.mpl.config import MaterialProvidersConfig
26+
from aws_cryptographic_materialproviders.mpl.models import (
27+
CreateAwsKmsKeyringInput,
28+
CreateDefaultCryptographicMaterialsManagerInput,
29+
)
30+
from aws_cryptographic_materialproviders.mpl.references import ICryptographicMaterialsManager, IKeyring
31+
from typing import Dict # noqa pylint: disable=wrong-import-order
32+
33+
import aws_encryption_sdk
34+
from aws_encryption_sdk import CommitmentPolicy
35+
36+
# TODO-MPL: Remove this as part of removing PYTHONPATH hacks.
37+
MODULE_ROOT_DIR = '/'.join(__file__.split("/")[:-1])
38+
39+
sys.path.append(MODULE_ROOT_DIR)
40+
41+
EXAMPLE_DATA: bytes = b"Hello World"
42+
43+
44+
def encrypt_and_decrypt_with_default_cmm(
45+
kms_key_id: str
46+
):
47+
"""Demonstrate an encrypt/decrypt cycle using default Cryptographic Material Managers.
48+
49+
Usage: encrypt_and_decrypt_with_default_cmm(kms_key_id)
50+
:param kms_key_id: KMS Key identifier for the KMS key you want to use for encryption and
51+
decryption of your data keys.
52+
:type kms_key_id: string
53+
54+
For more information on KMS Key identifiers, see
55+
https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id
56+
"""
57+
# 1. Instantiate the encryption SDK client.
58+
# This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
59+
# which enforces that this client only encrypts using committing algorithm suites and enforces
60+
# that this client will only decrypt encrypted messages that were created with a committing
61+
# algorithm suite.
62+
# This is the default commitment policy if you were to build the client as
63+
# `client = aws_encryption_sdk.EncryptionSDKClient()`.
64+
client = aws_encryption_sdk.EncryptionSDKClient(
65+
commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
66+
)
67+
68+
# 2. Create encryption context.
69+
# Remember that your encryption context is NOT SECRET.
70+
# For more information, see
71+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
72+
encryption_context: Dict[str, str] = {
73+
"encryption": "context",
74+
"is not": "secret",
75+
"but adds": "useful metadata",
76+
"that can help you": "be confident that",
77+
"the data you are handling": "is what you think it is",
78+
}
79+
80+
# 3. Create a KMS keyring to use with the CryptographicMaterialsManager
81+
kms_client = boto3.client('kms', region_name="us-west-2")
82+
83+
mat_prov: AwsCryptographicMaterialProviders = AwsCryptographicMaterialProviders(
84+
config=MaterialProvidersConfig()
85+
)
86+
87+
keyring_input: CreateAwsKmsKeyringInput = CreateAwsKmsKeyringInput(
88+
kms_key_id=kms_key_id,
89+
kms_client=kms_client
90+
)
91+
92+
kms_keyring: IKeyring = mat_prov.create_aws_kms_keyring(
93+
input=keyring_input
94+
)
95+
96+
# 4. Create a CryptographicMaterialsManager for encryption and decryption
97+
cmm_input: CreateDefaultCryptographicMaterialsManagerInput = \
98+
CreateDefaultCryptographicMaterialsManagerInput(
99+
keyring=kms_keyring
100+
)
101+
102+
cmm: ICryptographicMaterialsManager = mat_prov.create_default_cryptographic_materials_manager(
103+
input=cmm_input
104+
)
105+
106+
# 5. Encrypt the data with the encryptionContext.
107+
ciphertext, _ = client.encrypt(
108+
source=EXAMPLE_DATA,
109+
materials_manager=cmm,
110+
encryption_context=encryption_context
111+
)
112+
113+
# 6. Demonstrate that the ciphertext and plaintext are different.
114+
# (This is an example for demonstration; you do not need to do this in your own code.)
115+
assert ciphertext != EXAMPLE_DATA, \
116+
"Ciphertext and plaintext data are the same. Invalid encryption"
117+
118+
# 7. Decrypt your encrypted data using the same cmm you used on encrypt.
119+
plaintext_bytes, dec_header = client.decrypt(
120+
source=ciphertext,
121+
materials_manager=cmm
122+
)
123+
124+
# 8. Demonstrate that the encryption context is correct in the decrypted message header
125+
# (This is an example for demonstration; you do not need to do this in your own code.)
126+
for k, v in encryption_context.items():
127+
assert v == dec_header.encryption_context[k], \
128+
"Encryption context does not match expected values"
129+
130+
# 9. Demonstrate that the decrypted plaintext is identical to the original plaintext.
131+
# (This is an example for demonstration; you do not need to do this in your own code.)
132+
assert plaintext_bytes == EXAMPLE_DATA, \
133+
"Decrypted plaintext should be identical to the original plaintext. Invalid decryption"

0 commit comments

Comments
 (0)