[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