Skip to content

Commit a6433d4

Browse files
authored
feat: remove wrapping_algorithm input parameter from RawAESKeyring (aws#247)
* feat: remove wrapping_algorithm input parameter from RawAESKeyring * docs: make RawAESKeyring key material length error message deterministic and check for exact allowed options * docs: tweak wording explaining wrapping key generation
1 parent 8b7a609 commit a6433d4

File tree

8 files changed

+75
-91
lines changed

8 files changed

+75
-91
lines changed

examples/src/keyring/raw_aes/raw_aes.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import os
1111

1212
import aws_encryption_sdk
13-
from aws_encryption_sdk.identifiers import WrappingAlgorithm
1413
from aws_encryption_sdk.keyrings.raw import RawAESKeyring
1514

1615

@@ -30,14 +29,10 @@ def run(source_plaintext):
3029
"the data you are handling": "is what you think it is",
3130
}
3231

33-
# Choose the wrapping algorithm for the keyring to use.
34-
wrapping_algorithm = WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING
35-
36-
# Generate an AES key to use with your keyring.
37-
# The key size depends on the wrapping algorithm.
32+
# Generate a 256-bit (32 byte) AES key to use with your keyring.
3833
#
3934
# In practice, you should get this key from a secure key management system such as an HSM.
40-
key = os.urandom(wrapping_algorithm.algorithm.kdf_input_len)
35+
key = os.urandom(32)
4136

4237
# Create the keyring that determines how your data keys are protected.
4338
keyring = RawAESKeyring(
@@ -50,7 +45,6 @@ def run(source_plaintext):
5045
key_namespace="some managed raw keys",
5146
key_name=b"my AES wrapping key",
5247
wrapping_key=key,
53-
wrapping_algorithm=wrapping_algorithm,
5448
)
5549

5650
# Encrypt your plaintext data.

src/aws_encryption_sdk/keyrings/raw.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import attr
88
import six
9-
from attr.validators import instance_of, optional
9+
from attr.validators import in_, instance_of, optional
1010
from cryptography.hazmat.backends import default_backend
1111
from cryptography.hazmat.primitives import serialization
1212
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
@@ -76,7 +76,6 @@ class RawAESKeyring(Keyring):
7676
:param str key_namespace: String defining the keyring.
7777
:param bytes key_name: Key ID
7878
:param bytes wrapping_key: Encryption key with which to wrap plaintext data key.
79-
:param WrappingAlgorithm wrapping_algorithm: Wrapping Algorithm with which to wrap plaintext data key.
8079
8180
.. note::
8281
@@ -86,11 +85,28 @@ class RawAESKeyring(Keyring):
8685
key_namespace = attr.ib(validator=instance_of(six.string_types))
8786
key_name = attr.ib(validator=instance_of(six.binary_type))
8887
_wrapping_key = attr.ib(repr=False, validator=instance_of(six.binary_type))
89-
_wrapping_algorithm = attr.ib(repr=False, validator=instance_of(WrappingAlgorithm))
9088

9189
def __attrs_post_init__(self):
9290
# type: () -> None
9391
"""Prepares initial values not handled by attrs."""
92+
key_size_to_wrapping_algorithm = {
93+
wrapper.algorithm.kdf_input_len: wrapper
94+
for wrapper in (
95+
WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING,
96+
WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING,
97+
WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
98+
)
99+
}
100+
101+
try:
102+
self._wrapping_algorithm = key_size_to_wrapping_algorithm[len(self._wrapping_key)]
103+
except KeyError:
104+
raise ValueError(
105+
"Invalid wrapping key length. Must be one of {} bytes.".format(
106+
sorted(key_size_to_wrapping_algorithm.keys())
107+
)
108+
)
109+
94110
self._key_provider = MasterKeyInfo(provider_id=self.key_namespace, key_info=self.key_name)
95111

96112
self._wrapping_key_structure = WrappingKey(
@@ -244,7 +260,18 @@ class RawRSAKeyring(Keyring):
244260

245261
key_namespace = attr.ib(validator=instance_of(six.string_types))
246262
key_name = attr.ib(validator=instance_of(six.binary_type))
247-
_wrapping_algorithm = attr.ib(repr=False, validator=instance_of(WrappingAlgorithm))
263+
_wrapping_algorithm = attr.ib(
264+
repr=False,
265+
validator=in_(
266+
(
267+
WrappingAlgorithm.RSA_PKCS1,
268+
WrappingAlgorithm.RSA_OAEP_SHA1_MGF1,
269+
WrappingAlgorithm.RSA_OAEP_SHA256_MGF1,
270+
WrappingAlgorithm.RSA_OAEP_SHA384_MGF1,
271+
WrappingAlgorithm.RSA_OAEP_SHA512_MGF1,
272+
)
273+
),
274+
)
248275
_private_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(RSAPrivateKey)))
249276
_public_wrapping_key = attr.ib(default=None, repr=False, validator=optional(instance_of(RSAPublicKey)))
250277

test/functional/keyrings/raw/test_raw_aes.py

+6-27
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,14 @@ def sample_encryption_materials():
6565

6666

6767
@pytest.mark.parametrize("encryption_materials_samples", sample_encryption_materials())
68-
@pytest.mark.parametrize("wrapping_algorithm_samples", _WRAPPING_ALGORITHM)
69-
def test_raw_aes_encryption_decryption(encryption_materials_samples, wrapping_algorithm_samples):
68+
def test_raw_aes_encryption_decryption(encryption_materials_samples):
7069

7170
# Initializing attributes
7271
key_namespace = _PROVIDER_ID
7372
key_name = _KEY_ID
74-
_wrapping_algorithm = wrapping_algorithm_samples
7573

7674
# Creating an instance of a raw AES keyring
77-
test_raw_aes_keyring = RawAESKeyring(
78-
key_namespace=key_namespace,
79-
key_name=key_name,
80-
wrapping_key=_WRAPPING_KEY,
81-
wrapping_algorithm=_wrapping_algorithm,
82-
)
75+
test_raw_aes_keyring = RawAESKeyring(key_namespace=key_namespace, key_name=key_name, wrapping_key=_WRAPPING_KEY,)
8376

8477
# Call on_encrypt function for the keyring
8578
encryption_materials = test_raw_aes_keyring.on_encrypt(encryption_materials=encryption_materials_samples)
@@ -101,21 +94,14 @@ def test_raw_aes_encryption_decryption(encryption_materials_samples, wrapping_al
10194

10295

10396
@pytest.mark.parametrize("encryption_materials_samples", sample_encryption_materials())
104-
@pytest.mark.parametrize("wrapping_algorithm_samples", _WRAPPING_ALGORITHM)
105-
def test_raw_master_key_decrypts_what_raw_keyring_encrypts(encryption_materials_samples, wrapping_algorithm_samples):
97+
def test_raw_master_key_decrypts_what_raw_keyring_encrypts(encryption_materials_samples):
10698

10799
# Initializing attributes
108100
key_namespace = _PROVIDER_ID
109101
key_name = _KEY_ID
110-
_wrapping_algorithm = wrapping_algorithm_samples
111102

112103
# Creating an instance of a raw AES keyring
113-
test_raw_aes_keyring = RawAESKeyring(
114-
key_namespace=key_namespace,
115-
key_name=key_name,
116-
wrapping_key=_WRAPPING_KEY,
117-
wrapping_algorithm=_wrapping_algorithm,
118-
)
104+
test_raw_aes_keyring = RawAESKeyring(key_namespace=key_namespace, key_name=key_name, wrapping_key=_WRAPPING_KEY,)
119105

120106
# Creating an instance of a raw master key
121107
test_raw_master_key = RawMasterKey(
@@ -139,21 +125,14 @@ def test_raw_master_key_decrypts_what_raw_keyring_encrypts(encryption_materials_
139125

140126

141127
@pytest.mark.parametrize("encryption_materials_samples", sample_encryption_materials())
142-
@pytest.mark.parametrize("wrapping_algorithm_samples", _WRAPPING_ALGORITHM)
143-
def test_raw_keyring_decrypts_what_raw_master_key_encrypts(encryption_materials_samples, wrapping_algorithm_samples):
128+
def test_raw_keyring_decrypts_what_raw_master_key_encrypts(encryption_materials_samples):
144129

145130
# Initializing attributes
146131
key_namespace = _PROVIDER_ID
147132
key_name = _KEY_ID
148-
_wrapping_algorithm = wrapping_algorithm_samples
149133

150134
# Creating an instance of a raw AES keyring
151-
test_raw_aes_keyring = RawAESKeyring(
152-
key_namespace=key_namespace,
153-
key_name=key_name,
154-
wrapping_key=_WRAPPING_KEY,
155-
wrapping_algorithm=_wrapping_algorithm,
156-
)
135+
test_raw_aes_keyring = RawAESKeyring(key_namespace=key_namespace, key_name=key_name, wrapping_key=_WRAPPING_KEY,)
157136

158137
# Creating an instance of a raw master key
159138
test_raw_master_key = RawMasterKey(

test/functional/keyrings/test_multi.py

+2-12
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,7 @@
5050
)
5151

5252
_MULTI_KEYRING_WITH_GENERATOR_AND_CHILDREN = MultiKeyring(
53-
generator=RawAESKeyring(
54-
key_namespace=_PROVIDER_ID,
55-
key_name=_KEY_ID,
56-
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
57-
wrapping_key=_WRAPPING_KEY_AES,
58-
),
53+
generator=RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,),
5954
children=[
6055
RawRSAKeyring(
6156
key_namespace=_PROVIDER_ID,
@@ -95,12 +90,7 @@
9590
public_exponent=65537, key_size=2048, backend=default_backend()
9691
),
9792
),
98-
RawAESKeyring(
99-
key_namespace=_PROVIDER_ID,
100-
key_name=_KEY_ID,
101-
wrapping_algorithm=WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING,
102-
wrapping_key=_WRAPPING_KEY_AES,
103-
),
93+
RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,),
10494
]
10595
)
10696

test/unit/keyrings/raw/test_raw_aes.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,7 @@
4747

4848
@pytest.fixture
4949
def raw_aes_keyring():
50-
return RawAESKeyring(
51-
key_namespace=_PROVIDER_ID,
52-
key_name=_KEY_ID,
53-
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
54-
wrapping_key=_WRAPPING_KEY,
55-
)
50+
return RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY,)
5651

5752

5853
@pytest.fixture
@@ -113,12 +108,18 @@ def test_valid_parameters(raw_aes_keyring):
113108
def test_invalid_parameters(key_namespace, key_name, wrapping_algorithm, wrapping_key):
114109
with pytest.raises(TypeError):
115110
RawAESKeyring(
116-
key_namespace=key_namespace,
117-
key_name=key_name,
118-
wrapping_algorithm=wrapping_algorithm,
119-
wrapping_key=wrapping_key,
111+
key_namespace=key_namespace, key_name=key_name, wrapping_key=wrapping_key,
112+
)
113+
114+
115+
def test_invalid_key_length():
116+
with pytest.raises(ValueError) as excinfo:
117+
RawAESKeyring(
118+
key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=b"012345",
120119
)
121120

121+
excinfo.match(r"Invalid wrapping key length. Must be one of \[16, 24, 32\] bytes.")
122+
122123

123124
def test_on_encrypt_when_data_encryption_key_given(raw_aes_keyring, patch_generate_data_key):
124125
test_raw_aes_keyring = raw_aes_keyring

test/unit/keyrings/raw/test_raw_rsa.py

+18
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ def test_invalid_parameters(key_namespace, key_name, wrapping_algorithm, private
108108
)
109109

110110

111+
@pytest.mark.parametrize(
112+
"wrapping_algorithm",
113+
(
114+
WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING,
115+
WrappingAlgorithm.AES_192_GCM_IV12_TAG16_NO_PADDING,
116+
WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
117+
),
118+
)
119+
def test_invalid_wrapping_algorithm_suite(wrapping_algorithm):
120+
with pytest.raises(ValueError):
121+
RawRSAKeyring(
122+
key_namespace=_PROVIDER_ID,
123+
key_name=_KEY_ID,
124+
wrapping_algorithm=wrapping_algorithm,
125+
private_wrapping_key=raw_rsa_private_key(),
126+
)
127+
128+
111129
def test_public_and_private_key_not_provided():
112130
with pytest.raises(TypeError) as exc_info:
113131
RawRSAKeyring(

test/unit/keyrings/test_multi.py

+2-14
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,14 @@ def test_parent():
9696

9797

9898
def test_keyring_with_generator_but_no_children():
99-
generator_keyring = RawAESKeyring(
100-
key_namespace=_PROVIDER_ID,
101-
key_name=_KEY_ID,
102-
wrapping_key=_WRAPPING_KEY_AES,
103-
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
104-
)
99+
generator_keyring = RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,)
105100
test_multi_keyring = MultiKeyring(generator=generator_keyring)
106101
assert test_multi_keyring.generator is generator_keyring
107102
assert not test_multi_keyring.children
108103

109104

110105
def test_keyring_with_children_but_no_generator():
111-
children_keyring = [
112-
RawAESKeyring(
113-
key_namespace=_PROVIDER_ID,
114-
key_name=_KEY_ID,
115-
wrapping_key=_WRAPPING_KEY_AES,
116-
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
117-
)
118-
]
106+
children_keyring = [RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,)]
119107
test_multi_keyring = MultiKeyring(children=children_keyring)
120108
assert test_multi_keyring.children is children_keyring
121109
assert test_multi_keyring.generator is None

test/unit/unit_test_utils.py

+3-16
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,7 @@ def get_decryption_materials_without_data_key():
256256

257257
def get_multi_keyring_with_generator_and_children():
258258
return MultiKeyring(
259-
generator=RawAESKeyring(
260-
key_namespace=_PROVIDER_ID,
261-
key_name=_KEY_ID,
262-
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
263-
wrapping_key=_WRAPPING_KEY_AES,
264-
),
259+
generator=RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,),
265260
children=[
266261
RawRSAKeyring(
267262
key_namespace=_PROVIDER_ID,
@@ -307,12 +302,7 @@ def get_multi_keyring_with_no_generator():
307302
public_exponent=65537, key_size=2048, backend=default_backend()
308303
),
309304
),
310-
RawAESKeyring(
311-
key_namespace=_PROVIDER_ID,
312-
key_name=_KEY_ID,
313-
wrapping_algorithm=WrappingAlgorithm.AES_128_GCM_IV12_TAG16_NO_PADDING,
314-
wrapping_key=_WRAPPING_KEY_AES,
315-
),
305+
RawAESKeyring(key_namespace=_PROVIDER_ID, key_name=_KEY_ID, wrapping_key=_WRAPPING_KEY_AES,),
316306
]
317307
)
318308

@@ -482,10 +472,7 @@ def ephemeral_raw_aes_keyring(wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_I
482472
if key is None:
483473
key = os.urandom(key_length)
484474
return RawAESKeyring(
485-
key_namespace="fake",
486-
key_name="aes-{}".format(key_length * 8).encode("utf-8"),
487-
wrapping_algorithm=wrapping_algorithm,
488-
wrapping_key=key,
475+
key_namespace="fake", key_name="aes-{}".format(key_length * 8).encode("utf-8"), wrapping_key=key,
489476
)
490477

491478

0 commit comments

Comments
 (0)