Skip to content

Commit 9402826

Browse files
mattsb42-awsjohnwalkercaitlin-tibbettsragonalizroth
authored
Examples refresh, take 1 (#219)
* Update PR template * Added a check for max_age being greater than 0 (#172) * Added a check for max_age being greater than 0 * Fixed flake8 by adding missing pydocstyle dependency * Added the dependency to decrypt_oracle as well * Added test for max_age<=0 ValueError * Updated test for max_age<=0.0 ValueError * Added negative test case * Testing something, want AppVeyor to run * Quick change * Running AppVeyor * Added example for using multiple keyrings in multiple regions * Undid something quickly * Fixed importerror * Formatting fix * Update tox.ini * Update tox.ini * Made some changes to the multiple_kms_cmk_regions example/test * This is my next interation of the code for the example; however, I am still working on populating the tests correctly, so the CI will fail, but I tested the code with my own KMS CMK ARNs, so I know it will work once the tests are populated (working with Tejeswini on this) * Changed the example to test two CMKs in the same region until Issue #178 is cleared up * Found out how to make a new valid test key, so now there are two valid test keys in different regions for this example * Ran autoformat * Added some docstrings * Formatting will be the death of me * Used correct keys in test * Updated some comments * Fixed KMS master key provider tests when default AWS region is configured (#179) * Fixed KMS master key provider tests for users who have their default AWS region configured * created fixture for botocore session with no region set * add auto-used fixture in KMS master key provider unit tests to test against both with and without default region * Wrote example and test for using one kms cmk with an unsigned algorithm * Update the integration tests * Small changes * Update one_kms_cmk_unsigned.py * Update examples/src/one_kms_cmk_unsigned.py Co-Authored-By: Matt Bullock <[email protected]> * isort-check now succeeds * chore: move existing examples into "legacy" directory * chore: add automatic test runner for examples * chore: convert existing examples to work with automatic test runner * chore: move examples kwarg building into utils module * chore: convert multi-CMK test runners to new configuration format * fix: fix multi-CMK example logic * chore: convert multi-CMK example to new test runner signature and move into legacy examples * chore: make examples linting always run across both source and tests * fix: linting fixes * docs: add docstring description for legacy examples module * chore: add initial new-format examples * docs: add examples readme * docs: add instructions for writing examples * chore: address PR comments * chore: change examples parameter from aws_kms_cmk_arn to aws_kms_cmk for consistency * docs: clarify integration tests readme * Apply suggestions from code review Co-Authored-By: June Blender <[email protected]> * Apply suggestions from code review Co-Authored-By: June Blender <[email protected]> * docs: change from "one-shot" term to "one-step" * chore: apply changes based on PR comments * docs: reword parameter description * Apply suggestions from code review Co-Authored-By: June Blender <[email protected]> * chore: rename examples input parameters to move from "child" to "additional" naming * docs: clarify configuration intro * docs: apply examples comments consistently * chore: fix line length * fix: fix typo Co-authored-by: John Walker <[email protected]> Co-authored-by: Caitlin Tibbetts <[email protected]> Co-authored-by: Caitlin Tibbetts <[email protected]> Co-authored-by: Ryan Ragona <[email protected]> Co-authored-by: lizroth <[email protected]> Co-authored-by: June Blender <[email protected]>
1 parent 04b5f94 commit 9402826

26 files changed

+632
-354
lines changed

examples/README.md

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# AWS Encryption SDK Examples
2+
3+
This section features examples that show you
4+
how to use the AWS Encryption SDK.
5+
We demonstrate how to use the encryption and decryption APIs
6+
and how to set up some common configuration patterns.
7+
8+
## APIs
9+
10+
The AWS Encryption SDK provides two high-level APIs:
11+
one-step APIs that process the entire operation in memory
12+
and streaming APIs.
13+
14+
You can find examples that demonstrate these APIs
15+
in the [`examples/src/`](./src) directory.
16+
17+
## Configuration
18+
19+
To use the library APIs,
20+
you need to describe how you want the library to protect your data keys.
21+
You can do this using
22+
[keyrings][#keyrings] or [cryptographic materials managers][#cryptographic-materials-managers],
23+
or using [master key providers][#master-key-providers].
24+
These examples will show you how.
25+
26+
### Keyrings
27+
28+
Keyrings are the most common way for you to configure the AWS Encryption SDK.
29+
They determine how the AWS Encryption SDK protects your data.
30+
You can find these examples in [`examples/src/keyring`](./src/keyring).
31+
32+
### Cryptographic Materials Managers
33+
34+
Keyrings define how your data keys are protected,
35+
but there is more going on here than just protecting data keys.
36+
37+
Cryptographic materials managers give you higher-level controls
38+
over how the AWS Encryption SDK protects your data.
39+
This can include things like
40+
enforcing the use of certain algorithm suites or encryption context settings,
41+
reusing data keys across messages,
42+
or changing how you interact with keyrings.
43+
You can find these examples in
44+
[`examples/src/crypto_materials_manager`](./src/crypto_materials_manager).
45+
46+
### Master Key Providers
47+
48+
Before there were keyrings, there were master key providers.
49+
Master key providers were the original configuration structure
50+
that we provided for defining how you want to protect your data keys.
51+
Keyrings provide a simpler experience and often more powerful configuration options,
52+
but if you need to use master key providers,
53+
need help migrating from master key providers to keyrings,
54+
or simply want to see the difference between these configuration experiences,
55+
you can find these examples in [`examples/src/master_key_provider`](./src/master_key_provider).
56+
57+
## Legacy
58+
59+
This section includes older examples, including examples of using master keys and master key providers in Java and Python.
60+
You can use them as a reference,
61+
but we recommend looking at the newer examples, which explain the preferred ways of using this library.
62+
You can find these examples in [`examples/src/legacy`](./src/legacy).
63+
64+
# Writing Examples
65+
66+
If you want to contribute a new example, that's awesome!
67+
To make sure that your example is tested in our CI,
68+
please make sure that it meets the following requirements:
69+
70+
1. The example MUST be a distinct module in the [`examples/src/`](./src) directory.
71+
1. The example MAY be nested arbitrarily deeply,
72+
but every intermediate directory MUST contain a `__init__.py` file
73+
so that CPython 2.7 will recognize it as a module.
74+
1. Every example MUST be CPython 2.7 compatible.
75+
1. Each example file MUST contain exactly one example.
76+
1. Each example file MUST contain a function called `run` that runs the example.
77+
1. If your `run` function needs any of the following inputs,
78+
the parameters MUST have the following names:
79+
* `aws_kms_cmk` (`str`) : A single AWS KMS CMK ARN.
80+
* NOTE: You can assume that automatically discovered credentials have
81+
`kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK.
82+
* `aws_kms_generator_cmk` (`str`) : A single AWS KMS CMK ARN to use as a generator key.
83+
* NOTE: You can assume that automatically discovered credentials have
84+
`kms:GenerateDataKey`, `kms:Encrypt`, and `kms:Decrypt` permissions on this CMK.
85+
* `aws_kms_additional_cmks` (`List[str]`) :
86+
A list of AWS KMS CMK ARNs to use for encrypting and decrypting data keys.
87+
* NOTE: You can assume that automatically discovered credentials have
88+
`kms:Encrypt` and `kms:Decrypt` permissions on these CMKs.
89+
* `source_plaintext` (`bytes`) : Plaintext data to encrypt.
90+
* `source_plaintext_filename` (`str`) : A path to a file containing plaintext to encrypt.
91+
* NOTE: You can assume that you have write access to the parent directory
92+
and that anything you do in that directory will be cleaned up
93+
by our test runners.
94+
1. Any additional parameters MUST be optional.
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example shows how to use the streaming encrypt and decrypt APIs when working with files.
5+
6+
One benefit of using the streaming API is that
7+
we can check the encryption context in the header before we start decrypting.
8+
9+
In this example, we use an AWS KMS customer master key (CMK),
10+
but you can use other key management options with the AWS Encryption SDK.
11+
For examples that demonstrate how to use other key management configurations,
12+
see the ``keyring`` and ``master_key_provider`` directories.
13+
"""
14+
import filecmp
15+
16+
import aws_encryption_sdk
17+
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
18+
19+
20+
def run(aws_kms_cmk, source_plaintext_filename):
21+
# type: (str, str) -> None
22+
"""Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs with files.
23+
24+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
25+
:param str source_plaintext_filename: Path to plaintext file to encrypt
26+
"""
27+
# We assume that you can also write to the directory containing the plaintext file,
28+
# so that is where we will put all of the results.
29+
ciphertext_filename = source_plaintext_filename + ".encrypted"
30+
decrypted_filename = ciphertext_filename + ".decrypted"
31+
32+
# Prepare your encryption context.
33+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
34+
encryption_context = {
35+
"encryption": "context",
36+
"is not": "secret",
37+
"but adds": "useful metadata",
38+
"that can help you": "be confident that",
39+
"the data you are handling": "is what you think it is",
40+
}
41+
42+
# Create the keyring that determines how your data keys are protected.
43+
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)
44+
45+
# Open the files you want to work with.
46+
with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext:
47+
# The streaming API provides a context manager.
48+
# You can read from it just as you read from a file.
49+
with aws_encryption_sdk.stream(
50+
mode="encrypt", source=plaintext, encryption_context=encryption_context, keyring=keyring
51+
) as encryptor:
52+
# Iterate through the segments in the context manager
53+
# and write the results to the ciphertext.
54+
for segment in encryptor:
55+
ciphertext.write(segment)
56+
57+
# Verify that the ciphertext and plaintext are different.
58+
assert not filecmp.cmp(source_plaintext_filename, ciphertext_filename)
59+
60+
# Open the files you want to work with.
61+
with open(ciphertext_filename, "rb") as ciphertext, open(decrypted_filename, "wb") as decrypted:
62+
# Decrypt your encrypted data using the same keyring you used on encrypt.
63+
#
64+
# We do not need to specify the encryption context on decrypt
65+
# because the message header includes the encryption context.
66+
with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor:
67+
# Check the encryption context in the header before we start decrypting.
68+
#
69+
# Verify that the encryption context used in the decrypt operation includes
70+
# the encryption context that you specified when encrypting.
71+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
72+
#
73+
# In production, always use a meaningful encryption context.
74+
assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items())
75+
76+
# Now that we are more confident that we will decrypt the right message,
77+
# we can start decrypting.
78+
for segment in decryptor:
79+
decrypted.write(segment)
80+
81+
# Verify that the decrypted plaintext is identical to the original plaintext.
82+
assert filecmp.cmp(source_plaintext_filename, decrypted_filename)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
This example shows how to use the streaming encrypt and decrypt APIs on data in memory.
5+
6+
One benefit of using the streaming API is that
7+
we can check the encryption context in the header before we start decrypting.
8+
9+
In this example, we use an AWS KMS customer master key (CMK),
10+
but you can use other key management options with the AWS Encryption SDK.
11+
For examples that demonstrate how to use other key management configurations,
12+
see the ``keyring`` and ``mater_key_provider`` directories.
13+
"""
14+
import io
15+
16+
import aws_encryption_sdk
17+
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
18+
19+
20+
def run(aws_kms_cmk, source_plaintext):
21+
# type: (str, bytes) -> None
22+
"""Demonstrate an encrypt/decrypt cycle using the streaming encrypt/decrypt APIs in-memory.
23+
24+
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
25+
:param bytes source_plaintext: Plaintext to encrypt
26+
"""
27+
# Prepare your encryption context.
28+
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
29+
encryption_context = {
30+
"encryption": "context",
31+
"is not": "secret",
32+
"but adds": "useful metadata",
33+
"that can help you": "be confident that",
34+
"the data you are handling": "is what you think it is",
35+
}
36+
37+
# Create the keyring that determines how your data keys are protected.
38+
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)
39+
40+
ciphertext = io.BytesIO()
41+
42+
# The streaming API provides a context manager.
43+
# You can read from it just as you read from a file.
44+
with aws_encryption_sdk.stream(
45+
mode="encrypt", source=source_plaintext, encryption_context=encryption_context, keyring=keyring
46+
) as encryptor:
47+
# Iterate through the segments in the context manager
48+
# and write the results to the ciphertext.
49+
for segment in encryptor:
50+
ciphertext.write(segment)
51+
52+
# Verify that the ciphertext and plaintext are different.
53+
assert ciphertext.getvalue() != source_plaintext
54+
55+
# Reset the ciphertext stream position so that we can read from the beginning.
56+
ciphertext.seek(0)
57+
58+
# Decrypt your encrypted data using the same keyring you used on encrypt.
59+
#
60+
# We do not need to specify the encryption context on decrypt
61+
# because the header message includes the encryption context.
62+
decrypted = io.BytesIO()
63+
with aws_encryption_sdk.stream(mode="decrypt", source=ciphertext, keyring=keyring) as decryptor:
64+
# Check the encryption context in the header before we start decrypting.
65+
#
66+
# Verify that the encryption context used in the decrypt operation includes
67+
# the encryption context that you specified when encrypting.
68+
# The AWS Encryption SDK can add pairs, so don't require an exact match.
69+
#
70+
# In production, always use a meaningful encryption context.
71+
assert set(encryption_context.items()) <= set(decryptor.header.encryption_context.items())
72+
73+
# Now that we are more confident that we will decrypt the right message,
74+
# we can start decrypting.
75+
for segment in decryptor:
76+
decrypted.write(segment)
77+
78+
# Verify that the decrypted plaintext is identical to the original plaintext.
79+
assert decrypted.getvalue() == source_plaintext

examples/src/legacy/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""
4+
Legacy examples.
5+
6+
We keep these older examples as reference material,
7+
but we recommend that you use the new examples.
8+
The new examples reflect our current guidance for using the library.
9+
"""

examples/src/basic_encryption.py renamed to examples/src/legacy/basic_encryption.py

+6-16
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,19 @@
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-
"""Example showing basic encryption and decryption of a value already in memory."""
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Example showing how to encrypt and decrypt a value in memory."""
144
import aws_encryption_sdk
155

166

17-
def cycle_string(key_arn, source_plaintext, botocore_session=None):
7+
def run(aws_kms_cmk, source_plaintext, botocore_session=None):
188
"""Encrypts and then decrypts a string under a KMS customer master key (CMK).
199
20-
:param str key_arn: Amazon Resource Name (ARN) of the KMS CMK
10+
:param str aws_kms_cmk: Amazon Resource Name (ARN) of the AWS KMS CMK
2111
:param bytes source_plaintext: Data to encrypt
2212
:param botocore_session: existing botocore session instance
2313
:type botocore_session: botocore.session.Session
2414
"""
2515
# Create a KMS master key provider
26-
kms_kwargs = dict(key_ids=[key_arn])
16+
kms_kwargs = dict(key_ids=[aws_kms_cmk])
2717
if botocore_session is not None:
2818
kms_kwargs["botocore_session"] = botocore_session
2919
master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs)

examples/src/basic_file_encryption_with_multiple_providers.py renamed to examples/src/legacy/basic_file_encryption_with_multiple_providers.py

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
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.
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
133
"""Example showing creation of a RawMasterKeyProvider, how to use multiple
144
master key providers to encrypt, and demonstrating that each master key
155
provider can then be used independently to decrypt the same encrypted message.
@@ -60,12 +50,12 @@ def _get_raw_key(self, key_id):
6050
)
6151

6252

63-
def cycle_file(key_arn, source_plaintext_filename, botocore_session=None):
53+
def run(aws_kms_cmk, source_plaintext_filename, botocore_session=None):
6454
"""Encrypts and then decrypts a file using a KMS master key provider and a custom static master
6555
key provider. Both master key providers are used to encrypt the plaintext file, so either one alone
6656
can decrypt it.
6757
68-
:param str key_arn: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK)
58+
:param str aws_kms_cmk: Amazon Resource Name (ARN) of the KMS Customer Master Key (CMK)
6959
(http://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html)
7060
:param str source_plaintext_filename: Filename of file to encrypt
7161
:param botocore_session: existing botocore session instance
@@ -77,7 +67,7 @@ def cycle_file(key_arn, source_plaintext_filename, botocore_session=None):
7767
cycled_static_plaintext_filename = source_plaintext_filename + ".static.decrypted"
7868

7969
# Create a KMS master key provider
80-
kms_kwargs = dict(key_ids=[key_arn])
70+
kms_kwargs = dict(key_ids=[aws_kms_cmk])
8171
if botocore_session is not None:
8272
kms_kwargs["botocore_session"] = botocore_session
8373
kms_master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs)

examples/src/basic_file_encryption_with_raw_key_provider.py renamed to examples/src/legacy/basic_file_encryption_with_raw_key_provider.py

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
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.
1+
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
133
"""Example showing creation and use of a RawMasterKeyProvider."""
144
import filecmp
155
import os
@@ -48,7 +38,7 @@ def _get_raw_key(self, key_id):
4838
)
4939

5040

51-
def cycle_file(source_plaintext_filename):
41+
def run(source_plaintext_filename):
5242
"""Encrypts and then decrypts a file under a custom static master key provider.
5343
5444
:param str source_plaintext_filename: Filename of file to encrypt

0 commit comments

Comments
 (0)