Merge branch 'develop'

This commit is contained in:
Peter Hamilton 2014-10-27 08:09:09 -04:00
commit 58c42f1098
5 changed files with 259 additions and 29 deletions

View File

@ -1,46 +1,178 @@
======
------
PyKMIP
======
------
PyKMIP is a Python implementation of the Key Management Interoperability
Protocol (KMIP) specification, supporting version 1.1 of the KMIP standard.
The library currently provides a KMIP client, which supports the following
operations for KMIP SymmetricKey managed objects:
The KMIP standard is governed by the `Organization for the Advancement of
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
* register
* get
* destroy
The PyKMIP library currently provides a KMIP client and server supporting
the following operations for the KMIP SymmetricKey managed object:
PyKMIP also provides a software-based KMIP server, which is intended for use
in testing and demonstration environments. The server is NOT intended to be
a substitute for secured hardware-based KMIP appliances.
* Create
* Register
* Get
* Destroy
Version
=======
This distribution of PyKMIP is version 0.0.1. Future work includes adding
support for basic KMIP profiles, including the basic supporting operations.
Note that KMIP specifies profiles that tailor the standard to specific use
cases. The `KMIP Profile Support`_ section includes several profiles that
need to be developed for PyKMIP to fully support symmetric key storage and
generation capabilities. A list of operations necessary for these profiles
is included.
For more information on KMIP profiles, see the `OASIS documentation for
KMIP profiles
<http://docs.oasis-open.org/kmip/profiles/v1.1/os/kmip-profiles-v1.1-os.html>`_.
The PyKMIP software-based KMIP server is intended for use only in testing
and demonstration environments. Note that the PyKMIP server is **NOT**
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.
.. _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
==========
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
for KMIP
<http://docs.oasis-open.org/kmip/spec/v1.1/os/kmip-spec-v1.1-os.html>`_.
* `GitHub <https://github.com/OpenKMIP/PyKMIP>`_
* `PyPI <https://pypi.python.org/pypi/PyKMIP>`_
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
============
Many thanks to the developers who created PyKMIP:
Nathan Reller <nathan.reller@jhuapl.edu>
Peter Hamilton <peter.hamilton@jhuapl.edu>
Kaitlin Farr <kaitlin.farr@jhuapl.edu>
* `Nathan Reller <nathan.reller@jhuapl.edu>`_
* `Peter Hamilton <peter.hamilton@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

View File

@ -35,6 +35,8 @@ class ConfigHelper(object):
DEFAULT_CA_CERTS = os.path.normpath(os.path.join(
FILE_PATH, '../demos/certs/server.crt'))
DEFAULT_SSL_VERSION = 'PROTOCOL_SSLv23'
DEFAULT_USERNAME = None
DEFAULT_PASSWORD = None
def __init__(self):
self.logger = logging.getLogger(__name__)

View File

@ -8,6 +8,8 @@ ssl_version=PROTOCOL_SSLv23
ca_certs=../demos/certs/server.crt
do_handshake_on_connect=True
suppress_ragged_eofs=True
username=None
password=None
[server]
host=127.0.0.1

View File

@ -22,6 +22,9 @@ from kmip.services.results import LocateResult
from kmip.core import attributes as attr
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.server import KMIP
@ -55,13 +58,17 @@ class KMIPProxy(KMIP):
def __init__(self, host=None, port=None, keyfile=None, certfile=None,
cert_reqs=None, ssl_version=None, ca_certs=None,
do_handshake_on_connect=None,
suppress_ragged_eofs=None):
suppress_ragged_eofs=None,
username=None,
password=None):
super(self.__class__, self).__init__()
self.logger = logging.getLogger(__name__)
self.credential_factory = CredentialFactory()
self._set_variables(host, port, keyfile, certfile,
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)
self.socket = ssl.wrap_socket(
@ -327,9 +334,29 @@ class KMIPProxy(KMIP):
uuids)
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):
protocol_version = ProtocolVersion.create(1, 1)
if credential is None:
credential = self._build_credential()
authentication = None
if credential is not None:
authentication = Authentication(credential)
@ -352,7 +379,8 @@ class KMIPProxy(KMIP):
def _set_variables(self, host, port, keyfile, certfile,
cert_reqs, ssl_version, ca_certs,
do_handshake_on_connect, suppress_ragged_eofs):
do_handshake_on_connect, suppress_ragged_eofs,
username, password):
conf = ConfigHelper()
self.host = conf.get_valid_value(
@ -389,3 +417,9 @@ class KMIPProxy(KMIP):
self.suppress_ragged_eofs = True
else:
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)

View File

@ -248,6 +248,66 @@ class TestKMIPClient(TestCase):
expected, observed, 'value')
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):
if self.server.poll() is not None:
return