[Checkins] SVN: keas.kmi/ Initial import of key management infrastructure and encrypted

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Sep 4 01:59:33 EDT 2008


Log message for revision 90781:
  Initial import of key management infrastructure and encrypted 
  persistent support. The algorithm implemented by this package fulfills 
  US government guidelines for data encryption, including HIPAA.
  
  

Changed:
  A   keas.kmi/
  A   keas.kmi/keas.kmi.tgz
  A   keas.kmi/tags/
  A   keas.kmi/tags/08.07.29.1/
  A   keas.kmi/tags/08.07.29.1/CHANGES.txt
  A   keas.kmi/tags/08.07.29.1/README.txt
  A   keas.kmi/tags/08.07.29.1/bootstrap.py
  A   keas.kmi/tags/08.07.29.1/buildout.cfg
  A   keas.kmi/tags/08.07.29.1/setup.py
  A   keas.kmi/tags/08.07.29.1/src/
  A   keas.kmi/tags/08.07.29.1/src/keas/
  A   keas.kmi/tags/08.07.29.1/src/keas/__init__.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/README.txt
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/__init__.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/configure.zcml
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/db.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/facility.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/interfaces.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/rest.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/security.zcml
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.py
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.zcml
  A   keas.kmi/tags/08.07.29.1/src/keas/kmi/tests.py
  A   keas.kmi/trunk/
  A   keas.kmi/trunk/CHANGES.txt
  A   keas.kmi/trunk/README.txt
  A   keas.kmi/trunk/bootstrap.py
  A   keas.kmi/trunk/buildout.cfg
  A   keas.kmi/trunk/server.ini
  A   keas.kmi/trunk/setup.py
  A   keas.kmi/trunk/src/
  A   keas.kmi/trunk/src/keas/
  A   keas.kmi/trunk/src/keas/__init__.py
  A   keas.kmi/trunk/src/keas/kmi/
  A   keas.kmi/trunk/src/keas/kmi/README.txt
  A   keas.kmi/trunk/src/keas/kmi/__init__.py
  A   keas.kmi/trunk/src/keas/kmi/apidoc.zcml
  A   keas.kmi/trunk/src/keas/kmi/application.zcml
  A   keas.kmi/trunk/src/keas/kmi/configure.zcml
  A   keas.kmi/trunk/src/keas/kmi/db.py
  A   keas.kmi/trunk/src/keas/kmi/db.zcml
  A   keas.kmi/trunk/src/keas/kmi/facility.py
  A   keas.kmi/trunk/src/keas/kmi/interfaces.py
  A   keas.kmi/trunk/src/keas/kmi/persistent.py
  A   keas.kmi/trunk/src/keas/kmi/persistent.txt
  A   keas.kmi/trunk/src/keas/kmi/rest.py
  A   keas.kmi/trunk/src/keas/kmi/rest.zcml
  A   keas.kmi/trunk/src/keas/kmi/security.zcml
  A   keas.kmi/trunk/src/keas/kmi/testing.py
  A   keas.kmi/trunk/src/keas/kmi/testing.zcml
  A   keas.kmi/trunk/src/keas/kmi/tests.py
  A   keas.kmi/trunk/src/keas/kmi/wsgi.py
  A   keas.kmi/trunk/var/
  A   keas.kmi/trunk/zope.conf

-=-
Added: keas.kmi/keas.kmi.tgz
===================================================================
(Binary files differ)


Property changes on: keas.kmi/keas.kmi.tgz
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: keas.kmi/tags/08.07.29.1/CHANGES.txt
===================================================================
--- keas.kmi/tags/08.07.29.1/CHANGES.txt	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/CHANGES.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,9 @@
+=======
+CHANGES
+=======
+
+
+0.1.0 (2008-??-??)
+------------------
+
+- Initial Release


Property changes on: keas.kmi/tags/08.07.29.1/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/README.txt
===================================================================
--- keas.kmi/tags/08.07.29.1/README.txt	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/README.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,2 @@
+This package provides a NIST SP 800-57 compliant Key Management Infrastructure
+(KMI).


Property changes on: keas.kmi/tags/08.07.29.1/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/bootstrap.py
===================================================================
--- keas.kmi/tags/08.07.29.1/bootstrap.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/bootstrap.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: keas.kmi/tags/08.07.29.1/bootstrap.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/buildout.cfg
===================================================================
--- keas.kmi/tags/08.07.29.1/buildout.cfg	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/buildout.cfg	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,27 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report python
+
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = keas.kmi [test]
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+
+[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

Added: keas.kmi/tags/08.07.29.1/setup.py
===================================================================
--- keas.kmi/tags/08.07.29.1/setup.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/setup.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# 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 = '08.07.29.1',
+    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('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',
+        ],
+    zip_safe = False,
+)


Property changes on: keas.kmi/tags/08.07.29.1/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/__init__.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/__init__.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/__init__.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
\ No newline at end of file


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/README.txt
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/README.txt	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/README.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,292 @@
+=============================
+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 enrypting key.
+
+(B) The enrypting key must be sotred 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 key.
+
+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 accross multiple devices, and
+
+(B) keep encryption keys in memory.
+
+It is only required that the data transfer is completed via an encrypted
+comunication 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) controlls 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 enrypting key to a 3-tuple that contains the
+date/time the key has been fetched, the enryption (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 = '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 enryption 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'


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/__init__.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/__init__.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/__init__.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,17 @@
+##############################################################################
+#
+# 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$
+"""
+


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/configure.zcml
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/configure.zcml	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/configure.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,35 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <include file="security.zcml" />
+
+  <!-- We purposefully keep the access to the facility very limited -->
+  <class class=".facility.KeyManagementFacility">
+    <require
+	permission="keas.kmi.Encrypt"
+	attribute="encrypt decrypt"
+	/>
+    <require
+	permission="keas.kmi.GenerateKey"
+	attribute="generate"
+	/>
+    <require
+	permission="keas.kmi.AccessKey"
+	attribute="getEncryptionKey"
+	/>
+  </class>
+
+  <class class=".facility.LocalKeyManagementFacility">
+    <require
+	permission="keas.kmi.Encrypt"
+	attribute="encrypt decrypt"
+	/>
+    <require
+	permission="keas.kmi.GenerateKey"
+	attribute="generate"
+	/>
+  </class>
+
+
+</configure>


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/db.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/db.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/db.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Automatic ZODB installation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from keas.kmi import facility
+
+
+def installKeyManagementFacility(self, root):
+    root['keys'] = facility.KeyManagementFacility()


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/db.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/facility.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/facility.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/facility.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,156 @@
+##############################################################################
+#
+# 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$
+"""
+__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'
+    initializationVector = ''
+
+    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)


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/facility.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/interfaces.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/interfaces.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/interfaces.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,72 @@
+##############################################################################
+#
+# 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 zope.interface
+import zope.schema
+from zope.app.container import interfaces
+
+class IKey(zope.interface.Interface):
+    """Encryption Key"""
+
+    created = zope.schema.Datetime(
+        title=u'Creation Date/Time',
+        description=u'The date/time the key pair was created.',
+        required=True)
+
+    creator = zope.schema.TextLine(
+        title=u'Creator',
+        description=u'The principal/user that requested the new key.',
+        required=True)
+
+    key = zope.schema.Bytes(
+        title=u'Key',
+        description=u'The key used to encrypt and decrypt the data.',
+        required=True)
+
+
+class IEncryptionService(zope.interface.Interface):
+    """Utility providing encryption mechanism"""
+
+    def encrypt(key, data):
+        """Returns the encrypted data"""
+
+    def decrypt(key, data):
+        """Returns the decrypted data"""
+
+
+class IKeyGenerationService(zope.interface.Interface):
+    """A component that can generate a key encryption pair."""
+
+    def generate():
+        """Generate a new set of keys.
+
+        Returns the private key encrypting key.
+        """
+
+class IKeyManagementFacility(IEncryptionService, IKeyGenerationService):
+    """Key Management Facility
+
+    A key management facility provides several key management services.
+    """
+
+class IExtendedKeyManagementFacility(IKeyManagementFacility,
+                                     interfaces.IContainer):
+    """Extended Key Management Facility.
+
+    This facility also also the management of the keys via Python's mapping
+    API.
+    """


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/rest.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/rest.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/rest.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# 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 mast 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 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')
+        return self.context.getEncryptionKey(key)


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/rest.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/security.zcml
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/security.zcml	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/security.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,19 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <!-- Key Management Permissions -->
+  <permission
+      id="keas.kmi.Encrypt"
+      title="Encrypt Data"
+      />
+  <permission
+      id="keas.kmi.GenerateKey"
+      title="Generate Key"
+      />
+  <permission
+      id="keas.kmi.AccessKey"
+      title="Access Key"
+      />
+
+</configure>


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/security.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# 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 keas.kmi import facility, rest
+
+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


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.zcml
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.zcml	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,28 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <include file="security.zcml" />
+
+  <!-- We purposefully keep the access to the facility very limited -->
+  <class class=".testing.TestingKeyManagementFacility">
+    <require
+	permission="keas.kmi.Encrypt"
+	attribute="encrypt decrypt"
+	/>
+    <require
+	permission="keas.kmi.GenerateKey"
+	attribute="generate"
+	/>
+    <require
+	permission="keas.kmi.AccessKey"
+	attribute="getEncryptionKey"
+	/>
+  </class>
+
+  <utility
+      factory=".testing.TestingKeyManagementFacility"
+      provides=".interfaces.IKeyManagementFacility"
+      />
+
+</configure>


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/testing.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/tags/08.07.29.1/src/keas/kmi/tests.py
===================================================================
--- keas.kmi/tags/08.07.29.1/src/keas/kmi/tests.py	                        (rev 0)
+++ keas.kmi/tags/08.07.29.1/src/keas/kmi/tests.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# 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 unittest
+import doctest
+from zope.testing.doctestunit import DocFileSuite
+from zope.app.testing import placelesssetup
+
+def test_suite():
+    return unittest.TestSuite((
+        DocFileSuite(
+            'README.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        ))


Property changes on: keas.kmi/tags/08.07.29.1/src/keas/kmi/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/CHANGES.txt
===================================================================
--- keas.kmi/trunk/CHANGES.txt	                        (rev 0)
+++ keas.kmi/trunk/CHANGES.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,9 @@
+=======
+CHANGES
+=======
+
+
+0.1.0 (2008-??-??)
+------------------
+
+- Initial Release


Property changes on: keas.kmi/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/README.txt
===================================================================
--- keas.kmi/trunk/README.txt	                        (rev 0)
+++ keas.kmi/trunk/README.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,2 @@
+This package provides a NIST SP 800-57 compliant Key Management Infrastructure
+(KMI).


Property changes on: keas.kmi/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/bootstrap.py
===================================================================
--- keas.kmi/trunk/bootstrap.py	                        (rev 0)
+++ keas.kmi/trunk/bootstrap.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: keas.kmi/trunk/bootstrap.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/buildout.cfg
===================================================================
--- keas.kmi/trunk/buildout.cfg	                        (rev 0)
+++ keas.kmi/trunk/buildout.cfg	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,42 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report python paster 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
+       zope.app.component
+       zope.app.publication
+       zope.app.publisher
+       zope.app.security
+       zope.component
+       zope.error
+       zope.publisher
+       zope.securitypolicy
+       keas.kmi

Added: keas.kmi/trunk/server.ini
===================================================================
--- keas.kmi/trunk/server.ini	                        (rev 0)
+++ keas.kmi/trunk/server.ini	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,8 @@
+[app:main]
+use = egg:keas.kmi
+conf=zope.conf
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8080

Added: keas.kmi/trunk/setup.py
===================================================================
--- keas.kmi/trunk/setup.py	                        (rev 0)
+++ keas.kmi/trunk/setup.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,71 @@
+##############################################################################
+#
+# 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.1.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('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
+    """,
+)


Property changes on: keas.kmi/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/__init__.py
===================================================================
--- keas.kmi/trunk/src/keas/__init__.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/__init__.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)
\ No newline at end of file


Property changes on: keas.kmi/trunk/src/keas/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/README.txt
===================================================================
--- keas.kmi/trunk/src/keas/kmi/README.txt	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/README.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,300 @@
+=============================
+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'


Property changes on: keas.kmi/trunk/src/keas/kmi/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/__init__.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/__init__.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/__init__.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,17 @@
+##############################################################################
+#
+# 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$
+"""
+


Property changes on: keas.kmi/trunk/src/keas/kmi/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/apidoc.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/apidoc.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/apidoc.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,19 @@
+
+<configure xmlns="http://namespaces.zope.org/zope"
+           i18n_domain="keas">
+
+  <configure
+      xmlns:zcml="http://namespaces.zope.org/zcml"
+      xmlns:apidoc="http://namespaces.zope.org/apidoc"
+      zcml:condition="have apidoc">
+
+    <apidoc:bookchapter
+        id="keas.kmi"
+        title="Key Management Infrastructure (keas.kmi)"
+        doc_path="README.txt"
+        parent="keas"
+        />
+
+  </configure>
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/apidoc.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/application.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/application.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/application.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,40 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <include package="zope.app.component" file="meta.zcml" />
+  <include package="zope.app.publication" file="meta.zcml" />
+  <include package="zope.app.publisher.browser" file="meta.zcml" />
+  <include package="zope.app.security" file="meta.zcml" />
+
+  <include package="zope.publisher" />
+
+  <include package="zope.app.publication" />
+  <include package="zope.app.publisher" />
+  <include package="zope.app.publisher.browser" />
+  <include package="zope.app.security" />
+
+  <utility
+      provides="zope.error.interfaces.IErrorReportingUtility"
+      component="zope.error.error.globalErrorReportingUtility"
+      />
+
+  <include package="keas.kmi" />
+
+  <subscriber handler="zope.component.event.objectEventNotify" />
+
+  <include package="keas.kmi" />
+  <include package="keas.kmi" file="db.zcml" />
+
+  <include package="zope.securitypolicy" file="meta.zcml" />
+  <include package="zope.securitypolicy" />
+  <securityPolicy
+      component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+
+  <!-- Grant access to anybody -->
+  <unauthenticatedPrincipal id="zope.anybody" title="Unauthenticated User" />
+  <unauthenticatedGroup id="zope.Anybody" title="Unauthenticated Users" />
+
+  <grant principal="zope.Anybody" permission="zope.View" />
+  <grant principal="zope.Anybody" permission="keas.kmi.GenerateKey" />
+  <grant principal="zope.Anybody" permission="keas.kmi.AccessKey" />
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/application.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/configure.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/configure.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/configure.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -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"
+	/>
+  </class>
+
+  <class class=".facility.LocalKeyManagementFacility">
+    <require
+	permission="keas.kmi.Encrypt"
+	attributes="encrypt decrypt"
+	/>
+    <require
+	permission="keas.kmi.GenerateKey"
+	attributes="generate"
+	/>
+  </class>
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/db.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/db.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/db.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Automatic ZODB installation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import transaction
+from zope.component import adapter
+from zope.app.appsetup.interfaces import IDatabaseOpenedEvent
+from zope.app.appsetup.bootstrap import getInformationFromEvent
+from zope.app.publication.zopepublication import ZopePublication
+
+from keas.kmi.facility import KeyManagementFacility
+from keas.kmi.interfaces import IKeyManagementFacility
+
+
+ at adapter(IDatabaseOpenedEvent)
+def bootstrapKeyManagementFacility(event):
+    """Installs KeyManagementFacility as the root object of the DB."""
+    db, connection, root, root_object = getInformationFromEvent(event)
+    if root_object is None:
+        root[ZopePublication.root_name] = KeyManagementFacility()
+        transaction.commit()
+    elif not IKeyManagementFacility.providedBy(root_object):
+        raise RuntimeError('Your database root object is not a key management'
+                           ' facility.  Remove your Data.fs and try again.')
+    connection.close()
+


Property changes on: keas.kmi/trunk/src/keas/kmi/db.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/db.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/db.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/db.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,7 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <subscriber
+      handler=".db.bootstrapKeyManagementFacility"
+      />
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/db.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/facility.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/facility.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/facility.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -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)
+        encryptionKey = client.contents
+        self._cache[key] = (time.time(), encryptionKey)
+        return encryptionKey
+
+    def __repr__(self):
+        return '<%s %r>' %(self.__class__.__name__, self.url)


Property changes on: keas.kmi/trunk/src/keas/kmi/facility.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/interfaces.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/interfaces.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/interfaces.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# 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 zope.interface
+import zope.schema
+from zope.app.container import interfaces
+
+
+class IKey(zope.interface.Interface):
+    """Encryption Key"""
+
+    created = zope.schema.Datetime(
+        title=u'Creation Date/Time',
+        description=u'The date/time the key pair was created.',
+        required=True)
+
+    creator = zope.schema.TextLine(
+        title=u'Creator',
+        description=u'The principal/user that requested the new key.',
+        required=True)
+
+    key = zope.schema.Bytes(
+        title=u'Key',
+        description=u'The key used to encrypt and decrypt the data.',
+        required=True)
+
+
+class IEncryptionService(zope.interface.Interface):
+    """Utility providing encryption mechanism"""
+
+    def encrypt(key, data):
+        """Returns the encrypted data"""
+
+    def decrypt(key, data):
+        """Returns the decrypted data"""
+
+
+class IKeyGenerationService(zope.interface.Interface):
+    """A component that can generate a key encryption pair."""
+
+    def generate():
+        """Generate a new set of keys.
+
+        Returns the private key encrypting key.
+        """
+
+class IKeyManagementFacility(IEncryptionService, IKeyGenerationService):
+    """Key Management Facility
+
+    A key management facility provides several key management services.
+    """
+
+class IExtendedKeyManagementFacility(IKeyManagementFacility,
+                                     interfaces.IContainer):
+    """Extended Key Management Facility.
+
+    This facility also also the management of the keys via Python's mapping
+    API.
+    """
+
+
+class IKeyHolder(zope.interface.Interface):
+    """Key Holder
+
+    A key holder stores a single key.
+    """
+
+    key = zope.schema.Bytes(
+        title=u'Key',
+        description=u'The key used to encrypt and decrypt the data.',
+        required=True)
+


Property changes on: keas.kmi/trunk/src/keas/kmi/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/persistent.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/persistent.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/persistent.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,92 @@
+"""Encrypted persistent objects
+
+$Id$
+"""
+from __future__ import absolute_import
+import cPickle
+import cStringIO
+import persistent
+import persistent.wref
+from zope.component import getUtility
+from keas.kmi.interfaces import IEncryptionService
+from keas.kmi.interfaces import IKeyHolder
+
+
+class EncryptedPersistent(persistent.Persistent):
+    """A persistent object that is stored in encrypted form."""
+
+    def __getstate__(self):
+        state = super(EncryptedPersistent, self).__getstate__()
+        return encrypt_state(state)
+
+    def __setstate__(self, encrypted_state):
+        state = decrypt_state(encrypted_state)
+        super(EncryptedPersistent, self).__setstate__(state)
+
+
+def encrypt_state(state):
+    key = getUtility(IKeyHolder).key
+    service = getUtility(IEncryptionService)
+    stringified_data, persistent_refs = pickle_nonpersistent(state)
+    encrypted_data = service.encrypt(key, stringified_data)
+    return encrypted_data, persistent_refs
+
+
+def decrypt_state(state):
+    encrypted_data, persistent_refs = state
+    key = getUtility(IKeyHolder).key
+    service = getUtility(IEncryptionService)
+    stringified_data = service.decrypt(key, encrypted_data)
+    return unpickle_nonpersistent(stringified_data, persistent_refs)
+
+
+def pickle_nonpersistent(state):
+    buf = cStringIO.StringIO()
+    persistent_refs = []
+    cache = {}
+    def persistent_id(obj):
+        # this should probably handle the same kinds of objects that
+        # ZODB.serialize.ObjectWriter.persistent_id does.
+        if not isinstance(obj, (persistent.Persistent, type,
+                                persistent.wref.WeakRef)):
+            # Not persistent, pickle normally
+            return None
+        # Otherwise let ZODB (instead of our local pickler) handle these.
+        idx = cache.get(id(obj), None)
+        if idx is None:
+            idx = cache[id(obj)] = len(persistent_refs)
+            persistent_refs.append(obj)
+        return idx
+    pickler = cPickle.Pickler(buf, 2)
+    pickler.persistent_id = persistent_id
+    pickler.dump(state)
+    return buf.getvalue(), persistent_refs
+
+
+def unpickle_nonpersistent(data, persistent_refs):
+    buf = cStringIO.StringIO(data)
+    def persistent_load(ref):
+        return persistent_refs[ref]
+    unpickler = cPickle.Unpickler(buf)
+    unpickler.persistent_load = persistent_load
+    return unpickler.load()
+
+
+def convert_object_to_encrypted(obj):
+    """Convert a Persistent object to EncryptedPersistent.
+
+    ``obj`` should be a persistent ghost of a class that used to subclass
+    Persistent and now subclasses EncryptedPersistent.
+
+    DO NOT USE THIS METHOD FOR REAL DATA without fully understanding all
+    the implications (existing non-persistent bits will continue to be
+    stored on the disk).  Also, this function uses black magic.
+    """
+
+    # Black magic happens here
+    real_setstate = obj.__class__.__setstate__
+    obj.__class__.__setstate__ = persistent.Persistent.__setstate__
+    obj._p_activate()
+    obj.__class__.__setstate__ = real_setstate
+    obj._p_changed = True
+


Property changes on: keas.kmi/trunk/src/keas/kmi/persistent.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/persistent.txt
===================================================================
--- keas.kmi/trunk/src/keas/kmi/persistent.txt	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/persistent.txt	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,184 @@
+============================
+Encrypted Persistent Objects
+============================
+
+This package provides an `EncryptedPersistent` class that takes care of data
+encryption in the storage.  Usage is pretty simple: instead of subclassing
+`persistent.Persistent`, subclass `keas.kmi.persistent.EncryptedPersistent`:
+
+    >>> from keas.kmi.persistent import EncryptedPersistent
+    >>> class UserPrivateData(EncryptedPersistent):
+    ...     def __init__(self, name, ssn):
+    ...         self.name = name
+    ...         self.ssn = ssn
+    ...     def __repr__(self):
+    ...         return '<UserPrivateData %s %s>' % (self.name, self.ssn)
+
+    >>> userdata = UserPrivateData('Stephan Richter', '123456789')
+    >>> userdata
+    <UserPrivateData Stephan Richter 123456789>
+
+The key used for encryption and decryption comes from an IKeyHolder utility
+that you're supposed to provide in your application.
+
+    >>> from keas.kmi.testing import TestingKeyHolder
+    >>> from zope.component import provideUtility
+    >>> provideUtility(TestingKeyHolder())
+
+None of the raw data appears in the pickle
+
+    >>> import cPickle as pickle
+    >>> pickled_data = pickle.dumps(userdata)
+    >>> 'Stephan' in pickled_data
+    False
+    >>> '123456789' in pickled_data
+    False
+
+We can successfully load it
+
+    >>> pickle.loads(pickled_data)
+    <UserPrivateData Stephan Richter 123456789>
+
+Every persistent object is stored separately.  Only the objects that inherit
+from `EncryptedPersistent` will be encrypted.
+
+    >>> from zope.app.generations.generations import PersistentDict
+    >>> users = PersistentDict()
+    >>> users['stephan'] = UserPrivateData('Stephan Richter', '123456789')
+    >>> users['mgedmin'] = UserPrivateData('Marius Gedminas', '987654321')
+
+    >>> pickled_data = pickle.dumps(users)
+    >>> 'stephan' in pickled_data
+    True
+    >>> '123456789' in pickled_data
+    False
+
+
+Persistent References
+---------------------
+
+Enough pickling; we really should make sure our magic does not interfere
+with ZODB keeping track of persistent object references.
+
+First let's make our `EncryptedPersistent` objects have some references to
+other (encrypted and unencrypted) persistent objects
+
+    >>> users['stephan'].__parent__ = users
+    >>> users['mgedmin'].__parent__ = users
+
+    >>> users['stephan'].friend = users['mgedmin']
+    >>> users['mgedmin'].friend = users['stephan']
+
+Now let's create a database:
+
+    >>> import ZODB.DB
+    >>> import ZODB.MappingStorage
+    >>> db = ZODB.DB(ZODB.MappingStorage.MappingStorage())
+    >>> conn = db.open()
+    >>> conn.root()['users'] = users
+    >>> import transaction
+    >>> transaction.commit()
+
+And we can open a second connection (while carefully keeping the first one
+open, to ensure it's not reused and we actually load the pickles rather than
+receiving persistent objects from a cache) and load the whole object graph
+
+    >>> conn2 = db.open()
+    >>> users2 = conn2.root()['users']
+    >>> users2['stephan']
+    <UserPrivateData Stephan Richter 123456789>
+    >>> users2['mgedmin']
+    <UserPrivateData Marius Gedminas 987654321>
+
+All the object references between persistent and encrypted persistent objects
+are preserved correctly:
+
+    >>> users2['stephan'].friend
+    <UserPrivateData Marius Gedminas 987654321>
+    >>> users2['mgedmin'].friend
+    <UserPrivateData Stephan Richter 123456789>
+
+    >>> users2['stephan'].__parent__ is users2
+    True
+    >>> users2['mgedmin'].__parent__ is users2
+    True
+    >>> users2['stephan'].friend is users2['mgedmin']
+    True
+    >>> users2['mgedmin'].friend is users2['stephan']
+    True
+
+
+Data conversion
+---------------
+
+If you used to have simple persistent objects, and now want to convert them
+to `EncryptedPersistent`, think again.  This is not secure.  You already have
+unencrypted bits on your disk platters, and the only way to get rid of them
+is to physically destroy the disk.
+
+But if you have a testing-only database with fake data, and would like to
+continue using it with a small conversion step, you can use the
+``convert_object_to_encrypted()`` function.
+
+    >>> from keas.kmi.persistent import convert_object_to_encrypted
+
+Here's the old class definition that we'll store:
+
+    >>> from persistent import Persistent
+    >>> class Password(Persistent):
+    ...     def __init__(self, password):
+    ...         self.password = password
+
+    >>> db = ZODB.DB(ZODB.MappingStorage.MappingStorage())
+    >>> conn = db.open()
+    >>> conn.root()['pwd'] = Password('xyzzy')
+    >>> transaction.commit()
+
+And now we redefine the class:
+
+    >>> class Password(EncryptedPersistent):
+    ...     def __init__(self, password):
+    ...         self.password = password
+
+Once again we have to use a different connection object (while keeping the
+first one alive) to avoid stepping on a ZODB cache:
+
+    >>> conn2 = db.open()
+    >>> pwd = conn2.root()['pwd']
+
+If you try to use `Password` objects loaded from the database, you'll get an
+error:
+
+    >>> pwd.password
+    Traceback (most recent call last):
+      ...
+    ValueError: need more than 1 value to unpack
+
+But we can apply the conversion step:
+
+    >>> convert_object_to_encrypted(pwd)
+    >>> pwd.password
+    'xyzzy'
+
+The converted state is stored in the DB
+
+    >>> transaction.commit()
+    >>> conn3 = db.open()
+    >>> pwd = conn3.root()['pwd']
+    >>> pwd.password
+    'xyzzy'
+
+
+Encryption Stability
+--------------------
+
+Once encrypted, the same string can always be decrypted with the same key:
+
+    >>> pickle.loads("ccopy_reg\n__newobj__\np1\n(ckeas.kmi.tests.doctestfile\nUserPrivateData\np2\ntRp3\n(S'h:(\\xf7\\x81\\x07<\\x96\\xb2)\\xd2\\x11\\x80\\x16\\xcdo\\xec\\xe7\\xbfc\\x9dx^\\xfa\\xd0)z\\x91\\x17\\xd5o\\xe8(jH\\xfc{?\\x10k\\x9e\\x90D\\xbew\\xc6]r\\xd1\\xc5\\xfb\\xeaAT\\x16\\xd1\\xa4\\x07\\xaf\\x12\\xe3\\xe5s\\xb5'\n(ltb.")
+    <UserPrivateData Stephan Richter 123456789>
+
+This is a regression test for a problem we had with M2Crypto: if you pass an
+empty `initializationVector` to `M2Crypto.EVP.Cipher`, it propably uses a
+chunk of uninitialized memory and ends up not being able to decrypt the
+pickles after you restart the Python process.
+


Property changes on: keas.kmi/trunk/src/keas/kmi/persistent.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/rest.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/rest.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# 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)


Property changes on: keas.kmi/trunk/src/keas/kmi/rest.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/rest.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/rest.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,18 @@
+<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>


Property changes on: keas.kmi/trunk/src/keas/kmi/rest.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/security.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/security.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/security.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,19 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <!-- Key Management Permissions -->
+  <permission
+      id="keas.kmi.Encrypt"
+      title="Encrypt Data"
+      />
+  <permission
+      id="keas.kmi.GenerateKey"
+      title="Generate Key"
+      />
+  <permission
+      id="keas.kmi.AccessKey"
+      title="Access Key"
+      />
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/security.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/testing.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/testing.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/testing.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,83 @@
+##############################################################################
+#
+# 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
+


Property changes on: keas.kmi/trunk/src/keas/kmi/testing.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/testing.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/testing.zcml	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/testing.zcml	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,28 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="keas">
+
+  <include file="security.zcml" />
+
+  <!-- We purposefully keep the access to the facility very limited -->
+  <class class=".testing.TestingKeyManagementFacility">
+    <require
+	permission="keas.kmi.Encrypt"
+	attribute="encrypt decrypt"
+	/>
+    <require
+	permission="keas.kmi.GenerateKey"
+	attribute="generate"
+	/>
+    <require
+	permission="keas.kmi.AccessKey"
+	attribute="getEncryptionKey"
+	/>
+  </class>
+
+  <utility
+      factory=".testing.TestingKeyManagementFacility"
+      provides=".interfaces.IKeyManagementFacility"
+      />
+
+</configure>


Property changes on: keas.kmi/trunk/src/keas/kmi/testing.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: keas.kmi/trunk/src/keas/kmi/tests.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/tests.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/tests.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# 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 unittest
+
+import transaction
+from zope.testing import doctest
+from zope.app.testing import setup
+from zope.component import provideUtility
+
+from keas.kmi.testing import TestingKeyManagementFacility
+from keas.kmi.interfaces import IKeyManagementFacility
+
+
+def setUpPersistent(test):
+    setup.setUpTestAsModule(test, name='keas.kmi.tests.doctestfile')
+    setup.placelessSetUp()
+    provideUtility(TestingKeyManagementFacility(),
+                   provides=IKeyManagementFacility)
+
+
+def tearDownPersistent(test):
+    transaction.abort()
+    setup.placelessTearDown()
+    setup.tearDownTestAsModule(test)
+
+
+def test_suite():
+    return unittest.TestSuite([
+        doctest.DocFileSuite(
+            'README.txt',
+            setUp=setup.placelessSetUp, tearDown=setup.placelessTearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        doctest.DocFileSuite(
+            'persistent.txt',
+            setUp=setUpPersistent, tearDown=tearDownPersistent,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+    ])


Property changes on: keas.kmi/trunk/src/keas/kmi/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/src/keas/kmi/wsgi.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/wsgi.py	                        (rev 0)
+++ keas.kmi/trunk/src/keas/kmi/wsgi.py	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,15 @@
+"""
+WSGI application for the Key Management Server.
+"""
+
+import os
+
+import zope.app.appsetup
+from zope.app.wsgi import getWSGIApplication
+
+
+def application_factory(global_conf, conf='kms.conf', **local_conf):
+    configfile = os.path.join(global_conf['here'], conf)
+    application = getWSGIApplication(configfile)
+    return application
+


Property changes on: keas.kmi/trunk/src/keas/kmi/wsgi.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: keas.kmi/trunk/zope.conf
===================================================================
--- keas.kmi/trunk/zope.conf	                        (rev 0)
+++ keas.kmi/trunk/zope.conf	2008-09-04 05:59:31 UTC (rev 90781)
@@ -0,0 +1,23 @@
+site-definition src/keas/kmi/application.zcml
+
+<zodb>
+  <filestorage>
+    path var/Data.fs
+  </filestorage>
+</zodb>
+
+<eventlog>
+  # This sets up logging to both a file and to standard output
+  # (STDOUT).  The "path" setting can be a relative or absolute
+  # filesystem path or the tokens STDOUT or STDERR.
+
+  <logfile>
+    path var/zope.log
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+
+  <logfile>
+    path STDOUT
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+</eventlog>



More information about the Checkins mailing list