From 3e11002567fa8b5b253eed82defdc16b3501a082 Mon Sep 17 00:00:00 2001 From: Peter Hamilton Date: Tue, 10 Mar 2015 11:52:53 -0400 Subject: [PATCH] Adding support for Digest This change adds support for the Digest attribute, including updates and unit test suites for all dependent KMIP objects. --- kmip/core/attributes.py | 252 +++++++++- kmip/core/factories/attribute_values.py | 7 +- kmip/core/factories/secrets.py | 4 +- kmip/core/messages/contents.py | 10 - kmip/core/misc.py | 32 +- kmip/core/objects.py | 13 +- kmip/core/primitives.py | 43 +- kmip/core/server.py | 5 +- kmip/tests/core/attributes/test_attributes.py | 91 ++++ kmip/tests/core/attributes/test_digest.py | 451 ++++++++++++++++++ kmip/tests/core/messages/test_messages.py | 4 +- kmip/tests/core/misc/test_misc.py | 48 ++ kmip/tests/core/test_primitives.py | 44 +- kmip/tests/core/test_server.py | 9 +- 14 files changed, 938 insertions(+), 75 deletions(-) create mode 100644 kmip/tests/core/attributes/test_digest.py diff --git a/kmip/core/attributes.py b/kmip/core/attributes.py index c8aabd4..31b722f 100644 --- a/kmip/core/attributes.py +++ b/kmip/core/attributes.py @@ -15,13 +15,18 @@ from kmip.core import enums +from kmip.core.enums import HashingAlgorithm as HashingAlgorithmEnum +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum from kmip.core.enums import Tags from kmip.core.errors import ErrorStrings -from kmip.core.primitives import Struct -from kmip.core.primitives import Integer +from kmip.core.misc import KeyFormatType + +from kmip.core.primitives import ByteString from kmip.core.primitives import Enumeration +from kmip.core.primitives import Integer +from kmip.core.primitives import Struct from kmip.core.primitives import TextString from kmip.core.utils import BytearrayStream @@ -173,6 +178,28 @@ class CryptographicLength(Integer): # 3.6 +class HashingAlgorithm(Enumeration): + """ + An encodeable wrapper for the HashingAlgorithm enumeration. + + Used to specify the algorithm used to compute the Digest of a Managed + Object. See Sections 3.17 and 9.1.3.2.16 of the KMIP v1.1 specification + for more information. + """ + ENUM_TYPE = enums.HashingAlgorithm + + def __init__(self, value=HashingAlgorithmEnum.SHA_256): + """ + Construct a HashingAlgorithm object. + + Args: + value (HashingAlgorithm): A HashingAlgorithm enumeration value, + (e.g., HashingAlgorithm.MD5). Optional, defaults to + HashingAlgorithm.SHA_256. + """ + super(HashingAlgorithm, self).__init__(value, Tags.HASHING_ALGORITHM) + + class CryptographicParameters(Struct): class BlockCipherMode(Enumeration): @@ -189,13 +216,6 @@ class CryptographicParameters(Struct): super(self.__class__, self).__init__(value, Tags.PADDING_METHOD) - class HashingAlgorithm(Enumeration): - ENUM_TYPE = enums.HashingAlgorithm - - def __init__(self, value=None): - super(self.__class__, self).__init__(value, - Tags.HASHING_ALGORITHM) - class KeyRoleType(Enumeration): ENUM_TYPE = enums.KeyRoleType @@ -227,7 +247,7 @@ class CryptographicParameters(Struct): self.padding_method.read(tstream) if self.is_tag_next(Tags.HASHING_ALGORITHM, tstream): - self.hashing_algorithm = CryptographicParameters.HashingAlgorithm() + self.hashing_algorithm = HashingAlgorithm() self.hashing_algorithm.read(tstream) if self.is_tag_next(Tags.KEY_ROLE_TYPE, tstream): @@ -272,6 +292,218 @@ class CertificateType(Enumeration): Tags.CERTIFICATE_TYPE) +# 3.17 +class DigestValue(ByteString): + """ + A byte string representing the hash value of a Digest. + + Used to hold the bytes of the digest hash value. Automatically generated + by the KMIP server, the value is empty if the server does not have access + to the value or encoding of the related Managed Object. See Section 3.17 + of the KMIP 1.1 specification for more information. + + Attributes: + value: The bytes of the hash. + """ + + def __init__(self, value=b''): + """ + Construct a DigestValue object. + + Args: + value (bytes): The bytes of the hash. Optional, defaults to + the empty byte string. + """ + super(DigestValue, self).__init__(value, Tags.DIGEST_VALUE) + + +class Digest(Struct): + """ + A structure storing a hash digest of a Managed Object. + + Digests may be calculated for keys, secret data objects, certificates, and + opaque data objects and are generated when the object is created or + registered with the KMIP server. See Section 3.17 of the KMIP 1.1 + specification for more information. + + Attributes: + hashing_algorithm: The algorithm used to compute the hash digest. + digest_value: The bytes representing the hash digest value. + key_format_type: The type of the key the hash was generated for. + """ + + def __init__(self, + hashing_algorithm=None, + digest_value=None, + key_format_type=None): + """ + Construct a Digest object. + + Args: + hashing_algorithm (HashingAlgorithm): The hash algorithm used to + compute the value of the digest. Optional, defaults to None. + digest_value (DigestValue): The byte string representing the + value of the hash digest. Optional, defaults to None. + key_format_type (KeyFormatType): The format type of the key the + hash was computed for, if the object in question is a key. + Optional, defaults to None. + """ + super(Digest, self).__init__(Tags.DIGEST) + + if hashing_algorithm is None: + self.hashing_algorithm = HashingAlgorithm() + else: + self.hashing_algorithm = hashing_algorithm + + if digest_value is None: + self.digest_value = DigestValue() + else: + self.digest_value = digest_value + + if key_format_type is None: + self.key_format_type = KeyFormatType() + else: + self.key_format_type = key_format_type + + self.validate() + + def read(self, istream): + """ + Read the data encoding the Digest object and decode it into its + constituent parts. + + Args: + istream (Stream): A data stream containing encoded object data, + supporting a read method; usually a BytearrayStream object. + """ + super(Digest, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.hashing_algorithm.read(tstream) + self.digest_value.read(tstream) + self.key_format_type.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + """ + Write the data encoding the Digest object to a stream. + + Args: + ostream (Stream): A data stream in which to encode object data, + supporting a write method; usually a BytearrayStream object. + """ + tstream = BytearrayStream() + + self.hashing_algorithm.write(tstream) + self.digest_value.write(tstream) + self.key_format_type.write(tstream) + + self.length = tstream.length() + super(Digest, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + """ + Error check the attributes of the Digest object. + """ + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Add checks comparing the length of the digest + # value against the standard length for the stated hashing algorithm. + if not isinstance(self.hashing_algorithm, HashingAlgorithm): + msg = "invalid hashing algorithm" + msg += "; expected {0}, received {1}".format( + HashingAlgorithm, self.hashing_algorithm) + raise TypeError(msg) + + if not isinstance(self.digest_value, DigestValue): + msg = "invalid digest value" + msg += "; expected {0}, received {1}".format( + DigestValue, self.digest_value) + raise TypeError(msg) + + if not isinstance(self.key_format_type, KeyFormatType): + msg = "invalid key format type" + msg += "; expected {0}, received {1}".format( + KeyFormatType, self.key_format_type) + raise TypeError(msg) + + def __eq__(self, other): + if isinstance(other, Digest): + if self.hashing_algorithm != other.hashing_algorithm: + return False + elif self.digest_value != other.digest_value: + return False + elif self.key_format_type != other.key_format_type: + return False + else: + return True + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, Digest): + return not (self == other) + else: + return NotImplemented + + def __repr__(self): + hashing_algorithm = "hashing_algorithm={0}".format( + repr(self.hashing_algorithm)) + digest_value = "digest_value={0}".format( + repr(self.digest_value)) + key_format_type = "key_format_type={0}".format( + repr(self.key_format_type)) + + return "Digest({0}, {1}, {2})".format( + hashing_algorithm, digest_value, key_format_type) + + def __str__(self): + return str(self.digest_value) + + @classmethod + def create(cls, + hashing_algorithm=HashingAlgorithmEnum.SHA_256, + digest_value=b'', + key_format_type=KeyFormatTypeEnum.RAW): + """ + Construct a Digest object from provided digest values. + + Args: + hashing_algorithm (HashingAlgorithm): An enumeration representing + the hash algorithm used to compute the digest. Optional, + defaults to HashingAlgorithm.SHA_256. + digest_value (byte string): The bytes of the digest hash. Optional, + defaults to the empty byte string. + key_format_type (KeyFormatType): An enumeration representing the + format of the key corresponding to the digest. Optional, + defaults to KeyFormatType.RAW. + + Returns: + Digest: The newly created Digest. + + Example: + >>> x = Digest.create(HashingAlgorithm.MD5, b'\x00', + ... KeyFormatType.RAW) + >>> x.hashing_algorithm + HashingAlgorithm(value=HashingAlgorithm.MD5) + >>> x.digest_value + DigestValue(value=bytearray(b'\x00')) + >>> x.key_format_type + KeyFormatType(value=KeyFormatType.RAW) + """ + algorithm = HashingAlgorithm(hashing_algorithm) + value = DigestValue(bytearray(digest_value)) + format_type = KeyFormatType(key_format_type) + + return Digest(hashing_algorithm=algorithm, + digest_value=value, + key_format_type=format_type) + + # 3.18 class OperationPolicyName(TextString): diff --git a/kmip/core/factories/attribute_values.py b/kmip/core/factories/attribute_values.py index 4b7ad4d..dbfd9bb 100644 --- a/kmip/core/factories/attribute_values.py +++ b/kmip/core/factories/attribute_values.py @@ -21,6 +21,7 @@ from kmip.core.attributes import CryptographicAlgorithm from kmip.core.attributes import CryptographicLength from kmip.core.attributes import CryptographicUsageMask from kmip.core.attributes import CustomAttribute +from kmip.core.attributes import Digest from kmip.core.attributes import Name from kmip.core.attributes import ObjectGroup from kmip.core.attributes import UniqueIdentifier @@ -67,7 +68,7 @@ class AttributeValueFactory(object): elif name is AttributeType.DIGITAL_SIGNATURE_ALGORITHM: value = self._create_digital_signature_algorithm(value) elif name is AttributeType.DIGEST: - value = self._create_digest(value) + value = self._create_digest() elif name is AttributeType.OPERATION_POLICY_NAME: value = self._create_operation_policy_name(value) elif name is AttributeType.CRYPTOGRAPHIC_USAGE_MASK: @@ -182,8 +183,8 @@ class AttributeValueFactory(object): def _create_digital_signature_algorithm(self, alg): raise NotImplementedError() - def _create_digest(self, digest): - raise NotImplementedError() + def _create_digest(self): + return Digest() def _create_operation_policy_name(self, name): return OperationPolicyName(name) diff --git a/kmip/core/factories/secrets.py b/kmip/core/factories/secrets.py index cce24f2..818ce5e 100644 --- a/kmip/core/factories/secrets.py +++ b/kmip/core/factories/secrets.py @@ -19,8 +19,8 @@ from kmip.core.attributes import CryptographicAlgorithm from kmip.core.attributes import CryptographicLength from kmip.core.enums import ObjectType - from kmip.core.errors import ErrorStrings +from kmip.core.misc import KeyFormatType from kmip.core.objects import Attribute from kmip.core.objects import KeyBlock @@ -75,7 +75,7 @@ class SecretFactory(object): cryptographic_length = value.get('cryptographic_length') key_wrapping_data = value.get('key_wrapping_data') - key_format_type = KeyBlock.KeyFormatType(key_type) + key_format_type = KeyFormatType(key_type) key_comp_type = None if key_compression_type is not None: diff --git a/kmip/core/messages/contents.py b/kmip/core/messages/contents.py index ca7e2c7..3b2d982 100644 --- a/kmip/core/messages/contents.py +++ b/kmip/core/messages/contents.py @@ -15,7 +15,6 @@ from kmip.core.enums import BatchErrorContinuationOption from kmip.core.enums import KeyCompressionType -from kmip.core.enums import KeyFormatType from kmip.core.enums import Operation from kmip.core.enums import ResultStatus from kmip.core.enums import ResultReason @@ -270,12 +269,3 @@ class KeyCompressionType(Enumeration): def __init__(self, value=None): super(KeyCompressionType, self).\ __init__(value, Tags.KEY_COMPRESSION_TYPE) - - -# 9.1.3.2.3 -class KeyFormatType(Enumeration): - ENUM_TYPE = KeyFormatType - - def __init__(self, value=None): - super(KeyFormatType, self).\ - __init__(value, Tags.KEY_FORMAT_TYPE) diff --git a/kmip/core/misc.py b/kmip/core/misc.py index 69cf936..106c60f 100644 --- a/kmip/core/misc.py +++ b/kmip/core/misc.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum from kmip.core.enums import Tags from kmip.core.enums import QueryFunction as QueryFunctionEnum @@ -30,7 +31,7 @@ class Offset(Interval): Used by Rekey and Recertify requests to indicate the time difference between the InitializationDate and the ActivationDate of the replacement - item to be created. See Sections 4.4, 4.5, and 4.8 of the KMIP v1.1 + item to be created. See Sections 4.4, 4.5, and 4.8 of the KMIP 1.1 specification for more information. """ @@ -50,7 +51,7 @@ class QueryFunction(Enumeration): An encodeable wrapper for the QueryFunction enumeration. Used by Query requests to specify the information to retrieve from the - KMIP server. See Sections 4.25 and 9.1.3.2.24 of the KMIP v1.1 + KMIP server. See Sections 4.25 and 9.1.3.2.24 of the KMIP 1.1 specification for more information. """ ENUM_TYPE = QueryFunctionEnum @@ -72,7 +73,7 @@ class VendorIdentification(TextString): A text string uniquely identifying a KMIP vendor. Returned by KMIP servers upon receipt of a Query request for server - information. See Section 4.25 of the KMIP v1.1. specification for more + information. See Section 4.25 of the KMIP 1.1. specification for more information. """ @@ -93,7 +94,7 @@ class ServerInformation(Struct): A structure containing vendor-specific fields and/or substructures. Returned by KMIP servers upon receipt of a Query request for server - information. See Section 4.25 of the KMIP v1.1 specification for more + information. See Section 4.25 of the KMIP 1.1 specification for more information. Note: @@ -182,3 +183,26 @@ class ServerInformation(Struct): def __str__(self): return str(self.data) + + +class KeyFormatType(Enumeration): + """ + An encodeable wrapper for the KeyFormatType enumeration. + + Used to identify the format of different types of keys in KeyBlock and + Digest objects, it can also be used to specify the format in which a key + is returned when using the Get operation. See Sections 2.1.3, 2.1.7, 3.17, + 4.11, and 9.1.3.2.3 of the KMIP 1.1 specification for more information. + """ + ENUM_TYPE = KeyFormatTypeEnum + + def __init__(self, value=KeyFormatTypeEnum.RAW): + """ + Construct a KeyFormatType object. + + Args: + value (KeyFormatType): A KeyFormatType enumeration value, + (e.g., KeyFormatType.PKCS_1). Optional, default to + KeyFormatType.RAW. + """ + super(KeyFormatType, self).__init__(value, Tags.KEY_FORMAT_TYPE) diff --git a/kmip/core/objects.py b/kmip/core/objects.py index 597cdaf..84746a8 100644 --- a/kmip/core/objects.py +++ b/kmip/core/objects.py @@ -28,6 +28,7 @@ from kmip.core.enums import Types from kmip.core.enums import CredentialType from kmip.core.errors import ErrorStrings +from kmip.core.misc import KeyFormatType from kmip.core.primitives import Struct from kmip.core.primitives import TextString @@ -343,12 +344,6 @@ class Credential(Struct): # 2.1.3 class KeyBlock(Struct): - class KeyFormatType(Enumeration): - ENUM_TYPE = enums.KeyFormatType - - def __init__(self, value=None): - super(self.__class__, self).__init__(value, Tags.KEY_FORMAT_TYPE) - class KeyCompressionType(Enumeration): ENUM_TYPE = enums.KeyCompressionType @@ -376,7 +371,7 @@ class KeyBlock(Struct): super(self.__class__, self).read(istream) tstream = BytearrayStream(istream.read(self.length)) - self.key_format_type = KeyBlock.KeyFormatType() + self.key_format_type = KeyFormatType() self.key_format_type.read(tstream) key_format_type = self.key_format_type.enum @@ -429,9 +424,9 @@ class KeyBlock(Struct): def __validate(self): if self.key_format_type is not None: - if type(self.key_format_type) is not KeyBlock.KeyFormatType: + if type(self.key_format_type) is not KeyFormatType: member = 'KeyBlock.key_format_type' - exp_type = KeyBlock.KeyFormatType + exp_type = KeyFormatType rcv_type = type(self.key_format_type) msg = ErrorStrings.BAD_EXP_RECV.format(member, 'type', exp_type, rcv_type) diff --git a/kmip/core/primitives.py b/kmip/core/primitives.py index d4da2a5..d22ec02 100644 --- a/kmip/core/primitives.py +++ b/kmip/core/primitives.py @@ -417,7 +417,7 @@ class Enumeration(Integer): Enum, type(self.enum))) def __repr__(self): - return "Enumeration(value={0})".format(self.enum) + return "{0}(value={1})".format(type(self).__name__, self.enum) def __str__(self): return "{0} - {1} - {2}".format( @@ -570,7 +570,10 @@ class TextString(Base): return NotImplemented def __ne__(self, other): - return not self.__eq__(other) + if isinstance(other, TextString): + return not (self == other) + else: + return NotImplemented class ByteString(Base): @@ -579,7 +582,11 @@ class ByteString(Base): def __init__(self, value=None, tag=Tags.DEFAULT): super(ByteString, self).__init__(tag, type=Types.BYTE_STRING) - self.value = value + + if value is None: + self.value = bytes() + else: + self.value = bytes(value) self.validate() @@ -595,9 +602,10 @@ class ByteString(Base): def read_value(self, istream): # Read bytes into bytearray - self.value = bytearray() + data = bytearray() for _ in range(self.length): - self.value.append(istream.read(1)[0]) + data.append(istream.read(1)[0]) + self.value = bytes(data) # Read padding and check content self.padding_length = self.PADDING_SIZE - (self.length % @@ -618,7 +626,8 @@ class ByteString(Base): def write_value(self, ostream): # Write bytes to stream - for byte in self.value: + data = bytearray(self.value) + for byte in data: ostream.write(pack(self.BYTE_FORMAT, byte)) # Write padding to stream @@ -633,15 +642,31 @@ class ByteString(Base): self.__validate() def __validate(self): + # TODO (peter-hamilton) Test is pointless, value is always bytes. Fix. if self.value is not None: data_type = type(self.value) - if data_type is not bytearray: + if data_type is not bytes: msg = ErrorStrings.BAD_EXP_RECV - raise TypeError(msg.format('ByteString', 'value', bytearray, + raise TypeError(msg.format('ByteString', 'value', bytes, data_type)) def __repr__(self): - return '' % (self.value) + return "{0}(value={1})".format(type(self).__name__, repr(self.value)) + + def __str__(self): + return "{0}".format(str(self.value)) + + def __eq__(self, other): + if isinstance(other, ByteString): + return self.value == other.value + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, ByteString): + return not (self == other) + else: + return NotImplemented class DateTime(LongInteger): diff --git a/kmip/core/server.py b/kmip/core/server.py index 538f1d6..33b66de 100644 --- a/kmip/core/server.py +++ b/kmip/core/server.py @@ -31,11 +31,12 @@ from kmip.core.factories.keys import KeyFactory from kmip.core.factories.secrets import SecretFactory from kmip.core.keys import RawKey -from kmip.core.messages.contents import KeyFormatType from kmip.core.messages.contents import ResultStatus from kmip.core.messages.contents import ResultReason from kmip.core.messages.contents import ResultMessage +from kmip.core.misc import KeyFormatType + from kmip.core.objects import KeyBlock from kmip.core.objects import KeyValue from kmip.core.objects import KeyValueStruct @@ -344,7 +345,7 @@ class KMIPImpl(KMIP): return OperationResult(status, reason, message) def _gen_symmetric_key(self, bit_length, crypto_alg): - key_format_type = KeyBlock.KeyFormatType(KeyFormatTypeEnum.RAW) + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) key_material = RawKey(bytearray(os.urandom(int(bit_length/8)))) key_value = KeyValueStruct(key_format_type, key_material) crypto_length = CryptographicLength(bit_length) diff --git a/kmip/tests/core/attributes/test_attributes.py b/kmip/tests/core/attributes/test_attributes.py index 81fd9ea..d598c89 100644 --- a/kmip/tests/core/attributes/test_attributes.py +++ b/kmip/tests/core/attributes/test_attributes.py @@ -17,8 +17,12 @@ from testtools import TestCase from kmip.core.attributes import ApplicationData from kmip.core.attributes import ApplicationNamespace +from kmip.core.attributes import DigestValue +from kmip.core.attributes import HashingAlgorithm from kmip.core.attributes import OperationPolicyName +from kmip.core.enums import HashingAlgorithm as HashingAlgorithmEnum + from kmip.core.utils import BytearrayStream @@ -90,6 +94,93 @@ class TestOperationPolicyName(TestCase): self._test_operation_policy_name(None) +class TestHashingAlgorithm(TestCase): + """ + A test suite for the HashingAlgorithm class. + + Since HashingAlgorithm is a simple wrapper for the Enumeration primitive, + only a few tests pertaining to construction are needed. + """ + + def setUp(self): + super(TestHashingAlgorithm, self).setUp() + + def tearDown(self): + super(TestHashingAlgorithm, self).tearDown() + + def _test_init(self, value): + if (isinstance(value, HashingAlgorithmEnum)) or (value is None): + hashing_algorithm = HashingAlgorithm(value) + + msg = "expected {0}, observed {1}".format( + value, hashing_algorithm.enum) + self.assertEqual(value, hashing_algorithm.enum, msg) + else: + self.assertRaises(TypeError, HashingAlgorithm, value) + + def test_init_with_none(self): + """ + Test that a HashingAlgorithm object can be constructed with no + specified value. + """ + self._test_init(None) + + def test_init_with_valid(self): + """ + Test that a HashingAlgorithm object can be constructed with a valid + HashingAlgorithm enumeration value. + """ + self._test_init(HashingAlgorithmEnum.MD5) + + def test_init_with_invalid(self): + """ + Test that a TypeError exception is raised when a non HashingAlgorithm + enumeration value is used to construct a HashingAlgorithm object. + """ + self._test_init("invalid") + + +class TestDigestValue(TestCase): + """ + A test suite for the DigestValue class. + + Since DigestValue is a simple wrapper for the ByteString primitive, only + a few tests pertaining to construction are needed. + """ + + def setUp(self): + super(TestDigestValue, self).setUp() + + def tearDown(self): + super(TestDigestValue, self).tearDown() + + def _test_init(self, value): + if (isinstance(value, bytes)) or (value is None): + digest_value = DigestValue(value) + + if value is None: + value = bytes() + + msg = "expected {0}, observed {1}".format( + value, digest_value.value) + self.assertEqual(value, digest_value.value, msg) + else: + self.assertRaises(TypeError, DigestValue, value) + + def test_init_with_none(self): + """ + Test that a DigestValue object can be constructed with no specified + value. + """ + self._test_init(None) + + def test_init_with_valid(self): + """ + Test that a DigestValue object can be constructed with valid byte data. + """ + self._test_init(b'\x00\x01\x02\x03') + + class TestApplicationNamespace(TestCase): """ A test suite for the ApplicationNamespace class. diff --git a/kmip/tests/core/attributes/test_digest.py b/kmip/tests/core/attributes/test_digest.py new file mode 100644 index 0000000..3087cf5 --- /dev/null +++ b/kmip/tests/core/attributes/test_digest.py @@ -0,0 +1,451 @@ +# Copyright (c) 2015 The Johns Hopkins University/Applied Physics Laboratory +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from testtools import TestCase + +from kmip.core.attributes import Digest +from kmip.core.attributes import DigestValue +from kmip.core.attributes import HashingAlgorithm + +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum +from kmip.core.enums import HashingAlgorithm as HashingAlgorithmEnum + +from kmip.core.objects import KeyFormatType +from kmip.core.utils import BytearrayStream + + +class TestDigest(TestCase): + """ + A test suite for the Digest class. + """ + + def setUp(self): + super(TestDigest, self).setUp() + + self.hashing_algorithm_a = HashingAlgorithm( + HashingAlgorithmEnum.SHA_256) + self.hashing_algorithm_b = HashingAlgorithm( + HashingAlgorithmEnum.SHA_256) + self.hashing_algorithm_c = HashingAlgorithm( + HashingAlgorithmEnum.SHA_256) + + self.digest_value_a = DigestValue(b'') + self.digest_value_b = DigestValue( + b'\x6C\x06\x4F\xE0\x51\xAD\xD1\x1E\xDC\x07\x72\x7B\x59\x4E\xB4\x87' + b'\x11\xDF\x84\x3E\x08\x44\x5B\xBA\x2C\xD7\x86\xBC\x16\xBC\x58' + b'\xE8') + self.digest_value_c = DigestValue( + b'\x11\x11\x0A\x01\xED\x45\x89\xD9\x98\x7C\x9A\xD6\x03\x68\xE2\xB7' + b'\x62\xF2\xB2\x0C\x00\x94\x6E\x19\x32\xC1\x60\x5A\x18\x17\x2F' + b'\x55') + + self.key_format_type_a = KeyFormatType(KeyFormatTypeEnum.RAW) + self.key_format_type_b = KeyFormatType(KeyFormatTypeEnum.RAW) + self.key_format_type_c = KeyFormatType(KeyFormatTypeEnum.PKCS_1) + + # Encodings obtained from Section 18.1 and 18.2 of the KMIP 1.1 Test + # Cases document. + self.encoding_a = BytearrayStream(( + b'\x42\x00\x34\x01\x00\x00\x00\x28\x42\x00\x38\x05\x00\x00\x00\x04' + b'\x00\x00\x00\x06\x00\x00\x00\x00\x42\x00\x35\x08\x00\x00\x00\x00' + b'\x42\x00\x42\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00' + b'\x00')) + self.encoding_b = BytearrayStream(( + b'\x42\x00\x34\x01\x00\x00\x00\x48\x42\x00\x38\x05\x00\x00\x00\x04' + b'\x00\x00\x00\x06\x00\x00\x00\x00\x42\x00\x35\x08\x00\x00\x00\x20' + b'\x6C\x06\x4F\xE0\x51\xAD\xD1\x1E\xDC\x07\x72\x7B\x59\x4E\xB4\x87' + b'\x11\xDF\x84\x3E\x08\x44\x5B\xBA\x2C\xD7\x86\xBC\x16\xBC\x58\xE8' + b'\x42\x00\x42\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00' + b'\x00')) + self.encoding_c = BytearrayStream(( + b'\x42\x00\x34\x01\x00\x00\x00\x48\x42\x00\x38\x05\x00\x00\x00\x04' + b'\x00\x00\x00\x06\x00\x00\x00\x00\x42\x00\x35\x08\x00\x00\x00\x20' + b'\x11\x11\x0A\x01\xED\x45\x89\xD9\x98\x7C\x9A\xD6\x03\x68\xE2\xB7' + b'\x62\xF2\xB2\x0C\x00\x94\x6E\x19\x32\xC1\x60\x5A\x18\x17\x2F\x55' + b'\x42\x00\x42\x05\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00' + b'\x00')) + + def tearDown(self): + super(TestDigest, self).tearDown() + + def test_init_with_none(self): + """ + Test that a Digest object can be constructed with no specified values. + """ + Digest() + + def test_init_with_args(self): + """ + Test that a Digest object can be constructed with valid values. + """ + Digest(hashing_algorithm=HashingAlgorithm(), + digest_value=DigestValue(), + key_format_type=KeyFormatType()) + + def test_validate_with_invalid_hashing_algorithm(self): + """ + Test that a TypeError exception is raised when an invalid + HashingAlgorithm is used to construct a Digest object. + """ + hashing_algorithm = "invalid" + kwargs = {'hashing_algorithm': hashing_algorithm} + + self.assertRaisesRegexp( + TypeError, "invalid hashing algorithm", Digest, **kwargs) + + def test_validate_with_invalid_digest_value(self): + """ + Test that a TypeError exception is raised when an invalid DigestValue + is used to construct a Digest object. + """ + digest_value = "invalid" + kwargs = {'digest_value': digest_value} + + self.assertRaisesRegexp( + TypeError, "invalid digest value", Digest, **kwargs) + + def test_validate_with_invalid_key_format_type(self): + """ + Test that a TypeError exception is raised when an invalid + KeyFormatType is used to construct a Digeest object. + """ + key_format_type = "invalid" + kwargs = {'key_format_type': key_format_type} + + self.assertRaisesRegexp( + TypeError, "invalid key format type", Digest, **kwargs) + + def _test_read(self, stream, hashing_algorithm, digest_value, + key_format_type): + digest = Digest() + digest.read(stream) + + msg = "hashing algorithm encoding mismatch" + msg += "; expected {0}, observed {1}".format( + hashing_algorithm, + digest.hashing_algorithm) + self.assertEqual( + hashing_algorithm, + digest.hashing_algorithm, msg) + + msg = "digest value encoding mismatch" + msg += "; expected {0}, observed {1}".format( + digest_value, + digest.digest_value) + self.assertEqual( + digest_value, + digest.digest_value, msg) + + msg = "key format type encoding mismatch" + msg += "; expected {0}, observed {1}".format( + key_format_type, + digest.key_format_type) + self.assertEqual( + key_format_type, + digest.key_format_type, msg) + + def test_read_a(self): + """ + Test that a Digest object with some data can be read from a data + stream. + """ + self._test_read(self.encoding_a, self.hashing_algorithm_a, + self.digest_value_a, self.key_format_type_a) + + def test_read_b(self): + """ + Test that a Digest object with data can be read from a data stream. + """ + self._test_read(self.encoding_b, self.hashing_algorithm_b, + self.digest_value_b, self.key_format_type_b) + + def test_read_c(self): + """ + Test that a Digest object with data can be read from a data stream. + """ + self._test_read(self.encoding_c, self.hashing_algorithm_c, + self.digest_value_c, self.key_format_type_c) + + def _test_write(self, stream_expected, hashing_algorithm, digest_value, + key_format_type): + stream_observed = BytearrayStream() + digest = Digest( + hashing_algorithm=hashing_algorithm, + digest_value=digest_value, + key_format_type=key_format_type) + digest.write(stream_observed) + + length_expected = len(stream_expected) + length_observed = len(stream_observed) + + msg = "encoding lengths not equal" + msg += "; expected {0}, observed {1}".format( + length_expected, length_observed) + self.assertEqual(length_expected, length_observed, msg) + + msg = "encoding mismatch" + msg += ";\nexpected:\n{0}\nobserved:\n{1}".format( + stream_expected, stream_observed) + self.assertEqual(stream_expected, stream_observed, msg) + + def test_write_a(self): + """ + Test that a Digest object with some data can be written to a data + stream. + """ + self._test_write(self.encoding_a, self.hashing_algorithm_a, + self.digest_value_a, self.key_format_type_a) + + def test_write_b(self): + """ + Test that a Digest object with data can be written to a data stream. + """ + self._test_write(self.encoding_b, self.hashing_algorithm_b, + self.digest_value_b, self.key_format_type_b) + + def test_write_c(self): + """ + Test that a Digest object with data can be written to a data stream. + """ + self._test_write(self.encoding_c, self.hashing_algorithm_c, + self.digest_value_c, self.key_format_type_c) + + def test_equal_on_equal(self): + """ + Test that the equality operator returns True when comparing two Digest + objects with the same internal data. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_equal_and_empty(self): + """ + Test that the equality operator returns True when comparing two Digest + objects with no internal data. + """ + a = Digest() + b = Digest() + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_not_equal(self): + """ + Test that the equality operator returns False when comparing two + Digest objects with different sets of internal data. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = Digest() + + 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 Digest + object with a non-Digest object. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = "invalid" + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_not_equal_on_equal(self): + """ + Test that the inequality operator returns False when comparing two + Digest objects with the same internal data. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_equal_and_empty(self): + """ + Test that the inequality operator returns False when comparing two + Digest objects with no internal data. + """ + a = Digest() + b = Digest() + + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_not_equal(self): + """ + Test that the inequality operator returns True when comparing two + Digest objects with the different sets of internal data. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = Digest() + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_type_mismatch(self): + """ + Test that the inequality operator returns True when comparing an + Digest object with a non-ExtensionInformation object. + """ + a = Digest( + hashing_algorithm=self.hashing_algorithm_b, + digest_value=self.digest_value_b, + key_format_type=self.key_format_type_b) + b = "invalid" + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_repr(self): + """ + Test that the representation of a Digest object with data is formatted + properly and can be used by eval to create a new Digest object + identical to the original. + """ + digest = Digest( + hashing_algorithm=HashingAlgorithm(HashingAlgorithmEnum.MD5), + digest_value=DigestValue(b'\x00\x01\x02\x03'), + key_format_type=KeyFormatType(KeyFormatTypeEnum.RAW)) + byte_value = b'\x00\x01\x02\x03' + + expected = "Digest(" + expected += "hashing_algorithm=HashingAlgorithm(" + expected += "value=HashingAlgorithm.MD5), " + expected += "digest_value=DigestValue(" + expected += "value={0}), ".format(repr(byte_value)) + expected += "key_format_type=KeyFormatType(" + expected += "value=KeyFormatType.RAW))" + observed = repr(digest) + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + # Instead of using eval(repr(digest)), we need to use a manual string, + # eval(manual), due to the name collisions of the HashingAlgorithm + # and KeyFormatType objects and enumerations. + manual = "Digest(" + manual += "hashing_algorithm=HashingAlgorithm(" + manual += "value=HashingAlgorithmEnum.MD5), " + manual += "digest_value=DigestValue(" + manual += "value={0}), ".format(repr(byte_value)) + manual += "key_format_type=KeyFormatType(" + manual += "value=KeyFormatTypeEnum.RAW))" + + expected = digest + observed = eval(manual) + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + def _test_str(self, value, expected): + digest = Digest(digest_value=value) + + observed = str(digest) + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + def test_str_with_no_data(self): + """ + Test that the string representation of a Digest object is formatted + properly when there is no internal data. + """ + data = b'' + digest_value = DigestValue(data) + self._test_str(digest_value, str(data)) + + def test_str_with_data(self): + """ + Test that the string representation of a Digest object is formatted + properly when there is internal data. + """ + data = b'\x00\x01\x02\x03' + digest_value = DigestValue(data) + self._test_str(digest_value, str(data)) + + def _test_create(self, digest, hashing_algorithm, digest_value, + key_format_type): + self.assertIsInstance(digest, Digest) + + expected = HashingAlgorithm(hashing_algorithm) + observed = digest.hashing_algorithm + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + expected = DigestValue(digest_value) + observed = digest.digest_value + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + expected = KeyFormatType(key_format_type) + observed = digest.key_format_type + + msg = "expected {0}, observed {1}".format(expected, observed) + self.assertEqual(expected, observed, msg) + + def test_create_with_defaults(self): + """ + Test that a Digest object can be built using the create class method + with no arguments. + """ + digest = Digest.create() + hashing_algorithm = HashingAlgorithmEnum.SHA_256 + digest_value = b'' + key_format_type = KeyFormatTypeEnum.RAW + + self._test_create(digest, hashing_algorithm, digest_value, + key_format_type) + + def test_create_with_args(self): + """ + Test that a Digest object can be built using the create class method + with arguments. + """ + hashing_algorithm = HashingAlgorithmEnum.MD5 + digest_value = b'\x00\x01\x02\x03' + key_format_type = KeyFormatTypeEnum.PKCS_1 + digest = Digest.create(hashing_algorithm, digest_value, + key_format_type) + + self._test_create(digest, hashing_algorithm, digest_value, + key_format_type) diff --git a/kmip/tests/core/messages/test_messages.py b/kmip/tests/core/messages/test_messages.py index c10954b..1fa5d47 100644 --- a/kmip/tests/core/messages/test_messages.py +++ b/kmip/tests/core/messages/test_messages.py @@ -43,13 +43,13 @@ from kmip.core.keys import RawKey from kmip.core.messages import contents from kmip.core.messages import messages - from kmip.core.messages.payloads import create from kmip.core.messages.payloads import get from kmip.core.messages.payloads import register from kmip.core.messages.payloads import locate from kmip.core.messages.payloads import destroy +from kmip.core.misc import KeyFormatType from kmip.core.primitives import TextString from kmip.core.secrets import SymmetricKey @@ -1329,7 +1329,7 @@ class TestResponseMessage(TestCase): type(key_block))) key_format_type = key_block.key_format_type - exp_type = objects.KeyBlock.KeyFormatType + exp_type = KeyFormatType rcv_type = type(key_format_type) self.assertIsInstance(key_format_type, exp_type, self.msg.format('key_format_type', 'type', diff --git a/kmip/tests/core/misc/test_misc.py b/kmip/tests/core/misc/test_misc.py index 443dacb..ad7f01c 100644 --- a/kmip/tests/core/misc/test_misc.py +++ b/kmip/tests/core/misc/test_misc.py @@ -16,8 +16,10 @@ from six import string_types from testtools import TestCase +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum from kmip.core.enums import QueryFunction as QueryFunctionEnum +from kmip.core.misc import KeyFormatType from kmip.core.misc import QueryFunction from kmip.core.misc import VendorIdentification @@ -115,3 +117,49 @@ class TestVendorIdentification(TestCase): used to construct a VendorIdentification object. """ self._test_init(0) + + +class TestKeyFormatType(TestCase): + """ + A test suite for the KeyFormatType class. + + Since KeyFormatType is a simple wrapper for the Enumeration primitive, + only a few tests pertaining to construction are needed. + """ + + def setUp(self): + super(TestKeyFormatType, self).setUp() + + def tearDown(self): + super(TestKeyFormatType, self).tearDown() + + def _test_init(self, value): + if (isinstance(value, KeyFormatTypeEnum)) or (value is None): + key_format_type = KeyFormatType(value) + + msg = "expected {0}, observed {1}".format( + value, key_format_type.enum) + self.assertEqual(value, key_format_type.enum, msg) + else: + self.assertRaises(TypeError, KeyFormatType, value) + + def test_init_with_none(self): + """ + Test that a KeyFormatType object can be constructed with no specified + value. + """ + self._test_init(None) + + def test_init_with_valid(self): + """ + Test that a KeyFormatType object can be constructed with a valid + KeyFormatType enumeration value. + """ + self._test_init(KeyFormatTypeEnum.RAW) + + def test_init_with_invalid(self): + """ + Test that a TypeError exception is raised when a non KeyFormatType + enumeration value is used to construct a KeyFormatType object. + """ + self._test_init("invalid") diff --git a/kmip/tests/core/test_primitives.py b/kmip/tests/core/test_primitives.py index 28c2bef..d0d7b34 100644 --- a/kmip/tests/core/test_primitives.py +++ b/kmip/tests/core/test_primitives.py @@ -1151,11 +1151,11 @@ class TestByteString(TestCase): super(self.__class__, self).tearDown() def test_init(self): - value = bytearray(b'\x01\x02\x03') + value = b'\x01\x02\x03' bs = ByteString(value) - self.assertIsInstance(bs.value, bytearray, - self.bad_type.format('value', bytearray, + self.assertIsInstance(bs.value, bytes, + self.bad_type.format('value', bytes, type(bs.value))) self.assertEqual(value, bs.value, self.bad_value.format('value', value, bs.value)) @@ -1163,15 +1163,15 @@ class TestByteString(TestCase): def test_init_unset(self): bs = ByteString() - self.assertIsInstance(bs.value, type(None), + self.assertIsInstance(bs.value, bytes, self.bad_type.format('value', type(None), type(bs.value))) - self.assertEqual(None, bs.value, + self.assertEqual(bytes(), bs.value, self.bad_value.format('value', None, bs.value)) def test_validate_on_valid(self): bs = ByteString() - bs.value = bytearray(b'\x00') + bs.value = b'\x00' # Check no exception thrown. bs.validate() @@ -1189,7 +1189,7 @@ class TestByteString(TestCase): self.assertRaises(TypeError, bs.validate) def test_read_value(self): - encoding = (b'\x01\x02\x03\x00\x00\x00\x00\x00') + encoding = b'\x01\x02\x03\x00\x00\x00\x00\x00' self.stream = BytearrayStream(encoding) bs = ByteString() bs.length = 0x03 @@ -1200,7 +1200,7 @@ class TestByteString(TestCase): self.bad_read.format('value', expected, bs.value)) def test_read_value_no_padding(self): - encoding = (b'\x01\x02\x03\x04\x05\x06\x07\x08') + encoding = b'\x01\x02\x03\x04\x05\x06\x07\x08' self.stream = BytearrayStream(encoding) bs = ByteString() bs.length = 0x08 @@ -1211,24 +1211,24 @@ class TestByteString(TestCase): self.bad_read.format('value', expected, bs.value)) def test_read_value_max_padding(self): - encoding = (b'\x01\x00\x00\x00\x00\x00\x00\x00') + encoding = b'\x01\x00\x00\x00\x00\x00\x00\x00' self.stream = BytearrayStream(encoding) bs = ByteString() bs.length = 0x01 bs.read_value(self.stream) - expected = bytearray(b'\x01') + expected = b'\x01' self.assertEqual(expected, bs.value, self.bad_read.format('value', expected, bs.value)) def test_read_value_zero(self): - encoding = (b'\x00\x00\x00\x00\x00\x00\x00\x00') + encoding = b'\x00\x00\x00\x00\x00\x00\x00\x00' self.stream = BytearrayStream(encoding) bs = ByteString() bs.length = 0x01 bs.read_value(self.stream) - expected = bytearray(b'\x00') + expected = b'\x00' self.assertEqual(expected, bs.value, self.bad_read.format('value', expected, bs.value)) @@ -1239,7 +1239,7 @@ class TestByteString(TestCase): bs = ByteString() bs.read(self.stream) - expected = bytearray(b'\x01\x02\x03') + expected = b'\x01\x02\x03' self.assertEqual(expected, bs.value, self.bad_read.format('value', expected, bs.value)) @@ -1252,9 +1252,9 @@ class TestByteString(TestCase): self.assertRaises(errors.ReadValueError, bs.read, self.stream) def test_write_value(self): - encoding = (b'\x01\x02\x03\x00\x00\x00\x00\x00') + encoding = b'\x01\x02\x03\x00\x00\x00\x00\x00' self.stream = BytearrayStream() - value = bytearray(b'\x01\x02\x03') + value = b'\x01\x02\x03' bs = ByteString(value) bs.write_value(self.stream) @@ -1267,9 +1267,9 @@ class TestByteString(TestCase): self.assertEqual(encoding, result, self.bad_encoding) def test_write_value_no_padding(self): - encoding = (b'\x01\x02\x03\x04\x05\x06\x07\x08') + encoding = b'\x01\x02\x03\x04\x05\x06\x07\x08' self.stream = BytearrayStream() - value = bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08') + value = b'\x01\x02\x03\x04\x05\x06\x07\x08' bs = ByteString(value) bs.write_value(self.stream) @@ -1282,9 +1282,9 @@ class TestByteString(TestCase): self.assertEqual(encoding, result, self.bad_encoding) def test_write_value_max_padding(self): - encoding = (b'\x01\x00\x00\x00\x00\x00\x00\x00') + encoding = b'\x01\x00\x00\x00\x00\x00\x00\x00' self.stream = BytearrayStream() - value = bytearray(b'\x01') + value = b'\x01' bs = ByteString(value) bs.write_value(self.stream) @@ -1297,9 +1297,9 @@ class TestByteString(TestCase): self.assertEqual(encoding, result, self.bad_encoding) def test_write_value_zero(self): - encoding = (b'\x00\x00\x00\x00\x00\x00\x00\x00') + encoding = b'\x00\x00\x00\x00\x00\x00\x00\x00' self.stream = BytearrayStream() - value = bytearray(b'\x00') + value = b'\x00' bs = ByteString(value) bs.write_value(self.stream) @@ -1315,7 +1315,7 @@ class TestByteString(TestCase): encoding = (b'\x42\x00\x00\x08\x00\x00\x00\x03\x01\x02\x03\x00\x00\x00' b'\x00\x00') self.stream = BytearrayStream() - value = bytearray(b'\x01\x02\x03') + value = b'\x01\x02\x03' bs = ByteString(value) bs.write(self.stream) diff --git a/kmip/tests/core/test_server.py b/kmip/tests/core/test_server.py index 59044ed..17d7e70 100644 --- a/kmip/tests/core/test_server.py +++ b/kmip/tests/core/test_server.py @@ -21,6 +21,7 @@ from kmip.core.attributes import CryptographicUsageMask from kmip.core.attributes import UniqueIdentifier from kmip.core.attributes import ObjectType from kmip.core.attributes import Name + from kmip.core.enums import AttributeType from kmip.core.enums import CryptographicAlgorithm as CryptoAlgorithmEnum from kmip.core.enums import CryptographicUsageMask as CryptoUsageMaskEnum @@ -30,13 +31,17 @@ from kmip.core.enums import ObjectType as ObjectTypeEnum from kmip.core.enums import ResultReason from kmip.core.enums import ResultStatus from kmip.core.enums import NameType + from kmip.core.factories.attributes import AttributeFactory from kmip.core.keys import RawKey + from kmip.core.messages.contents import KeyCompressionType -from kmip.core.messages.contents import KeyFormatType +from kmip.core.misc import KeyFormatType + from kmip.core.objects import KeyBlock from kmip.core.objects import KeyValueStruct from kmip.core.objects import TemplateAttribute + from kmip.core.secrets import SymmetricKey from kmip.core.server import KMIPImpl @@ -466,7 +471,7 @@ class TestKMIPServer(TestCase): def _get_symmetric_key(self): # only need usage attribute attrs = [self._get_attrs()[1]] - key_format_type = KeyBlock.KeyFormatType(KeyFormatTypeEnum.RAW) + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) key_material = RawKey(self.key) key_value = KeyValueStruct(key_format_type, key_material, attrs) crypto_alg = CryptographicAlgorithm(self.algorithm_name)