diff --git a/kmip/core/objects.py b/kmip/core/objects.py index e5fc546..78f616a 100644 --- a/kmip/core/objects.py +++ b/kmip/core/objects.py @@ -815,21 +815,160 @@ class EncryptionKeyInformation(Struct): }) -class MACSignatureKeyInformation(KeyInformation): +class MACSignatureKeyInformation(primitives.Struct): + """ + A set of values detailing how an MAC/signed value was MAC/signed. + """ def __init__(self, unique_identifier=None, - cryptographic_parameters=None, - tag=Tags.MAC_SIGNATURE_KEY_INFORMATION): + cryptographic_parameters=None): + """ + Construct a MACSignatureKeyInformation struct. + + Args: + unique_identifier (string): The ID of the managed object (e.g., + a symmetric key) used for MAC/signing. Required for encoding + and decoding. + cryptographic_parameters (CryptographicParameters): A + CryptographicParameters struct containing the settings for + the MAC/signing process. Optional, defaults to None. If not + included, the CryptographicParameters associated with the + managed object will be used instead. + """ super(MACSignatureKeyInformation, self).__init__( - unique_identifier, cryptographic_parameters, tag) + tag=Tags.MAC_SIGNATURE_KEY_INFORMATION + ) - def validate(self): - self.__validate() + self._unique_identifier = None + self._cryptographic_parameters = None - def __validate(self): - # TODO (peter-hamilton) Finish implementation. - pass + self.unique_identifier = unique_identifier + self.cryptographic_parameters = cryptographic_parameters + + @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 cryptographic_parameters(self): + return self._cryptographic_parameters + + @cryptographic_parameters.setter + def cryptographic_parameters(self, value): + if value is None: + self._cryptographic_parameters = None + elif isinstance(value, CryptographicParameters): + self._cryptographic_parameters = value + else: + raise TypeError( + "Cryptographic parameters must be a CryptographicParameters " + "struct." + ) + + def read(self, input_stream): + """ + Read the data encoding the MACSignatureKeyInformation struct 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. + """ + super(MACSignatureKeyInformation, self).read(input_stream) + local_stream = 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 struct missing the unique identifier attribute." + ) + + if self.is_tag_next( + enums.Tags.CRYPTOGRAPHIC_PARAMETERS, + local_stream + ): + self._cryptographic_parameters = CryptographicParameters() + self._cryptographic_parameters.read(local_stream) + + self.is_oversized(local_stream) + + def write(self, output_stream): + """ + Write the data encoding the MACSignatureKeyInformation struct to a + stream. + + Args: + output_stream (stream): A data stream in which to encode object + data, supporting a write method; usually a BytearrayStream + object. + """ + local_stream = BytearrayStream() + + if self._unique_identifier: + self._unique_identifier.write(local_stream) + else: + raise ValueError( + "Invalid struct missing the unique identifier attribute." + ) + + if self._cryptographic_parameters: + self._cryptographic_parameters.write(local_stream) + + self.length = local_stream.length() + super(MACSignatureKeyInformation, self).write(output_stream) + output_stream.write(local_stream.buffer) + + def __eq__(self, other): + if isinstance(other, MACSignatureKeyInformation): + if self.unique_identifier != other.unique_identifier: + return False + elif self.cryptographic_parameters != \ + other.cryptographic_parameters: + return False + else: + return True + + def __ne__(self, other): + if isinstance(other, MACSignatureKeyInformation): + return not self == other + else: + return NotImplemented + + def __repr__(self): + args = ", ".join([ + "unique_identifier='{0}'".format(self.unique_identifier), + "cryptographic_parameters={0}".format( + repr(self.cryptographic_parameters) + ) + ]) + return "MACSignatureKeyInformation({0})".format(args) + + def __str__(self): + return str({ + 'unique_identifier': self.unique_identifier, + 'cryptographic_parameters': self.cryptographic_parameters + }) class KeyWrappingData(Struct): diff --git a/kmip/tests/unit/core/objects/test_objects.py b/kmip/tests/unit/core/objects/test_objects.py index 5e35918..2d4886a 100644 --- a/kmip/tests/unit/core/objects/test_objects.py +++ b/kmip/tests/unit/core/objects/test_objects.py @@ -748,3 +748,459 @@ class TestEncryptionKeyInformation(testtools.TestCase): observed = str(encryption_key_information) self.assertEqual(expected, observed) + + +class TestMACSignatureKeyInformation(testtools.TestCase): + """ + Test suite for the MACSignatureKeyInformation struct. + """ + + def setUp(self): + super(TestMACSignatureKeyInformation, self).setUp() + + # Encoding obtained in part from the KMIP 1.1 testing document, + # Section 14.1. The rest of the encoding was built by hand. + # + # This encoding matches the following set of values: + # Unique Identifier - 100182d5-72b8-47aa-8383-4d97d512e98a + # Cryptographic Parameters + # Block Cipher Mode - NIST_KEY_WRAP + + self.full_encoding = BytearrayStream( + b'\x42\x00\x4E\x01\x00\x00\x00\x48' + b'\x42\x00\x94\x07\x00\x00\x00\x24' + b'\x31\x30\x30\x31\x38\x32\x64\x35\x2D\x37\x32\x62\x38\x2D\x34\x37' + b'\x61\x61\x2D\x38\x33\x38\x33\x2D\x34\x64\x39\x37\x64\x35\x31\x32' + b'\x65\x39\x38\x61\x00\x00\x00\x00' + b'\x42\x00\x2B\x01\x00\x00\x00\x10' + b'\x42\x00\x11\x05\x00\x00\x00\x04\x00\x00\x00\x0D\x00\x00\x00\x00' + ) + + # Adapted from the full encoding above. This encoding matches the + # following set of values: + # Unique Identifier - 100182d5-72b8-47aa-8383-4d97d512e98a + + self.partial_encoding = BytearrayStream( + b'\x42\x00\x4E\x01\x00\x00\x00\x30' + b'\x42\x00\x94\x07\x00\x00\x00\x24' + b'\x31\x30\x30\x31\x38\x32\x64\x35\x2D\x37\x32\x62\x38\x2D\x34\x37' + b'\x61\x61\x2D\x38\x33\x38\x33\x2D\x34\x64\x39\x37\x64\x35\x31\x32' + b'\x65\x39\x38\x61\x00\x00\x00\x00' + ) + + self.empty_encoding = BytearrayStream( + b'\x42\x00\x4E\x01\x00\x00\x00\x00' + ) + + def tearDown(self): + super(TestMACSignatureKeyInformation, self).tearDown() + + def test_init(self): + """ + Test that a MACSignatureKeyInformation struct can be constructed with + no arguments. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation() + + self.assertEqual( + None, + mac_signature_key_information.unique_identifier + ) + self.assertEqual( + None, + mac_signature_key_information.cryptographic_parameters + ) + + def test_init_with_args(self): + """ + Test that a MACSignatureKeyInformation struct can be constructed with + valid values. + """ + cryptographic_parameters = attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CTR) + mac_signature_key_information = objects.MACSignatureKeyInformation( + unique_identifier="00000000-1111-2222-3333-444444444444", + cryptographic_parameters=cryptographic_parameters + ) + + self.assertEqual( + "00000000-1111-2222-3333-444444444444", + mac_signature_key_information.unique_identifier + ) + self.assertIsInstance( + mac_signature_key_information.cryptographic_parameters, + attributes.CryptographicParameters + ) + parameters = mac_signature_key_information.cryptographic_parameters + self.assertEqual( + enums.BlockCipherMode.CTR, + parameters.block_cipher_mode + ) + + def test_invalid_unique_identifier(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the unique identifier of a MACSignatureKeyInformation struct. + """ + kwargs = {'unique_identifier': 0} + self.assertRaisesRegexp( + TypeError, + "Unique identifier must be a string.", + objects.MACSignatureKeyInformation, + **kwargs + ) + + args = (objects.MACSignatureKeyInformation(), 'unique_identifier', 0) + self.assertRaisesRegexp( + TypeError, + "Unique identifier must be a string.", + setattr, + *args + ) + + def test_invalid_cryptographic_parameters(self): + """ + Test that a TypeError is raised when an invalid value is used to set + the cryptographic parameters of a MACSignatureKeyInformation struct. + """ + kwargs = {'cryptographic_parameters': 'invalid'} + self.assertRaisesRegexp( + TypeError, + "Cryptographic parameters must be a CryptographicParameters " + "struct.", + objects.MACSignatureKeyInformation, + **kwargs + ) + + args = ( + objects.MACSignatureKeyInformation(), + 'cryptographic_parameters', + 'invalid' + ) + self.assertRaisesRegexp( + TypeError, + "Cryptographic parameters must be a CryptographicParameters " + "struct.", + setattr, + *args + ) + + def test_read(self): + """ + Test that a MACSignatureKeyInformation struct can be read from a data + stream. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation() + + self.assertEqual( + None, + mac_signature_key_information.unique_identifier + ) + self.assertEqual( + None, + mac_signature_key_information.cryptographic_parameters + ) + + mac_signature_key_information.read(self.full_encoding) + + self.assertEqual( + "100182d5-72b8-47aa-8383-4d97d512e98a", + mac_signature_key_information.unique_identifier + ) + self.assertIsInstance( + mac_signature_key_information.cryptographic_parameters, + attributes.CryptographicParameters + ) + cryptographic_parameters = \ + mac_signature_key_information.cryptographic_parameters + self.assertEqual( + enums.BlockCipherMode.NIST_KEY_WRAP, + cryptographic_parameters.block_cipher_mode + ) + + def test_read_partial(self): + """ + Test that a MACSignatureKeyInformation struct can be read from a + partial data stream. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation() + + self.assertEqual( + None, + mac_signature_key_information.unique_identifier + ) + self.assertEqual( + None, + mac_signature_key_information.cryptographic_parameters + ) + + mac_signature_key_information.read(self.partial_encoding) + + self.assertEqual( + "100182d5-72b8-47aa-8383-4d97d512e98a", + mac_signature_key_information.unique_identifier + ) + self.assertEqual( + None, + mac_signature_key_information.cryptographic_parameters + ) + + def test_read_invalid(self): + """ + Test that a ValueError gets raised when a required + MACSignatureKeyInformation field is missing from the struct encoding. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation() + args = (self.empty_encoding,) + self.assertRaisesRegexp( + ValueError, + "Invalid struct missing the unique identifier attribute.", + mac_signature_key_information.read, + *args + ) + + def test_write(self): + """ + Test that a MACSignatureKeyInformation struct can be written to a data + stream. + """ + cryptographic_parameters = attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.NIST_KEY_WRAP + ) + mac_signature_key_information = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=cryptographic_parameters + ) + stream = BytearrayStream() + mac_signature_key_information.write(stream) + + self.assertEqual(len(self.full_encoding), len(stream)) + self.assertEqual(str(self.full_encoding), str(stream)) + + def test_write_partial(self): + """ + Test that a partially defined MACSignatureKeyInformation struct can be + written to a data stream. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a" + ) + stream = BytearrayStream() + mac_signature_key_information.write(stream) + + self.assertEqual(len(self.partial_encoding), len(stream)) + self.assertEqual(str(self.partial_encoding), str(stream)) + + def test_write_invalid(self): + """ + Test that a ValueError gets raised when a required + MACSignatureKeyInformation field is missing when encoding the struct. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation() + stream = utils.BytearrayStream() + args = (stream,) + self.assertRaisesRegexp( + ValueError, + "Invalid struct missing the unique identifier attribute.", + mac_signature_key_information.write, + *args + ) + + def test_equal_on_equal(self): + """ + Test that the equality operator returns True when comparing two + MACSignatureKeyInformation structs with the same data. + """ + a = objects.MACSignatureKeyInformation() + b = objects.MACSignatureKeyInformation() + + self.assertTrue(a == b) + self.assertTrue(b == a) + + a = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + b = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + + 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 + MACSignatureKeyInformation structs with different unique identifiers. + """ + a = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a" + ) + b = objects.MACSignatureKeyInformation( + unique_identifier="00000000-1111-2222-3333-444444444444" + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_not_equal_cryptographic_parameters(self): + """ + Test that the equality operator returns False when comparing two + MACSignatureKeyInformation structs with different cryptographic + parameters. + """ + a = objects.MACSignatureKeyInformation( + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + b = objects.MACSignatureKeyInformation( + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.GCM + ) + ) + + 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 + MACSignatureKeyInformation structs with different types. + """ + a = objects.MACSignatureKeyInformation() + 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 + MACSignatureKeyInformation structs with the same data. + """ + a = objects.MACSignatureKeyInformation() + b = objects.MACSignatureKeyInformation() + + self.assertFalse(a != b) + self.assertFalse(b != a) + + a = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + b = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + + 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 + MACSignatureKeyInformation structs with different unique identifiers. + """ + a = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a" + ) + b = objects.MACSignatureKeyInformation( + unique_identifier="00000000-1111-2222-3333-444444444444" + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_not_equal_cryptographic_parameters(self): + """ + Test that the inequality operator returns True when comparing two + MACSignatureKeyInformation structs with different cryptographic + parameters. + """ + a = objects.MACSignatureKeyInformation( + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + b = objects.MACSignatureKeyInformation( + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.GCM + ) + ) + + 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 + MACSignatureKeyInformation structs with different types. + """ + a = objects.MACSignatureKeyInformation() + b = 'invalid' + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_repr(self): + """ + Test that repr can be applied to an MACSignatureKeyInformation struct. + """ + mac_signature_key_information = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ) + + expected = ( + "MACSignatureKeyInformation(" + "unique_identifier='100182d5-72b8-47aa-8383-4d97d512e98a', " + "cryptographic_parameters=CryptographicParameters(" + "block_cipher_mode=BlockCipherMode.CBC, " + "padding_method=None, " + "hashing_algorithm=None, " + "key_role_type=None, " + "digital_signature_algorithm=None, " + "cryptographic_algorithm=None, " + "random_iv=None, " + "iv_length=None, " + "tag_length=None, " + "fixed_field_length=None, " + "invocation_field_length=None, " + "counter_length=None, " + "initial_counter_value=None))" + ) + observed = repr(mac_signature_key_information) + + self.assertEqual(expected, observed) + + def test_str(self): + """ + Test that str can be applied to a MACSignatureKeyInformation struct. + """ + cryptographic_parameters = attributes.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + mac_signature_key_information = objects.MACSignatureKeyInformation( + unique_identifier="100182d5-72b8-47aa-8383-4d97d512e98a", + cryptographic_parameters=cryptographic_parameters + ) + + expected = str({ + 'unique_identifier': "100182d5-72b8-47aa-8383-4d97d512e98a", + 'cryptographic_parameters': cryptographic_parameters + }) + observed = str(mac_signature_key_information) + + self.assertEqual(expected, observed)