def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the GetAttributeList response payload to a stream. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField: Raised if the unique identifier or attribute name are not defined. """ local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The GetAttributeList response payload is missing the unique " "identifier field.") if self._attribute_names: if kmip_version < enums.KMIPVersion.KMIP_2_0: for attribute_name in self._attribute_names: attribute_name.write(local_buffer, kmip_version=kmip_version) else: # NOTE (ph) This approach simplifies backwards compatible # issues but limits easy support for Attribute # Reference structures going forward, specifically # limiting the use of VendorIdentification for # custom attributes. If custom attributes need to # be retrieved using the GetAttributeList operation # for KMIP 2.0 applications this code will need to # change. for attribute_name in self._attribute_names: t = enums.convert_attribute_name_to_tag( attribute_name.value) e = primitives.Enumeration( enums.Tags, value=t, tag=enums.Tags.ATTRIBUTE_REFERENCE) e.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The GetAttributeList response payload is missing the " "attribute names field.") self.length = local_buffer.length() super(GetAttributeListResponsePayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def _build_core_object(self, obj): try: object_type = obj._object_type except Exception: raise exceptions.InvalidField( "Cannot build an unsupported object type." ) value = {} if object_type == enums.ObjectType.CERTIFICATE: value = { 'certificate_type': obj.certificate_type, 'certificate_value': obj.value } elif object_type == enums.ObjectType.SYMMETRIC_KEY: value = { 'cryptographic_algorithm': obj.cryptographic_algorithm, 'cryptographic_length': obj.cryptographic_length, 'key_format_type': obj.key_format_type, 'key_value': obj.value } elif object_type == enums.ObjectType.PUBLIC_KEY: value = { 'cryptographic_algorithm': obj.cryptographic_algorithm, 'cryptographic_length': obj.cryptographic_length, 'key_format_type': obj.key_format_type, 'key_value': obj.value } elif object_type == enums.ObjectType.PRIVATE_KEY: value = { 'cryptographic_algorithm': obj.cryptographic_algorithm, 'cryptographic_length': obj.cryptographic_length, 'key_format_type': obj.key_format_type, 'key_value': obj.value } elif object_type == enums.ObjectType.SECRET_DATA: value = { 'key_format_type': enums.KeyFormatType.OPAQUE, 'key_value': obj.value, 'secret_data_type': obj.data_type } elif object_type == enums.ObjectType.OPAQUE_DATA: value = { 'opaque_data_type': obj.opaque_type, 'opaque_data_value': obj.value } else: name = object_type.name raise exceptions.InvalidField( "The {0} object type is not supported.".format( ''.join( [x.capitalize() for x in name.split('_')] ) ) ) secret_factory = secrets.SecretFactory() return secret_factory.create(object_type, value)
def create_symmetric_key(self, algorithm, length): """ Create a symmetric key. Args: algorithm(CryptographicAlgorithm): An enumeration specifying the algorithm for which the created key will be compliant. length(int): The length of the key to be created. This value must be compliant with the constraints of the provided algorithm. Returns: dict: A dictionary containing the key data, with the following key/value fields: * value - the bytes of the key * format - a KeyFormatType enumeration for the bytes format Raises: InvalidField: Raised when the algorithm is unsupported or the length is incompatible with the algorithm. CryptographicFailure: Raised when the key generation process fails. Example: >>> engine = CryptographyEngine() >>> key = engine.create_symmetric_key( ... CryptographicAlgorithm.AES, 256) """ if algorithm not in self._symmetric_key_algorithms.keys(): raise exceptions.InvalidField( "The cryptographic algorithm {0} is not a supported symmetric " "key algorithm.".format(algorithm) ) cryptography_algorithm = self._symmetric_key_algorithms.get(algorithm) if length not in cryptography_algorithm.key_sizes: raise exceptions.InvalidField( "The cryptographic length ({0}) is not valid for " "the cryptographic algorithm ({1}).".format( length, algorithm.name ) ) self.logger.info( "Generating a {0} symmetric key with length: {1}".format( algorithm.name, length ) ) key_bytes = os.urandom(length // 8) try: cryptography_algorithm(key_bytes) except Exception as e: self.logger.exception(e) raise exceptions.CryptographicFailure( "Invalid bytes for the provided cryptographic algorithm.") return {'value': key_bytes, 'format': enums.KeyFormatType.RAW}
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the CreateKeyPair response payload to a buffer. Args: output_buffer (stream): A data buffer in which to encode object data, supporting a write method. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField: Raised if the private key unique identifier or the public key unique identifier is not defined. """ local_buffer = utils.BytearrayStream() if self._private_key_unique_identifier: self._private_key_unique_identifier.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The CreateKeyPair response payload is missing the private " "key unique identifier field." ) if self._public_key_unique_identifier: self._public_key_unique_identifier.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The CreateKeyPair response payload is missing the public " "key unique identifier field." ) if self._private_key_template_attribute: self._private_key_template_attribute.write( local_buffer, kmip_version=kmip_version ) if self._public_key_template_attribute: self._public_key_template_attribute.write( local_buffer, kmip_version=kmip_version ) self.length = local_buffer.length() super(CreateKeyPairResponsePayload, self).write( output_buffer, kmip_version=kmip_version ) output_buffer.write(local_buffer.buffer)
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the Create request payload to a buffer. Args: output_buffer (stream): A data buffer in which to encode object data, supporting a write method. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField: Raised if the object type attribute or template attribute is not defined. """ local_buffer = utils.BytearrayStream() if self._object_type: self._object_type.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The Create request payload is missing the object type field.") if kmip_version < enums.KMIPVersion.KMIP_2_0: if self._template_attribute: self._template_attribute.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The Create request payload is missing the template " "attribute field.") else: # NOTE (ph) For now, leave attributes natively in TemplateAttribute # form and just convert to the KMIP 2.0 Attributes form as needed # for encoding/decoding purposes. Changing the payload to require # the new Attributes structure will trigger a bunch of second-order # effects across the client and server codebases that is beyond # the scope of updating the Create payloads to support KMIP 2.0. if self._template_attribute: attributes = objects.convert_template_attribute_to_attributes( self._template_attribute) attributes.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The Create request payload is missing the template " "attribute field.") if kmip_version >= enums.KMIPVersion.KMIP_2_0: if self._protection_storage_masks: self._protection_storage_masks.write(local_buffer, kmip_version=kmip_version) self.length = local_buffer.length() super(CreateRequestPayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def _set_attribute_on_managed_object(self, managed_object, attribute): """ Set the attribute value on the kmip.pie managed object. """ attribute_name = attribute[0] attribute_value = attribute[1] if self._attribute_policy.is_attribute_multivalued(attribute_name): if attribute_name == 'Name': managed_object.names.extend( [x.name_value.value for x in attribute_value] ) for name in managed_object.names: if managed_object.names.count(name) > 1: raise exceptions.InvalidField( "Cannot set duplicate name values." ) else: # TODO (peterhamilton) Remove when all attributes are supported raise exceptions.InvalidField( "The {0} attribute is unsupported.".format(attribute_name) ) else: field = None value = attribute_value.value if attribute_name == 'Cryptographic Algorithm': field = 'cryptographic_algorithm' elif attribute_name == 'Cryptographic Length': field = 'cryptographic_length' elif attribute_name == 'Cryptographic Usage Mask': field = 'cryptographic_usage_masks' value = list() for e in enums.CryptographicUsageMask: if e.value & attribute_value.value: value.append(e) if field: existing_value = getattr(managed_object, field) if existing_value: if existing_value != value: raise exceptions.InvalidField( "Cannot overwrite the {0} attribute.".format( attribute_name ) ) else: setattr(managed_object, field, value) else: # TODO (peterhamilton) Remove when all attributes are supported raise exceptions.InvalidField( "The {0} attribute is unsupported.".format(attribute_name) )
def _process_register(self, payload): self._logger.info("Processing operation: Register") object_type = payload.object_type.value template_attribute = payload.template_attribute if self._object_map.get(object_type) is None: name = object_type.name raise exceptions.InvalidField( "The {0} object type is not supported.".format(''.join( [x.capitalize() for x in name.split('_')]))) if payload.secret: secret = payload.secret else: # TODO (peterhamilton) It is possible to register 'empty' secrets # like Private Keys. For now, that feature is not supported. raise exceptions.InvalidField( "Cannot register a secret in absentia.") object_attributes = {} if template_attribute: object_attributes = self._process_template_attribute( template_attribute) managed_object_factory = factory.ObjectFactory() managed_object = managed_object_factory.convert(secret) managed_object.names = [] self._set_attributes_on_managed_object(managed_object, object_attributes) # TODO (peterhamilton) Set additional server-only attributes. managed_object._owner = self._client_identity self._data_session.add(managed_object) # NOTE (peterhamilton) SQLAlchemy will *not* assign an ID until # commit is called. This makes future support for UNDO problematic. self._data_session.commit() self._logger.info("Registered a {0} with ID: {1}".format( ''.join([x.capitalize() for x in object_type.name.split('_')]), managed_object.unique_identifier)) response_payload = register.RegisterResponsePayload( unique_identifier=attributes.UniqueIdentifier( str(managed_object.unique_identifier))) self._id_placeholder = str(managed_object.unique_identifier) return response_payload
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the GetAttributes response payload to a stream. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. """ local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The GetAttributes response payload is missing the unique " "identifier field." ) if kmip_version < enums.KMIPVersion.KMIP_2_0: for attribute in self._attributes: attribute.write(local_buffer, kmip_version=kmip_version) else: if self._attributes: # TODO (ph) Add a new utility to avoid using TemplateAttributes template_attribute = objects.TemplateAttribute( attributes=self.attributes ) attributes = objects.convert_template_attribute_to_attributes( template_attribute ) attributes.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The GetAttributes response payload is missing the " "attributes list." ) self.length = local_buffer.length() super(GetAttributesResponsePayload, self).write( output_buffer, kmip_version=kmip_version ) output_buffer.write(local_buffer.buffer)
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the DeleteAttribute request payload to a stream. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField """ local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(local_buffer, kmip_version=kmip_version) if kmip_version < enums.KMIPVersion.KMIP_2_0: if self._attribute_name: self._attribute_name.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The DeleteAttribute request payload is missing the " "attribute name field.") if self._attribute_index: self._attribute_index.write(local_buffer, kmip_version=kmip_version) else: if self._current_attribute == self._attribute_reference: raise exceptions.InvalidField( "The DeleteAttribute request payload is missing either " "the current attribute or the attribute reference field.") if self._current_attribute: self._current_attribute.write(local_buffer, kmip_version=kmip_version) if self._attribute_reference: self._attribute_reference.write(local_buffer, kmip_version=kmip_version) self.length = local_buffer.length() super(DeleteAttributeRequestPayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the DeleteAttribute response payload to a buffer. Args: output_buffer (buffer): A data buffer in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (enum): A KMIPVersion enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField: Raised if a required field is missing from the payload object. """ local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The DeleteAttribute response payload is missing the unique " "identifier field." ) if kmip_version < enums.KMIPVersion.KMIP_2_0: if self._attribute: self._attribute.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The DeleteAttribute response payload is missing the " "attribute field." ) self.length = local_buffer.length() super(DeleteAttributeResponsePayload, self).write( output_buffer, kmip_version=kmip_version ) output_buffer.write(local_buffer.buffer)
def _get_object_type(self, unique_identifier): try: object_type = self._data_session.query( objects.ManagedObject._object_type).filter( objects.ManagedObject.unique_identifier == unique_identifier).one()[0] except exc.NoResultFound as e: self._logger.warning( "Could not identify object type for object: {0}".format( unique_identifier)) self._logger.exception(e) raise exceptions.ItemNotFound( "Could not locate object: {0}".format(unique_identifier)) except exc.MultipleResultsFound as e: self._logger.warning( "Multiple objects found for ID: {0}".format(unique_identifier)) raise e class_type = self._object_map.get(object_type) if class_type is None: name = object_type.name raise exceptions.InvalidField( "The {0} object type is not supported.".format(''.join( [x.capitalize() for x in name.split('_')]))) return class_type
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the DeriveKey response payload to a stream. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: ValueError: Raised if the data attribute is not defined. """ local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The DeriveKey response payload is missing the unique " "identifier field.") if kmip_version < enums.KMIPVersion.KMIP_2_0: if self._template_attribute: self._template_attribute.write(local_buffer, kmip_version=kmip_version) self.length = local_buffer.length() super(DeriveKeyResponsePayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def write(self, ostream, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the GetAttributes response payload to a stream. Args: ostream (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. """ tstream = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(tstream, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The GetAttributes response unique identifier is required.") for attribute in self._attributes: attribute.write(tstream, kmip_version=kmip_version) self.length = tstream.length() super(GetAttributesResponsePayload, self).write(ostream, kmip_version=kmip_version) ostream.write(tstream.buffer)
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the QueryRequestPayload object to a stream. Args: output_buffer (Stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: InvalidField: Raised if the query functions are not defined. """ local_buffer = utils.BytearrayStream() if self._query_functions: for query_function in self._query_functions: query_function.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The Query request payload is missing the query functions " "field.") self.length = local_buffer.length() super(QueryRequestPayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def write(self, ostream, kmip_version=enums.KMIPVersion.KMIP_1_0): tstream = BytearrayStream() if self._unique_identifier is not None: self._unique_identifier.write(tstream, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The mac response unique identifier is required") if self._mac_data is not None: self._mac_data.write(tstream, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The mac response mac data is required") self.length = tstream.length() super(MACResponsePayload, self).write(ostream, kmip_version=kmip_version) ostream.write(tstream.buffer)
def _process_template_attribute(self, template_attribute): """ Given a kmip.core TemplateAttribute object, extract the attribute value data into a usable dictionary format. """ attributes = {} if len(template_attribute.names) > 0: raise exceptions.ItemNotFound( "Attribute templates are not supported." ) for attribute in template_attribute.attributes: name = attribute.attribute_name.value if not self._attribute_policy.is_attribute_supported(name): raise exceptions.InvalidField( "The {0} attribute is unsupported.".format(name) ) if self._attribute_policy.is_attribute_multivalued(name): values = attributes.get(name, list()) if (not attribute.attribute_index) and len(values) > 0: raise exceptions.InvalidField( "Attribute index missing from multivalued attribute." ) values.append(attribute.attribute_value) attributes.update([(name, values)]) else: if attribute.attribute_index: if attribute.attribute_index.value != 0: raise exceptions.InvalidField( "Non-zero attribute index found for " "single-valued attribute." ) value = attributes.get(name, None) if value: raise exceptions.IndexOutOfBounds( "Cannot set multiple instances of the " "{0} attribute.".format(name) ) else: attributes.update([(name, attribute.attribute_value)]) return attributes
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the Locate request payload to a buffer. Args: output_buffer (stream): A data buffer in which to encode object data, supporting a write method. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. """ local_buffer = utils.BytearrayStream() if self._maximum_items: self._maximum_items.write(local_buffer, kmip_version=kmip_version) if self._offset_items: self._offset_items.write(local_buffer, kmip_version=kmip_version) if self._storage_status_mask: self._storage_status_mask.write(local_buffer, kmip_version=kmip_version) if self._object_group_member: self._object_group_member.write(local_buffer, kmip_version=kmip_version) if kmip_version < enums.KMIPVersion.KMIP_2_0: if self._attributes: for attribute in self.attributes: attribute.write(local_buffer, kmip_version=kmip_version) else: if self._attributes: # TODO (ph) Add a new utility to avoid using TemplateAttributes template_attribute = objects.TemplateAttribute( attributes=self.attributes) attributes = objects.convert_template_attribute_to_attributes( template_attribute) attributes.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The Locate request payload is missing the attributes " "list.") self.length = local_buffer.length() super(LocateRequestPayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def write(self, ostream): tstream = BytearrayStream() if self._unique_identifier is not None: self._unique_identifier.write(tstream) if self._cryptographic_parameters is not None: self._cryptographic_parameters.write(tstream) if self._data is not None: self.data.write(tstream) else: raise exceptions.InvalidField( "The mac request data is required" ) self.length = tstream.length() super(MACRequestPayload, self).write(ostream) ostream.write(tstream.buffer)
def _set_attributes_on_managed_object(self, managed_object, attributes): """ Given a kmip.pie object and a dictionary of attributes, attempt to set the attribute values on the object. """ for attribute_name, attribute_value in six.iteritems(attributes): object_type = managed_object._object_type if self._attribute_policy.is_attribute_applicable_to_object_type( attribute_name, object_type): self._set_attribute_on_managed_object( managed_object, (attribute_name, attribute_value)) else: name = object_type.name raise exceptions.InvalidField( "Cannot set {0} attribute on {1} object.".format( attribute_name, ''.join([x.capitalize() for x in name.split('_')])))
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_2_0): """ Write the data encoding the SetAttribute request payload to a stream. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. Raises: VersionNotSupported: Raised when a KMIP version is provided that does not support the SetAttribute operation. InvalidField: Raised if a required field is missing from the payload object. """ if kmip_version < enums.KMIPVersion.KMIP_2_0: raise exceptions.VersionNotSupported( "KMIP {} does not support the SetAttribute operation.".format( kmip_version.value)) local_buffer = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(local_buffer, kmip_version=kmip_version) if self._new_attribute: self._new_attribute.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The SetAttribute request payload is missing the new " "attribute field.") self.length = local_buffer.length() super(SetAttributeRequestPayload, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def write(self, ostream): """ Write the data encoding the GetAttributes response payload to a stream. Args: ostream (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. """ tstream = utils.BytearrayStream() if self._unique_identifier: self._unique_identifier.write(tstream) else: raise exceptions.InvalidField( "The GetAttributes response unique identifier is required.") for attribute in self._attributes: attribute.write(tstream) self.length = tstream.length() super(GetAttributesResponsePayload, self).write(ostream) ostream.write(tstream.buffer)
def create_asymmetric_key_pair(self, algorithm, length): """ Create an asymmetric key pair. Args: algorithm(CryptographicAlgorithm): An enumeration specifying the algorithm for which the created keys will be compliant. length(int): The length of the keys to be created. This value must be compliant with the constraints of the provided algorithm. Returns: dict: A dictionary containing the public key data, with at least the following key/value fields: * value - the bytes of the key * format - a KeyFormatType enumeration for the bytes format dict: A dictionary containing the private key data, identical in structure to the one above. Raises: InvalidField: Raised when the algorithm is unsupported or the length is incompatible with the algorithm. CryptographicFailure: Raised when the key generation process fails. Example: >>> engine = CryptographyEngine() >>> key = engine.create_asymmetric_key( ... CryptographicAlgorithm.RSA, 2048) """ if algorithm not in self._asymetric_key_algorithms.keys(): raise exceptions.InvalidField( "The cryptographic algorithm ({0}) is not a supported " "asymmetric key algorithm.".format(algorithm) ) engine_method = self._asymetric_key_algorithms.get(algorithm) return engine_method(length)
def mac(self, algorithm, key, data): """ Generate message authentication code. Args: algorithm(CryptographicAlgorithm): An enumeration specifying the algorithm for which the MAC operation will use. key(bytes): secret key used in the MAC operation data(bytes): The data to be MACed. Returns: bytes: The MACed data Raises: InvalidField: Raised when the algorithm is unsupported or the length is incompatible with the algorithm. CryptographicFailure: Raised when the key generation process fails. Example: >>> engine = CryptographyEngine() >>> mac_data = engine.mac( ... CryptographicAlgorithm.HMAC-SHA256, b'\x01\x02\x03\x04', ... b'\x05\x06\x07\x08') """ mac_data = None if algorithm in self._hash_algorithms.keys(): self.logger.info( "Generating a hash-based message authentication code using " "{0}".format(algorithm.name)) hash_algorithm = self._hash_algorithms.get(algorithm) try: h = hmac.HMAC(key, hash_algorithm(), backend=default_backend()) h.update(data) mac_data = h.finalize() except Exception as e: self.logger.exception(e) raise exceptions.CryptographicFailure( "An error occurred while computing an HMAC. " "See the server log for more information.") elif algorithm in self._symmetric_key_algorithms.keys(): self.logger.info( "Generating a cipher-based message authentication code using " "{0}".format(algorithm.name)) cipher_algorithm = self._symmetric_key_algorithms.get(algorithm) try: # ARC4 and IDEA algorithms will raise exception as CMAC # requires block ciphers c = cmac.CMAC(cipher_algorithm(key), backend=default_backend()) c.update(data) mac_data = c.finalize() except Exception as e: raise exceptions.CryptographicFailure( "An error occurred while computing a CMAC. " "See the server log for more information.") else: raise exceptions.InvalidField( "The cryptographic algorithm ({0}) is not a supported " "for a MAC operation.".format(algorithm)) return mac_data
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0): """ Write the data encoding the SplitKey object to a buffer. Args: output_buffer (stream): A data stream in which to encode object data, supporting a write method; usually a BytearrayStream object. kmip_version (KMIPVersion): An enumeration defining the KMIP version with which the object will be encoded. Optional, defaults to KMIP 1.0. """ local_buffer = utils.BytearrayStream() if self._split_key_parts: self._split_key_parts.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The SplitKey object is missing the SplitKeyParts field." ) if self._key_part_identifier: self._key_part_identifier.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The SplitKey object is missing the KeyPartIdentifier field." ) if self._split_key_threshold: self._split_key_threshold.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The SplitKey object is missing the SplitKeyThreshold field." ) if self._split_key_method: self._split_key_method.write( local_buffer, kmip_version=kmip_version ) else: raise exceptions.InvalidField( "The SplitKey object is missing the SplitKeyMethod field." ) if self._prime_field_size: self._prime_field_size.write( local_buffer, kmip_version=kmip_version ) else: corner_case = enums.SplitKeyMethod.POLYNOMIAL_SHARING_PRIME_FIELD if self.split_key_method == corner_case: raise exceptions.InvalidField( "The SplitKey object is missing the PrimeFieldSize field. " "This field is required when the SplitKeyMethod is " "PolynomialSharingPrimeField." ) if self._key_block: self._key_block.write(local_buffer, kmip_version=kmip_version) else: raise exceptions.InvalidField( "The SplitKey object is missing the KeyBlock field." ) self.length = local_buffer.length() super(SplitKey, self).write(output_buffer, kmip_version=kmip_version) output_buffer.write(local_buffer.buffer)
def _process_create_key_pair(self, payload): self._logger.info("Processing operation: CreateKeyPair") algorithm = None length = None # Process attribute sets public_key_attributes = {} private_key_attributes = {} common_attributes = {} if payload.public_key_template_attribute: public_key_attributes = self._process_template_attribute( payload.public_key_template_attribute) if payload.private_key_template_attribute: private_key_attributes = self._process_template_attribute( payload.private_key_template_attribute) if payload.common_template_attribute: common_attributes = self._process_template_attribute( payload.common_template_attribute) # Propagate common attributes if not overridden by the public/private # attribute sets for key, value in six.iteritems(common_attributes): if key not in public_key_attributes.keys(): public_key_attributes.update([(key, value)]) if key not in private_key_attributes.keys(): private_key_attributes.update([(key, value)]) # Error check for required attributes. public_algorithm = public_key_attributes.get('Cryptographic Algorithm') if public_algorithm: public_algorithm = public_algorithm.value else: raise exceptions.InvalidField( "The cryptographic algorithm must be specified as an " "attribute for the public key.") public_length = public_key_attributes.get('Cryptographic Length') if public_length: public_length = public_length.value else: # TODO (peterhamilton) The cryptographic length is technically not # required per the spec. Update the CryptographyEngine to accept a # None length, allowing it to pick the length dynamically. Default # to the strongest key size allowed for the algorithm type. raise exceptions.InvalidField( "The cryptographic length must be specified as an attribute " "for the public key.") public_usage_mask = public_key_attributes.get( 'Cryptographic Usage Mask') if public_usage_mask is None: raise exceptions.InvalidField( "The cryptographic usage mask must be specified as an " "attribute for the public key.") private_algorithm = private_key_attributes.get( 'Cryptographic Algorithm') if private_algorithm: private_algorithm = private_algorithm.value else: raise exceptions.InvalidField( "The cryptographic algorithm must be specified as an " "attribute for the private key.") private_length = private_key_attributes.get('Cryptographic Length') if private_length: private_length = private_length.value else: # TODO (peterhamilton) The cryptographic length is technically not # required per the spec. Update the CryptographyEngine to accept a # None length, allowing it to pick the length dynamically. Default # to the strongest key size allowed for the algorithm type. raise exceptions.InvalidField( "The cryptographic length must be specified as an attribute " "for the private key.") private_usage_mask = private_key_attributes.get( 'Cryptographic Usage Mask') if private_usage_mask is None: raise exceptions.InvalidField( "The cryptographic usage mask must be specified as an " "attribute for the private key.") if public_algorithm == private_algorithm: algorithm = public_algorithm else: raise exceptions.InvalidField( "The public and private key algorithms must be the same.") if public_length == private_length: length = public_length else: raise exceptions.InvalidField( "The public and private key lengths must be the same.") public, private = self._cryptography_engine.create_asymmetric_key_pair( algorithm, length) public_key = objects.PublicKey(algorithm, length, public.get('value'), public.get('format')) private_key = objects.PrivateKey(algorithm, length, private.get('value'), private.get('format')) public_key.names = [] private_key.names = [] self._set_attributes_on_managed_object(public_key, public_key_attributes) self._set_attributes_on_managed_object(private_key, private_key_attributes) # TODO (peterhamilton) Set additional server-only attributes. public_key._owner = self._client_identity private_key._owner = self._client_identity self._data_session.add(public_key) self._data_session.add(private_key) # NOTE (peterhamilton) SQLAlchemy will *not* assign an ID until # commit is called. This makes future support for UNDO problematic. self._data_session.commit() self._logger.info("Created a PublicKey with ID: {0}".format( public_key.unique_identifier)) self._logger.info("Created a PrivateKey with ID: {0}".format( private_key.unique_identifier)) response_payload = create_key_pair.CreateKeyPairResponsePayload( private_key_uuid=attributes.PrivateKeyUniqueIdentifier( str(private_key.unique_identifier)), public_key_uuid=attributes.PublicKeyUniqueIdentifier( str(public_key.unique_identifier))) self._id_placeholder = str(private_key.unique_identifier) return response_payload
def _process_create(self, payload): self._logger.info("Processing operation: Create") object_type = payload.object_type.value template_attribute = payload.template_attribute if object_type != enums.ObjectType.SYMMETRIC_KEY: name = object_type.name raise exceptions.InvalidField( "Cannot create a {0} object with the Create operation.".format( ''.join([x.capitalize() for x in name.split('_')]))) object_attributes = {} if template_attribute: object_attributes = self._process_template_attribute( template_attribute) algorithm = object_attributes.get('Cryptographic Algorithm') if algorithm: algorithm = algorithm.value else: raise exceptions.InvalidField( "The cryptographic algorithm must be specified as an " "attribute.") length = object_attributes.get('Cryptographic Length') if length: length = length.value else: # TODO (peterhamilton) The cryptographic length is technically not # required per the spec. Update the CryptographyEngine to accept a # None length, allowing it to pick the length dynamically. Default # to the strongest key size allowed for the algorithm type. raise exceptions.InvalidField( "The cryptographic length must be specified as an attribute.") usage_mask = object_attributes.get('Cryptographic Usage Mask') if usage_mask is None: raise exceptions.InvalidField( "The cryptographic usage mask must be specified as an " "attribute.") result = self._cryptography_engine.create_symmetric_key( algorithm, length) managed_object = objects.SymmetricKey(algorithm, length, result.get('value')) managed_object.names = [] self._set_attributes_on_managed_object(managed_object, object_attributes) # TODO (peterhamilton) Set additional server-only attributes. managed_object._owner = self._client_identity self._data_session.add(managed_object) # NOTE (peterhamilton) SQLAlchemy will *not* assign an ID until # commit is called. This makes future support for UNDO problematic. self._data_session.commit() self._logger.info("Created a SymmetricKey with ID: {0}".format( managed_object.unique_identifier)) response_payload = create.CreateResponsePayload( object_type=payload.object_type, unique_identifier=attributes.UniqueIdentifier( str(managed_object.unique_identifier)), template_attribute=None) self._id_placeholder = str(managed_object.unique_identifier) return response_payload