[Checkins] SVN: keas.kmi/tags/0.2.0/ Release keas.kmi 0.2.0.

Marius Gedminas marius at pov.lt
Thu Sep 4 17:06:25 EDT 2008


Log message for revision 90845:
  Release keas.kmi 0.2.0.
  
  

Changed:
  A   keas.kmi/tags/0.2.0/
  D   keas.kmi/tags/0.2.0/CHANGES.txt
  A   keas.kmi/tags/0.2.0/CHANGES.txt
  D   keas.kmi/tags/0.2.0/README.txt
  A   keas.kmi/tags/0.2.0/README.txt
  D   keas.kmi/tags/0.2.0/buildout.cfg
  A   keas.kmi/tags/0.2.0/buildout.cfg
  D   keas.kmi/tags/0.2.0/setup.py
  A   keas.kmi/tags/0.2.0/setup.py
  D   keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
  A   keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
  D   keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
  A   keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
  D   keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
  A   keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
  D   keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
  A   keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
  D   keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
  A   keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
  A   keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py
  D   keas.kmi/tags/0.2.0/src/keas/kmi/testing.py
  A   keas.kmi/tags/0.2.0/src/keas/kmi/testing.py

-=-
Copied: keas.kmi/tags/0.2.0 (from rev 90830, keas.kmi/trunk)

Deleted: keas.kmi/tags/0.2.0/CHANGES.txt
===================================================================
--- keas.kmi/trunk/CHANGES.txt	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/CHANGES.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,20 +0,0 @@
-=======
-CHANGES
-=======
-
-0.2.0 (unreleased)
-------------------
-
-
-0.1.0 (2008-09-03)
-------------------
-
-- Initial Release
-
-  * Key Generation Service
-
-  * Encryption Service (Master and Local)
-
-  * REST API for key communication between encryption services
-
-  * Encrypted Persistent Storage

Copied: keas.kmi/tags/0.2.0/CHANGES.txt (from rev 90844, keas.kmi/trunk/CHANGES.txt)
===================================================================
--- keas.kmi/tags/0.2.0/CHANGES.txt	                        (rev 0)
+++ keas.kmi/tags/0.2.0/CHANGES.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,30 @@
+=======
+CHANGES
+=======
+
+0.2.0 (2008-09-04)
+------------------
+
+- Sample server shows how to enable SSL
+
+- Front page now shows the number of stored keys instead of a
+  ComponentLookupError message.
+
+- Command-line client for testing a remote Key Management Server
+
+- Bugfix: LocalKeyManagementFacility was broken (AttributeError: 'RESTClient'
+  object has no attribute 'POST')
+
+
+0.1.0 (2008-09-03)
+------------------
+
+- Initial Release
+
+  * Key Generation Service
+
+  * Encryption Service (Master and Local)
+
+  * REST API for key communication between encryption services
+
+  * Encrypted Persistent Storage

Deleted: keas.kmi/tags/0.2.0/README.txt
===================================================================
--- keas.kmi/trunk/README.txt	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/README.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,23 +0,0 @@
-This package provides a NIST SP 800-57 compliant Key Management Infrastructure
-(KMI).
-
-To get started do::
-
-  $ python boostrap.py # Must be Python 2.5
-  $ ./bin/buildout     # Depends on successfull compilation of M2Crypto
-  $ ./bin/runserver    # or ./bin/paster serve server.ini
-
-The server will come up on port 8080. You can create a new key encrypting key
-using::
-
-  $ wget https://localhost:8080/new -O kek.dat --ca-certificate sample.pem
-
-The data encryption key can now be retrieved by posting the KEK to another
-URL::
-
-  $ wget https://localhost:8080/key --header 'Content-Type: text/plain' --post-file kek.dat -O datakey.dat --ca-certificate sample.pem
-
-Note: To be compliant, the server must use an encrypted communication channel
-of course.  The ``--ca-certificate`` tells wget to trust the sample self-signed
-certificate included in the keas.kmi distribution; you'll want to generate a
-new SSL certificate for production use.

Copied: keas.kmi/tags/0.2.0/README.txt (from rev 90836, keas.kmi/trunk/README.txt)
===================================================================
--- keas.kmi/tags/0.2.0/README.txt	                        (rev 0)
+++ keas.kmi/tags/0.2.0/README.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,31 @@
+This package provides a NIST SP 800-57 compliant Key Management Infrastructure
+(KMI).
+
+To get started do::
+
+  $ python boostrap.py # Must be Python 2.5
+  $ ./bin/buildout     # Depends on successfull compilation of M2Crypto
+  $ ./bin/runserver    # or ./bin/paster serve server.ini
+
+The server will come up on port 8080. You can create a new key encrypting key
+using::
+
+  $ wget https://localhost:8080/new -O kek.dat --ca-certificate sample.pem
+
+or, if you want a more convenient tool::
+
+  $ ./bin/testclient https://localhost:8080/new -n > kek.dat
+
+The data encryption key can now be retrieved by posting the KEK to another
+URL::
+
+  $ wget https://localhost:8080/key --header 'Content-Type: text/plain' --post-file kek.dat -O datakey.dat --ca-certificate sample.pem
+
+or ::
+
+  $ ./bin/testclient https://localhost:8080/new -g kek.dat > datakey.dat
+
+Note: To be compliant, the server must use an encrypted communication channel
+of course.  The ``--ca-certificate`` tells wget to trust the sample self-signed
+certificate included in the keas.kmi distribution; you'll want to generate a
+new SSL certificate for production use.

Deleted: keas.kmi/tags/0.2.0/buildout.cfg
===================================================================
--- keas.kmi/trunk/buildout.cfg	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/buildout.cfg	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,49 +0,0 @@
-[buildout]
-develop = .
-parts = test coverage-test coverage-report python paster runserver ctags
-
-[test]
-recipe = zc.recipe.testrunner
-eggs = keas.kmi [test]
-
-[coverage-test]
-recipe = zc.recipe.testrunner
-eggs = keas.kmi [test]
-defaults = ['--coverage', '../../coverage']
-
-[coverage-report]
-recipe = zc.recipe.egg
-eggs = z3c.coverage
-scripts = coverage=coverage-report
-arguments = ('coverage', 'coverage/report')
-
-[python]
-recipe = zc.recipe.egg
-eggs = keas.kmi
-interpreter = python
-
-[ctags]
-recipe = z3c.recipe.tag:tags
-eggs = keas.kmi
-
-[paster]
-recipe = zc.recipe.egg
-eggs = Paste
-       PasteScript
-       PasteDeploy
-       pyOpenSSL
-       zope.app.component
-       zope.app.publication
-       zope.app.publisher
-       zope.app.security
-       zope.component
-       zope.error
-       zope.publisher
-       zope.securitypolicy
-       keas.kmi
-
-[runserver]
-recipe = zc.recipe.egg
-eggs = ${paster:eggs}
-scripts = paster=runserver
-arguments = ['serve', 'server.ini']

Copied: keas.kmi/tags/0.2.0/buildout.cfg (from rev 90834, keas.kmi/trunk/buildout.cfg)
===================================================================
--- keas.kmi/tags/0.2.0/buildout.cfg	                        (rev 0)
+++ keas.kmi/tags/0.2.0/buildout.cfg	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,53 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report python paster runserver testclient ctags
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = keas.kmi [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = keas.kmi [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
+
+[python]
+recipe = zc.recipe.egg
+eggs = keas.kmi
+interpreter = python
+
+[ctags]
+recipe = z3c.recipe.tag:tags
+eggs = keas.kmi
+
+[paster]
+recipe = zc.recipe.egg
+eggs = Paste
+       PasteScript
+       PasteDeploy
+       pyOpenSSL
+       zope.app.component
+       zope.app.publication
+       zope.app.publisher
+       zope.app.security
+       zope.component
+       zope.error
+       zope.publisher
+       zope.securitypolicy
+       keas.kmi
+
+[runserver]
+recipe = zc.recipe.egg
+eggs = ${paster:eggs}
+scripts = paster=runserver
+arguments = ['serve', 'server.ini']
+
+[testclient]
+recipe = zc.recipe.egg
+eggs = keas.kmi

Deleted: keas.kmi/tags/0.2.0/setup.py
===================================================================
--- keas.kmi/trunk/setup.py	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/setup.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,75 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""
-$Id$
-"""
-import os
-from setuptools import setup, find_packages
-
-def read(*rnames):
-    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-setup (
-    name='keas.kmi',
-    version='0.2.0dev',
-    author = "Stephan Richter and the Zope Community",
-    author_email = "zope-dev at zope.org",
-    description = "A Key Management Infrastructure",
-    long_description=(
-        read('README.txt')
-        + '\n\n' +
-        read('src', 'keas', 'kmi', 'README.txt')
-        + '\n\n' +
-        read('src', 'keas', 'kmi', 'persistent.txt')
-        + '\n\n' +
-        read('CHANGES.txt')
-        ),
-    license = "ZPL 2.1",
-    keywords = "zope3 security key management infrastructure nist 800-57",
-    classifiers = [
-        'Development Status :: 4 - Beta',
-        'Environment :: Web Environment',
-        'Intended Audience :: Developers',
-        'License :: OSI Approved :: Zope Public License',
-        'Programming Language :: Python',
-        'Natural Language :: English',
-        'Operating System :: OS Independent',
-        'Topic :: Internet :: WWW/HTTP',
-        'Framework :: Zope3'],
-    url = 'http://pypi.python.org/pypi/keas.kmi',
-    packages = find_packages('src'),
-    include_package_data = True,
-    package_dir = {'':'src'},
-    namespace_packages = ['keas'],
-    extras_require = dict(
-        test = [
-            'zope.testing',
-            'zope.app.testing',
-            ],
-        ),
-    install_requires = [
-        'M2Crypto==0.18',
-        'setuptools',
-        'z3c.rest',
-        'zope.component',
-        'zope.interface',
-        'zope.schema',
-        'ZODB3',
-        ],
-    zip_safe = False,
-    entry_points = """
-    [paste.app_factory]
-    main = keas.kmi.wsgi:application_factory
-    """,
-)

Copied: keas.kmi/tags/0.2.0/setup.py (from rev 90844, keas.kmi/trunk/setup.py)
===================================================================
--- keas.kmi/tags/0.2.0/setup.py	                        (rev 0)
+++ keas.kmi/tags/0.2.0/setup.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+    name='keas.kmi',
+    version='0.2.0',
+    author = "Stephan Richter and the Zope Community",
+    author_email = "zope-dev at zope.org",
+    description = "A Key Management Infrastructure",
+    long_description=(
+        read('README.txt')
+        + '\n\n' +
+        read('src', 'keas', 'kmi', 'README.txt')
+        + '\n\n' +
+        read('src', 'keas', 'kmi', 'persistent.txt')
+        + '\n\n' +
+        read('CHANGES.txt')
+        ),
+    license = "ZPL 2.1",
+    keywords = "zope3 security key management infrastructure nist 800-57",
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+    url = 'http://pypi.python.org/pypi/keas.kmi',
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['keas'],
+    extras_require = dict(
+        test = [
+            'zope.testing',
+            'zope.app.testing',
+            ],
+        ),
+    install_requires = [
+        'M2Crypto==0.18',
+        'setuptools',
+        'z3c.rest',
+        'zope.component',
+        'zope.interface',
+        'zope.schema',
+        'ZODB3',
+        ],
+    zip_safe = False,
+    entry_points = """
+    [console_scripts]
+    testclient = keas.kmi.testclient:main
+
+    [paste.app_factory]
+    main = keas.kmi.wsgi:application_factory
+    """,
+)

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
===================================================================
--- keas.kmi/trunk/src/keas/kmi/README.txt	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/README.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,300 +0,0 @@
-=============================
-Key Management Infrastructure
-=============================
-
-This package provides a NIST SP 800-57 compliant key management
-infrastructure. Part of this infrastructure is a key management facility that
-provides several services related to keys.
-
-  >>> from keas.kmi import facility
-  >>> keys = facility.KeyManagementFacility()
-  >>> keys
-  <KeyManagementFacility (0)>
-
-  >>> from zope.interface import verify
-  >>> from keas.kmi import interfaces
-  >>> verify.verifyObject(interfaces.IKeyManagementFacility, keys)
-  True
-
-One of the services the facility provides in the generation of new keys.
-
-  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
-  True
-
-The algorithm to generate a new pair of keys is somewhat involved. The
-following features are required:
-
-(A) The key local to the data cannot be directly used as the encrypting key.
-
-(B) The encrypting key must be stored using a cipher that is at least as
-    strong as the key itself.
-
-(C) The computer storing the data cannot also store the key.
-
-This suggests the following algorithm to generate and store a new encrypting
-key:
-
-1. Create the key encrypting key (private and public).
-
-2. Create the encryption key.
-
-3. Use the public key encrypting key to encrypt both the encryption keys.
-
-4. Discard the public key encrypting key. It is important that this key is
-   never stored anywhere.
-
-5. Store the encrypted encryption key in the key management facility.
-
-6. Return the private key encrypting key.
-
-Let's now use the key generation service's API to generate a key.
-
-  >>> key = keys.generate()
-  >>> print key
-  -----BEGIN RSA PRIVATE KEY-----
-  ...
-  -----END RSA PRIVATE KEY-----
-
-By default the system uses the AES 256 cipher, because public commentary
-suggests that the AES 196 or AES 256 cipher sufficiently fulfill the PCI,
-HIPAA and NIST key strength requirement.
-
-You can now use this key encrypting key to extract the encryption keys:
-
-  >>> import md5
-  >>> hash = md5.new()
-  >>> hash.update(key)
-
-  >>> keys.get(hash.hexdigest())
-  <Key ...>
-
-Our key management facility also supports the encryption service, which allows
-you to encrypt and decrypt a string given the key encrypting key.
-
-  >>> verify.verifyObject(interfaces.IEncryptionService, keys)
-  True
-
-Let's now encrypt some data:
-
-  >>> encrypted = keys.encrypt(key, 'Stephan Richter')
-  >>> len(encrypted)
-  16
-
-We can also decrypt the data.
-
-  >>> keys.decrypt(key, encrypted)
-  'Stephan Richter'
-
-And that's pretty much all there is to it. Most of the complicated
-crypto-related work happens under the hood, transparent to the user.
-
-
-The Local Key Management Facility
----------------------------------
-
-However, using the master key management facility's encryption service is
-expensive, since each encryption and decryption request would require a
-network request. Fortunately, we can
-
-(A) communicate encryption keys across multiple devices, and
-
-(B) keep encryption keys in memory.
-
-It is only required that the data transfer is completed via an encrypted
-communication channel. In this implementation the communication protocol is
-HTTP and thus a sufficiently strong SSL connection is appropriate.
-
-Let's now instantiate the local key management facility:
-
-  >>> localKeys = facility.LocalKeyManagementFacility('http://localhost/keys')
-  >>> localKeys
-  <LocalKeyManagementFacility 'http://localhost/keys'>
-
-The argument to the constructor is the URL to the master key management
-facility. The local facility will use a small REST API to communicate with the
-server.
-
-For the purpose of this test, we are going to install a network component that
-only simulates the requests:
-
-  >>> from keas.kmi import testing
-  >>> testing.setupRestApi(localKeys, keys)
-
-As with the master facility, the local facility provides the
-``IEncryptionService`` interface:
-
-  >>> verify.verifyObject(interfaces.IEncryptionService, localKeys)
-  True
-
-So en- and decryption is very easy to do:
-
-  >>> encrypted = localKeys.encrypt(key, 'Stephan Richter')
-  >>> len(encrypted)
-  16
-
-  >>> localKeys.decrypt(key, encrypted)
-  'Stephan Richter'
-
-Instead of forwarding the en- an decryption request to the master facility,
-the local facility merely fetches the encryption key pair and executes the
-operation locally. This approach has the following advantages:
-
-(A) There is no general network latency for any en- and decryption call.
-
-(B) The expensive task of en- and decrypting a message is delegated to
-    multiple servers, allowing better scaling.
-
-(C) Fetched keys can be cached locally decreasing the network calls to a once
-    in a while process.
-
-In this implementation, we do cache the keys in a private attribute:
-
-  >>> key in localKeys._cache
-  True
-
-A timeout (in seconds) controls when a key must be refetched:
-
-  >>> localKeys.timeout
-  3600
-
-Let's now force a reload by setting the timeout to zero:
-
-  >>> localKeys.timeout = 0
-
-The cache is a dictionary of key encrypting key to a 3-tuple that contains the
-date/time the key has been fetched, the encryption (public) key, and the
-decryption (private) key.
-
-  >>> firstTime = localKeys._cache[key][0]
-
-  >>> localKeys.decrypt(key, encrypted)
-  'Stephan Richter'
-
-  >>> secondTime = localKeys._cache[key][0]
-
-  >>> firstTime < secondTime
-  True
-
-The local facility also provides the ``IKeyGenerationService`` interface:
-
-  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
-  True
-
-The local method call is identical to the master one:
-
-  >>> key2 = localKeys.generate()
-  >>> print key2
-  -----BEGIN RSA PRIVATE KEY-----
-  ...
-  -----END RSA PRIVATE KEY-----
-
-The operation is forwarded to the master server, so that the key is available
-there as well:
-
-  >>> hash = md5.new()
-  >>> hash.update(key2)
-
-  >>> hash.hexdigest() in keys
-  True
-
-
-The REST API
-------------
-
-The REST API of the master key management facility defines the communication
-with the local facility. When a new encryption key pair is created, we simply
-make a `POST` call to the following URL::
-
-  http://server:port/master/new
-
-The request should have no body and the response is simply the key encrypting
-key.
-
-So let's have a look at the call:
-
-  >>> from keas.kmi import rest
-  >>> from zope.publisher.browser import TestRequest
-
-  >>> request = TestRequest()
-  >>> request.method = 'GET'
-
-  >>> newCall = rest.NewView(keys, request)
-  >>> key3 = newCall()
-  >>> print key3
-  -----BEGIN RSA PRIVATE KEY-----
-  ...
-  -----END RSA PRIVATE KEY-----
-
-You can also use post for the new key:
-
-  >>> request.method = 'POST'
-  >>> print newCall()
-  -----BEGIN RSA PRIVATE KEY-----
-  ...
-  -----END RSA PRIVATE KEY-----
-
-The key is available in the facility of course:
-
-  >>> hash = md5.new()
-  >>> hash.update(key3)
-
-  >>> hash.hexdigest() in keys
-  True
-
-We can now fetch the encryption key pair using a `POST` call to this URL::
-
-  http://server:port/master/key
-
-The request sends the key encrypting key in its body. The response is the
-encryption key string:
-
-  >>> import cStringIO
-  >>> io = cStringIO.StringIO(key3)
-
-  >>> request = TestRequest(io)
-  >>> request.method = 'POST'
-
-  >>> keyCall = rest.KeyView(keys, request)
-  >>> encKey = keyCall()
-  >>> len(encKey)
-  32
-
-
-The Testing Key Management Facility
------------------------------------
-
-The testing facility only manages a single key that is always constant. This
-allows you to install a testing facility globally, not storing the keys in the
-database and still reuse a ZODB over multiple sessions.
-
-  >>> testingKeys = testing.TestingKeyManagementFacility()
-
-Of course, the key generation service is supported:
-
-  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
-  True
-
-However, you will always receive the same key:
-
-  >>> def getKeySegment(key):
-  ...     return key.split('\n')[1]
-
-  >>> getKeySegment(testingKeys.generate())
-  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
-  >>> getKeySegment(testingKeys.generate())
-  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
-  >>> testingKeys = testing.TestingKeyManagementFacility()
-  >>> getKeySegment(testingKeys.generate())
-  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
-
-All other methods remain the same:
-
-  >>> key = testingKeys.generate()
-  >>> testingKeys.getEncryptionKey(key)
-  '_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaa...'
-
-We can also safely en- and decrypt:
-
-  >>> encrypted = testingKeys.encrypt(key, 'Stephan Richter')
-  >>> testingKeys.decrypt(key, encrypted)
-  'Stephan Richter'

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/README.txt (from rev 90843, keas.kmi/trunk/src/keas/kmi/README.txt)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/README.txt	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/README.txt	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,316 @@
+=============================
+Key Management Infrastructure
+=============================
+
+This package provides a NIST SP 800-57 compliant key management
+infrastructure. Part of this infrastructure is a key management facility that
+provides several services related to keys.
+
+  >>> from keas.kmi import facility
+  >>> keys = facility.KeyManagementFacility()
+  >>> keys
+  <KeyManagementFacility (0)>
+
+  >>> from zope.interface import verify
+  >>> from keas.kmi import interfaces
+  >>> verify.verifyObject(interfaces.IKeyManagementFacility, keys)
+  True
+
+One of the services the facility provides in the generation of new keys.
+
+  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+  True
+
+The algorithm to generate a new pair of keys is somewhat involved. The
+following features are required:
+
+(A) The key local to the data cannot be directly used as the encrypting key.
+
+(B) The encrypting key must be stored using a cipher that is at least as
+    strong as the key itself.
+
+(C) The computer storing the data cannot also store the key.
+
+This suggests the following algorithm to generate and store a new encrypting
+key:
+
+1. Create the key encrypting key (private and public).
+
+2. Create the encryption key.
+
+3. Use the public key encrypting key to encrypt both the encryption keys.
+
+4. Discard the public key encrypting key. It is important that this key is
+   never stored anywhere.
+
+5. Store the encrypted encryption key in the key management facility.
+
+6. Return the private key encrypting key.
+
+Let's now use the key generation service's API to generate a key.
+
+  >>> key = keys.generate()
+  >>> print key
+  -----BEGIN RSA PRIVATE KEY-----
+  ...
+  -----END RSA PRIVATE KEY-----
+
+By default the system uses the AES 256 cipher, because public commentary
+suggests that the AES 196 or AES 256 cipher sufficiently fulfill the PCI,
+HIPAA and NIST key strength requirement.
+
+You can now use this key encrypting key to extract the encryption keys:
+
+  >>> import md5
+  >>> hash = md5.new()
+  >>> hash.update(key)
+
+  >>> keys.get(hash.hexdigest())
+  <Key ...>
+
+Our key management facility also supports the encryption service, which allows
+you to encrypt and decrypt a string given the key encrypting key.
+
+  >>> verify.verifyObject(interfaces.IEncryptionService, keys)
+  True
+
+Let's now encrypt some data:
+
+  >>> encrypted = keys.encrypt(key, 'Stephan Richter')
+  >>> len(encrypted)
+  16
+
+We can also decrypt the data.
+
+  >>> keys.decrypt(key, encrypted)
+  'Stephan Richter'
+
+And that's pretty much all there is to it. Most of the complicated
+crypto-related work happens under the hood, transparent to the user.
+
+
+The Local Key Management Facility
+---------------------------------
+
+However, using the master key management facility's encryption service is
+expensive, since each encryption and decryption request would require a
+network request. Fortunately, we can
+
+(A) communicate encryption keys across multiple devices, and
+
+(B) keep encryption keys in memory.
+
+It is only required that the data transfer is completed via an encrypted
+communication channel. In this implementation the communication protocol is
+HTTP and thus a sufficiently strong SSL connection is appropriate.
+
+Let's now instantiate the local key management facility:
+
+  >>> localKeys = facility.LocalKeyManagementFacility('http://localhost/keys')
+  >>> localKeys
+  <LocalKeyManagementFacility 'http://localhost/keys'>
+
+The argument to the constructor is the URL to the master key management
+facility. The local facility will use a small REST API to communicate with the
+server.
+
+For the purpose of this test, we are going to install a network component that
+only simulates the requests:
+
+  >>> from keas.kmi import testing
+  >>> testing.setupRestApi(localKeys, keys)
+
+As with the master facility, the local facility provides the
+``IEncryptionService`` interface:
+
+  >>> verify.verifyObject(interfaces.IEncryptionService, localKeys)
+  True
+
+So en- and decryption is very easy to do:
+
+  >>> encrypted = localKeys.encrypt(key, 'Stephan Richter')
+  >>> len(encrypted)
+  16
+
+  >>> localKeys.decrypt(key, encrypted)
+  'Stephan Richter'
+
+Instead of forwarding the en- an decryption request to the master facility,
+the local facility merely fetches the encryption key pair and executes the
+operation locally. This approach has the following advantages:
+
+(A) There is no general network latency for any en- and decryption call.
+
+(B) The expensive task of en- and decrypting a message is delegated to
+    multiple servers, allowing better scaling.
+
+(C) Fetched keys can be cached locally decreasing the network calls to a once
+    in a while process.
+
+In this implementation, we do cache the keys in a private attribute:
+
+  >>> key in localKeys._cache
+  True
+
+A timeout (in seconds) controls when a key must be refetched:
+
+  >>> localKeys.timeout
+  3600
+
+Let's now force a reload by setting the timeout to zero:
+
+  >>> localKeys.timeout = 0
+
+The cache is a dictionary of key encrypting key to a 3-tuple that contains the
+date/time the key has been fetched, the encryption (public) key, and the
+decryption (private) key.
+
+  >>> firstTime = localKeys._cache[key][0]
+
+  >>> localKeys.decrypt(key, encrypted)
+  'Stephan Richter'
+
+  >>> secondTime = localKeys._cache[key][0]
+
+  >>> firstTime < secondTime
+  True
+
+The local facility also provides the ``IKeyGenerationService`` interface:
+
+  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+  True
+
+The local method call is identical to the master one:
+
+  >>> key2 = localKeys.generate()
+  >>> print key2
+  -----BEGIN RSA PRIVATE KEY-----
+  ...
+  -----END RSA PRIVATE KEY-----
+
+The operation is forwarded to the master server, so that the key is available
+there as well:
+
+  >>> hash = md5.new()
+  >>> hash.update(key2)
+
+  >>> hash.hexdigest() in keys
+  True
+
+
+The REST API
+------------
+
+The REST API of the master key management facility defines the communication
+with the local facility. When a new encryption key pair is created, we simply
+make a `POST` call to the following URL::
+
+  http://server:port/new
+
+The request should have no body and the response is simply the key encrypting
+key.
+
+So let's have a look at the call:
+
+  >>> from keas.kmi import rest
+  >>> from zope.publisher.browser import TestRequest
+
+  >>> request = TestRequest()
+  >>> request.method = 'POST'
+
+  >>> newCall = rest.NewView(keys, request)
+  >>> key3 = newCall()
+  >>> print key3
+  -----BEGIN RSA PRIVATE KEY-----
+  ...
+  -----END RSA PRIVATE KEY-----
+
+The key is available in the facility of course:
+
+  >>> hash = md5.new()
+  >>> hash.update(key3)
+
+  >>> hash.hexdigest() in keys
+  True
+
+We can now fetch the encryption key pair using a `POST` call to this URL::
+
+  http://server:port/key
+
+The request sends the key encrypting key in its body. The response is the
+encryption key string:
+
+  >>> import cStringIO
+  >>> io = cStringIO.StringIO(key3)
+
+  >>> request = TestRequest(io)
+  >>> request.method = 'POST'
+
+  >>> keyCall = rest.KeyView(keys, request)
+  >>> encKey = keyCall()
+  >>> len(encKey)
+  32
+
+If you try to request a nonexistent key, you get a 404 error:
+encryption key string:
+
+  >>> import cStringIO
+  >>> io = cStringIO.StringIO('xyzzy')
+
+  >>> request = TestRequest(io)
+  >>> request.method = 'POST'
+
+  >>> keyCall = rest.KeyView(keys, request)
+  >>> print keyCall()
+  Key not found
+  >>> request.response.getStatus()
+  404
+
+A `GET` request to the root shows us a server status page
+
+  >>> request = TestRequest()
+  >>> request.method = 'GET'
+
+  >>> newCall = rest.StatusView(keys, request)
+  >>> print newCall()
+  KMS server holding 3 keys
+
+
+The Testing Key Management Facility
+-----------------------------------
+
+The testing facility only manages a single key that is always constant. This
+allows you to install a testing facility globally, not storing the keys in the
+database and still reuse a ZODB over multiple sessions.
+
+  >>> testingKeys = testing.TestingKeyManagementFacility()
+
+Of course, the key generation service is supported:
+
+  >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+  True
+
+However, you will always receive the same key:
+
+  >>> def getKeySegment(key):
+  ...     return key.split('\n')[1]
+
+  >>> getKeySegment(testingKeys.generate())
+  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+  >>> getKeySegment(testingKeys.generate())
+  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+  >>> testingKeys = testing.TestingKeyManagementFacility()
+  >>> getKeySegment(testingKeys.generate())
+  'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+
+All other methods remain the same:
+
+  >>> key = testingKeys.generate()
+  >>> testingKeys.getEncryptionKey(key)
+  '_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaa...'
+
+We can also safely en- and decrypt:
+
+  >>> encrypted = testingKeys.encrypt(key, 'Stephan Richter')
+  >>> testingKeys.decrypt(key, encrypted)
+  'Stephan Richter'

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/configure.zcml	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,35 +0,0 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope"
-    i18n_domain="keas">
-
-  <include file="rest.zcml" />
-  <include file="security.zcml" />
-
-  <!-- We purposefully keep the access to the facility very limited -->
-  <class class=".facility.KeyManagementFacility">
-    <require
-	permission="keas.kmi.Encrypt"
-	attributes="encrypt decrypt"
-	/>
-    <require
-	permission="keas.kmi.GenerateKey"
-	attributes="generate"
-	/>
-    <require
-	permission="keas.kmi.AccessKey"
-	attributes="getEncryptionKey"
-	/>
-  </class>
-
-  <class class=".facility.LocalKeyManagementFacility">
-    <require
-	permission="keas.kmi.Encrypt"
-	attributes="encrypt decrypt"
-	/>
-    <require
-	permission="keas.kmi.GenerateKey"
-	attributes="generate"
-	/>
-  </class>
-
-</configure>

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml (from rev 90839, keas.kmi/trunk/src/keas/kmi/configure.zcml)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,35 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <include file="rest.zcml" />
+  <include file="security.zcml" />
+
+  <!-- We purposefully keep the access to the facility very limited -->
+  <class class=".facility.KeyManagementFacility">
+    <require
+        permission="keas.kmi.Encrypt"
+        attributes="encrypt decrypt"
+        />
+    <require
+        permission="keas.kmi.GenerateKey"
+        attributes="generate"
+        />
+    <require
+        permission="keas.kmi.AccessKey"
+        attributes="getEncryptionKey __len__"
+        />
+  </class>
+
+  <class class=".facility.LocalKeyManagementFacility">
+    <require
+        permission="keas.kmi.Encrypt"
+        attributes="encrypt decrypt"
+        />
+    <require
+        permission="keas.kmi.GenerateKey"
+        attributes="generate"
+        />
+  </class>
+
+</configure>

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/facility.py	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/facility.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,163 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Implementation of Key Management Facility
-
-$Id$
-"""
-from __future__ import absolute_import
-__docformat__ = "reStructuredText"
-import M2Crypto
-import datetime
-import md5
-import persistent
-import time
-import zope.interface
-import zope.location
-from z3c.rest import client
-from zope.annotation.interfaces import IAttributeAnnotatable
-from zope.app.container import btree
-from zope.dublincore import property
-from zope.schema.fieldproperty import FieldProperty
-from keas.kmi import interfaces
-
-class Key(zope.location.Location, persistent.Persistent):
-    zope.interface.implements(interfaces.IKey, IAttributeAnnotatable)
-
-    created = property.DCProperty('created')
-    creator = property.DCProperty('creator')
-    key = FieldProperty(interfaces.IKey['key'])
-
-    def __init__(self, key):
-        self.key = key
-
-    def __repr__(self):
-        return '<%s %r>' %(self.__class__.__name__, self.key)
-
-
-class EncryptionService(object):
-
-    cipher = 'aes_256_cbc'
-
-    # Note: decryption fails if you use an empty initialization vector; but it
-    # only fails when you restart the Python process.  The length of the
-    # initialization vector is assumed to be 16 bytes because that's what
-    #   openssl aes-256-cbc -nosalt -P -k 'a'
-    # prints if you execute it on the command line
-    initializationVector = '0123456789ABCDEF'
-
-    def encrypt(self, key, data):
-        """See interfaces.IEncryptionService"""
-        # 1. Extract the encryption key
-        encryptionKey = self.getEncryptionKey(key)
-        # 2. Create a cipher object
-        cipher = M2Crypto.EVP.Cipher(
-            self.cipher, encryptionKey, self.initializationVector, 1)
-        # 3. Feed the data to the cipher
-        encrypted = cipher.update(data)
-        encrypted += cipher.final()
-        # 4. Return encrypted data
-        return encrypted
-
-    def decrypt(self, key, data):
-        """See interfaces.IEncryptionService"""
-        # 1. Extract the encryption key
-        encryptionKey = self.getEncryptionKey(key)
-        # 2. Create a cipher object
-        cipher = M2Crypto.EVP.Cipher(
-            self.cipher, encryptionKey, self.initializationVector, 0)
-        # 3. Feed the data to the cipher
-        decrypted = cipher.update(data)
-        decrypted += cipher.final()
-        # 4. Return encrypted data
-        return decrypted
-
-
-class KeyManagementFacility(EncryptionService, btree.BTreeContainer):
-    zope.interface.implements(interfaces.IExtendedKeyManagementFacility)
-
-    rsaKeyLength = 512 # The length of the key encrypting key
-    rsaKeyExponent = 161 # Should be sufficiently high and non-symmetric
-    rsaPassphrase = 'key management facility'
-    rsaPadding = M2Crypto.RSA.pkcs1_padding
-
-    keyLength = rsaKeyLength/16
-
-    def generate(self):
-        """See interfaces.IKeyGenerationService"""
-        # 1. Generate the private/public RSA key encrypting key
-        rsa = M2Crypto.RSA.gen_key(
-            self.rsaKeyLength, self.rsaKeyExponent,
-            lambda x: None)
-        # 2. Extract the private key from the RSA object
-        buf = M2Crypto.BIO.MemoryBuffer('')
-        rsa.save_key_bio(buf, None, lambda x: self.rsaPassphrase)
-        privateKey = buf.getvalue()
-        # 3. Generate the encryption key
-        key = M2Crypto.Rand.rand_bytes(self.keyLength)
-        # 4. Create the lookup key in the container
-        hash = md5.new()
-        hash.update(privateKey)
-        # 5. Save the encryption key
-        encryptedKey = rsa.public_encrypt(key, self.rsaPadding)
-        self[hash.hexdigest()] = Key(encryptedKey)
-        # 6. Return the private key encrypting key
-        return privateKey
-
-    def getEncryptionKey(self, key):
-        """Given the key encrypting key, get the encryption key."""
-        # 1. Create the lookup key in the container
-        hash = md5.new()
-        hash.update(key)
-        # 2. Extract the encrypted encryption key
-        encryptedKey = self[hash.hexdigest()].key
-        # 3. Decrypt the key.
-        rsa = M2Crypto.RSA.load_key_string(key)
-        decryptedKey = rsa.private_decrypt(encryptedKey, self.rsaPadding)
-        # 4. Return decrypted encryption key
-        return decryptedKey
-
-    def __repr__(self):
-        return '<%s (%i)>' %(self.__class__.__name__, len(self))
-
-
-class LocalKeyManagementFacility(EncryptionService):
-    """A local facility that requests keys from the master facility."""
-    zope.interface.implements(interfaces.IKeyManagementFacility)
-
-    timeout = 3600
-    clientClass = client.RESTClient
-
-    def __init__(self, url):
-        self.url = url
-        self._cache = {}
-
-    def generate(self):
-        """See interfaces.IKeyGenerationService"""
-        client = self.clientClass(self.url)
-        client.POST('/new')
-        return client.contents
-
-    def getEncryptionKey(self, key):
-        """Given the key encrypting key, get the encryption key."""
-        if (key in self._cache and
-            self._cache[key][0] + self.timeout > time.time()):
-            return self._cache[key][1]
-        client = self.clientClass(self.url)
-        client.POST('/key', key)
-        encryptionKey = client.contents
-        self._cache[key] = (time.time(), encryptionKey)
-        return encryptionKey
-
-    def __repr__(self):
-        return '<%s %r>' %(self.__class__.__name__, self.url)

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/facility.py (from rev 90842, keas.kmi/trunk/src/keas/kmi/facility.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/facility.py	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/facility.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,163 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Implementation of Key Management Facility
+
+$Id$
+"""
+from __future__ import absolute_import
+__docformat__ = "reStructuredText"
+import M2Crypto
+import datetime
+import md5
+import persistent
+import time
+import zope.interface
+import zope.location
+from z3c.rest import client
+from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.app.container import btree
+from zope.dublincore import property
+from zope.schema.fieldproperty import FieldProperty
+from keas.kmi import interfaces
+
+class Key(zope.location.Location, persistent.Persistent):
+    zope.interface.implements(interfaces.IKey, IAttributeAnnotatable)
+
+    created = property.DCProperty('created')
+    creator = property.DCProperty('creator')
+    key = FieldProperty(interfaces.IKey['key'])
+
+    def __init__(self, key):
+        self.key = key
+
+    def __repr__(self):
+        return '<%s %r>' %(self.__class__.__name__, self.key)
+
+
+class EncryptionService(object):
+
+    cipher = 'aes_256_cbc'
+
+    # Note: decryption fails if you use an empty initialization vector; but it
+    # only fails when you restart the Python process.  The length of the
+    # initialization vector is assumed to be 16 bytes because that's what
+    #   openssl aes-256-cbc -nosalt -P -k 'a'
+    # prints if you execute it on the command line
+    initializationVector = '0123456789ABCDEF'
+
+    def encrypt(self, key, data):
+        """See interfaces.IEncryptionService"""
+        # 1. Extract the encryption key
+        encryptionKey = self.getEncryptionKey(key)
+        # 2. Create a cipher object
+        cipher = M2Crypto.EVP.Cipher(
+            self.cipher, encryptionKey, self.initializationVector, 1)
+        # 3. Feed the data to the cipher
+        encrypted = cipher.update(data)
+        encrypted += cipher.final()
+        # 4. Return encrypted data
+        return encrypted
+
+    def decrypt(self, key, data):
+        """See interfaces.IEncryptionService"""
+        # 1. Extract the encryption key
+        encryptionKey = self.getEncryptionKey(key)
+        # 2. Create a cipher object
+        cipher = M2Crypto.EVP.Cipher(
+            self.cipher, encryptionKey, self.initializationVector, 0)
+        # 3. Feed the data to the cipher
+        decrypted = cipher.update(data)
+        decrypted += cipher.final()
+        # 4. Return encrypted data
+        return decrypted
+
+
+class KeyManagementFacility(EncryptionService, btree.BTreeContainer):
+    zope.interface.implements(interfaces.IExtendedKeyManagementFacility)
+
+    rsaKeyLength = 512 # The length of the key encrypting key
+    rsaKeyExponent = 161 # Should be sufficiently high and non-symmetric
+    rsaPassphrase = 'key management facility'
+    rsaPadding = M2Crypto.RSA.pkcs1_padding
+
+    keyLength = rsaKeyLength/16
+
+    def generate(self):
+        """See interfaces.IKeyGenerationService"""
+        # 1. Generate the private/public RSA key encrypting key
+        rsa = M2Crypto.RSA.gen_key(
+            self.rsaKeyLength, self.rsaKeyExponent,
+            lambda x: None)
+        # 2. Extract the private key from the RSA object
+        buf = M2Crypto.BIO.MemoryBuffer('')
+        rsa.save_key_bio(buf, None, lambda x: self.rsaPassphrase)
+        privateKey = buf.getvalue()
+        # 3. Generate the encryption key
+        key = M2Crypto.Rand.rand_bytes(self.keyLength)
+        # 4. Create the lookup key in the container
+        hash = md5.new()
+        hash.update(privateKey)
+        # 5. Save the encryption key
+        encryptedKey = rsa.public_encrypt(key, self.rsaPadding)
+        self[hash.hexdigest()] = Key(encryptedKey)
+        # 6. Return the private key encrypting key
+        return privateKey
+
+    def getEncryptionKey(self, key):
+        """Given the key encrypting key, get the encryption key."""
+        # 1. Create the lookup key in the container
+        hash = md5.new()
+        hash.update(key)
+        # 2. Extract the encrypted encryption key
+        encryptedKey = self[hash.hexdigest()].key
+        # 3. Decrypt the key.
+        rsa = M2Crypto.RSA.load_key_string(key)
+        decryptedKey = rsa.private_decrypt(encryptedKey, self.rsaPadding)
+        # 4. Return decrypted encryption key
+        return decryptedKey
+
+    def __repr__(self):
+        return '<%s (%i)>' %(self.__class__.__name__, len(self))
+
+
+class LocalKeyManagementFacility(EncryptionService):
+    """A local facility that requests keys from the master facility."""
+    zope.interface.implements(interfaces.IKeyManagementFacility)
+
+    timeout = 3600
+    clientClass = client.RESTClient
+
+    def __init__(self, url):
+        self.url = url
+        self._cache = {}
+
+    def generate(self):
+        """See interfaces.IKeyGenerationService"""
+        client = self.clientClass(self.url)
+        client.post('/new')
+        return client.contents
+
+    def getEncryptionKey(self, key):
+        """Given the key encrypting key, get the encryption key."""
+        if (key in self._cache and
+            self._cache[key][0] + self.timeout > time.time()):
+            return self._cache[key][1]
+        client = self.clientClass(self.url)
+        client.post('/key', key, headers={'content-type': 'text/plain'})
+        encryptionKey = client.contents
+        self._cache[key] = (time.time(), encryptionKey)
+        return encryptionKey
+
+    def __repr__(self):
+        return '<%s %r>' %(self.__class__.__name__, self.url)

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.py	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,44 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""REST-API to master key management facility
-
-$Id$
-"""
-from zope.publisher.browser import BrowserPage
-
-class RestView(BrowserPage):
-
-    def __call__(self):
-        method = self.request.method
-        if not hasattr(self, method):
-            self.request.response.setStatus(405)
-            return 'Method not allowed.'
-        return getattr(self, method)()
-
-class NewView(RestView):
-
-    def GET(self):
-        self.request.response.setHeader('content-type', 'text/plain')
-        return self.context.generate()
-
-    POST = GET
-
-class KeyView(RestView):
-
-    def POST(self):
-        stream = self.request.bodyStream.getCacheStream()
-        stream.seek(0)
-        key = stream.read()
-        self.request.response.setHeader('content-type', 'text/plain')
-        return self.context.getEncryptionKey(key)

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/rest.py (from rev 90843, keas.kmi/trunk/src/keas/kmi/rest.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/rest.py	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""REST-API to master key management facility
+
+$Id$
+"""
+from zope.publisher.browser import BrowserPage
+
+class RestView(BrowserPage):
+
+    def __call__(self):
+        method = self.request.method
+        if not hasattr(self, method):
+            self.request.response.setStatus(405)
+            return 'Method not allowed.'
+        return getattr(self, method)()
+
+class StatusView(RestView):
+
+    def GET(self):
+        self.request.response.setHeader('content-type', 'text/plain')
+        return 'KMS server holding %d keys' % len(self.context)
+
+class NewView(RestView):
+
+    def POST(self):
+        self.request.response.setHeader('content-type', 'text/plain')
+        return self.context.generate()
+
+class KeyView(RestView):
+
+    def POST(self):
+        stream = self.request.bodyStream.getCacheStream()
+        stream.seek(0)
+        key = stream.read()
+        self.request.response.setHeader('content-type', 'text/plain')
+        try:
+            return self.context.getEncryptionKey(key)
+        except KeyError:
+            self.request.response.setStatus(404)
+            return 'Key not found'
+

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.zcml	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,18 +0,0 @@
-<configure xmlns="http://namespaces.zope.org/zope"
-           xmlns:browser="http://namespaces.zope.org/browser">
-
-  <browser:page
-      for=".interfaces.IKeyManagementFacility"
-      name="new"
-      class=".rest.NewView"
-      permission="zope.Public"
-      />
-
-  <browser:page
-      for=".interfaces.IKeyManagementFacility"
-      name="key"
-      class=".rest.KeyView"
-      permission="zope.Public"
-      />
-
-</configure>

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml (from rev 90838, keas.kmi/trunk/src/keas/kmi/rest.zcml)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,30 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser">
+
+  <browser:defaultView
+      for=".interfaces.IKeyManagementFacility"
+      name="index.html"
+      />
+
+  <browser:page
+      for=".interfaces.IKeyManagementFacility"
+      name="index.html"
+      class=".rest.StatusView"
+      permission="zope.Public"
+      />
+
+  <browser:page
+      for=".interfaces.IKeyManagementFacility"
+      name="new"
+      class=".rest.NewView"
+      permission="zope.Public"
+      />
+
+  <browser:page
+      for=".interfaces.IKeyManagementFacility"
+      name="key"
+      class=".rest.KeyView"
+      permission="zope.Public"
+      />
+
+</configure>

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py (from rev 90835, keas.kmi/trunk/src/keas/kmi/testclient.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,124 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import sys
+import optparse
+import textwrap
+
+from keas.kmi.facility import LocalKeyManagementFacility
+
+
+def ping(kmf):
+    client = kmf.clientClass(kmf.url)
+    print client.fullStatus
+    print
+    print client.contents
+
+
+def new_key(kmf):
+    sys.stdout.write(kmf.generate())
+
+
+def read_kek(kekfile):
+    try:
+        return file(kekfile, 'rb').read()
+    except IOError, e:
+        print >> sys.stderr, "Could not read key encrypting key from %s" % kekfile
+        print >> sys.stderr, e
+        sys.exit(1)
+
+
+def read_data(filename=None):
+    if not filename:
+        return sys.stdin.read()
+    else:
+        try:
+            return file(filename, 'rb').read()
+        except IOError, e:
+            print >> sys.stderr, "Could not read %s" % filename
+            print >> sys.stderr, e
+            sys.exit(1)
+
+
+def get_key(kmf, kekfile):
+    key_encrypting_key = read_kek(kekfile)
+    key = kmf.getEncryptionKey(key_encrypting_key)
+    sys.stdout.write(key)
+
+
+def encrypt(kmf, kekfile, filename=None):
+    key_encrypting_key = read_kek(kekfile)
+    data = read_data(filename)
+    encrypted = kmf.encrypt(key_encrypting_key, data)
+    sys.stdout.write(encrypted)
+
+
+def decrypt(kmf, kekfile, filename=None):
+    key_encrypting_key = read_kek(kekfile)
+    data = read_data(filename)
+    decrypted = kmf.decrypt(key_encrypting_key, data)
+    sys.stdout.write(decrypted)
+
+
+def main():
+    parser = optparse.OptionParser(textwrap.dedent("""\
+                usage: %prog URL
+                            see if the server is alive
+
+                       %prog URL -n > key.txt
+                            generate a new key and key encrypting key
+
+                       %prog URL -e key.txt data.txt > encrypted.txt
+                            encrypt data
+
+                       %prog URL -d key.txt encrytped.txt > data.txt
+                            decrypt data
+
+                       %prog URL -g key.txt > secretkey.bin
+                            get the secret encryption key
+                """.rstrip()),
+                description="Client for a Key Management Server.")
+    parser.add_option('-n', '--new',
+                      help='generate a new key',
+                      action='store_const', dest='action',
+                      const=new_key)
+    parser.add_option('-g', '--get-key',
+                      help='get key',
+                      action='store_const', dest='action',
+                      const=get_key)
+    parser.add_option('-e', '--encrypt',
+                      help='encrypt data',
+                      action='store_const', dest='action',
+                      const=encrypt)
+    parser.add_option('-d', '--decrypt',
+                      help='decrypt data',
+                      action='store_const', dest='action',
+                      const=decrypt)
+    opts, args = parser.parse_args()
+    if not opts.action:
+        opts.action = ping
+    if not args:
+        parser.error('please specify the KMS server URL')
+
+    url = args.pop(0)
+    kmf = LocalKeyManagementFacility(url)
+
+    try:
+        opts.action(kmf, *args)
+    except TypeError:
+        parser.error('incorrect number of arguments')

Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/testing.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/testing.py	2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testing.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,83 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""
-$Id$
-"""
-import md5
-import cStringIO
-from zope.publisher import browser
-from zope.interface import implements
-from keas.kmi import facility, rest, interfaces
-
-KeyEncyptingKey = '''-----BEGIN RSA PRIVATE KEY-----
-MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9
-rQlU/x/NWxG0vvFCnrDn7VvQN+syb3+a0DMCAgChAkAzKw3lwPxw0VVccq1J7qeO
-4DXR1iEMIoWruiCyq0aLkHnQzrZpaHnd4w+JNKIGOVDEWItf3iZNMXkoqj2hoPmp
-AiEA5kWTkYrfH+glJUfV/GvU6jcPSNctcJCnqTfMjQU0KEUCIQDU/R3iz5Lojw1S
-R1v6C5gdY/mrQydegHVGFS/p276KFwIgDk124UnRb7IyAlDI6xteUVSDVZ4Pivd+
-fP6yEkylbQkCIDo1Ri4VvzRtDsVkmFdqhcucHhIu+UTCHN6uVjy7QmIpAiB7Gl9m
-F4a4UlXVivb82J7ew3ZABnFIC9Q+dHW7WeZhxg==
------END RSA PRIVATE KEY-----
-'''
-
-EncryptedEncryptionKey = (
-    '\xbc\x08\xdbo\x04\xe3\xc7G\x13\xd3\x86\x92\xfa\xe8i>,+\xda\xf8/B2]s\xd4'
-    '\x10}[\xfd\x19\x98\xb1\xfa*V~U\xdf\t\x02\x01\xa6\xa8\xae\x8b\x8cm\xd9n'
-    '\xd5\x83\xa1%k\x16lfuY\\q\x8c\x8b')
-
-class FakeRESTClient(object):
-
-    context = None
-
-    def __init__(self, url):
-        self.url = url
-
-    def POST(self, url, data=None):
-        io = cStringIO.StringIO(data) if data else None
-        request = browser.TestRequest(io)
-        request.method = 'POST'
-        if url == '/new':
-            klass = rest.NewView
-        elif url == '/key':
-            klass = rest.KeyView
-        else:
-            raise ValueError(url)
-
-        view = klass(self.context, request)
-        self.contents = view()
-
-
-def setupRestApi(localFacility, masterFacility):
-    MyFakeRESTClient = type(
-        'FakeRESTClient', (FakeRESTClient,), {'context': masterFacility})
-    localFacility.clientClass = MyFakeRESTClient
-
-
-class TestingKeyManagementFacility(facility.KeyManagementFacility):
-
-    def __init__(self):
-        super(TestingKeyManagementFacility, self).__init__()
-        hash = md5.new()
-        hash.update(KeyEncyptingKey)
-        md5Key = hash.hexdigest()
-        self[md5Key] = facility.Key(EncryptedEncryptionKey)
-
-    def generate(self):
-        return KeyEncyptingKey
-
-
-class TestingKeyHolder(object):
-    implements(interfaces.IKeyHolder)
-    key = KeyEncyptingKey
-

Copied: keas.kmi/tags/0.2.0/src/keas/kmi/testing.py (from rev 90842, keas.kmi/trunk/src/keas/kmi/testing.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/testing.py	                        (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testing.py	2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+import md5
+import cStringIO
+from zope.publisher import browser
+from zope.interface import implements
+from keas.kmi import facility, rest, interfaces
+
+KeyEncyptingKey = '''-----BEGIN RSA PRIVATE KEY-----
+MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9
+rQlU/x/NWxG0vvFCnrDn7VvQN+syb3+a0DMCAgChAkAzKw3lwPxw0VVccq1J7qeO
+4DXR1iEMIoWruiCyq0aLkHnQzrZpaHnd4w+JNKIGOVDEWItf3iZNMXkoqj2hoPmp
+AiEA5kWTkYrfH+glJUfV/GvU6jcPSNctcJCnqTfMjQU0KEUCIQDU/R3iz5Lojw1S
+R1v6C5gdY/mrQydegHVGFS/p276KFwIgDk124UnRb7IyAlDI6xteUVSDVZ4Pivd+
+fP6yEkylbQkCIDo1Ri4VvzRtDsVkmFdqhcucHhIu+UTCHN6uVjy7QmIpAiB7Gl9m
+F4a4UlXVivb82J7ew3ZABnFIC9Q+dHW7WeZhxg==
+-----END RSA PRIVATE KEY-----
+'''
+
+EncryptedEncryptionKey = (
+    '\xbc\x08\xdbo\x04\xe3\xc7G\x13\xd3\x86\x92\xfa\xe8i>,+\xda\xf8/B2]s\xd4'
+    '\x10}[\xfd\x19\x98\xb1\xfa*V~U\xdf\t\x02\x01\xa6\xa8\xae\x8b\x8cm\xd9n'
+    '\xd5\x83\xa1%k\x16lfuY\\q\x8c\x8b')
+
+class FakeRESTClient(object):
+
+    context = None
+
+    def __init__(self, url):
+        self.url = url
+
+    def post(self, url, data=None, headers={}):
+        io = cStringIO.StringIO(data) if data else None
+        request = browser.TestRequest(io)
+        request.method = 'POST'
+        if url == '/new':
+            klass = rest.NewView
+        elif url == '/key':
+            if headers.get('content-type') != 'text/plain':
+                # ensure we don't trip on
+                # http://trac.pythonpaste.org/pythonpaste/ticket/294
+                raise ValueError('bad content type')
+            klass = rest.KeyView
+        else:
+            raise ValueError(url)
+
+        view = klass(self.context, request)
+        self.contents = view()
+
+
+def setupRestApi(localFacility, masterFacility):
+    MyFakeRESTClient = type(
+        'FakeRESTClient', (FakeRESTClient,), {'context': masterFacility})
+    localFacility.clientClass = MyFakeRESTClient
+
+
+class TestingKeyManagementFacility(facility.KeyManagementFacility):
+
+    def __init__(self):
+        super(TestingKeyManagementFacility, self).__init__()
+        hash = md5.new()
+        hash.update(KeyEncyptingKey)
+        md5Key = hash.hexdigest()
+        self[md5Key] = facility.Key(EncryptedEncryptionKey)
+
+    def generate(self):
+        return KeyEncyptingKey
+
+
+class TestingKeyHolder(object):
+    implements(interfaces.IKeyHolder)
+    key = KeyEncyptingKey
+



More information about the Checkins mailing list