Skip to content

Commit

Permalink
docs: add CMM examples (#239)
Browse files Browse the repository at this point in the history
* docs: add CMM examples

* docs: add CMM examples to readme

* docs: apply suggestions from code review

Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com>

* docs: fix and refine examples docs

* chore: remove redundant example

* docs: clarify "filtering CMM" references in examples comments

* chore: autoformat

* docs: refactor algorithm restricting example focus

* docs: revise framing of encryption context example and KMS references

* docs: apply suggestions from code review

Co-Authored-By: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com>

Co-authored-by: Wesley Rosenblum <55108558+WesleyRosenblum@users.noreply.github.com>
  • Loading branch information
mattsb42-aws and WesleyRosenblum committed Apr 9, 2020
1 parent 5ac45bf commit cd2a171
Show file tree
Hide file tree
Showing 8 changed files with 416 additions and 4 deletions.
6 changes: 6 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ We start with AWS KMS examples, then show how to use other wrapping keys.
* How to combine AWS KMS with an offline escrow key
* [with keyrings](./src/keyring/multi/aws_kms_with_escrow.py)
* [with master key providers](./src/master_key_provider/multi/aws_kms_with_escrow.py)
* How to reuse data keys across multiple messages
* [with the caching cryptographic materials manager](./src/crypto_materials_manager/caching/simple_cache.py)
* How to restrict algorithm suites
* [with a custom cryptographic materials manager](src/crypto_materials_manager/custom/algorithm_suite_enforcement.py)
* How to require encryption context fields
* [with a custom cryptographic materials manager](src/crypto_materials_manager/custom/requiring_encryption_context_fields.py)

### Keyrings

Expand Down
7 changes: 7 additions & 0 deletions examples/src/crypto_materials_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Cryptographic materials manager examples.
These examples show how to create and use cryptographic materials managers.
"""
7 changes: 7 additions & 0 deletions examples/src/crypto_materials_manager/caching/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Caching cryptographic materials manager examples.
These examples show how to configure and use the caching cryptographic materials manager.
"""
94 changes: 94 additions & 0 deletions examples/src/crypto_materials_manager/caching/simple_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
The default cryptographic materials manager (CMM)
creates new encryption and decryption materials
on every call.
This means every encrypted message is protected by a unique data key,
but it also means that you need to interact with your key management system
in order to process any message.
If this causes performance, operations, or cost issues for you,
you might benefit from data key caching.
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-key-caching.html
This example shows how to configure the caching CMM
to reuse data keys across multiple encrypted messages.
In this example, we use an AWS KMS customer master key (CMK),
but you can use other key management options with the AWS Encryption SDK.
For examples that demonstrate how to use other key management configurations,
see the ``keyring`` and ``master_key_provider`` directories.
In this example, we use the one-step encrypt and decrypt APIs.
"""
import aws_encryption_sdk
from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager


def run(aws_kms_cmk, source_plaintext):
# type: (str, bytes) -> None
"""Demonstrate an encrypt/decrypt cycle using the caching cryptographic materials manager.
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
:param bytes source_plaintext: Plaintext to encrypt
"""
# Prepare your encryption context.
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
encryption_context = {
"encryption": "context",
"is not": "secret",
"but adds": "useful metadata",
"that can help you": "be confident that",
"the data you are handling": "is what you think it is",
}

# Create the keyring that determines how your data keys are protected.
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)

# Create the caching cryptographic materials manager using your keyring.
cmm = CachingCryptoMaterialsManager(
keyring=keyring,
# The cache is where the caching CMM stores the materials.
#
# LocalCryptoMaterialsCache gives you a local, in-memory, cache.
cache=LocalCryptoMaterialsCache(capacity=100),
# max_age determines how long the caching CMM will reuse materials.
#
# This example uses two minutes.
# In production, always choose as small a value as possible
# that works for your requirements.
max_age=120.0,
# max_messages_encrypted determines how many messages
# the caching CMM will protect with the same materials.
#
# In production, always choose as small a value as possible
# that works for your requirements.
max_messages_encrypted=10,
)

# Encrypt your plaintext data.
ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
source=source_plaintext, encryption_context=encryption_context, materials_manager=cmm
)

# Demonstrate that the ciphertext and plaintext are different.
assert ciphertext != source_plaintext

# Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
#
# You do not need to specify the encryption context on decrypt
# because the header of the encrypted message includes the encryption context.
decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, materials_manager=cmm)

# Demonstrate that the decrypted plaintext is identical to the original plaintext.
assert decrypted == source_plaintext

# Verify that the encryption context used in the decrypt operation includes
# the encryption context that you specified when encrypting.
# The AWS Encryption SDK can add pairs, so don't require an exact match.
#
# In production, always use a meaningful encryption context.
assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items())
10 changes: 10 additions & 0 deletions examples/src/crypto_materials_manager/custom/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Custom cryptographic materials manager (CMM) examples.
The AWS Encryption SDK includes CMMs for common use cases,
but you might need to do something else.
These examples show how you could create your own CMM for some specific requirements.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
The AWS Encryption SDK supports several different algorithm suites
that offer different security properties.
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html
By default, the AWS Encryption SDK will let you use any of these,
but you might want to restrict that further.
We recommend that you use the default algorithm suite,
which uses AES-GCM with 256-bit keys, HKDF, and ECDSA message signing.
If your readers and writers have the same permissions,
you might want to omit the message signature for faster operation.
For more information about choosing a signed or unsigned algorithm suite,
see the AWS Encryption SDK developer guide:
https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/supported-algorithms.html#other-algorithms
This example shows how you can make a custom cryptographic materials manager (CMM)
that only allows encrypt requests that either specify one of these two algorithm suites
or do not specify an algorithm suite, in which case the default CMM uses the default algorithm suite.
"""
import aws_encryption_sdk
from aws_encryption_sdk.identifiers import AlgorithmSuite
from aws_encryption_sdk.keyrings.aws_kms import KmsKeyring
from aws_encryption_sdk.keyrings.base import Keyring
from aws_encryption_sdk.materials_managers import (
DecryptionMaterials,
DecryptionMaterialsRequest,
EncryptionMaterials,
EncryptionMaterialsRequest,
)
from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager
from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager


class UnapprovedAlgorithmSuite(Exception):
"""Indicate that an unsupported algorithm suite was requested."""


class RequireApprovedAlgorithmSuitesCryptoMaterialsManager(CryptoMaterialsManager):
"""Only allow encryption requests for approved algorithm suites."""

def __init__(self, keyring):
# type: (Keyring) -> None
"""Set up the inner cryptographic materials manager using the provided keyring.
:param Keyring keyring: Keyring to use in the inner cryptographic materials manager
"""
self._allowed_algorithm_suites = {
None, # no algorithm suite in the request
AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, # the default algorithm suite
AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA256, # the recommended unsigned algorithm suite
}
# Wrap the provided keyring in the default cryptographic materials manager (CMM).
#
# This is the same thing that the encrypt and decrypt APIs, as well as the caching CMM,
# do if you provide a keyring instead of a CMM.
self._cmm = DefaultCryptoMaterialsManager(keyring=keyring)

def get_encryption_materials(self, request):
# type: (EncryptionMaterialsRequest) -> EncryptionMaterials
"""Block any requests that include an unapproved algorithm suite."""
if request.algorithm not in self._allowed_algorithm_suites:
raise UnapprovedAlgorithmSuite("Unapproved algorithm suite requested!")

return self._cmm.get_encryption_materials(request)

def decrypt_materials(self, request):
# type: (DecryptionMaterialsRequest) -> DecryptionMaterials
"""Be more permissive on decrypt and just pass through."""
return self._cmm.decrypt_materials(request)


def run(aws_kms_cmk, source_plaintext):
# type: (str, bytes) -> None
"""Demonstrate an encrypt/decrypt cycle using a custom cryptographic materials manager that filters requests.
:param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
:param bytes source_plaintext: Plaintext to encrypt
"""
# Prepare your encryption context.
# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
encryption_context = {
"encryption": "context",
"is not": "secret",
"but adds": "useful metadata",
"that can help you": "be confident that",
"the data you are handling": "is what you think it is",
}

# Create the keyring that determines how your data keys are protected.
keyring = KmsKeyring(generator_key_id=aws_kms_cmk)

# Create the algorithm suite restricting cryptographic materials manager using your keyring.
cmm = RequireApprovedAlgorithmSuitesCryptoMaterialsManager(keyring=keyring)

# Demonstrate that the algorithm suite restricting CMM will not let you use an unapproved algorithm suite.
try:
aws_encryption_sdk.encrypt(
source=source_plaintext,
encryption_context=encryption_context,
materials_manager=cmm,
algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16,
)
except UnapprovedAlgorithmSuite:
# You asked for an unapproved algorithm suite.
# Reaching this point means everything is working as expected.
pass
else:
# The algorithm suite restricting CMM keeps this from happening.
raise AssertionError("The algorithm suite restricting CMM does not let this happen!")

# Encrypt your plaintext data.
ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
source=source_plaintext, encryption_context=encryption_context, materials_manager=cmm
)

# Demonstrate that the ciphertext and plaintext are different.
assert ciphertext != source_plaintext

# Decrypt your encrypted data using the same cryptographic materials manager you used on encrypt.
#
# You do not need to specify the encryption context on decrypt
# because the header of the encrypted message includes the encryption context.
decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext, materials_manager=cmm)

# Demonstrate that the decrypted plaintext is identical to the original plaintext.
assert decrypted == source_plaintext

# Verify that the encryption context used in the decrypt operation includes
# the encryption context that you specified when encrypting.
# The AWS Encryption SDK can add pairs, so don't require an exact match.
#
# In production, always use a meaningful encryption context.
assert set(encryption_context.items()) <= set(decrypt_header.encryption_context.items())

0 comments on commit cd2a171

Please sign in to comment.