Adding support for Digest

This change adds support for the Digest attribute, including updates and
unit test suites for all dependent KMIP objects.
This commit is contained in:
Peter Hamilton 2015-03-10 11:52:53 -04:00
parent f784b67f3a
commit 3e11002567
14 changed files with 938 additions and 75 deletions

View File

@ -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):

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 '<Integer, %s>' % (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):

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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',

View File

@ -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")

View File

@ -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)

View File

@ -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)