From fc86e1bef484b3fa8962d449e70dfc4f1683c2d0 Mon Sep 17 00:00:00 2001 From: Peter Hamilton Date: Sat, 30 Sep 2017 16:22:52 -0400 Subject: [PATCH] Update the object data model to support storing key wrapping data This change updates the KMIP object model to support explicitly storing key wrapping data attributes. Key wrapping data is treated externally as a dictionary and is stored as individual fields in the back end. Various unit tests have been updated and added to support these additions. --- kmip/pie/factory.py | 79 +++- kmip/pie/objects.py | 386 +++++++++++++++++- kmip/pie/sqltypes.py | 7 +- kmip/tests/unit/pie/objects/test_key.py | 14 + .../unit/pie/objects/test_private_key.py | 60 ++- .../tests/unit/pie/objects/test_public_key.py | 60 ++- .../unit/pie/objects/test_symmetric_key.py | 60 ++- kmip/tests/unit/pie/test_factory.py | 100 +++++ 8 files changed, 736 insertions(+), 30 deletions(-) diff --git a/kmip/pie/factory.py b/kmip/pie/factory.py index efb87fa..36401c6 100644 --- a/kmip/pie/factory.py +++ b/kmip/pie/factory.py @@ -85,9 +85,17 @@ class ObjectFactory: length = key.key_block.cryptographic_length.value value = key.key_block.key_value.key_material.value format_type = key.key_block.key_format_type.value + key_wrapping_data = key.key_block.key_wrapping_data if cls is pobjects.SymmetricKey: - key = cls(algorithm, length, value) + key = cls( + algorithm, + length, + value, + key_wrapping_data=self._build_key_wrapping_data( + key_wrapping_data + ) + ) if key.key_format_type != format_type: raise TypeError( "core key format type not compatible with Pie " @@ -96,7 +104,15 @@ class ObjectFactory: else: return key else: - return cls(algorithm, length, value, format_type) + return cls( + algorithm, + length, + value, + format_type, + key_wrapping_data=self._build_key_wrapping_data( + key_wrapping_data + ) + ) def _build_pie_secret_data(self, secret): secret_data_type = secret.secret_data_type.value @@ -117,6 +133,11 @@ class ObjectFactory: key_material = cobjects.KeyMaterial(value) key_value = cobjects.KeyValue(key_material) + key_wrapping_data = None + if key.key_wrapping_data: + key_wrapping_data = cobjects.KeyWrappingData( + **key.key_wrapping_data + ) key_block = cobjects.KeyBlock( key_format_type=misc.KeyFormatType(format_type), key_compression_type=None, @@ -124,7 +145,8 @@ class ObjectFactory: cryptographic_algorithm=attributes.CryptographicAlgorithm( algorithm), cryptographic_length=attributes.CryptographicLength(length), - key_wrapping_data=None) + key_wrapping_data=key_wrapping_data + ) return cls(key_block) @@ -155,3 +177,54 @@ class ObjectFactory: opaque_data_type = secrets.OpaqueObject.OpaqueDataType(opaque_type) opaque_data_value = secrets.OpaqueObject.OpaqueDataValue(value) return secrets.OpaqueObject(opaque_data_type, opaque_data_value) + + def _build_cryptographic_parameters(self, value): + cryptographic_parameters = { + 'block_cipher_mode': value.block_cipher_mode, + 'padding_method': value.padding_method, + 'hashing_algorithm': value.hashing_algorithm, + 'key_role_type': value.key_role_type, + 'digital_signature_algorithm': value.digital_signature_algorithm, + 'cryptographic_algorithm': value.cryptographic_algorithm, + 'random_iv': value.random_iv, + 'iv_length': value.iv_length, + 'tag_length': value.tag_length, + 'fixed_field_length': value.fixed_field_length, + 'invocation_field_length': value.invocation_field_length, + 'counter_length': value.counter_length, + 'initial_counter_value': value.initial_counter_value + } + return cryptographic_parameters + + def _build_key_wrapping_data(self, value): + if value is None: + return None + encryption_key_info = value.encryption_key_information + encryption_key_information = {} + if encryption_key_info: + encryption_key_information = { + 'unique_identifier': encryption_key_info.unique_identifier, + 'cryptographic_parameters': + self._build_cryptographic_parameters( + encryption_key_info.cryptographic_parameters + ) + } + mac_signature_key_info = value.mac_signature_key_information + mac_signature_key_information = {} + if mac_signature_key_info: + mac_signature_key_information = { + 'unique_identifier': mac_signature_key_info.unique_identifier, + 'cryptographic_parameters': + self._build_cryptographic_parameters( + mac_signature_key_info.cryptographic_parameters + ) + } + key_wrapping_data = { + 'wrapping_method': value.wrapping_method, + 'encryption_key_information': encryption_key_information, + 'mac_signature_key_information': mac_signature_key_information, + 'mac_signature': value.mac_signature, + 'iv_counter_nonce': value.iv_counter_nonce, + 'encoding_option': value.encoding_option + } + return key_wrapping_data diff --git a/kmip/pie/objects.py b/kmip/pie/objects.py index 6c4f547..f12baa8 100644 --- a/kmip/pie/objects.py +++ b/kmip/pie/objects.py @@ -15,6 +15,7 @@ from abc import abstractmethod from sqlalchemy import Column, event, ForeignKey, Integer, String, VARBINARY +from sqlalchemy import Boolean from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import relationship @@ -212,6 +213,8 @@ class Key(CryptographicObject): cryptographic_length: An int defining the length of the key in bits. key_format_type: A KeyFormatType enumeration defining the format of the key value. + key_wrapping_data: A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. """ __tablename__ = 'keys' @@ -224,6 +227,167 @@ class Key(CryptographicObject): key_format_type = Column( 'key_format_type', sql.EnumType(enums.KeyFormatType)) + # Key wrapping data fields + _kdw_wrapping_method = Column( + '_kdw_wrapping_method', + sql.EnumType(enums.WrappingMethod), + default=None + ) + _kdw_eki_unique_identifier = Column( + '_kdw_eki_unique_identifier', + String, + default=None + ) + _kdw_eki_cp_block_cipher_mode = Column( + '_kdw_eki_cp_block_cipher_mode', + sql.EnumType(enums.BlockCipherMode), + default=None + ) + _kdw_eki_cp_padding_method = Column( + '_kdw_eki_cp_padding_method', + sql.EnumType(enums.PaddingMethod), + default=None + ) + _kdw_eki_cp_hashing_algorithm = Column( + '_kdw_eki_cp_hashing_algorithm', + sql.EnumType(enums.HashingAlgorithm), + default=None + ) + _kdw_eki_cp_key_role_type = Column( + '_kdw_eki_cp_key_role_type', + sql.EnumType(enums.KeyRoleType), + default=None + ) + _kdw_eki_cp_digital_signature_algorithm = Column( + '_kdw_eki_cp_digital_signature_algorithm', + sql.EnumType(enums.DigitalSignatureAlgorithm), + default=None + ) + _kdw_eki_cp_cryptographic_algorithm = Column( + '_kdw_eki_cp_cryptographic_algorithm', + sql.EnumType(enums.CryptographicAlgorithm), + default=None + ) + _kdw_eki_cp_random_iv = Column( + '_kdw_eki_cp_random_iv', + Boolean, + default=None + ) + _kdw_eki_cp_iv_length = Column( + '_kdw_eki_cp_iv_length', + Integer, + default=None + ) + _kdw_eki_cp_tag_length = Column( + '_kdw_eki_cp_tag_length', + Integer, + default=None + ) + _kdw_eki_cp_fixed_field_length = Column( + '_kdw_eki_cp_fixed_field_length', + Integer, + default=None + ) + _kdw_eki_cp_invocation_field_length = Column( + '_kdw_eki_cp_invocation_field_length', + Integer + ) + _kdw_eki_cp_counter_length = Column( + '_kdw_eki_cp_counter_length', + Integer, + default=None + ) + _kdw_eki_cp_initial_counter_value = Column( + '_kdw_eki_cp_initial_counter_value', + Integer, + default=None + ) + _kdw_mski_unique_identifier = Column( + '_kdw_mski_unique_identifier', + String, + default=None + ) + _kdw_mski_cp_block_cipher_mode = Column( + '_kdw_mski_cp_block_cipher_mode', + sql.EnumType(enums.BlockCipherMode), + default=None + ) + _kdw_mski_cp_padding_method = Column( + '_kdw_mski_cp_padding_method', + sql.EnumType(enums.PaddingMethod), + default=None + ) + _kdw_mski_cp_hashing_algorithm = Column( + '_kdw_mski_cp_hashing_algorithm', + sql.EnumType(enums.HashingAlgorithm), + default=None + ) + _kdw_mski_cp_key_role_type = Column( + '_kdw_mski_cp_key_role_type', + sql.EnumType(enums.KeyRoleType), + default=None + ) + _kdw_mski_cp_digital_signature_algorithm = Column( + '_kdw_mski_cp_digital_signature_algorithm', + sql.EnumType(enums.DigitalSignatureAlgorithm), + default=None + ) + _kdw_mski_cp_cryptographic_algorithm = Column( + '_kdw_mski_cp_cryptographic_algorithm', + sql.EnumType(enums.CryptographicAlgorithm), + default=None + ) + _kdw_mski_cp_random_iv = Column( + '_kdw_mski_cp_random_iv', + Boolean, + default=None + ) + _kdw_mski_cp_iv_length = Column( + '_kdw_mski_cp_iv_length', + Integer, + default=None + ) + _kdw_mski_cp_tag_length = Column( + '_kdw_mski_cp_tag_length', + Integer, + default=None + ) + _kdw_mski_cp_fixed_field_length = Column( + '_kdw_mski_cp_fixed_field_length', + Integer, + default=None + ) + _kdw_mski_cp_invocation_field_length = Column( + '_kdw_mski_cp_invocation_field_length', + Integer, + default=None + ) + _kdw_mski_cp_counter_length = Column( + '_kdw_mski_cp_counter_length', + Integer, + default=None + ) + _kdw_mski_cp_initial_counter_value = Column( + '_kdw_mski_cp_initial_counter_value', + Integer, + default=None + ) + _kdw_mac_signature = Column( + '_kdw_mac_signature', + VARBINARY(1024), + default=None + ) + _kdw_iv_counter_nonce = Column( + '_kdw_iv_counter_nonce', + VARBINARY(1024), + default=None + ) + _kdw_encoding_option = Column( + '_kdw_encoding_option', + sql.EnumType(enums.EncodingOption), + default=None + ) + __mapper_args__ = { 'polymorphic_identity': 'Key' } @@ -232,15 +396,21 @@ class Key(CryptographicObject): } @abstractmethod - def __init__(self): + def __init__(self, key_wrapping_data=None): """ Create a Key object. + + Args: + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. + Optional, defaults to None. """ super(Key, self).__init__() self.cryptographic_algorithm = None self.cryptographic_length = None self.key_format_type = None + self.key_wrapping_data = key_wrapping_data # All remaining attributes are not considered part of the public API # and are subject to change. @@ -250,6 +420,145 @@ class Key(CryptographicObject): # unsupported by kmip.core self._usage_limits = None + @property + def key_wrapping_data(self): + """ + Retrieve all of the relevant key wrapping data fields and return them + as a dictionary. + """ + key_wrapping_data = {} + encryption_key_info = { + 'unique_identifier': self._kdw_eki_unique_identifier, + 'cryptographic_parameters': { + 'block_cipher_mode': self._kdw_eki_cp_block_cipher_mode, + 'padding_method': self._kdw_eki_cp_padding_method, + 'hashing_algorithm': self._kdw_eki_cp_hashing_algorithm, + 'key_role_type': self._kdw_eki_cp_key_role_type, + 'digital_signature_algorithm': + self._kdw_eki_cp_digital_signature_algorithm, + 'cryptographic_algorithm': + self._kdw_eki_cp_cryptographic_algorithm, + 'random_iv': self._kdw_eki_cp_random_iv, + 'iv_length': self._kdw_eki_cp_iv_length, + 'tag_length': self._kdw_eki_cp_tag_length, + 'fixed_field_length': self._kdw_eki_cp_fixed_field_length, + 'invocation_field_length': + self._kdw_eki_cp_invocation_field_length, + 'counter_length': self._kdw_eki_cp_counter_length, + 'initial_counter_value': + self._kdw_eki_cp_initial_counter_value + } + } + if not any(encryption_key_info['cryptographic_parameters'].values()): + encryption_key_info['cryptographic_parameters'] = {} + if not any(encryption_key_info.values()): + encryption_key_info = {} + + mac_sign_key_info = { + 'unique_identifier': self._kdw_mski_unique_identifier, + 'cryptographic_parameters': { + 'block_cipher_mode': self._kdw_mski_cp_block_cipher_mode, + 'padding_method': self._kdw_mski_cp_padding_method, + 'hashing_algorithm': self._kdw_mski_cp_hashing_algorithm, + 'key_role_type': self._kdw_mski_cp_key_role_type, + 'digital_signature_algorithm': + self._kdw_mski_cp_digital_signature_algorithm, + 'cryptographic_algorithm': + self._kdw_mski_cp_cryptographic_algorithm, + 'random_iv': self._kdw_mski_cp_random_iv, + 'iv_length': self._kdw_mski_cp_iv_length, + 'tag_length': self._kdw_mski_cp_tag_length, + 'fixed_field_length': self._kdw_mski_cp_fixed_field_length, + 'invocation_field_length': + self._kdw_mski_cp_invocation_field_length, + 'counter_length': self._kdw_mski_cp_counter_length, + 'initial_counter_value': + self._kdw_mski_cp_initial_counter_value + } + } + if not any(mac_sign_key_info['cryptographic_parameters'].values()): + mac_sign_key_info['cryptographic_parameters'] = {} + if not any(mac_sign_key_info.values()): + mac_sign_key_info = {} + + key_wrapping_data['wrapping_method'] = self._kdw_wrapping_method + key_wrapping_data['encryption_key_information'] = encryption_key_info + key_wrapping_data['mac_signature_key_information'] = mac_sign_key_info + key_wrapping_data['mac_signature'] = self._kdw_mac_signature + key_wrapping_data['iv_counter_nonce'] = self._kdw_iv_counter_nonce + key_wrapping_data['encoding_option'] = self._kdw_encoding_option + if not any(key_wrapping_data.values()): + key_wrapping_data = {} + + return key_wrapping_data + + @key_wrapping_data.setter + def key_wrapping_data(self, value): + """ + Set the key wrapping data attributes using a dictionary. + """ + if value is None: + value = {} + elif not isinstance(value, dict): + raise TypeError("Key wrapping data must be a dictionary.") + + self._kdw_wrapping_method = value.get('wrapping_method') + + eki = value.get('encryption_key_information') + if eki is None: + eki = {} + self._kdw_eki_unique_identifier = eki.get('unique_identifier') + eki_cp = eki.get('cryptographic_parameters') + if eki_cp is None: + eki_cp = {} + self._kdw_eki_cp_block_cipher_mode = eki_cp.get('block_cipher_mode') + self._kdw_eki_cp_padding_method = eki_cp.get('padding_method') + self._kdw_eki_cp_hashing_algorithm = eki_cp.get('hashing_algorithm') + self._kdw_eki_cp_key_role_type = eki_cp.get('key_role_type') + self._kdw_eki_cp_digital_signature_algorithm = \ + eki_cp.get('digital_signature_algorithm') + self._kdw_eki_cp_cryptographic_algorithm = \ + eki_cp.get('cryptographic_algorithm') + self._kdw_eki_cp_random_iv = eki_cp.get('random_iv') + self._kdw_eki_cp_iv_length = eki_cp.get('iv_length') + self._kdw_eki_cp_tag_length = eki_cp.get('tag_length') + self._kdw_eki_cp_fixed_field_length = eki_cp.get('fixed_field_length') + self._kdw_eki_cp_invocation_field_length = \ + eki_cp.get('invocation_field_length') + self._kdw_eki_cp_counter_length = eki_cp.get('counter_length') + self._kdw_eki_cp_initial_counter_value = \ + eki_cp.get('initial_counter_value') + + mski = value.get('mac_signature_key_information') + if mski is None: + mski = {} + self._kdw_mski_unique_identifier = mski.get('unique_identifier') + mski_cp = mski.get('cryptographic_parameters') + if mski_cp is None: + mski_cp = {} + self._kdw_mski_cp_block_cipher_mode = mski_cp.get('block_cipher_mode') + self._kdw_mski_cp_padding_method = mski_cp.get('padding_method') + self._kdw_mski_cp_hashing_algorithm = mski_cp.get('hashing_algorithm') + self._kdw_mski_cp_key_role_type = mski_cp.get('key_role_type') + self._kdw_mski_cp_digital_signature_algorithm = \ + mski_cp.get('digital_signature_algorithm') + self._kdw_mski_cp_cryptographic_algorithm = \ + mski_cp.get('cryptographic_algorithm') + self._kdw_mski_cp_random_iv = mski_cp.get('random_iv') + self._kdw_mski_cp_iv_length = mski_cp.get('iv_length') + self._kdw_mski_cp_tag_length = mski_cp.get('tag_length') + self._kdw_mski_cp_fixed_field_length = \ + mski_cp.get('fixed_field_length') + self._kdw_mski_cp_invocation_field_length = \ + mski_cp.get('invocation_field_length') + self._kdw_mski_cp_counter_length = mski_cp.get('counter_length') + self._kdw_mski_cp_initial_counter_value = \ + mski_cp.get('initial_counter_value') + + self._kdw_mac_signature = value.get('mac_signature') + self._kdw_iv_counter_nonce = value.get('iv_counter_nonce') + self._kdw_encoding_option = value.get('encoding_option') + class SymmetricKey(Key): """ @@ -267,6 +576,8 @@ class SymmetricKey(Key): cryptographic_usage_masks: The list of usage mask flags for SymmetricKey application. names: The string names of the SymmetricKey. + key_wrapping_data: A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. """ __tablename__ = 'symmetric_keys' @@ -282,7 +593,7 @@ class SymmetricKey(Key): } def __init__(self, algorithm, length, value, masks=None, - name='Symmetric Key'): + name='Symmetric Key', key_wrapping_data=None): """ Create a SymmetricKey. @@ -295,8 +606,13 @@ class SymmetricKey(Key): how the key will be used. Optional, defaults to None. name(string): The string name of the key. Optional, defaults to 'Symmetric Key'. + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. + Optional, defaults to None. """ - super(SymmetricKey, self).__init__() + super(SymmetricKey, self).__init__( + key_wrapping_data=key_wrapping_data + ) self._object_type = enums.ObjectType.SYMMETRIC_KEY self.key_format_type = enums.KeyFormatType.RAW @@ -353,17 +669,29 @@ class SymmetricKey(Key): raise TypeError("key name {0} must be a string".format( position)) - if (len(self.value) * 8) != self.cryptographic_length: - msg = "key length ({0}) not equal to key value length ({1})" - msg = msg.format(self.cryptographic_length, len(self.value) * 8) - raise ValueError(msg) + if not self.key_wrapping_data: + if (len(self.value) * 8) != self.cryptographic_length: + msg = "key length ({0}) not equal to key value length ({1})" + msg = msg.format( + self.cryptographic_length, + len(self.value) * 8 + ) + raise ValueError(msg) def __repr__(self): algorithm = "algorithm={0}".format(self.cryptographic_algorithm) length = "length={0}".format(self.cryptographic_length) value = "value={0}".format(binascii.hexlify(self.value)) + key_wrapping_data = "key_wrapping_data={0}".format( + self.key_wrapping_data + ) - return "SymmetricKey({0}, {1}, {2})".format(algorithm, length, value) + return "SymmetricKey({0}, {1}, {2}, {3})".format( + algorithm, + length, + value, + key_wrapping_data + ) def __str__(self): return str(binascii.hexlify(self.value)) @@ -376,6 +704,8 @@ class SymmetricKey(Key): return False elif self.cryptographic_length != other.cryptographic_length: return False + elif self.key_wrapping_data != other.key_wrapping_data: + return False else: return True else: @@ -408,6 +738,8 @@ class PublicKey(Key): cryptographic_usage_masks: The list of usage mask flags for PublicKey application. names: The list of string names of the PublicKey. + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. """ __tablename__ = 'public_keys' @@ -424,7 +756,7 @@ class PublicKey(Key): def __init__(self, algorithm, length, value, format_type=enums.KeyFormatType.X_509, masks=None, - name='Public Key'): + name='Public Key', key_wrapping_data=None): """ Create a PublicKey. @@ -439,8 +771,13 @@ class PublicKey(Key): defining how the key will be used. Optional, defaults to None. name(string): The string name of the key. Optional, defaults to 'Public Key'. + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. + Optional, defaults to None. """ - super(PublicKey, self).__init__() + super(PublicKey, self).__init__( + key_wrapping_data=key_wrapping_data + ) self._object_type = enums.ObjectType.PUBLIC_KEY self._valid_formats = [ @@ -512,9 +849,12 @@ class PublicKey(Key): length = "length={0}".format(self.cryptographic_length) value = "value={0}".format(binascii.hexlify(self.value)) format_type = "format_type={0}".format(self.key_format_type) + key_wrapping_data = "key_wrapping_data={0}".format( + self.key_wrapping_data + ) - return "PublicKey({0}, {1}, {2}, {3})".format( - algorithm, length, value, format_type) + return "PublicKey({0}, {1}, {2}, {3}, {4})".format( + algorithm, length, value, format_type, key_wrapping_data) def __str__(self): return str(binascii.hexlify(self.value)) @@ -529,6 +869,8 @@ class PublicKey(Key): return False elif self.cryptographic_length != other.cryptographic_length: return False + elif self.key_wrapping_data != other.key_wrapping_data: + return False else: return True else: @@ -562,6 +904,8 @@ class PrivateKey(Key): application. Optional, defaults to None. names: The list of string names of the PrivateKey. Optional, defaults to 'Private Key'. + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. """ __tablename__ = 'private_keys' @@ -577,7 +921,7 @@ class PrivateKey(Key): } def __init__(self, algorithm, length, value, format_type, masks=None, - name='Private Key'): + name='Private Key', key_wrapping_data=None): """ Create a PrivateKey. @@ -591,8 +935,13 @@ class PrivateKey(Key): masks(list): A list of CryptographicUsageMask enumerations defining how the key will be used. name(string): The string name of the key. + key_wrapping_data(dict): A dictionary containing key wrapping data + settings, describing how the key value has been wrapped. + Optional, defaults to None. """ - super(PrivateKey, self).__init__() + super(PrivateKey, self).__init__( + key_wrapping_data=key_wrapping_data + ) self._object_type = enums.ObjectType.PRIVATE_KEY self._valid_formats = [ @@ -664,9 +1013,12 @@ class PrivateKey(Key): length = "length={0}".format(self.cryptographic_length) value = "value={0}".format(binascii.hexlify(self.value)) format_type = "format_type={0}".format(self.key_format_type) + key_wrapping_data = "key_wrapping_data={0}".format( + self.key_wrapping_data + ) - return "PrivateKey({0}, {1}, {2}, {3})".format( - algorithm, length, value, format_type) + return "PrivateKey({0}, {1}, {2}, {3}, {4})".format( + algorithm, length, value, format_type, key_wrapping_data) def __str__(self): return str(binascii.hexlify(self.value)) @@ -681,6 +1033,8 @@ class PrivateKey(Key): return False elif self.cryptographic_length != other.cryptographic_length: return False + elif self.key_wrapping_data != other.key_wrapping_data: + return False else: return True else: diff --git a/kmip/pie/sqltypes.py b/kmip/pie/sqltypes.py index e5d1413..6b27616 100644 --- a/kmip/pie/sqltypes.py +++ b/kmip/pie/sqltypes.py @@ -107,7 +107,10 @@ class EnumType(types.TypeDecorator): value(Enum): An Enum instance whose integer value is to be stored. dialect(string): SQL dialect """ - return value.value + if value: + return value.value + else: + return -1 def process_result_value(self, value, dialect): """ @@ -120,6 +123,8 @@ class EnumType(types.TypeDecorator): to create the Enum dialect(string): SQL dialect """ + if value == -1: + return None return self._cls(value) diff --git a/kmip/tests/unit/pie/objects/test_key.py b/kmip/tests/unit/pie/objects/test_key.py index d315940..e9d3d19 100644 --- a/kmip/tests/unit/pie/objects/test_key.py +++ b/kmip/tests/unit/pie/objects/test_key.py @@ -94,3 +94,17 @@ class TestKey(TestCase): """ dummy = DummyKey() self.assertFalse(dummy != dummy) + + def test_key_wrapping_data_invalid(self): + """ + Test that the right error is raised when setting the key wrapping + data with an invalid value. + """ + dummy = DummyKey() + args = (dummy, 'key_wrapping_data', 'invalid') + self.assertRaisesRegexp( + TypeError, + "Key wrapping data must be a dictionary.", + setattr, + *args + ) diff --git a/kmip/tests/unit/pie/objects/test_private_key.py b/kmip/tests/unit/pie/objects/test_private_key.py index 63848d0..c58daf8 100644 --- a/kmip/tests/unit/pie/objects/test_private_key.py +++ b/kmip/tests/unit/pie/objects/test_private_key.py @@ -300,9 +300,13 @@ class TestPrivateKey(testtools.TestCase): key = PrivateKey( enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024, enums.KeyFormatType.PKCS_8) - args = "algorithm={0}, length={1}, value={2}, format_type={3}".format( - enums.CryptographicAlgorithm.RSA, 1024, - binascii.hexlify(self.bytes_1024), enums.KeyFormatType.PKCS_8) + args = "{0}, {1}, {2}, {3}, {4}".format( + "algorithm={0}".format(enums.CryptographicAlgorithm.RSA), + "length={0}".format(1024), + "value={0}".format(binascii.hexlify(self.bytes_1024)), + "format_type={0}".format(enums.KeyFormatType.PKCS_8), + "key_wrapping_data={0}".format({}) + ) expected = "PrivateKey({0})".format(args) observed = repr(key) self.assertEqual(expected, observed) @@ -388,6 +392,31 @@ class TestPrivateKey(testtools.TestCase): self.assertFalse(a == b) self.assertFalse(b == a) + def test_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the equality operator returns False when comparing two + PrivateKey objects with different key wrapping data. + """ + a = PrivateKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.PKCS_8, + key_wrapping_data={} + ) + b = PrivateKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.PKCS_8, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + def test_equal_on_type_mismatch(self): """ Test that the equality operator returns False when comparing a @@ -470,6 +499,31 @@ class TestPrivateKey(testtools.TestCase): self.assertTrue(a != b) self.assertTrue(b != a) + def test_not_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the inequality operator returns True when comparing two + PrivateKey objects with different key wrapping data. + """ + a = PrivateKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.PKCS_8, + key_wrapping_data={} + ) + b = PrivateKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.PKCS_8, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + def test_not_equal_on_type_mismatch(self): """ Test that the equality operator returns True when comparing a diff --git a/kmip/tests/unit/pie/objects/test_public_key.py b/kmip/tests/unit/pie/objects/test_public_key.py index d269ea6..f057191 100644 --- a/kmip/tests/unit/pie/objects/test_public_key.py +++ b/kmip/tests/unit/pie/objects/test_public_key.py @@ -198,9 +198,13 @@ class TestPublicKey(testtools.TestCase): key = PublicKey( enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024, enums.KeyFormatType.X_509) - args = "algorithm={0}, length={1}, value={2}, format_type={3}".format( - enums.CryptographicAlgorithm.RSA, 1024, - binascii.hexlify(self.bytes_1024), enums.KeyFormatType.X_509) + args = "{0}, {1}, {2}, {3}, {4}".format( + "algorithm={0}".format(enums.CryptographicAlgorithm.RSA), + "length={0}".format(1024), + "value={0}".format(binascii.hexlify(self.bytes_1024)), + "format_type={0}".format(enums.KeyFormatType.X_509), + "key_wrapping_data={0}".format({}) + ) expected = "PublicKey({0})".format(args) observed = repr(key) self.assertEqual(expected, observed) @@ -286,6 +290,31 @@ class TestPublicKey(testtools.TestCase): self.assertFalse(a == b) self.assertFalse(b == a) + def test_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the equality operator returns False when comparing two + PublicKey objects with different key wrapping data. + """ + a = PublicKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.X_509, + key_wrapping_data={} + ) + b = PublicKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.X_509, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + def test_equal_on_type_mismatch(self): """ Test that the equality operator returns False when comparing a @@ -368,6 +397,31 @@ class TestPublicKey(testtools.TestCase): self.assertTrue(a != b) self.assertTrue(b != a) + def test_not_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the inequality operator returns True when comparing two + PublicKey objects with different key wrapping data. + """ + a = PublicKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.X_509, + key_wrapping_data={} + ) + b = PublicKey( + enums.CryptographicAlgorithm.RSA, + 1024, + self.bytes_1024, + enums.KeyFormatType.X_509, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + def test_not_equal_on_type_mismatch(self): """ Test that the equality operator returns True when comparing a diff --git a/kmip/tests/unit/pie/objects/test_symmetric_key.py b/kmip/tests/unit/pie/objects/test_symmetric_key.py index 441996c..ccc4cdf 100644 --- a/kmip/tests/unit/pie/objects/test_symmetric_key.py +++ b/kmip/tests/unit/pie/objects/test_symmetric_key.py @@ -179,11 +179,17 @@ class TestSymmetricKey(testtools.TestCase): Test that repr can be applied to a SymmetricKey. """ key = SymmetricKey( - enums.CryptographicAlgorithm.AES, 128, self.bytes_128a) + enums.CryptographicAlgorithm.AES, + 128, + self.bytes_128a + ) - args = "algorithm={0}, length={1}, value={2}".format( - enums.CryptographicAlgorithm.AES, 128, - binascii.hexlify(self.bytes_128a)) + args = "{0}, {1}, {2}, {3}".format( + "algorithm={0}".format(enums.CryptographicAlgorithm.AES), + "length={0}".format(128), + "value={0}".format(binascii.hexlify(self.bytes_128a)), + "key_wrapping_data={0}".format({}) + ) expected = "SymmetricKey({0})".format(args) observed = repr(key) @@ -253,6 +259,29 @@ class TestSymmetricKey(testtools.TestCase): self.assertFalse(a == b) self.assertFalse(b == a) + def test_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the equality operator returns False when comparing two + SymmetricKey objects with different key wrapping data. + """ + a = SymmetricKey( + enums.CryptographicAlgorithm.AES, + 128, + self.bytes_128a, + key_wrapping_data={} + ) + b = SymmetricKey( + enums.CryptographicAlgorithm.AES, + 128, + self.bytes_128a, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertFalse(a == b) + self.assertFalse(b == a) + def test_equal_on_type_mismatch(self): """ Test that the equality operator returns False when comparing a @@ -317,6 +346,29 @@ class TestSymmetricKey(testtools.TestCase): self.assertTrue(a != b) self.assertTrue(b != a) + def test_not_equal_on_not_equal_key_wrapping_data(self): + """ + Test that the inequality operator returns True when comparing two + SymmetricKey objects with different key wrapping data. + """ + a = SymmetricKey( + enums.CryptographicAlgorithm.AES, + 128, + self.bytes_128a, + key_wrapping_data={} + ) + b = SymmetricKey( + enums.CryptographicAlgorithm.AES, + 128, + self.bytes_128a, + key_wrapping_data={ + 'wrapping_method': enums.WrappingMethod.ENCRYPT + } + ) + + self.assertTrue(a != b) + self.assertTrue(b != a) + def test_not_equal_on_type_mismatch(self): """ Test that the equality operator returns True when comparing a diff --git a/kmip/tests/unit/pie/test_factory.py b/kmip/tests/unit/pie/test_factory.py index 3939289..04008c5 100644 --- a/kmip/tests/unit/pie/test_factory.py +++ b/kmip/tests/unit/pie/test_factory.py @@ -562,3 +562,103 @@ class TestObjectFactory(testtools.TestCase): self.assertEqual(key.cryptographic_length, length) self.assertEqual(key.key_format_type, format_type) self.assertEqual(key.value, value) + + def test_build_cryptographic_parameters(self): + cryptographic_parameters = cobjects.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC, + padding_method=enums.PaddingMethod.ANSI_X923, + hashing_algorithm=enums.HashingAlgorithm.SHA_256, + key_role_type=enums.KeyRoleType.KEK, + digital_signature_algorithm=enums.DigitalSignatureAlgorithm. + DSA_WITH_SHA1, + cryptographic_algorithm=enums.CryptographicAlgorithm.AES, + random_iv=True, + iv_length=32, + tag_length=33, + fixed_field_length=34, + invocation_field_length=35, + counter_length=36, + initial_counter_value=0 + ) + + result = self.factory._build_cryptographic_parameters( + cryptographic_parameters + ) + self.assertIsInstance(result, dict) + self.assertEqual( + enums.BlockCipherMode.CBC, + result.get('block_cipher_mode') + ) + self.assertEqual( + enums.PaddingMethod.ANSI_X923, + result.get('padding_method') + ) + self.assertEqual( + enums.HashingAlgorithm.SHA_256, + result.get('hashing_algorithm') + ) + self.assertEqual( + enums.KeyRoleType.KEK, + result.get('key_role_type') + ) + self.assertEqual( + enums.DigitalSignatureAlgorithm.DSA_WITH_SHA1, + result.get('digital_signature_algorithm') + ) + self.assertEqual( + enums.CryptographicAlgorithm.AES, + result.get('cryptographic_algorithm') + ) + self.assertEqual(True, result.get('random_iv')) + self.assertEqual(32, result.get('iv_length')) + self.assertEqual(33, result.get('tag_length')) + self.assertEqual(34, result.get('fixed_field_length')) + self.assertEqual(35, result.get('invocation_field_length')) + self.assertEqual(36, result.get('counter_length')) + self.assertEqual(0, result.get('initial_counter_value')) + + def test_build_key_wrapping_data(self): + key_wrapping_data = cobjects.KeyWrappingData( + wrapping_method=enums.WrappingMethod.ENCRYPT, + encryption_key_information=cobjects.EncryptionKeyInformation( + unique_identifier='1', + cryptographic_parameters=cobjects.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CBC + ) + ), + mac_signature_key_information=cobjects.MACSignatureKeyInformation( + unique_identifier='2', + cryptographic_parameters=cobjects.CryptographicParameters( + block_cipher_mode=enums.BlockCipherMode.CCM + ) + ), + mac_signature=b'\x01', + iv_counter_nonce=b'\x02', + encoding_option=enums.EncodingOption.NO_ENCODING + ) + + result = self.factory._build_key_wrapping_data( + key_wrapping_data + ) + self.assertIsInstance(result, dict) + self.assertEqual( + enums.WrappingMethod.ENCRYPT, + result.get('wrapping_method') + ) + self.assertIsInstance(result.get('encryption_key_information'), dict) + eki = result.get('encryption_key_information') + self.assertEqual('1', eki.get('unique_identifier')) + self.assertIsInstance(eki.get('cryptographic_parameters'), dict) + self.assertIsInstance( + result.get('mac_signature_key_information'), + dict + ) + mski = result.get('mac_signature_key_information') + self.assertEqual('2', mski.get('unique_identifier')) + self.assertIsInstance(mski.get('cryptographic_parameters'), dict) + self.assertEqual(b'\x01', result.get('mac_signature')) + self.assertEqual(b'\x02', result.get('iv_counter_nonce')) + self.assertEqual( + enums.EncodingOption.NO_ENCODING, + result.get('encoding_option') + )