Skip to content

Commit 83db9a6

Browse files
authored
feat: AWS KMS multi-Region Key support (#715)
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.
1 parent 7619133 commit 83db9a6

Some content is hidden

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

51 files changed

+4711
-320
lines changed

.clang-format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ RawStringFormats:
123123
- ParseTextProtoOrDie
124124
CanonicalDelimiter: ''
125125
BasedOnStyle: google
126-
ReflowComments: true
126+
ReflowComments: false
127127
SortIncludes: true
128128
SortUsingDeclarations: true
129129
SpaceAfterCStyleCast: false

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ build
55
.DS_Store
66
*.goto
77
*.log
8+
/.history
9+
/specification_compliance_report.html
10+
/cmake-build-*

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
[submodule "verification/litani"]
1111
path = verification/litani
1212
url = https://github.com/awslabs/aws-build-accumulator
13+
[submodule "aws-encryption-sdk-specification"]
14+
path = aws-encryption-sdk-specification
15+
url = https://github.com/awslabs/aws-encryption-sdk-specification.git

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## 2.3.0 -- 2021-06-16
4+
5+
- AWS KMS multi-Region Key support
6+
7+
Added the new keyring KmsMrkAwareSymmetricKeyring that support AWS KMS
8+
multi-Region keys.
9+
Added the helper MultiKeyringBuilder that compose multiple
10+
KmsMrkAwareSymmetricKeyrings together to handle multiple CMKs.
11+
12+
See https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
13+
for more details about AWS KMS multi-Region Keys.
14+
See https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks
15+
for more details about how the AWS Encryption SDK interoperates
16+
with AWS KMS multi-Region keys.
17+
318
## 2.2.0 -- 2021-05-27
419

520
* Improvements to the message decryption process.

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ set(PROJECT_NAME aws-encryption-sdk)
5050

5151
# Version number of the SDK to be consumed by C code and Doxygen
5252
set(MAJOR 2)
53-
set(MINOR 2)
53+
set(MINOR 3)
5454
set(PATCH 0)
5555

5656
# Compiler feature tests and feature flags
@@ -68,6 +68,8 @@ if(AWS_ENC_SDK_END_TO_END_TESTS)
6868
include(FindCURL)
6969
endif()
7070

71+
option(AWS_ENC_SDK_KNOWN_GOOD_TESTS "Enable static test vectors.")
72+
7173
option(AWS_ENC_SDK_END_TO_END_EXAMPLES "Enable end-to-end examples. If set to FALSE (the default), runs local examples only.")
7274

7375
option(PERFORM_HEADER_CHECK "Performs compile-time checks that each header can be included independently. Requires a C++ compiler.")

aws-encryption-sdk-cpp/CMakeLists.txt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,19 +125,25 @@ if (AWS_ENC_SDK_KNOWN_GOOD_TESTS)
125125
)
126126

127127
# Also unzip the test vectors from the aws-encryption-sdk-test-vectors submodule
128-
# TODO-RS: If desired, we can use a custom target to avoid doing this
129-
# on every build when the zip hasn't changed.
130-
message(STATUS "Unzipping test vectors")
128+
set(AWS_ENC_SDK_TEST_VECTORS_ZIP
129+
"${CMAKE_CURRENT_SOURCE_DIR}/tests/test_vectors/aws-encryption-sdk-test-vectors/vectors/awses-decrypt/python-2.3.0.zip"
130+
CACHE FILEPATH
131+
"Path to the test vectors zip file")
132+
if(NOT EXISTS ${AWS_ENC_SDK_TEST_VECTORS_ZIP})
133+
message(FATAL_ERROR "Could not find test vectors at ${AWS_ENC_SDK_TEST_VECTORS_ZIP}")
134+
endif()
135+
131136
set(TEST_VECTORS_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test-vectors)
132137
file(MAKE_DIRECTORY ${TEST_VECTORS_DIRECTORY})
133-
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
134-
WORKING_DIRECTORY ${TEST_VECTORS_DIRECTORY}
135-
RESULT_VARIABLE _exit_code)
136-
if(${_exit_code})
137-
message(FATAL_ERROR "Unzipping test vectors failed")
138-
endif()
138+
add_custom_target(unzip_test_vectors ALL
139+
COMMENT "Unzipping test vectors"
140+
COMMAND ${CMAKE_COMMAND} -E tar xf ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
141+
WORKING_DIRECTORY ${TEST_VECTORS_DIRECTORY}
142+
DEPENDS ${AWS_ENC_SDK_TEST_VECTORS_ZIP}
143+
)
139144

140145
aws_add_test(static_test_vectors ${CMAKE_CURRENT_BINARY_DIR}/static_test_vectors ${TEST_VECTORS_DIRECTORY})
146+
add_dependencies(static_test_vectors unzip_test_vectors)
141147
else()
142148
message(STATUS "Static known good tests off")
143149
endif()

aws-encryption-sdk-cpp/include/aws/cryptosdk/cpp/kms_keyring.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class AWS_CRYPTOSDK_CPP_API CachingClientSupplier : public ClientSupplier {
201201
static std::shared_ptr<CachingClientSupplier> Create();
202202

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

240-
static const char *AWS_CRYPTO_SDK_DISCOVERY_FILTER_CLASS_TAG = "DiscoveryFilter";
241-
242241
/**
243242
* Builder for DiscoveryFilter objects.
244243
*/
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. A copy of the License is
6+
* located at
7+
*
8+
* http://aws.amazon.com/apache2.0/
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
* implied. See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
#ifndef AWS_ENCRYPTION_SDK_KMS_MRK_KEYRING_H
16+
#define AWS_ENCRYPTION_SDK_KMS_MRK_KEYRING_H
17+
18+
#include <aws/cryptosdk/cpp/exports.h>
19+
20+
#include <aws/core/Aws.h>
21+
#include <aws/core/utils/Outcome.h>
22+
#include <aws/core/utils/memory/stl/AWSMap.h>
23+
#include <aws/core/utils/memory/stl/AWSSet.h>
24+
#include <aws/core/utils/memory/stl/AWSString.h>
25+
#include <aws/core/utils/memory/stl/AWSVector.h>
26+
#include <aws/cryptosdk/materials.h>
27+
#include <aws/kms/KMSClient.h>
28+
#include <functional>
29+
#include <mutex>
30+
31+
namespace Aws {
32+
namespace Cryptosdk {
33+
namespace KmsMrkAwareSymmetricKeyring {
34+
35+
/**
36+
* @defgroup kms_mrk_keyring KMS Multi-Region Key aware keyring (AWS SDK for C++)
37+
*
38+
* We have implemented a keyring on top of KMS, which uses the AWS SDK for C++
39+
* as its underlying KMS client.
40+
*
41+
* Because there is no pure-C AWS KMS client at the moment, C++ is required to
42+
* use this keyring. We expect that a parallel pure-C API will be added in the
43+
* future when a pure-C KMS client becomes available.
44+
*
45+
* @{
46+
*/
47+
48+
/**
49+
* Helper class for building a new KmsMrkAwareSymmetricKeyring object. You
50+
* cannot construct a KmsMrkAwareSymmetricKeyring directly and must use this
51+
* class instead. This class is the only API you need to interact with these
52+
* keyrings. You will set all of the configuration of the keyring with this
53+
* class before calling Build, and once the keyring is built, its configuration
54+
* cannot be changed.
55+
56+
* After the keyring is constructed, the only ways you should interact with the
57+
* (aws_cryptosdk_keyring *) are to pass it to a CMM or another keyring (such as the multi-keyring)
58+
* and to release the pointer with aws_cryptosdk_keyring_release.
59+
*
60+
* For general documentation about keyrings see include/aws/cryptosdk/materials.h. This header will
61+
* only document what is specific to the KmsMrkAwareSymmetricKeyring.
62+
*/
63+
class AWS_CRYPTOSDK_CPP_API Builder {
64+
public:
65+
/**
66+
* Adds a single grant token. For more information, see
67+
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
68+
*
69+
* May be called multiple times, adding additional grant tokens to the list that the keyring
70+
* is configured with. Once a grant token is added to the builder, it is not removable.
71+
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
72+
*/
73+
Builder &WithGrantToken(const Aws::String &grant_token);
74+
75+
/**
76+
* Adds multiple grant tokens. For more information, see
77+
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
78+
*
79+
* May be called multiple times, adding additional grant tokens to the list that the keyring
80+
* is configured with. Once a grant token is added to the builder, it is not removable.
81+
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
82+
*/
83+
Builder &WithGrantTokens(const Aws::Vector<Aws::String> &grant_tokens);
84+
85+
/**
86+
* Sets the object that supplies and caches KMSClient instances. This allows sharing of a
87+
* client cache among multiple KMS keyrings. A client supplier which caches KMS clients
88+
* only within this KMS keyring will be created by default if one is not provided.
89+
*
90+
* This option is invalid for a KmsMrkAwareSymmetricKeyring in discovery
91+
* mode. Instead, supply a single KMS client using WithKmsClient.
92+
*/
93+
Builder &WithClientSupplier(const std::shared_ptr<KmsKeyring::ClientSupplier> &client_supplier);
94+
95+
/**
96+
* KmsMrkAwareSymmetricKeyring will use only this KMS Client. Note that this is only suitable if all
97+
* KMS keys are in one region. If this is set then the client supplier parameter is ignored.
98+
*
99+
* This option is required for a KmsMrkAwareSymmetricKeyring in discovery mode.
100+
*/
101+
Builder &WithKmsClient(const std::shared_ptr<KMS::KMSClient> &kms_client);
102+
103+
/**
104+
* Creates a new KmsMrkAwareSymmetricKeyring object using the provided KMS
105+
* CMK, or returns NULL if parameters are invalid. The keyring will use the
106+
* CMK for both encryption and decryption. (If you need to encrypt or
107+
* decrypt with multiple KMS CMKs, use @ref MultiKeyringBuilder.)
108+
*
109+
* If this keyring is called for encryption after another keyring has
110+
* already generated the data key (for example, in a multi-keyring) then
111+
* the CMK will encrypt an existing data key. In that case, you will need
112+
* KMS Encrypt permissions on this CMK. Otherwise, if this keyring is
113+
* called for encryption before any other keyring has generated a data key,
114+
* then you additionally need KMS GenerateDataKey permissions.
115+
*
116+
* If this keyring is called for decryption, you will need KMS Decrypt
117+
* permission on the CMK.
118+
*
119+
* Key IDs for encryption may be specified in two different ways:
120+
*
121+
* (1) key ARN: arn:aws:kms:us-east-1:999999999999:key/01234567-89ab-cdef-fedc-ba9876543210
122+
* (2) alias ARN: arn:aws:kms:us-east-1:999999999999:alias/MyCryptoKey
123+
*
124+
* Key IDs for decryption must be specified as key ARNs only, i.e., format
125+
* (1) above. Format (2) will not work for decryption. The AWS Encryption
126+
* SDK will allow you to attempt decrypts with a
127+
* KmsMrkAwareSymmetricKeyring configured with keys in format (2) without
128+
* errors, but it will only succeed in decrypting data that was encrypted
129+
* with keys that were specified in key ARN format. This is a limitation of
130+
* the message format of encryption and of the KMS APIs, not of this
131+
* library.
132+
*/
133+
aws_cryptosdk_keyring *Build(const Aws::String &key_id) const;
134+
135+
/**
136+
* Creates a new KmsMrkAwareSymmetricKeyring object with no KMS keys configured, i.e., in "discovery" mode.
137+
* This means the following:
138+
*
139+
* (1) This keyring will not do anything on encryption attempts. If you attempt encryption
140+
* with this as your only keyring, it will fail.
141+
*
142+
* (2) On attempts to decrypt, the AWS Encryption SDK will attempt KMS DecryptDataKey calls for
143+
* every KMS key that was used to encrypt the data until it finds one that you have permission
144+
* to use, and is in the specified region. This may include calls to KMS keys that are outside
145+
* of your account, unless prevented by policies on the IAM user or role.
146+
*
147+
* IMPORTANT: The provided region MUST match the region of the configured KMS client.
148+
*
149+
* If you need to decrypt in multiple regions, use @ref MultiKeyringBuilder.
150+
*/
151+
aws_cryptosdk_keyring *BuildDiscovery(const Aws::String &region) const;
152+
153+
/**
154+
* Creates a new KmsMrkAwareSymmetricKeyring object in discovery mode (i.e., no KMS keys
155+
* configured) but with a DiscoveryFilter. This means the following:
156+
*
157+
* (1) As in discovery mode without a DiscoveryFilter, this keyring will
158+
* not do anything on encryption attempts.
159+
*
160+
* (2) On attempts to decrypt, the AWS Encryption SDK will attempt KMS
161+
* DecryptDataKey calls for every KMS key that was used to encrypt the
162+
* data until it finds one that:
163+
*
164+
* (a) you have permission to use, and
165+
* (b) is in the specified region, and
166+
* (c) is in an account specified by the DiscoveryFilter
167+
*
168+
* The discovery_filter argument must not be nullptr, or else this function
169+
* fails and returns nullptr.
170+
*
171+
* IMPORTANT: The provided region MUST match the region of the configured KMS client.
172+
*
173+
* If you need to decrypt in multiple regions, use @ref MultiKeyringBuilder.
174+
*/
175+
aws_cryptosdk_keyring *BuildDiscovery(
176+
const Aws::String &region, std::shared_ptr<KmsKeyring::DiscoveryFilter> discovery_filter) const;
177+
178+
private:
179+
std::shared_ptr<KMS::KMSClient> kms_client;
180+
Aws::Vector<Aws::String> grant_tokens;
181+
std::shared_ptr<KmsKeyring::ClientSupplier> client_supplier;
182+
Aws::String region;
183+
};
184+
185+
/**
186+
* Helper class for building multi-keyrings composed of
187+
* KmsMrkAwareSymmetricKeyring objects.
188+
*/
189+
class AWS_CRYPTOSDK_CPP_API MultiKeyringBuilder {
190+
public:
191+
/**
192+
* Adds a single grant token. For more information, see
193+
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
194+
*
195+
* May be called multiple times, adding additional grant tokens to the list that the keyring
196+
* is configured with. Once a grant token is added to the builder, it is not removable.
197+
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
198+
*/
199+
MultiKeyringBuilder &WithGrantToken(const Aws::String &grant_token);
200+
201+
/**
202+
* Adds multiple grant tokens. For more information, see
203+
* http://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token
204+
*
205+
* May be called multiple times, adding additional grant tokens to the list that the keyring
206+
* is configured with. Once a grant token is added to the builder, it is not removable.
207+
* To build a KmsMrkAwareSymmetricKeyring with a different set of grant tokens, use a different builder.
208+
*/
209+
MultiKeyringBuilder &WithGrantTokens(const Aws::Vector<Aws::String> &grant_tokens);
210+
211+
/**
212+
* Sets the object that supplies and caches KMSClient instances. This allows sharing of a
213+
* client cache among multiple KMS keyrings. A client supplier which caches KMS clients
214+
* only within this KMS keyring will be created by default if one is not provided.
215+
*/
216+
MultiKeyringBuilder &WithClientSupplier(const std::shared_ptr<KmsKeyring::ClientSupplier> &client_supplier);
217+
218+
/**
219+
* Builds a strict-mode multi-keyring, i.e. a multi-keyring composed of
220+
* KmsMrkAwareSymmetricKeyring objects in strict mode. The resulting
221+
* multi-keyring will only attempt to encrypt or decrypt using key
222+
* identifiers provided as the generator key ID or additional key IDs, as
223+
* well as key identifiers which are multi-region replicas.
224+
*/
225+
aws_cryptosdk_keyring *Build(
226+
const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
227+
228+
/**
229+
* Builds a strict-mode multi-keyring, i.e. a multi-keyring composed of
230+
* KmsMrkAwareSymmetricKeyring objects in strict mode. The resulting
231+
* multi-keyring will only attempt to encrypt or decrypt using the key
232+
* identifiers provided, well as key identifiers which are multi-region
233+
* replicas.
234+
*
235+
* It will not generate data keys - to create a strict-mode multi-keyring
236+
* that generates data keys, use @ref Build(const Aws::String &, const Aws::Vector<Aws::String> &).
237+
*/
238+
aws_cryptosdk_keyring *Build(const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
239+
240+
/**
241+
* Builds a discovery-mode multi-keyring, i.e. a multi-keyring composed of
242+
* KmsMrkAwareSymmetricKeyring objects in discovery mode, one for each
243+
* provided region.
244+
*/
245+
aws_cryptosdk_keyring *BuildDiscovery(
246+
const Aws::Set<Aws::String> &regions,
247+
std::shared_ptr<KmsKeyring::DiscoveryFilter> discovery_filter = nullptr) const;
248+
249+
private:
250+
Aws::Vector<Aws::String> grant_tokens;
251+
std::shared_ptr<KmsKeyring::ClientSupplier> client_supplier;
252+
253+
/**
254+
* Builds a strict-mode multi-keyring. `generator_key_id` may be blank to
255+
* indicate that the multi-keyring should not include a generator keyring.
256+
*/
257+
aws_cryptosdk_keyring *_Build(
258+
const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
259+
};
260+
261+
/** @} */ // doxygen group kms_mrk_keyring
262+
263+
} // namespace KmsMrkAwareSymmetricKeyring
264+
} // namespace Cryptosdk
265+
} // namespace Aws
266+
267+
#endif // AWS_ENCRYPTION_SDK_KMS_KEYRING_H

0 commit comments

Comments
 (0)