Merge branch 'develop'
This commit is contained in:
		
							
								
								
									
										184
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								README.rst
									
									
									
									
									
								
							| @@ -1,46 +1,178 @@ | |||||||
| ====== | ------ | ||||||
| PyKMIP | PyKMIP | ||||||
| ====== | ------ | ||||||
|  |  | ||||||
| PyKMIP is a Python implementation of the Key Management Interoperability | PyKMIP is a Python implementation of the Key Management Interoperability | ||||||
| Protocol (KMIP) specification, supporting version 1.1 of the KMIP standard. | Protocol (KMIP) specification, supporting version 1.1 of the KMIP standard. | ||||||
| The library currently provides a KMIP client, which supports the following | The KMIP standard is governed by the `Organization for the Advancement of | ||||||
| operations for KMIP SymmetricKey managed objects: | Structured Information Standards`_ (OASIS) and specifies a | ||||||
|  | client/server-based protocol to perform key, certificate, and secret object | ||||||
|  | management, including storage and maintenance operations. | ||||||
|  |  | ||||||
| * create | The PyKMIP library currently provides a KMIP client and server supporting | ||||||
| * register | the following operations for the KMIP SymmetricKey managed object: | ||||||
| * get |  | ||||||
| * destroy |  | ||||||
|  |  | ||||||
| PyKMIP also provides a software-based KMIP server, which is intended for use | * Create | ||||||
| in testing and demonstration environments. The server is NOT intended to be | * Register | ||||||
| a substitute for secured hardware-based KMIP appliances. | * Get | ||||||
|  | * Destroy | ||||||
|  |  | ||||||
| Version | Note that KMIP specifies profiles that tailor the standard to specific use | ||||||
| ======= | cases. The `KMIP Profile Support`_ section includes several profiles that | ||||||
| This distribution of PyKMIP is version 0.0.1. Future work includes adding | need to be developed for PyKMIP to fully support symmetric key storage and | ||||||
| support for basic KMIP profiles, including the basic supporting operations. | generation capabilities.  A list of operations necessary for these profiles | ||||||
|  | is included. | ||||||
|  |  | ||||||
| For more information on KMIP profiles, see the `OASIS documentation for | The PyKMIP software-based KMIP server is intended for use only in testing | ||||||
| KMIP profiles | and demonstration environments. Note that the PyKMIP server is **NOT** | ||||||
| <http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html>`_. | intended to be a substitute for secured, hardware-based KMIP appliances. | ||||||
|  | The PyKMIP client should be used for operational purposes only with a | ||||||
|  | hardware-based KMIP server. The development of the PyKMIP client and server | ||||||
|  | should take place in parallel to facilitate testing of each operation as it | ||||||
|  | is developed. | ||||||
|  |  | ||||||
| Platform | Platforms | ||||||
| ======== | ========= | ||||||
| PyKMIP has been tested and runs on Ubuntu 12.04 LTS. | PyKMIP has been tested and runs on Ubuntu 12.04 LTS. | ||||||
|  |  | ||||||
|  | .. _KMIP Profile Support: | ||||||
|  |  | ||||||
|  | KMIP Profile Support | ||||||
|  | ==================== | ||||||
|  | The KMIP standard includes various profiles that tailor the standard for | ||||||
|  | specific use cases (e.g., symmetric key storage with TLS 1.2). These | ||||||
|  | profiles specify conformance to certain operations and attributes. The | ||||||
|  | operations listed below are needed to support symmetric key profiles, which | ||||||
|  | are also provided below. We would appreciate help in the development of | ||||||
|  | these operations, and have listed our recommended order of development | ||||||
|  | prioritization in descending order. Since active development of these | ||||||
|  | features is already underway, please check the `code base`_ to assess the | ||||||
|  | status of operations prior to development. | ||||||
|  |  | ||||||
|  | KMIP operations to add to PyKMIP: | ||||||
|  |  | ||||||
|  | * Discover Versions | ||||||
|  | * List | ||||||
|  | * Locate | ||||||
|  | * Check | ||||||
|  | * Revoke | ||||||
|  | * Get Attributes | ||||||
|  | * Get Attribute List | ||||||
|  | * Add Attribute | ||||||
|  | * Modify Attribute | ||||||
|  | * Delete Attribute | ||||||
|  | * Activate | ||||||
|  | * Query | ||||||
|  |  | ||||||
|  | Note that the Create, Register, Get, and Destroy operations were completed | ||||||
|  | with the initial version of PyKMIP to allow very basic KMIP symmetric key | ||||||
|  | operations. | ||||||
|  |  | ||||||
|  | Server Profiles | ||||||
|  | --------------- | ||||||
|  | Server profiles that support KMIP symmetric key operations: | ||||||
|  |  | ||||||
|  | * `Basic Baseline Server KMIP Profile`_ (includes TLS 1.0+) | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Baseline Server Clause`_) include: | ||||||
|  |  | ||||||
|  |     * Locate | ||||||
|  |     * Check | ||||||
|  |     * Get | ||||||
|  |     * Get Attributes | ||||||
|  |     * Get Attribute | ||||||
|  |     * List | ||||||
|  |     * Add Attribute | ||||||
|  |     * Modify Attribute | ||||||
|  |     * Delete Attribute | ||||||
|  |     * Activate | ||||||
|  |     * Revoke | ||||||
|  |     * Destroy | ||||||
|  |     * Query | ||||||
|  |     * Discover Versions | ||||||
|  |  | ||||||
|  | * `Symmetric Key Store and Server TLS 1.2 Authentication KMIP Profile`_ | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Symmetric Key Store and Server Conformance Clause`_) include all operations from the `Basic Baseline Server KMIP Profile`_ and also the Register operation. | ||||||
|  |  | ||||||
|  | * `Symmetric Key Foundry and Server TLS 1.2 Authentication KMIP profile`_ | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Symmetric Key Foundry and Server Conformance Clause`_) include all operations from the `Basic Baseline Server KMIP Profile`_ and also the Create operation. | ||||||
|  |  | ||||||
|  | Client Profiles | ||||||
|  | --------------- | ||||||
|  | Client profiles that support KMIP symmetric key operations: | ||||||
|  |  | ||||||
|  | * `Basic Baseline Client KMIP Profile`_ (includes TLS 1.0+) | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Baseline Client Clause`_) include: | ||||||
|  |  | ||||||
|  |     * Locate | ||||||
|  |     * Check | ||||||
|  |     * Get | ||||||
|  |     * Get Attributes | ||||||
|  |     * Get Attribute | ||||||
|  |     * List | ||||||
|  |     * Add Attribute | ||||||
|  |     * Modify Attribute | ||||||
|  |     * Delete Attribute | ||||||
|  |     * Activate | ||||||
|  |     * Revoke | ||||||
|  |     * Destroy | ||||||
|  |     * Query | ||||||
|  |     * Discover Versions | ||||||
|  |  | ||||||
|  | * `Symmetric Key Store Client TLS 1.2 Authentication KMIP Profile`_ | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Symmetric Key Store Client Conformance Clause`_) include all operations from the `Basic Baseline Client KMIP Profile`_ and also the Register operation. | ||||||
|  |  | ||||||
|  | * `Symmetric Key Foundry Client TLS 1.2 Authentication KMIP Profile`_ | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Symmetric Key Foundry Client Conformance Clause`_) include all operations from the `Basic Baseline Client KMIP Profile`_ and also the Create operation. | ||||||
|  |  | ||||||
|  | * `Storage Client TLS 1.2 Authentication KMIP Profile`_ | ||||||
|  |  | ||||||
|  |   * Client-to-Server operations needed for this (see the `Storage Client Conformance Clauses`_) include all operations from the `Basic Baseline Client KMIP Profile`_, the Register operation from the `Symmetric Key Store Client TLS 1.2 Authentication KMIP Profile`_, and the Create operation from the `Symmetric Key Foundry Client TLS 1.2 Authentication KMIP Profile`_. | ||||||
|  |  | ||||||
| References | References | ||||||
| ========== | ========== | ||||||
|  | The source code for PyKMIP is hosted on GitHub and the library is available | ||||||
|  | for installation from the Python Package Index (PyPI): | ||||||
|  |  | ||||||
| For more information on the KMIP specification, see the `OASIS documentation | * `GitHub <https://github.com/OpenKMIP/PyKMIP>`_ | ||||||
| for KMIP | * `PyPI <https://pypi.python.org/pypi/PyKMIP>`_ | ||||||
| <http://docs.oasis-open.org/kmip/spec/v1.1/os/kmip-spec-v1.1-os.html>`_. |  | ||||||
|  | For more information on KMIP version 1.1, see the following documentation: | ||||||
|  |  | ||||||
|  | * `Key Management Interoperability Protocol Specification Version 1.1`_ | ||||||
|  | * `Key Management Interoperability Protocol Profiles Version 1.1`_ | ||||||
|  | * `Key Management Interoperability Protocol Test Cases Version 1.1`_ | ||||||
|  |  | ||||||
| Contributors | Contributors | ||||||
| ============ | ============ | ||||||
|  |  | ||||||
| Many thanks to the developers who created PyKMIP: | Many thanks to the developers who created PyKMIP: | ||||||
|  |  | ||||||
| Nathan Reller <nathan.reller@jhuapl.edu> | * `Nathan Reller <nathan.reller@jhuapl.edu>`_ | ||||||
| Peter Hamilton <peter.hamilton@jhuapl.edu> | * `Peter Hamilton <peter.hamilton@jhuapl.edu>`_ | ||||||
| Kaitlin Farr <kaitlin.farr@jhuapl.edu> | * `Kaitlin Farr <kaitlin.farr@jhuapl.edu>`_ | ||||||
|  |  | ||||||
|  | .. _code base: https://github.com/OpenKMIP/PyKMIP | ||||||
|  | .. _Organization for the Advancement of Structured Information Standards: https://www.oasis-open.org/ | ||||||
|  | .. _Key Management Interoperability Protocol Specification Version 1.1: http://docs.oasis-open.org/kmip/spec/v1.1/os/kmip-spec-v1.1-os.html | ||||||
|  | .. _Key Management Interoperability Protocol Profiles Version 1.1: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html | ||||||
|  | .. _Key Management Interoperability Protocol Test Cases Version 1.1: http://docs.oasis-open.org/kmip/testcases/v1.1/cn01/kmip-testcases-v1.1-cn01.html | ||||||
|  | .. _Basic Baseline Server KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820691 | ||||||
|  | .. _Symmetric Key Store and Server TLS 1.2 Authentication KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820703 | ||||||
|  | .. _Symmetric Key Foundry and Server TLS 1.2 Authentication KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820704 | ||||||
|  | .. _Basic Baseline Client KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820711 | ||||||
|  | .. _Symmetric Key Store Client TLS 1.2 Authentication KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820723 | ||||||
|  | .. _Symmetric Key Foundry Client TLS 1.2 Authentication KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820724 | ||||||
|  | .. _Storage Client TLS 1.2 Authentication KMIP Profile: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820731 | ||||||
|  | .. _Baseline Server Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820736 | ||||||
|  | .. _Symmetric Key Store and Server Conformance Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820742 | ||||||
|  | .. _Symmetric Key Foundry and Server Conformance Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820745 | ||||||
|  | .. _Baseline Client Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820766 | ||||||
|  | .. _Symmetric Key Store Client Conformance Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820772 | ||||||
|  | .. _Symmetric Key Foundry Client Conformance Clause: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820775 | ||||||
|  | .. _Storage Client Conformance Clauses: http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html#_Toc332820793 | ||||||
| @@ -35,6 +35,8 @@ class ConfigHelper(object): | |||||||
|     DEFAULT_CA_CERTS = os.path.normpath(os.path.join( |     DEFAULT_CA_CERTS = os.path.normpath(os.path.join( | ||||||
|         FILE_PATH, '../demos/certs/server.crt')) |         FILE_PATH, '../demos/certs/server.crt')) | ||||||
|     DEFAULT_SSL_VERSION = 'PROTOCOL_SSLv23' |     DEFAULT_SSL_VERSION = 'PROTOCOL_SSLv23' | ||||||
|  |     DEFAULT_USERNAME = None | ||||||
|  |     DEFAULT_PASSWORD = None | ||||||
|  |  | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.logger = logging.getLogger(__name__) |         self.logger = logging.getLogger(__name__) | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ ssl_version=PROTOCOL_SSLv23 | |||||||
| ca_certs=../demos/certs/server.crt | ca_certs=../demos/certs/server.crt | ||||||
| do_handshake_on_connect=True | do_handshake_on_connect=True | ||||||
| suppress_ragged_eofs=True | suppress_ragged_eofs=True | ||||||
|  | username=None | ||||||
|  | password=None | ||||||
|  |  | ||||||
| [server] | [server] | ||||||
| host=127.0.0.1 | host=127.0.0.1 | ||||||
|   | |||||||
| @@ -22,6 +22,9 @@ from kmip.services.results import LocateResult | |||||||
| from kmip.core import attributes as attr | from kmip.core import attributes as attr | ||||||
|  |  | ||||||
| from kmip.core.enums import Operation as OperationEnum | from kmip.core.enums import Operation as OperationEnum | ||||||
|  | from kmip.core.enums import CredentialType | ||||||
|  |  | ||||||
|  | from kmip.core.factories.credentials import CredentialFactory | ||||||
|  |  | ||||||
| from kmip.core import objects | from kmip.core import objects | ||||||
| from kmip.core.server import KMIP | from kmip.core.server import KMIP | ||||||
| @@ -55,13 +58,17 @@ class KMIPProxy(KMIP): | |||||||
|     def __init__(self, host=None, port=None, keyfile=None, certfile=None, |     def __init__(self, host=None, port=None, keyfile=None, certfile=None, | ||||||
|                  cert_reqs=None, ssl_version=None, ca_certs=None, |                  cert_reqs=None, ssl_version=None, ca_certs=None, | ||||||
|                  do_handshake_on_connect=None, |                  do_handshake_on_connect=None, | ||||||
|                  suppress_ragged_eofs=None): |                  suppress_ragged_eofs=None, | ||||||
|  |                  username=None, | ||||||
|  |                  password=None): | ||||||
|         super(self.__class__, self).__init__() |         super(self.__class__, self).__init__() | ||||||
|         self.logger = logging.getLogger(__name__) |         self.logger = logging.getLogger(__name__) | ||||||
|  |         self.credential_factory = CredentialFactory() | ||||||
|  |  | ||||||
|         self._set_variables(host, port, keyfile, certfile, |         self._set_variables(host, port, keyfile, certfile, | ||||||
|                             cert_reqs, ssl_version, ca_certs, |                             cert_reqs, ssl_version, ca_certs, | ||||||
|                             do_handshake_on_connect, suppress_ragged_eofs) |                             do_handshake_on_connect, suppress_ragged_eofs, | ||||||
|  |                             username, password) | ||||||
|  |  | ||||||
|         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||||||
|         self.socket = ssl.wrap_socket( |         self.socket = ssl.wrap_socket( | ||||||
| @@ -327,9 +334,29 @@ class KMIPProxy(KMIP): | |||||||
|                               uuids) |                               uuids) | ||||||
|         return result |         return result | ||||||
|  |  | ||||||
|  |     # TODO (peter-hamilton) Augment to handle device credentials | ||||||
|  |     def _build_credential(self): | ||||||
|  |         if (self.username is None) and (self.password is None): | ||||||
|  |             return None | ||||||
|  |         if self.username is None: | ||||||
|  |             raise ValueError('cannot build credential, username is None') | ||||||
|  |         if self.password is None: | ||||||
|  |             raise ValueError('cannot build credential, password is None') | ||||||
|  |  | ||||||
|  |         credential_type = CredentialType.USERNAME_AND_PASSWORD | ||||||
|  |         credential_value = {'Username': self.username, | ||||||
|  |                             'Password': self.password} | ||||||
|  |         credential = self.credential_factory.create_credential( | ||||||
|  |             credential_type, | ||||||
|  |             credential_value) | ||||||
|  |         return credential | ||||||
|  |  | ||||||
|     def _build_request_message(self, credential, batch_items): |     def _build_request_message(self, credential, batch_items): | ||||||
|         protocol_version = ProtocolVersion.create(1, 1) |         protocol_version = ProtocolVersion.create(1, 1) | ||||||
|  |  | ||||||
|  |         if credential is None: | ||||||
|  |             credential = self._build_credential() | ||||||
|  |  | ||||||
|         authentication = None |         authentication = None | ||||||
|         if credential is not None: |         if credential is not None: | ||||||
|             authentication = Authentication(credential) |             authentication = Authentication(credential) | ||||||
| @@ -352,7 +379,8 @@ class KMIPProxy(KMIP): | |||||||
|  |  | ||||||
|     def _set_variables(self, host, port, keyfile, certfile, |     def _set_variables(self, host, port, keyfile, certfile, | ||||||
|                        cert_reqs, ssl_version, ca_certs, |                        cert_reqs, ssl_version, ca_certs, | ||||||
|                        do_handshake_on_connect, suppress_ragged_eofs): |                        do_handshake_on_connect, suppress_ragged_eofs, | ||||||
|  |                        username, password): | ||||||
|         conf = ConfigHelper() |         conf = ConfigHelper() | ||||||
|  |  | ||||||
|         self.host = conf.get_valid_value( |         self.host = conf.get_valid_value( | ||||||
| @@ -389,3 +417,9 @@ class KMIPProxy(KMIP): | |||||||
|             self.suppress_ragged_eofs = True |             self.suppress_ragged_eofs = True | ||||||
|         else: |         else: | ||||||
|             self.suppress_ragged_eofs = False |             self.suppress_ragged_eofs = False | ||||||
|  |  | ||||||
|  |         self.username = conf.get_valid_value( | ||||||
|  |             username, 'client', 'username', conf.DEFAULT_USERNAME) | ||||||
|  |  | ||||||
|  |         self.password = conf.get_valid_value( | ||||||
|  |             password, 'client', 'password', conf.DEFAULT_PASSWORD) | ||||||
|   | |||||||
| @@ -248,6 +248,66 @@ class TestKMIPClient(TestCase): | |||||||
|                                        expected, observed, 'value') |                                        expected, observed, 'value') | ||||||
|         self.assertEqual(expected, observed, message) |         self.assertEqual(expected, observed, message) | ||||||
|  |  | ||||||
|  |     # TODO (peter-hamilton) Modify for credential type and/or add new test | ||||||
|  |     def test_build_credential(self): | ||||||
|  |         username = 'username' | ||||||
|  |         password = 'password' | ||||||
|  |         cred_type = CredentialType.USERNAME_AND_PASSWORD | ||||||
|  |         self.client.username = username | ||||||
|  |         self.client.password = password | ||||||
|  |  | ||||||
|  |         credential = self.client._build_credential() | ||||||
|  |  | ||||||
|  |         message = utils.build_er_error(credential.__class__, 'type', | ||||||
|  |                                        cred_type, | ||||||
|  |                                        credential.credential_type.enum, | ||||||
|  |                                        'value') | ||||||
|  |         self.assertEqual(CredentialType.USERNAME_AND_PASSWORD, | ||||||
|  |                          credential.credential_type.enum, | ||||||
|  |                          message) | ||||||
|  |  | ||||||
|  |         message = utils.build_er_error( | ||||||
|  |             credential.__class__, 'type', username, | ||||||
|  |             credential.credential_value.username.value, 'value') | ||||||
|  |         self.assertEqual(username, credential.credential_value.username.value, | ||||||
|  |                          message) | ||||||
|  |  | ||||||
|  |         message = utils.build_er_error( | ||||||
|  |             credential.__class__, 'type', password, | ||||||
|  |             credential.credential_value.password.value, 'value') | ||||||
|  |         self.assertEqual(password, credential.credential_value.password.value, | ||||||
|  |                          message) | ||||||
|  |  | ||||||
|  |     def test_build_credential_no_username(self): | ||||||
|  |         username = None | ||||||
|  |         password = 'password' | ||||||
|  |         self.client.username = username | ||||||
|  |         self.client.password = password | ||||||
|  |  | ||||||
|  |         exception = self.assertRaises(ValueError, | ||||||
|  |                                       self.client._build_credential) | ||||||
|  |         self.assertEqual('cannot build credential, username is None', | ||||||
|  |                          str(exception)) | ||||||
|  |  | ||||||
|  |     def test_build_credential_no_password(self): | ||||||
|  |         username = 'username' | ||||||
|  |         password = None | ||||||
|  |         self.client.username = username | ||||||
|  |         self.client.password = password | ||||||
|  |  | ||||||
|  |         exception = self.assertRaises(ValueError, | ||||||
|  |                                       self.client._build_credential) | ||||||
|  |         self.assertEqual('cannot build credential, password is None', | ||||||
|  |                          str(exception)) | ||||||
|  |  | ||||||
|  |     def test_build_credential_no_creds(self): | ||||||
|  |         self.client.username = None | ||||||
|  |         self.client.password = None | ||||||
|  |  | ||||||
|  |         credential = self.client._build_credential() | ||||||
|  |  | ||||||
|  |         self.assertEqual(None, credential) | ||||||
|  |  | ||||||
|     def _shutdown_server(self): |     def _shutdown_server(self): | ||||||
|         if self.server.poll() is not None: |         if self.server.poll() is not None: | ||||||
|             return |             return | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Peter Hamilton
					Peter Hamilton