Skip to content

Commit 5efa515

Browse files
authored
Fix MasterKeyprovider.decrypt_data_key_from_list error handling (#152)
* MasterKeyprovider.decrypt_data_key_from_list should catch expected exceptions from both MasterKeyProvider.decrypt_data_key and MasterKey.decrypt_data_key #150 * add MKP.decrypt_data_key_from_list fix to changelog
1 parent d182155 commit 5efa515

File tree

4 files changed

+156
-1
lines changed

4 files changed

+156
-1
lines changed

CHANGELOG.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ Maintenance
1717
* Move all remaining ``unittest`` tests to ``pytest``.
1818
`#99 <https://github.com/aws/aws-encryption-sdk-python/issues/99>`_
1919

20+
21+
Bugfixes
22+
--------
23+
24+
* Fix ``MasterKeyprovider.decrypt_data_key_from_list`` error handling.
25+
`#150 <https://github.com/aws/aws-encryption-sdk-python/issues/150>`_
26+
27+
2028
1.3.8 -- 2018-11-15
2129
===================
2230

src/aws_encryption_sdk/key_providers/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,9 @@ def decrypt_data_key_from_list(self, encrypted_data_keys, algorithm, encryption_
278278
for encrypted_data_key in encrypted_data_keys:
279279
try:
280280
data_key = self.decrypt_data_key(encrypted_data_key, algorithm, encryption_context)
281-
except DecryptKeyError:
281+
# MasterKeyProvider.decrypt_data_key throws DecryptKeyError
282+
# but MasterKey.decrypt_data_key throws IncorrectMasterKeyError
283+
except (DecryptKeyError, IncorrectMasterKeyError):
282284
continue
283285
else:
284286
break
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""Functional tests for ``aws_encryption_sdk.key_providers.base``."""
14+
import itertools
15+
16+
import attr
17+
import pytest
18+
19+
from aws_encryption_sdk.exceptions import InvalidKeyIdError
20+
from aws_encryption_sdk.identifiers import AlgorithmSuite, EncryptionKeyType, WrappingAlgorithm
21+
from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig
22+
from aws_encryption_sdk.key_providers.raw import RawMasterKey, RawMasterKeyProvider, WrappingKey
23+
from aws_encryption_sdk.structures import EncryptedDataKey, MasterKeyInfo
24+
25+
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
26+
from typing import Iterable # noqa pylint: disable=unused-import
27+
except ImportError: # pragma: no cover
28+
# We only actually need these imports when running the mypy checks
29+
pass
30+
31+
pytestmark = [pytest.mark.functional, pytest.mark.local]
32+
33+
34+
_PLAINTEXT_DATA_KEY = b'*!\xa1"^-(\xf3\x105\x05i@B\xc2\xa2\xb7\xdd\xd5\xd5\xa9\xddm\xfae\xa8\\$\xf9d\x1e('
35+
_ENCRYPTION_CONTEXT = {"encryption": "context", "values": "here"}
36+
_PROVIDER_ID = "Random Raw Keys"
37+
_ENCRYPTED_DATA_KEYS = [
38+
{
39+
"wrapping_key": (
40+
b"\xeby-\x80A6\x15rA8\x83#,\xe4\xab\xac`\xaf\x99Z\xc1\xce\xdb\xb6\x0f\xb7\x805\xb2\x14J3"
41+
),
42+
"key_id": b"5325b043-5843-4629-869c-64794af77ada",
43+
"key_info": (
44+
b"5325b043-5843-4629-869c-64794af77ada\x00\x00\x00\x80\x00\x00\x00\x0c\xe0h\xe2NT\x1c\xb8\x8f!\t\xc2\x94"
45+
),
46+
"edk": (
47+
b"\xde^\x97\x7f\x84\xe9\x9e\x98\xd0\xe2\xf8\xd5\xcb\xe9\x7f.}\x87\x16,\x11n#\xc8p\xdb\xbf\x94\x86*Q\x06"
48+
b"\xd2\xf5\xdah\x08\xa4p\x81\xf7\xf4G\x07FzE\xde"
49+
),
50+
},
51+
{
52+
"wrapping_key": (
53+
b"Q\xfd\xaa[\"\xb3\x00\xc3E\xc0\xa7\xba_\xea\x92'vS$\x12\xa4h\x04\xd8\xdf\x80\xce\x16\x0ca\x9c\xc7"
54+
),
55+
"key_id": b"ead3f97e-49fe-48ce-be12-5c126c0d6adf",
56+
"key_info": (
57+
b"ead3f97e-49fe-48ce-be12-5c126c0d6adf\x00\x00\x00\x80\x00\x00\x00\x0c\xb6r9\x14Q\xd2\x0f\x02\x87\xcet\xec"
58+
),
59+
"edk": (
60+
b"\x86\xe2\x80\xc9\x7f\x93\x13\xdf\x8e\xcc\xde_\xa0\x88p\xa5\xd3\x1b\x1atqUW\x96\xfft\x85gB\xadjy\xedeQ\r"
61+
b"\xebL\x17\xf7\xd85\xea7_\xb3\xdb\x99"
62+
),
63+
},
64+
]
65+
_EDK_MAP = {_key["key_id"]: _key for _key in _ENCRYPTED_DATA_KEYS}
66+
67+
68+
class RawMultiMKP(RawMasterKeyProvider):
69+
@attr.s
70+
class _RawMultiMKPConfig(MasterKeyProviderConfig):
71+
valid_key_ids = attr.ib()
72+
73+
def __init__(self, *args, **kwargs):
74+
for key_id in self.config.valid_key_ids:
75+
self.add_master_key(key_id)
76+
77+
provider_id = _PROVIDER_ID
78+
_config_class = _RawMultiMKPConfig
79+
80+
def _get_raw_key(self, key_id):
81+
if key_id in self.config.valid_key_ids:
82+
return WrappingKey(
83+
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
84+
wrapping_key=_EDK_MAP[key_id]["wrapping_key"],
85+
wrapping_key_type=EncryptionKeyType.SYMMETRIC,
86+
)
87+
88+
raise InvalidKeyIdError("Unknown key id")
89+
90+
91+
def _keys_to_mkp(keys):
92+
if len(keys) > 1:
93+
return RawMultiMKP(valid_key_ids=[key["key_id"] for key in keys])
94+
95+
_key = keys[0]
96+
return RawMasterKey(
97+
provider_id=_PROVIDER_ID,
98+
key_id=_key["key_id"],
99+
wrapping_key=WrappingKey(
100+
wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING,
101+
wrapping_key=_key["wrapping_key"],
102+
wrapping_key_type=EncryptionKeyType.SYMMETRIC,
103+
),
104+
)
105+
106+
107+
def _edk_cobinations():
108+
_edks = [
109+
EncryptedDataKey(key_provider=MasterKeyInfo(_PROVIDER_ID, edk["key_info"]), encrypted_data_key=edk["edk"])
110+
for edk in _ENCRYPTED_DATA_KEYS
111+
]
112+
edks = itertools.permutations(_edks)
113+
114+
mkps = [
115+
_keys_to_mkp(_keys)
116+
for _keys in itertools.chain.from_iterable(
117+
[itertools.permutations(_ENCRYPTED_DATA_KEYS, i) for i in range(1, len(_ENCRYPTED_DATA_KEYS) + 1)]
118+
)
119+
]
120+
121+
for edk_group, mkp_group in itertools.product(edks, mkps):
122+
yield mkp_group, edk_group
123+
124+
125+
@pytest.mark.parametrize("mkp, edks", _edk_cobinations())
126+
def test_decrypt_data_keys(mkp, edks):
127+
# type: (RawMasterKey, Iterable[EncryptedDataKey]) -> None
128+
data_key = mkp.decrypt_data_key_from_list(
129+
encrypted_data_keys=edks,
130+
algorithm=AlgorithmSuite.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384,
131+
encryption_context=_ENCRYPTION_CONTEXT,
132+
)
133+
assert data_key.data_key == _PLAINTEXT_DATA_KEY

0 commit comments

Comments
 (0)