Skip to content

Commit ef90351

Browse files
feat: Updates to the AWS Encryption SDK.
This change includes fixes for issues that were reported by Thai Duong from Google's Security team, and for issues that were identified by AWS Cryptography. See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/migration.html
1 parent eea79fc commit ef90351

File tree

69 files changed

+4161
-397
lines changed

Some content is hidden

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

69 files changed

+4161
-397
lines changed

Diff for: CHANGELOG.rst

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog
33
*********
44

5+
1.7.0 -- 2020-09-24
6+
===================
7+
8+
TODO
9+
510
1.4.1 -- 2019-09-20
611
===================
712

Diff for: README.rst

+85-30
Original file line numberDiff line numberDiff line change
@@ -92,76 +92,121 @@ uses a key derivation function, the data key is used to generate the key that di
9292
*****
9393
Usage
9494
*****
95-
To use this client, you (the caller) must provide an instance of either a master key provider
96-
or a CMM. The examples in this readme use the ``KMSMasterKeyProvider`` class.
9795

98-
KMSMasterKeyProvider
99-
====================
100-
Because the ``KMSMasterKeyProvider`` uses the `boto3 SDK`_ to interact with `AWS KMS`_, it requires AWS Credentials.
101-
To provide these credentials, use the `standard means by which boto3 locates credentials`_ or provide a
102-
pre-existing instance of a ``botocore session`` to the ``KMSMasterKeyProvider``.
103-
This latter option can be useful if you have an alternate way to store your AWS credentials or
104-
you want to reuse an existing instance of a botocore session in order to decrease startup costs.
96+
EncryptionSDKClient
97+
===================
98+
To use this module, you (the caller) must first create an instance of the ``EncryptionSDKClient`` class.
99+
The constructor to this class requires a single keyword argument, ``commitment_policy``. There is
100+
currently only one valid value for this argument: ``FORBID_ENCRYPT_ALLOW_DECRYPT``.
105101

106102
.. code:: python
107103
108104
import aws_encryption_sdk
109-
import botocore.session
105+
from aws_encryption_sdk.identifiers import CommitmentPolicy
106+
107+
108+
client = aws_encryption_sdk.EncryptionSDKClient(
109+
commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
110+
)
111+
110112
111-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider()
113+
You must then create an instance of either a master key provider or a CMM. The examples in this
114+
readme use the ``StrictAwsKmsMasterKeyProvider`` class.
112115

113-
existing_botocore_session = botocore.session.Session()
114-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(botocore_session=existing_botocore_session)
115116

117+
StrictAwsKmsMasterKeyProvider
118+
=============================
119+
A ``StrictAwsKmsMasterKeyProvider`` is configured with an explicit list of AWS KMS
120+
CMKs with which to encrypt and decrypt data. On encryption, it encrypts the plaintext with all
121+
configured CMKs. On decryption, it only attempts to decrypt ciphertexts that have been wrapped
122+
with one of the configured CMKs.
116123

117-
You can pre-load the ``KMSMasterKeyProvider`` with one or more CMKs.
118-
To encrypt data, you must configure the ``KMSMasterKeyProvider`` with as least one CMK.
119-
If you configure the the ``KMSMasterKeyProvider`` with multiple CMKs, the `final message`_
124+
Because the ``StrictAwsKmsMasterKeyProvider`` uses the `boto3 SDK`_ to interact with `AWS KMS`_,
125+
it requires AWS Credentials.
126+
To provide these credentials, use the `standard means by which boto3 locates credentials`_ or provide a
127+
pre-existing instance of a ``botocore session`` to the ``StrictAwsKmsMasterKeyProvider``.
128+
This latter option can be useful if you have an alternate way to store your AWS credentials or
129+
you want to reuse an existing instance of a botocore session in order to decrease startup costs.
130+
131+
To create a ``StrictAwsKmsMasterKeyProvider`` you must provide one or more CMKs.
132+
If you configure the the ``StrictAwsKmsMasterKeyProvider`` with multiple CMKs, the `final message`_
120133
will include a copy of the data key encrypted by each configured CMK.
121134

122135
.. code:: python
123136
124137
import aws_encryption_sdk
125138
126-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[
139+
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
127140
'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222',
128141
'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333'
129142
])
130143
131-
You can add CMKs from multiple regions to the ``KMSMasterKeyProvider``.
144+
You can add CMKs from multiple regions to the ``StrictAwsKmsMasterKeyProvider``.
132145

133146
.. code:: python
134147
135148
import aws_encryption_sdk
136149
137-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[
150+
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
138151
'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222',
139152
'arn:aws:kms:us-west-2:3333333333333:key/33333333-3333-3333-3333-333333333333',
140153
'arn:aws:kms:ap-northeast-1:4444444444444:key/44444444-4444-4444-4444-444444444444'
141154
])
142155
143156
157+
DiscoveryAwsKmsMasterKeyProvider
158+
================================
159+
We recommend using a ``StrictAwsKmsMasterKeyProvider`` in order to ensure that you can only
160+
encrypt and decrypt data using the AWS KMS CMKs you expect. However, if you are unable to
161+
explicitly identify the AWS KMS CMKs that should be used for decryption, you can instead
162+
use a ``DiscoveryAwsKmsMasterKeyProvider`` for decryption operations. This provider
163+
attempts decryption of any ciphertexts as long as they match a ``DiscoveryFilter`` that
164+
you configure. A ``DiscoveryFilter`` consists of a list of AWS account ids and an AWS
165+
partition.
166+
167+
.. code:: python
168+
169+
import aws_encryption_sdk
170+
from aws_encryption_sdk.key_providers.kms import DiscoveryFilter
171+
172+
discovery_filter = DiscoveryFilter(
173+
account_ids=['222222222222', '333333333333'],
174+
partition='aws'
175+
)
176+
kms_key_provider = aws_encryption_sdk.DiscoveryAwsKmsMasterKeyProvider(
177+
discovery_filter=discovery_filter
178+
)
179+
180+
If you do not want to filter the set of allowed accounts, you can also omit the ``discovery_filter`` argument.
181+
182+
Note that a ``DiscoveryAwsKmsMasterKeyProvider`` cannot be used for encryption operations.
183+
144184
Encryption and Decryption
145185
=========================
146-
After you create an instance of a ``MasterKeyProvider``, you can use either of the two
147-
high-level ``encrypt``/``decrypt`` functions to encrypt and decrypt your data.
186+
After you create an instance of an ``EncryptionSDKClient`` and a ``MasterKeyProvider``, you can use either of
187+
the client's two ``encrypt``/``decrypt`` functions to encrypt and decrypt your data.
148188

149189
.. code:: python
150190
151191
import aws_encryption_sdk
192+
from aws_encryption_sdk.identifiers import CommitmentPolicy
152193
153-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[
194+
client = aws_encryption_sdk.EncryptionSDKClient(
195+
commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
196+
)
197+
198+
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
154199
'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222',
155200
'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333'
156201
])
157202
my_plaintext = b'This is some super secret data! Yup, sure is!'
158203
159-
my_ciphertext, encryptor_header = aws_encryption_sdk.encrypt(
204+
my_ciphertext, encryptor_header = client.encrypt(
160205
source=my_plaintext,
161206
key_provider=kms_key_provider
162207
)
163208
164-
decrypted_plaintext, decryptor_header = aws_encryption_sdk.decrypt(
209+
decrypted_plaintext, decryptor_header = client.decrypt(
165210
source=my_ciphertext,
166211
key_provider=kms_key_provider
167212
)
@@ -174,14 +219,19 @@ You can provide an `encryption context`_: a form of additional authenticating in
174219
.. code:: python
175220
176221
import aws_encryption_sdk
222+
from aws_encryption_sdk.identifiers import CommitmentPolicy
223+
224+
client = aws_encryption_sdk.EncryptionSDKClient(
225+
commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
226+
)
177227
178-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[
228+
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
179229
'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222',
180230
'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333'
181231
])
182232
my_plaintext = b'This is some super secret data! Yup, sure is!'
183233
184-
my_ciphertext, encryptor_header = aws_encryption_sdk.encrypt(
234+
my_ciphertext, encryptor_header = client.encrypt(
185235
source=my_plaintext,
186236
key_provider=kms_key_provider,
187237
encryption_context={
@@ -190,7 +240,7 @@ You can provide an `encryption context`_: a form of additional authenticating in
190240
}
191241
)
192242
193-
decrypted_plaintext, decryptor_header = aws_encryption_sdk.decrypt(
243+
decrypted_plaintext, decryptor_header = client.decrypt(
194244
source=my_ciphertext,
195245
key_provider=kms_key_provider
196246
)
@@ -209,17 +259,22 @@ offering context manager and iteration support.
209259
.. code:: python
210260
211261
import aws_encryption_sdk
262+
from aws_encryption_sdk.identifiers import CommitmentPolicy
212263
import filecmp
213264
214-
kms_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[
265+
client = aws_encryption_sdk.EncryptionSDKClient(
266+
commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
267+
)
268+
269+
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[
215270
'arn:aws:kms:us-east-1:2222222222222:key/22222222-2222-2222-2222-222222222222',
216271
'arn:aws:kms:us-east-1:3333333333333:key/33333333-3333-3333-3333-333333333333'
217272
])
218273
plaintext_filename = 'my-secret-data.dat'
219274
ciphertext_filename = 'my-encrypted-data.ct'
220275
221276
with open(plaintext_filename, 'rb') as pt_file, open(ciphertext_filename, 'wb') as ct_file:
222-
with aws_encryption_sdk.stream(
277+
with client.stream(
223278
mode='e',
224279
source=pt_file,
225280
key_provider=kms_key_provider
@@ -230,7 +285,7 @@ offering context manager and iteration support.
230285
new_plaintext_filename = 'my-decrypted-data.dat'
231286
232287
with open(ciphertext_filename, 'rb') as ct_file, open(new_plaintext_filename, 'wb') as pt_file:
233-
with aws_encryption_sdk.stream(
288+
with client.stream(
234289
mode='d',
235290
source=ct_file,
236291
key_provider=kms_key_provider

Diff for: decrypt_oracle/test/integration/integration_test_utils.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ def kms_master_key_provider(cache: Optional[bool] = True):
7777
return _KMS_MKP
7878

7979
cmk_arn = get_cmk_arn()
80-
_kms_master_key_provider = KMSMasterKeyProvider()
81-
_kms_master_key_provider.add_master_key(cmk_arn)
80+
_kms_master_key_provider = KMSMasterKeyProvider(key_ids=[cmk_arn])
8281

8382
if cache:
8483
_KMS_MKP = _kms_master_key_provider

Diff for: examples/src/basic_encryption.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# language governing permissions and limitations under the License.
1313
"""Example showing basic encryption and decryption of a value already in memory."""
1414
import aws_encryption_sdk
15+
from aws_encryption_sdk import CommitmentPolicy
1516

1617

1718
def cycle_string(key_arn, source_plaintext, botocore_session=None):
@@ -22,17 +23,20 @@ def cycle_string(key_arn, source_plaintext, botocore_session=None):
2223
:param botocore_session: existing botocore session instance
2324
:type botocore_session: botocore.session.Session
2425
"""
26+
# Set up an encryption client with an explicit commitment policy
27+
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)
28+
2529
# Create a KMS master key provider
2630
kms_kwargs = dict(key_ids=[key_arn])
2731
if botocore_session is not None:
2832
kms_kwargs["botocore_session"] = botocore_session
29-
master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs)
33+
master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs)
3034

3135
# Encrypt the plaintext source data
32-
ciphertext, encryptor_header = aws_encryption_sdk.encrypt(source=source_plaintext, key_provider=master_key_provider)
36+
ciphertext, encryptor_header = client.encrypt(source=source_plaintext, key_provider=master_key_provider)
3337

3438
# Decrypt the ciphertext
35-
cycled_plaintext, decrypted_header = aws_encryption_sdk.decrypt(source=ciphertext, key_provider=master_key_provider)
39+
cycled_plaintext, decrypted_header = client.decrypt(source=ciphertext, key_provider=master_key_provider)
3640

3741
# Verify that the "cycled" (encrypted, then decrypted) plaintext is identical to the source plaintext
3842
assert cycled_plaintext == source_plaintext

Diff for: examples/src/basic_file_encryption_with_multiple_providers.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from cryptography.hazmat.primitives.asymmetric import rsa
2323

2424
import aws_encryption_sdk
25-
from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm
25+
from aws_encryption_sdk.identifiers import CommitmentPolicy, EncryptionKeyType, WrappingAlgorithm
2626
from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey
2727
from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider
2828

@@ -76,11 +76,14 @@ def cycle_file(key_arn, source_plaintext_filename, botocore_session=None):
7676
cycled_kms_plaintext_filename = source_plaintext_filename + ".kms.decrypted"
7777
cycled_static_plaintext_filename = source_plaintext_filename + ".static.decrypted"
7878

79+
# Set up an encryption client with an explicit commitment policy
80+
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)
81+
7982
# Create a KMS master key provider
8083
kms_kwargs = dict(key_ids=[key_arn])
8184
if botocore_session is not None:
8285
kms_kwargs["botocore_session"] = botocore_session
83-
kms_master_key_provider = aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs)
86+
kms_master_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs)
8487

8588
# Create a static master key provider and add a master key to it
8689
static_key_id = os.urandom(8)
@@ -94,23 +97,21 @@ def cycle_file(key_arn, source_plaintext_filename, botocore_session=None):
9497

9598
# Encrypt plaintext with both AWS KMS and static master keys
9699
with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext:
97-
with aws_encryption_sdk.stream(source=plaintext, mode="e", key_provider=kms_master_key_provider) as encryptor:
100+
with client.stream(source=plaintext, mode="e", key_provider=kms_master_key_provider) as encryptor:
98101
for chunk in encryptor:
99102
ciphertext.write(chunk)
100103

101104
# Decrypt the ciphertext with only the AWS KMS master key
102105
with open(ciphertext_filename, "rb") as ciphertext, open(cycled_kms_plaintext_filename, "wb") as plaintext:
103-
with aws_encryption_sdk.stream(
104-
source=ciphertext, mode="d", key_provider=aws_encryption_sdk.KMSMasterKeyProvider(**kms_kwargs)
106+
with client.stream(
107+
source=ciphertext, mode="d", key_provider=aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**kms_kwargs)
105108
) as kms_decryptor:
106109
for chunk in kms_decryptor:
107110
plaintext.write(chunk)
108111

109112
# Decrypt the ciphertext with only the static master key
110113
with open(ciphertext_filename, "rb") as ciphertext, open(cycled_static_plaintext_filename, "wb") as plaintext:
111-
with aws_encryption_sdk.stream(
112-
source=ciphertext, mode="d", key_provider=static_master_key_provider
113-
) as static_decryptor:
114+
with client.stream(source=ciphertext, mode="d", key_provider=static_master_key_provider) as static_decryptor:
114115
for chunk in static_decryptor:
115116
plaintext.write(chunk)
116117

Diff for: examples/src/basic_file_encryption_with_raw_key_provider.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import os
1616

1717
import aws_encryption_sdk
18-
from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm
18+
from aws_encryption_sdk.identifiers import CommitmentPolicy, EncryptionKeyType, WrappingAlgorithm
1919
from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey
2020
from aws_encryption_sdk.key_providers.raw import RawMasterKeyProvider
2121

@@ -53,6 +53,9 @@ def cycle_file(source_plaintext_filename):
5353
5454
:param str source_plaintext_filename: Filename of file to encrypt
5555
"""
56+
# Set up an encryption client with an explicit commitment policy
57+
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)
58+
5659
# Create a static random master key provider
5760
key_id = os.urandom(8)
5861
master_key_provider = StaticRandomMasterKeyProvider()
@@ -63,13 +66,13 @@ def cycle_file(source_plaintext_filename):
6366

6467
# Encrypt the plaintext source data
6568
with open(source_plaintext_filename, "rb") as plaintext, open(ciphertext_filename, "wb") as ciphertext:
66-
with aws_encryption_sdk.stream(mode="e", source=plaintext, key_provider=master_key_provider) as encryptor:
69+
with client.stream(mode="e", source=plaintext, key_provider=master_key_provider) as encryptor:
6770
for chunk in encryptor:
6871
ciphertext.write(chunk)
6972

7073
# Decrypt the ciphertext
7174
with open(ciphertext_filename, "rb") as ciphertext, open(cycled_plaintext_filename, "wb") as plaintext:
72-
with aws_encryption_sdk.stream(mode="d", source=ciphertext, key_provider=master_key_provider) as decryptor:
75+
with client.stream(mode="d", source=ciphertext, key_provider=master_key_provider) as decryptor:
7376
for chunk in decryptor:
7477
plaintext.write(chunk)
7578

Diff for: examples/src/data_key_caching_basic.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# language governing permissions and limitations under the License.
1313
"""Example of encryption with data key caching."""
1414
import aws_encryption_sdk
15+
from aws_encryption_sdk import CommitmentPolicy
1516

1617

1718
def encrypt_with_caching(kms_cmk_arn, max_age_in_cache, cache_capacity):
@@ -31,8 +32,11 @@ def encrypt_with_caching(kms_cmk_arn, max_age_in_cache, cache_capacity):
3132
# Create an encryption context
3233
encryption_context = {"purpose": "test"}
3334

35+
# Set up an encryption client with an explicit commitment policy
36+
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)
37+
3438
# Create a master key provider for the KMS customer master key (CMK)
35-
key_provider = aws_encryption_sdk.KMSMasterKeyProvider(key_ids=[kms_cmk_arn])
39+
key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[kms_cmk_arn])
3640

3741
# Create a local cache
3842
cache = aws_encryption_sdk.LocalCryptoMaterialsCache(cache_capacity)
@@ -48,7 +52,7 @@ def encrypt_with_caching(kms_cmk_arn, max_age_in_cache, cache_capacity):
4852
# When the call to encrypt data specifies a caching CMM,
4953
# the encryption operation uses the data key cache specified
5054
# in the caching CMM
51-
encrypted_message, _header = aws_encryption_sdk.encrypt(
55+
encrypted_message, _header = client.encrypt(
5256
source=my_data, materials_manager=caching_cmm, encryption_context=encryption_context
5357
)
5458

0 commit comments

Comments
 (0)