diff --git a/.gitignore b/.gitignore index dae79de..d1df503 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ .project .pydevproject *.pyc + +.coverage +.tox/ +ChangeLog +PyKMIP.egg-info/ +dist/ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..87ea0dd --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Nathan Reller +Peter Hamilton diff --git a/LICENSE b/LICENSE index 439b80a..5e81d2a 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,201 @@ -/* FIXME Insert a license here */ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013, Rackspace (http://www.rackspace.com) + + 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. diff --git a/bin/run_server.py b/bin/run_server.py new file mode 100644 index 0000000..f2848cd --- /dev/null +++ b/bin/run_server.py @@ -0,0 +1,70 @@ +# Copyright (c) 2014 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. + +import logging +import optparse +import sys + +from thrift.server import TServer +from thrift.transport import TSocket +from thrift.transport import TTransport + +from kmip.core.server import KMIPImpl + +from kmip.services.kmip_protocol import KMIPProtocolFactory +from kmip.services.kmip_server import Processor + + +def run_server(host='127.0.0.1', port=5696): + logger = logging.getLogger(__name__) + + handler = KMIPImpl() + processor = Processor(handler) + transport = TSocket.TServerSocket(host, port) + tfactory = TTransport.TBufferedTransportFactory() + pfactory = KMIPProtocolFactory() + server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) + + logger.info('Starting the KMIP server') + + try: + server.serve() + except KeyboardInterrupt: + logger.info('KeyboardInterrupt received while serving') + except Exception, e: + logger.info('Exception received while serving: {0}'.format(e)) + finally: + transport.close() + + logger.info('Shutting down KMIP server') + + +def build_cli_parser(): + parser = optparse.OptionParser(usage="%prog [options]", + description="Run KMIP Server") + parser.add_option("-n", "--hostname", action="store", default='127.0.0.1', + dest="hostname", + help="Hostname/IP address of platform running the KMIP " + "server (e.g., localhost, 127.0.0.1)") + parser.add_option("-p", "--port", action="store", default=5696, + dest="port", help="Port number for KMIP services") + return parser + +if __name__ == '__main__': + parser = build_cli_parser() + + opts, args = parser.parse_args(sys.argv[1:]) + + run_server(opts.hostname, opts.port) diff --git a/bin/run_server.sh b/bin/run_server.sh new file mode 100755 index 0000000..ed85da3 --- /dev/null +++ b/bin/run_server.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -eu + +python run_server.py + diff --git a/bin/run_tests.sh b/bin/run_tests.sh new file mode 100755 index 0000000..c6ef0ec --- /dev/null +++ b/bin/run_tests.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -eu + +tox + diff --git a/docs/README b/docs/README new file mode 100644 index 0000000..58feb74 --- /dev/null +++ b/docs/README @@ -0,0 +1 @@ +PyKMIP documentation goes here. \ No newline at end of file diff --git a/kmip/__init__.py b/kmip/__init__.py index e69de29..c91f990 100644 --- a/kmip/__init__.py +++ b/kmip/__init__.py @@ -0,0 +1,57 @@ +# Copyright (c) 2014 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. + +import logging.config +import os +import sys + +path = os.path.join(os.path.dirname(__file__), 'logconfig.ini') + +if os.path.exists(path): + logging.config.fileConfig(path) +else: + minor_version = sys.version_info[1] + + if minor_version == 7: + config = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'simpleFormatter': { + 'format': + '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + } + }, + 'handlers': { + 'consoleHandler': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'simpleFormatter', + 'stream': sys.stdout + } + }, + 'loggers': { + 'root': { + 'level': 'DEBUG', + 'handlers': ['consoleHandler'] + } + } + } + + logging.config.dictConfig(config) + else: + logging.basicConfig() + +__all__ = ['test', 'transport'] diff --git a/kmip/core/__init__.py b/kmip/core/__init__.py new file mode 100644 index 0000000..fc73ed1 --- /dev/null +++ b/kmip/core/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) 2014 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. + +__all__ = ['attributes', 'enums', 'errors', 'messages', 'objects', + 'primitives', 'protocol', 'utils'] diff --git a/kmip/core/attributes.py b/kmip/core/attributes.py new file mode 100644 index 0000000..049f104 --- /dev/null +++ b/kmip/core/attributes.py @@ -0,0 +1,350 @@ +# Copyright (c) 2014 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. + +import enums + +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.primitives import Enumeration +from kmip.core.primitives import TextString + +from utils import BytearrayStream + + +# 3.1 +class UniqueIdentifier(TextString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.UNIQUE_IDENTIFIER) + + +# 3.2 +class Name(Struct): + + class NameValue(TextString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.NAME_VALUE) + + class NameType(Enumeration): + + ENUM_TYPE = enums.NameType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.NAME_TYPE) + + def __init__(self, name_value=None, name_type=None): + super(self.__class__, self).__init__(tag=Tags.NAME) + self.name_value = name_value + self.name_type = name_type + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the value and type of the name + self.name_value = Name.NameValue() + self.name_type = Name.NameType() + self.name_value.read(tstream) + self.name_type.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the value and type of the name + self.name_value.write(tstream) + self.name_type.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + @classmethod + def create(cls, name_value, name_type): + value = cls.NameValue(name_value) + n_type = cls.NameType(name_type) + return Name(name_value=value, + name_type=n_type) + + +# 3.3 +class ObjectType(Enumeration): + + ENUM_TYPE = enums.ObjectType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.OBJECT_TYPE) + + +# 3.4 +class CryptographicAlgorithm(Enumeration): + + ENUM_TYPE = enums.CryptographicAlgorithm + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.CRYPTOGRAPHIC_ALGORITHM) + + +# 3.5 +class CryptographicLength(Integer): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.CRYPTOGRAPHIC_LENGTH) + + +# 3.6 +class CryptographicParameters(Struct): + + class BlockCipherMode(Enumeration): + ENUM_TYPE = enums.BlockCipherMode + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.BLOCK_CIPHER_MODE) + + class PaddingMethod(Enumeration): + ENUM_TYPE = enums.PaddingMethod + + def __init__(self, value=None): + 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 + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.KEY_ROLE_TYPE) + + def __init__(self, + block_cipher_mode=None, + padding_method=None, + hashing_algorithm=None, + key_role_type=None): + super(self.__class__, self).__init__(tag=Tags.CRYPTOGRAPHIC_PARAMETERS) + self.block_cipher_mode = block_cipher_mode + self.padding_method = padding_method + self.hashing_algorithm = hashing_algorithm + self.key_role_type = key_role_type + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + if self.is_tag_next(Tags.BLOCK_CIPHER_MODE, tstream): + self.block_cipher_mode = CryptographicParameters.BlockCipherMode() + self.block_cipher_mode.read(tstream) + + if self.is_tag_next(Tags.PADDING_METHOD, tstream): + self.padding_method = CryptographicParameters.PaddingMethod() + self.padding_method.read(tstream) + + if self.is_tag_next(Tags.HASHING_ALGORITHM, tstream): + self.hashing_algorithm = CryptographicParameters.HashingAlgorithm() + self.hashing_algorithm.read(tstream) + + if self.is_tag_next(Tags.KEY_ROLE_TYPE, tstream): + self.key_role_type = CryptographicParameters.KeyRoleType() + self.key_role_type.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the request payload + if self.block_cipher_mode is not None: + self.block_cipher_mode.write(tstream) + if self.padding_method is not None: + self.padding_method.write(tstream) + if self.hashing_algorithm is not None: + self.hashing_algorithm.write(tstream) + if self.key_role_type is not None: + self.key_role_type.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 3.8 +class CertificateType(Enumeration): + ENUM_TYPE = enums.CertificateType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.CERTIFICATE_TYPE) + + +# 3.19 +class CryptographicUsageMask(Integer): + + ENUM_TYPE = enums.CryptographicUsageMask + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.CRYPTOGRAPHIC_USAGE_MASK) + + +# 3.33 +class ObjectGroup(TextString): + + def __init__(self, value=None): + super(self.__class__, + self).__init__(value, Tags.OBJECT_GROUP) + + +# 3.36 +class ApplicationSpecificInformation(Struct): + + class ApplicationNamespace(TextString): + + def __init__(self, value=None): + super(self.__class__, + self).__init__(value, Tags.APPLICATION_NAMESPACE) + + class ApplicationData(TextString): + + def __init__(self, value=None): + super(self.__class__, + self).__init__(value, Tags.APPLICATION_DATA) + + def __init__(self, application_namespace=None, + application_data=None): + super(self.__class__, + self).__init__(Tags.APPLICATION_SPECIFIC_INFORMATION) + + self.application_namespace = application_namespace + self.application_data = application_data + + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.application_namespace = self.ApplicationNamespace() + self.application_data = self.ApplicationData() + + self.application_namespace.read(tstream) + self.application_data.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.application_namespace.write(tstream) + self.application_data.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + name = self.__class__.__name__ + msg = ErrorStrings.BAD_EXP_RECV + + if self.application_namespace is not None: + if self.application_data is None: + member = 'application_data' + raise ValueError(msg.format('{}.{}'.format(name, member), + 'value', 'not None', 'None')) + else: + member = 'application_namespace' + exp_type = self.ApplicationNamespace + if not isinstance(self.application_namespace, exp_type): + rcv_type = type(self.application_namespace) + raise TypeError(msg.format('{}.{}'.format(name, member), + 'type', exp_type, rcv_type)) + + if self.application_data is not None: + if self.application_namespace is None: + member = 'application_namespace' + raise ValueError(msg.format('{}.{}'.format(name, member), + 'value', 'not None', 'None')) + else: + member = 'application_data' + exp_type = self.ApplicationData + if not isinstance(self.application_data, exp_type): + rcv_type = type(self.application_data) + raise TypeError(msg.format('{}.{}'.format(name, member), + 'type', exp_type, rcv_type)) + + @classmethod + def create(cls, application_namespace, application_data): + namespace = ApplicationSpecificInformation.\ + ApplicationNamespace(application_namespace) + data = ApplicationSpecificInformation.\ + ApplicationData(application_data) + return ApplicationSpecificInformation(application_namespace=namespace, + application_data=data) + + +# 3.37 +class ContactInformation(TextString): + + def __init__(self, value=None): + super(self.__class__, + self).__init__(value, Tags.CONTACT_INFORMATION) + + +# 3.39 +# TODO (peter-hamilton) A CustomAttribute TextString is not sufficient to +# TODO (peter-hamilton) cover all potential custom attributes. This is a +# TODO (peter-hamilton) temporary stopgap. +class CustomAttribute(TextString): + + def __init__(self, value=None): + super(self.__class__, + self).__init__(value, Tags.ATTRIBUTE_VALUE) diff --git a/kmip/core/enums.py b/kmip/core/enums.py new file mode 100644 index 0000000..e29df2f --- /dev/null +++ b/kmip/core/enums.py @@ -0,0 +1,561 @@ +# Copyright (c) 2014 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. + +# In case of new content, remove the following line to enable flake8 tests +# flake8: noqa + +from enum import Enum + + +class AttributeType(Enum): + UNIQUE_IDENTIFIER = 'Unique Identifier' + NAME = 'Name' + OBJECT_TYPE = 'Object Type' + CRYPTOGRAPHIC_ALGORITHM = 'Cryptographic Algorithm' + CRYPTOGRAPHIC_LENGTH = 'Cryptographic Length' + CRYPTOGRAPHIC_PARAMETERS = 'Cryptographic Parameters' + CRYPTOGRAPHIC_DOMAIN_PARAMETERS = 'Cryptographic Domain Parameters' + CERTIFICATE_TYPE = 'Certificate Type' + CERTIFICATE_LENGTH = 'Certificate Length' + X_509_CERTIFICATE_IDENTIFIER = 'X.509 Certificate Identifier' + X_509_CERTIFICATE_SUBJECT = 'X.509 Certificate Subject' + X_509_CERTIFICATE_ISSUER = 'X.509 Certificate Issuer' + CERTIFICATE_IDENTIFIER = 'Certificate Identifier' + CERTIFICATE_SUBJECT = 'Certificate Subject' + CERTIFICATE_ISSUER = 'Certificate Issuer' + DIGITAL_SIGNATURE_ALGORITHM = 'Digital Signature Algorithm' + DIGEST = 'Digest' + OPERATION_POLICY_NAME = 'Operation Policy Name' + CRYPTOGRAPHIC_USAGE_MASK = 'Cryptographic Usage Mask' + LEASE_TIME = 'Lease Time' + USAGE_LIMITS = 'Usage Limits' + STATE = 'State' + INITIAL_DATE = 'Initial Date' + ACTIVATION_DATE = 'Activation Date' + PROCESS_START_DATE = 'Process Start Date' + PROTECT_STOP_DATE = 'Protect Stop Date' + DEACTIVATION_DATE = 'Deactivation Date' + DESTROY_DATE = 'Destroy Date' + COMPROMISE_OCCURRENCE_DATE = 'Compromise Occurrence Date' + COMPROMISE_DATE = 'Compromise Date' + REVOCATION_REASON = 'Revocation Reason' + ARCHIVE_DATE = 'Archive Date' + OBJECT_GROUP = 'Object Group' + FRESH = 'Fresh' + LINK = 'Link' + APPLICATION_SPECIFIC_INFORMATION = 'Application Specific Information' + CONTACT_INFORMATION = 'Contact Information' + LAST_CHANGE_DATE = 'Last Change Date' + CUSTOM_ATTRIBUTE = 'Custom Attribute' + + +# 9.1.1.2 +class Types(Enum): + DEFAULT = 0x00 + STRUCTURE = 0x01 + INTEGER = 0x02 + LONG_INTEGER = 0x03 + BIG_INTEGER = 0x04 + ENUMERATION = 0x05 + BOOLEAN = 0x06 + TEXT_STRING = 0x07 + BYTE_STRING = 0x08 + DATE_TIME = 0x09 + INTERVAL = 0x0A + + +# 9.1.3.1 +class Tags(Enum): + DEFAULT = 0x420000 + ACTIVATION_DATE = 0x420001 + APPLICATION_DATA = 0x420002 + APPLICATION_NAMESPACE = 0x420003 + APPLICATION_SPECIFIC_INFORMATION = 0x420004 + ARCHIVE_DATE = 0x420005 + ASYNCHRONOUS_CORRELATION_VALUE = 0x420006 + ASYNCHRONOUS_INDICATOR = 0x420007 + ATTRIBUTE = 0x420008 + ATTRIBUTE_INDEX = 0x420009 + ATTRIBUTE_NAME = 0x42000A + ATTRIBUTE_VALUE = 0x42000B + AUTHENTICATION = 0x42000C + BATCH_COUNT = 0x42000D + BATCH_ERROR_CONTINUATION_OPTION = 0x42000E + BATCH_ITEM = 0x42000F + BATCH_ORDER_OPTION = 0x420010 + BLOCK_CIPHER_MODE = 0x420011 + CANCELLATION_RESULT = 0x420012 + CERTIFICATE = 0x420013 + CERTIFICATE_IDENTIFIER = 0x420014 # DEPRECATED + CERTIFICATE_ISSUER = 0x420015 # DEPRECATED + CERTIFICATE_ISSUER_ALTERNATIVE_NAME = 0x420016 # DEPRECATED + CERTIFICATE_ISSUER_DISTINGUISHED_NAME = 0x420017 # DEPRECATED + CERTIFICATE_REQUEST = 0x420018 + CERTIFICATE_REQUEST_TYPE = 0x420019 + CERTIFICATE_SUBJECT = 0x42001A # DEPRECATED + CERTIFICATE_SUBJECT_ALTERNATIVE_NAME = 0x42001B # DEPRECATED + CERTIFICATE_SUBJECT_DISTINGUISHED_NAME = 0x42001C # DEPRECATED + CERTIFICATE_TYPE = 0x42001D + CERTIFICATE_VALUE = 0x42001E + COMMON_TEMPLATE_ATTRIBUTE = 0x42001F + COMPROMISE_DATE = 0x420020 + COMPROMISE_OCCURRENCE_DATE = 0x420021 + CONTACT_INFORMATION = 0x420022 + CREDENTIAL = 0x420023 + CREDENTIAL_TYPE = 0x420024 + CREDENTIAL_VALUE = 0x420025 + CRITICALITY_INDICATOR = 0x420026 + CRT_COEFFICIENT = 0x420027 + CRYPTOGRAPHIC_ALGORITHM = 0x420028 + CRYPTOGRAPHIC_DOMAIN_PARAMETERS = 0x420029 + CRYPTOGRAPHIC_LENGTH = 0x42002A + CRYPTOGRAPHIC_PARAMETERS = 0x42002B + CRYPTOGRAPHIC_USAGE_MASK = 0x42002C + CUSTOM_ATTRIBUTE = 0x42002D + D = 0x42002E + DEACTIVATION_DATE = 0x42002F + DERIVATION_DATA = 0x420030 + DERIVATION_METHOD = 0x420031 + DERIVATION_PARAMETERS = 0x420032 + DESTROY_DATE = 0x420033 + DIGEST = 0x420034 + DIGEST_VALUE = 0x420035 + ENCRYPTION_KEY_INFORMATION = 0x420036 + G = 0x420037 + HASHING_ALGORITHM = 0x420038 + INITIAL_DATE = 0x420039 + INITIALIZATION_VECTOR = 0x42003A + ISSUER = 0x42003B # DEPRECATED + ITERATION_COUNT = 0x42003C + IV_COUNTER_NONCE = 0x42003D + J = 0x42003E + KEY = 0x42003F + KEY_BLOCK = 0x420040 + KEY_COMPRESSION_TYPE = 0x420041 + KEY_FORMAT_TYPE = 0x420042 + KEY_MATERIAL = 0x420043 + KEY_PART_IDENTIFIER = 0x420044 + KEY_VALUE = 0x420045 + KEY_WRAPPING_DATA = 0x420046 + KEY_WRAPPING_SPECIFICATION = 0x420047 + LAST_CHANGE_DATE = 0x420048 + LEASE_TIME = 0x420049 + LINK = 0x42004A + LINK_TYPE = 0x42004B + LINKED_OBJECT_IDENTIFIER = 0x42004C + MAC_SIGNATURE = 0x42004D + MAC_SIGNATURE_KEY_INFORMATION = 0x42004E + MAXIMUM_ITEMS = 0x42004F + MAXIMUM_RESPONSE_SIZE = 0x420050 + MESSAGE_EXTENSION = 0x420051 + MODULUS = 0x420052 + NAME = 0x420053 + NAME_TYPE = 0x420054 + NAME_VALUE = 0x420055 + OBJECT_GROUP = 0x420056 + OBJECT_TYPE = 0x420057 + OFFSET = 0x420058 + OPAQUE_DATA_TYPE = 0x420059 + OPAQUE_DATA_VALUE = 0x42005A + OPAQUE_OBJECT = 0x42005B + OPERATION = 0x42005C + OPERATION_POLICY_NAME = 0x42005D + P = 0x42005E + PADDING_METHOD = 0x42005F + PRIME_EXPONENT_P = 0x420060 + PRIME_EXPONENT_Q = 0x420061 + PRIME_FIELD_SIZE = 0x420062 + PRIVATE_EXPONENT = 0x420063 + PRIVATE_KEY = 0x420064 + PRIVATE_KEY_TEMPLATE_ATTRIBUTE = 0x420065 + PRIVATE_KEY_UNIQUE_IDENTIFIER = 0x420066 + PROCESS_START_DATE = 0x420067 + PROTECT_STOP_DATE = 0x420068 + PROTOCOL_VERSION = 0x420069 + PROTOCOL_VERSION_MAJOR = 0x42006A + PROTOCOL_VERSION_MINOR = 0x42006B + PUBLIC_EXPONENT = 0x42006C + PUBLIC_KEY = 0x42006D + PUBLIC_KEY_TEMPLATE_ATTRIBUTE = 0x42006E + PUBLIC_KEY_UNIQUE_IDENTIFIER = 0x42006F + PUT_FUNCTION = 0x420070 + Q = 0x420071 + Q_STRING = 0x420072 + QLENGTH = 0x420073 + QUERY_FUNCTION = 0x420074 + RECOMMENDED_CURVE = 0x420075 + REPLACED_UNIQUE_IDENTIFIER = 0x420076 + REQUEST_BATCH_ITEM = 0x42000F + REQUEST_HEADER = 0x420077 + REQUEST_MESSAGE = 0x420078 + REQUEST_PAYLOAD = 0x420079 + RESPONSE_BATCH_ITEM = 0x42000F + RESPONSE_HEADER = 0x42007A + RESPONSE_MESSAGE = 0x42007B + RESPONSE_PAYLOAD = 0x42007C + RESULT_MESSAGE = 0x42007D + RESULT_REASON = 0x42007E + RESULT_STATUS = 0x42007F + REVOCATION_MESSAGE = 0x420080 + REVOCATION_REASON = 0x420081 + REVOCATION_REASON_CODE = 0x420082 + KEY_ROLE_TYPE = 0x420083 + SALT = 0x420084 + SECRET_DATA = 0x420085 + SECRET_DATA_TYPE = 0x420086 + SERIAL_NUMBER = 0x420087 # DEPRECATED + SERVER_INFORMATION = 0x420088 + SPLIT_KEY = 0x420089 + SPLIT_KEY_METHOD = 0x42008A + SPLIT_KEY_PARTS = 0x42008B + SPLIT_KEY_THRESHOLD = 0x42008C + STATE = 0x42008D + STORAGE_STATUS_MASK = 0x42008E + SYMMETRIC_KEY = 0x42008F + TEMPLATE = 0x420090 + TEMPLATE_ATTRIBUTE = 0x420091 + TIME_STAMP = 0x420092 + UNIQUE_BATCH_ITEM_ID = 0x420093 + UNIQUE_IDENTIFIER = 0x420094 + USAGE_LIMITS = 0x420095 + USAGE_LIMITS_COUNT = 0x420096 + USAGE_LIMITS_TOTAL = 0x420097 + USAGE_LIMITS_UNIT = 0x420098 + USERNAME = 0x420099 + VALIDITY_DATE = 0x42009A + VALIDITY_INDICATOR = 0x42009B + VENDOR_EXTENSION = 0x42009C + VENDOR_IDENTIFICATION = 0x42009D + WRAPPING_METHOD = 0x42009E + X = 0x42009F + Y = 0x4200A0 + PASSWORD = 0x4200A1 + DEVICE_IDENTIFIER = 0x4200A2 + ENCODING_OPTION = 0x4200A3 + EXTENSION_INFORMATION = 0x4200A4 + EXTENSION_NAME = 0x4200A5 + EXTENSION_TAG = 0x4200A6 + EXTENSION_TYPE = 0x4200A7 + FRESH = 0x4200A8 + MACHINE_IDENTIFIER = 0x4200A9 + MEDIA_IDENTIFIER = 0x4200AA + NETWORK_IDENTIFIER = 0x4200AB + OBJECT_GROUP_MEMBER = 0x4200AC + CERTIFICATE_LENGTH = 0x4200AD + DIGITAL_SIGNATURE_ALGORITHM = 0x4200AE + CERTIFICATE_SERIAL_NUMBER = 0x4200AF + DEVICE_SERIAL_NUMBER = 0x4200B0 + ISSUER_ALTERNATIVE_NAME = 0x4200B1 + ISSUER_DISTINGUISHED_NAME = 0x4200B2 + SUBJECT_ALTERNATIVE_NAME = 0x4200B3 + SUBJECT_DISTINGUISHED_NAME = 0x4200B4 + X_509_CERTIFICATE_IDENTIFER = 0x4200B5 + X_509_CERTIFICATE_ISSUER = 0x4200B6 + X_509_CERTIFICATE_SUBJECT = 0x4200B7 + + +# 9.1.3.2.1 +class CredentialType(Enum): + USERNAME_AND_PASSWORD = 0x00000001 + DEVICE = 0x00000002 + + +# 9.1.3.2.2 +class KeyCompressionType(Enum): + EC_PUBLIC_KEY_TYPE_UNCOMPRESSED = 0x00000001 + EC_PUBLIC_KEY_TYPE_X9_62_COMPRESSED_PRIME = 0x00000002 + EC_PUBLIC_KEY_TYPE_X9_62_COMPRESSED_CHAR2 = 0x00000003 + EC_PUBLIC_KEY_TYPE_X9_62_HYBRID = 0x00000004 + + +# 9.1.3.2.3 +class KeyFormatType(Enum): + RAW = 0x00000001 + OPAQUE = 0x00000002 + PKCS_1 = 0x00000003 + PKCS_8 = 0x00000004 + X_509 = 0x00000005 + EC_PRIVATE_KEY = 0x00000006 + TRANSPARENT_SYMMETRIC_KEY = 0x00000007 + TRANSPARENT_DSA_PRIVATE_KEY = 0x00000008 + TRANSPARENT_DSA_PUBLIC_KEY = 0x00000009 + TRANSPARENT_RSA_PRIVATE_KEY = 0x0000000A + TRANSPARENT_RSA_PUBLIC_KEY = 0x0000000B + TRANSPARENT_DH_PRIVATE_KEY = 0x0000000C + TRANSPARENT_DH_PUBLIC_KEY = 0x0000000D + TRANSPARENT_ECDSA_PRIVATE_KEY = 0x0000000E + TRANSPARENT_ECDSA_PUBLIC_KEY = 0x0000000F + TRANSPARENT_ECDH_PRIVATE_KEY = 0x00000010 + TRANSPARENT_ECDH_PUBLIC_KEY = 0x00000011 + TRANSPARENT_ECMQV_PRIVATE_KEY = 0x00000012 + TRANSPARENT_ECMQV_PUBLIC_KEY = 0x00000013 + + +# 9.1.3.2.4 +class WrappingMethod(Enum): + ENCRYPT = 0x00000001 + MAC_SIGN = 0x00000002 + ENCRYPT_THEN_MAC_SIGN = 0x00000003 + MAC_SIGN_THEN_ENCRYPT = 0x00000004 + TR_31 = 0x00000005 + + +# 9.1.3.2.6 +class CertificateType(Enum): + X_509 = 0x00000001 + PGP = 0x00000002 + + +# 9.1.3.2.8 +class SplitKeyMethod(Enum): + XOR = 0x00000001 + POLYNOMIAL_SHARING_GF = 0x00000002 + POLYNOMIAL_SHARING_PRIME_FIELD = 0x00000003 + + +# 9.1.3.2.9 +class SecretDataType(Enum): + PASSWORD = 0x00000001 + SEED = 0x00000002 + + +# 9.1.3.2.10 +class OpaqueDataType(Enum): + pass + + +# 9.1.3.2.11 +class NameType(Enum): + UNINTERPRETED_TEXT_STRING = 0x00000001 + URI = 0x00000002 + + +# 9.1.3.2.12 +class ObjectType(Enum): + CERTIFICATE = 0x00000001 + SYMMETRIC_KEY = 0x00000002 + PUBLIC_KEY = 0x00000003 + PRIVATE_KEY = 0x00000004 + SPLIT_KEY = 0x00000005 + TEMPLATE = 0x00000006 + SECRET_DATA = 0x00000007 + OPAQUE_DATA = 0x00000008 + + +# 9.1.3.2.13 +class CryptographicAlgorithm(Enum): + DES = 0x00000001 + TRIPLE_DES = 0x00000002 # '3DES' is invalid syntax + AES = 0x00000003 + RSA = 0x00000004 + DSA = 0x00000005 + ECDSA = 0x00000006 + HMAC_SHA1 = 0x00000007 + HMAC_SHA224 = 0x00000008 + HMAC_SHA256 = 0x00000009 + HMAC_SHA384 = 0x0000000A + HMAC_SHA512 = 0x0000000B + HMAC_MD5 = 0x0000000C + DH = 0x0000000D + ECDH = 0x0000000E + ECMQV = 0x0000000F + BLOWFISH = 0x00000010 + CAMELLIA = 0x00000011 + CAST5 = 0x00000012 + IDEA = 0x00000013 + MARS = 0x00000014 + RC2 = 0x00000015 + RC4 = 0x00000016 + RC5 = 0x00000017 + SKIPJACK = 0x00000018 + TWOFISH = 0x00000019 + + +# 9.1.3.2.14 +class BlockCipherMode(Enum): + CBC = 0x00000001 + ECB = 0x00000002 + PCBC = 0x00000003 + CFB = 0x00000004 + OFB = 0x00000005 + CTR = 0x00000006 + CMAC = 0x00000007 + CCM = 0x00000008 + GCM = 0x00000009 + CBC_MAC = 0x0000000A + XTS = 0x0000000B + AES_KEY_WRAP_PADDING = 0x0000000C + NIST_KEY_WRAP = 0x0000000D + X9_102_AESKW = 0x0000000E + X9_102_TDKW = 0x0000000F + X9_102_AKW1 = 0x00000010 + X9_102_AKW2 = 0x00000011 + + +# 9.1.3.2.15 +class PaddingMethod(Enum): + NONE = 0x00000001 + OAEP = 0x00000002 + PKCS5 = 0x00000003 + SSL3 = 0x00000004 + ZEROS = 0x00000005 + ANSI_X9_23 = 0x00000006 + ISO_10126 = 0x00000007 + PKCS1_V_1_5 = 0x00000008 + X9_31 = 0x00000009 + PSS = 0x0000000A + + +# 9.1.3.2.16 +class HashingAlgorithm(Enum): + MD2 = 0x00000001 + MD4 = 0x00000002 + MD5 = 0x00000003 + SHA_1 = 0x00000004 + SHA_224 = 0x00000005 + SHA_256 = 0x00000006 + SHA_384 = 0x00000007 + SHA_512 = 0x00000008 + RIPEMD_160 = 0x00000009 + TIGER = 0x0000000A + WHIRLPOOL = 0x0000000B + + +# 9.1.3.2.17 +class KeyRoleType(Enum): + BDK = 0x00000001 + CVK = 0x00000002 + DEK = 0x00000003 + MKAC = 0x00000004 + MKSMC = 0x00000005 + MKSMI = 0x00000006 + MKDAC = 0x00000007 + MKDN = 0x00000008 + MKCP = 0x00000009 + MKOTH = 0x0000000A + KEK = 0x0000000B + MAC_16609 = 0x0000000C + MAC_97971 = 0x0000000D + MAC_97972 = 0x0000000E + MAC_97973 = 0x0000000F + MAC_97974 = 0x00000010 + MAC_97975 = 0x00000011 + ZPK = 0x00000012 + PVKIBM = 0x00000013 + PVKPVV = 0x00000014 + PVKOTH = 0x00000015 + + +# 9.1.3.2.27 +class Operation(Enum): + CREATE = 0x00000001 + CREATE_KEY_PAIR = 0x00000002 + REGISTER = 0x00000003 + REKEY = 0x00000004 + DERIVE_KEY = 0x00000005 + CERTIFY = 0x00000006 + RECERTIFY = 0x00000007 + LOCATE = 0x00000008 + CHECK = 0x00000009 + GET = 0x0000000A + GET_ATTRIBUTES = 0x0000000B + GET_ATTRIBUTE_LIST = 0x0000000C + ADD_ATTRIBUTE = 0x0000000D + MODIFY_ATTRIBUTE = 0x0000000E + DELETE_ATTRIBUTE = 0x0000000F + OBTAIN_LEASE = 0x00000010 + GET_USAGE_ALLOCATION = 0x00000011 + ACTIVATE = 0x00000012 + REVOKE = 0x00000013 + DESTROY = 0x00000014 + ARCHIVE = 0x00000015 + RECOVER = 0x00000016 + VALIDATE = 0x00000017 + QUERY = 0x00000018 + CANCEL = 0x00000019 + POLL = 0x0000001A + NOTIFY = 0x0000001B + PUT = 0x0000001C + REKEY_KEY_PAIR = 0x0000001D + DISCOVER_VERSIONS = 0x0000001E + + +# 9.1.3.2.28 +class ResultStatus(Enum): + SUCCESS = 0x00000000 + OPERATION_FAILED = 0x00000001 + OPERATION_PENDING = 0x00000002 + OPERATION_UNDONE = 0x00000003 + + +# 9.1.3.2.29 +class ResultReason(Enum): + ITEM_NOT_FOUND = 0x00000001 + RESPONSE_TOO_LARGE = 0x00000002 + AUTHENTICATION_NOT_SUCCESSFUL = 0x00000003 + INVALID_MESSAGE = 0x00000004 + OPERATION_NOT_SUPPORTED = 0x00000005 + MISSING_DATA = 0x00000006 + INVALID_FIELD = 0x00000007 + FEATURE_NOT_SUPPORTED = 0x00000008 + OPERATION_CANCELED_BY_REQUESTER = 0x00000009 + CRYPTOGRAPHIC_FAILURE = 0x0000000A + ILLEGAL_OPERATION = 0x0000000B + PERMISSION_DENIED = 0x0000000C + OBJECT_ARCHIVED = 0x0000000D + INDEX_OUT_OF_BOUNDS = 0x0000000E + APPLICATION_NAMESPACE_NOT_SUPPORTED = 0x0000000F + KEY_FORMAT_TYPE_NOT_SUPPORTED = 0x00000010 + KEY_COMPRESSION_TYPE_NOT_SUPPORTED = 0x00000011 + ENCODING_OPTION_ERROR = 0x00000012 + GENERAL_FAILURE = 0x00000100 + + +# 9.1.3.2.30 +class BatchErrorContinuationOption(Enum): + CONTINUE = 0x00000001 + STOP = 0x00000002 + UNDO = 0x00000003 + + +# 9.1.3.2.32 +class EncodingOption(Enum): + NO_ENCODING = 0x00000001 + TTLV_ENCODING = 0x00000002 + + +# 9.1.3.3 +# 9.1.3.3.1 +class CryptographicUsageMask(Enum): + SIGN = 0x00000001 + VERIFY = 0x00000002 + ENCRYPT = 0x00000004 + DECRYPT = 0x00000008 + WRAP_KEY = 0x00000010 + UNWRAP_KEY = 0x00000020 + EXPORT = 0x00000040 + MAC_GENERATE = 0x00000080 + MAC_VERIFY = 0x00000100 + DERIVE_KEY = 0x00000200 + CONTENT_COMMITMENT = 0x00000400 + KEY_AGREEMENT = 0x00000800 + CERTIFICATE_SIGN = 0x00001000 + CRL_SIGN = 0x00002000 + GENERATE_CRYPTOGRAM = 0x00004000 + VALIDATE_CRYPTOGRAM = 0x00008000 + TRANSLATE_ENCRYPT = 0x00010000 + TRANSLATE_DECRYPT = 0x00020000 + TRANSLATE_WRAP = 0x00040000 + TRANSLATE_UNWRAP = 0x00080000 diff --git a/kmip/core/errors.py b/kmip/core/errors.py new file mode 100644 index 0000000..e6712b7 --- /dev/null +++ b/kmip/core/errors.py @@ -0,0 +1,130 @@ +# Copyright (c) 2014 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. + + +class ErrorStrings: + BAD_EXP_RECV = "Bad {0} {1}: expected {2}, received {3}" + BAD_ENCODING = "Bad {0} {1}: encoding mismatch" + + +class BaseError(Exception): + """Base class for exceptions defined in this module.""" + + def __init__(self, args): + [setattr(self, k, v) for k, v in args.iteritems() if k is not 'self'] + + +class KMIPServerError(BaseError): + """Base Exception for KMIP server errors.""" + def __init__(self, args): + super(KMIPServerError, self).__init__(args) + + +class KMIPServerZombieError(KMIPServerError): + """KMIP server error for hung and persistent live KMIP servers.""" + def __init__(self, pid): + message = 'KMIP server alive after termination: PID {0}'.format(pid) + super(KMIPServerZombieError, self).__init__({'message': message}) + + def __str__(self): + return self.message + + +class KMIPServerSuicideError(KMIPServerError): + """KMIP server error for prematurely dead KMIP servers.""" + def __init__(self, pid): + message = 'KMIP server dead prematurely: PID {0}'.format(pid) + super(KMIPServerSuicideError, self).__init__({'message': message}) + + def __str__(self): + return self.message + + +class InitError(BaseError): + """Exception thrown for bad initializations.""" + def __init__(self, cls, exp, recv): + super(InitError, self).__init__(locals()) + + def __str__(self): + msg = "Tried to initialize {0} instance with bad type: " + msg += "expected {1}, received {2}" + return msg.format(self.cls, self.exp, self.recv) + + +class WriteValueError(BaseError): + def __init__(self, cls, attr, value): + super(WriteValueError, self).__init__(locals()) + + def __str__(self): + msg = "Tried to write {0}.{1} with invalid value: {2}" + return msg.format(self.cls, self.attr, self.value) + + +class WriteTypeError(BaseError): + def __init__(self, cls, attr, value): + super(WriteTypeError, self).__init__(locals()) + + def __str__(self): + msg = "Tried to write {0}.{1} with invalid type: {2}" + return msg.format(self.cls, self.attr, self.value) + + +class WriteOverflowError(BaseError): + def __init__(self, cls, attr, exp, recv): + super(WriteOverflowError, self).__init__(locals()) + + def __str__(self): + msg = "Tried to write {0}.{1} with too many bytes: " + msg += "expected {2}, received {3}" + return msg.format(self.cls, self.attr, self.exp, self.recv) + + +class ReadValueError(BaseError): + def __init__(self, cls, attr, exp, recv): + super(ReadValueError, self).__init__(locals()) + + def __str__(self): + msg = "Tried to read {0}.{1}: expected {2}, received {3}" + return msg.format(self.cls, self.attr, self.exp, self.recv) + + +class InvalidLengthError(ValueError): + def __init__(self, cls, exp, recv): + msg = "Invalid length read for {0}: expected {1}, received {2}" + super(InvalidLengthError, self).__init__(msg.format(cls, exp, recv)) + + +class StreamNotEmptyError(BaseError): + def __init__(self, cls, extra): + super(StreamNotEmptyError, self).__init__(locals()) + + def __str__(self): + msg = "Invalid length used to read {0}, bytes remaining: {1}" + return msg.format(self.cls, self.extra) + + +class StateTypeError(TypeError): + def __init__(self, cls, exp, recv): + msg = "Tried to initialize {0} instance with bad type: " + msg += "expected {1}, received {2}" + super(StateTypeError, self).__init__(msg.format(cls, exp, recv)) + + +class StateOverflowError(ValueError): + def __init__(self, cls, attr, exp, recv): + msg = "Tried to write {0}.{1} with too many bytes: " + msg += "expected {2}, received {3}" + super(StateOverflowError, self).__init__(msg.format(cls, attr, exp, + recv)) diff --git a/kmip/core/factories/__init__.py b/kmip/core/factories/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/core/factories/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/core/factories/attribute_values.py b/kmip/core/factories/attribute_values.py new file mode 100644 index 0000000..d0ab783 --- /dev/null +++ b/kmip/core/factories/attribute_values.py @@ -0,0 +1,290 @@ +# Copyright (c) 2014 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 kmip.core.enums import AttributeType + +from kmip.core.attributes import ApplicationSpecificInformation +from kmip.core.attributes import ContactInformation +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 Name +from kmip.core.attributes import ObjectGroup +from kmip.core.attributes import UniqueIdentifier + +from kmip.core import utils + + +class AttributeValueFactory(object): + + def create_attribute_value(self, name, value): + # Switch on the name of the attribute + if name is AttributeType.UNIQUE_IDENTIFIER: + value = self._create_unique_identifier(value) + elif name is AttributeType.NAME: + value = self._create_name(value) + elif name is AttributeType.OBJECT_TYPE: + value = self._create_object_type(value) + elif name is AttributeType.CRYPTOGRAPHIC_ALGORITHM: + value = self._create_cryptographic_algorithm(value) + elif name is AttributeType.CRYPTOGRAPHIC_LENGTH: + value = self._create_cryptographic_length(value) + elif name is AttributeType.CRYPTOGRAPHIC_PARAMETERS: + value = self._create_cryptographic_parameters(value) + elif name is AttributeType.CRYPTOGRAPHIC_DOMAIN_PARAMETERS: + value = self._create_cryptographic_domain_parameters(value) + elif name is AttributeType.CERTIFICATE_TYPE: + value = self._create_certificate_type(value) + elif name is AttributeType.CERTIFICATE_LENGTH: + value = self._create_certificate_length(value) + elif name is AttributeType.X_509_CERTIFICATE_IDENTIFIER: + value = self._create_x_509_certificate_identifier(value) + elif name is AttributeType.X_509_CERTIFICATE_SUBJECT: + value = self._create_x_509_certificate_subject(value) + elif name is AttributeType.X_509_CERTIFICATE_ISSUER: + value = self._create_x_509_certificate_issuer(value) + elif name is AttributeType.CERTIFICATE_IDENTIFIER: + value = self._create_certificate_identifier(value) + elif name is AttributeType.CERTIFICATE_SUBJECT: + value = self._create_certificate_subject(value) + elif name is AttributeType.CERTIFICATE_ISSUER: + value = self._create_certificate_issuer(value) + elif name is AttributeType.DIGITAL_SIGNATURE_ALGORITHM: + value = self._create_digital_signature_algorithm(value) + elif name is AttributeType.DIGEST: + value = self._create_digest(value) + elif name is AttributeType.OPERATION_POLICY_NAME: + value = self._create_operation_policy_name(value) + elif name is AttributeType.CRYPTOGRAPHIC_USAGE_MASK: + value = self._create_cryptographic_usage_mask(value) + elif name is AttributeType.LEASE_TIME: + value = self._create_lease_time(value) + elif name is AttributeType.USAGE_LIMITS: + value = self._create_usage_limits(value) + elif name is AttributeType.STATE: + value = self._create_state(value) + elif name is AttributeType.INITIAL_DATE: + value = self._create_initial_date(value) + elif name is AttributeType.ACTIVATION_DATE: + value = self._create_activation_date(value) + elif name is AttributeType.PROCESS_START_DATE: + value = self._create_process_start_date(value) + elif name is AttributeType.PROTECT_STOP_DATE: + value = self._create_protect_stop_date(value) + elif name is AttributeType.DEACTIVATION_DATE: + value = self._create_deactivation_date(value) + elif name is AttributeType.DESTROY_DATE: + value = self._create_destroy_date(value) + elif name is AttributeType.COMPROMISE_OCCURRENCE_DATE: + value = self._create_compromise_occurrence_date(value) + elif name is AttributeType.COMPROMISE_DATE: + value = self._create_compromise_date(value) + elif name is AttributeType.REVOCATION_REASON: + value = self._create_revocation_reason(value) + elif name is AttributeType.ARCHIVE_DATE: + value = self._create_archive_date(value) + elif name is AttributeType.OBJECT_GROUP: + value = self._create_object_group(value) + elif name is AttributeType.FRESH: + value = self._create_fresh(value) + elif name is AttributeType.LINK: + value = self._create_link(value) + elif name is AttributeType.APPLICATION_SPECIFIC_INFORMATION: + value = self._create_application_specific_information(value) + elif name is AttributeType.CONTACT_INFORMATION: + value = self._create_contact_information(value) + elif name is AttributeType.LAST_CHANGE_DATE: + value = self._create_last_change_date(value) + elif name is AttributeType.CUSTOM_ATTRIBUTE: + value = self._create_custom_attribute(value) + else: + if not isinstance(name, str): + raise ValueError('Unrecognized attribute type: ' + '{}'.format(name)) + elif name.startswith('x-'): + # Custom attribute indicated + value = self._create_custom_attribute(value) + + return value + + def _create_unique_identifier(self, uuid): + return UniqueIdentifier(uuid) + + def _create_name(self, name): + if name is not None: + name_value = name.get('name_value') + name_type = name.get('name_type') + + return Name.create(name_value, name_type) + else: + return Name() + + def _create_object_type(self, obj): + raise NotImplementedError() + + def _create_cryptographic_algorithm(self, alg): + return CryptographicAlgorithm(alg) + + def _create_cryptographic_length(self, length): + if length is not None and not isinstance(length, int): + msg = utils.build_er_error(CryptographicLength, + 'constructor argument type', int, + type(length)) + raise TypeError(msg) + + return CryptographicLength(length) + + def _create_cryptographic_parameters(self, params): + raise NotImplementedError() + + def _create_cryptographic_domain_parameters(self, params): + raise NotImplementedError() + + def _create_certificate_type(self, cert): + raise NotImplementedError() + + def _create_certificate_length(self, length): + raise NotImplementedError() + + def _create_x_509_certificate_identifier(self, ident): + raise NotImplementedError() + + def _create_x_509_certificate_subject(self, subject): + raise NotImplementedError() + + def _create_x_509_certificate_issuer(self, issuer): + raise NotImplementedError() + + def _create_certificate_identifier(self, ident): + raise NotImplementedError() + + def _create_certificate_subject(self, subject): + raise NotImplementedError() + + def _create_certificate_issuer(self, issuer): + raise NotImplementedError() + + def _create_digital_signature_algorithm(self, alg): + raise NotImplementedError() + + def _create_digest(self, digest): + raise NotImplementedError() + + def _create_operation_policy_name(self, name): + raise NotImplementedError() + + def _create_cryptographic_usage_mask(self, flags): + mask = None + if flags is not None: + mask = 0 + for flag in flags: + mask |= flag.value + + return CryptographicUsageMask(mask) + + def _create_lease_time(self, lease): + raise NotImplementedError() + + def _create_usage_limits(self, limits): + raise NotImplementedError() + + def _create_state(self, state): + raise NotImplementedError() + + def _create_initial_date(self, date): + raise NotImplementedError() + + def _create_activation_date(self, date): + raise NotImplementedError() + + def _create_process_start_date(self, date): + raise NotImplementedError() + + def _create_protect_stop_date(self, date): + raise NotImplementedError() + + def _create_deactivation_date(self, date): + raise NotImplementedError() + + def _create_destroy_date(self, date): + raise NotImplementedError() + + def _create_compromise_occurrence_date(self, date): + raise NotImplementedError() + + def _create_compromise_date(self, date): + raise NotImplementedError() + + def _create_revocation_reason(self, reason): + raise NotImplementedError() + + def _create_archive_date(self, date): + raise NotImplementedError() + + def _create_object_group(self, group): + if group is not None and not isinstance(group, str): + msg = utils.build_er_error(ObjectGroup, + 'constructor argument type', str, + type(group)) + raise TypeError(msg) + + return ObjectGroup(group) + + def _create_fresh(self, fresh): + raise NotImplementedError() + + def _create_link(self, link): + raise NotImplementedError() + + def _create_application_specific_information(self, info): + if info is None: + return ApplicationSpecificInformation() + else: + application_namespace = info.get('application_namespace') + application_data = info.get('application_data') + + if not isinstance(application_namespace, str): + msg = utils.build_er_error(ApplicationSpecificInformation, + 'constructor argument type', + str, type(application_namespace)) + raise TypeError(msg) + + if not isinstance(application_data, str): + msg = utils.build_er_error(ApplicationSpecificInformation, + 'constructor argument type', + str, type(application_data)) + raise TypeError(msg) + + return ApplicationSpecificInformation.create(application_namespace, + application_data) + + def _create_contact_information(self, info): + if info is None: + return ContactInformation() + else: + if not isinstance(info, str): + msg = utils.build_er_error(ContactInformation, + 'constructor argument type', str, + type(info)) + raise TypeError(msg) + + return ContactInformation(info) + + def _create_last_change_date(self, date): + raise NotImplementedError() + + def _create_custom_attribute(self, data): + return CustomAttribute(data) diff --git a/kmip/core/factories/attributes.py b/kmip/core/factories/attributes.py new file mode 100644 index 0000000..47dfdbf --- /dev/null +++ b/kmip/core/factories/attributes.py @@ -0,0 +1,56 @@ +# Copyright (c) 2014 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 enum import Enum + +from kmip.core.factories.attribute_values import AttributeValueFactory + +from kmip.core.objects import Attribute + +from kmip.core import utils + + +class AttributeFactory(object): + + def __init__(self): + self.value_factory = AttributeValueFactory() + + def _create_attribute(self, name, value, index): + attribute_name = Attribute.AttributeName(name) + + if index is None: + return Attribute(attribute_name=attribute_name, + attribute_value=value) + else: + attribute_index = Attribute.AttributeIndex(index) + return Attribute(attribute_name=attribute_name, + attribute_index=attribute_index, + attribute_value=value) + + def create_attribute(self, name, value, index=None): + value = self.value_factory.create_attribute_value(name, value) + + if isinstance(name, Enum): + name = name.value + elif isinstance(name, str): + # Name is already a string, pass + pass + else: + msg = utils.build_er_error(Attribute, 'name', + '{} or {}'.format('Enum', 'str'), + type(name)) + raise TypeError(msg) + + return self._create_attribute(name, value, index) diff --git a/kmip/core/factories/credentials.py b/kmip/core/factories/credentials.py new file mode 100644 index 0000000..9912c5b --- /dev/null +++ b/kmip/core/factories/credentials.py @@ -0,0 +1,72 @@ +# Copyright (c) 2014 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 kmip.core.enums import CredentialType + +from kmip.core.objects import Credential + + +class CredentialFactory(object): + def __init__(self): + pass + + def _create_credential(self, credential_type, credential_value): + credential_type = Credential.CredentialType(credential_type) + return Credential(credential_type=credential_type, + credential_value=credential_value) + + def create_credential(self, cred_type, value): + # Switch on the type of the credential + if cred_type is CredentialType.USERNAME_AND_PASSWORD: + value = self._create_username_password_credential(value) + elif cred_type is CredentialType.DEVICE: + value = self._create_device_credential(value) + else: + msg = 'Unrecognized credential type: {}' + raise ValueError(msg.format(cred_type)) + + return self._create_credential(cred_type, value) + + def _create_username_password_credential(self, value): + username = value.get('Username') + password = value.get('Password') + + username = Credential.UsernamePasswordCredential.Username(username) + password = Credential.UsernamePasswordCredential.Password(password) + + return Credential.UsernamePasswordCredential(username=username, + password=password) + + def _create_device_credential(self, value): + dsn = value.get('Device Serial Number') + password = value.get('Password') + dev_id = value.get('Device Identifier') + net_id = value.get('Network Identifier') + mach_id = value.get('Machine Identifier') + med_id = value.get('Media Identifier') + + dsn = Credential.DeviceCredential.DeviceSerialNumber(dsn) + password = Credential.DeviceCredential.Password(password) + dev_id = Credential.DeviceCredential.DeviceIdentifier(dev_id) + net_id = Credential.DeviceCredential.NetworkIdentifier(net_id) + mach_id = Credential.DeviceCredential.MachineIdentifier(mach_id) + med_id = Credential.DeviceCredential.MediaIdentifier(med_id) + + return Credential.DeviceCredential(device_serial_number=dsn, + password=password, + device_identifier=dev_id, + network_identifier=net_id, + machine_identifier=mach_id, + media_identifier=med_id) diff --git a/kmip/core/factories/keys.py b/kmip/core/factories/keys.py new file mode 100644 index 0000000..15a49f7 --- /dev/null +++ b/kmip/core/factories/keys.py @@ -0,0 +1,126 @@ +# Copyright (c) 2014 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 kmip.core.enums import KeyFormatType + +from kmip.core.keys import RawKey + + +class KeyFactory(object): + + def create_key(self, key_format, value=None): + if value is None: + value = {} + + # Switch on the format type of the key + if key_format is KeyFormatType.RAW: + return self._create_raw_key(value) + elif key_format is KeyFormatType.OPAQUE: + return self._create_opaque_key(value) + elif key_format is KeyFormatType.PKCS_1: + return self._create_pkcs_1_key(value) + elif key_format is KeyFormatType.PKCS_8: + return self._create_pkcs_8_key(value) + elif key_format is KeyFormatType.X_509: + return self._create_x_509_key(value) + elif key_format is KeyFormatType.EC_PRIVATE_KEY: + return self._create_ec_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_SYMMETRIC_KEY: + return self._create_transparent_symmetric_key(value) + elif key_format is KeyFormatType.TRANSPARENT_DSA_PRIVATE_KEY: + return self._create_transparent_dsa_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_DSA_PUBLIC_KEY: + return self._create_transparent_dsa_public_key(value) + elif key_format is KeyFormatType.TRANSPARENT_RSA_PRIVATE_KEY: + return self._create_transparent_rsa_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_RSA_PUBLIC_KEY: + return self._create_transparent_rsa_public_key(value) + elif key_format is KeyFormatType.TRANSPARENT_DH_PRIVATE_KEY: + return self._create_transparent_dh_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_DH_PUBLIC_KEY: + return self._create_transparent_dh_public_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECDSA_PRIVATE_KEY: + return self._create_transparent_ecdsa_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECDSA_PUBLIC_KEY: + return self._create_transparent_ecdsa_public_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECDH_PRIVATE_KEY: + return self._create_transparent_ecdh_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECDH_PUBLIC_KEY: + return self._create_transparent_ecdh_public_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECMQV_PRIVATE_KEY: + return self._create_transparent_ecmqv_private_key(value) + elif key_format is KeyFormatType.TRANSPARENT_ECMQV_PUBLIC_KEY: + return self._create_transparent_ecmqv_public_key(value) + else: + msg = 'Unrecognized key format type: {}' + raise ValueError(msg.format(key_format)) + + def _create_raw_key(self, value): + data = value.get('bytes') + return RawKey(data) + + def _create_opaque_key(self, value): + raise NotImplementedError() + + def _create_pkcs_1_key(self, value): + raise NotImplementedError() + + def _create_pkcs_8_key(self, value): + raise NotImplementedError() + + def _create_x_509_key(self, value): + raise NotImplementedError() + + def _create_ec_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_symmetric_key(self, value): + raise NotImplementedError() + + def _create_transparent_dsa_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_dsa_public_key(self, value): + raise NotImplementedError() + + def _create_transparent_rsa_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_rsa_public_key(self, value): + raise NotImplementedError() + + def _create_transparent_dh_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_dh_public_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecdsa_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecdsa_public_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecdh_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecdh_public_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecmqv_private_key(self, value): + raise NotImplementedError() + + def _create_transparent_ecmqv_public_key(self, value): + raise NotImplementedError() diff --git a/kmip/core/factories/secrets.py b/kmip/core/factories/secrets.py new file mode 100644 index 0000000..cce24f2 --- /dev/null +++ b/kmip/core/factories/secrets.py @@ -0,0 +1,140 @@ +# Copyright (c) 2014 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 kmip.core.factories.keys import KeyFactory + +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.objects import Attribute +from kmip.core.objects import KeyBlock +from kmip.core.objects import KeyWrappingData +from kmip.core.objects import KeyValueStruct +from kmip.core.objects import KeyValue + +from kmip.core.secrets import SymmetricKey +from kmip.core.secrets import Template + +from kmip.core import utils + + +class SecretFactory(object): + + def __init__(self): + self.key_factory = KeyFactory() + + self.base_error = ErrorStrings.BAD_EXP_RECV + self.template_input = self.base_error.format('Template', '{0}', '{1}', + '{2}') + + def create_secret(self, secret_type, value=None): + if secret_type is ObjectType.CERTIFICATE: + return self._create_certificate(value) + elif secret_type is ObjectType.SYMMETRIC_KEY: + return self._create_symmetric_key(value) + elif secret_type is ObjectType.PUBLIC_KEY: + return self._create_public_key(value) + elif secret_type is ObjectType.PRIVATE_KEY: + return self._create_private_key(value) + elif secret_type is ObjectType.SPLIT_KEY: + return self._create_split_key(value) + elif secret_type is ObjectType.TEMPLATE: + return self._create_template(value) + elif secret_type is ObjectType.SECRET_DATA: + return self._create_secret_data(value) + elif secret_type is ObjectType.OPAQUE_DATA: + return self._create_opaque_data(value) + + def _create_certificate(self, value): + raise NotImplementedError() + + def _create_symmetric_key(self, value): + if value is None: + return SymmetricKey() + else: + key_type = value.get('key_format_type') + key_compression_type = value.get('key_compression_type') + key_value = value.get('key_value') + cryptographic_algorithm = value.get('cryptographic_algorithm') + cryptographic_length = value.get('cryptographic_length') + key_wrapping_data = value.get('key_wrapping_data') + + key_format_type = KeyBlock.KeyFormatType(key_type) + + key_comp_type = None + if key_compression_type is not None: + key_comp_type = KeyBlock.KeyCompressionType( + key_compression_type) + + key_material = self.key_factory.create_key(key_type, + key_value) + key_val_struc = KeyValueStruct(key_format_type=key_format_type, + key_material=key_material) + key_value = KeyValue(key_value=key_val_struc, + key_format_type=key_format_type) + crypto_algorithm = CryptographicAlgorithm(cryptographic_algorithm) + crypto_length = CryptographicLength(cryptographic_length) + + key_wrap_data = None + if key_wrapping_data is not None: + # TODO (peter-hamilton) This currently isn't used in the tests + # TODO (peter-hamilton) but needs to be updated to properly + # TODO (peter-hamilton) create a KeyWrappingData object. + key_wrap_data = KeyWrappingData(key_wrapping_data) + + key_block = KeyBlock(key_format_type, + key_comp_type, + key_value, + crypto_algorithm, + crypto_length, + key_wrap_data) + return SymmetricKey(key_block) + + def _create_public_key(self, value): + raise NotImplementedError() + + def _create_private_key(self, value): + raise NotImplementedError() + + def _create_split_key(self, value): + raise NotImplementedError() + + def _create_template(self, value): + if value is None: + return Template() + else: + if not isinstance(value, list): + msg = utils.build_er_error(Template, + 'constructor argument type', list, + type(value)) + raise TypeError(msg) + else: + for val in value: + if not isinstance(val, Attribute): + msg = utils.build_er_error(Template, + 'constructor argument type', + Attribute, type(val)) + raise TypeError(msg) + return Template(value) + + def _create_secret_data(self, value): + raise NotImplementedError() + + def _create_opaque_data(self, value): + raise NotImplementedError() diff --git a/kmip/core/keys.py b/kmip/core/keys.py new file mode 100644 index 0000000..17c7823 --- /dev/null +++ b/kmip/core/keys.py @@ -0,0 +1,102 @@ +# Copyright (c) 2014 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. + +# This module defines classes representing all of the different key types +# used by KMIP, including the more detailed structures of the Transparent +# Keys defined in Section 2.1.7. + +from kmip.core.enums import Tags + +from kmip.core.primitives import Struct +from kmip.core.primitives import ByteString + +from kmip.core.utils import BytearrayStream + + +class RawKey(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +class OpaqueKey(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +class PKCS1Key(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +class PKCS8Key(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +class X509Key(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +class ECPrivateKey(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_MATERIAL) + + +# 2.1.7.1 +class TransparentSymmetricKey(Struct): + + class Key(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY) + + def __init__(self, key=None): + super(self.__class__, self).__init__(Tags.KEY_MATERIAL) + self.key = key + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.key = TransparentSymmetricKey.Key() + self.key.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.key.write(tstream) + + # Write the length and value of the key wrapping data + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass diff --git a/kmip/core/messages/.gitignore b/kmip/core/messages/.gitignore new file mode 100644 index 0000000..1732a27 --- /dev/null +++ b/kmip/core/messages/.gitignore @@ -0,0 +1,2 @@ +/test_in.txt +/test_out.txt diff --git a/kmip/core/messages/__init__.py b/kmip/core/messages/__init__.py new file mode 100644 index 0000000..8581af8 --- /dev/null +++ b/kmip/core/messages/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2014 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. + +__all__ = ['contents', 'messages', 'operations'] diff --git a/kmip/core/messages/contents.py b/kmip/core/messages/contents.py new file mode 100644 index 0000000..5794758 --- /dev/null +++ b/kmip/core/messages/contents.py @@ -0,0 +1,224 @@ +# Copyright (c) 2014 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 kmip.core import enums +from kmip.core import objects +from kmip.core import utils + +from kmip.core.primitives import Struct +from kmip.core.primitives import Integer +from kmip.core.primitives import Enumeration +from kmip.core.primitives import Boolean +from kmip.core.primitives import TextString +from kmip.core.primitives import ByteString +from kmip.core.primitives import DateTime + + +# 6.1 +class ProtocolVersion(Struct): + + class ProtocolVersionMajor(Integer): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.PROTOCOL_VERSION_MAJOR) + + class ProtocolVersionMinor(Integer): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.PROTOCOL_VERSION_MINOR) + + def __init__(self, + protocol_version_major=None, + protocol_version_minor=None): + super(self.__class__, self).__init__(tag=enums.Tags.PROTOCOL_VERSION) + self.protocol_version_major = protocol_version_major + self.protocol_version_minor = protocol_version_minor + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = utils.BytearrayStream(istream.read(self.length)) + + # Read the major and minor portions of the version number + self.protocol_version_major = ProtocolVersion.ProtocolVersionMajor() + self.protocol_version_minor = ProtocolVersion.ProtocolVersionMinor() + self.protocol_version_major.read(tstream) + self.protocol_version_minor.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = utils.BytearrayStream() + + # Write the major and minor portions of the protocol version + self.protocol_version_major.write(tstream) + self.protocol_version_minor.write(tstream) + + # Write the length and value of the protocol version + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + @classmethod + def create(cls, major, minor): + major_version = cls.ProtocolVersionMajor(major) + minor_version = cls.ProtocolVersionMinor(minor) + return ProtocolVersion(major_version, minor_version) + + +# 6.2 +class Operation(Enumeration): + ENUM_TYPE = enums.Operation + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.OPERATION) + + +# 6.3 +class MaximumResponseSize(Integer): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.MAXIMUM_RESPONSE_SIZE) + + +# 6.4 +class UniqueBatchItemID(ByteString): + def __init__(self, value=None): + super(self.__class__, self)\ + .__init__(value, enums.Tags.UNIQUE_BATCH_ITEM_ID) + + +# 6.5 +class TimeStamp(DateTime): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.TIME_STAMP) + + +# 6.6 +class Authentication(Struct): + + def __init__(self, credential=None): + super(self.__class__, self).__init__(tag=enums.Tags.AUTHENTICATION) + self.credential = credential + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = utils.BytearrayStream(istream.read(self.length)) + + # Read the credential + self.credential = objects.Credential() + self.credential.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = utils.BytearrayStream() + + # Write the credential + self.credential.write(tstream) + + # Write the length and value of the protocol version + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 6.7 +class AsynchronousIndicator(Boolean): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.ASYNCHRONOUS_INDICATOR) + + +# 6.8 +class AsynchronousCorrelationValue(ByteString): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.ASYNCHRONOUS_CORRELATION_VALUE) + + +# 6.9 +class ResultStatus(Enumeration): + ENUM_TYPE = enums.ResultStatus + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.RESULT_STATUS) + + +# 6.10 +class ResultReason(Enumeration): + ENUM_TYPE = enums.ResultReason + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.RESULT_REASON) + + +# 6.11 +class ResultMessage(TextString): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.RESULT_MESSAGE) + + +# 6.12 +class BatchOrderOption(Boolean): + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.BATCH_ORDER_OPTION) + + +# 6.13 +class BatchErrorContinuationOption(Enumeration): + ENUM_TYPE = enums.BatchErrorContinuationOption + + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.BATCH_ERROR_CONTINUATION_OPTION) + + +# 6.14 +class BatchCount(Integer): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, enums.Tags.BATCH_COUNT) + + +# 6.16 +class MessageExtension(Struct): + def __init__(self): + super(self.__class__, self).__init__(tag=enums.Tags.MESSAGE_EXTENSION) + + +# 9.1.3.2.2 +class KeyCompressionType(Enumeration): + ENUM_TYPE = enums.KeyCompressionType + + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.KEY_COMPRESSION_TYPE) + + +# 9.1.3.2.3 +class KeyFormatType(Enumeration): + ENUM_TYPE = enums.KeyFormatType + + def __init__(self, value=None): + super(self.__class__, self).\ + __init__(value, enums.Tags.KEY_FORMAT_TYPE) diff --git a/kmip/core/messages/messages.py b/kmip/core/messages/messages.py new file mode 100644 index 0000000..feeef2d --- /dev/null +++ b/kmip/core/messages/messages.py @@ -0,0 +1,397 @@ +# Copyright (c) 2014 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 kmip.core.enums import Tags + +from kmip.core.messages import contents +from kmip.core.messages.contents import AsynchronousCorrelationValue +from kmip.core.messages.contents import BatchErrorContinuationOption + +from kmip.core.messages import operations + +from kmip.core.primitives import Struct + +from kmip.core.utils import BytearrayStream + + +class RequestHeader(Struct): + + def __init__(self, + protocol_version=None, + maximum_response_size=None, + asynchronous_indicator=None, + authentication=None, + batch_error_cont_option=None, + batch_order_option=None, + time_stamp=None, + batch_count=None): + super(self.__class__, self).__init__(tag=Tags.REQUEST_HEADER) + self.protocol_version = protocol_version + self.maximum_response_size = maximum_response_size + self.asynchronous_indicator = asynchronous_indicator + self.authentication = authentication + self.batch_error_cont_option = batch_error_cont_option + self.batch_order_option = batch_order_option + self.time_stamp = time_stamp + self.batch_count = batch_count + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.protocol_version = contents.ProtocolVersion() + self.protocol_version.read(tstream) + + # Read the maximum response size if it is present + if self.is_tag_next(Tags.MAXIMUM_RESPONSE_SIZE, tstream): + self.maximum_response_size = contents.MaximumResponseSize() + self.maximum_response_size.read(tstream) + + # Read the asynchronous indicator if it is present + if self.is_tag_next(Tags.ASYNCHRONOUS_INDICATOR, tstream): + self.asynchronous_indicator = contents.AsynchronousIndicator() + self.asynchronous_indicator.read(tstream) + + # Read the authentication if it is present + if self.is_tag_next(Tags.AUTHENTICATION, tstream): + self.authentication = contents.Authentication() + self.authentication.read(tstream) + + # Read the batch error continuation option if it is present + if self.is_tag_next(Tags.BATCH_ERROR_CONTINUATION_OPTION, tstream): + self.batch_error_cont_option = BatchErrorContinuationOption() + self.batch_error_cont_option.read(tstream) + + # Read the batch order option if it is present + if self.is_tag_next(Tags.BATCH_ORDER_OPTION, tstream): + self.batch_order_option = contents.BatchOrderOption() + self.batch_order_option.read(tstream) + + # Read the time stamp if it is present + if self.is_tag_next(Tags.TIME_STAMP, tstream): + self.time_stamp = contents.TimeStamp() + self.time_stamp.read(tstream) + + self.batch_count = contents.BatchCount() + self.batch_count.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of a request header to the stream + self.protocol_version.write(tstream) + if self.maximum_response_size is not None: + self.maximum_response_size.write(tstream) + if self.asynchronous_indicator is not None: + self.asynchronous_indicator.write(tstream) + if self.authentication is not None: + self.authentication.write(tstream) + if self.batch_error_cont_option is not None: + self.batch_error_cont_option.write(tstream) + if self.batch_order_option is not None: + self.batch_order_option.write(tstream) + if self.time_stamp is not None: + self.time_stamp.write(tstream) + self.batch_count.write(tstream) + + # Write the length and value of the request header + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + +class ResponseHeader(Struct): + + def __init__(self, + protocol_version=None, + time_stamp=None, + batch_count=None): + super(self.__class__, self).__init__(tag=Tags.RESPONSE_HEADER) + self.protocol_version = protocol_version + self.time_stamp = time_stamp + self.batch_count = batch_count + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.protocol_version = contents.ProtocolVersion() + self.protocol_version.read(tstream) + + self.time_stamp = contents.TimeStamp() + self.time_stamp.read(tstream) + + self.batch_count = contents.BatchCount() + self.batch_count.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of a response header to the stream + self.protocol_version.write(tstream) + self.time_stamp.write(tstream) + self.batch_count.write(tstream) + + # Write the length and value of the request header + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + if self.protocol_version is not None: + # TODO (peter-hamilton) conduct type check + self.protocol_version.validate() + if self.time_stamp is not None: + # TODO (peter-hamilton) conduct type check + self.time_stamp.validate() + if self.batch_count is not None: + # TODO (peter-hamilton) conduct type check + self.batch_count.validate() + + +class RequestBatchItem(Struct): + + def __init__(self, + operation=None, + unique_batch_item_id=None, + request_payload=None, + message_extension=None): + super(self.__class__, self).__init__(tag=Tags.REQUEST_BATCH_ITEM) + self.operation = operation + self.unique_batch_item_id = unique_batch_item_id + self.request_payload = request_payload + self.message_extension = message_extension + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the batch item operation + self.operation = contents.Operation() + self.operation.read(tstream) + + # Read the unique batch item ID if it is present + if self.is_tag_next(Tags.UNIQUE_BATCH_ITEM_ID, tstream): + self.unique_batch_item_id = contents.UniqueBatchItemID() + self.unique_batch_item_id.read(tstream) + + # Lookup the response payload class that belongs to the operation + cls = operations.REQUEST_MAP.get(self.operation.enum) + self.request_payload = cls() + self.request_payload.read(tstream) + + # Read the message extension if it is present + if self.is_tag_next(Tags.MESSAGE_EXTENSION, tstream): + self.message_extension = contents.MessageExtension() + self.message_extension.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the batch item to the stream + self.operation.write(tstream) + + if self.unique_batch_item_id is not None: + self.unique_batch_item_id.write(tstream) + + self.request_payload.write(tstream) + + if self.message_extension is not None: + self.message_extension.write(tstream) + + # Write the length and value of the batch item + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + +class ResponseBatchItem(Struct): + + def __init__(self, + operation=None, + unique_batch_item_id=None, + result_status=None, + result_reason=None, + result_message=None, + async_correlation_value=None, + response_payload=None, + message_extension=None): + super(self.__class__, self).__init__(tag=Tags.RESPONSE_BATCH_ITEM) + self.operation = operation + self.unique_batch_item_id = unique_batch_item_id + self.result_status = result_status + self.result_reason = result_reason + self.result_message = result_message + self.async_correlation_value = async_correlation_value + self.response_payload = response_payload + self.message_extension = message_extension + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the batch item operation if it is present + if self.is_tag_next(Tags.OPERATION, tstream): + self.operation = contents.Operation() + self.operation.read(tstream) + + # Read the unique batch item ID if it is present + if self.is_tag_next(Tags.UNIQUE_BATCH_ITEM_ID, tstream): + self.unique_batch_item_id = contents.UniqueBatchItemID() + self.unique_batch_item_id.read(tstream) + + # Read the batch item result status + self.result_status = contents.ResultStatus() + self.result_status.read(tstream) + + # Read the batch item result reason if it is present + if self.is_tag_next(Tags.RESULT_REASON, tstream): + self.result_reason = contents.ResultReason() + self.result_reason.read(tstream) + + # Read the batch item result message if it is present + if self.is_tag_next(Tags.RESULT_MESSAGE, tstream): + self.result_message = contents.ResultMessage() + self.result_message.read(tstream) + + # Read the batch item asynchronous correlation value if it is present + if self.is_tag_next(Tags.ASYNCHRONOUS_CORRELATION_VALUE, tstream): + self.async_correlation_value = AsynchronousCorrelationValue() + self.async_correlation_value.read(tstream) + + # Lookup the response payload class that belongs to the operation + cls = operations.RESPONSE_MAP.get(self.operation.enum) + expected = cls() + if self.is_tag_next(expected.tag, tstream): + self.response_payload = cls() + self.response_payload.read(tstream) + + # Read the message extension if it is present + if self.is_tag_next(Tags.MESSAGE_EXTENSION, tstream): + self.message_extension = contents.MessageExtension() + self.message_extension.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the batch item to the stream + if self.operation is not None: + self.operation.write(tstream) + if self.unique_batch_item_id is not None: + self.unique_batch_item_id.write(tstream) + + self.result_status.write(tstream) + + if self.result_reason is not None: + self.result_reason.write(tstream) + if self.result_message is not None: + self.result_message.write(tstream) + if self.async_correlation_value is not None: + self.async_correlation_value.write(tstream) + if self.response_payload is not None: + self.response_payload.write(tstream) + if self.message_extension is not None: + self.message_extension.write(tstream) + + # Write the length and value of the batch item + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + pass + + +class RequestMessage(Struct): + + def __init__(self, request_header=None, batch_items=None,): + super(self.__class__, self).__init__(tag=Tags.REQUEST_MESSAGE) + self.request_header = request_header + self.batch_items = batch_items + + def read(self, istream): + super(self.__class__, self).read(istream) + + self.request_header = RequestHeader() + self.request_header.read(istream) + + self.batch_items = [] + for _ in xrange(self.request_header.batch_count.value): + batch_item = RequestBatchItem() + batch_item.read(istream) + self.batch_items.append(batch_item) + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the request header and all batch items + self.request_header.write(tstream) + for batch_item in self.batch_items: + batch_item.write(tstream) + + # Write the TTLV encoding of the request message + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + +class ResponseMessage(Struct): + + def __init__(self, response_header=None, batch_items=None,): + super(self.__class__, self).__init__(tag=Tags.RESPONSE_MESSAGE) + self.response_header = response_header + self.batch_items = batch_items + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + + self.response_header = ResponseHeader() + self.response_header.read(istream) + + self.batch_items = [] + for _ in xrange(self.response_header.batch_count.value): + batch_item = ResponseBatchItem() + batch_item.read(istream) + self.batch_items.append(batch_item) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the request header and all batch items + self.response_header.write(tstream) + for batch_item in self.batch_items: + batch_item.write(tstream) + + # Write the TTLV encoding of the request message + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + pass diff --git a/kmip/core/messages/operations.py b/kmip/core/messages/operations.py new file mode 100644 index 0000000..3cf89e2 --- /dev/null +++ b/kmip/core/messages/operations.py @@ -0,0 +1,439 @@ +# Copyright (c) 2014 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 kmip.core.factories.secrets import SecretFactory + +from kmip.core import attributes +from kmip.core import enums +from kmip.core.enums import Tags + +from kmip.core.objects import KeyWrappingSpecification +from kmip.core.objects import TemplateAttribute + +from kmip.core.primitives import Struct +from kmip.core.primitives import Enumeration + +from kmip.core.utils import BytearrayStream + + +# 4.1 +class CreateRequestPayload(Struct): + + def __init__(self, + object_type=None, + template_attribute=None): + super(self.__class__, self).__init__(tag=enums.Tags.REQUEST_PAYLOAD) + self.object_type = object_type + self.template_attribute = template_attribute + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.object_type = attributes.ObjectType() + self.template_attribute = TemplateAttribute() + + self.object_type.read(tstream) + self.template_attribute.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the object type and template attribute of the request payload + self.object_type.write(tstream) + self.template_attribute.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class CreateResponsePayload(Struct): + + def __init__(self, + object_type=None, + unique_identifier=None, + template_attribute=None): + super(self.__class__, self).__init__(tag=enums.Tags.RESPONSE_PAYLOAD) + self.object_type = object_type + self.unique_identifier = unique_identifier + self.template_attribute = template_attribute + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.object_type = attributes.ObjectType() + self.unique_identifier = attributes.UniqueIdentifier() + + self.object_type.read(tstream) + self.unique_identifier.read(tstream) + + if self.is_tag_next(Tags.TEMPLATE_ATTRIBUTE, tstream): + self.template_attribute = TemplateAttribute() + self.template_attribute.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the request payload + self.object_type.write(tstream) + self.unique_identifier.write(tstream) + + if self.template_attribute is not None: + self.template_attribute.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 4.3 +class RegisterRequestPayload(Struct): + + def __init__(self, + object_type=None, + template_attribute=None, + secret=None): + super(self.__class__, self).__init__(Tags.REQUEST_PAYLOAD) + + self.secret_factory = SecretFactory() + self.object_type = object_type + self.template_attribute = template_attribute + self.secret = secret + + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.object_type = attributes.ObjectType() + self.template_attribute = TemplateAttribute() + + self.object_type.read(tstream) + self.template_attribute.read(tstream) + + secret_type = self.object_type.enum + secret = self.secret_factory.create_secret(secret_type) + + if self.is_tag_next(secret.tag, tstream): + self.secret = secret + self.secret.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the request payload + self.object_type.write(tstream) + self.template_attribute.write(tstream) + + if self.secret is not None: + self.secret.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class RegisterResponsePayload(Struct): + + def __init__(self, + unique_identifier=None, + template_attribute=None): + super(self.__class__, self).__init__(Tags.RESPONSE_PAYLOAD) + + self.unique_identifier = unique_identifier + self.template_attribute = template_attribute + + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.unique_identifier = attributes.UniqueIdentifier() + self.unique_identifier.read(tstream) + + if self.is_tag_next(Tags.TEMPLATE_ATTRIBUTE, tstream): + self.template_attribute = TemplateAttribute() + self.template_attribute.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the request payload + self.unique_identifier.write(tstream) + + if self.template_attribute is not None: + self.template_attribute.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 4.11 +class GetRequestPayload(Struct): + + # 9.1.3.2.2 + class KeyCompressionType(Enumeration): + ENUM_TYPE = enums.KeyCompressionType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.KEY_COMPRESSION_TYPE) + + # 9.1.3.2.3 + class KeyFormatType(Enumeration): + ENUM_TYPE = enums.KeyFormatType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_FORMAT_TYPE) + + def __init__(self, + unique_identifier=None, + key_format_type=None, + key_compression_type=None, + key_wrapping_specification=None): + super(self.__class__, self).__init__(tag=enums.Tags.REQUEST_PAYLOAD) + self.unique_identifier = unique_identifier + self.key_format_type = key_format_type + self.key_compression_type = key_compression_type + self.key_wrapping_specification = key_wrapping_specification + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + if self.is_tag_next(Tags.UNIQUE_IDENTIFIER, tstream): + self.unique_identifier = attributes.UniqueIdentifier() + self.unique_identifier.read(tstream) + + if self.is_tag_next(Tags.KEY_FORMAT_TYPE, tstream): + self.key_format_type = GetRequestPayload.KeyFormatType() + self.key_format_type.read(tstream) + + if self.is_tag_next(Tags.KEY_COMPRESSION_TYPE, tstream): + self.key_compression_type = GetRequestPayload.KeyCompressionType() + self.key_compression_type.read(tstream) + + if self.is_tag_next(Tags.KEY_WRAPPING_SPECIFICATION, tstream): + self.key_wrapping_specification = KeyWrappingSpecification() + self.key_wrapping_specification.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the request payload + if self.unique_identifier is not None: + self.unique_identifier.write(tstream) + if self.key_format_type is not None: + self.key_format_type.write(tstream) + if self.key_compression_type is not None: + self.key_compression_type.write(tstream) + if self.key_wrapping_specification is not None: + self.key_wrapping_specification.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation + pass + + +class GetResponsePayload(Struct): + + def __init__(self, + object_type=None, + unique_identifier=None, + secret=None): + super(self.__class__, self).__init__(tag=Tags.RESPONSE_PAYLOAD) + self.object_type = object_type + self.unique_identifier = unique_identifier + self.secret = secret + self.secret_factory = SecretFactory() + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.object_type = attributes.ObjectType() + self.unique_identifier = attributes.UniqueIdentifier() + + self.object_type.read(tstream) + self.unique_identifier.read(tstream) + + secret_type = self.object_type.enum + self.secret = self.secret_factory.create_secret(secret_type) + self.secret.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.object_type.write(tstream) + self.unique_identifier.write(tstream) + self.secret.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 4.21 +class DestroyRequestPayload(Struct): + + def __init__(self, + unique_identifier=None): + super(self.__class__, self).__init__(enums.Tags.REQUEST_PAYLOAD) + self.unique_identifier = unique_identifier + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + if self.is_tag_next(Tags.UNIQUE_IDENTIFIER, tstream): + self.unique_identifier = attributes.UniqueIdentifier() + self.unique_identifier.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + if self.unique_identifier is not None: + self.unique_identifier.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class DestroyResponsePayload(Struct): + + def __init__(self, + unique_identifier=None): + super(self.__class__, self).__init__(enums.Tags.RESPONSE_PAYLOAD) + self.unique_identifier = unique_identifier + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.unique_identifier = attributes.UniqueIdentifier() + self.unique_identifier.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.unique_identifier.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +REQUEST_MAP = {enums.Operation.CREATE: CreateRequestPayload, + enums.Operation.GET: GetRequestPayload, + enums.Operation.DESTROY: DestroyRequestPayload, + enums.Operation.REGISTER: RegisterRequestPayload} +RESPONSE_MAP = {enums.Operation.CREATE: CreateResponsePayload, + enums.Operation.GET: GetResponsePayload, + enums.Operation.DESTROY: DestroyResponsePayload, + enums.Operation.REGISTER: RegisterResponsePayload} diff --git a/kmip/core/objects.py b/kmip/core/objects.py new file mode 100644 index 0000000..65c3261 --- /dev/null +++ b/kmip/core/objects.py @@ -0,0 +1,845 @@ +# Copyright (c) 2014 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. + +import attributes +from kmip.core.attributes import CryptographicParameters + +from kmip.core.factories.attribute_values import AttributeValueFactory +from kmip.core.factories.keys import KeyFactory + +from kmip.core import enums +from kmip.core.enums import AttributeType +from kmip.core.enums import Tags +from kmip.core.enums import Types +from kmip.core.enums import CredentialType + +from kmip.core.errors import ErrorStrings + +from kmip.core.primitives import Struct +from kmip.core.primitives import TextString +from kmip.core.primitives import ByteString +from kmip.core.primitives import Integer +from kmip.core.primitives import Enumeration + +from utils import BytearrayStream + + +# 2.1 +# 2.1.1 +class Attribute(Struct): + + class AttributeName(TextString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.ATTRIBUTE_NAME) + + class AttributeIndex(Integer): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.ATTRIBUTE_INDEX) + + def __init__(self, + attribute_name=None, + attribute_index=None, + attribute_value=None): + super(self.__class__, self).__init__(tag=Tags.ATTRIBUTE) + + self.value_factory = AttributeValueFactory() + + self.attribute_name = attribute_name + self.attribute_index = attribute_index + self.attribute_value = attribute_value + + if attribute_value is not None: + attribute_value.tag = Tags.ATTRIBUTE_VALUE + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the name of the attribute + self.attribute_name = Attribute.AttributeName() + self.attribute_name.read(tstream) + + # Read the attribute index if it is next + if self.is_tag_next(Tags.ATTRIBUTE_INDEX, tstream): + self.attribute_index = Attribute.AttributeIndex() + self.attribute_index.read(tstream) + + # Lookup the attribute class that belongs to the attribute name + name = self.attribute_name.value + enum_name = name.replace('.', '_').replace(' ', '_').upper() + enum_type = None + + try: + enum_type = AttributeType[enum_name] + except KeyError: + # Likely custom attribute, pass raw name string as attribute type + enum_type = name + + value = self.value_factory.create_attribute_value(enum_type, None) + self.attribute_value = value + self.attribute_value.tag = Tags.ATTRIBUTE_VALUE + self.attribute_value.read(tstream) + + self.is_oversized(tstream) + + def write(self, ostream): + tstream = BytearrayStream() + + self.attribute_name.write(tstream) + if self.attribute_index is not None: + self.attribute_index.write(tstream) + self.attribute_value.write(tstream) + + # Write the length and value of the attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + +# 2.1.2 +class Credential(Struct): + + class CredentialType(Enumeration): + + ENUM_TYPE = CredentialType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.CREDENTIAL_TYPE) + + class UsernamePasswordCredential(Struct): + + class Username(TextString): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.USERNAME) + + class Password(TextString): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.PASSWORD) + + def __init__(self, username=None, password=None): + super(self.__class__, self).__init__(tag=Tags.CREDENTIAL_VALUE) + self.username = username + self.password = password + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the username of the credential + self.username = self.Username() + self.username.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.PASSWORD, tstream): + self.password = self.Password() + self.password.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.username.write(tstream) + if self.password is not None: + self.password.write(tstream) + + # Write the length and value of the credential + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + pass + + class DeviceCredential(Struct): + + class DeviceSerialNumber(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.DeviceSerialNumber, self).\ + __init__(value, Tags.DEVICE_SERIAL_NUMBER) + + class Password(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.Password, self).\ + __init__(value, Tags.PASSWORD) + + class DeviceIdentifier(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.DeviceIdentifier, self).\ + __init__(value, Tags.DEVICE_IDENTIFIER) + + class NetworkIdentifier(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.NetworkIdetifier, self).\ + __init__(value, Tags.NETWORK_IDENTIFIER) + + class MachineIdentifier(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.MachineIdentifier, self).\ + __init__(value, Tags.MACHINE_IDENTIFIER) + + class MediaIdentifier(TextString): + + def __init__(self, value=None): + super(Credential.DeviceCredential.MediaIdentifier, self).\ + __init__(value, Tags.MEDIA_IDENTIFIER) + + def __init__(self, + device_serial_number=None, + password=None, + device_identifier=None, + network_identifier=None, + machine_identifier=None, + media_identifier=None): + super(self.__class__, self).__init__(tag=Tags.CREDENTIAL_VALUE) + super.device_serial_number = device_serial_number + super.password = password + super.device_identifier = device_identifier + super.network_identifier = network_identifier + super.machine_identifier = machine_identifier + super.media_identifier = media_identifier + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the password if it is next + if self.is_tag_next(Tags.DEVICE_SERIAL_NUMBER, tstream): + self.device_serial_number = self.DeviceSerialNumber() + self.device_serial_number.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.PASSWORD, tstream): + self.password = self.Password() + self.password.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.DEVICE_IDENTIFIER, tstream): + self.device_identifier = self.DeviceIdentifier() + self.device_identifier.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.NETWORK_IDENTIFIER, tstream): + self.network_identifier = self.NetworkIdentifier() + self.network_identifier.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.MACHINE_IDENTIFIER, tstream): + self.machine_identifier = self.MachineIdentifier() + self.machine_identifier.read(tstream) + + # Read the password if it is next + if self.is_tag_next(Tags.MEDIA_IDENTIFIER, tstream): + self.media_identifier = self.MediaIdentifier() + self.media_identifier.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + if self.device_serial_number is not None: + self.device_serial_number.write(tstream) + if self.password is not None: + self.password.write(tstream) + if self.device_identifier is not None: + self.device_identifier.write(tstream) + if self.network_identifier is not None: + self.network_identifier.write(tstream) + if self.machine_identifier is not None: + self.machine_identifier.write(tstream) + if self.media_identifier is not None: + self.media_identifier.write(tstream) + + # Write the length and value of the credential + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + pass + + def __init__(self, credential_type=None, credential_value=None): + super(self.__class__, self).__init__(tag=Tags.CREDENTIAL) + self.credential_type = credential_type + self.credential_value = credential_value + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + # Read the type of the credential + self.credential_type = self.CredentialType() + self.credential_type.read(tstream) + + # Use the type to determine what credential value to read + if self.credential_type.enum is CredentialType.USERNAME_AND_PASSWORD: + self.credential_value = self.UsernamePasswordCredential() + elif self.credential_type.enum is CredentialType.DEVICE: + self.credential_value = self.DeviceCredential() + else: + # TODO (peter-hamilton) Use more descriptive error here + raise NotImplementedError() + self.credential_value.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.credential_type.write(tstream) + self.credential_value.write(tstream) + + # Write the length and value of the credential + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + pass + + +# 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 + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.KEY_COMPRESSION_TYPE) + + def __init__(self, + key_format_type=None, + key_compression_type=None, + key_value=None, + cryptographic_algorithm=None, + cryptographic_length=None, + key_wrapping_data=None): + super(self.__class__, self).__init__(Tags.KEY_BLOCK) + self.key_format_type = key_format_type + self.key_compression_type = key_compression_type + self.key_value = key_value + self.cryptographic_algorithm = cryptographic_algorithm + self.cryptographic_length = cryptographic_length + self.key_wrapping_data = key_wrapping_data + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.key_format_type = KeyBlock.KeyFormatType() + self.key_format_type.read(tstream) + key_format_type = self.key_format_type.enum + + if self.is_tag_next(Tags.KEY_COMPRESSION_TYPE, tstream): + self.key_compression_type = KeyBlock.KeyCompressionType() + self.key_compression_type.read(tstream) + + self.key_value = KeyValue(key_format_type=key_format_type) + self.key_value.read(tstream) + + if self.is_tag_next(Tags.CRYPTOGRAPHIC_ALGORITHM, tstream): + self.cryptographic_algorithm = attributes.CryptographicAlgorithm() + self.cryptographic_algorithm.read(tstream) + + if self.is_tag_next(Tags.CRYPTOGRAPHIC_LENGTH, tstream): + self.cryptographic_length = attributes.CryptographicLength() + self.cryptographic_length.read(tstream) + + if self.is_tag_next(Tags.KEY_WRAPPING_DATA, tstream): + self.key_wrapping_data = KeyWrappingData() + self.key_wrapping_data.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.key_format_type.write(tstream) + + if self.key_compression_type is not None: + self.key_compression_type.write(tstream) + + self.key_value.write(tstream) + + if self.cryptographic_algorithm is not None: + self.cryptographic_algorithm.write(tstream) + if self.cryptographic_length is not None: + self.cryptographic_length.write(tstream) + if self.key_wrapping_data is not None: + self.key_wrapping_data.write(tstream) + + # Write the length and value of the credential + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + if self.key_format_type is not None: + if type(self.key_format_type) is not KeyBlock.KeyFormatType: + member = 'KeyBlock.key_format_type' + exp_type = KeyBlock.KeyFormatType + rcv_type = type(self.key_format_type) + msg = ErrorStrings.BAD_EXP_RECV.format(member, 'type', + exp_type, rcv_type) + raise TypeError(msg) + + +# 2.1.4 +class KeyValueString(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.KEY_VALUE) + + +class KeyValueStruct(Struct): + + def __init__(self, + key_format_type=None, + key_material=None, + attributes=None): + super(self.__class__, self).__init__(Tags.KEY_VALUE) + self.key_format_type = key_format_type + self.key_material = key_material + self.attributes = attributes + self.key_factory = KeyFactory() + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.key_material = self.key_factory.create_key(self.key_format_type) + self.key_material.read(tstream) + + self.attributes = list() + + # Read the attributes, 0 or more + while self.is_tag_next(Tags.ATTRIBUTE, tstream): + attribute = Attribute() + attribute.read(tstream) + self.attributes.append(attribute) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.key_material.write(tstream) + + if self.attributes is not None: + for attribute in self.attributes: + attribute.write(tstream) + + # Write the length and value of the credential + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class KeyValue(Struct): + ''' + KeyValue can be either a ByteString or a Struct. Therefore, this class + acts as a wrapper for two different KeyValue objects, KeyValueString, + which represents the ByteString format, and KeyValueStruct, which + represents the Struct format, both of which are defined above. This + KeyValue object does not read or write itself; instead, it reads and + writes its internal key_value attribute, which is either a KeyValueString + or a KeyValueStruct. + + When reading, the class determines what the format of its internal + structure should be by looking at the type of the object it will read + using KeyValue.is_type_next(). This is one of the only places in the + code where this approach is used. + ''' + + def __init__(self, + key_value=None, + key_format_type=None): + super(self.__class__, self).__init__(Tags.KEY_VALUE) + self.key_value = key_value + self.key_format_type = key_format_type + if self.key_value is not None: + self.type = key_value.type + self.validate() + + def read(self, istream): + if self.is_type_next(Types.BYTE_STRING, istream): + self.key_value = KeyValueString() + self.key_value.read(istream) + elif self.is_type_next(Types.STRUCTURE, istream): + kft = self.key_format_type + self.key_value = KeyValueStruct(key_format_type=kft) + self.key_value.read(istream) + + def write(self, ostream): + tstream = BytearrayStream() + self.key_value.write(tstream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.1.5 +class WrappingMethod(Enumeration): + ENUM_TYPE = enums.WrappingMethod + + def __init__(self, value=None): + super(WrappingMethod, self).__init__(value, Tags.WRAPPING_METHOD) + + +class EncodingOption(Enumeration): + ENUM_TYPE = enums.EncodingOption + + def __init__(self, value=None): + super(WrappingMethod, self).__init__(value, Tags.ENCODING_OPTION) + + +class KeyInformation(Struct): + + def __init__(self, + unique_identifier=None, + cryptographic_parameters=None, + tag=Tags.ENCRYPTION_KEY_INFORMATION): + super(self.__class__, self).\ + __init__(tag=Tags.ENCRYPTION_KEY_INFORMATION) + self.unique_identifier = unique_identifier + self.cryptographic_parameters = cryptographic_parameters + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.unique_identifier = attributes.UniqueIdentifier() + self.unique_identifier.read(tstream) + + if self.is_tag_next(Tags.CRYPTOGRAPHIC_PARAMETERS, tstream): + self.cryptographic_parameters = CryptographicParameters() + self.cryptographic_parameters.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.unique_identifier.write(tstream) + + if self.cryptographic_parameters is not None: + self.cryptographic_parameters.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class EncryptionKeyInformation(KeyInformation): + + def __init__(self, + unique_identifier=None, + cryptographic_parameters=None, + tag=Tags.ENCRYPTION_KEY_INFORMATION): + super(self.__class__, self).\ + __init__(unique_identifier, cryptographic_parameters, tag) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class MACSignatureKeyInformation(KeyInformation): + + def __init__(self, + unique_identifier=None, + cryptographic_parameters=None, + tag=Tags.MAC_SIGNATURE_KEY_INFORMATION): + super(self.__class__, self).\ + __init__(unique_identifier, cryptographic_parameters, tag) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class KeyWrappingData(Struct): + + class MACSignature(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.MAC_SIGNATURE) + + class IVCounterNonce(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.IV_COUNTER_NONCE) + + def __init__(self, + wrapping_method=None, + encryption_key_information=None, + mac_signature_key_information=None, + mac_signature=None, + iv_counter_nonce=None, + encoding_option=None): + super(self.__class__, self).__init__(Tags.KEY_WRAPPING_DATA) + self.wrapping_method = wrapping_method + self.encryption_key_information = encryption_key_information + self.mac_signature_key_information = mac_signature_key_information + self.mac_signature = mac_signature + self.iv_counter_nonce = iv_counter_nonce + self.encoding_option = encoding_option + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.wrapping_method = WrappingMethod() + self.wrapping_method.read(tstream) + + if self.is_tag_next(Tags.ENCRYPTION_KEY_INFORMATION, tstream): + self.encryption_key_information = EncryptionKeyInformation() + self.encryption_key_information.read(tstream) + + if self.is_tag_next(Tags.MAC_SIGNATURE_KEY_INFORMATION, tstream): + self.mac_signature_key_information = MACSignatureKeyInformation() + self.mac_signature_key_information.read(tstream) + + if self.is_tag_next(Tags.MAC_SIGNATURE, tstream): + self.mac_signature = KeyWrappingData.MACSignature() + self.mac_signature.read(tstream) + + if self.is_tag_next(Tags.IV_COUNTER_NONCE, tstream): + self.iv_counter_nonce = KeyWrappingData.IVCounterNonce() + self.iv_counter_nonce.read(tstream) + + if self.is_tag_next(Tags.ENCODING_OPTION, tstream): + self.encoding_option = EncodingOption() + self.encoding_option.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the key wrapping data + self.wrapping_method.write(tstream) + + if self.encryption_key_information is not None: + self.encryption_key_information.write(tstream) + if self.mac_signature_key_information is not None: + self.mac_signature_key_information.write(tstream) + if self.mac_signature is not None: + self.mac_signature.write(tstream) + if self.iv_counter_nonce is not None: + self.iv_counter_nonce.write(tstream) + if self.encoding_option is not None: + self.encoding_option.write(tstream) + + # Write the length and value of the key wrapping data + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation + pass + + +# 2.1.6 +class KeyWrappingSpecification(Struct): + + class AttributeName(TextString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.ATTRIBUTE_NAME) + + def __init__(self, + wrapping_method=None, + encryption_key_information=None, + mac_signature_key_information=None, + attribute_name=None, + encoding_option=None): + super(self.__class__, self).\ + __init__(tag=Tags.KEY_WRAPPING_SPECIFICATION) + self.wrapping_method = wrapping_method + self.encryption_key_information = encryption_key_information + self.mac_signature_key_information = mac_signature_key_information + self.attribute_name = attribute_name + self.encoding_option = encoding_option + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.wrapping_method = WrappingMethod() + self.wrapping_method.read(tstream) + + if self.is_tag_next(Tags.ENCRYPTION_KEY_INFORMATION, tstream): + self.encryption_key_information = EncryptionKeyInformation() + self.encryption_key_information.read(tstream) + + if self.is_tag_next(Tags.MAC_SIGNATURE_KEY_INFORMATION, tstream): + self.mac_signature_key_information = MACSignatureKeyInformation() + self.mac_signature_key_information.read(tstream) + + if self.is_tag_next(Tags.ATTRIBUTE_NAME, tstream): + self.attribute_name = KeyWrappingSpecification.AttributeName() + self.attribute_name.read(tstream) + + if self.is_tag_next(Tags.ENCODING_OPTION, tstream): + self.encoding_option = EncodingOption() + self.encoding_option.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the contents of the key wrapping data + self.wrapping_method.write(tstream) + + if self.encryption_key_information is not None: + self.encryption_key_information.write(tstream) + if self.mac_signature_key_information is not None: + self.mac_signature_key_information.write(tstream) + if self.attribute_name is not None: + self.attribute_name.write(tstream) + if self.encoding_option is not None: + self.encoding_option.write(tstream) + + # Write the length and value of the key wrapping data + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.1.8 +class TemplateAttribute(Struct): + + def __init__(self, + names=None, + attributes=None): + super(self.__class__, self).__init__(tag=Tags.TEMPLATE_ATTRIBUTE) + self.names = names + self.attributes = attributes + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.names = list() + self.attributes = list() + + # Read the names of the template attribute, 0 or more + while self.is_tag_next(Tags.NAME, tstream): + name = attributes.Name() + name.read(tstream) + self.names.append(name) + + # Read the attributes of the template attribute, 0 or more + while self.is_tag_next(Tags.ATTRIBUTE, tstream): + attribute = Attribute() + attribute.read(tstream) + self.attributes.append(attribute) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the names and attributes of the template attribute + if self.names is not None: + for name in self.names: + name.write(tstream) + if self.attributes is not None: + for attribute in self.attributes: + attribute.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass diff --git a/kmip/core/primitives.py b/kmip/core/primitives.py new file mode 100644 index 0000000..327d4e7 --- /dev/null +++ b/kmip/core/primitives.py @@ -0,0 +1,618 @@ +# Copyright (c) 2014 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 struct import pack, unpack +from enum import Enum + +from kmip.core.enums import Types +from kmip.core.enums import Tags + +from kmip.core.errors import ErrorStrings + +import errors +import utils + + +class Base(object): + TAG_SIZE = 3 + TYPE_SIZE = 1 + LENGTH_SIZE = 4 + + def __init__(self, tag=Tags.DEFAULT, type=Types.DEFAULT): + self.tag = tag + self.type = type + self.length = None + + # TODO (peter-hamilton) Convert this into a classmethod, class name can be + # obtained from cls parameter that replaces self + def is_oversized(self, stream): + extra = len(stream.peek()) + if extra > 0: + raise errors.StreamNotEmptyError(Base.__name__, extra) + + def read_tag(self, istream): + # Read in the bytes for the tag + tts = istream.read(self.TAG_SIZE) + tag = unpack('!I', '\x00' + tts[0:self.TAG_SIZE])[0] + + enum_tag = Tags(tag) + + # Verify that the tag matches for the current object + if enum_tag is not self.tag: + raise errors.ReadValueError(Base.__name__, 'tag', + hex(self.tag.value), hex(tag)) + + def read_type(self, istream): + # Read in the bytes for the type + tts = istream.read(self.TYPE_SIZE) + num_bytes = len(tts) + if num_bytes != self.TYPE_SIZE: + min_bytes = 'a minimum of {0} bytes'.format(self.TYPE_SIZE) + raise errors.ReadValueError(Base.__name__, 'type', min_bytes, + '{0} bytes'.format(num_bytes)) + typ = unpack('!B', tts)[0] + + enum_typ = Types(typ) + + if enum_typ is not self.type: + raise errors.ReadValueError(Base.__name__, 'type', + self.type.value, typ) + + def read_length(self, istream): + # Read in the bytes for the length + lst = istream.read(self.LENGTH_SIZE) + num_bytes = len(lst) + if num_bytes != self.LENGTH_SIZE: + min_bytes = 'a minimum of {0} bytes'.format(self.LENGTH_SIZE) + raise errors.ReadValueError(Base.__name__, 'length', min_bytes, + '{0} bytes'.format(num_bytes)) + length = unpack('!I', lst)[0] + + # Verify that the length matches the expected length, if one exists + if self.length is not None: + if length is not self.length: + raise errors.ReadValueError(Base.__name__, 'length', + self.length, length) + else: + self.length = length + + def read_value(self, istream): + raise NotImplementedError() + + def read(self, istream): + self.read_tag(istream) + self.read_type(istream) + self.read_length(istream) + + def write_tag(self, ostream): + # Write the tag to the output stream + ostream.write(pack('!I', self.tag.value)[1:]) + + def write_type(self, ostream): + if type(self.type) is not Types: + msg = ErrorStrings.BAD_EXP_RECV + raise TypeError(msg.format(Base.__name__, 'type', + Types, type(self.type))) + ostream.write(pack('!B', self.type.value)) + + def write_length(self, ostream): + if type(self.length) is not int: + msg = ErrorStrings.BAD_EXP_RECV + raise TypeError(msg.format(Base.__name__, 'length', + int, type(self.length))) + num_bytes = utils.count_bytes(self.length) + if num_bytes > self.LENGTH_SIZE: + raise errors.WriteOverflowError(Base.__name__, 'length', + self.LENGTH_SIZE, num_bytes) + ostream.write(pack('!I', self.length)) + + def write_value(self, ostream): + raise NotImplementedError() + + def write(self, ostream): + self.write_tag(ostream) + self.write_type(ostream) + self.write_length(ostream) + + def validate(self): + raise NotImplementedError() + + @staticmethod + def is_tag_next(tag, stream): + next_tag = stream.peek(Base.TAG_SIZE) + if len(next_tag) != Base.TAG_SIZE: + return False + next_tag = unpack('!I', '\x00' + next_tag)[0] + if next_tag == tag.value: + return True + else: + return False + + @staticmethod + def is_type_next(kmip_type, stream): + tag_type_size = Base.TAG_SIZE + Base.TYPE_SIZE + tt = stream.peek(tag_type_size) + + if len(tt) != tag_type_size: + return False + + typ = unpack('!B', tt[Base.TAG_SIZE:])[0] + + if typ == kmip_type.value: + return True + else: + return False + + +class Struct(Base): + + def __init__(self, tag=Tags.DEFAULT): + super(Struct, self).__init__(tag, type=Types.STRUCTURE) + + def __repr__(self): + return '' + + +class Integer(Base): + LENGTH = 4 + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(Integer, self).__init__(tag, type=Types.INTEGER) + + self.value = value + self.length = self.LENGTH + self.padding_length = self.LENGTH + + self.validate() + + def read_value(self, istream): + if self.length is not self.LENGTH: + raise errors.ReadValueError(Integer.__name__, 'length', + self.LENGTH, self.length) + + self.value = unpack('!i', str(istream.read(self.length)))[0] + pad = unpack('!i', str(istream.read(self.padding_length)))[0] + + if pad is not 0: + raise errors.ReadValueError(Integer.__name__, 'pad', 0, + pad) + self.validate() + + def read(self, istream): + super(Integer, self).read(istream) + self.read_value(istream) + + def write_value(self, ostream): + ostream.write(pack('!i', self.value)) + ostream.write(pack('!i', 0)) + + def write(self, ostream): + super(Integer, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.value is not None: + data_type = type(self.value) + if data_type is not int: + raise errors.StateTypeError(Integer.__name__, int, + data_type) + num_bytes = utils.count_bytes(self.value) + if num_bytes > self.length: + raise errors.StateOverflowError(Integer.__name__, + 'value', self.length, + num_bytes) + + def __repr__(self): + return '' % (self.value) + + +class LongInteger(Base): + LENGTH = 8 + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(LongInteger, self).__init__(tag, type=Types.LONG_INTEGER) + self.value = value + self.length = self.LENGTH + + self.validate() + + def read_value(self, istream): + if self.length is not self.LENGTH: + raise errors.ReadValueError(LongInteger.__name__, 'length', + self.LENGTH, self.length) + + self.value = unpack('!q', str(istream.read(self.length)))[0] + self.validate() + + def read(self, istream): + super(LongInteger, self).read(istream) + self.read_value(istream) + + def write_value(self, ostream): + ostream.write(pack('!q', self.value)) + + def write(self, ostream): + super(LongInteger, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.value is not None: + data_type = type(self.value) + if data_type not in (int, long): + raise errors.StateTypeError(LongInteger.__name__, + '{0} or {1}'.format(int, long), + data_type) + num_bytes = utils.count_bytes(self.value) + if num_bytes > self.length: + raise errors.StateOverflowError(LongInteger.__name__, + 'value', self.length, + num_bytes) + + def __repr__(self): + return '' % (self.value) + + +class BigInteger(Base): + BLOCK_SIZE = 8 + SHIFT_SIZE = 64 + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(BigInteger, self).__init__(tag, type=Types.BIG_INTEGER) + self.value = value + + if self.value is not None: + self.real_length = utils.count_bytes(self.value) + self.padding_length = self.BLOCK_SIZE - (self.length % + self.BLOCK_SIZE) + if self.padding_length == self.BLOCK_SIZE: + self.padding_length = 0 + else: + self.length = None + self.padding_length = None + + self.validate() + + def read_value(self, istream): + if (self.length < self.BLOCK_SIZE) or (self.length % self.BLOCK_SIZE): + raise errors.InvalidLengthError(BigInteger.__name__, + ('multiple' + 'of {0}'.format(self.BLOCK_SIZE)), + self.length) + self.value = 0 + num_blocks = self.length / self.BLOCK_SIZE + + # Read first block as signed data + self.value = unpack('!q', str(istream.read(self.BLOCK_SIZE)))[0] + + # Shift current value and add on next unsigned block + for _ in xrange(num_blocks - 1): + self.value = self.value << self.SHIFT_SIZE + stream_data = istream.read(self.BLOCK_SIZE) + self.value += unpack('!Q', stream_data)[0] + + self.validate() + + def read(self, istream): + super(BigInteger, self).read(istream) + self.read_value(istream) + + def write_value(self, ostream): + # 1. Determine the sign of the value (+/-); save it. + # 2. Extend hex of value with 0s until encoding is right size (8x). + # 3. Write out each block of the encoding as signed, 2s complement: + # pack('!q', sign * block) + + # Determine sign for padding + pad_byte = 0x00 + pad_nybl = 0x0 + + if self.value < 0: + pad_byte = 0xff + pad_nybl = 0xf + + # Compose padding bytes + pad = '' + for _ in xrange(self.padding_length): + pad += hex(pad_byte)[2:] + + str_rep = hex(self.value).rstrip("Ll")[2:] + if len(str_rep) % 2: + pad += hex(pad_nybl)[2] + + # Compose value for block-based write + str_rep = pad + str_rep + num_blocks = len(str_rep) / self.BLOCK_SIZE + + # Write first block as signed data + block = int(str_rep[0:self.BLOCK_SIZE], 16) + ostream.write(pack('!q', block)) + + # Write remaining blocks as unsigned data + for i in xrange(1, num_blocks): + block = str_rep[(self.BLOCK_SIZE * i):(self.BLOCK_SIZE * (i + 1))] + block = int(block, 16) + ostream.write(pack('!Q', block)) + + def write(self, ostream): + super(BigInteger, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.value is not None: + data_type = type(self.value) + if data_type not in (int, long): + raise errors.StateTypeError(BigInteger.__name__, + '{0} or {1}'.format(int, long), + data_type) + num_bytes = utils.count_bytes(self.length) + if num_bytes > self.LENGTH_SIZE: + raise errors.StateOverflowError(BigInteger.__name__, + 'length', self.LENGTH_SIZE, + num_bytes) + + +class Enumeration(Integer): + ENUM_TYPE = None + + def __init__(self, value=None, tag=Tags.DEFAULT): + self.enum = value + self.validate() + + if self.enum is None: + super(Enumeration, self).__init__(None, tag) + else: + super(Enumeration, self).__init__(self.enum.value, tag) + self.type = Types.ENUMERATION + + def read(self, istream): + super(Enumeration, self).read(istream) + self.enum = self.ENUM_TYPE(self.value) + self.validate() + + def write(self, ostream): + super(Enumeration, self).write(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.enum is not None: + if type(self.enum) is not self.ENUM_TYPE: + msg = ErrorStrings.BAD_EXP_RECV + raise TypeError(msg.format(Enumeration.__name__, 'value', + Enum, type(self.enum))) + + def __repr__(self): + return '' % (self.enum.name, self.enum.value) + + +class Boolean(Base): + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(Boolean, self).__init__(tag, type=Types.BOOLEAN) + self.value = value + self.length = 8 + + def read_value(self, istream): + value = unpack('!Q', str(istream[0:self.length]))[0] + + if value == 1: + self.value = True + elif value == 0: + self.value = False + else: + raise errors.ReadValueError(Boolean.__name__, 'value', + value) + + for _ in xrange(self.length): + istream.pop(0) + + def read(self, istream): + super(Boolean, self).read(istream) + self.read_value(istream) + + def write_value(self, ostream): + if self.value is None: + raise errors.WriteValueError(Boolean.__name__, 'value', + self.value) + + data_buffer = bytearray() + + if isinstance(self.value, type(True)): + if self.value: + data_buffer.extend(pack('!Q', 1)) + else: + data_buffer.extend(pack('!Q', 0)) + else: + raise errors.WriteTypeError(Boolean.__name__, 'value', + type(self.value)) + + ostream.extend(data_buffer) + + def write(self, ostream): + super(Boolean, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + pass + + def __repr__(self): + return '' % (self.value) + + +class TextString(Base): + PADDING_SIZE = 8 + BYTE_FORMAT = '!c' + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(TextString, self).__init__(tag, type=Types.TEXT_STRING) + self.value = value + + self.validate() + + if self.value is not None: + self.length = len(self.value) + self.padding_length = self.PADDING_SIZE - (self.length % + self.PADDING_SIZE) + if self.padding_length == self.PADDING_SIZE: + self.padding_length = 0 + else: + self.length = None + self.padding_length = None + + def read_value(self, istream): + # Read string text + self.value = '' + for _ in xrange(self.length): + self.value += unpack(self.BYTE_FORMAT, str(istream.read(1)))[0] + + # Read padding and check content + self.padding_length = self.PADDING_SIZE - (self.length % + self.PADDING_SIZE) + if self.padding_length < self.PADDING_SIZE: + for _ in xrange(self.padding_length): + pad = unpack('!B', str(istream.read(1)))[0] + if pad is not 0: + raise errors.ReadValueError(TextString.__name__, 'pad', 0, + pad) + + def read(self, istream): + super(TextString, self).read(istream) + self.read_value(istream) + self.validate() + + def write_value(self, ostream): + # Write string to stream + for char in self.value: + ostream.write(pack(self.BYTE_FORMAT, char)) + + # Write padding to stream + for _ in xrange(self.padding_length): + ostream.write(pack('!B', 0)) + + def write(self, ostream): + super(TextString, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.value is not None: + data_type = type(self.value) + if data_type is not str: + msg = ErrorStrings.BAD_EXP_RECV + raise TypeError(msg.format('TextString', 'value', str, + data_type)) + + def __repr__(self): + return '' % (self.value) + + +class ByteString(Base): + PADDING_SIZE = 8 + BYTE_FORMAT = '!B' + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(ByteString, self).__init__(tag, type=Types.BYTE_STRING) + self.value = value + + self.validate() + + if self.value is not None: + self.length = len(self.value) + self.padding_length = self.PADDING_SIZE - (self.length % + self.PADDING_SIZE) + if self.padding_length == self.PADDING_SIZE: + self.padding_length = 0 + else: + self.length = None + self.padding_length = None + + def read_value(self, istream): + # Read bytes into bytearray + self.value = bytearray() + for _ in xrange(self.length): + self.value.append(istream.read(1)) + + # Read padding and check content + self.padding_length = self.PADDING_SIZE - (self.length % + self.PADDING_SIZE) + if self.padding_length == self.PADDING_SIZE: + self.padding_length = 0 + + if self.padding_length < self.PADDING_SIZE: + for _ in xrange(self.padding_length): + pad = unpack('!B', str(istream.read(1)))[0] + if pad is not 0: + raise errors.ReadValueError(TextString.__name__, 'pad', 0, + pad) + + def read(self, istream): + super(ByteString, self).read(istream) + self.read_value(istream) + + def write_value(self, ostream): + # Write bytes to stream + for byte in self.value: + ostream.write(pack(self.BYTE_FORMAT, byte)) + + # Write padding to stream + for _ in xrange(self.padding_length): + ostream.write(pack('!B', 0)) + + def write(self, ostream): + super(ByteString, self).write(ostream) + self.write_value(ostream) + + def validate(self): + self.__validate() + + def __validate(self): + if self.value is not None: + data_type = type(self.value) + if data_type is not bytearray: + msg = ErrorStrings.BAD_EXP_RECV + raise TypeError(msg.format('ByteString', 'value', bytearray, + data_type)) + + def __repr__(self): + return '' % (self.value) + + +class DateTime(LongInteger): + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(DateTime, self).__init__(value, tag) + self.type = Types.DATE_TIME + + +class Interval(Integer): + + def __init__(self, value=None, tag=Tags.DEFAULT): + super(Interval, self).__init__(value, tag) + self.type = Types.INTERVAL diff --git a/kmip/core/repo/__init__.py b/kmip/core/repo/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/core/repo/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/core/repo/mem_repo.py b/kmip/core/repo/mem_repo.py new file mode 100644 index 0000000..8726f38 --- /dev/null +++ b/kmip/core/repo/mem_repo.py @@ -0,0 +1,47 @@ +# Copyright (c) 2014 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 kmip.core.repo.repo import ManagedObjectRepo + + +class MemRepo(ManagedObjectRepo): + + def __init__(self): + self.repo = {} + self.uuid = 1 + + def save(self, managed_object, attributes): + # TODO (nate) verify the parameters + uuid = "{0}".format(self.uuid) + self.repo[uuid] = (managed_object, attributes) + self.uuid += 1 + return uuid + + def get(self, uuid): + if uuid is None or uuid not in self.repo: + return (None, None) + return self.repo[uuid] + + def update(self, uuid, managed_object, attributes): + if uuid is None: + return False + self.repo[uuid] = (managed_object, attributes) + return True + + def delete(self, uuid): + if uuid is None or uuid not in self.repo: + return False + del self.repo[uuid] + return True diff --git a/kmip/core/repo/repo.py b/kmip/core/repo/repo.py new file mode 100644 index 0000000..7b075c1 --- /dev/null +++ b/kmip/core/repo/repo.py @@ -0,0 +1,73 @@ +# Copyright (c) 2014 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. + + +class ManagedObjectRepo(object): + """Stores and manages KMIP managed objects. + + The KMIP specification details the managed objects that are stored by a + KMIP server. This repository abstraction is an interface for KMIP servers + to store managed objects. + """ + + def __init__(self): + pass + + def save(self, managed_object, attributes): + """Save a managed object + + This saves a managed object into the repository and returns a UUID + string that can be used to reference the object in the repository. + :param managed_object: managed object to save from secrets.py + :param attributes: attributes to store with the managed object + :returns: a UUID string that can be used to retrieve the object later + """ + raise NotImplementedError + + def get(self, uuid): + """Retrieve a managed object + + Retrieve a managed object from the repository. The UUID is used to + identify the managed object to return. The UUID is returned from the + save call. + + A tuple is returned that contains the managed object and all of its + attributes. + :param uuid: UUID of the managed object + :returns: (managed_object, attributes) if object exists, otherwise + (None, None) + """ + raise NotImplementedError + + def update(self, uuid, managed_object, attributes): + """Updates a managed object + + Updates the values for a managed_object. + :param uuid: UUID of the managed object + :param managed_object: managed object + :param attributes: attributes to store with the managed object + :returns: True if object existed and successfully updated, otherwise + False + """ + raise NotImplementedError + + def delete(self, uuid): + """Delete a managed object from the repository + + Delete a managed object from the repository. + :param uuid: UUID of the managed object + :returns: True if successfully deleted, False if not found + """ + raise NotImplementedError diff --git a/kmip/core/secrets.py b/kmip/core/secrets.py new file mode 100644 index 0000000..08b080d --- /dev/null +++ b/kmip/core/secrets.py @@ -0,0 +1,408 @@ +# Copyright (c) 2014 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 kmip.core.attributes import CertificateType + +from kmip.core import enums +from kmip.core.enums import Tags + +from kmip.core.objects import Attribute +from kmip.core.objects import KeyBlock + +from kmip.core.primitives import Struct +from kmip.core.primitives import Integer +from kmip.core.primitives import Enumeration +from kmip.core.primitives import BigInteger +from kmip.core.primitives import ByteString + +from kmip.core.utils import BytearrayStream + + +# 2.2 +# 2.2.1 +class Certificate(Struct): + + class CertificateValue(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.CERTIFICATE_VALUE) + + def __init__(self, + certificate_type=None, + certificate_value=None): + super(self.__class__, self).__init__(Tags.CERTIFICATE) + self.certificate_type = certificate_type + self.certificate_value = certificate_value + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.certificate_type = CertificateType() + self.certificate_value = Certificate.CertificateValue() + + self.certificate_type.read(tstream) + self.certificate_value.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + # Write the details of the certificate + self.certificate_type.write(tstream) + self.certificate_value.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.2 +class KeyBlockKey(Struct): + + def __init__(self, key_block=None, tag=Tags.DEFAULT): + super(KeyBlockKey, self).__init__(tag) + self.key_block = key_block + self.validate() + + def read(self, istream): + super(KeyBlockKey, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.key_block = KeyBlock() + self.key_block.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.key_block.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(KeyBlockKey, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +class SymmetricKey(KeyBlockKey): + + def __init__(self, key_block=None): + super(self.__class__, self).__init__(key_block, Tags.SYMMETRIC_KEY) + self.validate() + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.3 +class PublicKey(KeyBlockKey): + + def __init__(self, key_block=None): + super(self.__class__, self).__init__(key_block, Tags.PUBLIC_KEY) + self.validate() + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.4 +class PrivateKey(KeyBlockKey): + + def __init__(self, key_block=None): + super(self.__class__, self).__init__(key_block, Tags.PRIVATE_KEY) + self.validate() + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.5 +class SplitKey(Struct): + + class SplitKeyParts(Integer): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.SPLIT_KEY_PARTS) + + class KeyPartIdentifier(Integer): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.KEY_PART_IDENTIFIER) + + class SplitKeyThreshold(Integer): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.SPLIT_KEY_THRESHOLD) + + class SplitKeyMethod(Enumeration): + ENUM_TYPE = enums.SplitKeyMethod + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.SPLIT_KEY_METHOD) + + class PrimeFieldSize(BigInteger): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.PRIME_FIELD_SIZE) + + def __init__(self, + split_key_parts=None, + key_part_identifier=None, + split_key_threshold=None, + split_key_method=None, + prime_field_size=None, + key_block=None): + super(self.__class__, self).__init__(Tags.SPLIT_KEY) + self.split_key_parts = split_key_parts + self.key_part_identifier = key_part_identifier + self.split_key_threshold = split_key_threshold + self.split_key_method = split_key_method + self.prime_field_size = prime_field_size + self.key_block = key_block + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.split_key_parts = SplitKey.SplitKeyParts() + self.split_key_parts.read(tstream) + + self.key_part_identifier = SplitKey.KeyPartIdentifier() + self.key_part_identifier.read(tstream) + + self.split_key_threshold = SplitKey.SplitKeyThreshold() + self.split_key_threshold.read(tstream) + + if self.is_tag_next(Tags.PRIME_FIELD_SIZE, tstream): + self.prime_field_size = SplitKey.PrimeFieldSize() + self.prime_field_size.read(tstream) + + self.key_block = KeyBlock() + self.key_block.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.split_key_parts.write(tstream) + self.key_part_identifier.write(tstream) + self.split_key_threshold.write(tstream) + self.split_key_method.write(tstream) + + if self.prime_field_size is not None: + self.prime_field_size.write(tstream) + + self.key_block.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.6 +class Template(Struct): + + def __init__(self, attributes=None): + super(self.__class__, self).__init__(Tags.TEMPLATE) + self.attributes = attributes + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.attributes = list() + + attribute = Attribute() + attribute.read(tstream) + self.attributes.append(attribute) + + while self.is_tag_next(Tags.ATTRIBUTE, tstream): + attribute = Attribute() + attribute.read(tstream) + self.attributes.append(attribute) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + for attribute in self.attributes: + attribute.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.7 +class SecretData(Struct): + + class SecretDataType(Enumeration): + ENUM_TYPE = enums.SecretDataType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.SECRET_DATA_TYPE) + + def __init__(self, + secret_data_type=None, + key_block=None): + super(self.__class__, self).__init__(Tags.SECRET_DATA) + self.secret_data_type = secret_data_type + self.key_block = key_block + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.secret_data_type = SecretData.SecretDataType() + self.key_block = KeyBlock() + + self.secret_data_type.read(tstream) + self.key_block.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.secret_data_type.write(tstream) + self.key_block.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + +# 2.2.8 +class OpaqueObject(Struct): + + class OpaqueDataType(Enumeration): + ENUM_TYPE = enums.OpaqueDataType + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.OPAQUE_DATA_TYPE) + + class OpaqueDataValue(ByteString): + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, Tags.OPAQUE_DATA_VALUE) + + def __init__(self, + opaque_data_type=None, + opaque_data_value=None): + super(self.__class__, self).__init__(Tags.OPAQUE_OBJECT) + self.opaque_data_type = opaque_data_type + self.opaque_data_value = opaque_data_value + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + self.opaque_data_type = OpaqueObject.OpaqueDataType() + self.opaque_data_value = OpaqueObject.OpaqueDataValue() + + self.opaque_data_type.read(tstream) + self.opaque_data_value.read(tstream) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + self.opaque_data_type.write(tstream) + self.opaque_data_value.write(tstream) + + # Write the length and value of the template attribute + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass diff --git a/kmip/core/server.py b/kmip/core/server.py new file mode 100644 index 0000000..d8e9bb8 --- /dev/null +++ b/kmip/core/server.py @@ -0,0 +1,357 @@ +# Copyright (c) 2014 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. + +import logging +import os + +from kmip.core.attributes import CryptographicLength +from kmip.core.attributes import CryptographicAlgorithm +from kmip.core.attributes import ObjectType +from kmip.core.attributes import UniqueIdentifier +from kmip.core.enums import AttributeType as AT +from kmip.core.enums import CryptographicAlgorithm as CA +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum +from kmip.core.enums import ObjectType as OT +from kmip.core.enums import ResultReason as ResultReasonEnum +from kmip.core.enums import ResultStatus as RS +from kmip.core.factories.attributes import AttributeFactory +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.objects import KeyBlock +from kmip.core.objects import KeyValue +from kmip.core.objects import KeyValueStruct +from kmip.core.objects import TemplateAttribute +from kmip.core.repo.mem_repo import MemRepo +from kmip.core.secrets import SymmetricKey +from kmip.services.results import CreateResult +from kmip.services.results import DestroyResult +from kmip.services.results import GetResult +from kmip.services.results import OperationResult +from kmip.services.results import RegisterResult + + +class KMIP(object): + + def __init__(self): + pass + + def create(self, object_type, template_attribute, credential=None): + raise NotImplementedError + + def register(self, object_type, template_attribute, secret, + credential=None): + raise NotImplementedError + + def get(self, uuid=None, key_format_type=None, key_compression_type=None, + key_wrapping_specification=None, credential=None): + raise NotImplementedError + + def destroy(self, uuid, credential=None): + raise NotImplementedError + + +class KMIPImpl(KMIP): + + def __init__(self): + super(self.__class__, self).__init__() + self.logger = logging.getLogger(__name__) + self.key_factory = KeyFactory() + self.secret_factory = SecretFactory() + self.attribute_factory = AttributeFactory() + self.repo = MemRepo() + + def create(self, object_type, template_attribute, credential=None): + self.logger.debug('create() called') + self.logger.debug('object type = %s' % object_type) + bit_length = 256 + attributes = template_attribute.attributes + ret_attributes = [] + if object_type.enum != OT.SYMMETRIC_KEY: + self.logger.debug('invalid object type') + return self._get_invalid_field_result('invalid object type') + try: + alg_attr =\ + self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_ALGORITHM.value, + (CA.AES.value,), + 'unsupported algorithm') + len_attr = self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_LENGTH.value, + (128, 256, 512), + 'unsupported key length', + False) + self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_USAGE_MASK.value, + (), + '') + except InvalidFieldException as e: + self.logger.debug('InvalidFieldException raised') + return e.result + + crypto_alg = CryptographicAlgorithm(CA(alg_attr.attribute_value.value)) + + if len_attr is None: + self.logger.debug('cryptographic length not supplied') + attribute_type = AT.CRYPTOGRAPHIC_LENGTH + length_attribute = self.attribute_factory.\ + create_attribute(attribute_type, bit_length) + attributes.append(length_attribute) + ret_attributes.append(length_attribute) + else: + bit_length = len_attr.attribute_value.value + + key = self._gen_symmetric_key(bit_length, crypto_alg) + s_uuid, uuid_attribute = self._save(key, attributes) + ret_attributes.append(uuid_attribute) + template_attribute = TemplateAttribute(attributes=ret_attributes) + return CreateResult(ResultStatus(RS.SUCCESS), + object_type=object_type, + uuid=UniqueIdentifier(s_uuid), + template_attribute=template_attribute) + + def register(self, object_type, template_attribute, secret, + credential=None): + self.logger.debug('register() called') + self.logger.debug('object type = %s' % object_type) + attributes = template_attribute.attributes + ret_attributes = [] + if object_type is None: + self.logger.debug('invalid object type') + return self._get_missing_field_result('object type') + if object_type.enum != OT.SYMMETRIC_KEY: + self.logger.debug('invalid object type') + return self._get_invalid_field_result('invalid object type') + if secret is None or not isinstance(secret, SymmetricKey): + msg = 'object type does not match that of secret' + self.logger.debug(msg) + return self._get_invalid_field_result(msg) + + self.logger.debug('Collecting all attributes') + if attributes is None: + attributes = [] + attributes.extend(self._get_key_block_attributes(secret.key_block)) + + self.logger.debug('Verifying all attributes are valid and set') + try: + self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_ALGORITHM.value, + (CA.AES.value,), + 'unsupported algorithm') + self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_LENGTH.value, + (128, 256, 512), + 'unsupported key length') + self._validate_req_field(attributes, + AT.CRYPTOGRAPHIC_USAGE_MASK.value, + (), + '') + except InvalidFieldException as e: + self.logger.debug('InvalidFieldException raised') + return RegisterResult(e.result.result_status, + e.result.result_reason, + e.result.result_message) + + s_uuid, uuid_attribute = self._save(secret, attributes) + ret_attributes.append(uuid_attribute) + template_attribute = TemplateAttribute(attributes=ret_attributes) + return RegisterResult(ResultStatus(RS.SUCCESS), + uuid=UniqueIdentifier(s_uuid), + template_attribute=template_attribute) + + def get(self, + uuid=None, + key_format_type=None, + key_compression_type=None, + key_wrapping_specification=None, + credential=None): + self.logger.debug('get() called') + ret_value = RS.OPERATION_FAILED + if uuid is None or not hasattr(uuid, 'value'): + self.logger.debug('no uuid provided') + reason = ResultReason(ResultReasonEnum.ITEM_NOT_FOUND) + message = ResultMessage('') + return GetResult(ResultStatus(ret_value), reason, message) + if key_format_type is None: + self.logger.debug('key format type is None, setting to raw') + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + if key_format_type.enum != KeyFormatTypeEnum.RAW: + self.logger.debug('key format type is not raw') + reason = ResultReason(ResultReasonEnum. + KEY_FORMAT_TYPE_NOT_SUPPORTED) + message = ResultMessage('') + return GetResult(ResultStatus(ret_value), reason, message) + if key_compression_type is not None: + self.logger.debug('key compression type is not None') + reason = ResultReason(ResultReasonEnum. + KEY_COMPRESSION_TYPE_NOT_SUPPORTED) + message = ResultMessage('') + return GetResult(ResultStatus(ret_value), reason, message) + if key_wrapping_specification is not None: + self.logger.debug('key wrapping specification is not None') + reason = ResultReason(ResultReasonEnum.FEATURE_NOT_SUPPORTED) + message = ResultMessage('key wrapping is not currently supported') + return GetResult(ResultStatus(ret_value), reason, message) + + self.logger.debug('retrieving object from repo') + managed_object, _ = self.repo.get(uuid.value) + + if managed_object is None: + self.logger.debug('object not found in repo') + reason = ResultReason(ResultReasonEnum.ITEM_NOT_FOUND) + message = ResultMessage('') + return GetResult(ResultStatus(ret_value), reason, message) + + # currently only symmetric keys are supported, fix this in future + object_type = ObjectType(OT.SYMMETRIC_KEY) + ret_value = RS.SUCCESS + return GetResult(ResultStatus(ret_value), + object_type=object_type, + uuid=uuid, + secret=managed_object) + + def destroy(self, uuid): + self.logger.debug('destroy() called') + ret_value = RS.OPERATION_FAILED + if uuid is None or not hasattr(uuid, 'value'): + self.logger.debug('no uuid provided') + reason = ResultReason(ResultReasonEnum.ITEM_NOT_FOUND) + message = ResultMessage('') + return DestroyResult(ResultStatus(ret_value), reason, message) + + msg = 'deleting object from repo: {0}'.format(uuid) + self.logger.debug(msg) + if not self.repo.delete(uuid.value): + self.logger.debug('repo did not find and delete managed object') + reason = ResultReason(ResultReasonEnum.ITEM_NOT_FOUND) + message = ResultMessage('') + return DestroyResult(ResultStatus(ret_value), reason, message) + + ret_value = RS.SUCCESS + return DestroyResult(ResultStatus(ret_value), uuid=uuid) + + def _validate_req_field(self, attrs, name, expected, msg, required=True): + self.logger.debug('Validating attribute %s' % name) + seen = False + found_attr = None + for attr in attrs: + if self._validate_field(attr, name, expected, msg): + if seen: + # TODO check what spec says to do on this + msg = 'duplicate attribute: %s' % name + self.logger.debug(msg) + result = self._get_duplicate_attribute_result(name) + raise InvalidFieldException(result) + seen = True + found_attr = attr + if required and not seen: + result = self._get_missing_field_result(name) + raise InvalidFieldException(result) + return found_attr + + def _validate_field(self, attr, name, expected, msg): + if attr.attribute_name.value == name: + self.logger.debug('validating attribute %s' % name) + if not expected or attr.attribute_value.value in expected: + self.logger.debug('attribute validated') + return True + else: + self.logger.debug('attribute not validated') + result = self._get_invalid_field_result(msg) + raise InvalidFieldException(result) + else: + return False + + def _get_invalid_field_result(self, msg): + status = ResultStatus(RS.OPERATION_FAILED) + reason = ResultReason(ResultReasonEnum.INVALID_FIELD) + message = ResultMessage(msg) + return OperationResult(status, reason, message) + + def _get_missing_field_result(self, name): + msg = '%s not supplied' % name + self.logger.debug(msg) + status = ResultStatus(RS.OPERATION_FAILED) + reason = ResultReason(ResultReasonEnum.ITEM_NOT_FOUND) + message = ResultMessage(msg) + return OperationResult(status, reason, message) + + def _get_duplicate_attribute_result(self, name): + msg = '%s supplied multiple times' % name + self.logger.debug(msg) + status = ResultStatus(RS.OPERATION_FAILED) + reason = ResultReason(ResultReasonEnum.INDEX_OUT_OF_BOUNDS) + message = ResultMessage(msg) + return OperationResult(status, reason, message) + + def _gen_symmetric_key(self, bit_length, crypto_alg): + key_format_type = KeyBlock.KeyFormatType(KeyFormatTypeEnum.RAW) + key_material = RawKey(bytearray(os.urandom(bit_length/8))) + key_value = KeyValueStruct(key_format_type, key_material) + crypto_length = CryptographicLength(bit_length) + key_block = KeyBlock(key_format_type, None, key_value, crypto_alg, + crypto_length, None) + return SymmetricKey(key_block) + + def _save(self, key, attributes): + s_uuid = self.repo.save(key, attributes) + self.logger.debug('creating object with uuid = %s' % s_uuid) + attribute_type = AT.UNIQUE_IDENTIFIER + attribute = self.attribute_factory.create_attribute(attribute_type, + s_uuid) + attributes.append(attribute) + # Calling update to also store the UUID + self.repo.update(s_uuid, key, attributes) + return s_uuid, attribute + + def _get_key_block_attributes(self, key_block): + self.logger.debug('getting all key attributes from key block') + attributes = [] + if key_block.cryptographic_algorithm is not None: + self.logger.debug('crypto_alg set on key block') + self.logger.debug('adding crypto algorithm attribute') + at = AT.CRYPTOGRAPHIC_ALGORITHM + alg = key_block.cryptographic_algorithm.enum + attributes.append(self.attribute_factory.create_attribute(at, alg)) + if key_block.cryptographic_length is not None: + self.logger.debug('crypto_length set on key block') + self.logger.debug('adding crypto length attribute') + at = AT.CRYPTOGRAPHIC_LENGTH + len = key_block.cryptographic_length.value + attributes.append(self.attribute_factory.create_attribute(at, len)) + self.logger.debug('getting key value attributes') + if key_block.key_wrapping_data is not None: + self.logger.debug('no wrapping data so key value is struct') + kv = key_block.key_value + if isinstance(kv, KeyValue): + kv = key_block.key_value + if isinstance(kv, KeyValueStruct): + if kv.attributes is not None: + self.logger.debug('adding the key value struct attributes') + attributes.extend(kv.attributes) + return attributes + + +class InvalidFieldException(Exception): + + def __init__(self, result): + super(self.__class__, self).__init__() + self.result = result diff --git a/kmip/core/utils.py b/kmip/core/utils.py new file mode 100644 index 0000000..2c00d87 --- /dev/null +++ b/kmip/core/utils.py @@ -0,0 +1,97 @@ +# Copyright (c) 2014 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 binascii import hexlify + +from kmip.core.errors import ErrorStrings + + +def bit_length(num): + s = bin(num) + s = s.lstrip('0b') + return len(s) + + +def count_bytes(num): + bits = bit_length(num) + num_bytes = bits / 8 + if bits == 0 or bits % 8: + num_bytes += 1 + return num_bytes + + +def print_bytearray(array): + sbuffer = hexlify_bytearray(array) + print 'buffer: {0}'.format(sbuffer) + + +def hexlify_bytearray(array): + sbuffer = bytes(array[0:]) + return hexlify(sbuffer) + + +def is_stream_empty(stream): + if len(stream.peek(1)) > 0: + return False + else: + return True + + +def build_er_error(class_object, descriptor, expected, received, + attribute=None): + msg = ErrorStrings.BAD_EXP_RECV + + class_string = '' + if attribute is None: + class_string = '{0}'.format(class_object.__name__) + else: + class_string = '{0}.{1}'.format(class_object.__name__, attribute) + + return msg.format(class_string, descriptor, expected, received) + + +class BytearrayStream(object): + def __init__(self, data=None): + if data is None: + self.buffer = bytearray() + else: + self.buffer = bytearray(data) + + def read(self, n=None): + if n is None: + return str(self.buffer[0:]) + + length = len(self.buffer) + if n > length: + n = length + + data = self.buffer[0:n] + for _ in xrange(n): + self.buffer.pop(0) + return str(data) + + def peek(self, n=None): + length = len(self.buffer) + if n is None or n > length: + n = length + return str(self.buffer[0:n]) + + def write(self, b): + prev_bytes = len(self.buffer) + self.buffer.extend(b) + return len(self.buffer) - prev_bytes + + def length(self): + return len(self.buffer) diff --git a/kmip/demos/__init__.py b/kmip/demos/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/demos/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/demos/create.py b/kmip/demos/create.py new file mode 100644 index 0000000..7dab20d --- /dev/null +++ b/kmip/demos/create.py @@ -0,0 +1,69 @@ +# Copyright (c) 2014 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 kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import ObjectType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory + +from kmip.core.objects import TemplateAttribute + +from kmip.services.kmip_client import KMIPProxy + +import logging +import os + +if __name__ == '__main__': + f_log = os.path.join(os.path.dirname(__file__), '..', 'logconfig.ini') + logging.config.fileConfig(f_log) + logger = logging.getLogger(__name__) + + attribute_factory = AttributeFactory() + credential_factory = CredentialFactory() + + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = credential_factory.create_credential(credential_type, + credential_value) + + client = KMIPProxy() + client.open() + + object_type = ObjectType.SYMMETRIC_KEY + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + algorithm = attribute_factory.create_attribute(attribute_type, + CryptographicAlgorithm.AES) + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = attribute_factory.create_attribute(attribute_type, + mask_flags) + attributes = [algorithm, usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + result = client.create(object_type, template_attribute, + credential) + client.close() + + logger.debug('create() result status: {}'.format( + result.result_status.enum)) + logger.debug('created object type: {}'.format(result.object_type.enum)) + logger.debug('created UUID: {}'.format(result.uuid.value)) + logger.debug('created template attribute: {}'. + format(result.template_attribute)) diff --git a/kmip/demos/destroy.py b/kmip/demos/destroy.py new file mode 100644 index 0000000..91c44d9 --- /dev/null +++ b/kmip/demos/destroy.py @@ -0,0 +1,70 @@ +# Copyright (c) 2014 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 kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import ObjectType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory + +from kmip.core.objects import TemplateAttribute + +from kmip.services.kmip_client import KMIPProxy + +import logging +import os + +if __name__ == '__main__': + f_log = os.path.join(os.path.dirname(__file__), '..', 'logconfig.ini') + logging.config.fileConfig(f_log) + logger = logging.getLogger(__name__) + + attribute_factory = AttributeFactory() + credential_factory = CredentialFactory() + + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = credential_factory.create_credential(credential_type, + credential_value) + client = KMIPProxy() + client.open() + + # Create a SYMMETRIC_KEY object + object_type = ObjectType.SYMMETRIC_KEY + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + algorithm = attribute_factory.create_attribute(attribute_type, + CryptographicAlgorithm.AES) + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = attribute_factory.create_attribute(attribute_type, + mask_flags) + attributes = [algorithm, usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + result = client.create(object_type, template_attribute, + credential) + uuid = result.uuid.value + + # Destroy the SYMMETRIC_KEY object + result = client.destroy(uuid, credential) + client.close() + + logger.debug('destroy() result status: {}'.format( + result.result_status.enum)) + logger.debug('destroyed UUID: {}'.format(result.uuid.value)) diff --git a/kmip/demos/get.py b/kmip/demos/get.py new file mode 100644 index 0000000..38df8a8 --- /dev/null +++ b/kmip/demos/get.py @@ -0,0 +1,69 @@ +# Copyright (c) 2014 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 kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import ObjectType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory + +from kmip.core.objects import TemplateAttribute + +from kmip.services.kmip_client import KMIPProxy + +import logging +import os + +if __name__ == '__main__': + f_log = os.path.join(os.path.dirname(__file__), '..', 'logconfig.ini') + logging.config.fileConfig(f_log) + logger = logging.getLogger(__name__) + + attribute_factory = AttributeFactory() + credential_factory = CredentialFactory() + + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = credential_factory.create_credential(credential_type, + credential_value) + client = KMIPProxy() + client.open() + + object_type = ObjectType.SYMMETRIC_KEY + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + algorithm = attribute_factory.create_attribute(attribute_type, + CryptographicAlgorithm.AES) + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = attribute_factory.create_attribute(attribute_type, + mask_flags) + attributes = [algorithm, usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + result = client.create(object_type, template_attribute, + credential) + uuid = result.uuid.value + + result = client.get(uuid, credential) + client.close() + + logger.debug('get() result status: {}'.format(result.result_status.enum)) + logger.debug('retrieved object type: {}'.format(result.object_type.enum)) + logger.debug('retrieved UUID: {}'.format(result.uuid.value)) + logger.debug('retrieved secret: {}'.format(result.secret)) diff --git a/kmip/demos/register.py b/kmip/demos/register.py new file mode 100644 index 0000000..4d4b4d4 --- /dev/null +++ b/kmip/demos/register.py @@ -0,0 +1,84 @@ +# Copyright (c) 2014 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 kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask +from kmip.core.enums import KeyFormatType +from kmip.core.enums import ObjectType + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory +from kmip.core.factories.secrets import SecretFactory + +from kmip.core.objects import TemplateAttribute + +from kmip.services.kmip_client import KMIPProxy + +import logging +import os + +if __name__ == '__main__': + f_log = os.path.join(os.path.dirname(__file__), '..', 'logconfig.ini') + logging.config.fileConfig(f_log) + logger = logging.getLogger(__name__) + + attribute_factory = AttributeFactory() + credential_factory = CredentialFactory() + secret_factory = SecretFactory() + + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = credential_factory.create_credential(credential_type, + credential_value) + client = KMIPProxy() + client.open() + + object_type = ObjectType.SYMMETRIC_KEY + algorithm_value = CryptographicAlgorithm.AES + + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = attribute_factory.create_attribute(attribute_type, + mask_flags) + attributes = [usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + secret_features = {} + + key_format_type = KeyFormatType.RAW + secret_features.update([('key_format_type', key_format_type)]) + + # TODO (peter-hamilton) Replace with calls to crypto libraries + key_data = {'bytes': bytearray('\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00')} + + secret_features.update([('key_value', key_data)]) + secret_features.update([('cryptographic_algorithm', algorithm_value)]) + secret_features.update([('cryptographic_length', 128)]) + + secret = secret_factory.create_secret(object_type, secret_features) + + result = client.register(object_type, template_attribute, secret, + credential) + client.close() + + logger.debug('register() result status: {}'.format( + result.result_status.enum)) + logger.debug('registered UUID: {}'.format(result.uuid.value)) + logger.debug('registered template attribute: {}'. + format(result.template_attribute)) diff --git a/kmip/kmip_client.py b/kmip/kmip_client.py deleted file mode 100755 index f84178f..0000000 --- a/kmip/kmip_client.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -# TODO insert license here - -from transport.kmip import KMIP -from transport.kmip.ttypes import * - -from thrift import Thrift -from thrift.transport import TSocket -from thrift.transport import TTransport -from thrift.protocol import TBinaryProtocol - -try: - - transport = TSocket.TSocket('localhost', 9090) - transport = TTransport.TBufferedTransport(transport) - protocol = TBinaryProtocol.TBinaryProtocol(transport) - client = KMIP.Client(protocol) - transport.open() - - print 'ping-client()' - client.create() - print 'register_mo-client()' - client.register_mo() - - transport.close() - -except Thrift.TException, tx: - print '%s' % (tx.message) diff --git a/kmip/kmip_server.py b/kmip/kmip_server.py deleted file mode 100755 index 889af51..0000000 --- a/kmip/kmip_server.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -# TODO insert license here - -from transport.kmip import KMIP -from transport.kmip.ttypes import * - -from thrift.transport import TSocket -from thrift.transport import TTransport -from thrift.protocol import TBinaryProtocol -from thrift.server import TServer - -class KMIPHandler: - def __init__(self): - pass - - def create(self): - print 'create()' - - def register_mo(self): - print 'register_mo()' - -handler = KMIPHandler() -processor = KMIP.Processor(handler) -transport = TSocket.TServerSocket(port=9090) -tfactory = TTransport.TBufferedTransportFactory() -pfactory = TBinaryProtocol.TBinaryProtocolFactory() -server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) - -print 'Starting the KMIP server...' -server.serve() -print 'done.' diff --git a/kmip/logconfig.ini b/kmip/logconfig.ini new file mode 100644 index 0000000..ed4b825 --- /dev/null +++ b/kmip/logconfig.ini @@ -0,0 +1,21 @@ +[loggers] +keys=root + +[handlers] +keys=consoleHandler + +[formatters] +keys=simpleFormatter + +[logger_root] +level=DEBUG +handlers=consoleHandler + +[handler_consoleHandler] +class=StreamHandler +level=DEBUG +formatter=simpleFormatter +args=(sys.stdout,) + +[formatter_simpleFormatter] +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s \ No newline at end of file diff --git a/kmip/services/__init__.py b/kmip/services/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/services/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/services/kmip_client.py b/kmip/services/kmip_client.py new file mode 100644 index 0000000..9813d9d --- /dev/null +++ b/kmip/services/kmip_client.py @@ -0,0 +1,276 @@ +# Copyright (c) 2014 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 kmip.services.results import CreateResult +from kmip.services.results import GetResult +from kmip.services.results import DestroyResult +from kmip.services.results import RegisterResult + +from kmip.core import attributes as attr + +from kmip.core.enums import Operation as OperationEnum + +from kmip.core import objects +from kmip.core.server import KMIP + +from kmip.core.messages.contents import Authentication +from kmip.core.messages.contents import BatchCount +from kmip.core.messages.contents import ProtocolVersion +from kmip.core.messages.contents import Operation + +from kmip.core.messages import messages +from kmip.core.messages import operations + +from kmip.services.kmip_protocol import KMIPProtocol + +from kmip.core.utils import BytearrayStream + +from thrift.transport import TSocket +from thrift.transport import TTransport + +import logging +import logging.config + + +class KMIPProxy(KMIP): + + # TODO (peter-hamilton) Move these defaults into config + def __init__(self, hostname='127.0.0.1', port=5696): + super(self.__class__, self).__init__() + self.logger = logging.getLogger(__name__) + self.socket = TSocket.TSocket(hostname, port) + self.transport = TTransport.TBufferedTransport(self.socket) + self.protocol = KMIPProtocol(self.transport) + + def open(self): + self.transport.open() + + def close(self): + self.transport.close() + + def create(self, object_type, template_attribute, credential=None): + object_type = attr.ObjectType(object_type) + return self._create(object_type=object_type, + template_attribute=template_attribute, + credential=credential) + + def get(self, uuid=None, key_format_type=None, key_compression_type=None, + key_wrapping_specification=None, credential=None): + return self._get(unique_identifier=uuid, credential=credential) + + def destroy(self, uuid, credential=None): + return self._destroy(unique_identifier=uuid, + credential=credential) + + def register(self, object_type, template_attribute, secret, + credential=None): + object_type = attr.ObjectType(object_type) + return self._register(object_type=object_type, + template_attribute=template_attribute, + secret=secret, + credential=credential) + + def _create(self, + object_type=None, + template_attribute=None, + credential=None): + operation = Operation(OperationEnum.CREATE) + + if object_type is None: + raise ValueError('object_type cannot be None') + + req_pl = operations.CreateRequestPayload( + object_type=object_type, + template_attribute=template_attribute) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=req_pl) + + message = self._build_request_message(credential, [batch_item]) + self._send_message(message) + message = messages.ResponseMessage() + data = self._receive_message() + message.read(data) + batch_items = message.batch_items + batch_item = batch_items[0] + payload = batch_item.response_payload + + if payload is None: + payload_unique_identifier = None + payload_template_attribute = None + payload_object_type = None + else: + payload_unique_identifier = payload.unique_identifier + payload_template_attribute = payload.template_attribute + payload_object_type = payload.object_type + + result = CreateResult(batch_item.result_status, + batch_item.result_reason, + batch_item.result_message, + payload_object_type, + payload_unique_identifier, + payload_template_attribute) + return result + + def _get(self, + unique_identifier=None, + key_format_type=None, + key_compression_type=None, + key_wrapping_specification=None, + credential=None): + operation = Operation(OperationEnum.GET) + + uuid = None + kft = None + kct = None + kws = None + + if unique_identifier is not None: + uuid = attr.UniqueIdentifier(unique_identifier) + if key_format_type is not None: + kft = operations.GetRequestPayload.KeyFormatType(key_format_type) + if key_compression_type is not None: + kct = key_compression_type + kct = operations.GetRequestPayload.KeyCompressionType(kct) + if key_wrapping_specification is not None: + kws = objects.KeyWrappingSpecification(key_wrapping_specification) + + req_pl = operations.GetRequestPayload(unique_identifier=uuid, + key_format_type=kft, + key_compression_type=kct, + key_wrapping_specification=kws) + + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=req_pl) + message = self._build_request_message(credential, [batch_item]) + self._send_message(message) + message = messages.ResponseMessage() + data = self._receive_message() + message.read(data) + batch_items = message.batch_items + batch_item = batch_items[0] + payload = batch_item.response_payload + + if payload is None: + payload_unique_identifier = None + payload_object_type = None + payload_secret = None + else: + payload_unique_identifier = payload.unique_identifier + payload_object_type = payload.object_type + payload_secret = payload.secret + + result = GetResult(batch_item.result_status, + batch_item.result_reason, + batch_item.result_message, + payload_object_type, + payload_unique_identifier, + payload_secret) + return result + + def _destroy(self, + unique_identifier=None, + credential=None): + operation = Operation(OperationEnum.DESTROY) + + uuid = None + if unique_identifier is not None: + uuid = attr.UniqueIdentifier(unique_identifier) + + payload = operations.DestroyRequestPayload(unique_identifier=uuid) + + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=payload) + message = self._build_request_message(credential, [batch_item]) + self._send_message(message) + message = messages.ResponseMessage() + data = self._receive_message() + message.read(data) + batch_items = message.batch_items + batch_item = batch_items[0] + payload = batch_item.response_payload + + if payload is None: + payload_unique_identifier = None + else: + payload_unique_identifier = payload.unique_identifier + + result = DestroyResult(batch_item.result_status, + batch_item.result_reason, + batch_item.result_message, + payload_unique_identifier) + return result + + def _register(self, + object_type=None, + template_attribute=None, + secret=None, + credential=None): + operation = Operation(OperationEnum.REGISTER) + + if object_type is None: + raise ValueError('object_type cannot be None') + + req_pl = operations.RegisterRequestPayload( + object_type=object_type, + template_attribute=template_attribute, + secret=secret) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=req_pl) + + message = self._build_request_message(credential, [batch_item]) + self._send_message(message) + message = messages.ResponseMessage() + data = self._receive_message() + message.read(data) + batch_items = message.batch_items + batch_item = batch_items[0] + payload = batch_item.response_payload + + if payload is None: + payload_unique_identifier = None + payload_template_attribute = None + else: + payload_unique_identifier = payload.unique_identifier + payload_template_attribute = payload.template_attribute + + result = RegisterResult(batch_item.result_status, + batch_item.result_reason, + batch_item.result_message, + payload_unique_identifier, + payload_template_attribute) + return result + + def _build_request_message(self, credential, batch_items): + protocol_version = ProtocolVersion.create(1, 1) + + authentication = None + if credential is not None: + authentication = Authentication(credential) + + batch_count = BatchCount(len(batch_items)) + req_header = messages.RequestHeader(protocol_version=protocol_version, + authentication=authentication, + batch_count=batch_count) + + return messages.RequestMessage(request_header=req_header, + batch_items=batch_items) + + def _send_message(self, message): + stream = BytearrayStream() + message.write(stream) + self.protocol.write(stream.buffer) + + def _receive_message(self): + return self.protocol.read() diff --git a/kmip/services/kmip_protocol.py b/kmip/services/kmip_protocol.py new file mode 100644 index 0000000..26b13ca --- /dev/null +++ b/kmip/services/kmip_protocol.py @@ -0,0 +1,50 @@ +# Copyright (c) 2014 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 struct import unpack + +from thrift.protocol.TProtocol import TProtocolBase + +import binascii +import logging + +from kmip.core.utils import BytearrayStream + + +class KMIPProtocol(TProtocolBase): + HEADER_SIZE = 8 + + def __init__(self, trans, buffer_size=1024): + TProtocolBase.__init__(self, trans) + self.logger = logging.getLogger(__name__) + + def write(self, data): + if len(data) > 0: + sbuffer = bytes(data) + self.logger.debug('buffer: {0}'.format(binascii.hexlify(sbuffer))) + self.trans.write(sbuffer) + self.trans.flush() + + def read(self): + header = self.trans.readAll(self.HEADER_SIZE) + msg_size = unpack('!I', header[4:])[0] + payload = self.trans.readAll(msg_size) + return BytearrayStream(header + payload) + + +class KMIPProtocolFactory(object): + + def getProtocol(self, trans): + return KMIPProtocol(trans) diff --git a/kmip/services/kmip_server.py b/kmip/services/kmip_server.py new file mode 100755 index 0000000..28d1100 --- /dev/null +++ b/kmip/services/kmip_server.py @@ -0,0 +1,253 @@ +# Copyright (c) 2014 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. + +import time + +from thrift.Thrift import TProcessor + +from kmip.core.messages.messages import RequestMessage +from kmip.core.messages.messages import ResponseMessage +from kmip.core.messages.messages import ResponseBatchItem +from kmip.core.messages.messages import ResponseHeader + +from kmip.core.messages.contents import AsynchronousIndicator +from kmip.core.messages.contents import BatchErrorContinuationOption +from kmip.core.messages.contents import BatchCount +from kmip.core.messages.contents import TimeStamp + +from kmip.core.primitives import Base + +from kmip.core.messages.operations import CreateResponsePayload +from kmip.core.messages.operations import GetResponsePayload +from kmip.core.messages.operations import DestroyResponsePayload +from kmip.core.messages.operations import RegisterResponsePayload + +from kmip.core.enums import Operation +from kmip.core.enums import ResultStatus as RS +from kmip.core.enums import Tags +from kmip.core.enums import BatchErrorContinuationOption as BECO + +from kmip.core.utils import BytearrayStream + + +class Processor(TProcessor): + def __init__(self, handler): + self._handler = handler + + def process(self, istream, ostream): + stream = istream.read() + + if Base.is_tag_next(Tags.REQUEST_MESSAGE, stream): + message = RequestMessage() + message.read(stream) + result = self._process_request(message) + tstream = BytearrayStream() + result.write(tstream) + ostream.write(tstream.buffer) + elif Base.is_tag_next(Tags.RESPONSE_MESSAGE, stream): + message = ResponseMessage() + message.read(stream) + self._process_response(message) + else: + raise ValueError('Processing error: stream contains unknown' + 'message type') + + def _process_request(self, message): + header = message.request_header + + protocol_version = header.protocol_version +# maximum_response_size = header.maximum_response_size + asynchronous_indicator = header.asynchronous_indicator +# authentication = header.authentication + batch_error_cont_option = header.batch_error_cont_option +# batch_order_option = header.batch_order_option +# time_stamp = header.time_stamp + request_batch_count = header.batch_count.value + + # TODO (peter-hamilton) Log receipt of message with time stamp + + if asynchronous_indicator is None: + asynchronous_indicator = AsynchronousIndicator(False) + + if batch_error_cont_option is None: + batch_error_cont_option = BatchErrorContinuationOption(BECO.STOP) + + request_batch_items = message.batch_items + response_batch_items = [] + + for i in xrange(request_batch_count): + request_batch_item = request_batch_items[i] + failure_occurred = False + + operation = request_batch_item.operation + ubi_id = request_batch_item.unique_batch_item_id + payload = request_batch_item.request_payload + message_extension = request_batch_item.message_extension + + result = self._process_operation(operation, payload) + + result_status = result[0] + result_reason = result[1] + result_message = result[2] + asyn_cv = None + response_payload = None + message_extension = None + + if result_status.enum is RS.SUCCESS: + response_payload = result[3] + elif result_status.enum is RS.OPERATION_FAILED: + failure_occurred = True + result_reason = result[1] + elif result_status.enum is RS.OPERATION_PENDING: + # TODO (peter-hamilton) Need to add a way to track async + # TODO (peter-hamilton) operations. + asyn_cv = '\x00' + elif result_status.enum is RS.OPERATION_UNDONE: + result_reason = result[1] + else: + msg = 'Unrecognized operation result status: {}' + raise RuntimeError(msg.format(result_status)) + + resp_bi = ResponseBatchItem(operation=operation, + unique_batch_item_id=ubi_id, + result_status=result_status, + result_reason=result_reason, + result_message=result_message, + async_correlation_value=asyn_cv, + response_payload=response_payload, + message_extension=message_extension) + response_batch_items.append(resp_bi) + + if failure_occurred: + if batch_error_cont_option.enum is BECO.STOP: + break + elif batch_error_cont_option.enum is BECO.UNDO: + # TODO (peter-hamilton) Tell client to undo operations. + # TODO (peter-hamilton) Unclear what response should be. + break + elif batch_error_cont_option.enum is BECO.CONTINUE: + continue + else: + msg = 'Unrecognized batch error continuation option: {}' + raise RuntimeError(msg.format(batch_error_cont_option)) + + response_batch_count = BatchCount(len(response_batch_items)) + response_time_stamp = TimeStamp(int(time.time())) + response_header = ResponseHeader(protocol_version=protocol_version, + time_stamp=response_time_stamp, + batch_count=response_batch_count) + + response_message = ResponseMessage(response_header=response_header, + batch_items=response_batch_items) + return response_message + + def _process_response(self, message): + raise NotImplementedError() + + def _process_operation(self, operation, payload): + op = operation.enum + + if op is Operation.CREATE: + return self._process_create_request(payload) + elif op is Operation.GET: + return self._process_get_request(payload) + elif op is Operation.DESTROY: + return self._process_destroy_request(payload) + elif op is Operation.REGISTER: + return self._process_register_request(payload) + else: + raise NotImplementedError() + + def _process_create_request(self, payload): + object_type = payload.object_type + template_attribute = payload.template_attribute + result = self._handler.create(object_type, template_attribute) + + result_status = result.result_status + result_reason = result.result_reason + result_message = result.result_message + created_type = result.object_type + uuid = result.uuid + template_attribute = result.template_attribute + + resp_pl = CreateResponsePayload(object_type=created_type, + unique_identifier=uuid, + template_attribute=template_attribute) + + return (result_status, result_reason, result_message, resp_pl) + + def _process_get_request(self, payload): + uuid = None + kft = None + kct = None + + unique_identifier = payload.unique_identifier + key_format_type = payload.key_format_type + key_compression_type = payload.key_compression_type + key_wrapping_specification = payload.key_wrapping_specification + + if unique_identifier is not None: + uuid = unique_identifier + if key_format_type is not None: + kft = key_format_type + if key_compression_type is not None: + kct = key_compression_type + + result = self._handler.get(uuid, kft, kct, + key_wrapping_specification) + + result_status = result.result_status + result_reason = result.result_reason + result_message = result.result_message + retrieved_type = result.object_type + uuid = result.uuid + secret = result.secret + + resp_pl = GetResponsePayload(object_type=retrieved_type, + unique_identifier=uuid, + secret=secret) + + return (result_status, result_reason, result_message, resp_pl) + + def _process_destroy_request(self, payload): + uuid = payload.unique_identifier + result = self._handler.destroy(uuid) + + result_status = result.result_status + result_reason = result.result_reason + result_message = result.result_message + uuid = result.uuid + + payload = DestroyResponsePayload(unique_identifier=uuid) + + return (result_status, result_reason, result_message, payload) + + def _process_register_request(self, payload): + object_type = payload.object_type + template_attribute = payload.template_attribute + secret = payload.secret + result = self._handler.register(object_type, template_attribute, + secret) + + result_status = result.result_status + result_reason = result.result_reason + result_message = result.result_message + uuid = result.uuid + template_attr = result.template_attribute + + resp_pl = RegisterResponsePayload(unique_identifier=uuid, + template_attribute=template_attr) + + return (result_status, result_reason, result_message, resp_pl) diff --git a/kmip/services/results.py b/kmip/services/results.py new file mode 100644 index 0000000..2ee6134 --- /dev/null +++ b/kmip/services/results.py @@ -0,0 +1,127 @@ +# Copyright (c) 2014 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. + + +class OperationResult(object): + + def __init__(self, + result_status, + result_reason=None, + result_message=None): + self.result_status = result_status + + if result_reason is not None: + self.result_reason = result_reason + else: + self.result_reason = None + + if result_message is not None: + self.result_message = result_message + else: + self.result_message = None + + +class CreateResult(OperationResult): + + def __init__(self, + result_status, + result_reason=None, + result_message=None, + object_type=None, + uuid=None, + template_attribute=None): + super(self.__class__, self).__init__(result_status, + result_reason, + result_message) + if object_type is not None: + self.object_type = object_type + else: + self.object_type = None + + if uuid is not None: + self.uuid = uuid + else: + self.uuid = None + + if template_attribute is not None: + self.template_attribute = template_attribute + else: + self.template_attribute = None + + +class RegisterResult(OperationResult): + + def __init__(self, + result_status, + result_reason=None, + result_message=None, + uuid=None, + template_attribute=None): + super(self.__class__, self).__init__(result_status, + result_reason, + result_message) + if uuid is not None: + self.uuid = uuid + else: + self.uuid = None + + if template_attribute is not None: + self.template_attribute = template_attribute + else: + self.template_attribute = None + + +class GetResult(OperationResult): + + def __init__(self, + result_status, + result_reason=None, + result_message=None, + object_type=None, + uuid=None, + secret=None): + super(self.__class__, self).__init__(result_status, + result_reason, + result_message) + if object_type is not None: + self.object_type = object_type + else: + self.object_type = None + + if uuid is not None: + self.uuid = uuid + else: + self.uuid = None + + if secret is not None: + self.secret = secret + else: + self.secret = None + + +class DestroyResult(OperationResult): + + def __init__(self, + result_status, + result_reason=None, + result_message=None, + uuid=None): + super(self.__class__, self).__init__(result_status, + result_reason, + result_message) + if uuid is not None: + self.uuid = uuid + else: + self.uuid = None diff --git a/kmip/tests/__init__.py b/kmip/tests/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/core/__init__.py b/kmip/tests/core/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/core/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/core/messages/__init__.py b/kmip/tests/core/messages/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/core/messages/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/core/messages/test_contents.py b/kmip/tests/core/messages/test_contents.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/core/messages/test_contents.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/core/messages/test_messages.py b/kmip/tests/core/messages/test_messages.py new file mode 100644 index 0000000..f2807b6 --- /dev/null +++ b/kmip/tests/core/messages/test_messages.py @@ -0,0 +1,1537 @@ +# Copyright (c) 2014 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.factories.keys import KeyFactory +from kmip.core.factories.secrets import SecretFactory +from kmip.core.factories.attributes import AttributeFactory + +from kmip.core import attributes as attr +from kmip.core.attributes import ApplicationSpecificInformation +from kmip.core.attributes import ContactInformation +from kmip.core.attributes import Name +from kmip.core.attributes import ObjectGroup + +from kmip.core import enums +from kmip.core.enums import AttributeType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask +from kmip.core.enums import NameType +from kmip.core.enums import ObjectType + +from kmip.core import errors +from kmip.core.errors import ErrorStrings + +from kmip.core import objects + +from kmip.core.keys import RawKey + +from kmip.core.messages import contents +from kmip.core.messages import messages +from kmip.core.messages import operations + +from kmip.core.messages.operations import DestroyRequestPayload +from kmip.core.messages.operations import RegisterRequestPayload +from kmip.core.messages.operations import DestroyResponsePayload +from kmip.core.messages.operations import RegisterResponsePayload + +from kmip.core.primitives import TextString + +from kmip.core.secrets import SymmetricKey +from kmip.core.secrets import Template + +from kmip.core import utils +from kmip.core.utils import BytearrayStream + + +class TestRequestMessage(TestCase): + + def setUp(self): + super(TestRequestMessage, self).setUp() + self.stream = BytearrayStream() + self.attribute_factory = AttributeFactory() + self.msg = errors.ErrorStrings.BAD_EXP_RECV + self.create = ( + '\x42\x00\x78\x01\x00\x00\x01\x20\x42\x00\x77\x01\x00\x00\x00\x38' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\xD8' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00' + '\x42\x00\x79\x01\x00\x00\x00\xC0\x42\x00\x57\x05\x00\x00\x00\x04' + '\x00\x00\x00\x02\x00\x00\x00\x00\x42\x00\x91\x01\x00\x00\x00\xA8' + '\x42\x00\x08\x01\x00\x00\x00\x30\x42\x00\x0A\x07\x00\x00\x00\x17' + '\x43\x72\x79\x70\x74\x6F\x67\x72\x61\x70\x68\x69\x63\x20\x41\x6C' + '\x67\x6F\x72\x69\x74\x68\x6D\x00\x42\x00\x0B\x05\x00\x00\x00\x04' + '\x00\x00\x00\x03\x00\x00\x00\x00\x42\x00\x08\x01\x00\x00\x00\x30' + '\x42\x00\x0A\x07\x00\x00\x00\x14\x43\x72\x79\x70\x74\x6F\x67\x72' + '\x61\x70\x68\x69\x63\x20\x4C\x65\x6E\x67\x74\x68\x00\x00\x00\x00' + '\x42\x00\x0B\x02\x00\x00\x00\x04\x00\x00\x00\x80\x00\x00\x00\x00' + '\x42\x00\x08\x01\x00\x00\x00\x30\x42\x00\x0A\x07\x00\x00\x00\x18' + '\x43\x72\x79\x70\x74\x6F\x67\x72\x61\x70\x68\x69\x63\x20\x55\x73' + '\x61\x67\x65\x20\x4D\x61\x73\x6B\x42\x00\x0B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x0C\x00\x00\x00\x00') + self.register = ( + '\x42\x00\x78\x01\x00\x00\x01\xC8\x42\x00\x77\x01\x00\x00\x00\x38' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x01\x80' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00' + '\x42\x00\x79\x01\x00\x00\x01\x68\x42\x00\x57\x05\x00\x00\x00\x04' + '\x00\x00\x00\x06\x00\x00\x00\x00\x42\x00\x91\x01\x00\x00\x00\x00' + '\x42\x00\x90\x01\x00\x00\x01\x48\x42\x00\x08\x01\x00\x00\x00\x28' + '\x42\x00\x0A\x07\x00\x00\x00\x0C\x4F\x62\x6A\x65\x63\x74\x20\x47' + '\x72\x6F\x75\x70\x00\x00\x00\x00\x42\x00\x0B\x07\x00\x00\x00\x06' + '\x47\x72\x6F\x75\x70\x31\x00\x00\x42\x00\x08\x01\x00\x00\x00\x58' + '\x42\x00\x0A\x07\x00\x00\x00\x20\x41\x70\x70\x6C\x69\x63\x61\x74' + '\x69\x6F\x6E\x20\x53\x70\x65\x63\x69\x66\x69\x63\x20\x49\x6E\x66' + '\x6F\x72\x6D\x61\x74\x69\x6F\x6E\x42\x00\x0B\x01\x00\x00\x00\x28' + '\x42\x00\x03\x07\x00\x00\x00\x03\x73\x73\x6C\x00\x00\x00\x00\x00' + '\x42\x00\x02\x07\x00\x00\x00\x0F\x77\x77\x77\x2E\x65\x78\x61\x6D' + '\x70\x6C\x65\x2E\x63\x6F\x6D\x00\x42\x00\x08\x01\x00\x00\x00\x30' + '\x42\x00\x0A\x07\x00\x00\x00\x13\x43\x6F\x6E\x74\x61\x63\x74\x20' + '\x49\x6E\x66\x6F\x72\x6D\x61\x74\x69\x6F\x6E\x00\x00\x00\x00\x00' + '\x42\x00\x0B\x07\x00\x00\x00\x03\x4A\x6F\x65\x00\x00\x00\x00\x00' + '\x42\x00\x08\x01\x00\x00\x00\x30\x42\x00\x0A\x07\x00\x00\x00\x09' + '\x78\x2D\x50\x75\x72\x70\x6F\x73\x65\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x0B\x07\x00\x00\x00\x0D\x64\x65\x6D\x6F\x6E\x73\x74\x72' + '\x61\x74\x69\x6F\x6E\x00\x00\x00\x42\x00\x08\x01\x00\x00\x00\x40' + '\x42\x00\x0A\x07\x00\x00\x00\x04\x4E\x61\x6D\x65\x00\x00\x00\x00' + '\x42\x00\x0B\x01\x00\x00\x00\x28\x42\x00\x55\x07\x00\x00\x00\x09' + '\x54\x65\x6D\x70\x6C\x61\x74\x65\x31\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x54\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00') + self.get = ( + '\x42\x00\x78\x01\x00\x00\x00\x90\x42\x00\x77\x01\x00\x00\x00\x38' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\x48' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x0A\x00\x00\x00\x00' + '\x42\x00\x79\x01\x00\x00\x00\x30\x42\x00\x94\x07\x00\x00\x00\x24' + '\x34\x39\x61\x31\x63\x61\x38\x38\x2D\x36\x62\x65\x61\x2D\x34\x66' + '\x62\x32\x2D\x62\x34\x35\x30\x2D\x37\x65\x35\x38\x38\x30\x32\x63' + '\x33\x30\x33\x38\x00\x00\x00\x00') + self.destroy = ( + '\x42\x00\x78\x01\x00\x00\x00\x90\x42\x00\x77\x01\x00\x00\x00\x38' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\x48' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x14\x00\x00\x00\x00' + '\x42\x00\x79\x01\x00\x00\x00\x30\x42\x00\x94\x07\x00\x00\x00\x24' + '\x66\x62\x34\x62\x35\x62\x39\x63\x2D\x36\x31\x38\x38\x2D\x34\x63' + '\x36\x33\x2D\x38\x31\x34\x32\x2D\x66\x65\x39\x63\x33\x32\x38\x31' + '\x32\x39\x66\x63\x00\x00\x00\x00') + + def tearDown(self): + super(TestRequestMessage, self).tearDown() + + def test_create_request_read(self): + self.stream = BytearrayStream(self.create) + + request_message = messages.RequestMessage() + request_message.read(self.stream) + + request_header = request_message.request_header + msg = "Bad request header type: expected {0}, received{1}" + self.assertIsInstance(request_header, messages.RequestHeader, + msg.format(messages.RequestHeader, + type(request_header))) + + protocol_version = request_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + batch_count = request_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = request_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + batch_item = batch_items[0] + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.RequestBatchItem, + msg.format(messages.RequestBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + self.assertEqual(enums.Operation.CREATE, operation.enum, + msg.format(enums.Operation.CREATE, + operation.enum)) + + request_payload = batch_item.request_payload + msg = "Bad request payload type: expected {0}, received {1}" + self.assertIsInstance(request_payload, + operations.CreateRequestPayload, + msg.format(operations.CreateRequestPayload, + type(request_payload))) + + object_type = request_payload.object_type + msg = "Bad object type type: expected {0}, received {1}" + self.assertIsInstance(object_type, attr.ObjectType, + msg.format(attr.ObjectType, + type(object_type))) + msg = "Bad object type value: expected {0}, received {1}" + self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type.enum, + msg.format(enums.ObjectType.SYMMETRIC_KEY, + object_type.enum)) + + template_attribute = request_payload.template_attribute + msg = "Bad template attribute type: expected {0}, received {1}" + self.assertIsInstance(template_attribute, + objects.TemplateAttribute, + msg.format(objects.TemplateAttribute, + type(template_attribute))) + + attributes = template_attribute.attributes + self.assertIsInstance(attributes, list, + self.msg.format('attributes', 'type', + list, type(attributes))) + self.assertEquals(3, len(attributes), + self.msg.format('attributes', 'length', + 3, len(attributes))) + + attribute_a = attributes[0] + self.assertIsInstance(attribute_a, objects.Attribute, + self.msg.format('attribute', 'type', + objects.Attribute, + type(attribute_a))) + + attribute_name = attribute_a.attribute_name + self.assertIsInstance(attribute_name, objects.Attribute.AttributeName, + self.msg.format('attribute name', 'type', + objects.Attribute.AttributeName, + type(attribute_name))) + self.assertEquals('Cryptographic Algorithm', attribute_name.value, + self.msg.format('attribute name', 'value', + 'Cryptographic Algorithm', + attribute_name.value)) + + attribute_value = attribute_a.attribute_value + exp_type = attr.CryptographicAlgorithm + rcv_type = type(attribute_value) + self.assertIsInstance(attribute_value, exp_type, + self.msg.format('attribute value', 'type', + exp_type, rcv_type)) + self.assertEquals(attribute_value.enum, + enums.CryptographicAlgorithm.AES, + self.msg.format('cryptographic algorithm', 'value', + enums.CryptographicAlgorithm.AES, + attribute_value.enum)) + + attribute_b = attributes[1] + self.assertIsInstance(attribute_b, objects.Attribute, + self.msg.format('attribute', 'type', + objects.Attribute, + type(attribute_b))) + + attribute_name = attribute_b.attribute_name + self.assertIsInstance(attribute_name, objects.Attribute.AttributeName, + self.msg.format('attribute name', 'type', + objects.Attribute.AttributeName, + type(attribute_name))) + self.assertEquals('Cryptographic Length', attribute_name.value, + self.msg.format('attribute name', 'value', + 'Cryptographic Length', + attribute_name.value)) + + attribute_value = attribute_b.attribute_value + exp_type = attr.CryptographicLength + rcv_type = type(attribute_value) + self.assertIsInstance(attribute_value, exp_type, + self.msg.format('attribute value', 'type', + exp_type, rcv_type)) + self.assertEquals(attribute_value.value, 128, + self.msg.format('cryptographic length', 'value', + 128, attribute_value.value)) + + attribute_c = attributes[2] + self.assertIsInstance(attribute_c, objects.Attribute, + self.msg.format('attribute', 'type', + objects.Attribute, + type(attribute_b))) + + attribute_name = attribute_c.attribute_name + self.assertIsInstance(attribute_name, objects.Attribute.AttributeName, + self.msg.format('attribute name', 'type', + objects.Attribute.AttributeName, + type(attribute_name))) + self.assertEquals('Cryptographic Usage Mask', attribute_name.value, + self.msg.format('attribute name', 'value', + 'Cryptographic Usage Mask', + attribute_name.value)) + + attribute_value = attribute_c.attribute_value + exp_type = attr.CryptographicUsageMask + rcv_type = type(attribute_value) + self.assertIsInstance(attribute_value, exp_type, + self.msg.format('attribute value', 'type', + exp_type, rcv_type)) + flag_encrypt = CryptographicUsageMask.ENCRYPT + flag_decrypt = CryptographicUsageMask.DECRYPT + exp_value = flag_encrypt.value | flag_decrypt.value + self.assertEquals(attribute_value.value, exp_value, + self.msg.format('cryptographic usage mask', 'value', + exp_value, attribute_value.value)) + + def test_create_request_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + batch_count = contents.BatchCount(1) + request_header = messages.RequestHeader(protocol_version=prot_ver, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.CREATE) + object_type = attr.ObjectType(enums.ObjectType.SYMMETRIC_KEY) + + name = AttributeType.CRYPTOGRAPHIC_ALGORITHM + value = CryptographicAlgorithm.AES + attr_a = self.attribute_factory.create_attribute(name, value) + + name = AttributeType.CRYPTOGRAPHIC_LENGTH + value = 128 + attr_b = self.attribute_factory.create_attribute(name, value) + + name = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + value = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attr_c = self.attribute_factory.create_attribute(name, value) + + temp_attr = objects.TemplateAttribute(attributes=[attr_a, attr_b, + attr_c]) + req_pl = operations.CreateRequestPayload(object_type=object_type, + template_attribute=temp_attr) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=req_pl) + req_message = messages.RequestMessage(request_header=request_header, + batch_items=[batch_item]) + req_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.create) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('request message', 'write', + len_exp, len_rcv)) + + msg = "Bad request message write: encoding mismatch" + self.assertEqual(self.create, result, msg) + + def test_get_request_read(self): + self.stream = BytearrayStream(self.get) + + request_message = messages.RequestMessage() + request_message.read(self.stream) + + request_header = request_message.request_header + msg = "Bad request header type: expected {0}, received{0}" + self.assertIsInstance(request_header, messages.RequestHeader, + msg.format(messages.RequestHeader, + type(request_header))) + + protocol_version = request_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + batch_count = request_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = request_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + batch_item = batch_items[0] + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.RequestBatchItem, + msg.format(messages.RequestBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + self.assertEqual(enums.Operation.GET, operation.enum, + msg.format(enums.Operation.GET, + operation.enum)) + + request_payload = batch_item.request_payload + msg = "Bad request payload type: expected {0}, received {1}" + self.assertIsInstance(request_payload, + operations.GetRequestPayload, + msg.format(operations.GetRequestPayload, + type(request_payload))) + + unique_identifier = request_payload.unique_identifier + msg = "Bad unique identifier type: expected {0}, received {1}" + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + msg.format(attr.UniqueIdentifier, + type(unique_identifier))) + msg = "Bad unique identifier value: expected {0}, received {1}" + self.assertEqual('49a1ca88-6bea-4fb2-b450-7e58802c3038', + unique_identifier.value, + msg.format('49a1ca88-6bea-4fb2-b450-7e58802c3038', + unique_identifier.value)) + + def test_get_request_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + batch_count = contents.BatchCount(1) + req_header = messages.RequestHeader(protocol_version=prot_ver, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.GET) + + uuid = attr.UniqueIdentifier('49a1ca88-6bea-4fb2-b450-7e58802c3038') + request_payload = operations.GetRequestPayload(unique_identifier=uuid) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=request_payload) + request_message = messages.RequestMessage(request_header=req_header, + batch_items=[batch_item]) + request_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.get) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('request message', 'write', + len_exp, len_rcv)) + + msg = "Bad request message write: encoding mismatch" + self.assertEqual(self.get, result, msg) + + def test_destroy_request_read(self): + self.stream = BytearrayStream(self.destroy) + + request_message = messages.RequestMessage() + request_message.read(self.stream) + + request_header = request_message.request_header + msg = "Bad request header type: expected {0}, received{0}" + self.assertIsInstance(request_header, messages.RequestHeader, + msg.format(messages.RequestHeader, + type(request_header))) + + protocol_version = request_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + batch_count = request_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = request_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + batch_item = batch_items[0] + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.RequestBatchItem, + msg.format(messages.RequestBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + exp_value = enums.Operation.DESTROY + rcv_value = operation.enum + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + request_payload = batch_item.request_payload + msg = "Bad request payload type: expected {0}, received {1}" + exp_type = operations.DestroyRequestPayload + rcv_type = type(request_payload) + self.assertIsInstance(request_payload, exp_type, + msg.format(exp_type, rcv_type)) + + unique_identifier = request_payload.unique_identifier + msg = "Bad unique identifier type: expected {0}, received {1}" + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + msg.format(attr.UniqueIdentifier, + type(unique_identifier))) + msg = "Bad unique identifier value: expected {0}, received {1}" + exp_value = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + rcv_value = unique_identifier.value + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + def test_destroy_request_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + batch_count = contents.BatchCount(1) + req_header = messages.RequestHeader(protocol_version=prot_ver, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.DESTROY) + + uuid = attr.UniqueIdentifier('fb4b5b9c-6188-4c63-8142-fe9c328129fc') + request_payload = DestroyRequestPayload(unique_identifier=uuid) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=request_payload) + request_message = messages.RequestMessage(request_header=req_header, + batch_items=[batch_item]) + request_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.destroy) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('request message', 'write', + len_exp, len_rcv)) + + msg = "Bad request message write: encoding mismatch" + self.assertEqual(self.destroy, result, msg) + + def test_register_request_read(self): + self.stream = BytearrayStream(self.register) + + request_message = messages.RequestMessage() + request_message.read(self.stream) + + request_header = request_message.request_header + msg = "Bad request header type: expected {0}, received{0}" + self.assertIsInstance(request_header, messages.RequestHeader, + msg.format(messages.RequestHeader, + type(request_header))) + + protocol_version = request_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + batch_count = request_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = request_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + for batch_item in batch_items: + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.RequestBatchItem, + msg.format(messages.RequestBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + exp_value = enums.Operation.REGISTER + rcv_value = operation.enum + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + request_payload = batch_item.request_payload + msg = "Bad request payload type: expected {0}, received {1}" + exp_type = operations.RegisterRequestPayload + rcv_type = type(request_payload) + self.assertIsInstance(request_payload, exp_type, + msg.format(exp_type, rcv_type)) + + object_type = request_payload.object_type + msg = "Bad object type type: expected {0}, received {1}" + self.assertIsInstance(object_type, attr.ObjectType, + msg.format(attr.ObjectType, + type(object_type))) + msg = "Bad object type value: expected {0}, received {1}" + exp_value = enums.ObjectType.TEMPLATE + rcv_value = object_type.enum + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + template_attribute = request_payload.template_attribute + msg = "Bad template attribute type: expected {0}, received {1}" + exp_type = objects.TemplateAttribute + rcv_type = type(template_attribute) + self.assertIsInstance(template_attribute, exp_type, + msg.format(exp_type, rcv_type)) + + names = template_attribute.names + exp_type = list + rcv_type = type(names) + msg = ErrorStrings.BAD_EXP_RECV.format('TemplateAttribute.names', + 'type', '{0}', '{0}') + self.assertIsInstance(names, exp_type, + msg.format(exp_type, rcv_type)) + exp_length = 0 + rcv_length = len(names) + msg = ErrorStrings.BAD_EXP_RECV.format('TemplateAttribute.names', + 'length', '{0}', '{0}') + self.assertEqual(exp_length, rcv_length, + msg.format(exp_length, rcv_length)) + + attributes = template_attribute.attributes + exp_type = list + rcv_type = type(attributes) + msg = ErrorStrings.BAD_EXP_RECV.format( + 'TemplateAttribute.attributes', 'type', '{0}', '{1}') + self.assertIsInstance(names, exp_type, + msg.format(exp_type, rcv_type)) + exp_length = 0 + rcv_length = len(attributes) + msg = ErrorStrings.BAD_EXP_RECV.format( + 'TemplateAttribute.attributes', 'length', '{0}', '{1}') + self.assertEqual(exp_length, rcv_length, + msg.format(exp_length, rcv_length)) + + def test_register_request_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + batch_count = contents.BatchCount(1) + req_header = messages.RequestHeader(protocol_version=prot_ver, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.REGISTER) + + object_type = attr.ObjectType(enums.ObjectType.TEMPLATE) + tmpl_attr = objects.TemplateAttribute() + + attributes = [] + + name = objects.Attribute.AttributeName('Object Group') + value = ObjectGroup('Group1') + attribute = objects.Attribute(attribute_name=name, + attribute_value=value) + attributes.append(attribute) + + name = objects.Attribute.AttributeName('Application Specific ' + 'Information') + ap_n_name = 'ssl' + ap_n_value = 'www.example.com' + ap_n = ApplicationSpecificInformation.ApplicationNamespace(ap_n_name) + ap_d = ApplicationSpecificInformation.ApplicationData(ap_n_value) + value = ApplicationSpecificInformation(application_namespace=ap_n, + application_data=ap_d) + attribute = objects.Attribute(attribute_name=name, + attribute_value=value) + attributes.append(attribute) + + name = objects.Attribute.AttributeName('Contact Information') + value = ContactInformation('Joe') + attribute = objects.Attribute(attribute_name=name, + attribute_value=value) + attributes.append(attribute) + + name = objects.Attribute.AttributeName('x-Purpose') + value = TextString('demonstration') + attribute = objects.Attribute(attribute_name=name, + attribute_value=value) + attributes.append(attribute) + + name = objects.Attribute.AttributeName('Name') + name_value = Name.NameValue('Template1') + name_type = Name.NameType(NameType.UNINTERPRETED_TEXT_STRING) + value = Name(name_value=name_value, + name_type=name_type) + attribute = objects.Attribute(attribute_name=name, + attribute_value=value) + attributes.append(attribute) + + template = Template(attributes=attributes) + + request_payload = RegisterRequestPayload(object_type=object_type, + template_attribute=tmpl_attr, + secret=template) + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=request_payload) + request_message = messages.RequestMessage(request_header=req_header, + batch_items=[batch_item]) + request_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.register) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('request message', 'write', + len_exp, len_rcv)) + + msg = "Bad request message write: encoding mismatch" + self.assertEqual(self.register, result, msg) + + +class TestResponseMessage(TestCase): + + def setUp(self): + super(TestResponseMessage, self).setUp() + self.stream = BytearrayStream() + self.key_factory = KeyFactory() + self.secret_factory = SecretFactory() + self.msg = errors.ErrorStrings.BAD_EXP_RECV + self.create = ( + '\x42\x00\x7B\x01\x00\x00\x00\xC0\x42\x00\x7A\x01\x00\x00\x00\x48' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x92\x09\x00\x00\x00\x08' + '\x00\x00\x00\x00\x4F\x9A\x54\xE5\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\x68' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00' + '\x42\x00\x7F\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x7C\x01\x00\x00\x00\x40\x42\x00\x57\x05\x00\x00\x00\x04' + '\x00\x00\x00\x02\x00\x00\x00\x00\x42\x00\x94\x07\x00\x00\x00\x24' + '\x66\x62\x34\x62\x35\x62\x39\x63\x2D\x36\x31\x38\x38\x2D\x34\x63' + '\x36\x33\x2D\x38\x31\x34\x32\x2D\x66\x65\x39\x63\x33\x32\x38\x31' + '\x32\x39\x66\x63\x00\x00\x00\x00') + self.register = ( + '\x42\x00\x7B\x01\x00\x00\x00\xB0\x42\x00\x7A\x01\x00\x00\x00\x48' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x92\x09\x00\x00\x00\x08' + '\x00\x00\x00\x00\x4F\x9A\x54\xE5\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\x58' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00' + '\x42\x00\x7F\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x7C\x01\x00\x00\x00\x30\x42\x00\x94\x07\x00\x00\x00\x24' + '\x35\x63\x39\x62\x38\x31\x65\x66\x2D\x34\x65\x65\x35\x2D\x34\x32' + '\x63\x64\x2D\x62\x61\x32\x64\x2D\x63\x30\x30\x32\x66\x64\x64\x30' + '\x63\x37\x62\x33\x00\x00\x00\x00') + self.get = ( + '\x42\x00\x7B\x01\x00\x00\x01\x28\x42\x00\x7A\x01\x00\x00\x00\x48' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x92\x09\x00\x00\x00\x08' + '\x00\x00\x00\x00\x4F\x9A\x54\xE7\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\xD0' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x0A\x00\x00\x00\x00' + '\x42\x00\x7F\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x7C\x01\x00\x00\x00\xA8\x42\x00\x57\x05\x00\x00\x00\x04' + '\x00\x00\x00\x02\x00\x00\x00\x00\x42\x00\x94\x07\x00\x00\x00\x24' + '\x34\x39\x61\x31\x63\x61\x38\x38\x2D\x36\x62\x65\x61\x2D\x34\x66' + '\x62\x32\x2D\x62\x34\x35\x30\x2D\x37\x65\x35\x38\x38\x30\x32\x63' + '\x33\x30\x33\x38\x00\x00\x00\x00\x42\x00\x8F\x01\x00\x00\x00\x60' + '\x42\x00\x40\x01\x00\x00\x00\x58\x42\x00\x42\x05\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x45\x01\x00\x00\x00\x20' + '\x42\x00\x43\x08\x00\x00\x00\x18\x73\x67\x57\x80\x51\x01\x2A\x6D' + '\x13\x4A\x85\x5E\x25\xC8\xCD\x5E\x4C\xA1\x31\x45\x57\x29\xD3\xC8' + '\x42\x00\x28\x05\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00' + '\x42\x00\x2A\x02\x00\x00\x00\x04\x00\x00\x00\xA8\x00\x00\x00\x00') + self.destroy = ( + '\x42\x00\x7B\x01\x00\x00\x00\xB0\x42\x00\x7A\x01\x00\x00\x00\x48' + '\x42\x00\x69\x01\x00\x00\x00\x20\x42\x00\x6A\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x6B\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x92\x09\x00\x00\x00\x08' + '\x00\x00\x00\x00\x4F\x9A\x54\xE5\x42\x00\x0D\x02\x00\x00\x00\x04' + '\x00\x00\x00\x01\x00\x00\x00\x00\x42\x00\x0F\x01\x00\x00\x00\x58' + '\x42\x00\x5C\x05\x00\x00\x00\x04\x00\x00\x00\x14\x00\x00\x00\x00' + '\x42\x00\x7F\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00' + '\x42\x00\x7C\x01\x00\x00\x00\x30\x42\x00\x94\x07\x00\x00\x00\x24' + '\x66\x62\x34\x62\x35\x62\x39\x63\x2D\x36\x31\x38\x38\x2D\x34\x63' + '\x36\x33\x2D\x38\x31\x34\x32\x2D\x66\x65\x39\x63\x33\x32\x38\x31' + '\x32\x39\x66\x63\x00\x00\x00\x00') + + def tearDown(self): + super(TestResponseMessage, self).tearDown() + + def test_create_response_read(self): + self.stream = BytearrayStream(str(self.create)) + + response_message = messages.ResponseMessage() + response_message.read(self.stream) + + response_header = response_message.response_header + self.assertIsInstance(response_header, messages.ResponseHeader, + self.msg.format('response header', 'type', + messages.ResponseHeader, + type(response_header))) + protocol_version = response_header.protocol_version + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + self.msg.format('response header', 'value', + contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + self.msg.format('protocol version major', + 'type', exp_type, rcv_type)) + self.assertEqual(1, protocol_version_major.value, + self.msg.format('protocol version major', 'value', + 1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, + contents.ProtocolVersion.ProtocolVersionMinor, + self.msg.format('protocol version minor', + 'type', exp_type, rcv_type)) + self.assertEqual(1, protocol_version_minor.value, + self.msg.format('protocol version minor', 'value', + 1, protocol_version_minor.value)) + + time_stamp = response_header.time_stamp + value = 0x4f9a54e5 # Fri Apr 27 10:12:21 CEST 2012 + self.assertIsInstance(time_stamp, contents.TimeStamp, + self.msg.format('time stamp', 'value', + contents.TimeStamp, + type(time_stamp))) + self.assertEqual(time_stamp.value, value, + self.msg.format('time stamp', 'value', + time_stamp.value, value)) + + batch_count = response_header.batch_count + self.assertIsInstance(batch_count, contents.BatchCount, + self.msg.format('batch count', 'type', + contents.BatchCount, + type(batch_count))) + self.assertEqual(1, batch_count.value, + self.msg.format('batch count', 'value', 1, + batch_count.value)) + + batch_items = response_message.batch_items + self.assertIsInstance(batch_items, list, + self.msg.format('batch items', 'type', + list, type(batch_items))) + + for batch_item in batch_items: + self.assertIsInstance(batch_item, messages.ResponseBatchItem, + self.msg.format('batch item', 'type', + messages.ResponseBatchItem, + type(batch_item))) + + operation = batch_item.operation + self.assertIsInstance(operation, contents.Operation, + self.msg.format('operation', 'type', + contents.Operation, + type(operation))) + self.assertEqual(enums.Operation.CREATE, operation.enum, + self.msg.format('operation', 'value', + enums.Operation.CREATE, + operation.enum)) + + result_status = batch_item.result_status + self.assertIsInstance(result_status, contents.ResultStatus, + self.msg.format('result status', 'type', + contents.ResultStatus, + type(result_status))) + self.assertEqual(enums.ResultStatus.SUCCESS, result_status.enum, + self.msg.format('result status', 'value', + enums.ResultStatus.SUCCESS, + result_status.enum)) + + response_payload = batch_item.response_payload + exp_type = operations.CreateResponsePayload + rcv_type = type(response_payload) + self.assertIsInstance(response_payload, exp_type, + self.msg.format('response payload', 'type', + exp_type, rcv_type)) + + object_type = response_payload.object_type + self.assertIsInstance(object_type, attr.ObjectType, + self.msg.format('object type', 'type', + attr.ObjectType, + type(object_type))) + self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type.enum, + self.msg.format('object type', 'value', + enums.ObjectType.SYMMETRIC_KEY, + object_type.enum)) + + unique_identifier = response_payload.unique_identifier + value = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + self.msg.format('unique identifier', 'type', + attr.UniqueIdentifier, + type(unique_identifier))) + self.assertEqual(value, unique_identifier.value, + self.msg.format('unique identifier', 'value', + unique_identifier.value, value)) + + def test_create_response_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + # Fri Apr 27 10:12:21 CEST 2012 + time_stamp = contents.TimeStamp(0x4f9a54e5) + + batch_count = contents.BatchCount(1) + response_header = messages.ResponseHeader(protocol_version=prot_ver, + time_stamp=time_stamp, + batch_count=batch_count) + operation = contents.Operation(enums.Operation.CREATE) + result_status = contents.ResultStatus(enums.ResultStatus.SUCCESS) + object_type = attr.ObjectType(enums.ObjectType.SYMMETRIC_KEY) + + uuid = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + uniq_id = attr.UniqueIdentifier(uuid) + resp_pl = operations.CreateResponsePayload(object_type=object_type, + unique_identifier=uniq_id) + batch_item = messages.ResponseBatchItem(operation=operation, + result_status=result_status, + response_payload=resp_pl) + rm = messages.ResponseMessage(response_header=response_header, + batch_items=[batch_item]) + rm.write(self.stream) + + result = self.stream.read() + len_exp = len(self.create) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('response message', 'write', len_exp, + len_rcv)) + + msg = "Bad response message write: encoding mismatch" + self.assertEqual(self.create, result, msg) + + def test_get_response_read(self): + self.stream = BytearrayStream(str(self.get)) + + response_message = messages.ResponseMessage() + response_message.read(self.stream) + + response_header = response_message.response_header + self.assertIsInstance(response_header, messages.ResponseHeader, + self.msg.format('response header', 'type', + messages.ResponseHeader, + type(response_header))) + protocol_version = response_header.protocol_version + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + self.msg.format('response header', 'value', + contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + self.msg.format('protocol version major', 'type', + exp_type, rcv_type)) + self.assertEqual(1, protocol_version_major.value, + self.msg.format('protocol version major', 'value', + 1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + self.msg.format('protocol version minor', 'type', + exp_type, rcv_type)) + self.assertEqual(1, protocol_version_minor.value, + self.msg.format('protocol version minor', 'value', + 1, protocol_version_minor.value)) + + time_stamp = response_header.time_stamp + value = 0x4f9a54e7 # Fri Apr 27 10:12:23 CEST 2012 + self.assertIsInstance(time_stamp, contents.TimeStamp, + self.msg.format('time stamp', 'value', + contents.TimeStamp, + type(time_stamp))) + self.assertEqual(time_stamp.value, value, + self.msg.format('time stamp', 'value', + time_stamp.value, value)) + + batch_count = response_header.batch_count + self.assertIsInstance(batch_count, contents.BatchCount, + self.msg.format('batch count', 'type', + contents.BatchCount, + type(batch_count))) + self.assertEqual(1, batch_count.value, + self.msg.format('batch count', 'value', 1, + batch_count.value)) + + batch_items = response_message.batch_items + self.assertIsInstance(batch_items, list, + self.msg.format('batch items', 'type', + list, type(batch_items))) + + for batch_item in batch_items: + self.assertIsInstance(batch_item, messages.ResponseBatchItem, + self.msg.format('batch item', 'type', + messages.ResponseBatchItem, + type(batch_item))) + + operation = batch_item.operation + self.assertIsInstance(operation, contents.Operation, + self.msg.format('operation', 'type', + contents.Operation, + type(operation))) + self.assertEqual(enums.Operation.GET, operation.enum, + self.msg.format('operation', 'value', + enums.Operation.GET, + operation.enum)) + + result_status = batch_item.result_status + self.assertIsInstance(result_status, contents.ResultStatus, + self.msg.format('result status', 'type', + contents.ResultStatus, + type(result_status))) + self.assertEqual(enums.ResultStatus.SUCCESS, result_status.enum, + self.msg.format('result status', 'value', + enums.ResultStatus.SUCCESS, + result_status.enum)) + + response_payload = batch_item.response_payload + exp_type = operations.GetResponsePayload + rcv_type = type(response_payload) + self.assertIsInstance(response_payload, exp_type, + self.msg.format('response payload', 'type', + exp_type, rcv_type)) + + object_type = response_payload.object_type + self.assertIsInstance(object_type, attr.ObjectType, + self.msg.format('object type', 'type', + attr.ObjectType, + type(object_type))) + self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type.enum, + self.msg.format('object type', 'value', + enums.ObjectType.SYMMETRIC_KEY, + object_type.enum)) + + unique_identifier = response_payload.unique_identifier + value = '49a1ca88-6bea-4fb2-b450-7e58802c3038' + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + self.msg.format('unique identifier', 'type', + attr.UniqueIdentifier, + type(unique_identifier))) + self.assertEqual(value, unique_identifier.value, + self.msg.format('unique identifier', 'value', + unique_identifier.value, value)) + + secret = response_payload.secret + self.assertIsInstance(secret, SymmetricKey, + self.msg.format('secret', 'type', + SymmetricKey, type(secret))) + + key_block = secret.key_block + self.assertIsInstance(key_block, objects.KeyBlock, + self.msg.format('key_block', 'type', + objects.KeyBlock, + type(key_block))) + + key_format_type = key_block.key_format_type + exp_type = objects.KeyBlock.KeyFormatType + rcv_type = type(key_format_type) + self.assertIsInstance(key_format_type, exp_type, + self.msg.format('key_format_type', 'type', + exp_type, rcv_type)) + + key_value = key_block.key_value + self.assertIsInstance(key_value, objects.KeyValue, + self.msg.format('key_value', 'type', + objects.KeyValue, + type(key_value))) + + key_material = key_value.key_value.key_material + value = bytearray('\x73\x67\x57\x80\x51\x01\x2A\x6D\x13\x4A\x85' + '\x5E\x25\xC8\xCD\x5E\x4C\xA1\x31\x45\x57\x29' + '\xD3\xC8') + self.assertIsInstance(key_material, RawKey, + self.msg.format('key_material', 'type', + RawKey, + type(key_material))) + exp = utils.hexlify_bytearray(value) + obs = utils.hexlify_bytearray(key_material.value) + self.assertEqual(exp, obs, self.msg.format('key_material', 'value', + exp, obs)) + + cryptographic_algorithm = key_block.cryptographic_algorithm + exp_type = attr.CryptographicAlgorithm + rcv_type = type(cryptographic_algorithm) + self.assertIsInstance(cryptographic_algorithm, exp_type, + self.msg.format('cryptographic_algorithm', + 'type', exp_type, rcv_type)) + exp = enums.CryptographicAlgorithm.TRIPLE_DES + obs = cryptographic_algorithm.enum + self.assertEqual(exp, obs, + self.msg.format('cryptographic_algorithm', + 'value', exp, obs)) + + cryptographic_length = key_block.cryptographic_length + self.assertIsInstance(cryptographic_length, + attr.CryptographicLength, + self.msg.format('cryptographic_length', + 'type', + attr.CryptographicLength, + type(cryptographic_length))) + exp = 168 + obs = cryptographic_length.value + self.assertEqual(exp, obs, self.msg.format('cryptographic_length', + 'value', exp, obs)) + + def test_get_response_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + # Fri Apr 27 10:12:23 CEST 2012 + time_stamp = contents.TimeStamp(0x4f9a54e7) + + batch_count = contents.BatchCount(1) + response_header = messages.ResponseHeader(protocol_version=prot_ver, + time_stamp=time_stamp, + batch_count=batch_count) + operation = contents.Operation(enums.Operation.GET) + result_status = contents.ResultStatus(enums.ResultStatus.SUCCESS) + object_type = attr.ObjectType(enums.ObjectType.SYMMETRIC_KEY) + + uuid = '49a1ca88-6bea-4fb2-b450-7e58802c3038' + uniq_id = attr.UniqueIdentifier(uuid) + + key_type = enums.KeyFormatType.RAW + key = bytearray('\x73\x67\x57\x80\x51\x01\x2A\x6D\x13\x4A\x85\x5E\x25' + '\xC8\xCD\x5E\x4C\xA1\x31\x45\x57\x29\xD3\xC8') + + crypto_algorithm = enums.CryptographicAlgorithm.TRIPLE_DES + cryptographic_length = 168 + value = {'key_format_type': key_type, + 'key_value': {'bytes': key}, + 'cryptographic_algorithm': crypto_algorithm, + 'cryptographic_length': cryptographic_length} + secret = self.secret_factory.create_secret(ObjectType.SYMMETRIC_KEY, + value) + resp_pl = operations.GetResponsePayload(object_type=object_type, + unique_identifier=uniq_id, + secret=secret) + batch_item = messages.ResponseBatchItem(operation=operation, + result_status=result_status, + response_payload=resp_pl) + rm = messages.ResponseMessage(response_header=response_header, + batch_items=[batch_item]) + rm.write(self.stream) + + result = self.stream.read() + len_exp = len(self.get) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('get response message', 'write', + len_exp, len_rcv)) + + msg = "Bad get response message write: encoding mismatch" + print self.get + print result + self.assertEqual(self.get, result, msg) + + def test_destroy_response_read(self): + self.stream = BytearrayStream(self.destroy) + + response_message = messages.ResponseMessage() + response_message.read(self.stream) + + response_header = response_message.response_header + msg = "Bad response header type: expected {0}, received{1}" + self.assertIsInstance(response_header, messages.ResponseHeader, + msg.format(messages.ResponseHeader, + type(response_header))) + + protocol_version = response_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + time_stamp = response_header.time_stamp + value = 0x4f9a54e5 # Fri Apr 27 10:12:21 CEST 2012 + self.assertIsInstance(time_stamp, contents.TimeStamp, + self.msg.format('time stamp', 'value', + contents.TimeStamp, + type(time_stamp))) + self.assertEqual(time_stamp.value, value, + self.msg.format('time stamp', 'value', + time_stamp.value, value)) + + batch_count = response_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = response_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + for batch_item in batch_items: + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.ResponseBatchItem, + msg.format(messages.ResponseBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + exp_value = enums.Operation.DESTROY + rcv_value = operation.enum + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + result_status = batch_item.result_status + self.assertIsInstance(result_status, contents.ResultStatus, + self.msg.format('result status', 'type', + contents.ResultStatus, + type(result_status))) + self.assertEqual(enums.ResultStatus.SUCCESS, result_status.enum, + self.msg.format('result status', 'value', + enums.ResultStatus.SUCCESS, + result_status.enum)) + + response_payload = batch_item.response_payload + msg = "Bad response payload type: expected {0}, received {1}" + exp_type = operations.DestroyResponsePayload + rcv_type = type(response_payload) + self.assertIsInstance(response_payload, exp_type, + msg.format(exp_type, rcv_type)) + + unique_identifier = response_payload.unique_identifier + msg = "Bad unique identifier type: expected {0}, received {1}" + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + msg.format(attr.UniqueIdentifier, + type(unique_identifier))) + msg = "Bad unique identifier value: expected {0}, received {1}" + exp_value = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc' + rcv_value = unique_identifier.value + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + def test_destroy_response_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + # Fri Apr 27 10:12:21 CEST 2012 + time_stamp = contents.TimeStamp(0x4f9a54e5) + + batch_count = contents.BatchCount(1) + resp_hdr = messages.ResponseHeader(protocol_version=prot_ver, + time_stamp=time_stamp, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.DESTROY) + result_status = contents.ResultStatus(enums.ResultStatus.SUCCESS) + + uuid = attr.UniqueIdentifier('fb4b5b9c-6188-4c63-8142-fe9c328129fc') + resp_pl = DestroyResponsePayload(unique_identifier=uuid) + batch_item = messages.ResponseBatchItem(operation=operation, + result_status=result_status, + response_payload=resp_pl) + response_message = messages.ResponseMessage(response_header=resp_hdr, + batch_items=[batch_item]) + response_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.destroy) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('response message', 'write', + len_exp, len_rcv)) + + msg = "Bad response message write: encoding mismatch" + self.assertEqual(self.destroy, result, msg) + + def test_register_response_read(self): + self.stream = BytearrayStream(self.register) + + response_message = messages.ResponseMessage() + response_message.read(self.stream) + + response_header = response_message.response_header + msg = "Bad response header type: expected {0}, received{1}" + self.assertIsInstance(response_header, messages.ResponseHeader, + msg.format(messages.ResponseHeader, + type(response_header))) + + protocol_version = response_header.protocol_version + msg = "Bad protocol version type: expected {0}, received {1}" + self.assertIsInstance(protocol_version, contents.ProtocolVersion, + msg.format(contents.ProtocolVersion, + type(protocol_version))) + + protocol_version_major = protocol_version.protocol_version_major + msg = "Bad protocol version major type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMajor + rcv_type = type(protocol_version_major) + self.assertIsInstance(protocol_version_major, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version major value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_major.value, + msg.format(1, protocol_version_major.value)) + + protocol_version_minor = protocol_version.protocol_version_minor + msg = "Bad protocol version minor type: expected {0}, received {1}" + exp_type = contents.ProtocolVersion.ProtocolVersionMinor + rcv_type = type(protocol_version_minor) + self.assertIsInstance(protocol_version_minor, exp_type, + msg.format(exp_type, rcv_type)) + msg = "Bad protocol version minor value: expected {0}, received {1}" + self.assertEqual(1, protocol_version_minor.value, + msg.format(1, protocol_version_minor.value)) + + time_stamp = response_header.time_stamp + value = 0x4f9a54e5 # Fri Apr 27 10:12:21 CEST 2012 + self.assertIsInstance(time_stamp, contents.TimeStamp, + self.msg.format('time stamp', 'value', + contents.TimeStamp, + type(time_stamp))) + self.assertEqual(time_stamp.value, value, + self.msg.format('time stamp', 'value', + time_stamp.value, value)) + + batch_count = response_header.batch_count + msg = "Bad batch count type: expected {0}, received {1}" + self.assertIsInstance(batch_count, contents.BatchCount, + msg.format(contents.BatchCount, + type(batch_count))) + msg = "Bad batch count value: expected {0}, received {1}" + self.assertEqual(1, batch_count.value, + msg.format(1, batch_count.value)) + + batch_items = response_message.batch_items + msg = "Bad batch items type: expected {0}, received {1}" + self.assertIsInstance(batch_items, list, + msg.format(list, type(batch_items))) + self.assertEquals(1, len(batch_items), + self.msg.format('batch items', 'length', + 1, len(batch_items))) + + for batch_item in batch_items: + msg = "Bad batch item type: expected {0}, received {1}" + self.assertIsInstance(batch_item, messages.ResponseBatchItem, + msg.format(messages.ResponseBatchItem, + type(batch_item))) + + operation = batch_item.operation + msg = "Bad operation type: expected {0}, received {1}" + self.assertIsInstance(operation, contents.Operation, + msg.format(contents.Operation, + type(operation))) + msg = "Bad operation value: expected {0}, received {1}" + exp_value = enums.Operation.REGISTER + rcv_value = operation.enum + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + result_status = batch_item.result_status + self.assertIsInstance(result_status, contents.ResultStatus, + self.msg.format('result status', 'type', + contents.ResultStatus, + type(result_status))) + self.assertEqual(enums.ResultStatus.SUCCESS, result_status.enum, + self.msg.format('result status', 'value', + enums.ResultStatus.SUCCESS, + result_status.enum)) + + response_payload = batch_item.response_payload + msg = "Bad response payload type: expected {0}, received {1}" + exp_type = operations.RegisterResponsePayload + rcv_type = type(response_payload) + self.assertIsInstance(response_payload, exp_type, + msg.format(exp_type, rcv_type)) + + unique_identifier = response_payload.unique_identifier + msg = "Bad unique identifier type: expected {0}, received {1}" + self.assertIsInstance(unique_identifier, attr.UniqueIdentifier, + msg.format(attr.UniqueIdentifier, + type(unique_identifier))) + msg = "Bad unique identifier value: expected {0}, received {1}" + exp_value = '5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3' + rcv_value = unique_identifier.value + self.assertEqual(exp_value, rcv_value, + msg.format(exp_value, rcv_value)) + + def test_register_response_write(self): + prot_ver = contents.ProtocolVersion.create(1, 1) + + # Fri Apr 27 10:12:21 CEST 2012 + time_stamp = contents.TimeStamp(0x4f9a54e5) + + batch_count = contents.BatchCount(1) + resp_hdr = messages.ResponseHeader(protocol_version=prot_ver, + time_stamp=time_stamp, + batch_count=batch_count) + + operation = contents.Operation(enums.Operation.REGISTER) + result_status = contents.ResultStatus(enums.ResultStatus.SUCCESS) + + uuid = attr.UniqueIdentifier('5c9b81ef-4ee5-42cd-ba2d-c002fdd0c7b3') + resp_pl = RegisterResponsePayload(unique_identifier=uuid) + batch_item = messages.ResponseBatchItem(operation=operation, + result_status=result_status, + response_payload=resp_pl) + response_message = messages.ResponseMessage(response_header=resp_hdr, + batch_items=[batch_item]) + response_message.write(self.stream) + + result = self.stream.read() + len_exp = len(self.register) + len_rcv = len(result) + self.assertEqual(len_exp, len_rcv, + self.msg.format('response message', 'write', + len_exp, len_rcv)) + + msg = "Bad response message write: encoding mismatch" + self.assertEqual(self.register, result, msg) diff --git a/kmip/tests/core/messages/test_operations.py b/kmip/tests/core/messages/test_operations.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/core/messages/test_operations.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/core/test_attributes.py b/kmip/tests/core/test_attributes.py new file mode 100644 index 0000000..af0427b --- /dev/null +++ b/kmip/tests/core/test_attributes.py @@ -0,0 +1,62 @@ +# Copyright (c) 2014 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.utils import BytearrayStream + + +class TestNameValue(TestCase): + + def setUp(self): + super(TestNameValue, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(TestNameValue, self).tearDown() + + def test_write_no_padding(self): + pass + + def test_write_with_padding(self): + pass + + def test_read_no_padding(self): + pass + + def test_read_with_padding(self): + pass + + +class TestName(TestCase): + + def setUp(self): + super(TestName, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(TestName, self).tearDown() + + def test_minimum_write(self): + pass + + def test_maximum_write(self): + pass + + def test_minimum_read(self): + pass + + def test_maximum_read(self): + pass diff --git a/kmip/tests/core/test_primitives.py b/kmip/tests/core/test_primitives.py new file mode 100644 index 0000000..b296c19 --- /dev/null +++ b/kmip/tests/core/test_primitives.py @@ -0,0 +1,1392 @@ +# Copyright (c) 2014 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.enums import Tags +from kmip.core.enums import Types + +from kmip.core.utils import BytearrayStream + +import kmip.core.errors as errors +from kmip.core.errors import ErrorStrings + +from kmip.core.primitives import Base +from kmip.core.primitives import Integer +from kmip.core.primitives import LongInteger +from kmip.core.primitives import BigInteger +from kmip.core.primitives import Enumeration +from kmip.core.primitives import TextString +from kmip.core.primitives import ByteString + + +class TestBase(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.bad_init = 'Bad Base initialization: attribute {0} missing' + self.bad_write = ErrorStrings.BAD_EXP_RECV.format('Base.{0}', 'write', + '{1}', '{2}') + self.bad_encoding = ErrorStrings.BAD_ENCODING.format('Base.{0}', + 'write') + self.bad_match = ErrorStrings.BAD_EXP_RECV.format('Base.{0}', + 'comparison', '{1}', + '{2}') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_is_oversized(self): + base = Base() + + # Check no exception thrown + base.is_oversized(self.stream) + + def test_is_oversized_error(self): + self.stream.write('\x00') + base = Base() + + self.assertRaises(errors.StreamNotEmptyError, base.is_oversized, + self.stream) + + def test_read_tag(self): + encoding = ('\x42\x00\x00') + base = Base() + self.stream = BytearrayStream(str(encoding)) + + # Check no exception thrown + base.read_tag(self.stream) + + def test_read_tag_invalid(self): + encoding = ('\x42\x00\x01') + base = Base() + self.stream = BytearrayStream(str(encoding)) + + self.assertRaises(errors.ReadValueError, base.read_tag, self.stream) + + def test_read_type(self): + self.stream.write('\x00') + base = Base() + + # Check no exception thrown + base.read_type(self.stream) + + def test_read_type_error(self): + self.stream.write('\x01') + base = Base() + + self.assertRaises(errors.ReadValueError, base.read_type, self.stream) + + def test_read_type_underflow(self): + base = Base() + + self.assertRaises(errors.ReadValueError, base.read_type, + self.stream) + + def test_read_type_overflow(self): + self.stream.write('\x00\x00') + base = Base() + + # Check no exception thrown + base.read_type(self.stream) + + def test_read_length(self): + self.stream.write('\x00\x00\x00\x04') + base = Base() + base.length = 4 + + # Check no exception thrown + base.read_length(self.stream) + + def test_read_length_error(self): + self.stream.write('\x00\x00\x00\x00') + base = Base() + base.length = 4 + + self.assertRaises(errors.ReadValueError, base.read_length, + self.stream) + + def test_read_length_underflow(self): + self.stream.write('\x00') + base = Base() + base.length = 4 + + self.assertRaises(errors.ReadValueError, base.read_length, + self.stream) + + def test_read_length_overflow(self): + self.stream.write('\x00\x00\x00\x04\x00') + base = Base() + base.length = 4 + + # Check no exception thrown + base.read_length(self.stream) + + def test_read_value(self): + base = Base() + + self.assertRaises(NotImplementedError, base.read_value, self.stream) + + def test_read(self): + self.stream.write('\x42\x00\x00\x00\x00\x00\x00\x04') + base = Base() + base.length = 4 + + # Check no exception thrown + base.read(self.stream) + + def test_write_tag(self): + encoding = ('\x42\x00\x00') + base = Base() + base.write_tag(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_write.format('tag', + '{0} bytes'.format(len_exp), + '{0} bytes'.format(len_rcv))) + self.assertEqual(encoding, result, self.bad_encoding.format('tag')) + + def test_write_type(self): + encoding = '\x00' + base = Base() + base.write_type(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_write.format('type', + '{0} bytes'.format(len_exp), + '{0} bytes'.format(len_rcv))) + self.assertEqual(encoding, result, self.bad_encoding.format('type')) + + def test_write_type_invalid(self): + base = Base() + base.type = '' + + self.assertRaises(TypeError, base.write_type, self.stream) + + def test_write_length(self): + encoding = '\x00\x00\x00\x04' + base = Base() + base.length = 4 + base.write_length(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_write.format('length', + '{0} bytes'.format(len_exp), + '{0} bytes'.format(len_rcv))) + self.assertEqual(encoding, result, self.bad_encoding.format('length')) + + def test_write_length_invalid(self): + base = Base() + base.length = '' + + self.assertRaises(TypeError, base.write_length, self.stream) + + def test_write_length_overflow(self): + self.skip('No easy way to test with a number requiring more than ' + '2 ** 0xffffffff bytes for representation. Test preserved ' + 'for completeness.') + + def test_write_value(self): + base = Base() + + self.assertRaises(NotImplementedError, base.write_value, self.stream) + + def test_write(self): + encoding = '\x42\x00\x00\x00\x00\x00\x00\x04' + base = Base() + base.length = 4 + base.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_write.format('type/length', + '{0} bytes'.format(len_exp), + '{0} bytes'.format(len_rcv))) + self.assertEqual(encoding, result, + self.bad_encoding.format('type/length')) + + def test_is_tag_next(self): + encoding = ('\x42\x00\x00') + base = Base() + self.stream = BytearrayStream(str(encoding)) + + self.assertTrue(Base.is_tag_next(base.tag, self.stream), + self.bad_match.format('tag', 'match', 'mismatch')) + + def test_is_tag_next_invalid(self): + encoding = ('\x42\x00\x01') + base = Base() + self.stream = BytearrayStream(str(encoding)) + + self.assertFalse(Base.is_tag_next(base.tag, self.stream), + self.bad_match.format('tag', 'mismatch', 'match')) + + +class TestInteger(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.max_byte_int = 4294967295 + self.max_int = 2147483647 + self.bad_value = ('Bad Integer.{0} after init: expected {1}, ' + 'received {2}') + self.bad_write = ('Bad Integer write: expected {0} bytes, ' + 'received {1} bytes') + self.bad_encoding = 'Bad Integer write: encoding mismatch' + self.bad_read = ('Bad Integer.value read: expected {0}, received {1}') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + i = Integer(0) + + self.assertEqual(0, i.value, + self.bad_value.format('value', 0, i.value)) + self.assertEqual(i.LENGTH, i.length, + self.bad_value.format('length', i.LENGTH, i.length)) + self.assertEqual(i.LENGTH, i.padding_length, + self.bad_value.format('padding_length', i.LENGTH, + i.padding_length)) + + def test_init_unset(self): + i = Integer() + + self.assertEqual(None, i.value, + self.bad_value.format('value', None, i.value)) + self.assertEqual(i.LENGTH, i.length, + self.bad_value.format('length', i.LENGTH, i.length)) + self.assertEqual(i.LENGTH, i.padding_length, + self.bad_value.format('padding_length', i.LENGTH, + i.padding_length)) + + def test_validate_on_valid(self): + i = Integer() + i.value = 0 + + # Check no exception thrown + i.validate() + + def test_validate_on_valid_unset(self): + i = Integer() + + # Check no exception thrown + i.validate() + + def test_validate_on_invalid_type(self): + i = Integer() + i.value = 'test' + + self.assertRaises(errors.StateTypeError, i.validate) + + def test_validate_on_invalid_value(self): + self.assertRaises(errors.StateOverflowError, Integer, + self.max_byte_int + 1) + + def test_read_value(self): + encoding = ('\x00\x00\x00\x01\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + i.read_value(self.stream) + + self.assertEqual(1, i.value, self.bad_read.format(1, i.value)) + + def test_read_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + i.read_value(self.stream) + + self.assertEqual(0, i.value, self.bad_read.format(0, i.value)) + + def test_read_value_max_positive(self): + encoding = ('\x7f\xff\xff\xff\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + i.read_value(self.stream) + + self.assertEqual(self.max_int, i.value, + self.bad_read.format(1, i.value)) + + def test_read_value_min_negative(self): + encoding = ('\xff\xff\xff\xff\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + i.read_value(self.stream) + + self.assertEqual(-1, i.value, + self.bad_read.format(1, i.value)) + + def test_read(self): + encoding = ('\x42\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + i.read(self.stream) + + self.assertEqual(1, i.value, self.bad_read.format(1, i.value)) + + def test_read_on_invalid_length(self): + encoding = ('\x42\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + + self.assertRaises(errors.ReadValueError, i.read, self.stream) + + def test_read_on_invalid_padding(self): + encoding = ('\x42\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x00\xff\xff' + '\xff\xff') + self.stream = BytearrayStream(str(encoding)) + i = Integer() + + self.assertRaises(errors.ReadValueError, i.read, self.stream) + + def test_write_value(self): + encoding = ('\x00\x00\x00\x01\x00\x00\x00\x00') + i = Integer(1) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + i = Integer(0) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_max_positive(self): + encoding = ('\x7f\xff\xff\xff\x00\x00\x00\x00') + i = Integer(self.max_int) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_min_negative(self): + encoding = ('\xff\xff\xff\xff\x00\x00\x00\x00') + i = Integer(-1) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write(self): + encoding = ('\x42\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00' + '\x00\x00') + i = Integer(1) + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + +class TestLongInteger(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.max_byte_long = 18446744073709551615 + self.max_long = 9223372036854775807 + self.bad_value = ('Bad LongInteger.{0} after init: expected {1}, ' + 'received {2}') + self.bad_write = ('Bad LongInteger write: expected {0} bytes, ' + 'received {1} bytes') + self.bad_encoding = 'Bad LongInteger write: encoding mismatch' + self.bad_read = ('Bad LongInteger.value read: expected {0}, received ' + '{1}') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + i = LongInteger(0) + + self.assertEqual(0, i.value, + self.bad_value.format('value', 0, i.value)) + self.assertEqual(i.LENGTH, i.length, + self.bad_value.format('length', i.LENGTH, i.length)) + + def test_init_unset(self): + i = LongInteger() + + self.assertEqual(None, i.value, + self.bad_value.format('value', None, i.value)) + self.assertEqual(i.LENGTH, i.length, + self.bad_value.format('length', i.LENGTH, i.length)) + + def test_validate_on_valid(self): + i = LongInteger() + i.value = 0 + + # Check no exception thrown + i.validate() + + def test_validate_on_valid_long(self): + i = LongInteger() + i.value = self.max_long + 1 + + # Check no exception thrown + i.validate() + + def test_validate_on_valid_unset(self): + i = LongInteger() + + # Check no exception thrown + i.validate() + + def test_validate_on_invalid_type(self): + i = LongInteger() + i.value = 'test' + + self.assertRaises(errors.StateTypeError, i.validate) + + def test_validate_on_invalid_value(self): + self.assertRaises(errors.StateOverflowError, LongInteger, + self.max_byte_long + 1) + + def test_read_value(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x01') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + i.read_value(self.stream) + + self.assertEqual(1, i.value, self.bad_read.format(1, i.value)) + + def test_read_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + i.read_value(self.stream) + + self.assertEqual(0, i.value, self.bad_read.format(0, i.value)) + + def test_read_value_max_positive(self): + encoding = ('\x7f\xff\xff\xff\xff\xff\xff\xff') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + i.read_value(self.stream) + + self.assertEqual(self.max_long, i.value, + self.bad_read.format(1, i.value)) + + def test_read_value_min_negative(self): + encoding = ('\xff\xff\xff\xff\xff\xff\xff\xff') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + i.read_value(self.stream) + + self.assertEqual(-1, i.value, + self.bad_read.format(1, i.value)) + + def test_read(self): + encoding = ('\x42\x00\x00\x03\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x01') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + i.read(self.stream) + + self.assertEqual(1, i.value, self.bad_read.format(1, i.value)) + + def test_read_on_invalid_length(self): + encoding = ('\x42\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = LongInteger() + + self.assertRaises(errors.ReadValueError, i.read, self.stream) + + def test_write_value(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x01') + i = LongInteger(1) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + i = LongInteger(0) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_max_positive(self): + encoding = ('\x7f\xff\xff\xff\xff\xff\xff\xff') + i = LongInteger(self.max_long) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_min_negative(self): + encoding = ('\xff\xff\xff\xff\xff\xff\xff\xff') + i = LongInteger(-1) + i.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write(self): + encoding = ('\x42\x00\x00\x03\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x01') + i = LongInteger(1) + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + +class TestBigInteger(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.max_byte_long = 18446744073709551615 + self.max_long = 9223372036854775807 + self.bad_value = ('Bad BigInteger.{0} after init: expected {1}, ' + 'received {2}') + self.bad_write = ('Bad BigInteger write: expected {0} bytes, ' + 'received {1} bytes') + self.bad_encoding = 'Bad BigInteger write: encoding mismatch' + self.bad_read = ('Bad BigInteger.value read: expected {0}, ' + 'received {1}') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_big_integer(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger(0) + + self.assertEqual(0, i.value, + self.bad_value.format('value', 0, i.value)) + self.assertEqual(1, i.length, + self.bad_value.format('length', 1, i.length)) + self.assertEqual(i.BLOCK_SIZE - 1, i.padding_length, + self.bad_value.format('padding_length', + i.BLOCK_SIZE - 1, + i.padding_length)) + + def test_big_integer_unset(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger() + + self.assertEqual(None, i.value, + self.bad_value.format('value', None, i.value)) + self.assertEqual(None, i.length, + self.bad_value.format('length', None, i.length)) + self.assertEqual(None, i.padding_length, + self.bad_value.format('padding_length', None, + i.padding_length)) + + def test_validate_on_valid(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger() + i.value = 0 + i.length = i.BLOCK_SIZE + i.padding_length = 0 + + # Check no exception thrown + i.validate() + + def test_validate_on_valid_long(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger() + i.value = self.max_long + 1 + i.length = i.BLOCK_SIZE + i.padding_length = 0 + + # Check no exception thrown + i.validate() + + def test_validate_on_valid_unset(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger() + + # Check no exception thrown + i.validate() + + def test_validate_on_invalid_type(self): + self.skip('BigInteger implementation incomplete') + i = BigInteger() + i.value = 'test' + + self.assertRaises(errors.StateTypeError, i.validate) + + def test_write(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x01') + i = BigInteger(1) + i.TAG = Tags.ACTIVATION_DATE + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_zero(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x00') + i = BigInteger(0) + i.TAG = Tags.ACTIVATION_DATE + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_max_positive_value(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x7f\xff\xff\xff\xff\xff' + '\xff\xff') + i = BigInteger(self.max_long) + i.TAG = Tags.ACTIVATION_DATE + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_min_negative_value(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\xff\xff\xff\xff\xff\xff' + '\xff\xff') + i = BigInteger(-1) + i.TAG = Tags.ACTIVATION_DATE + i.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_read(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x01') + self.stream = BytearrayStream(str(encoding)) + i = BigInteger() + i.TAG = Tags.ACTIVATION_DATE + i.read(self.stream) + + self.assertEqual(1, i.value, self.bad_read.format(1, i.value)) + + def test_read_zero(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = BigInteger() + i.TAG = Tags.ACTIVATION_DATE + i.read(self.stream) + + self.assertEqual(0, i.value, self.bad_read.format(0, i.value)) + + def test_read_max_positive_value(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\x7f\xff\xff\xff\xff\xff' + '\xff\xff') + self.stream = BytearrayStream(str(encoding)) + i = BigInteger() + i.TAG = Tags.ACTIVATION_DATE + i.read(self.stream) + + self.assertEqual(self.max_long, i.value, + self.bad_read.format(1, i.value)) + + def test_read_min_negative_value(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x08\xff\xff\xff\xff\xff\xff' + '\xff\xff') + self.stream = BytearrayStream(str(encoding)) + i = BigInteger() + i.TAG = Tags.ACTIVATION_DATE + i.read(self.stream) + + self.assertEqual(-1, i.value, + self.bad_read.format(1, i.value)) + + def test_read_on_invalid_length(self): + self.skip('BigInteger implementation incomplete') + encoding = ('\x42\x00\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + i = BigInteger() + i.TAG = Tags.ACTIVATION_DATE + + self.assertRaises(errors.InvalidLengthError, i.read, self.stream) + + +class TestEnumeration(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + Enumeration.ENUM_TYPE = Types + self.bad_type = ErrorStrings.BAD_EXP_RECV.format('Enumeration.{0}', + 'type', '{1}', '{2}') + self.bad_value = ErrorStrings.BAD_EXP_RECV.format('Enumeration.{0}', + 'value', '{1}', + '{2}') + self.bad_write = ErrorStrings.BAD_EXP_RECV.format('Enumeration', + 'write', + '{0} bytes', + '{1} bytes') + self.bad_encoding = ErrorStrings.BAD_ENCODING.format('Enumeration', + 'write') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + e = Enumeration(Types.DEFAULT) + + self.assertIsInstance(e.enum, Types, + self.bad_type.format('enum', Types, + type(e.enum))) + self.assertEqual(Types.DEFAULT, e.enum, + self.bad_value.format('enum', Types.DEFAULT, e.enum)) + + default = Types.DEFAULT + self.assertEqual(default.value, e.value, + self.bad_value.format('value', default.value, + e.value)) + + def test_init_unset(self): + e = Enumeration() + + self.assertEqual(None, e.enum, + self.bad_value.format('enum', None, e.enum)) + self.assertEqual(None, e.value, + self.bad_value.format('value', None, e.value)) + + def test_validate_on_valid(self): + e = Enumeration() + e.enum = Types.DEFAULT + + # Check no exception thrown + e.validate() + + def test_validate_on_valid_unset(self): + e = Enumeration() + + # Check no exception thrown + e.validate() + + def test_validate_on_invalid_type(self): + e = Enumeration() + e.enum = 0 + + self.assertRaises(TypeError, e.validate) + + def test_read(self): + encoding = ('\x42\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + e = Enumeration() + e.read(self.stream) + + self.assertIsInstance(e.enum, Types, + self.bad_type.format('enum', Types, + type(e.enum))) + self.assertEqual(Types.DEFAULT, e.enum, + self.bad_value.format('enum', Types.DEFAULT, + type(e.enum))) + default = Types.DEFAULT + self.assertEqual(default.value, e.value, + self.bad_value.format('value', default.value, + e.value)) + + def test_write(self): + encoding = ('\x42\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00' + '\x00\x00') + e = Enumeration(Types.DEFAULT) + e.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, self.bad_write.format(len_exp, + len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + +class TestBoolean(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + self.skip('') + + def test_init_unset(self): + self.skip('') + + def test_validate_on_valid(self): + self.skip('') + + def test_validate_on_valid_unset(self): + self.skip('') + + def test_validate_on_invalid_type(self): + self.skip('') + + def test_read_value(self): + self.skip('') + + def test_read(self): + self.skip('') + + def test_write_value(self): + self.skip('') + + def test_write(self): + self.skip('') + + +class TestTextString(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.bad_type = ErrorStrings.BAD_EXP_RECV.format('TextString.{0}', + 'type', '{1}', '{2}') + self.bad_value = ErrorStrings.BAD_EXP_RECV.format('TextString.{0}', + 'value', '{1}', + '{2}') + self.bad_read = ErrorStrings.BAD_EXP_RECV.format('TextString.{0}', + '', '{1}', '{2}') + self.bad_write = ErrorStrings.BAD_EXP_RECV.format('TextString.{0}', + 'write', '{1}', + '{2}') + self.bad_encoding = ErrorStrings.BAD_ENCODING.format('TextString', '') + self.bad_length = ErrorStrings.BAD_EXP_RECV.format('TextString', + 'length', + '{0} bytes', + '{1} bytes') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + value = 'Hello World' + ts = TextString(value) + + self.assertIsInstance(ts.value, str, + self.bad_type.format('value', str, + type(ts.value))) + self.assertEqual(value, ts.value, + self.bad_value.format('value', value, ts.value)) + + def test_init_unset(self): + ts = TextString() + + self.assertIsInstance(ts.value, type(None), + self.bad_type.format('value', type(None), + type(ts.value))) + self.assertEqual(None, ts.value, + self.bad_value.format('value', None, ts.value)) + + def test_validate_on_valid(self): + ts = TextString() + ts.value = 'Hello World' + + # Check no exception thrown. + ts.validate() + + def test_validate_on_valid_unset(self): + ts = TextString() + + # Check no exception thrown. + ts.validate() + + def test_validate_on_invalid_type(self): + ts = TextString() + ts.value = 0 + + self.assertRaises(TypeError, ts.validate) + + def test_read_value(self): + encoding = ('\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + ts = TextString() + ts.length = 0x0B + ts.read_value(self.stream) + + expected = 'Hello World' + self.assertEqual(expected, ts.value, + self.bad_read.format('value', expected, ts.value)) + + def test_read_value_no_padding(self): + encoding = ('\x48\x65\x6C\x6C\x6F\x20\x57\x6F') + self.stream = BytearrayStream(str(encoding)) + ts = TextString() + ts.length = 0x08 + ts.read_value(self.stream) + + expected = 'Hello Wo' + self.assertEqual(expected, ts.value, + self.bad_read.format('value', expected, ts.value)) + + def test_read_value_max_padding(self): + encoding = ('\x48\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + ts = TextString() + ts.length = 0x01 + ts.read_value(self.stream) + + expected = 'H' + self.assertEqual(expected, ts.value, + self.bad_read.format('value', expected, ts.value)) + + def test_read(self): + encoding = ('\x42\x00\x00\x07\x00\x00\x00\x0B\x48\x65\x6C\x6C\x6F\x20' + '\x57\x6F\x72\x6C\x64\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + ts = TextString() + ts.read(self.stream) + + expected = 'Hello World' + self.assertEqual(expected, ts.value, + self.bad_read.format('value', expected, ts.value)) + + def test_read_on_invalid_padding(self): + encoding = ('\x42\x00\x00\x07\x00\x00\x00\x0B\x48\x65\x6C\x6C\x6F\x20' + '\x57\x6F\x72\x6C\x64\xff\xff\xff\xff\xff') + self.stream = BytearrayStream(str(encoding)) + ts = TextString() + + self.assertRaises(errors.ReadValueError, ts.read, self.stream) + + def test_write_value(self): + encoding = ('\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream() + value = 'Hello World' + ts = TextString(value) + ts.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_no_padding(self): + encoding = ('\x48\x65\x6C\x6C\x6F\x20\x57\x6F') + self.stream = BytearrayStream() + value = 'Hello Wo' + ts = TextString(value) + ts.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_max_padding(self): + encoding = ('\x48\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream() + value = 'H' + ts = TextString(value) + ts.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write(self): + encoding = ('\x42\x00\x00\x07\x00\x00\x00\x0B\x48\x65\x6C\x6C\x6F\x20' + '\x57\x6F\x72\x6C\x64\x00\x00\x00\x00\x00') + self.stream = BytearrayStream() + value = 'Hello World' + ts = TextString(value) + ts.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + +class TestByteString(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + self.bad_type = ErrorStrings.BAD_EXP_RECV.format('ByteString.{0}', + 'type', '{1}', '{2}') + self.bad_value = ErrorStrings.BAD_EXP_RECV.format('ByteString.{0}', + 'value', '{1}', + '{2}') + self.bad_read = ErrorStrings.BAD_EXP_RECV.format('ByteString.{0}', + '', '{1}', '{2}') + self.bad_write = ErrorStrings.BAD_EXP_RECV.format('ByteString.{0}', + 'write', '{1}', + '{2}') + self.bad_encoding = ErrorStrings.BAD_ENCODING.format('ByteString', '') + self.bad_length = ErrorStrings.BAD_EXP_RECV.format('ByteString', + 'length', + '{0} bytes', + '{1} bytes') + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + value = bytearray('\x01\x02\x03') + bs = ByteString(value) + + self.assertIsInstance(bs.value, bytearray, + self.bad_type.format('value', bytearray, + type(bs.value))) + self.assertEqual(value, bs.value, + self.bad_value.format('value', value, bs.value)) + + def test_init_unset(self): + bs = ByteString() + + self.assertIsInstance(bs.value, type(None), + self.bad_type.format('value', type(None), + type(bs.value))) + self.assertEqual(None, bs.value, + self.bad_value.format('value', None, bs.value)) + + def test_validate_on_valid(self): + bs = ByteString() + bs.value = bytearray('\x00') + + # Check no exception thrown. + bs.validate() + + def test_validate_on_valid_unset(self): + bs = ByteString() + + # Check no exception thrown. + bs.validate() + + def test_validate_on_invalid_type(self): + bs = ByteString() + bs.value = 0 + + self.assertRaises(TypeError, bs.validate) + + def test_read_value(self): + encoding = ('\x01\x02\x03\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + bs.length = 0x03 + bs.read_value(self.stream) + + expected = bytearray('\x01\x02\x03') + self.assertEqual(expected, bs.value, + self.bad_read.format('value', expected, bs.value)) + + def test_read_value_no_padding(self): + encoding = ('\x01\x02\x03\x04\x05\x06\x07\x08') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + bs.length = 0x08 + bs.read_value(self.stream) + + expected = bytearray('\x01\x02\x03\x04\x05\x06\x07\x08') + self.assertEqual(expected, bs.value, + self.bad_read.format('value', expected, bs.value)) + + def test_read_value_max_padding(self): + encoding = ('\x01\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + bs.length = 0x01 + bs.read_value(self.stream) + + expected = bytearray('\x01') + self.assertEqual(expected, bs.value, + self.bad_read.format('value', expected, bs.value)) + + def test_read_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + bs.length = 0x01 + bs.read_value(self.stream) + + expected = bytearray('\x00') + self.assertEqual(expected, bs.value, + self.bad_read.format('value', expected, bs.value)) + + def test_read(self): + encoding = ('\x42\x00\x00\x08\x00\x00\x00\x03\x01\x02\x03\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + bs.read(self.stream) + + expected = bytearray('\x01\x02\x03') + self.assertEqual(expected, bs.value, + self.bad_read.format('value', expected, bs.value)) + + def test_read_on_invalid_padding(self): + encoding = ('\x42\x00\x00\x08\x00\x00\x00\x03\x01\x02\x03\xff\xff\xff' + '\xff\xff') + self.stream = BytearrayStream(str(encoding)) + bs = ByteString() + + self.assertRaises(errors.ReadValueError, bs.read, self.stream) + + def test_write_value(self): + encoding = ('\x01\x02\x03\x00\x00\x00\x00\x00') + self.stream = BytearrayStream() + value = bytearray('\x01\x02\x03') + bs = ByteString(value) + bs.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_no_padding(self): + encoding = ('\x01\x02\x03\x04\x05\x06\x07\x08') + self.stream = BytearrayStream() + value = bytearray('\x01\x02\x03\x04\x05\x06\x07\x08') + bs = ByteString(value) + bs.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_max_padding(self): + encoding = ('\x01\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream() + value = bytearray('\x01') + bs = ByteString(value) + bs.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write_value_zero(self): + encoding = ('\x00\x00\x00\x00\x00\x00\x00\x00') + self.stream = BytearrayStream() + value = bytearray('\x00') + bs = ByteString(value) + bs.write_value(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + def test_write(self): + encoding = ('\x42\x00\x00\x08\x00\x00\x00\x03\x01\x02\x03\x00\x00\x00' + '\x00\x00') + self.stream = BytearrayStream() + value = bytearray('\x01\x02\x03') + bs = ByteString(value) + bs.write(self.stream) + + result = self.stream.read() + len_exp = len(encoding) + len_rcv = len(result) + + self.assertEqual(len_exp, len_rcv, + self.bad_length.format(len_exp, len_rcv)) + self.assertEqual(encoding, result, self.bad_encoding) + + +class TestDateTime(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + self.skip('') + + def test_init_unset(self): + self.skip('') + + def test_validate_on_valid(self): + self.skip('') + + def test_validate_on_valid_unset(self): + self.skip('') + + def test_validate_on_invalid_type(self): + self.skip('') + + def test_read(self): + self.skip('') + + def test_write(self): + self.skip('') + + +class TestInterval(TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(self.__class__, self).tearDown() + + def test_init(self): + self.skip('') + + def test_init_unset(self): + self.skip('') + + def test_validate_on_valid(self): + self.skip('') + + def test_validate_on_valid_unset(self): + self.skip('') + + def test_validate_on_invalid_type(self): + self.skip('') + + def test_read(self): + self.skip('') + + def test_write(self): + self.skip('') diff --git a/kmip/tests/core/test_server.py b/kmip/tests/core/test_server.py new file mode 100644 index 0000000..7a1ad46 --- /dev/null +++ b/kmip/tests/core/test_server.py @@ -0,0 +1,508 @@ +# Copyright (c) 2014 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 CryptographicAlgorithm +from kmip.core.attributes import CryptographicLength +from kmip.core.attributes import CryptographicUsageMask +from kmip.core.attributes import UniqueIdentifier +from kmip.core.attributes import ObjectType +from kmip.core.enums import AttributeType +from kmip.core.enums import CryptographicAlgorithm as CryptoAlgorithmEnum +from kmip.core.enums import CryptographicUsageMask as CryptoUsageMaskEnum +from kmip.core.enums import KeyCompressionType as KeyCompressionTypeEnum +from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum +from kmip.core.enums import ObjectType as ObjectTypeEnum +from kmip.core.enums import ResultReason +from kmip.core.enums import ResultStatus +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.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 + + +class TestKMIPServer(TestCase): + + def setUp(self): + super(TestKMIPServer, self).setUp() + self.kmip = KMIPImpl() + self.algorithm_name = CryptoAlgorithmEnum.AES + self.key_length = 256 + self.key = bytearray(range(0, 32)) + self.usage_mask = CryptoUsageMaskEnum.ENCRYPT.value |\ + CryptoUsageMaskEnum.DECRYPT.value + + def tearDown(self): + super(TestKMIPServer, self).tearDown() + + def test_create(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + attributes = self._get_attrs() + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.create(obj_type, template_attribute) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_create_no_length(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + attributes = self._get_attrs()[0:2] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.create(obj_type, template_attribute) + self.assertNotEqual(None, res, 'result is None') + attrs = res.template_attribute.attributes + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + self.assertTrue(self._check_attr_exists(attributes[2], attrs), + 'length attribute not returned') + + def test_create_no_alg(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + attributes = [self._get_attrs()[1]] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.create(obj_type, template_attribute) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.OPERATION_FAILED, res.result_status.enum, + 'result status did not return failed') + + def test_create_no_usage_mask(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + attributes = [self._get_attrs()[0]] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.create(obj_type, template_attribute) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.OPERATION_FAILED, res.result_status.enum, + 'result status did not return failed') + + def test_register(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_register_attrs_in_key_value(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_algorithm = None + key.key_block.cryptographic_length = None + key.key_block.key_value.attributes = self._get_attrs() + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_register_attrs_in_template(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_algorithm = None + key.key_block.cryptographic_length = None + key.key_block.key_value.attributes = [] + attributes = self._get_attrs() + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_register_no_alg(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_algorithm = None + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_register_alg_in_key_value_and_key_block(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.key_value.attributes = [self._get_alg_attr()] + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_alg_in_template_and_key_block(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + attributes = [self._get_alg_attr()] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_alg_in_template_and_key_value(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_algorithm = None + key.key_block.key_value.attributes = [self._get_alg_attr()] + attributes = [self._get_alg_attr()] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_invalid_alg(self): + unsupported_algs = (CryptoAlgorithmEnum.RSA, + CryptoAlgorithmEnum.DSA, + CryptoAlgorithmEnum.ECDSA, + CryptoAlgorithmEnum.HMAC_SHA1, + CryptoAlgorithmEnum.HMAC_SHA224, + CryptoAlgorithmEnum.HMAC_SHA256, + CryptoAlgorithmEnum.HMAC_SHA384, + CryptoAlgorithmEnum.HMAC_SHA512, + CryptoAlgorithmEnum.HMAC_MD5, + CryptoAlgorithmEnum.DH, + CryptoAlgorithmEnum.ECDH, + CryptoAlgorithmEnum.ECMQV, + CryptoAlgorithmEnum.BLOWFISH, + CryptoAlgorithmEnum.CAMELLIA, + CryptoAlgorithmEnum.CAST5, + CryptoAlgorithmEnum.IDEA, + CryptoAlgorithmEnum.MARS, + CryptoAlgorithmEnum.RC2, + CryptoAlgorithmEnum.RC4, + CryptoAlgorithmEnum.RC5, + CryptoAlgorithmEnum.SKIPJACK, + CryptoAlgorithmEnum.TWOFISH) + for alg in unsupported_algs: + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_algorithm = CryptographicAlgorithm(alg) + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INVALID_FIELD, + res.result_reason.enum, + 'result reason did not match') + + def test_register_no_length(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_length = None + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_register_length_in_key_value_and_key_block(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.key_value.attributes = [self._get_length_attr()] + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_length_in_template_and_key_block(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + attributes = [self._get_length_attr()] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_length_in_template_and_key_value(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_length = None + key.key_block.key_value.attributes = [self._get_length_attr()] + attributes = [self._get_length_attr()] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INDEX_OUT_OF_BOUNDS, + res.result_reason.enum, + 'result reason did not match') + + def test_register_invalid_length(self): + unsupported_lens = (-1, 0, 2048, 5, 18) + for len in unsupported_lens: + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.cryptographic_length = CryptographicLength(len) + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INVALID_FIELD, + res.result_reason.enum, + 'result reason did not match') + + def test_register_no_usage_mask(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + key = self._get_symmetric_key() + key.key_block.key_value.attributes = [] + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_register_no_object_type(self): + obj_type = None + key = self._get_symmetric_key() + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_register_unsupported_object_type(self): + unsupported_types = (ObjectTypeEnum.CERTIFICATE, + ObjectTypeEnum.PUBLIC_KEY, + ObjectTypeEnum.PRIVATE_KEY, + ObjectTypeEnum.SPLIT_KEY, + ObjectTypeEnum.TEMPLATE, + ObjectTypeEnum.SECRET_DATA, + ObjectTypeEnum.OPAQUE_DATA) + for unsupported_type in unsupported_types: + obj_type = ObjectType(unsupported_type) + key = self._get_symmetric_key() + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INVALID_FIELD, + res.result_reason.enum, + 'result reason did not match') + + def test_register_object_type_mismatch(self): + unsupported_types = (ObjectTypeEnum.CERTIFICATE, + ObjectTypeEnum.PUBLIC_KEY, + ObjectTypeEnum.PRIVATE_KEY, + ObjectTypeEnum.SPLIT_KEY, + ObjectTypeEnum.TEMPLATE, + ObjectTypeEnum.SECRET_DATA, + ObjectTypeEnum.OPAQUE_DATA) + for unsupported_type in unsupported_types: + obj_type = ObjectType(unsupported_type) + key = self._get_symmetric_key() + attributes = [] + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.register(obj_type, template_attribute, key) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.INVALID_FIELD, + res.result_reason.enum, + 'result reason did not match') + + def test_get(self): + uuid = self._create() + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + res = self.kmip.get(uuid, key_format_type) + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_get_no_key_format_type(self): + uuid = self._create() + res = self.kmip.get(uuid, None) + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + + def test_get_unknown(self): + uuids = ('some random string', UniqueIdentifier('no key here')) + for uuid in uuids: + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + res = self.kmip.get(uuid, key_format_type) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_get_no_uuid(self): + self._create() + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + res = self.kmip.get(None, key_format_type) + self.assertEqual(ResultStatus.OPERATION_FAILED, res.result_status.enum, + 'result status did not return failed') + + def test_get_with_key_compression(self): + uuid = self._create() + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + key_compression = KeyCompressionType(KeyCompressionTypeEnum. + EC_PUBLIC_KEY_TYPE_UNCOMPRESSED) + res = self.kmip.get(uuid, key_format_type, key_compression) + self.assertEqual(ResultStatus.OPERATION_FAILED, res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.KEY_COMPRESSION_TYPE_NOT_SUPPORTED, + res.result_reason.enum, + 'result reason did not match') + + def test_destroy(self): + uuid = self._create() + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + res = self.kmip.get(uuid, key_format_type) + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + res = self.kmip.destroy(uuid) + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + res = self.kmip.destroy(uuid) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_destroy_no_uuid(self): + res = self.kmip.destroy(None) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def test_destroy_unknown(self): + uuids = ('some random string', UniqueIdentifier('no key here')) + for uuid in uuids: + key_format_type = KeyFormatType(KeyFormatTypeEnum.RAW) + res = self.kmip.get(uuid, key_format_type) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + res = self.kmip.destroy(uuid) + self.assertEqual(ResultStatus.OPERATION_FAILED, + res.result_status.enum, + 'result status did not return failed') + self.assertEqual(ResultReason.ITEM_NOT_FOUND, + res.result_reason.enum, + 'result reason did not match') + + def _create(self): + obj_type = ObjectType(ObjectTypeEnum.SYMMETRIC_KEY) + attributes = self._get_attrs() + template_attribute = TemplateAttribute(attributes=attributes) + res = self.kmip.create(obj_type, template_attribute) + self.assertNotEqual(None, res, 'result is None') + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'result status did not return success') + return res.uuid + + def _get_symmetric_key(self): + # only need usage attribute + attrs = [self._get_attrs()[1]] + key_format_type = KeyBlock.KeyFormatType(KeyFormatTypeEnum.RAW) + key_material = RawKey(self.key) + key_value = KeyValueStruct(key_format_type, key_material, attrs) + crypto_alg = CryptographicAlgorithm(self.algorithm_name) + crypto_length = CryptographicLength(self.key_length) + usage = CryptographicUsageMask(self.usage_mask) + key_block = KeyBlock(key_format_type, None, key_value, crypto_alg, + crypto_length, usage) + return SymmetricKey(key_block) + + def _get_attrs(self): + attr_factory = AttributeFactory() + algorithm = self._get_alg_attr(self.algorithm_name) + length = self._get_length_attr(self.key_length) + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + mask_flags = [CryptoUsageMaskEnum.ENCRYPT, + CryptoUsageMaskEnum.DECRYPT] + usage_mask = attr_factory.create_attribute(attribute_type, + mask_flags) + return [algorithm, usage_mask, length] + + def _get_alg_attr(self, alg=None): + if alg is None: + alg = self.algorithm_name + attr_factory = AttributeFactory() + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + return attr_factory.create_attribute(attribute_type, alg) + + def _get_length_attr(self, length=None): + if length is None: + length = self.key_length + attr_factory = AttributeFactory() + attribute_type = AttributeType.CRYPTOGRAPHIC_LENGTH + return attr_factory.create_attribute(attribute_type, length) + + def _check_attr_exists(self, attr_expected, attributes): + for attribute in attributes: + if attribute.attribute_name.value ==\ + attr_expected.attribute_name.value: + return attribute.attribute_value.value ==\ + attr_expected.attribute_value.value + return False diff --git a/kmip/tests/core/test_utils.py b/kmip/tests/core/test_utils.py new file mode 100644 index 0000000..7129823 --- /dev/null +++ b/kmip/tests/core/test_utils.py @@ -0,0 +1,134 @@ +# Copyright (c) 2014 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.errors import ErrorStrings + +from kmip.core import utils + + +class TestUtils(TestCase): + + def setUp(self): + super(TestUtils, self).setUp() + + def tearDown(self): + super(TestUtils, self).tearDown() + + def test_count_bytes(self): + num = 65535 + bytes_exp = 2 + bytes_obs = utils.count_bytes(num) + self.assertEqual(bytes_exp, bytes_obs, + 'Value {0} requires {1} bytes to encode, ' + 'received {2} byte(s)'.format(num, bytes_exp, + bytes_obs)) + + def test_count_bytes_overflow(self): + num = 65536 + bytes_exp = 3 + bytes_obs = utils.count_bytes(num) + self.assertEqual(bytes_exp, bytes_obs, + 'Value {0} requires {1} bytes to encode, ' + 'received {2} bytes'.format(num, bytes_exp, + bytes_obs)) + + def test_count_bytes_zero(self): + num = 0 + bytes_exp = 1 + bytes_obs = utils.count_bytes(num) + self.assertEqual(bytes_exp, bytes_obs, + 'Value {0} requires {1} bytes to encode, ' + 'received {2} byte(s)'.format(num, bytes_exp, + bytes_obs)) + + +class TestBytearrayStream(TestCase): + + def setUp(self): + super(TestBytearrayStream, self).setUp() + self.stream = utils.BytearrayStream() + + self.bad_type = ErrorStrings.BAD_EXP_RECV.format('BytearrayStream.{0}', + 'type', '{1}', '{2}') + self.bad_len = ErrorStrings.BAD_EXP_RECV.format('BytearrayStream.{0}', + 'length', '{1}', '{2}') + self.bad_val = ErrorStrings.BAD_EXP_RECV.format('BytearrayStream.{0}', + 'value', '{1}', '{2}') + + def tearDown(self): + super(TestBytearrayStream, self).tearDown() + + def test_init(self): + value = '\x00' + b = utils.BytearrayStream(value) + + buf_type = type(b.buffer) + msg = self.bad_type.format('buffer', bytearray, buf_type) + self.assertIsInstance(b.buffer, bytearray, + msg.format(bytearray, type(b.buffer))) + + length = len(b.buffer) + msg = self.bad_len.format('buffer', 1, length) + self.assertEqual(1, length, msg) + + content = str(b.buffer) + msg = self.bad_val.format('buffer', value, content) + self.assertEqual(value, content, msg) + + def test_init_unset(self): + b = utils.BytearrayStream() + + buf_type = type(b.buffer) + msg = self.bad_type.format('buffer', bytearray, buf_type) + self.assertIsInstance(b.buffer, bytearray, + msg.format(bytearray, type(b.buffer))) + + length = len(b.buffer) + msg = self.bad_len.format('buffer', 0, length) + self.assertEqual(0, length, msg) + + def test_read(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') + + def test_write(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') + + def test_peek(self): + # TODO (peter-hamilton) Finish implementation. + value = ('\x00\x01\x02\x03') + expected = value + b = expected + expected = b + b = utils.BytearrayStream(value) + + def test_peek_overflow(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') + + def test_peek_empty(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') + + def test_peek_none(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') + + def test_length(self): + # TODO (peter-hamilton) Finish implementation. + self.skip('') diff --git a/kmip/tests/services/__init__.py b/kmip/tests/services/__init__.py new file mode 100644 index 0000000..87b311e --- /dev/null +++ b/kmip/tests/services/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2014 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. diff --git a/kmip/tests/services/test_kmip_client.py b/kmip/tests/services/test_kmip_client.py new file mode 100644 index 0000000..53b1a2d --- /dev/null +++ b/kmip/tests/services/test_kmip_client.py @@ -0,0 +1,385 @@ +# Copyright (c) 2014 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 subprocess import Popen + +import os +import sys +import time + +from kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask +from kmip.core.enums import ObjectType +from kmip.core.enums import KeyFormatType +from kmip.core.enums import ResultStatus +from kmip.core.enums import ResultReason + +from kmip.core.errors import KMIPServerSuicideError +from kmip.core.errors import KMIPServerZombieError + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory +from kmip.core.factories.secrets import SecretFactory + +from kmip.core.objects import Attribute +from kmip.core.objects import TemplateAttribute + +from kmip.core.secrets import SymmetricKey + +from kmip.services.kmip_client import KMIPProxy + +import kmip.core.utils as utils + + +class TestKMIPClient(TestCase): + STARTUP_TIME = 1.0 + SHUTDOWN_TIME = 0.1 + KMIP_PORT = 9090 + + def setUp(self): + super(TestKMIPClient, self).setUp() + + self.attr_factory = AttributeFactory() + self.cred_factory = CredentialFactory() + self.secret_factory = SecretFactory() + + # Set up the KMIP server process + path = os.path.join(os.path.dirname(__file__), os.path.pardir, + os.path.pardir, os.path.pardir, 'bin', + 'run_server.py') + self.server = Popen(['python', '{0}'.format(path), '-p', + '{0}'.format(self.KMIP_PORT)], stderr=sys.stdout) + + time.sleep(self.STARTUP_TIME) + + if self.server.poll() is not None: + raise KMIPServerSuicideError(self.server.pid) + + # Set up and open the client proxy; shutdown the server if open fails + try: + self.client = KMIPProxy(port=self.KMIP_PORT) + self.client.open() + except Exception, e: + self._shutdown_server() + raise e + + def tearDown(self): + super(TestKMIPClient, self).tearDown() + + # Close the client proxy and shutdown the server + self.client.close() + self._shutdown_server() + + def test_create(self): + result = self._create_symmetric_key() + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_object_type(result.object_type.enum, ObjectType, + ObjectType.SYMMETRIC_KEY) + self._check_uuid(result.uuid.value, str) + + # Check the template attribute type + self._check_template_attribute(result.template_attribute, + TemplateAttribute, 2, + [[str, 'Cryptographic Length', int, + 256], + [str, 'Unique Identifier', str, + None]]) + + def test_get(self): + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = self.cred_factory.create_credential(credential_type, + credential_value) + result = self._create_symmetric_key() + uuid = result.uuid.value + + result = self.client.get(uuid, credential) + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_object_type(result.object_type.enum, ObjectType, + ObjectType.SYMMETRIC_KEY) + self._check_uuid(result.uuid.value, str) + + # Check the secret type + secret = result.secret + + expected = SymmetricKey + message = utils.build_er_error(result.__class__, 'type', expected, + secret, 'secret') + self.assertIsInstance(secret, expected, message) + + def test_destroy(self): + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = self.cred_factory.create_credential(credential_type, + credential_value) + result = self._create_symmetric_key() + uuid = result.uuid.value + + # Verify the secret was created + result = self.client.get(uuid, credential) + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_object_type(result.object_type.enum, ObjectType, + ObjectType.SYMMETRIC_KEY) + self._check_uuid(result.uuid.value, str) + + secret = result.secret + + expected = SymmetricKey + message = utils.build_er_error(result.__class__, 'type', expected, + secret, 'secret') + self.assertIsInstance(secret, expected, message) + + # Destroy the SYMMETRIC_KEY object + result = self.client.destroy(uuid, credential) + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_uuid(result.uuid.value, str) + + # Verify the secret was destroyed + result = self.client.get(uuid, credential) + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.OPERATION_FAILED) + + expected = ResultReason + observed = type(result.result_reason.enum) + message = utils.build_er_error(result.result_reason.__class__, 'type', + expected, observed) + self.assertEqual(expected, observed, message) + + expected = ResultReason.ITEM_NOT_FOUND + observed = result.result_reason.enum + message = utils.build_er_error(result.result_reason.__class__, + 'value', expected, observed) + self.assertEqual(expected, observed, message) + + def test_register(self): + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = self.cred_factory.create_credential(credential_type, + credential_value) + + object_type = ObjectType.SYMMETRIC_KEY + algorithm_value = CryptographicAlgorithm.AES + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = self.attr_factory.create_attribute(attribute_type, + mask_flags) + attributes = [usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + secret_features = {} + + key_format_type = KeyFormatType.RAW + secret_features.update([('key_format_type', key_format_type)]) + + key_data = {'bytes': bytearray('\x00\x00\x00\x00\x00\x00\x00\x00' + '\x00\x00\x00\x00\x00\x00\x00\x00')} + + secret_features.update([('key_value', key_data)]) + secret_features.update([('cryptographic_algorithm', algorithm_value)]) + secret_features.update([('cryptographic_length', 128)]) + + secret = self.secret_factory.create_secret(object_type, + secret_features) + + result = self.client.register(object_type, template_attribute, secret, + credential) + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_uuid(result.uuid.value, str) + + # Check the template attribute type + self._check_template_attribute(result.template_attribute, + TemplateAttribute, 1, + [[str, 'Unique Identifier', str, + None]]) + # Check that the returned key bytes match what was provided + uuid = result.uuid.value + result = self.client.get(uuid, credential) + + self._check_result_status(result.result_status.enum, ResultStatus, + ResultStatus.SUCCESS) + self._check_object_type(result.object_type.enum, ObjectType, + ObjectType.SYMMETRIC_KEY) + self._check_uuid(result.uuid.value, str) + + # Check the secret type + secret = result.secret + + expected = SymmetricKey + message = utils.build_er_error(result.__class__, 'type', expected, + secret, 'secret') + self.assertIsInstance(secret, expected, message) + + key_block = result.secret.key_block + key_value = key_block.key_value + key_material = key_value.key_value.key_material + + expected = key_data.get('bytes') + observed = key_material.value + message = utils.build_er_error(key_material.__class__, 'value', + expected, observed, 'value') + self.assertEqual(expected, observed, message) + + def _shutdown_server(self): + if self.server.poll() is not None: + return + else: + # Terminate the server process. If it resists, kill it. + pid = self.server.pid + self.server.terminate() + time.sleep(self.SHUTDOWN_TIME) + + if self.server.poll() is None: + raise KMIPServerZombieError(pid) + + def _create_symmetric_key(self): + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = self.cred_factory.create_credential(credential_type, + credential_value) + + object_type = ObjectType.SYMMETRIC_KEY + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + algorithm = self.attr_factory.create_attribute( + attribute_type, + CryptographicAlgorithm.AES) + + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = self.attr_factory.create_attribute(attribute_type, + mask_flags) + attributes = [algorithm, usage_mask] + template_attribute = TemplateAttribute(attributes=attributes) + + return self.client.create(object_type, template_attribute, + credential) + + def _check_result_status(self, result_status, result_status_type, + result_status_value): + # Error check the result status type and value + expected = result_status_type + message = utils.build_er_error(result_status_type, 'type', expected, + result_status) + self.assertIsInstance(result_status, expected, message) + + expected = result_status_value + message = utils.build_er_error(result_status_type, 'value', expected, + result_status) + self.assertEqual(expected, result_status, message) + + def _check_uuid(self, uuid, uuid_type): + # Error check the UUID type and value + not_expected = None + message = utils.build_er_error(uuid_type, 'type', + 'not {0}'.format(not_expected), uuid) + self.assertNotEqual(not_expected, uuid, message) + + expected = uuid_type + message = utils.build_er_error(uuid_type, 'type', expected, uuid) + self.assertEqual(expected, type(uuid), message) + + def _check_object_type(self, object_type, object_type_type, + object_type_value): + # Error check the object type type and value + expected = object_type_type + message = utils.build_er_error(object_type_type, 'type', expected, + object_type) + self.assertIsInstance(object_type, expected, message) + + expected = object_type_value + message = utils.build_er_error(object_type_type, 'value', expected, + object_type) + self.assertEqual(expected, object_type, message) + + def _check_template_attribute(self, template_attribute, + template_attribute_type, num_attributes, + attribute_features): + # Error check the template attribute type + expected = template_attribute_type + message = utils.build_er_error(template_attribute.__class__, 'type', + expected, template_attribute) + self.assertIsInstance(template_attribute, expected, message) + + attributes = template_attribute.attributes + + expected = num_attributes + observed = len(attributes) + message = utils.build_er_error(TemplateAttribute.__class__, 'number', + expected, observed, 'attributes') + + for i in xrange(num_attributes): + features = attribute_features[i] + self._check_attribute(attributes[i], features[0], features[1], + features[2], features[3]) + + def _check_attribute(self, attribute, attribute_name_type, + attribute_name_value, attribute_value_type, + attribute_value_value): + # Error check the attribute name and value type and value + attribute_name = attribute.attribute_name + attribute_value = attribute.attribute_value + + self._check_attribute_name(attribute_name, attribute_name_type, + attribute_name_value) + + if attribute_name_value == 'Unique Identifier': + self._check_uuid(attribute_value.value, attribute_value_type) + else: + self._check_attribute_value(attribute_value, attribute_value_type, + attribute_value_value) + + def _check_attribute_name(self, attribute_name, attribute_name_type, + attribute_name_value): + # Error check the attribute name type and value + expected = attribute_name_type + observed = type(attribute_name.value) + message = utils.build_er_error(attribute_name_type, 'type', expected, + observed) + self.assertEqual(expected, observed, message) + + expected = attribute_name_value + observed = attribute_name.value + message = utils.build_er_error(attribute_name_type, 'value', expected, + observed) + self.assertEqual(expected, observed, message) + + def _check_attribute_value(self, attribute_value, attribute_value_type, + attribute_value_value): + expected = attribute_value_type + observed = type(attribute_value.value) + message = utils.build_er_error(Attribute, 'type', expected, observed, + 'attribute_value') + self.assertEqual(expected, observed, message) + + expected = attribute_value_value + observed = attribute_value.value + message = utils.build_er_error(Attribute, 'value', expected, observed, + 'attribute_value') + self.assertEqual(expected, observed, message) diff --git a/kmip/transport/__init__.py b/kmip/transport/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/kmip/transport/kmip/KMIP-remote b/kmip/transport/kmip/KMIP-remote deleted file mode 100755 index 04377d2..0000000 --- a/kmip/transport/kmip/KMIP-remote +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# -# Autogenerated by Thrift Compiler (0.9.1) -# -# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING -# -# options string: py -# - -import sys -import pprint -from urlparse import urlparse -from thrift.transport import TTransport -from thrift.transport import TSocket -from thrift.transport import THttpClient -from thrift.protocol import TBinaryProtocol - -from kmip import KMIP -from kmip.ttypes import * - -if len(sys.argv) <= 1 or sys.argv[1] == '--help': - print '' - print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]' - print '' - print 'Functions:' - print ' void create()' - print ' void register_mo()' - print '' - sys.exit(0) - -pp = pprint.PrettyPrinter(indent = 2) -host = 'localhost' -port = 9090 -uri = '' -framed = False -http = False -argi = 1 - -if sys.argv[argi] == '-h': - parts = sys.argv[argi+1].split(':') - host = parts[0] - if len(parts) > 1: - port = int(parts[1]) - argi += 2 - -if sys.argv[argi] == '-u': - url = urlparse(sys.argv[argi+1]) - parts = url[1].split(':') - host = parts[0] - if len(parts) > 1: - port = int(parts[1]) - else: - port = 80 - uri = url[2] - if url[4]: - uri += '?%s' % url[4] - http = True - argi += 2 - -if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed': - framed = True - argi += 1 - -cmd = sys.argv[argi] -args = sys.argv[argi+1:] - -if http: - transport = THttpClient.THttpClient(host, port, uri) -else: - socket = TSocket.TSocket(host, port) - if framed: - transport = TTransport.TFramedTransport(socket) - else: - transport = TTransport.TBufferedTransport(socket) -protocol = TBinaryProtocol.TBinaryProtocol(transport) -client = KMIP.Client(protocol) -transport.open() - -if cmd == 'create': - if len(args) != 0: - print 'create requires 0 args' - sys.exit(1) - pp.pprint(client.create()) - -elif cmd == 'register_mo': - if len(args) != 0: - print 'register_mo requires 0 args' - sys.exit(1) - pp.pprint(client.register_mo()) - -else: - print 'Unrecognized method %s' % cmd - sys.exit(1) - -transport.close() diff --git a/kmip/transport/kmip/KMIP.py b/kmip/transport/kmip/KMIP.py deleted file mode 100644 index 8d333d7..0000000 --- a/kmip/transport/kmip/KMIP.py +++ /dev/null @@ -1,295 +0,0 @@ -# -# Autogenerated by Thrift Compiler (0.9.1) -# -# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING -# -# options string: py -# - -from thrift.Thrift import TType, TMessageType, TException, TApplicationException -from ttypes import * -from thrift.Thrift import TProcessor -from thrift.transport import TTransport -from thrift.protocol import TBinaryProtocol, TProtocol -try: - from thrift.protocol import fastbinary -except: - fastbinary = None - - -class Iface: - def create(self): - pass - - def register_mo(self): - pass - - -class Client(Iface): - def __init__(self, iprot, oprot=None): - self._iprot = self._oprot = iprot - if oprot is not None: - self._oprot = oprot - self._seqid = 0 - - def create(self): - self.send_create() - self.recv_create() - - def send_create(self): - self._oprot.writeMessageBegin('create', TMessageType.CALL, self._seqid) - args = create_args() - args.write(self._oprot) - self._oprot.writeMessageEnd() - self._oprot.trans.flush() - - def recv_create(self): - (fname, mtype, rseqid) = self._iprot.readMessageBegin() - if mtype == TMessageType.EXCEPTION: - x = TApplicationException() - x.read(self._iprot) - self._iprot.readMessageEnd() - raise x - result = create_result() - result.read(self._iprot) - self._iprot.readMessageEnd() - return - - def register_mo(self): - self.send_register_mo() - self.recv_register_mo() - - def send_register_mo(self): - self._oprot.writeMessageBegin('register_mo', TMessageType.CALL, self._seqid) - args = register_mo_args() - args.write(self._oprot) - self._oprot.writeMessageEnd() - self._oprot.trans.flush() - - def recv_register_mo(self): - (fname, mtype, rseqid) = self._iprot.readMessageBegin() - if mtype == TMessageType.EXCEPTION: - x = TApplicationException() - x.read(self._iprot) - self._iprot.readMessageEnd() - raise x - result = register_mo_result() - result.read(self._iprot) - self._iprot.readMessageEnd() - return - - -class Processor(Iface, TProcessor): - def __init__(self, handler): - self._handler = handler - self._processMap = {} - self._processMap["create"] = Processor.process_create - self._processMap["register_mo"] = Processor.process_register_mo - - def process(self, iprot, oprot): - (name, type, seqid) = iprot.readMessageBegin() - if name not in self._processMap: - iprot.skip(TType.STRUCT) - iprot.readMessageEnd() - x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) - oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) - x.write(oprot) - oprot.writeMessageEnd() - oprot.trans.flush() - return - else: - self._processMap[name](self, seqid, iprot, oprot) - return True - - def process_create(self, seqid, iprot, oprot): - args = create_args() - args.read(iprot) - iprot.readMessageEnd() - result = create_result() - self._handler.create() - oprot.writeMessageBegin("create", TMessageType.REPLY, seqid) - result.write(oprot) - oprot.writeMessageEnd() - oprot.trans.flush() - - def process_register_mo(self, seqid, iprot, oprot): - args = register_mo_args() - args.read(iprot) - iprot.readMessageEnd() - result = register_mo_result() - self._handler.register_mo() - oprot.writeMessageBegin("register_mo", TMessageType.REPLY, seqid) - result.write(oprot) - oprot.writeMessageEnd() - oprot.trans.flush() - - -# HELPER FUNCTIONS AND STRUCTURES - -class create_args: - - thrift_spec = ( - ) - - def read(self, iprot): - if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: - fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) - return - iprot.readStructBegin() - while True: - (fname, ftype, fid) = iprot.readFieldBegin() - if ftype == TType.STOP: - break - else: - iprot.skip(ftype) - iprot.readFieldEnd() - iprot.readStructEnd() - - def write(self, oprot): - if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: - oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) - return - oprot.writeStructBegin('create_args') - oprot.writeFieldStop() - oprot.writeStructEnd() - - def validate(self): - return - - - def __repr__(self): - L = ['%s=%r' % (key, value) - for key, value in self.__dict__.iteritems()] - return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) - - def __eq__(self, other): - return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ - - def __ne__(self, other): - return not (self == other) - -class create_result: - - thrift_spec = ( - ) - - def read(self, iprot): - if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: - fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) - return - iprot.readStructBegin() - while True: - (fname, ftype, fid) = iprot.readFieldBegin() - if ftype == TType.STOP: - break - else: - iprot.skip(ftype) - iprot.readFieldEnd() - iprot.readStructEnd() - - def write(self, oprot): - if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: - oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) - return - oprot.writeStructBegin('create_result') - oprot.writeFieldStop() - oprot.writeStructEnd() - - def validate(self): - return - - - def __repr__(self): - L = ['%s=%r' % (key, value) - for key, value in self.__dict__.iteritems()] - return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) - - def __eq__(self, other): - return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ - - def __ne__(self, other): - return not (self == other) - -class register_mo_args: - - thrift_spec = ( - ) - - def read(self, iprot): - if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: - fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) - return - iprot.readStructBegin() - while True: - (fname, ftype, fid) = iprot.readFieldBegin() - if ftype == TType.STOP: - break - else: - iprot.skip(ftype) - iprot.readFieldEnd() - iprot.readStructEnd() - - def write(self, oprot): - if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: - oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) - return - oprot.writeStructBegin('register_mo_args') - oprot.writeFieldStop() - oprot.writeStructEnd() - - def validate(self): - return - - - def __repr__(self): - L = ['%s=%r' % (key, value) - for key, value in self.__dict__.iteritems()] - return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) - - def __eq__(self, other): - return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ - - def __ne__(self, other): - return not (self == other) - -class register_mo_result: - - thrift_spec = ( - ) - - def read(self, iprot): - if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: - fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) - return - iprot.readStructBegin() - while True: - (fname, ftype, fid) = iprot.readFieldBegin() - if ftype == TType.STOP: - break - else: - iprot.skip(ftype) - iprot.readFieldEnd() - iprot.readStructEnd() - - def write(self, oprot): - if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: - oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) - return - oprot.writeStructBegin('register_mo_result') - oprot.writeFieldStop() - oprot.writeStructEnd() - - def validate(self): - return - - - def __repr__(self): - L = ['%s=%r' % (key, value) - for key, value in self.__dict__.iteritems()] - return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) - - def __eq__(self, other): - return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ - - def __ne__(self, other): - return not (self == other) diff --git a/kmip/transport/kmip/__init__.py b/kmip/transport/kmip/__init__.py deleted file mode 100644 index 41fa036..0000000 --- a/kmip/transport/kmip/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['ttypes', 'constants', 'KMIP'] diff --git a/kmip/transport/kmip/constants.py b/kmip/transport/kmip/constants.py deleted file mode 100644 index 35216c6..0000000 --- a/kmip/transport/kmip/constants.py +++ /dev/null @@ -1,11 +0,0 @@ -# -# Autogenerated by Thrift Compiler (0.9.1) -# -# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING -# -# options string: py -# - -from thrift.Thrift import TType, TMessageType, TException, TApplicationException -from ttypes import * - diff --git a/kmip/transport/kmip/ttypes.py b/kmip/transport/kmip/ttypes.py deleted file mode 100644 index 424ff7e..0000000 --- a/kmip/transport/kmip/ttypes.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Autogenerated by Thrift Compiler (0.9.1) -# -# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING -# -# options string: py -# - -from thrift.Thrift import TType, TMessageType, TException, TApplicationException - -from thrift.transport import TTransport -from thrift.protocol import TBinaryProtocol, TProtocol -try: - from thrift.protocol import fastbinary -except: - fastbinary = None - - diff --git a/requirements.txt b/requirements.txt index c334a20..876ce95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ -# TODO: Add any library dependencies here +enum34 +sqlalchemy +thrift + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a6ae9d3 --- /dev/null +++ b/setup.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# +# 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. + +import setuptools + +setuptools.setup( + name='PyKMIP', + version='0.0.1', + description='KMIP v1.1 library', + keywords='KMIP', + author='Peter Hamilton', + author_email='peter.hamilton@jhuapl.edu', + url='http://www.jhuapl.edu', + license='Apache License, Version 2.0', + packages=setuptools.find_packages(exclude=["kmip.tests", "kmip.tests.*"]), + classifiers=[ + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + ], +) diff --git a/test-requirements.txt b/test-requirements.txt index 85cc5c0..5135f64 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,11 @@ # TODO: add any test requirements here +coverage +pytest +flake8 +testtools +fixtures +testresources +mock +testscenarios +testrepository +sphinx diff --git a/thrift/kmip.thrift b/thrift/kmip.thrift deleted file mode 100644 index fcb2715..0000000 --- a/thrift/kmip.thrift +++ /dev/null @@ -1,43 +0,0 @@ -/** - * This is the thrift definition file. It defines all of the methods and objects - * for the KMIP server. This file is used by thrift to generate the plumbing for - * a KMIP client and server. - * - * This file generated the files under kmip/thrift. - * - * If this file is updated then run the following command to update the - * kmip/thrift directory. - * - * thrift -o kmip -r --gen py thrift/tutorial.thrift - */ - -/** - * The first thing to know about are types. The available types in Thrift are: - * - * bool Boolean, one byte - * byte Signed byte - * i16 Signed 16-bit integer - * i32 Signed 32-bit integer - * i64 Signed 64-bit integer - * double 64-bit floating point value - * string String - * binary Blob (byte array) - * map Map from one type to another - * list Ordered list of one type - * set Set of unique elements of one type - * - */ - -namespace cpp thrift -namespace d thrift -namespace java thrift -namespace php thrift -namespace perl thrift - -service KMIP { - - void create(), - /* Did not use register because it is a reserved word */ - void register_mo() - -} \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..bcecc39 --- /dev/null +++ b/tox.ini @@ -0,0 +1,16 @@ +[tox] +envlist = pep8,py27,py26 + +[testenv] +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +commands = + coverage run --source=kmip/ -m pytest --strict {posargs} + coverage report + +[testenv:pep8] +commands = flake8 {posargs} + +[flake8] +exclude = .git,.tox,dist,rpmbuild,*.egg-info