diff --git a/kmip/core/factories/payloads/request.py b/kmip/core/factories/payloads/request.py index ee0a4f6..e405287 100644 --- a/kmip/core/factories/payloads/request.py +++ b/kmip/core/factories/payloads/request.py @@ -19,6 +19,7 @@ from kmip.core.messages.payloads import activate from kmip.core.messages.payloads import create from kmip.core.messages.payloads import create_key_pair from kmip.core.messages.payloads import decrypt +from kmip.core.messages.payloads import derive_key from kmip.core.messages.payloads import destroy from kmip.core.messages.payloads import discover_versions from kmip.core.messages.payloads import encrypt @@ -44,6 +45,9 @@ class RequestPayloadFactory(PayloadFactory): def _create_register_payload(self): return register.RegisterRequestPayload() + def _create_derive_key_payload(self): + return derive_key.DeriveKeyRequestPayload() + def _create_rekey_key_pair_payload(self): return rekey_key_pair.RekeyKeyPairRequestPayload() diff --git a/kmip/core/factories/payloads/response.py b/kmip/core/factories/payloads/response.py index 39c488a..8444176 100644 --- a/kmip/core/factories/payloads/response.py +++ b/kmip/core/factories/payloads/response.py @@ -20,6 +20,7 @@ from kmip.core.messages.payloads import create from kmip.core.messages.payloads import create_key_pair from kmip.core.messages.payloads import decrypt from kmip.core.messages.payloads import destroy +from kmip.core.messages.payloads import derive_key from kmip.core.messages.payloads import discover_versions from kmip.core.messages.payloads import encrypt from kmip.core.messages.payloads import get @@ -44,6 +45,9 @@ class ResponsePayloadFactory(PayloadFactory): def _create_register_payload(self): return register.RegisterResponsePayload() + def _create_derive_key_payload(self): + return derive_key.DeriveKeyResponsePayload() + def _create_rekey_key_pair_payload(self): return rekey_key_pair.RekeyKeyPairResponsePayload() diff --git a/kmip/core/messages/payloads/derive_key.py b/kmip/core/messages/payloads/derive_key.py new file mode 100644 index 0000000..b7fbbb0 --- /dev/null +++ b/kmip/core/messages/payloads/derive_key.py @@ -0,0 +1,519 @@ +# Copyright (c) 2017 The Johns Hopkins University/Applied Physics Laboratory +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import six + +from kmip.core import attributes +from kmip.core import enums +from kmip.core import objects +from kmip.core import primitives +from kmip.core import utils + + +class DeriveKeyRequestPayload(primitives.Struct): + """ + A request payload for the DeriveKey operation. + + Attributes: + object_type: The type of the object that should be derived. + unique_identifiers: A set of unique IDs of managed objects to be used + with the derivation process. + derivation_method: The method that should be used to derive the new + cryptographic object. + derivation_parameters: A collection of settings relevant for the + derivation method. + template_attribute: A collection of attributes that should be set on + the newly derived cryptographic object. + """ + + def __init__(self, + object_type=None, + unique_identifiers=None, + derivation_method=None, + derivation_parameters=None, + template_attribute=None): + """ + Construct a DeriveKey request payload struct. + + Args: + object_type (ObjectType): An enumeration specifying the type of + the object to derive. Optional, defaults to None. Required + for encoding and decoding. + unique_identifiers (list): A list of strings representing the IDs + of managed objects (e.g., symmetric keys) to be used for + derivation. Optional, defaults to None. At least one value is + required for encoding and decoding. + derivation_method (DerivationMethod): An enumeration specifying + the type of derivation function to use (e.g., PBKDF2). + Optional, defaults to None. Required for encoding and + decoding. + derivation_parameters (DerivationParameters): A structure + containing cryptographic settings relevant for the derivation + method. Optional, defaults to None. Required for encoding and + decoding. + template_attribute (TemplateAttribute): A structure containing a + set of attributes (e.g., cryptographic algorithm, + cryptographic length) that should be set on the newly derived + cryptographic object. Optional, defaults to None. Required + for encoding and decoding. + """ + super(DeriveKeyRequestPayload, self).__init__( + enums.Tags.REQUEST_PAYLOAD + ) + + self._object_type = None + self._unique_identifiers = None + self._derivation_method = None + self._derivation_parameters = None + self._template_attribute = None + + self.object_type = object_type + self.unique_identifiers = unique_identifiers + self.derivation_method = derivation_method + self.derivation_parameters = derivation_parameters + self.template_attribute = template_attribute + + @property + def object_type(self): + if self._object_type: + return self._object_type.value + else: + return None + + @object_type.setter + def object_type(self, value): + if value is None: + self._object_type = None + elif isinstance(value, enums.ObjectType): + self._object_type = primitives.Enumeration( + enums.ObjectType, + value=value, + tag=enums.Tags.OBJECT_TYPE + ) + else: + raise TypeError("object type must be an ObjectType enumeration") + + @property + def unique_identifiers(self): + if self._unique_identifiers: + unique_identifiers = [] + for i in self._unique_identifiers: + unique_identifiers.append(i.value) + return unique_identifiers + else: + return None + + @unique_identifiers.setter + def unique_identifiers(self, value): + if value is None: + self._unique_identifiers = None + elif isinstance(value, list): + unique_identifiers = [] + for i in value: + if isinstance(i, six.string_types): + unique_identifiers.append( + primitives.TextString( + value=i, + tag=enums.Tags.UNIQUE_IDENTIFIER + ) + ) + else: + raise TypeError( + "unique identifiers must be a list of strings" + ) + self._unique_identifiers = unique_identifiers + else: + raise TypeError("unique identifiers must be a list of strings") + + @property + def derivation_method(self): + if self._derivation_method: + return self._derivation_method.value + else: + return None + + @derivation_method.setter + def derivation_method(self, value): + if value is None: + self._derivation_method = None + elif isinstance(value, enums.DerivationMethod): + self._derivation_method = primitives.Enumeration( + enums.DerivationMethod, + value=value, + tag=enums.Tags.DERIVATION_METHOD + ) + else: + raise TypeError( + "derivation method must be a DerivationMethod enumeration" + ) + + @property + def derivation_parameters(self): + if self._derivation_parameters: + return self._derivation_parameters + else: + return None + + @derivation_parameters.setter + def derivation_parameters(self, value): + if value is None: + self._derivation_parameters = None + elif isinstance(value, attributes.DerivationParameters): + self._derivation_parameters = value + else: + raise TypeError( + "derivation parameters must be a DerivationParameters struct" + ) + + @property + def template_attribute(self): + if self._template_attribute: + return self._template_attribute + else: + return None + + @template_attribute.setter + def template_attribute(self, value): + if value is None: + self._template_attribute = None + elif isinstance(value, objects.TemplateAttribute): + self._template_attribute = value + else: + raise TypeError( + "template attribute must be a TemplateAttribute struct" + ) + + def read(self, input_stream): + """ + Read the data encoding the DeriveKey request payload and decode it + into its constituent parts. + + Args: + input_stream (stream): A data stream containing encoded object + data, supporting a read method; usually a BytearrayStream + object. + + Raises: + ValueError: Raised if the data attribute is missing from the + encoded payload. + """ + super(DeriveKeyRequestPayload, self).read(input_stream) + local_stream = utils.BytearrayStream(input_stream.read(self.length)) + + if self.is_tag_next(enums.Tags.OBJECT_TYPE, local_stream): + self._object_type = primitives.Enumeration( + enums.ObjectType, + tag=enums.Tags.OBJECT_TYPE + ) + self._object_type.read(local_stream) + else: + raise ValueError( + "invalid payload missing object type" + ) + + unique_identifiers = [] + while self.is_tag_next(enums.Tags.UNIQUE_IDENTIFIER, local_stream): + unique_identifier = primitives.TextString( + tag=enums.Tags.UNIQUE_IDENTIFIER + ) + unique_identifier.read(local_stream) + unique_identifiers.append(unique_identifier) + if not unique_identifiers: + raise ValueError("invalid payload missing unique identifiers") + else: + self._unique_identifiers = unique_identifiers + + if self.is_tag_next(enums.Tags.DERIVATION_METHOD, local_stream): + self._derivation_method = primitives.Enumeration( + enums.DerivationMethod, + tag=enums.Tags.DERIVATION_METHOD + ) + self._derivation_method.read(local_stream) + else: + raise ValueError( + "invalid payload missing derivation method" + ) + + if self.is_tag_next(enums.Tags.DERIVATION_PARAMETERS, local_stream): + self._derivation_parameters = attributes.DerivationParameters() + self._derivation_parameters.read(local_stream) + else: + raise ValueError( + "invalid payload missing derivation parameters" + ) + + if self.is_tag_next(enums.Tags.TEMPLATE_ATTRIBUTE, local_stream): + self._template_attribute = objects.TemplateAttribute() + self._template_attribute.read(local_stream) + else: + raise ValueError( + "invalid payload missing template attribute" + ) + + self.is_oversized(local_stream) + + def write(self, output_stream): + """ + Write the data encoding the DeriveKey request payload to a stream. + + Args: + output_stream (stream): A data stream in which to encode object + data, supporting a write method; usually a BytearrayStream + object. + + Raises: + ValueError: Raised if the data attribute is not defined. + """ + local_stream = utils.BytearrayStream() + + if self._object_type: + self._object_type.write(local_stream) + else: + raise ValueError("invalid payload missing object type") + + if self._unique_identifiers: + for unique_identifier in self._unique_identifiers: + unique_identifier.write(local_stream) + else: + raise ValueError("invalid payload missing unique identifiers") + + if self._derivation_method: + self._derivation_method.write(local_stream) + else: + raise ValueError("invalid payload missing derivation method") + + if self._derivation_parameters: + self._derivation_parameters.write(local_stream) + else: + raise ValueError("invalid payload missing derivation parameters") + + if self._template_attribute: + self._template_attribute.write(local_stream) + else: + raise ValueError("invalid payload missing template attributes") + + self.length = local_stream.length() + super(DeriveKeyRequestPayload, self).write(output_stream) + output_stream.write(local_stream.buffer) + + def __eq__(self, other): + if isinstance(other, DeriveKeyRequestPayload): + if self.object_type != other.object_type: + return False + elif self.unique_identifiers != other.unique_identifiers: + return False + elif self.derivation_method != other.derivation_method: + return False + elif self.derivation_parameters != other.derivation_parameters: + return False + elif self.template_attribute != other.template_attribute: + return False + else: + return True + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, DeriveKeyRequestPayload): + return not (self == other) + else: + return NotImplemented + + def __repr__(self): + args = ", ".join([ + "object_type={0}".format(self.object_type), + "unique_identifiers={0}".format(self.unique_identifiers), + "derivation_method={0}".format(self.derivation_method), + "derivation_parameters={0}".format( + repr(self.derivation_parameters) + ), + "template_attribute={0}".format(repr(self.template_attribute)) + ]) + return "DeriveKeyRequestPayload({0})".format(args) + + def __str__(self): + return str({ + 'object_type': self.object_type, + 'unique_identifiers': self.unique_identifiers, + 'derivation_method': self.derivation_method, + 'derivation_parameters': self.derivation_parameters, + 'template_attribute': self.template_attribute + }) + + +class DeriveKeyResponsePayload(primitives.Struct): + """ + A response payload for the DeriveKey operation. + + Attributes: + unique_identifier: The unique ID of the newly derived cryptographic + object. + template_attribute: A collection of attributes that were implicitly + set by the server on the newly derived cryptographic object. + """ + + def __init__(self, + unique_identifier=None, + template_attribute=None): + """ + Construct a DeriveKey response payload struct. + + Args: + unique_identifier (string): A string representing the ID of the + newly derived managed object. Optional, defaults to None. At + least one value is required for encoding and decoding. + template_attribute (TemplateAttribute): A structure containing a + set of attributes (e.g., cryptographic algorithm, + cryptographic length) implicitly set by the server on the + newly derived cryptographic object. Optional, defaults to + None. + """ + super(DeriveKeyResponsePayload, self).__init__( + enums.Tags.RESPONSE_PAYLOAD + ) + + self._unique_identifier = None + self._template_attribute = None + + self.unique_identifier = unique_identifier + self.template_attribute = template_attribute + + @property + def unique_identifier(self): + if self._unique_identifier: + return self._unique_identifier.value + else: + return None + + @unique_identifier.setter + def unique_identifier(self, value): + if value is None: + self._unique_identifier = None + elif isinstance(value, six.string_types): + self._unique_identifier = primitives.TextString( + value=value, + tag=enums.Tags.UNIQUE_IDENTIFIER + ) + else: + raise TypeError("unique identifier must be a string") + + @property + def template_attribute(self): + if self._template_attribute: + return self._template_attribute + else: + return None + + @template_attribute.setter + def template_attribute(self, value): + if value is None: + self._template_attribute = None + elif isinstance(value, objects.TemplateAttribute): + self._template_attribute = value + else: + raise TypeError( + "template attribute must be a TemplateAttribute struct" + ) + + def read(self, input_stream): + """ + Read the data encoding the DeriveKey response payload and decode it + into its constituent parts. + + Args: + input_stream (stream): A data stream containing encoded object + data, supporting a read method; usually a BytearrayStream + object. + + Raises: + ValueError: Raised if the data attribute is missing from the + encoded payload. + """ + super(DeriveKeyResponsePayload, self).read(input_stream) + local_stream = utils.BytearrayStream(input_stream.read(self.length)) + + if self.is_tag_next(enums.Tags.UNIQUE_IDENTIFIER, local_stream): + self._unique_identifier = primitives.TextString( + tag=enums.Tags.UNIQUE_IDENTIFIER + ) + self._unique_identifier.read(local_stream) + else: + raise ValueError( + "invalid payload missing unique identifier" + ) + + if self.is_tag_next(enums.Tags.TEMPLATE_ATTRIBUTE, local_stream): + self._template_attribute = objects.TemplateAttribute() + self._template_attribute.read(local_stream) + + self.is_oversized(local_stream) + + def write(self, output_stream): + """ + Write the data encoding the DeriveKey response payload to a stream. + + Args: + output_stream (stream): A data stream in which to encode object + data, supporting a write method; usually a BytearrayStream + object. + + Raises: + ValueError: Raised if the data attribute is not defined. + """ + local_stream = utils.BytearrayStream() + + if self._unique_identifier: + self._unique_identifier.write(local_stream) + else: + raise ValueError( + "invalid payload missing unique identifier" + ) + + if self._template_attribute: + self._template_attribute.write(local_stream) + + self.length = local_stream.length() + super(DeriveKeyResponsePayload, self).write(output_stream) + output_stream.write(local_stream.buffer) + + def __eq__(self, other): + if isinstance(other, DeriveKeyResponsePayload): + if self.unique_identifier != other.unique_identifier: + return False + elif self.template_attribute != other.template_attribute: + return False + else: + return True + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, DeriveKeyResponsePayload): + return not (self == other) + else: + return NotImplemented + + def __repr__(self): + args = ", ".join([ + "unique_identifier='{0}'".format(self.unique_identifier), + "template_attribute={0}".format(repr(self.template_attribute)) + ]) + return "DeriveKeyResponsePayload({0})".format(args) + + def __str__(self): + return str({ + 'unique_identifier': self.unique_identifier, + 'template_attribute': self.template_attribute + }) diff --git a/kmip/core/objects.py b/kmip/core/objects.py index 83613c4..2238260 100644 --- a/kmip/core/objects.py +++ b/kmip/core/objects.py @@ -947,6 +947,12 @@ class TemplateAttribute(Struct): else: return NotImplemented + def __ne__(self, other): + if isinstance(other, TemplateAttribute): + return not (self == other) + else: + return NotImplemented + class CommonTemplateAttribute(TemplateAttribute): diff --git a/kmip/tests/unit/core/factories/payloads/test_request.py b/kmip/tests/unit/core/factories/payloads/test_request.py index 92de41e..06896cd 100644 --- a/kmip/tests/unit/core/factories/payloads/test_request.py +++ b/kmip/tests/unit/core/factories/payloads/test_request.py @@ -23,6 +23,7 @@ from kmip.core.messages.payloads import create from kmip.core.messages.payloads import create_key_pair from kmip.core.messages.payloads import decrypt from kmip.core.messages.payloads import destroy +from kmip.core.messages.payloads import derive_key from kmip.core.messages.payloads import discover_versions from kmip.core.messages.payloads import encrypt from kmip.core.messages.payloads import get @@ -71,10 +72,8 @@ class TestRequestPayloadFactory(testtools.TestCase): self._test_not_implemented(self.factory.create, enums.Operation.REKEY) def test_create_derive_key_payload(self): - self._test_not_implemented( - self.factory.create, - enums.Operation.DERIVE_KEY - ) + payload = self.factory.create(enums.Operation.DERIVE_KEY) + self._test_payload_type(payload, derive_key.DeriveKeyRequestPayload) def test_create_certify_payload(self): self._test_not_implemented( diff --git a/kmip/tests/unit/core/factories/payloads/test_response.py b/kmip/tests/unit/core/factories/payloads/test_response.py index 4560234..b76b71c 100644 --- a/kmip/tests/unit/core/factories/payloads/test_response.py +++ b/kmip/tests/unit/core/factories/payloads/test_response.py @@ -23,6 +23,7 @@ from kmip.core.messages.payloads import create from kmip.core.messages.payloads import create_key_pair from kmip.core.messages.payloads import decrypt from kmip.core.messages.payloads import destroy +from kmip.core.messages.payloads import derive_key from kmip.core.messages.payloads import discover_versions from kmip.core.messages.payloads import encrypt from kmip.core.messages.payloads import get @@ -71,10 +72,8 @@ class TestResponsePayloadFactory(testtools.TestCase): self._test_not_implemented(self.factory.create, enums.Operation.REKEY) def test_create_derive_key_payload(self): - self._test_not_implemented( - self.factory.create, - enums.Operation.DERIVE_KEY - ) + payload = self.factory.create(enums.Operation.DERIVE_KEY) + self._test_payload_type(payload, derive_key.DeriveKeyResponsePayload) def test_create_certify_payload(self): self._test_not_implemented( diff --git a/kmip/tests/unit/core/messages/payloads/test_derive_key.py b/kmip/tests/unit/core/messages/payloads/test_derive_key.py new file mode 100644 index 0000000..389cb1d --- /dev/null +++ b/kmip/tests/unit/core/messages/payloads/test_derive_key.py @@ -0,0 +1,2244 @@ +# Copyright (c) 2017 The Johns Hopkins University/Applied Physics Laboratory +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import testtools + +from kmip.core import attributes +from kmip.core import enums +from kmip.core import objects +from kmip.core import primitives +from kmip.core import utils + +from kmip.core.messages.payloads import derive_key + + +class TestDeriveKeyRequestPayload(testtools.TestCase): + """ + Test suite for the DeriveKey request payload. + """ + + def setUp(self): + super(TestDeriveKeyRequestPayload, self).setUp() + + # Encoding obtained in part from the KMIP 1.1 testing document. The + # rest of the encoding is a manual construction, since DeriveKey is + # not specifically detailed by the testing document. + # + # This encoding matches the following set of values: + # Object Type - SymmetricKey + # Unique Identifiers + # fb4b5b9c-6188-4c63-8142-fe9c328129fc + # 5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3 + # 1703250b-4d40-4de2-93a0-c494a1d4ae40 + # Derivation Method - HMAC + # Derivation Parameters + # Cryptographic Parameters + # Hashing Algorithm - SHA-256 + # Initialization Vector - 0x39487432492834A3 + # Derivation Data - 0xFAD98B6ACA6D87DD + # Template Attribute + # Attribute + # Attribute Name - Cryptographic Algorithm + # Attribute Value - AES + # Attribute + # Attribute Name - Cryptographic Length + # Attribute Value - 128 + + self.full_encoding = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x01\x68' + b'\x42\x00\x57\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x35\x63\x39\x62\x38\x31\x65\x66' + b'\x2D\x34\x65\x65\x35\x2D\x34\x32\x63\x64\x2D\x62\x61\x32\x64\x2D' + b'\x63\x30\x30\x32\x66\x64\x64\x30\x63\x37\x62\x33\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x31\x37\x30\x33\x32\x35\x30\x62' + b'\x2D\x34\x64\x34\x30\x2D\x34\x64\x65\x32\x2D\x39\x33\x61\x30\x2D' + b'\x63\x34\x39\x34\x61\x31\x64\x34\x61\x65\x34\x30\x00\x00\x00\x00' + b'\x42\x00\x31\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x32\x01\x00\x00\x00\x38' + b'\x42\x00\x2B\x01\x00\x00\x00\x10' + b'\x42\x00\x38\x05\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00' + b'\x42\x00\x3A\x08\x00\x00\x00\x08\x39\x48\x74\x32\x49\x28\x34\xA3' + b'\x42\x00\x30\x08\x00\x00\x00\x08\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + b'\x42\x00\x91\x01\x00\x00\x00\x70' + b'\x42\x00\x08\x01\x00\x00\x00\x30' + b'\x42\x00\x0A\x07\x00\x00\x00\x17\x43\x72\x79\x70\x74\x6F\x67\x72' + b'\x61\x70\x68\x69\x63\x20\x41\x6C\x67\x6F\x72\x69\x74\x68\x6D\x00' + b'\x42\x00\x0B\x05\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00' + b'\x42\x00\x08\x01\x00\x00\x00\x30' + b'\x42\x00\x0A\x07\x00\x00\x00\x14\x43\x72\x79\x70\x74\x6F\x67\x72' + b'\x61\x70\x68\x69\x63\x20\x4C\x65\x6E\x67\x74\x68\x00\x00\x00\x00' + b'\x42\x00\x0B\x02\x00\x00\x00\x04\x00\x00\x00\x80\x00\x00\x00\x00' + ) + + # All of the following partial encodings are trimmed versions of the + # above full encoding. + + self.partial_encoding_no_object_type = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x00\x00' + ) + self.partial_encoding_no_unique_identifiers = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x00\x10' + b'\x42\x00\x57\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + ) + self.partial_encoding_no_derivation_method = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x00\xA0' + b'\x42\x00\x57\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x35\x63\x39\x62\x38\x31\x65\x66' + b'\x2D\x34\x65\x65\x35\x2D\x34\x32\x63\x64\x2D\x62\x61\x32\x64\x2D' + b'\x63\x30\x30\x32\x66\x64\x64\x30\x63\x37\x62\x33\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x31\x37\x30\x33\x32\x35\x30\x62' + b'\x2D\x34\x64\x34\x30\x2D\x34\x64\x65\x32\x2D\x39\x33\x61\x30\x2D' + b'\x63\x34\x39\x34\x61\x31\x64\x34\x61\x65\x34\x30\x00\x00\x00\x00' + ) + self.partial_encoding_no_derivation_parameters = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x00\xB0' + b'\x42\x00\x57\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x35\x63\x39\x62\x38\x31\x65\x66' + b'\x2D\x34\x65\x65\x35\x2D\x34\x32\x63\x64\x2D\x62\x61\x32\x64\x2D' + b'\x63\x30\x30\x32\x66\x64\x64\x30\x63\x37\x62\x33\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x31\x37\x30\x33\x32\x35\x30\x62' + b'\x2D\x34\x64\x34\x30\x2D\x34\x64\x65\x32\x2D\x39\x33\x61\x30\x2D' + b'\x63\x34\x39\x34\x61\x31\x64\x34\x61\x65\x34\x30\x00\x00\x00\x00' + b'\x42\x00\x31\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + ) + self.partial_encoding_no_template_attribute = utils.BytearrayStream( + b'\x42\x00\x79\x01\x00\x00\x00\xF0' + b'\x42\x00\x57\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x35\x63\x39\x62\x38\x31\x65\x66' + b'\x2D\x34\x65\x65\x35\x2D\x34\x32\x63\x64\x2D\x62\x61\x32\x64\x2D' + b'\x63\x30\x30\x32\x66\x64\x64\x30\x63\x37\x62\x33\x00\x00\x00\x00' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x31\x37\x30\x33\x32\x35\x30\x62' + b'\x2D\x34\x64\x34\x30\x2D\x34\x64\x65\x32\x2D\x39\x33\x61\x30\x2D' + b'\x63\x34\x39\x34\x61\x31\x64\x34\x61\x65\x34\x30\x00\x00\x00\x00' + b'\x42\x00\x31\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + b'\x42\x00\x32\x01\x00\x00\x00\x38' + b'\x42\x00\x2B\x01\x00\x00\x00\x10' + b'\x42\x00\x38\x05\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00' + b'\x42\x00\x3A\x08\x00\x00\x00\x08\x39\x48\x74\x32\x49\x28\x34\xA3' + b'\x42\x00\x30\x08\x00\x00\x00\x08\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + + def tearDown(self): + super(TestDeriveKeyRequestPayload, self).tearDown() + + def test_init(self): + """ + Test that a DeriveKey request payload can be constructed with no + arguments. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + def test_init_with_args(self): + """ + Test that a DeriveKey request payload can be constructed with valid + values + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=['00000000-1111-2222-3333-444444444444'], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters(), + template_attribute=objects.TemplateAttribute() + ) + + self.assertEqual( + enums.ObjectType.SYMMETRIC_KEY, + payload.object_type + ) + self.assertEqual( + ['00000000-1111-2222-3333-444444444444'], + payload.unique_identifiers + ) + self.assertEqual( + enums.DerivationMethod.HASH, + payload.derivation_method + ) + self.assertEqual( + attributes.DerivationParameters(), + payload.derivation_parameters + ) + self.assertEqual( + objects.TemplateAttribute(), + payload.template_attribute + ) + + def test_invalid_object_type(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the object type of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyRequestPayload() + args = (payload, 'object_type', 'invalid') + self.assertRaisesRegexp( + TypeError, + "object type must be an ObjectType enumeration", + setattr, + *args + ) + + def test_invalid_unique_identifiers(self): + """ + Test that a TypeError is raised when invalid values are used to set + the unique identifiers of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyRequestPayload() + args = (payload, 'unique_identifiers', 'invalid') + self.assertRaisesRegexp( + TypeError, + "unique identifiers must be a list of strings", + setattr, + *args + ) + + args = (payload, 'unique_identifiers', [0]) + self.assertRaisesRegexp( + TypeError, + "unique identifiers must be a list of strings", + setattr, + *args + ) + + args = (payload, 'unique_identifiers', ['valid', 'valid', 0]) + self.assertRaisesRegexp( + TypeError, + "unique identifiers must be a list of strings", + setattr, + *args + ) + + def test_invalid_derivation_method(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the derivation method of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyRequestPayload() + args = (payload, 'derivation_method', 'invalid') + self.assertRaisesRegexp( + TypeError, + "derivation method must be a DerivationMethod enumeration", + setattr, + *args + ) + + def test_invalid_derivation_parameters(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the derivation parameters of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyRequestPayload() + args = (payload, 'derivation_parameters', 'invalid') + self.assertRaisesRegexp( + TypeError, + "derivation parameters must be a DerivationParameters struct", + setattr, + *args + ) + + def test_invalid_template_attribute(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the template attribute of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyRequestPayload() + args = (payload, 'template_attribute', 'invalid') + self.assertRaisesRegexp( + TypeError, + "template attribute must be a TemplateAttribute struct", + setattr, + *args + ) + + def test_read(self): + """ + Test that a DeriveKey request payload can be read from a data stream. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + payload.read(self.full_encoding) + + self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, payload.object_type) + self.assertEqual( + [ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + payload.unique_identifiers + ) + self.assertEqual( + enums.DerivationMethod.HASH, + payload.derivation_method + ) + self.assertEqual( + attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + payload.derivation_parameters + ) + self.assertEqual( + objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ), + payload.template_attribute + ) + + def test_read_missing_object_type(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey request + payload encoding missing the object type. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_object_type, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing object type", + payload.read, + *args + ) + + def test_read_missing_unique_identifiers(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey request + payload encoding missing the unique identifiers. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_unique_identifiers, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing unique identifiers", + payload.read, + *args + ) + + def test_read_missing_derivation_method(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey request + payload encoding missing the derivation method. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_derivation_method, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing derivation method", + payload.read, + *args + ) + + def test_read_missing_derivation_parameters(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey request + payload encoding missing the derivation parameters. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_derivation_parameters, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing derivation parameters", + payload.read, + *args + ) + + def test_read_missing_template_attribute(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey request + payload encoding missing the template attribute. + """ + payload = derive_key.DeriveKeyRequestPayload() + + self.assertEqual(None, payload.object_type) + self.assertEqual(None, payload.unique_identifiers) + self.assertEqual(None, payload.derivation_method) + self.assertEqual(None, payload.derivation_parameters) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_template_attribute, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing template attribute", + payload.read, + *args + ) + + def test_write(self): + """ + Test that a DeriveKey request payload can be written to a data stream. + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + stream = utils.BytearrayStream() + payload.write(stream) + + self.assertEqual(len(self.full_encoding), len(stream)) + self.assertEqual(str(self.full_encoding), str(stream)) + + def test_write_missing_object_type(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey request + payload missing the object type. + """ + payload = derive_key.DeriveKeyRequestPayload() + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing object type", + payload.write, + *args + ) + + def test_write_missing_unique_identifiers(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey request + payload missing the unique identifiers. + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY + ) + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing unique identifiers", + payload.write, + *args + ) + + def test_write_missing_derivation_method(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey request + payload missing the derivation method. + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ] + ) + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing derivation method", + payload.write, + *args + ) + + def test_write_missing_derivation_parameters(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey request + payload missing the derivation parameters. + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH + ) + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing derivation parameters", + payload.write, + *args + ) + + def test_write_missing_template_attribute(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey request + payload missing the template attribute. + """ + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing template attribute", + payload.write, + *args + ) + + def test_equal_on_equal(self): + """ + Test that the equality operator returns True when comparing two + DeriveKey request payloads with the same data. + """ + a = derive_key.DeriveKeyRequestPayload() + b = derive_key.DeriveKeyRequestPayload() + + self.assertTrue(a == b) + self.assertTrue(b == a) + + a = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_not_equal_object_type(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different object types. + """ + a = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY + ) + b = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SECRET_DATA + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_unique_identifiers(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different sets of unique identifiers. + """ + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=['fb4b5b9c-6188-4c63-8142-fe9c328129fc'] + ) + b = derive_key.DeriveKeyRequestPayload( + unique_identifiers=['5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3'] + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ] + ) + b = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + '1703250b-4d40-4de2-93a0-c494a1d4ae40', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + ] + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ] + ) + b = derive_key.DeriveKeyRequestPayload(unique_identifiers=[]) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_derivation_method(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different derivation methods. + """ + a = derive_key.DeriveKeyRequestPayload( + derivation_method=enums.DerivationMethod.HASH + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_method=enums.DerivationMethod.PBKDF2 + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_derivation_parameters(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different derivation parameters. + """ + a = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_1 + ), + initialization_vector=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD', + derivation_data=b'\x39\x48\x74\x32\x49\x28\x34\xA3' + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters() + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload(derivation_parameters=None) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_template_attribute(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different template attributes. + """ + a = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.BLOWFISH, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=64, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute() + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyRequestPayload(template_attribute=None) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_type_mismatch(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey request payloads with different types. + """ + a = derive_key.DeriveKeyRequestPayload() + b = 'invalid' + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_not_equal_on_equal(self): + """ + Test that the inequality operator returns False when comparing two + DeriveKey request payloads with the same data. + """ + a = derive_key.DeriveKeyRequestPayload() + b = derive_key.DeriveKeyRequestPayload() + + self.assertFalse(a != b) + self.assertFalse(b != a) + + a = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ), + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_not_equal_object_type(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different object types. + """ + a = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY + ) + b = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SECRET_DATA + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_unique_identifiers(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different sets of unique identifiers. + """ + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=['fb4b5b9c-6188-4c63-8142-fe9c328129fc'] + ) + b = derive_key.DeriveKeyRequestPayload( + unique_identifiers=['5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3'] + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ] + ) + b = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + '1703250b-4d40-4de2-93a0-c494a1d4ae40', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + ] + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload( + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ] + ) + b = derive_key.DeriveKeyRequestPayload(unique_identifiers=[]) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_derivation_method(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different derivation methods. + """ + a = derive_key.DeriveKeyRequestPayload( + derivation_method=enums.DerivationMethod.HASH + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_method=enums.DerivationMethod.PBKDF2 + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_derivation_parameters(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different derivation parameters. + """ + a = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_1 + ), + initialization_vector=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD', + derivation_data=b'\x39\x48\x74\x32\x49\x28\x34\xA3' + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters() + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload(derivation_parameters=None) + b = derive_key.DeriveKeyRequestPayload( + derivation_parameters=attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_template_attribute(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different template attribute. + """ + a = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.BLOWFISH, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=64, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute() + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyRequestPayload(template_attribute=None) + b = derive_key.DeriveKeyRequestPayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_type_mismatch(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey request payloads with different types. + """ + a = derive_key.DeriveKeyRequestPayload() + b = 'invalid' + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_repr(self): + """ + Test that repr can be applied to a DeriveKey request payload. + """ + derivation_parameters = attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + template_attribute = objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=derivation_parameters, + template_attribute=template_attribute + ) + + # TODO(peter-hamilton) Update this test string when TemplateAttribute + # supports repr. + expected = ( + "DeriveKeyRequestPayload(" + "object_type=ObjectType.SYMMETRIC_KEY, " + "unique_identifiers=[" + "'fb4b5b9c-6188-4c63-8142-fe9c328129fc', " + "'5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', " + "'1703250b-4d40-4de2-93a0-c494a1d4ae40'], " + "derivation_method=DerivationMethod.HASH, " + "derivation_parameters={0}, " + "template_attribute={1})".format( + repr(derivation_parameters), + repr(template_attribute) + ) + ) + observed = repr(payload) + + self.assertEqual(expected, observed) + + def test_str(self): + """ + Test that str can be applied to a DeriveKey request payload + """ + derivation_parameters = attributes.DerivationParameters( + cryptographic_parameters=attributes.CryptographicParameters( + hashing_algorithm=enums.HashingAlgorithm.SHA_256 + ), + initialization_vector=b'\x39\x48\x74\x32\x49\x28\x34\xA3', + derivation_data=b'\xFA\xD9\x8B\x6A\xCA\x6D\x87\xDD' + ) + template_attribute = objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + payload = derive_key.DeriveKeyRequestPayload( + object_type=enums.ObjectType.SYMMETRIC_KEY, + unique_identifiers=[ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + derivation_method=enums.DerivationMethod.HASH, + derivation_parameters=derivation_parameters, + template_attribute=template_attribute + ) + + # TODO(peter-hamilton) Update this test string when TemplateAttribute + # supports str. + expected = str({ + 'object_type': enums.ObjectType.SYMMETRIC_KEY, + 'unique_identifiers': [ + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3', + '1703250b-4d40-4de2-93a0-c494a1d4ae40' + ], + 'derivation_method': enums.DerivationMethod.HASH, + 'derivation_parameters': derivation_parameters, + 'template_attribute': template_attribute + }) + observed = str(payload) + + self.assertEqual(expected, observed) + + +class TestDeriveKeyResponsePayload(testtools.TestCase): + """ + Test suite for the DeriveKey response payload. + """ + + def setUp(self): + super(TestDeriveKeyResponsePayload, self).setUp() + + # Encoding obtained in part from the KMIP 1.1 testing document. The + # rest of the encoding is a manual construction, since DeriveKey is + # not specifically detailed by the testing document. + # + # This encoding matches the following set of values: + # Unique Identifier - fb4b5b9c-6188-4c63-8142-fe9c328129fc + # Template Attribute + # Attribute + # Attribute Name - Cryptographic Algorithm + # Attribute Value - AES + # Attribute + # Attribute Name - Cryptographic Length + # Attribute Value - 128 + + self.full_encoding = utils.BytearrayStream( + b'\x42\x00\x7C\x01\x00\x00\x00\xA8' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + b'\x42\x00\x91\x01\x00\x00\x00\x70' + b'\x42\x00\x08\x01\x00\x00\x00\x30' + b'\x42\x00\x0A\x07\x00\x00\x00\x17\x43\x72\x79\x70\x74\x6F\x67\x72' + b'\x61\x70\x68\x69\x63\x20\x41\x6C\x67\x6F\x72\x69\x74\x68\x6D\x00' + b'\x42\x00\x0B\x05\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00' + b'\x42\x00\x08\x01\x00\x00\x00\x30' + b'\x42\x00\x0A\x07\x00\x00\x00\x14\x43\x72\x79\x70\x74\x6F\x67\x72' + b'\x61\x70\x68\x69\x63\x20\x4C\x65\x6E\x67\x74\x68\x00\x00\x00\x00' + b'\x42\x00\x0B\x02\x00\x00\x00\x04\x00\x00\x00\x80\x00\x00\x00\x00' + ) + + # All of the following partial encodings are trimmed versions of the + # above full encoding. + + self.partial_encoding_no_unique_identifier = utils.BytearrayStream( + b'\x42\x00\x7C\x01\x00\x00\x00\x00' + ) + self.partial_encoding_no_template_attribute = utils.BytearrayStream( + b'\x42\x00\x7C\x01\x00\x00\x00\x30' + b'\x42\x00\x94\x07\x00\x00\x00\x24\x66\x62\x34\x62\x35\x62\x39\x63' + b'\x2D\x36\x31\x38\x38\x2D\x34\x63\x36\x33\x2D\x38\x31\x34\x32\x2D' + b'\x66\x65\x39\x63\x33\x32\x38\x31\x32\x39\x66\x63\x00\x00\x00\x00' + ) + + def tearDown(self): + super(TestDeriveKeyResponsePayload, self).tearDown() + + def test_init(self): + """ + Test that a DeriveKey response payload can be constructed with no + arguments. + """ + payload = derive_key.DeriveKeyResponsePayload() + + self.assertEqual(None, payload.unique_identifier) + self.assertEqual(None, payload.template_attribute) + + def test_init_with_args(self): + """ + Test that a DeriveKey response payload can be constructed with valid + values + """ + payload = derive_key.DeriveKeyResponsePayload( + unique_identifier='00000000-1111-2222-3333-444444444444', + template_attribute=objects.TemplateAttribute() + ) + + self.assertEqual( + '00000000-1111-2222-3333-444444444444', + payload.unique_identifier + ) + self.assertEqual( + objects.TemplateAttribute(), + payload.template_attribute + ) + + def test_invalid_unique_identifier(self): + """ + Test that a TypeError is raised when invalid values are used to set + the unique identifier of a DeriveKey request payload. + """ + payload = derive_key.DeriveKeyResponsePayload() + args = (payload, 'unique_identifier', 0) + self.assertRaisesRegexp( + TypeError, + "unique identifier must be a string", + setattr, + *args + ) + + def test_invalid_template_attribute(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the template attribute of a DeriveKey response payload. + """ + payload = derive_key.DeriveKeyResponsePayload() + args = (payload, 'template_attribute', 'invalid') + self.assertRaisesRegexp( + TypeError, + "template attribute must be a TemplateAttribute struct", + setattr, + *args + ) + + def test_read(self): + """ + Test that a DeriveKey response payload can be read from a data stream. + """ + payload = derive_key.DeriveKeyResponsePayload() + + self.assertEqual(None, payload.unique_identifier) + self.assertEqual(None, payload.template_attribute) + + payload.read(self.full_encoding) + + self.assertEqual( + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + payload.unique_identifier + ) + self.assertEqual( + objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ), + payload.template_attribute + ) + + def test_read_missing_unique_identifier(self): + """ + Test that a ValueError gets raised when decoding a DeriveKey response + payload encoding missing the unique identifier. + """ + payload = derive_key.DeriveKeyResponsePayload() + + self.assertEqual(None, payload.unique_identifier) + self.assertEqual(None, payload.template_attribute) + + args = (self.partial_encoding_no_unique_identifier, ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing unique identifier", + payload.read, + *args + ) + + def test_read_missing_template_attribute(self): + """ + + Test that a DeriveKey response payload missing a template attribute + can be read from a data stream. + """ + payload = derive_key.DeriveKeyResponsePayload() + + self.assertEqual(None, payload.unique_identifier) + self.assertEqual(None, payload.template_attribute) + + payload.read(self.partial_encoding_no_template_attribute) + + self.assertEqual( + 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + payload.unique_identifier + ) + self.assertEqual(None, payload.template_attribute) + + def test_write(self): + """ + Test that a DeriveKey response payload can be written to a data stream. + """ + payload = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + stream = utils.BytearrayStream() + payload.write(stream) + + self.assertEqual(len(self.full_encoding), len(stream)) + self.assertEqual(str(self.full_encoding), str(stream)) + + def test_write_missing_unique_identifier(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey response + payload missing the unique identifier. + """ + payload = derive_key.DeriveKeyResponsePayload() + + args = (utils.BytearrayStream(), ) + self.assertRaisesRegexp( + ValueError, + "invalid payload missing unique identifier", + payload.write, + *args + ) + + def test_write_missing_template_attribute(self): + """ + Test that a ValueError gets raised when encoding a DeriveKey response + payload missing the template attribute. + """ + payload = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc' + ) + stream = utils.BytearrayStream() + + payload.write(stream) + + self.assertEqual( + len(self.partial_encoding_no_template_attribute), + len(stream) + ) + self.assertEqual( + str(self.partial_encoding_no_template_attribute), + str(stream) + ) + + def test_equal_on_equal(self): + """ + Test that the equality operator returns True when comparing two + DeriveKey response payloads with the same data. + """ + a = derive_key.DeriveKeyResponsePayload() + b = derive_key.DeriveKeyResponsePayload() + + self.assertTrue(a == b) + self.assertTrue(b == a) + + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_not_equal_unique_identifier(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey response payloads with different unique identifiers. + """ + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc' + ) + b = derive_key.DeriveKeyResponsePayload( + unique_identifier='5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3' + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='1703250b-4d40-4de2-93a0-c494a1d4ae40' + ) + b = derive_key.DeriveKeyResponsePayload(unique_identifier=None) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_template_attribute(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey response payloads with different template attributes. + """ + a = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.BLOWFISH, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=64, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute() + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + a = derive_key.DeriveKeyResponsePayload(template_attribute=None) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_type_mismatch(self): + """ + Test that the equality operator returns False when comparing two + DeriveKey response payloads with different types. + """ + a = derive_key.DeriveKeyResponsePayload() + b = 'invalid' + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_not_equal_on_equal(self): + """ + Test that the inequality operator returns False when comparing two + DeriveKey response payloads with the same data. + """ + a = derive_key.DeriveKeyResponsePayload() + b = derive_key.DeriveKeyResponsePayload() + + self.assertFalse(a != b) + self.assertFalse(b != a) + + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + +# x = a.template_attribute +# y = b.template_attribute +# raise ValueError(x, y, x == y, x != y) + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_not_equal_unique_identifier(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey response payloads with different unique identifiers. + """ + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc' + ) + b = derive_key.DeriveKeyResponsePayload( + unique_identifier='5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3' + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyResponsePayload( + unique_identifier='1703250b-4d40-4de2-93a0-c494a1d4ae40' + ) + b = derive_key.DeriveKeyResponsePayload(unique_identifier=None) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_template_attribute(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey response payloads with different template attribute. + """ + a = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.BLOWFISH, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=64, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute() + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + a = derive_key.DeriveKeyResponsePayload(template_attribute=None) + b = derive_key.DeriveKeyResponsePayload( + template_attribute=objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_type_mismatch(self): + """ + Test that the inequality operator returns True when comparing two + DeriveKey response payloads with different types. + """ + a = derive_key.DeriveKeyResponsePayload() + b = 'invalid' + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_repr(self): + """ + Test that repr can be applied to a DeriveKey response payload. + """ + template_attribute = objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + payload = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=template_attribute + ) + + # TODO(peter-hamilton) Update this test string when TemplateAttribute + # supports repr. + expected = ( + "DeriveKeyResponsePayload(" + "unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', " + "template_attribute={0})".format( + repr(template_attribute) + ) + ) + observed = repr(payload) + + self.assertEqual(expected, observed) + + def test_str(self): + """ + Test that str can be applied to a DeriveKey response payload + """ + template_attribute = objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Algorithm' + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ), + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + 'Cryptographic Length' + ), + attribute_value=primitives.Integer( + value=128, + tag=enums.Tags.CRYPTOGRAPHIC_LENGTH + ) + ) + ] + ) + payload = derive_key.DeriveKeyResponsePayload( + unique_identifier='fb4b5b9c-6188-4c63-8142-fe9c328129fc', + template_attribute=template_attribute + ) + + # TODO(peter-hamilton) Update this test string when TemplateAttribute + # supports str. + expected = str({ + 'unique_identifier': 'fb4b5b9c-6188-4c63-8142-fe9c328129fc', + 'template_attribute': template_attribute + }) + observed = str(payload) + + self.assertEqual(expected, observed)