Skip to content

Commit

Permalink
feat: AWS KMS multi-Region Key support (#715)
Browse files Browse the repository at this point in the history
Added the new keyring KmsMrkAwareSymmetricKeyring that supports AWS KMS
multi-Region keys.
Added the helper MultiKeyringBuilder that composes multiple
KmsMrkAwareSymmetricKeyrings together to handle multiple CMKs.

See https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
for more details about AWS KMS multi-Region Keys.
See https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
for more details about how the AWS Encryption SDK interoperates
with AWS KMS multi-Region keys.
  • Loading branch information
alex-chew authored Jun 16, 2021
1 parent 7619133 commit 83db9a6
Show file tree
Hide file tree
Showing 51 changed files with 4,711 additions and 320 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ RawStringFormats:
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
ReflowComments: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ build
.DS_Store
*.goto
*.log
/.history
/specification_compliance_report.html
/cmake-build-*
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "verification/litani"]
path = verification/litani
url = https://github.com/awslabs/aws-build-accumulator
[submodule "aws-encryption-sdk-specification"]
path = aws-encryption-sdk-specification
url = https://github.com/awslabs/aws-encryption-sdk-specification.git
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 2.3.0 -- 2021-06-16

- AWS KMS multi-Region Key support

Added the new keyring KmsMrkAwareSymmetricKeyring that support AWS KMS
multi-Region keys.
Added the helper MultiKeyringBuilder that compose multiple
KmsMrkAwareSymmetricKeyrings together to handle multiple CMKs.

See https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
for more details about AWS KMS multi-Region Keys.
See https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
for more details about how the AWS Encryption SDK interoperates
with AWS KMS multi-Region keys.

## 2.2.0 -- 2021-05-27

* Improvements to the message decryption process.
Expand Down
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ set(PROJECT_NAME aws-encryption-sdk)

# Version number of the SDK to be consumed by C code and Doxygen
set(MAJOR 2)
set(MINOR 2)
set(MINOR 3)
set(PATCH 0)

# Compiler feature tests and feature flags
Expand All @@ -68,6 +68,8 @@ if(AWS_ENC_SDK_END_TO_END_TESTS)
include(FindCURL)
endif()

option(AWS_ENC_SDK_KNOWN_GOOD_TESTS "Enable static test vectors.")

option(AWS_ENC_SDK_END_TO_END_EXAMPLES "Enable end-to-end examples. If set to FALSE (the default), runs local examples only.")

option(PERFORM_HEADER_CHECK "Performs compile-time checks that each header can be included independently. Requires a C++ compiler.")
Expand Down
24 changes: 15 additions & 9 deletions aws-encryption-sdk-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,25 @@ if (AWS_ENC_SDK_KNOWN_GOOD_TESTS)
)

# Also unzip the test vectors from the aws-encryption-sdk-test-vectors submodule
# TODO-RS: If desired, we can use a custom target to avoid doing this
# on every build when the zip hasn't changed.
message(STATUS "Unzipping test vectors")
set(AWS_ENC_SDK_TEST_VECTORS_ZIP
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_vectors/aws-encryption-sdk-test-vectors/vectors/awses-decrypt/python-2.3.0.zip"
CACHE FILEPATH
"Path to the test vectors zip file")
if(NOT EXISTS ${AWS_ENC_SDK_TEST_VECTORS_ZIP})
message(FATAL_ERROR "Could not find test vectors at ${AWS_ENC_SDK_TEST_VECTORS_ZIP}")
endif()

set(TEST_VECTORS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test-vectors)
file(MAKE_DIRECTORY ${TEST_VECTORS_DIRECTORY})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
WORKING_DIRECTORY ${TEST_VECTORS_DIRECTORY}
RESULT_VARIABLE _exit_code)
if(${_exit_code})
message(FATAL_ERROR "Unzipping test vectors failed")
endif()
add_custom_target(unzip_test_vectors ALL
COMMENT "Unzipping test vectors"
COMMAND ${CMAKE_COMMAND} -E tar xf ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
WORKING_DIRECTORY ${TEST_VECTORS_DIRECTORY}
DEPENDS ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
)

aws_add_test(static_test_vectors ${CMAKE_CURRENT_BINARY_DIR}/static_test_vectors ${TEST_VECTORS_DIRECTORY})
add_dependencies(static_test_vectors unzip_test_vectors)
else()
message(STATUS "Static known good tests off")
endif()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class AWS_CRYPTOSDK_CPP_API CachingClientSupplier : public ClientSupplier {
static std::shared_ptr<CachingClientSupplier> Create();

/**
* If @param region is the empty string, then returns a KMS client in the SDK's default region.
* If a client is already cached for this region, returns that one and provides a no-op callable.
* If a client is not already cached for this region, returns a KMS client with default settings
* and provides a callable which will cache the client. Never returns nullptr.
Expand Down Expand Up @@ -237,8 +238,6 @@ class AWS_CRYPTOSDK_CPP_API SingleClientSupplier : public ClientSupplier {
std::shared_ptr<KMS::KMSClient> kms_client;
};

static const char *AWS_CRYPTO_SDK_DISCOVERY_FILTER_CLASS_TAG = "DiscoveryFilter";

/**
* Builder for DiscoveryFilter objects.
*/
Expand Down
267 changes: 267 additions & 0 deletions aws-encryption-sdk-cpp/include/aws/cryptosdk/cpp/kms_mrk_keyring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
* this file except in compliance with the License. A copy of the License is
* located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef AWS_ENCRYPTION_SDK_KMS_MRK_KEYRING_H
#define AWS_ENCRYPTION_SDK_KMS_MRK_KEYRING_H

#include <aws/cryptosdk/cpp/exports.h>

#include <aws/core/Aws.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/memory/stl/AWSMap.h>
#include <aws/core/utils/memory/stl/AWSSet.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/core/utils/memory/stl/AWSVector.h>
#include <aws/cryptosdk/materials.h>
#include <aws/kms/KMSClient.h>
#include <functional>
#include <mutex>

namespace Aws {
namespace Cryptosdk {
namespace KmsMrkAwareSymmetricKeyring {

/**
* @defgroup kms_mrk_keyring KMS Multi-Region Key aware keyring (AWS SDK for C++)
*
* We have implemented a keyring on top of KMS, which uses the AWS SDK for C++
* as its underlying KMS client.
*
* Because there is no pure-C AWS KMS client at the moment, C++ is required to
* use this keyring. We expect that a parallel pure-C API will be added in the
* future when a pure-C KMS client becomes available.
*
* @{
*/

/**
* Helper class for building a new KmsMrkAwareSymmetricKeyring object. You
* cannot construct a KmsMrkAwareSymmetricKeyring directly and must use this
* class instead. This class is the only API you need to interact with these
* keyrings. You will set all of the configuration of the keyring with this
* class before calling Build, and once the keyring is built, its configuration
* cannot be changed.
* After the keyring is constructed, the only ways you should interact with the
* (aws_cryptosdk_keyring *) are to pass it to a CMM or another keyring (such as the multi-keyring)
* and to release the pointer with aws_cryptosdk_keyring_release.
*
* For general documentation about keyrings see include/aws/cryptosdk/materials.h. This header will
* only document what is specific to the KmsMrkAwareSymmetricKeyring.
*/
class AWS_CRYPTOSDK_CPP_API Builder {
public:
/**
* Adds a single grant token. For more information, see
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
*
* May be called multiple times, adding additional grant tokens to the list that the keyring
* is configured with. Once a grant token is added to the builder, it is not removable.
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
*/
Builder &WithGrantToken(const Aws::String &grant_token);

/**
* Adds multiple grant tokens. For more information, see
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
*
* May be called multiple times, adding additional grant tokens to the list that the keyring
* is configured with. Once a grant token is added to the builder, it is not removable.
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
*/
Builder &WithGrantTokens(const Aws::Vector<Aws::String> &grant_tokens);

/**
* Sets the object that supplies and caches KMSClient instances. This allows sharing of a
* client cache among multiple KMS keyrings. A client supplier which caches KMS clients
* only within this KMS keyring will be created by default if one is not provided.
*
* This option is invalid for a KmsMrkAwareSymmetricKeyring in discovery
* mode. Instead, supply a single KMS client using WithKmsClient.
*/
Builder &WithClientSupplier(const std::shared_ptr<KmsKeyring::ClientSupplier> &client_supplier);

/**
* KmsMrkAwareSymmetricKeyring will use only this KMS Client. Note that this is only suitable if all
* KMS keys are in one region. If this is set then the client supplier parameter is ignored.
*
* This option is required for a KmsMrkAwareSymmetricKeyring in discovery mode.
*/
Builder &WithKmsClient(const std::shared_ptr<KMS::KMSClient> &kms_client);

/**
* Creates a new KmsMrkAwareSymmetricKeyring object using the provided KMS
* CMK, or returns NULL if parameters are invalid. The keyring will use the
* CMK for both encryption and decryption. (If you need to encrypt or
* decrypt with multiple KMS CMKs, use @ref MultiKeyringBuilder.)
*
* If this keyring is called for encryption after another keyring has
* already generated the data key (for example, in a multi-keyring) then
* the CMK will encrypt an existing data key. In that case, you will need
* KMS Encrypt permissions on this CMK. Otherwise, if this keyring is
* called for encryption before any other keyring has generated a data key,
* then you additionally need KMS GenerateDataKey permissions.
*
* If this keyring is called for decryption, you will need KMS Decrypt
* permission on the CMK.
*
* Key IDs for encryption may be specified in two different ways:
*
* (1) key ARN: arn:aws:kms:us-east-1:999999999999:key/01234567-89ab-cdef-fedc-ba9876543210
* (2) alias ARN: arn:aws:kms:us-east-1:999999999999:alias/MyCryptoKey
*
* Key IDs for decryption must be specified as key ARNs only, i.e., format
* (1) above. Format (2) will not work for decryption. The AWS Encryption
* SDK will allow you to attempt decrypts with a
* KmsMrkAwareSymmetricKeyring configured with keys in format (2) without
* errors, but it will only succeed in decrypting data that was encrypted
* with keys that were specified in key ARN format. This is a limitation of
* the message format of encryption and of the KMS APIs, not of this
* library.
*/
aws_cryptosdk_keyring *Build(const Aws::String &key_id) const;

/**
* Creates a new KmsMrkAwareSymmetricKeyring object with no KMS keys configured, i.e., in "discovery" mode.
* This means the following:
*
* (1) This keyring will not do anything on encryption attempts. If you attempt encryption
* with this as your only keyring, it will fail.
*
* (2) On attempts to decrypt, the AWS Encryption SDK will attempt KMS DecryptDataKey calls for
* every KMS key that was used to encrypt the data until it finds one that you have permission
* to use, and is in the specified region. This may include calls to KMS keys that are outside
* of your account, unless prevented by policies on the IAM user or role.
*
* IMPORTANT: The provided region MUST match the region of the configured KMS client.
*
* If you need to decrypt in multiple regions, use @ref MultiKeyringBuilder.
*/
aws_cryptosdk_keyring *BuildDiscovery(const Aws::String &region) const;

/**
* Creates a new KmsMrkAwareSymmetricKeyring object in discovery mode (i.e., no KMS keys
* configured) but with a DiscoveryFilter. This means the following:
*
* (1) As in discovery mode without a DiscoveryFilter, this keyring will
* not do anything on encryption attempts.
*
* (2) On attempts to decrypt, the AWS Encryption SDK will attempt KMS
* DecryptDataKey calls for every KMS key that was used to encrypt the
* data until it finds one that:
*
* (a) you have permission to use, and
* (b) is in the specified region, and
* (c) is in an account specified by the DiscoveryFilter
*
* The discovery_filter argument must not be nullptr, or else this function
* fails and returns nullptr.
*
* IMPORTANT: The provided region MUST match the region of the configured KMS client.
*
* If you need to decrypt in multiple regions, use @ref MultiKeyringBuilder.
*/
aws_cryptosdk_keyring *BuildDiscovery(
const Aws::String &region, std::shared_ptr<KmsKeyring::DiscoveryFilter> discovery_filter) const;

private:
std::shared_ptr<KMS::KMSClient> kms_client;
Aws::Vector<Aws::String> grant_tokens;
std::shared_ptr<KmsKeyring::ClientSupplier> client_supplier;
Aws::String region;
};

/**
* Helper class for building multi-keyrings composed of
* KmsMrkAwareSymmetricKeyring objects.
*/
class AWS_CRYPTOSDK_CPP_API MultiKeyringBuilder {
public:
/**
* Adds a single grant token. For more information, see
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
*
* May be called multiple times, adding additional grant tokens to the list that the keyring
* is configured with. Once a grant token is added to the builder, it is not removable.
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
*/
MultiKeyringBuilder &WithGrantToken(const Aws::String &grant_token);

/**
* Adds multiple grant tokens. For more information, see
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
*
* May be called multiple times, adding additional grant tokens to the list that the keyring
* is configured with. Once a grant token is added to the builder, it is not removable.
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
*/
MultiKeyringBuilder &WithGrantTokens(const Aws::Vector<Aws::String> &grant_tokens);

/**
* Sets the object that supplies and caches KMSClient instances. This allows sharing of a
* client cache among multiple KMS keyrings. A client supplier which caches KMS clients
* only within this KMS keyring will be created by default if one is not provided.
*/
MultiKeyringBuilder &WithClientSupplier(const std::shared_ptr<KmsKeyring::ClientSupplier> &client_supplier);

/**
* Builds a strict-mode multi-keyring, i.e. a multi-keyring composed of
* KmsMrkAwareSymmetricKeyring objects in strict mode. The resulting
* multi-keyring will only attempt to encrypt or decrypt using key
* identifiers provided as the generator key ID or additional key IDs, as
* well as key identifiers which are multi-region replicas.
*/
aws_cryptosdk_keyring *Build(
const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;

/**
* Builds a strict-mode multi-keyring, i.e. a multi-keyring composed of
* KmsMrkAwareSymmetricKeyring objects in strict mode. The resulting
* multi-keyring will only attempt to encrypt or decrypt using the key
* identifiers provided, well as key identifiers which are multi-region
* replicas.
*
* It will not generate data keys - to create a strict-mode multi-keyring
* that generates data keys, use @ref Build(const Aws::String &, const Aws::Vector<Aws::String> &).
*/
aws_cryptosdk_keyring *Build(const Aws::Vector<Aws::String> &additional_key_ids = {}) const;

/**
* Builds a discovery-mode multi-keyring, i.e. a multi-keyring composed of
* KmsMrkAwareSymmetricKeyring objects in discovery mode, one for each
* provided region.
*/
aws_cryptosdk_keyring *BuildDiscovery(
const Aws::Set<Aws::String> &regions,
std::shared_ptr<KmsKeyring::DiscoveryFilter> discovery_filter = nullptr) const;

private:
Aws::Vector<Aws::String> grant_tokens;
std::shared_ptr<KmsKeyring::ClientSupplier> client_supplier;

/**
* Builds a strict-mode multi-keyring. `generator_key_id` may be blank to
* indicate that the multi-keyring should not include a generator keyring.
*/
aws_cryptosdk_keyring *_Build(
const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
};

/** @} */ // doxygen group kms_mrk_keyring

} // namespace KmsMrkAwareSymmetricKeyring
} // namespace Cryptosdk
} // namespace Aws

#endif // AWS_ENCRYPTION_SDK_KMS_KEYRING_H
Loading

0 comments on commit 83db9a6

Please sign in to comment.