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.
This commit is contained in:
Peter Hamilton 2017-09-30 16:22:52 -04:00
parent aa798d939c
commit fc86e1bef4
8 changed files with 736 additions and 30 deletions

View File

@ -85,9 +85,17 @@ class ObjectFactory:
length = key.key_block.cryptographic_length.value length = key.key_block.cryptographic_length.value
value = key.key_block.key_value.key_material.value value = key.key_block.key_value.key_material.value
format_type = key.key_block.key_format_type.value format_type = key.key_block.key_format_type.value
key_wrapping_data = key.key_block.key_wrapping_data
if cls is pobjects.SymmetricKey: 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: if key.key_format_type != format_type:
raise TypeError( raise TypeError(
"core key format type not compatible with Pie " "core key format type not compatible with Pie "
@ -96,7 +104,15 @@ class ObjectFactory:
else: else:
return key return key
else: 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): def _build_pie_secret_data(self, secret):
secret_data_type = secret.secret_data_type.value secret_data_type = secret.secret_data_type.value
@ -117,6 +133,11 @@ class ObjectFactory:
key_material = cobjects.KeyMaterial(value) key_material = cobjects.KeyMaterial(value)
key_value = cobjects.KeyValue(key_material) 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_block = cobjects.KeyBlock(
key_format_type=misc.KeyFormatType(format_type), key_format_type=misc.KeyFormatType(format_type),
key_compression_type=None, key_compression_type=None,
@ -124,7 +145,8 @@ class ObjectFactory:
cryptographic_algorithm=attributes.CryptographicAlgorithm( cryptographic_algorithm=attributes.CryptographicAlgorithm(
algorithm), algorithm),
cryptographic_length=attributes.CryptographicLength(length), cryptographic_length=attributes.CryptographicLength(length),
key_wrapping_data=None) key_wrapping_data=key_wrapping_data
)
return cls(key_block) return cls(key_block)
@ -155,3 +177,54 @@ class ObjectFactory:
opaque_data_type = secrets.OpaqueObject.OpaqueDataType(opaque_type) opaque_data_type = secrets.OpaqueObject.OpaqueDataType(opaque_type)
opaque_data_value = secrets.OpaqueObject.OpaqueDataValue(value) opaque_data_value = secrets.OpaqueObject.OpaqueDataValue(value)
return secrets.OpaqueObject(opaque_data_type, opaque_data_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

View File

@ -15,6 +15,7 @@
from abc import abstractmethod from abc import abstractmethod
from sqlalchemy import Column, event, ForeignKey, Integer, String, VARBINARY from sqlalchemy import Column, event, ForeignKey, Integer, String, VARBINARY
from sqlalchemy import Boolean
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -212,6 +213,8 @@ class Key(CryptographicObject):
cryptographic_length: An int defining the length of the key in bits. cryptographic_length: An int defining the length of the key in bits.
key_format_type: A KeyFormatType enumeration defining the format of key_format_type: A KeyFormatType enumeration defining the format of
the key value. the key value.
key_wrapping_data: A dictionary containing key wrapping data
settings, describing how the key value has been wrapped.
""" """
__tablename__ = 'keys' __tablename__ = 'keys'
@ -224,6 +227,167 @@ class Key(CryptographicObject):
key_format_type = Column( key_format_type = Column(
'key_format_type', sql.EnumType(enums.KeyFormatType)) '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__ = { __mapper_args__ = {
'polymorphic_identity': 'Key' 'polymorphic_identity': 'Key'
} }
@ -232,15 +396,21 @@ class Key(CryptographicObject):
} }
@abstractmethod @abstractmethod
def __init__(self): def __init__(self, key_wrapping_data=None):
""" """
Create a Key object. 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__() super(Key, self).__init__()
self.cryptographic_algorithm = None self.cryptographic_algorithm = None
self.cryptographic_length = None self.cryptographic_length = None
self.key_format_type = None self.key_format_type = None
self.key_wrapping_data = key_wrapping_data
# All remaining attributes are not considered part of the public API # All remaining attributes are not considered part of the public API
# and are subject to change. # and are subject to change.
@ -250,6 +420,145 @@ class Key(CryptographicObject):
# unsupported by kmip.core # unsupported by kmip.core
self._usage_limits = None 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): class SymmetricKey(Key):
""" """
@ -267,6 +576,8 @@ class SymmetricKey(Key):
cryptographic_usage_masks: The list of usage mask flags for cryptographic_usage_masks: The list of usage mask flags for
SymmetricKey application. SymmetricKey application.
names: The string names of the SymmetricKey. 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' __tablename__ = 'symmetric_keys'
@ -282,7 +593,7 @@ class SymmetricKey(Key):
} }
def __init__(self, algorithm, length, value, masks=None, def __init__(self, algorithm, length, value, masks=None,
name='Symmetric Key'): name='Symmetric Key', key_wrapping_data=None):
""" """
Create a SymmetricKey. Create a SymmetricKey.
@ -295,8 +606,13 @@ class SymmetricKey(Key):
how the key will be used. Optional, defaults to None. how the key will be used. Optional, defaults to None.
name(string): The string name of the key. Optional, defaults to name(string): The string name of the key. Optional, defaults to
'Symmetric Key'. '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._object_type = enums.ObjectType.SYMMETRIC_KEY
self.key_format_type = enums.KeyFormatType.RAW self.key_format_type = enums.KeyFormatType.RAW
@ -353,17 +669,29 @@ class SymmetricKey(Key):
raise TypeError("key name {0} must be a string".format( raise TypeError("key name {0} must be a string".format(
position)) position))
if (len(self.value) * 8) != self.cryptographic_length: if not self.key_wrapping_data:
msg = "key length ({0}) not equal to key value length ({1})" if (len(self.value) * 8) != self.cryptographic_length:
msg = msg.format(self.cryptographic_length, len(self.value) * 8) msg = "key length ({0}) not equal to key value length ({1})"
raise ValueError(msg) msg = msg.format(
self.cryptographic_length,
len(self.value) * 8
)
raise ValueError(msg)
def __repr__(self): def __repr__(self):
algorithm = "algorithm={0}".format(self.cryptographic_algorithm) algorithm = "algorithm={0}".format(self.cryptographic_algorithm)
length = "length={0}".format(self.cryptographic_length) length = "length={0}".format(self.cryptographic_length)
value = "value={0}".format(binascii.hexlify(self.value)) 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): def __str__(self):
return str(binascii.hexlify(self.value)) return str(binascii.hexlify(self.value))
@ -376,6 +704,8 @@ class SymmetricKey(Key):
return False return False
elif self.cryptographic_length != other.cryptographic_length: elif self.cryptographic_length != other.cryptographic_length:
return False return False
elif self.key_wrapping_data != other.key_wrapping_data:
return False
else: else:
return True return True
else: else:
@ -408,6 +738,8 @@ class PublicKey(Key):
cryptographic_usage_masks: The list of usage mask flags for PublicKey cryptographic_usage_masks: The list of usage mask flags for PublicKey
application. application.
names: The list of string names of the PublicKey. 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' __tablename__ = 'public_keys'
@ -424,7 +756,7 @@ class PublicKey(Key):
def __init__(self, algorithm, length, value, def __init__(self, algorithm, length, value,
format_type=enums.KeyFormatType.X_509, masks=None, format_type=enums.KeyFormatType.X_509, masks=None,
name='Public Key'): name='Public Key', key_wrapping_data=None):
""" """
Create a PublicKey. Create a PublicKey.
@ -439,8 +771,13 @@ class PublicKey(Key):
defining how the key will be used. Optional, defaults to None. defining how the key will be used. Optional, defaults to None.
name(string): The string name of the key. Optional, defaults to name(string): The string name of the key. Optional, defaults to
'Public Key'. '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._object_type = enums.ObjectType.PUBLIC_KEY
self._valid_formats = [ self._valid_formats = [
@ -512,9 +849,12 @@ class PublicKey(Key):
length = "length={0}".format(self.cryptographic_length) length = "length={0}".format(self.cryptographic_length)
value = "value={0}".format(binascii.hexlify(self.value)) value = "value={0}".format(binascii.hexlify(self.value))
format_type = "format_type={0}".format(self.key_format_type) 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( return "PublicKey({0}, {1}, {2}, {3}, {4})".format(
algorithm, length, value, format_type) algorithm, length, value, format_type, key_wrapping_data)
def __str__(self): def __str__(self):
return str(binascii.hexlify(self.value)) return str(binascii.hexlify(self.value))
@ -529,6 +869,8 @@ class PublicKey(Key):
return False return False
elif self.cryptographic_length != other.cryptographic_length: elif self.cryptographic_length != other.cryptographic_length:
return False return False
elif self.key_wrapping_data != other.key_wrapping_data:
return False
else: else:
return True return True
else: else:
@ -562,6 +904,8 @@ class PrivateKey(Key):
application. Optional, defaults to None. application. Optional, defaults to None.
names: The list of string names of the PrivateKey. Optional, defaults names: The list of string names of the PrivateKey. Optional, defaults
to 'Private Key'. 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' __tablename__ = 'private_keys'
@ -577,7 +921,7 @@ class PrivateKey(Key):
} }
def __init__(self, algorithm, length, value, format_type, masks=None, def __init__(self, algorithm, length, value, format_type, masks=None,
name='Private Key'): name='Private Key', key_wrapping_data=None):
""" """
Create a PrivateKey. Create a PrivateKey.
@ -591,8 +935,13 @@ class PrivateKey(Key):
masks(list): A list of CryptographicUsageMask enumerations masks(list): A list of CryptographicUsageMask enumerations
defining how the key will be used. defining how the key will be used.
name(string): The string name of the key. 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._object_type = enums.ObjectType.PRIVATE_KEY
self._valid_formats = [ self._valid_formats = [
@ -664,9 +1013,12 @@ class PrivateKey(Key):
length = "length={0}".format(self.cryptographic_length) length = "length={0}".format(self.cryptographic_length)
value = "value={0}".format(binascii.hexlify(self.value)) value = "value={0}".format(binascii.hexlify(self.value))
format_type = "format_type={0}".format(self.key_format_type) 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( return "PrivateKey({0}, {1}, {2}, {3}, {4})".format(
algorithm, length, value, format_type) algorithm, length, value, format_type, key_wrapping_data)
def __str__(self): def __str__(self):
return str(binascii.hexlify(self.value)) return str(binascii.hexlify(self.value))
@ -681,6 +1033,8 @@ class PrivateKey(Key):
return False return False
elif self.cryptographic_length != other.cryptographic_length: elif self.cryptographic_length != other.cryptographic_length:
return False return False
elif self.key_wrapping_data != other.key_wrapping_data:
return False
else: else:
return True return True
else: else:

View File

@ -107,7 +107,10 @@ class EnumType(types.TypeDecorator):
value(Enum): An Enum instance whose integer value is to be stored. value(Enum): An Enum instance whose integer value is to be stored.
dialect(string): SQL dialect dialect(string): SQL dialect
""" """
return value.value if value:
return value.value
else:
return -1
def process_result_value(self, value, dialect): def process_result_value(self, value, dialect):
""" """
@ -120,6 +123,8 @@ class EnumType(types.TypeDecorator):
to create the Enum to create the Enum
dialect(string): SQL dialect dialect(string): SQL dialect
""" """
if value == -1:
return None
return self._cls(value) return self._cls(value)

View File

@ -94,3 +94,17 @@ class TestKey(TestCase):
""" """
dummy = DummyKey() dummy = DummyKey()
self.assertFalse(dummy != dummy) 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
)

View File

@ -300,9 +300,13 @@ class TestPrivateKey(testtools.TestCase):
key = PrivateKey( key = PrivateKey(
enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024, enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024,
enums.KeyFormatType.PKCS_8) enums.KeyFormatType.PKCS_8)
args = "algorithm={0}, length={1}, value={2}, format_type={3}".format( args = "{0}, {1}, {2}, {3}, {4}".format(
enums.CryptographicAlgorithm.RSA, 1024, "algorithm={0}".format(enums.CryptographicAlgorithm.RSA),
binascii.hexlify(self.bytes_1024), enums.KeyFormatType.PKCS_8) "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) expected = "PrivateKey({0})".format(args)
observed = repr(key) observed = repr(key)
self.assertEqual(expected, observed) self.assertEqual(expected, observed)
@ -388,6 +392,31 @@ class TestPrivateKey(testtools.TestCase):
self.assertFalse(a == b) self.assertFalse(a == b)
self.assertFalse(b == a) 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): def test_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns False when comparing a Test that the equality operator returns False when comparing a
@ -470,6 +499,31 @@ class TestPrivateKey(testtools.TestCase):
self.assertTrue(a != b) self.assertTrue(a != b)
self.assertTrue(b != a) 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): def test_not_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns True when comparing a Test that the equality operator returns True when comparing a

View File

@ -198,9 +198,13 @@ class TestPublicKey(testtools.TestCase):
key = PublicKey( key = PublicKey(
enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024, enums.CryptographicAlgorithm.RSA, 1024, self.bytes_1024,
enums.KeyFormatType.X_509) enums.KeyFormatType.X_509)
args = "algorithm={0}, length={1}, value={2}, format_type={3}".format( args = "{0}, {1}, {2}, {3}, {4}".format(
enums.CryptographicAlgorithm.RSA, 1024, "algorithm={0}".format(enums.CryptographicAlgorithm.RSA),
binascii.hexlify(self.bytes_1024), enums.KeyFormatType.X_509) "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) expected = "PublicKey({0})".format(args)
observed = repr(key) observed = repr(key)
self.assertEqual(expected, observed) self.assertEqual(expected, observed)
@ -286,6 +290,31 @@ class TestPublicKey(testtools.TestCase):
self.assertFalse(a == b) self.assertFalse(a == b)
self.assertFalse(b == a) 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): def test_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns False when comparing a Test that the equality operator returns False when comparing a
@ -368,6 +397,31 @@ class TestPublicKey(testtools.TestCase):
self.assertTrue(a != b) self.assertTrue(a != b)
self.assertTrue(b != a) 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): def test_not_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns True when comparing a Test that the equality operator returns True when comparing a

View File

@ -179,11 +179,17 @@ class TestSymmetricKey(testtools.TestCase):
Test that repr can be applied to a SymmetricKey. Test that repr can be applied to a SymmetricKey.
""" """
key = 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( args = "{0}, {1}, {2}, {3}".format(
enums.CryptographicAlgorithm.AES, 128, "algorithm={0}".format(enums.CryptographicAlgorithm.AES),
binascii.hexlify(self.bytes_128a)) "length={0}".format(128),
"value={0}".format(binascii.hexlify(self.bytes_128a)),
"key_wrapping_data={0}".format({})
)
expected = "SymmetricKey({0})".format(args) expected = "SymmetricKey({0})".format(args)
observed = repr(key) observed = repr(key)
@ -253,6 +259,29 @@ class TestSymmetricKey(testtools.TestCase):
self.assertFalse(a == b) self.assertFalse(a == b)
self.assertFalse(b == a) 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): def test_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns False when comparing a Test that the equality operator returns False when comparing a
@ -317,6 +346,29 @@ class TestSymmetricKey(testtools.TestCase):
self.assertTrue(a != b) self.assertTrue(a != b)
self.assertTrue(b != a) 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): def test_not_equal_on_type_mismatch(self):
""" """
Test that the equality operator returns True when comparing a Test that the equality operator returns True when comparing a

View File

@ -562,3 +562,103 @@ class TestObjectFactory(testtools.TestCase):
self.assertEqual(key.cryptographic_length, length) self.assertEqual(key.cryptographic_length, length)
self.assertEqual(key.key_format_type, format_type) self.assertEqual(key.key_format_type, format_type)
self.assertEqual(key.value, value) 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')
)