Skip to content

Commit 144fd6b

Browse files
committedMar 7, 2018
core identifiers, structures, and helpers
1 parent 1d3c1bc commit 144fd6b

File tree

11 files changed

+673
-0
lines changed

11 files changed

+673
-0
lines changed
 
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2018 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+
""""""
14+
from dynamodb_encryption_sdk.encrypted.item import (
15+
decrypt_dynamodb_item, decrypt_python_item,
16+
encrypt_dynamodb_item, encrypt_python_item
17+
)
18+
19+
# encrypt_item
20+
# encrypt_raw_item
21+
# decrypt_item
22+
# decrypt_raw_item
23+
# EncryptedTable
24+
# EncryptedResource
25+
# EncryptedClient
26+
27+
# TableConfiguration
28+
# MaterialDescription
29+
# ItemConfiguration
30+
31+
__all__ = (
32+
'decrypt_dynamodb_item', 'decrypt_python_item',
33+
'encrypt_dynamodb_item', 'encrypt_python_item'
34+
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright 2018 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+
14+
15+
class DynamodbEncryptionSdkError(Exception):
16+
"""Base class for all custom exceptions."""
17+
18+
19+
class SerializationError(DynamodbEncryptionSdkError):
20+
"""Otherwise undifferentiated errors encountered while serializing data."""
21+
22+
23+
class DeserializationError(DynamodbEncryptionSdkError):
24+
"""Otherwise undifferentiated errors encountered while deserializing data."""
25+
26+
27+
class InvalidMaterialsetError(DeserializationError):
28+
"""Raised when errors are encountered processing a material description."""
29+
# TODO: MaterialDescription, not Materialset...
30+
31+
32+
class InvalidMaterialsetVersionError(DeserializationError):
33+
"""Raised when a material description is encountered with an invalid version."""
34+
# TODO: MaterialDescription, not Materialset...
35+
36+
37+
class InvalidAlgorithmError(DynamodbEncryptionSdkError):
38+
"""Raised when an invalid algorithm identifier is encountered."""
39+
40+
41+
class JceTransformationError(DynamodbEncryptionSdkError):
42+
""""""
43+
44+
45+
class DelegatedKeyError(DynamodbEncryptionSdkError):
46+
""""""
47+
48+
49+
class DelegatedKeyEncryptionError(DelegatedKeyError):
50+
""""""
51+
52+
53+
class DelegatedKeyDecryptionError(DelegatedKeyError):
54+
""""""
55+
56+
57+
class AwsKmsMaterialsProviderError(DynamodbEncryptionSdkError):
58+
""""""
59+
60+
61+
class UnknownRegionError(AwsKmsMaterialsProviderError):
62+
""""""
63+
64+
65+
class DecryptionError(DynamodbEncryptionSdkError):
66+
""""""
67+
68+
69+
class UnwrappingError(DynamodbEncryptionSdkError):
70+
""""""
71+
72+
73+
class EncryptionError(DynamodbEncryptionSdkError):
74+
""""""
75+
76+
77+
class WrappingError(DynamodbEncryptionSdkError):
78+
""""""
79+
80+
81+
class SigningError(DynamodbEncryptionSdkError):
82+
""""""
83+
84+
85+
class SignatureVerificationError(DynamodbEncryptionSdkError):
86+
""""""
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2018 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+
from enum import Enum
14+
15+
__version__ = '0.0.0'
16+
17+
LOGGER_NAME = 'dynamodb_encryption_sdk'
18+
19+
20+
class ItemAction(Enum):
21+
"""Possible actions to take on an item attribute."""
22+
DO_NOTHING = 0
23+
SIGN_ONLY = 1
24+
ENCRYPT_AND_SIGN = 2
25+
26+
def __lt__(self, other):
27+
return self.value < other.value
28+
29+
def __eq__(self, other):
30+
return self.value == other.value
31+
32+
33+
class EncryptionKeyTypes(Enum):
34+
"""Supported types of encryption keys."""
35+
SYMMETRIC = 0
36+
PRIVATE = 1
37+
PUBLIC = 2
38+
39+
40+
class KeyEncodingType(Enum):
41+
"""Supported key encoding schemes."""
42+
RAW = 0
43+
DER = 1
44+
PEM = 2
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2018 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+
"""Internal implementation details.
14+
15+
.. warning::
16+
No guarantee is provided on the modules and APIs within this
17+
namespace staying consistent. Directly reference at your own risk.
18+
"""
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2018 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+
""""""
14+
15+
ENCODING = 'utf-8'
16+
LOGGING_NAME = 'dynamodb_encryption_sdk'
17+
MATERIAL_DESCRIPTION_VERSION = b'\00' * 4
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright 2018 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+
""""""
14+
from enum import Enum
15+
16+
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
17+
from typing import Any, ByteString, Dict, List, Text, Union # pylint: disable=unused-import
18+
except ImportError: # pragma: no cover
19+
# We only actually need these imports when running the mypy checks
20+
pass
21+
22+
23+
class ReservedAttributes(Enum):
24+
"""Item attributes reserved for use by DynamoDBEncryptionClient"""
25+
MATERIAL_DESCRIPTION = '*amzn-ddb-map-desc*'
26+
SIGNATURE = '*amzn-ddb-map-sig*'
27+
28+
29+
class Tag(Enum):
30+
"""Attribute data type identifiers used for serialization and deserialization of attributes."""
31+
32+
BINARY = (b'b', 'B')
33+
BINARY_SET = (b'B', 'BS', b'b')
34+
NUMBER = (b'n', 'N')
35+
NUMBER_SET = (b'N', 'NS', b'n')
36+
STRING = (b's', 'S')
37+
STRING_SET = (b'S', 'SS', b's')
38+
BOOLEAN = (b'?', 'BOOL')
39+
NULL = (b'\x00', 'NULL')
40+
LIST = (b'L', 'L')
41+
MAP = (b'M', 'M')
42+
43+
def __init__(self, tag, dynamodb_tag, element_tag=None):
44+
# type: (bytes, Text, Optional[bytes]) -> None
45+
"""Sets up new Tag object.
46+
47+
:param bytes tag: DynamoDB Encryption SDK tag
48+
:param bytes dynamodb_tag: DynamoDB tag
49+
:param bytes element_tag: The type of tag contained within attributes of this type
50+
"""
51+
self.tag = tag
52+
self.dynamodb_tag = dynamodb_tag
53+
self.element_tag = element_tag
54+
55+
56+
class TagValues(Enum):
57+
"""Static values to use when serializing attribute values."""
58+
FALSE = b'\x00'
59+
TRUE = b'\x01'
60+
61+
62+
class SignatureValues(Enum):
63+
"""Values used when building the string to sign.
64+
65+
.. note::
66+
67+
The only time we actually use these values, we use the SHA256 hash of the value, so
68+
we pre-compute these hashes here.
69+
"""
70+
ENCRYPTED = (
71+
b'ENCRYPTED',
72+
b"9A\x15\xacN\xb0\x9a\xa4\x94)4\x88\x16\xb2\x03\x81'\xb0\xf9\xe3\xa5 7*\xe1\x00\xca\x19\xfb\x08\xfdP"
73+
)
74+
PLAINTEXT = (
75+
b'PLAINTEXT',
76+
b'\xcb@\xe7\xda\xdc\x86\x16\x1b\x97\x98\xdeHQ/3-!\xc1A\xfc\xc1\xe2\x8a\x08o\xdeJ3u\xaa\xb1\xb5'
77+
)
78+
79+
def __init__(self, raw, sha256):
80+
# type: (bytes, bytes) -> None
81+
"""Set up a new SignatureValues object.
82+
83+
:param bytes raw: Raw value
84+
:param bytes sha256: SHA256 hash of raw value
85+
"""
86+
self.raw = raw
87+
self.sha256 = sha256
88+
89+
90+
class MaterialDescriptionKeys(Enum):
91+
"""Static keys for use when building and reading material descriptions."""
92+
ATTRIBUTE_ENCRYPTION_MODE = 'amzn-ddb-map-sym-mode'
93+
SIGNING_KEY_ALGORITHM = 'amzn-ddb-map-signingAlg'
94+
WRAPPED_DATA_KEY = 'amzn-ddb-env-key'
95+
CONTENT_ENCRYPTION_ALGORITHM = 'amzn-ddb-env-alg'
96+
CONTENT_KEY_WRAPPING_ALGORITHM = 'amzn-ddb-wrap-alg'
97+
ITEM_SIGNATURE_ALGORITHM = 'amzn-ddb-sig-alg'
98+
99+
100+
class MaterialDescriptionValues(Enum):
101+
"""Static default values for use when building material descriptions."""
102+
CBC_PKCS5_ATTRIBUTE_ENCRYPTION = '/CBC/PKCS5Padding'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2018 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+
"""Helper functions for consistently obtaining str and bytes objects in both Python2 and Python3."""
14+
import codecs
15+
16+
import six
17+
18+
19+
def to_str(data):
20+
"""Takes an input str or bytes object and returns an equivalent str object.
21+
22+
:param data: Input data
23+
:type data: str or bytes
24+
:returns: Data normalized to str
25+
:rtype: str
26+
"""
27+
if isinstance(data, bytes):
28+
return codecs.decode(data, 'utf-8')
29+
return data
30+
31+
32+
def to_bytes(data):
33+
"""Takes an input str or bytes object and returns an equivalent bytes object.
34+
35+
:param data: Input data
36+
:type data: str or bytes
37+
:returns: Data normalized to bytes
38+
:rtype: bytes
39+
"""
40+
if isinstance(data, six.string_types) and not isinstance(data, bytes):
41+
return codecs.encode(data, 'utf-8')
42+
return data
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Copyright 2018 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+
""""""
14+
import attr
15+
import copy
16+
17+
import six
18+
19+
from .identifiers import ItemAction
20+
21+
22+
@attr.s(hash=False)
23+
class EncryptionContext(object):
24+
"""Additional information about an encryption request.
25+
26+
:param str table_name: Table name
27+
:param str partition_key_name: Name of primary index partition attribute
28+
:param str sort_key_name: Name of primary index sort attribute
29+
:param dict attributes: Plaintext item attributes
30+
:param dict material_description: Material description to use with this request
31+
"""
32+
table_name = attr.ib(
33+
validator=attr.validators.optional(attr.validators.instance_of(six.string_types)),
34+
default=None
35+
)
36+
partition_key_name = attr.ib(
37+
validator=attr.validators.optional(attr.validators.instance_of(six.string_types)),
38+
default=None
39+
)
40+
sort_key_name = attr.ib(
41+
validator=attr.validators.optional(attr.validators.instance_of(six.string_types)),
42+
default=None
43+
)
44+
# TODO: converter to make sure that attributes are in DDB form
45+
attributes = attr.ib(
46+
validator=attr.validators.optional(attr.validators.instance_of(dict)),
47+
default=attr.Factory(dict)
48+
)
49+
material_description = attr.ib(
50+
validator=attr.validators.instance_of(dict),
51+
converter=copy.deepcopy,
52+
default=attr.Factory(dict)
53+
)
54+
55+
56+
@attr.s(hash=False)
57+
class AttributeActions(object):
58+
"""Configuration resource used to determine what action should be taken for a specific attribute.
59+
60+
:param default_action: Action to take if no specific action is defined in ``attribute_actions``
61+
:type default_action: dynamodb_encryption_sdk.identifiers.ItemAction
62+
:param dict attribute_actions: Dictionary mapping attribute names to specific actions
63+
"""
64+
default_action = attr.ib(
65+
validator=attr.validators.instance_of(ItemAction),
66+
default=ItemAction.ENCRYPT_AND_SIGN
67+
)
68+
attribute_actions = attr.ib(
69+
validator=attr.validators.instance_of(dict),
70+
default=attr.Factory(dict)
71+
)
72+
73+
def action(self, attribute_name):
74+
# (text) -> ItemAction
75+
"""Determines the correct ItemAction to apply to a supplied attribute based on this config."""
76+
return self.attribute_actions.get(attribute_name, self.default_action)
77+
78+
def copy(self):
79+
# () -> AttributeActions
80+
"""Returns a new copy of this object."""
81+
return AttributeActions(
82+
default_action=self.default_action,
83+
attribute_actions=self.attribute_actions.copy()
84+
)
85+
86+
def set_index_keys(self, *keys):
87+
"""Sets the appropriate action for the specified indexed attribute names.
88+
89+
DO_NOTHING -> DO_NOTHING
90+
SIGN_ONLY -> SIGN_ONLY
91+
ENCRYPT_AND_SIGN -> SIGN_ONLY
92+
"""
93+
for key in keys:
94+
current_action = self.action(key)
95+
self.attribute_actions[key] = min(current_action, ItemAction.SIGN_ONLY)
96+
97+
def __add__(self, other):
98+
# (AttributeActions) -> AttributeActions
99+
"""Merges two AttributeActions objects into a new instance, applying the dominant
100+
action in each discovered case.
101+
"""
102+
default_action = self.default_action + other.default_action
103+
all_attributes = set(self.attribute_actions.keys()).union(set(other.attribute_actions.keys()))
104+
attribute_actions = {}
105+
for attribute in all_attributes:
106+
attribute_actions[attribute] = max(self.action(attribute), other.action(attribute))
107+
return AttributeActions(
108+
default_action=default_action,
109+
attribute_actions=attribute_actions
110+
)
111+
112+
113+
@attr.s(hash=False)
114+
class TableIndex(object):
115+
"""Describes a table index.
116+
117+
:param str partition: Name of the partition attribute
118+
:param str sort: Name of the sort attribute (optional)
119+
"""
120+
partition = attr.ib(validator=attr.validators.instance_of(six.string_types))
121+
sort = attr.ib(
122+
default=None,
123+
validator=attr.validators.optional(attr.validators.instance_of(six.string_types))
124+
)
125+
126+
def __attrs_post_init__(self):
127+
"""Set the ``attributes`` attribute for ease of access later."""
128+
self.attributes = set([self.partition])
129+
if self.sort is None:
130+
self.attributes.add(self.sort)
131+
132+
133+
@attr.s(hash=False)
134+
class TableInfo(object):
135+
"""Description of a DynamoDB table.
136+
137+
:param str name: Table name
138+
:param bool all_encrypting_secondary_indexes: Should we allow secondary index attributes to be encrypted?
139+
:param primary_index: Description of primary index
140+
:type primary_index: dynamodb_encryption_sdk.structures.TableIndex
141+
:param indexed_attributes: Listing of all indexes attribute names
142+
:type indexed_attributes: set of str
143+
"""
144+
name = attr.ib(validator=attr.validators.instance_of(six.string_types))
145+
allow_encrypting_secondary_indexes = attr.ib(
146+
validator=attr.validators.instance_of(bool),
147+
default=False
148+
)
149+
_primary_index = attr.ib(
150+
validator=attr.validators.optional(attr.validators.instance_of(TableIndex)),
151+
default=None
152+
)
153+
_indexed_attributes = attr.ib(
154+
validator=attr.validators.optional(attr.validators.instance_of(set)),
155+
default=None
156+
)
157+
158+
@property
159+
def primary_index(self):
160+
# type: () -> TableIndex
161+
""""""
162+
if self._primary_index is None:
163+
raise Exception('TODO:Indexes unknown. Run refresh_indexed_attributes')
164+
return self._primary_index
165+
166+
@property
167+
def indexed_attributes(self):
168+
# type: () -> TableIndex
169+
# TODO: Think about merging this and all_index_keys
170+
""""""
171+
if self._indexed_attributes is None:
172+
raise Exception('TODO:Indexes unknown. Run refresh_indexed_attributes')
173+
return self._indexed_attributes
174+
175+
def all_index_keys(self):
176+
# type: () -> Set[str]
177+
"""Provide a set containing the names of all indexed attributes that must not be encrypted."""
178+
if self._primary_index is None:
179+
return set()
180+
181+
if self.allow_encrypting_secondary_indexes:
182+
return self.primary_index.attributes
183+
184+
return self.indexed_attributes
185+
186+
def refresh_indexed_attributes(self, client):
187+
"""Use the provided boto3 DynamoDB client to determine all indexes for this table.
188+
189+
:param client: Pre-configured boto3 DynamoDB client
190+
:type client: TODO:
191+
"""
192+
table = client.describe_table(TableName=self.name)['Table']
193+
primary_index = {
194+
key['KeyType']: key['AttributeName']
195+
for key in table['KeySchema']
196+
}
197+
indexed_attributes = set(primary_index.values())
198+
self._primary_index = TableIndex(
199+
partition=primary_index['HASH'],
200+
sort=primary_index.get('RANGE', None)
201+
)
202+
for group in ('LocalSecondaryIndexes', 'GlobalSecondaryIndexes'):
203+
try:
204+
for index in table[group]:
205+
indexed_attributes.update(set([
206+
key['AttributeName'] for key in index['KeySchema']
207+
]))
208+
except KeyError:
209+
pass # Not all tables will have secondary indexes.
210+
self._indexed_attributes = indexed_attributes

‎test/functional/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2018 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.

‎test/functional/test_f_identifiers.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2018 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+
import operator
14+
15+
import pytest
16+
17+
from dynamodb_encryption_sdk.identifiers import ItemAction
18+
19+
pytestmark = [pytest.mark.functional, pytest.mark.local]
20+
21+
22+
@pytest.mark.parametrize('left, right, expected', (
23+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN),
24+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.SIGN_ONLY, ItemAction.ENCRYPT_AND_SIGN),
25+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.DO_NOTHING, ItemAction.ENCRYPT_AND_SIGN),
26+
(ItemAction.SIGN_ONLY, ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN),
27+
(ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY),
28+
(ItemAction.SIGN_ONLY, ItemAction.DO_NOTHING, ItemAction.SIGN_ONLY),
29+
(ItemAction.DO_NOTHING, ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN),
30+
(ItemAction.DO_NOTHING, ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY),
31+
(ItemAction.DO_NOTHING, ItemAction.DO_NOTHING, ItemAction.DO_NOTHING),
32+
))
33+
def test_item_action_max(left, right, expected):
34+
assert max(left, right) == expected
35+
36+
37+
@pytest.mark.parametrize('left, right, expected', (
38+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN),
39+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY),
40+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.DO_NOTHING, ItemAction.DO_NOTHING),
41+
(ItemAction.SIGN_ONLY, ItemAction.ENCRYPT_AND_SIGN, ItemAction.SIGN_ONLY),
42+
(ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY),
43+
(ItemAction.SIGN_ONLY, ItemAction.DO_NOTHING, ItemAction.DO_NOTHING),
44+
(ItemAction.DO_NOTHING, ItemAction.ENCRYPT_AND_SIGN, ItemAction.DO_NOTHING),
45+
(ItemAction.DO_NOTHING, ItemAction.SIGN_ONLY, ItemAction.DO_NOTHING),
46+
(ItemAction.DO_NOTHING, ItemAction.DO_NOTHING, ItemAction.DO_NOTHING),
47+
))
48+
def test_item_action_min(left, right, expected):
49+
assert min(left, right) == expected
50+
51+
52+
@pytest.mark.parametrize('left, right, expected_comparison', (
53+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.ENCRYPT_AND_SIGN, operator.eq),
54+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.SIGN_ONLY, operator.gt),
55+
(ItemAction.ENCRYPT_AND_SIGN, ItemAction.DO_NOTHING, operator.gt),
56+
(ItemAction.SIGN_ONLY, ItemAction.ENCRYPT_AND_SIGN, operator.lt),
57+
(ItemAction.SIGN_ONLY, ItemAction.SIGN_ONLY, operator.eq),
58+
(ItemAction.SIGN_ONLY, ItemAction.DO_NOTHING, operator.gt),
59+
(ItemAction.DO_NOTHING, ItemAction.ENCRYPT_AND_SIGN, operator.lt),
60+
(ItemAction.DO_NOTHING, ItemAction.SIGN_ONLY, operator.lt),
61+
(ItemAction.DO_NOTHING, ItemAction.DO_NOTHING, operator.eq)
62+
))
63+
def test_item_action_comp(left, right, expected_comparison):
64+
assert expected_comparison(left, right)

‎test/functional/test_f_str_ops.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"). You
5+
# may not use this file except in compliance with the License. A copy of
6+
# the License is located at
7+
#
8+
# http://aws.amazon.com/apache2.0/
9+
#
10+
# or in the "license" file accompanying this file. This file is
11+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12+
# ANY KIND, either express or implied. See the License for the specific
13+
# language governing permissions and limitations under the License.
14+
"""Test suite for dynamodb_encryption_sdk.internal.str_ops"""
15+
import codecs
16+
17+
import pytest
18+
19+
from dynamodb_encryption_sdk.internal.str_ops import to_bytes, to_str
20+
21+
pytestmark = [pytest.mark.functional, pytest.mark.local]
22+
23+
24+
@pytest.mark.parametrize('data, expected_output', (
25+
('asdf', 'asdf'),
26+
(b'asdf', 'asdf'),
27+
(codecs.encode(u'Предисловие', 'utf-8'), u'Предисловие'),
28+
(u'Предисловие', u'Предисловие')
29+
))
30+
def test_to_str(data, expected_output):
31+
test = to_str(data)
32+
assert test == expected_output
33+
34+
35+
@pytest.mark.parametrize('data, expected_output', (
36+
('asdf', b'asdf'),
37+
(b'asdf', b'asdf'),
38+
(b'\x3a\x00\x99', b'\x3a\x00\x99'),
39+
(u'Предисловие', codecs.encode(u'Предисловие', 'utf-8')),
40+
(codecs.encode(u'Предисловие', 'utf-8'), codecs.encode(u'Предисловие', 'utf-8'))
41+
))
42+
def test_to_bytes(data, expected_output):
43+
test = to_bytes(data)
44+
assert test == expected_output

0 commit comments

Comments
 (0)
Please sign in to comment.