[Checkins] SVN: keas.kmi/tags/0.2.0/ Release keas.kmi 0.2.0.
Marius Gedminas
marius at pov.lt
Thu Sep 4 17:06:25 EDT 2008
Log message for revision 90845:
Release keas.kmi 0.2.0.
Changed:
A keas.kmi/tags/0.2.0/
D keas.kmi/tags/0.2.0/CHANGES.txt
A keas.kmi/tags/0.2.0/CHANGES.txt
D keas.kmi/tags/0.2.0/README.txt
A keas.kmi/tags/0.2.0/README.txt
D keas.kmi/tags/0.2.0/buildout.cfg
A keas.kmi/tags/0.2.0/buildout.cfg
D keas.kmi/tags/0.2.0/setup.py
A keas.kmi/tags/0.2.0/setup.py
D keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
A keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
D keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
A keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
D keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
A keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
D keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
A keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
D keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
A keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
A keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py
D keas.kmi/tags/0.2.0/src/keas/kmi/testing.py
A keas.kmi/tags/0.2.0/src/keas/kmi/testing.py
-=-
Copied: keas.kmi/tags/0.2.0 (from rev 90830, keas.kmi/trunk)
Deleted: keas.kmi/tags/0.2.0/CHANGES.txt
===================================================================
--- keas.kmi/trunk/CHANGES.txt 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/CHANGES.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,20 +0,0 @@
-=======
-CHANGES
-=======
-
-0.2.0 (unreleased)
-------------------
-
-
-0.1.0 (2008-09-03)
-------------------
-
-- Initial Release
-
- * Key Generation Service
-
- * Encryption Service (Master and Local)
-
- * REST API for key communication between encryption services
-
- * Encrypted Persistent Storage
Copied: keas.kmi/tags/0.2.0/CHANGES.txt (from rev 90844, keas.kmi/trunk/CHANGES.txt)
===================================================================
--- keas.kmi/tags/0.2.0/CHANGES.txt (rev 0)
+++ keas.kmi/tags/0.2.0/CHANGES.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,30 @@
+=======
+CHANGES
+=======
+
+0.2.0 (2008-09-04)
+------------------
+
+- Sample server shows how to enable SSL
+
+- Front page now shows the number of stored keys instead of a
+ ComponentLookupError message.
+
+- Command-line client for testing a remote Key Management Server
+
+- Bugfix: LocalKeyManagementFacility was broken (AttributeError: 'RESTClient'
+ object has no attribute 'POST')
+
+
+0.1.0 (2008-09-03)
+------------------
+
+- Initial Release
+
+ * Key Generation Service
+
+ * Encryption Service (Master and Local)
+
+ * REST API for key communication between encryption services
+
+ * Encrypted Persistent Storage
Deleted: keas.kmi/tags/0.2.0/README.txt
===================================================================
--- keas.kmi/trunk/README.txt 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/README.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,23 +0,0 @@
-This package provides a NIST SP 800-57 compliant Key Management Infrastructure
-(KMI).
-
-To get started do::
-
- $ python boostrap.py # Must be Python 2.5
- $ ./bin/buildout # Depends on successfull compilation of M2Crypto
- $ ./bin/runserver # or ./bin/paster serve server.ini
-
-The server will come up on port 8080. You can create a new key encrypting key
-using::
-
- $ wget https://localhost:8080/new -O kek.dat --ca-certificate sample.pem
-
-The data encryption key can now be retrieved by posting the KEK to another
-URL::
-
- $ wget https://localhost:8080/key --header 'Content-Type: text/plain' --post-file kek.dat -O datakey.dat --ca-certificate sample.pem
-
-Note: To be compliant, the server must use an encrypted communication channel
-of course. The ``--ca-certificate`` tells wget to trust the sample self-signed
-certificate included in the keas.kmi distribution; you'll want to generate a
-new SSL certificate for production use.
Copied: keas.kmi/tags/0.2.0/README.txt (from rev 90836, keas.kmi/trunk/README.txt)
===================================================================
--- keas.kmi/tags/0.2.0/README.txt (rev 0)
+++ keas.kmi/tags/0.2.0/README.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,31 @@
+This package provides a NIST SP 800-57 compliant Key Management Infrastructure
+(KMI).
+
+To get started do::
+
+ $ python boostrap.py # Must be Python 2.5
+ $ ./bin/buildout # Depends on successfull compilation of M2Crypto
+ $ ./bin/runserver # or ./bin/paster serve server.ini
+
+The server will come up on port 8080. You can create a new key encrypting key
+using::
+
+ $ wget https://localhost:8080/new -O kek.dat --ca-certificate sample.pem
+
+or, if you want a more convenient tool::
+
+ $ ./bin/testclient https://localhost:8080/new -n > kek.dat
+
+The data encryption key can now be retrieved by posting the KEK to another
+URL::
+
+ $ wget https://localhost:8080/key --header 'Content-Type: text/plain' --post-file kek.dat -O datakey.dat --ca-certificate sample.pem
+
+or ::
+
+ $ ./bin/testclient https://localhost:8080/new -g kek.dat > datakey.dat
+
+Note: To be compliant, the server must use an encrypted communication channel
+of course. The ``--ca-certificate`` tells wget to trust the sample self-signed
+certificate included in the keas.kmi distribution; you'll want to generate a
+new SSL certificate for production use.
Deleted: keas.kmi/tags/0.2.0/buildout.cfg
===================================================================
--- keas.kmi/trunk/buildout.cfg 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/buildout.cfg 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,49 +0,0 @@
-[buildout]
-develop = .
-parts = test coverage-test coverage-report python paster runserver ctags
-
-[test]
-recipe = zc.recipe.testrunner
-eggs = keas.kmi [test]
-
-[coverage-test]
-recipe = zc.recipe.testrunner
-eggs = keas.kmi [test]
-defaults = ['--coverage', '../../coverage']
-
-[coverage-report]
-recipe = zc.recipe.egg
-eggs = z3c.coverage
-scripts = coverage=coverage-report
-arguments = ('coverage', 'coverage/report')
-
-[python]
-recipe = zc.recipe.egg
-eggs = keas.kmi
-interpreter = python
-
-[ctags]
-recipe = z3c.recipe.tag:tags
-eggs = keas.kmi
-
-[paster]
-recipe = zc.recipe.egg
-eggs = Paste
- PasteScript
- PasteDeploy
- pyOpenSSL
- zope.app.component
- zope.app.publication
- zope.app.publisher
- zope.app.security
- zope.component
- zope.error
- zope.publisher
- zope.securitypolicy
- keas.kmi
-
-[runserver]
-recipe = zc.recipe.egg
-eggs = ${paster:eggs}
-scripts = paster=runserver
-arguments = ['serve', 'server.ini']
Copied: keas.kmi/tags/0.2.0/buildout.cfg (from rev 90834, keas.kmi/trunk/buildout.cfg)
===================================================================
--- keas.kmi/tags/0.2.0/buildout.cfg (rev 0)
+++ keas.kmi/tags/0.2.0/buildout.cfg 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,53 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report python paster runserver testclient ctags
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = keas.kmi [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = keas.kmi [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
+
+[python]
+recipe = zc.recipe.egg
+eggs = keas.kmi
+interpreter = python
+
+[ctags]
+recipe = z3c.recipe.tag:tags
+eggs = keas.kmi
+
+[paster]
+recipe = zc.recipe.egg
+eggs = Paste
+ PasteScript
+ PasteDeploy
+ pyOpenSSL
+ zope.app.component
+ zope.app.publication
+ zope.app.publisher
+ zope.app.security
+ zope.component
+ zope.error
+ zope.publisher
+ zope.securitypolicy
+ keas.kmi
+
+[runserver]
+recipe = zc.recipe.egg
+eggs = ${paster:eggs}
+scripts = paster=runserver
+arguments = ['serve', 'server.ini']
+
+[testclient]
+recipe = zc.recipe.egg
+eggs = keas.kmi
Deleted: keas.kmi/tags/0.2.0/setup.py
===================================================================
--- keas.kmi/trunk/setup.py 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/setup.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,75 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""
-$Id$
-"""
-import os
-from setuptools import setup, find_packages
-
-def read(*rnames):
- return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-setup (
- name='keas.kmi',
- version='0.2.0dev',
- author = "Stephan Richter and the Zope Community",
- author_email = "zope-dev at zope.org",
- description = "A Key Management Infrastructure",
- long_description=(
- read('README.txt')
- + '\n\n' +
- read('src', 'keas', 'kmi', 'README.txt')
- + '\n\n' +
- read('src', 'keas', 'kmi', 'persistent.txt')
- + '\n\n' +
- read('CHANGES.txt')
- ),
- license = "ZPL 2.1",
- keywords = "zope3 security key management infrastructure nist 800-57",
- classifiers = [
- 'Development Status :: 4 - Beta',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Zope Public License',
- 'Programming Language :: Python',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Topic :: Internet :: WWW/HTTP',
- 'Framework :: Zope3'],
- url = 'http://pypi.python.org/pypi/keas.kmi',
- packages = find_packages('src'),
- include_package_data = True,
- package_dir = {'':'src'},
- namespace_packages = ['keas'],
- extras_require = dict(
- test = [
- 'zope.testing',
- 'zope.app.testing',
- ],
- ),
- install_requires = [
- 'M2Crypto==0.18',
- 'setuptools',
- 'z3c.rest',
- 'zope.component',
- 'zope.interface',
- 'zope.schema',
- 'ZODB3',
- ],
- zip_safe = False,
- entry_points = """
- [paste.app_factory]
- main = keas.kmi.wsgi:application_factory
- """,
-)
Copied: keas.kmi/tags/0.2.0/setup.py (from rev 90844, keas.kmi/trunk/setup.py)
===================================================================
--- keas.kmi/tags/0.2.0/setup.py (rev 0)
+++ keas.kmi/tags/0.2.0/setup.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup (
+ name='keas.kmi',
+ version='0.2.0',
+ author = "Stephan Richter and the Zope Community",
+ author_email = "zope-dev at zope.org",
+ description = "A Key Management Infrastructure",
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ read('src', 'keas', 'kmi', 'README.txt')
+ + '\n\n' +
+ read('src', 'keas', 'kmi', 'persistent.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ license = "ZPL 2.1",
+ keywords = "zope3 security key management infrastructure nist 800-57",
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url = 'http://pypi.python.org/pypi/keas.kmi',
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['keas'],
+ extras_require = dict(
+ test = [
+ 'zope.testing',
+ 'zope.app.testing',
+ ],
+ ),
+ install_requires = [
+ 'M2Crypto==0.18',
+ 'setuptools',
+ 'z3c.rest',
+ 'zope.component',
+ 'zope.interface',
+ 'zope.schema',
+ 'ZODB3',
+ ],
+ zip_safe = False,
+ entry_points = """
+ [console_scripts]
+ testclient = keas.kmi.testclient:main
+
+ [paste.app_factory]
+ main = keas.kmi.wsgi:application_factory
+ """,
+)
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/README.txt
===================================================================
--- keas.kmi/trunk/src/keas/kmi/README.txt 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/README.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,300 +0,0 @@
-=============================
-Key Management Infrastructure
-=============================
-
-This package provides a NIST SP 800-57 compliant key management
-infrastructure. Part of this infrastructure is a key management facility that
-provides several services related to keys.
-
- >>> from keas.kmi import facility
- >>> keys = facility.KeyManagementFacility()
- >>> keys
- <KeyManagementFacility (0)>
-
- >>> from zope.interface import verify
- >>> from keas.kmi import interfaces
- >>> verify.verifyObject(interfaces.IKeyManagementFacility, keys)
- True
-
-One of the services the facility provides in the generation of new keys.
-
- >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
- True
-
-The algorithm to generate a new pair of keys is somewhat involved. The
-following features are required:
-
-(A) The key local to the data cannot be directly used as the encrypting key.
-
-(B) The encrypting key must be stored using a cipher that is at least as
- strong as the key itself.
-
-(C) The computer storing the data cannot also store the key.
-
-This suggests the following algorithm to generate and store a new encrypting
-key:
-
-1. Create the key encrypting key (private and public).
-
-2. Create the encryption key.
-
-3. Use the public key encrypting key to encrypt both the encryption keys.
-
-4. Discard the public key encrypting key. It is important that this key is
- never stored anywhere.
-
-5. Store the encrypted encryption key in the key management facility.
-
-6. Return the private key encrypting key.
-
-Let's now use the key generation service's API to generate a key.
-
- >>> key = keys.generate()
- >>> print key
- -----BEGIN RSA PRIVATE KEY-----
- ...
- -----END RSA PRIVATE KEY-----
-
-By default the system uses the AES 256 cipher, because public commentary
-suggests that the AES 196 or AES 256 cipher sufficiently fulfill the PCI,
-HIPAA and NIST key strength requirement.
-
-You can now use this key encrypting key to extract the encryption keys:
-
- >>> import md5
- >>> hash = md5.new()
- >>> hash.update(key)
-
- >>> keys.get(hash.hexdigest())
- <Key ...>
-
-Our key management facility also supports the encryption service, which allows
-you to encrypt and decrypt a string given the key encrypting key.
-
- >>> verify.verifyObject(interfaces.IEncryptionService, keys)
- True
-
-Let's now encrypt some data:
-
- >>> encrypted = keys.encrypt(key, 'Stephan Richter')
- >>> len(encrypted)
- 16
-
-We can also decrypt the data.
-
- >>> keys.decrypt(key, encrypted)
- 'Stephan Richter'
-
-And that's pretty much all there is to it. Most of the complicated
-crypto-related work happens under the hood, transparent to the user.
-
-
-The Local Key Management Facility
----------------------------------
-
-However, using the master key management facility's encryption service is
-expensive, since each encryption and decryption request would require a
-network request. Fortunately, we can
-
-(A) communicate encryption keys across multiple devices, and
-
-(B) keep encryption keys in memory.
-
-It is only required that the data transfer is completed via an encrypted
-communication channel. In this implementation the communication protocol is
-HTTP and thus a sufficiently strong SSL connection is appropriate.
-
-Let's now instantiate the local key management facility:
-
- >>> localKeys = facility.LocalKeyManagementFacility('http://localhost/keys')
- >>> localKeys
- <LocalKeyManagementFacility 'http://localhost/keys'>
-
-The argument to the constructor is the URL to the master key management
-facility. The local facility will use a small REST API to communicate with the
-server.
-
-For the purpose of this test, we are going to install a network component that
-only simulates the requests:
-
- >>> from keas.kmi import testing
- >>> testing.setupRestApi(localKeys, keys)
-
-As with the master facility, the local facility provides the
-``IEncryptionService`` interface:
-
- >>> verify.verifyObject(interfaces.IEncryptionService, localKeys)
- True
-
-So en- and decryption is very easy to do:
-
- >>> encrypted = localKeys.encrypt(key, 'Stephan Richter')
- >>> len(encrypted)
- 16
-
- >>> localKeys.decrypt(key, encrypted)
- 'Stephan Richter'
-
-Instead of forwarding the en- an decryption request to the master facility,
-the local facility merely fetches the encryption key pair and executes the
-operation locally. This approach has the following advantages:
-
-(A) There is no general network latency for any en- and decryption call.
-
-(B) The expensive task of en- and decrypting a message is delegated to
- multiple servers, allowing better scaling.
-
-(C) Fetched keys can be cached locally decreasing the network calls to a once
- in a while process.
-
-In this implementation, we do cache the keys in a private attribute:
-
- >>> key in localKeys._cache
- True
-
-A timeout (in seconds) controls when a key must be refetched:
-
- >>> localKeys.timeout
- 3600
-
-Let's now force a reload by setting the timeout to zero:
-
- >>> localKeys.timeout = 0
-
-The cache is a dictionary of key encrypting key to a 3-tuple that contains the
-date/time the key has been fetched, the encryption (public) key, and the
-decryption (private) key.
-
- >>> firstTime = localKeys._cache[key][0]
-
- >>> localKeys.decrypt(key, encrypted)
- 'Stephan Richter'
-
- >>> secondTime = localKeys._cache[key][0]
-
- >>> firstTime < secondTime
- True
-
-The local facility also provides the ``IKeyGenerationService`` interface:
-
- >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
- True
-
-The local method call is identical to the master one:
-
- >>> key2 = localKeys.generate()
- >>> print key2
- -----BEGIN RSA PRIVATE KEY-----
- ...
- -----END RSA PRIVATE KEY-----
-
-The operation is forwarded to the master server, so that the key is available
-there as well:
-
- >>> hash = md5.new()
- >>> hash.update(key2)
-
- >>> hash.hexdigest() in keys
- True
-
-
-The REST API
-------------
-
-The REST API of the master key management facility defines the communication
-with the local facility. When a new encryption key pair is created, we simply
-make a `POST` call to the following URL::
-
- http://server:port/master/new
-
-The request should have no body and the response is simply the key encrypting
-key.
-
-So let's have a look at the call:
-
- >>> from keas.kmi import rest
- >>> from zope.publisher.browser import TestRequest
-
- >>> request = TestRequest()
- >>> request.method = 'GET'
-
- >>> newCall = rest.NewView(keys, request)
- >>> key3 = newCall()
- >>> print key3
- -----BEGIN RSA PRIVATE KEY-----
- ...
- -----END RSA PRIVATE KEY-----
-
-You can also use post for the new key:
-
- >>> request.method = 'POST'
- >>> print newCall()
- -----BEGIN RSA PRIVATE KEY-----
- ...
- -----END RSA PRIVATE KEY-----
-
-The key is available in the facility of course:
-
- >>> hash = md5.new()
- >>> hash.update(key3)
-
- >>> hash.hexdigest() in keys
- True
-
-We can now fetch the encryption key pair using a `POST` call to this URL::
-
- http://server:port/master/key
-
-The request sends the key encrypting key in its body. The response is the
-encryption key string:
-
- >>> import cStringIO
- >>> io = cStringIO.StringIO(key3)
-
- >>> request = TestRequest(io)
- >>> request.method = 'POST'
-
- >>> keyCall = rest.KeyView(keys, request)
- >>> encKey = keyCall()
- >>> len(encKey)
- 32
-
-
-The Testing Key Management Facility
------------------------------------
-
-The testing facility only manages a single key that is always constant. This
-allows you to install a testing facility globally, not storing the keys in the
-database and still reuse a ZODB over multiple sessions.
-
- >>> testingKeys = testing.TestingKeyManagementFacility()
-
-Of course, the key generation service is supported:
-
- >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
- True
-
-However, you will always receive the same key:
-
- >>> def getKeySegment(key):
- ... return key.split('\n')[1]
-
- >>> getKeySegment(testingKeys.generate())
- 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
- >>> getKeySegment(testingKeys.generate())
- 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
- >>> testingKeys = testing.TestingKeyManagementFacility()
- >>> getKeySegment(testingKeys.generate())
- 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
-
-All other methods remain the same:
-
- >>> key = testingKeys.generate()
- >>> testingKeys.getEncryptionKey(key)
- '_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaa...'
-
-We can also safely en- and decrypt:
-
- >>> encrypted = testingKeys.encrypt(key, 'Stephan Richter')
- >>> testingKeys.decrypt(key, encrypted)
- 'Stephan Richter'
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/README.txt (from rev 90843, keas.kmi/trunk/src/keas/kmi/README.txt)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/README.txt (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/README.txt 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,316 @@
+=============================
+Key Management Infrastructure
+=============================
+
+This package provides a NIST SP 800-57 compliant key management
+infrastructure. Part of this infrastructure is a key management facility that
+provides several services related to keys.
+
+ >>> from keas.kmi import facility
+ >>> keys = facility.KeyManagementFacility()
+ >>> keys
+ <KeyManagementFacility (0)>
+
+ >>> from zope.interface import verify
+ >>> from keas.kmi import interfaces
+ >>> verify.verifyObject(interfaces.IKeyManagementFacility, keys)
+ True
+
+One of the services the facility provides in the generation of new keys.
+
+ >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+ True
+
+The algorithm to generate a new pair of keys is somewhat involved. The
+following features are required:
+
+(A) The key local to the data cannot be directly used as the encrypting key.
+
+(B) The encrypting key must be stored using a cipher that is at least as
+ strong as the key itself.
+
+(C) The computer storing the data cannot also store the key.
+
+This suggests the following algorithm to generate and store a new encrypting
+key:
+
+1. Create the key encrypting key (private and public).
+
+2. Create the encryption key.
+
+3. Use the public key encrypting key to encrypt both the encryption keys.
+
+4. Discard the public key encrypting key. It is important that this key is
+ never stored anywhere.
+
+5. Store the encrypted encryption key in the key management facility.
+
+6. Return the private key encrypting key.
+
+Let's now use the key generation service's API to generate a key.
+
+ >>> key = keys.generate()
+ >>> print key
+ -----BEGIN RSA PRIVATE KEY-----
+ ...
+ -----END RSA PRIVATE KEY-----
+
+By default the system uses the AES 256 cipher, because public commentary
+suggests that the AES 196 or AES 256 cipher sufficiently fulfill the PCI,
+HIPAA and NIST key strength requirement.
+
+You can now use this key encrypting key to extract the encryption keys:
+
+ >>> import md5
+ >>> hash = md5.new()
+ >>> hash.update(key)
+
+ >>> keys.get(hash.hexdigest())
+ <Key ...>
+
+Our key management facility also supports the encryption service, which allows
+you to encrypt and decrypt a string given the key encrypting key.
+
+ >>> verify.verifyObject(interfaces.IEncryptionService, keys)
+ True
+
+Let's now encrypt some data:
+
+ >>> encrypted = keys.encrypt(key, 'Stephan Richter')
+ >>> len(encrypted)
+ 16
+
+We can also decrypt the data.
+
+ >>> keys.decrypt(key, encrypted)
+ 'Stephan Richter'
+
+And that's pretty much all there is to it. Most of the complicated
+crypto-related work happens under the hood, transparent to the user.
+
+
+The Local Key Management Facility
+---------------------------------
+
+However, using the master key management facility's encryption service is
+expensive, since each encryption and decryption request would require a
+network request. Fortunately, we can
+
+(A) communicate encryption keys across multiple devices, and
+
+(B) keep encryption keys in memory.
+
+It is only required that the data transfer is completed via an encrypted
+communication channel. In this implementation the communication protocol is
+HTTP and thus a sufficiently strong SSL connection is appropriate.
+
+Let's now instantiate the local key management facility:
+
+ >>> localKeys = facility.LocalKeyManagementFacility('http://localhost/keys')
+ >>> localKeys
+ <LocalKeyManagementFacility 'http://localhost/keys'>
+
+The argument to the constructor is the URL to the master key management
+facility. The local facility will use a small REST API to communicate with the
+server.
+
+For the purpose of this test, we are going to install a network component that
+only simulates the requests:
+
+ >>> from keas.kmi import testing
+ >>> testing.setupRestApi(localKeys, keys)
+
+As with the master facility, the local facility provides the
+``IEncryptionService`` interface:
+
+ >>> verify.verifyObject(interfaces.IEncryptionService, localKeys)
+ True
+
+So en- and decryption is very easy to do:
+
+ >>> encrypted = localKeys.encrypt(key, 'Stephan Richter')
+ >>> len(encrypted)
+ 16
+
+ >>> localKeys.decrypt(key, encrypted)
+ 'Stephan Richter'
+
+Instead of forwarding the en- an decryption request to the master facility,
+the local facility merely fetches the encryption key pair and executes the
+operation locally. This approach has the following advantages:
+
+(A) There is no general network latency for any en- and decryption call.
+
+(B) The expensive task of en- and decrypting a message is delegated to
+ multiple servers, allowing better scaling.
+
+(C) Fetched keys can be cached locally decreasing the network calls to a once
+ in a while process.
+
+In this implementation, we do cache the keys in a private attribute:
+
+ >>> key in localKeys._cache
+ True
+
+A timeout (in seconds) controls when a key must be refetched:
+
+ >>> localKeys.timeout
+ 3600
+
+Let's now force a reload by setting the timeout to zero:
+
+ >>> localKeys.timeout = 0
+
+The cache is a dictionary of key encrypting key to a 3-tuple that contains the
+date/time the key has been fetched, the encryption (public) key, and the
+decryption (private) key.
+
+ >>> firstTime = localKeys._cache[key][0]
+
+ >>> localKeys.decrypt(key, encrypted)
+ 'Stephan Richter'
+
+ >>> secondTime = localKeys._cache[key][0]
+
+ >>> firstTime < secondTime
+ True
+
+The local facility also provides the ``IKeyGenerationService`` interface:
+
+ >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+ True
+
+The local method call is identical to the master one:
+
+ >>> key2 = localKeys.generate()
+ >>> print key2
+ -----BEGIN RSA PRIVATE KEY-----
+ ...
+ -----END RSA PRIVATE KEY-----
+
+The operation is forwarded to the master server, so that the key is available
+there as well:
+
+ >>> hash = md5.new()
+ >>> hash.update(key2)
+
+ >>> hash.hexdigest() in keys
+ True
+
+
+The REST API
+------------
+
+The REST API of the master key management facility defines the communication
+with the local facility. When a new encryption key pair is created, we simply
+make a `POST` call to the following URL::
+
+ http://server:port/new
+
+The request should have no body and the response is simply the key encrypting
+key.
+
+So let's have a look at the call:
+
+ >>> from keas.kmi import rest
+ >>> from zope.publisher.browser import TestRequest
+
+ >>> request = TestRequest()
+ >>> request.method = 'POST'
+
+ >>> newCall = rest.NewView(keys, request)
+ >>> key3 = newCall()
+ >>> print key3
+ -----BEGIN RSA PRIVATE KEY-----
+ ...
+ -----END RSA PRIVATE KEY-----
+
+The key is available in the facility of course:
+
+ >>> hash = md5.new()
+ >>> hash.update(key3)
+
+ >>> hash.hexdigest() in keys
+ True
+
+We can now fetch the encryption key pair using a `POST` call to this URL::
+
+ http://server:port/key
+
+The request sends the key encrypting key in its body. The response is the
+encryption key string:
+
+ >>> import cStringIO
+ >>> io = cStringIO.StringIO(key3)
+
+ >>> request = TestRequest(io)
+ >>> request.method = 'POST'
+
+ >>> keyCall = rest.KeyView(keys, request)
+ >>> encKey = keyCall()
+ >>> len(encKey)
+ 32
+
+If you try to request a nonexistent key, you get a 404 error:
+encryption key string:
+
+ >>> import cStringIO
+ >>> io = cStringIO.StringIO('xyzzy')
+
+ >>> request = TestRequest(io)
+ >>> request.method = 'POST'
+
+ >>> keyCall = rest.KeyView(keys, request)
+ >>> print keyCall()
+ Key not found
+ >>> request.response.getStatus()
+ 404
+
+A `GET` request to the root shows us a server status page
+
+ >>> request = TestRequest()
+ >>> request.method = 'GET'
+
+ >>> newCall = rest.StatusView(keys, request)
+ >>> print newCall()
+ KMS server holding 3 keys
+
+
+The Testing Key Management Facility
+-----------------------------------
+
+The testing facility only manages a single key that is always constant. This
+allows you to install a testing facility globally, not storing the keys in the
+database and still reuse a ZODB over multiple sessions.
+
+ >>> testingKeys = testing.TestingKeyManagementFacility()
+
+Of course, the key generation service is supported:
+
+ >>> verify.verifyObject(interfaces.IKeyGenerationService, keys)
+ True
+
+However, you will always receive the same key:
+
+ >>> def getKeySegment(key):
+ ... return key.split('\n')[1]
+
+ >>> getKeySegment(testingKeys.generate())
+ 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+ >>> getKeySegment(testingKeys.generate())
+ 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+ >>> testingKeys = testing.TestingKeyManagementFacility()
+ >>> getKeySegment(testingKeys.generate())
+ 'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
+
+All other methods remain the same:
+
+ >>> key = testingKeys.generate()
+ >>> testingKeys.getEncryptionKey(key)
+ '_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaa...'
+
+We can also safely en- and decrypt:
+
+ >>> encrypted = testingKeys.encrypt(key, 'Stephan Richter')
+ >>> testingKeys.decrypt(key, encrypted)
+ 'Stephan Richter'
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/configure.zcml 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,35 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="keas">
-
- <include file="rest.zcml" />
- <include file="security.zcml" />
-
- <!-- We purposefully keep the access to the facility very limited -->
- <class class=".facility.KeyManagementFacility">
- <require
- permission="keas.kmi.Encrypt"
- attributes="encrypt decrypt"
- />
- <require
- permission="keas.kmi.GenerateKey"
- attributes="generate"
- />
- <require
- permission="keas.kmi.AccessKey"
- attributes="getEncryptionKey"
- />
- </class>
-
- <class class=".facility.LocalKeyManagementFacility">
- <require
- permission="keas.kmi.Encrypt"
- attributes="encrypt decrypt"
- />
- <require
- permission="keas.kmi.GenerateKey"
- attributes="generate"
- />
- </class>
-
-</configure>
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml (from rev 90839, keas.kmi/trunk/src/keas/kmi/configure.zcml)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/configure.zcml 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,35 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="keas">
+
+ <include file="rest.zcml" />
+ <include file="security.zcml" />
+
+ <!-- We purposefully keep the access to the facility very limited -->
+ <class class=".facility.KeyManagementFacility">
+ <require
+ permission="keas.kmi.Encrypt"
+ attributes="encrypt decrypt"
+ />
+ <require
+ permission="keas.kmi.GenerateKey"
+ attributes="generate"
+ />
+ <require
+ permission="keas.kmi.AccessKey"
+ attributes="getEncryptionKey __len__"
+ />
+ </class>
+
+ <class class=".facility.LocalKeyManagementFacility">
+ <require
+ permission="keas.kmi.Encrypt"
+ attributes="encrypt decrypt"
+ />
+ <require
+ permission="keas.kmi.GenerateKey"
+ attributes="generate"
+ />
+ </class>
+
+</configure>
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/facility.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/facility.py 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/facility.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,163 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Implementation of Key Management Facility
-
-$Id$
-"""
-from __future__ import absolute_import
-__docformat__ = "reStructuredText"
-import M2Crypto
-import datetime
-import md5
-import persistent
-import time
-import zope.interface
-import zope.location
-from z3c.rest import client
-from zope.annotation.interfaces import IAttributeAnnotatable
-from zope.app.container import btree
-from zope.dublincore import property
-from zope.schema.fieldproperty import FieldProperty
-from keas.kmi import interfaces
-
-class Key(zope.location.Location, persistent.Persistent):
- zope.interface.implements(interfaces.IKey, IAttributeAnnotatable)
-
- created = property.DCProperty('created')
- creator = property.DCProperty('creator')
- key = FieldProperty(interfaces.IKey['key'])
-
- def __init__(self, key):
- self.key = key
-
- def __repr__(self):
- return '<%s %r>' %(self.__class__.__name__, self.key)
-
-
-class EncryptionService(object):
-
- cipher = 'aes_256_cbc'
-
- # Note: decryption fails if you use an empty initialization vector; but it
- # only fails when you restart the Python process. The length of the
- # initialization vector is assumed to be 16 bytes because that's what
- # openssl aes-256-cbc -nosalt -P -k 'a'
- # prints if you execute it on the command line
- initializationVector = '0123456789ABCDEF'
-
- def encrypt(self, key, data):
- """See interfaces.IEncryptionService"""
- # 1. Extract the encryption key
- encryptionKey = self.getEncryptionKey(key)
- # 2. Create a cipher object
- cipher = M2Crypto.EVP.Cipher(
- self.cipher, encryptionKey, self.initializationVector, 1)
- # 3. Feed the data to the cipher
- encrypted = cipher.update(data)
- encrypted += cipher.final()
- # 4. Return encrypted data
- return encrypted
-
- def decrypt(self, key, data):
- """See interfaces.IEncryptionService"""
- # 1. Extract the encryption key
- encryptionKey = self.getEncryptionKey(key)
- # 2. Create a cipher object
- cipher = M2Crypto.EVP.Cipher(
- self.cipher, encryptionKey, self.initializationVector, 0)
- # 3. Feed the data to the cipher
- decrypted = cipher.update(data)
- decrypted += cipher.final()
- # 4. Return encrypted data
- return decrypted
-
-
-class KeyManagementFacility(EncryptionService, btree.BTreeContainer):
- zope.interface.implements(interfaces.IExtendedKeyManagementFacility)
-
- rsaKeyLength = 512 # The length of the key encrypting key
- rsaKeyExponent = 161 # Should be sufficiently high and non-symmetric
- rsaPassphrase = 'key management facility'
- rsaPadding = M2Crypto.RSA.pkcs1_padding
-
- keyLength = rsaKeyLength/16
-
- def generate(self):
- """See interfaces.IKeyGenerationService"""
- # 1. Generate the private/public RSA key encrypting key
- rsa = M2Crypto.RSA.gen_key(
- self.rsaKeyLength, self.rsaKeyExponent,
- lambda x: None)
- # 2. Extract the private key from the RSA object
- buf = M2Crypto.BIO.MemoryBuffer('')
- rsa.save_key_bio(buf, None, lambda x: self.rsaPassphrase)
- privateKey = buf.getvalue()
- # 3. Generate the encryption key
- key = M2Crypto.Rand.rand_bytes(self.keyLength)
- # 4. Create the lookup key in the container
- hash = md5.new()
- hash.update(privateKey)
- # 5. Save the encryption key
- encryptedKey = rsa.public_encrypt(key, self.rsaPadding)
- self[hash.hexdigest()] = Key(encryptedKey)
- # 6. Return the private key encrypting key
- return privateKey
-
- def getEncryptionKey(self, key):
- """Given the key encrypting key, get the encryption key."""
- # 1. Create the lookup key in the container
- hash = md5.new()
- hash.update(key)
- # 2. Extract the encrypted encryption key
- encryptedKey = self[hash.hexdigest()].key
- # 3. Decrypt the key.
- rsa = M2Crypto.RSA.load_key_string(key)
- decryptedKey = rsa.private_decrypt(encryptedKey, self.rsaPadding)
- # 4. Return decrypted encryption key
- return decryptedKey
-
- def __repr__(self):
- return '<%s (%i)>' %(self.__class__.__name__, len(self))
-
-
-class LocalKeyManagementFacility(EncryptionService):
- """A local facility that requests keys from the master facility."""
- zope.interface.implements(interfaces.IKeyManagementFacility)
-
- timeout = 3600
- clientClass = client.RESTClient
-
- def __init__(self, url):
- self.url = url
- self._cache = {}
-
- def generate(self):
- """See interfaces.IKeyGenerationService"""
- client = self.clientClass(self.url)
- client.POST('/new')
- return client.contents
-
- def getEncryptionKey(self, key):
- """Given the key encrypting key, get the encryption key."""
- if (key in self._cache and
- self._cache[key][0] + self.timeout > time.time()):
- return self._cache[key][1]
- client = self.clientClass(self.url)
- client.POST('/key', key)
- encryptionKey = client.contents
- self._cache[key] = (time.time(), encryptionKey)
- return encryptionKey
-
- def __repr__(self):
- return '<%s %r>' %(self.__class__.__name__, self.url)
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/facility.py (from rev 90842, keas.kmi/trunk/src/keas/kmi/facility.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/facility.py (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/facility.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,163 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Implementation of Key Management Facility
+
+$Id$
+"""
+from __future__ import absolute_import
+__docformat__ = "reStructuredText"
+import M2Crypto
+import datetime
+import md5
+import persistent
+import time
+import zope.interface
+import zope.location
+from z3c.rest import client
+from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.app.container import btree
+from zope.dublincore import property
+from zope.schema.fieldproperty import FieldProperty
+from keas.kmi import interfaces
+
+class Key(zope.location.Location, persistent.Persistent):
+ zope.interface.implements(interfaces.IKey, IAttributeAnnotatable)
+
+ created = property.DCProperty('created')
+ creator = property.DCProperty('creator')
+ key = FieldProperty(interfaces.IKey['key'])
+
+ def __init__(self, key):
+ self.key = key
+
+ def __repr__(self):
+ return '<%s %r>' %(self.__class__.__name__, self.key)
+
+
+class EncryptionService(object):
+
+ cipher = 'aes_256_cbc'
+
+ # Note: decryption fails if you use an empty initialization vector; but it
+ # only fails when you restart the Python process. The length of the
+ # initialization vector is assumed to be 16 bytes because that's what
+ # openssl aes-256-cbc -nosalt -P -k 'a'
+ # prints if you execute it on the command line
+ initializationVector = '0123456789ABCDEF'
+
+ def encrypt(self, key, data):
+ """See interfaces.IEncryptionService"""
+ # 1. Extract the encryption key
+ encryptionKey = self.getEncryptionKey(key)
+ # 2. Create a cipher object
+ cipher = M2Crypto.EVP.Cipher(
+ self.cipher, encryptionKey, self.initializationVector, 1)
+ # 3. Feed the data to the cipher
+ encrypted = cipher.update(data)
+ encrypted += cipher.final()
+ # 4. Return encrypted data
+ return encrypted
+
+ def decrypt(self, key, data):
+ """See interfaces.IEncryptionService"""
+ # 1. Extract the encryption key
+ encryptionKey = self.getEncryptionKey(key)
+ # 2. Create a cipher object
+ cipher = M2Crypto.EVP.Cipher(
+ self.cipher, encryptionKey, self.initializationVector, 0)
+ # 3. Feed the data to the cipher
+ decrypted = cipher.update(data)
+ decrypted += cipher.final()
+ # 4. Return encrypted data
+ return decrypted
+
+
+class KeyManagementFacility(EncryptionService, btree.BTreeContainer):
+ zope.interface.implements(interfaces.IExtendedKeyManagementFacility)
+
+ rsaKeyLength = 512 # The length of the key encrypting key
+ rsaKeyExponent = 161 # Should be sufficiently high and non-symmetric
+ rsaPassphrase = 'key management facility'
+ rsaPadding = M2Crypto.RSA.pkcs1_padding
+
+ keyLength = rsaKeyLength/16
+
+ def generate(self):
+ """See interfaces.IKeyGenerationService"""
+ # 1. Generate the private/public RSA key encrypting key
+ rsa = M2Crypto.RSA.gen_key(
+ self.rsaKeyLength, self.rsaKeyExponent,
+ lambda x: None)
+ # 2. Extract the private key from the RSA object
+ buf = M2Crypto.BIO.MemoryBuffer('')
+ rsa.save_key_bio(buf, None, lambda x: self.rsaPassphrase)
+ privateKey = buf.getvalue()
+ # 3. Generate the encryption key
+ key = M2Crypto.Rand.rand_bytes(self.keyLength)
+ # 4. Create the lookup key in the container
+ hash = md5.new()
+ hash.update(privateKey)
+ # 5. Save the encryption key
+ encryptedKey = rsa.public_encrypt(key, self.rsaPadding)
+ self[hash.hexdigest()] = Key(encryptedKey)
+ # 6. Return the private key encrypting key
+ return privateKey
+
+ def getEncryptionKey(self, key):
+ """Given the key encrypting key, get the encryption key."""
+ # 1. Create the lookup key in the container
+ hash = md5.new()
+ hash.update(key)
+ # 2. Extract the encrypted encryption key
+ encryptedKey = self[hash.hexdigest()].key
+ # 3. Decrypt the key.
+ rsa = M2Crypto.RSA.load_key_string(key)
+ decryptedKey = rsa.private_decrypt(encryptedKey, self.rsaPadding)
+ # 4. Return decrypted encryption key
+ return decryptedKey
+
+ def __repr__(self):
+ return '<%s (%i)>' %(self.__class__.__name__, len(self))
+
+
+class LocalKeyManagementFacility(EncryptionService):
+ """A local facility that requests keys from the master facility."""
+ zope.interface.implements(interfaces.IKeyManagementFacility)
+
+ timeout = 3600
+ clientClass = client.RESTClient
+
+ def __init__(self, url):
+ self.url = url
+ self._cache = {}
+
+ def generate(self):
+ """See interfaces.IKeyGenerationService"""
+ client = self.clientClass(self.url)
+ client.post('/new')
+ return client.contents
+
+ def getEncryptionKey(self, key):
+ """Given the key encrypting key, get the encryption key."""
+ if (key in self._cache and
+ self._cache[key][0] + self.timeout > time.time()):
+ return self._cache[key][1]
+ client = self.clientClass(self.url)
+ client.post('/key', key, headers={'content-type': 'text/plain'})
+ encryptionKey = client.contents
+ self._cache[key] = (time.time(), encryptionKey)
+ return encryptionKey
+
+ def __repr__(self):
+ return '<%s %r>' %(self.__class__.__name__, self.url)
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/rest.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.py 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,44 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""REST-API to master key management facility
-
-$Id$
-"""
-from zope.publisher.browser import BrowserPage
-
-class RestView(BrowserPage):
-
- def __call__(self):
- method = self.request.method
- if not hasattr(self, method):
- self.request.response.setStatus(405)
- return 'Method not allowed.'
- return getattr(self, method)()
-
-class NewView(RestView):
-
- def GET(self):
- self.request.response.setHeader('content-type', 'text/plain')
- return self.context.generate()
-
- POST = GET
-
-class KeyView(RestView):
-
- def POST(self):
- stream = self.request.bodyStream.getCacheStream()
- stream.seek(0)
- key = stream.read()
- self.request.response.setHeader('content-type', 'text/plain')
- return self.context.getEncryptionKey(key)
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/rest.py (from rev 90843, keas.kmi/trunk/src/keas/kmi/rest.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/rest.py (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""REST-API to master key management facility
+
+$Id$
+"""
+from zope.publisher.browser import BrowserPage
+
+class RestView(BrowserPage):
+
+ def __call__(self):
+ method = self.request.method
+ if not hasattr(self, method):
+ self.request.response.setStatus(405)
+ return 'Method not allowed.'
+ return getattr(self, method)()
+
+class StatusView(RestView):
+
+ def GET(self):
+ self.request.response.setHeader('content-type', 'text/plain')
+ return 'KMS server holding %d keys' % len(self.context)
+
+class NewView(RestView):
+
+ def POST(self):
+ self.request.response.setHeader('content-type', 'text/plain')
+ return self.context.generate()
+
+class KeyView(RestView):
+
+ def POST(self):
+ stream = self.request.bodyStream.getCacheStream()
+ stream.seek(0)
+ key = stream.read()
+ self.request.response.setHeader('content-type', 'text/plain')
+ try:
+ return self.context.getEncryptionKey(key)
+ except KeyError:
+ self.request.response.setStatus(404)
+ return 'Key not found'
+
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml
===================================================================
--- keas.kmi/trunk/src/keas/kmi/rest.zcml 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,18 +0,0 @@
-<configure xmlns="http://namespaces.zope.org/zope"
- xmlns:browser="http://namespaces.zope.org/browser">
-
- <browser:page
- for=".interfaces.IKeyManagementFacility"
- name="new"
- class=".rest.NewView"
- permission="zope.Public"
- />
-
- <browser:page
- for=".interfaces.IKeyManagementFacility"
- name="key"
- class=".rest.KeyView"
- permission="zope.Public"
- />
-
-</configure>
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml (from rev 90838, keas.kmi/trunk/src/keas/kmi/rest.zcml)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/rest.zcml 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,30 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser">
+
+ <browser:defaultView
+ for=".interfaces.IKeyManagementFacility"
+ name="index.html"
+ />
+
+ <browser:page
+ for=".interfaces.IKeyManagementFacility"
+ name="index.html"
+ class=".rest.StatusView"
+ permission="zope.Public"
+ />
+
+ <browser:page
+ for=".interfaces.IKeyManagementFacility"
+ name="new"
+ class=".rest.NewView"
+ permission="zope.Public"
+ />
+
+ <browser:page
+ for=".interfaces.IKeyManagementFacility"
+ name="key"
+ class=".rest.KeyView"
+ permission="zope.Public"
+ />
+
+</configure>
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py (from rev 90835, keas.kmi/trunk/src/keas/kmi/testclient.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testclient.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,124 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import sys
+import optparse
+import textwrap
+
+from keas.kmi.facility import LocalKeyManagementFacility
+
+
+def ping(kmf):
+ client = kmf.clientClass(kmf.url)
+ print client.fullStatus
+ print
+ print client.contents
+
+
+def new_key(kmf):
+ sys.stdout.write(kmf.generate())
+
+
+def read_kek(kekfile):
+ try:
+ return file(kekfile, 'rb').read()
+ except IOError, e:
+ print >> sys.stderr, "Could not read key encrypting key from %s" % kekfile
+ print >> sys.stderr, e
+ sys.exit(1)
+
+
+def read_data(filename=None):
+ if not filename:
+ return sys.stdin.read()
+ else:
+ try:
+ return file(filename, 'rb').read()
+ except IOError, e:
+ print >> sys.stderr, "Could not read %s" % filename
+ print >> sys.stderr, e
+ sys.exit(1)
+
+
+def get_key(kmf, kekfile):
+ key_encrypting_key = read_kek(kekfile)
+ key = kmf.getEncryptionKey(key_encrypting_key)
+ sys.stdout.write(key)
+
+
+def encrypt(kmf, kekfile, filename=None):
+ key_encrypting_key = read_kek(kekfile)
+ data = read_data(filename)
+ encrypted = kmf.encrypt(key_encrypting_key, data)
+ sys.stdout.write(encrypted)
+
+
+def decrypt(kmf, kekfile, filename=None):
+ key_encrypting_key = read_kek(kekfile)
+ data = read_data(filename)
+ decrypted = kmf.decrypt(key_encrypting_key, data)
+ sys.stdout.write(decrypted)
+
+
+def main():
+ parser = optparse.OptionParser(textwrap.dedent("""\
+ usage: %prog URL
+ see if the server is alive
+
+ %prog URL -n > key.txt
+ generate a new key and key encrypting key
+
+ %prog URL -e key.txt data.txt > encrypted.txt
+ encrypt data
+
+ %prog URL -d key.txt encrytped.txt > data.txt
+ decrypt data
+
+ %prog URL -g key.txt > secretkey.bin
+ get the secret encryption key
+ """.rstrip()),
+ description="Client for a Key Management Server.")
+ parser.add_option('-n', '--new',
+ help='generate a new key',
+ action='store_const', dest='action',
+ const=new_key)
+ parser.add_option('-g', '--get-key',
+ help='get key',
+ action='store_const', dest='action',
+ const=get_key)
+ parser.add_option('-e', '--encrypt',
+ help='encrypt data',
+ action='store_const', dest='action',
+ const=encrypt)
+ parser.add_option('-d', '--decrypt',
+ help='decrypt data',
+ action='store_const', dest='action',
+ const=decrypt)
+ opts, args = parser.parse_args()
+ if not opts.action:
+ opts.action = ping
+ if not args:
+ parser.error('please specify the KMS server URL')
+
+ url = args.pop(0)
+ kmf = LocalKeyManagementFacility(url)
+
+ try:
+ opts.action(kmf, *args)
+ except TypeError:
+ parser.error('incorrect number of arguments')
Deleted: keas.kmi/tags/0.2.0/src/keas/kmi/testing.py
===================================================================
--- keas.kmi/trunk/src/keas/kmi/testing.py 2008-09-04 20:00:48 UTC (rev 90830)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testing.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -1,83 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2008 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""
-$Id$
-"""
-import md5
-import cStringIO
-from zope.publisher import browser
-from zope.interface import implements
-from keas.kmi import facility, rest, interfaces
-
-KeyEncyptingKey = '''-----BEGIN RSA PRIVATE KEY-----
-MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9
-rQlU/x/NWxG0vvFCnrDn7VvQN+syb3+a0DMCAgChAkAzKw3lwPxw0VVccq1J7qeO
-4DXR1iEMIoWruiCyq0aLkHnQzrZpaHnd4w+JNKIGOVDEWItf3iZNMXkoqj2hoPmp
-AiEA5kWTkYrfH+glJUfV/GvU6jcPSNctcJCnqTfMjQU0KEUCIQDU/R3iz5Lojw1S
-R1v6C5gdY/mrQydegHVGFS/p276KFwIgDk124UnRb7IyAlDI6xteUVSDVZ4Pivd+
-fP6yEkylbQkCIDo1Ri4VvzRtDsVkmFdqhcucHhIu+UTCHN6uVjy7QmIpAiB7Gl9m
-F4a4UlXVivb82J7ew3ZABnFIC9Q+dHW7WeZhxg==
------END RSA PRIVATE KEY-----
-'''
-
-EncryptedEncryptionKey = (
- '\xbc\x08\xdbo\x04\xe3\xc7G\x13\xd3\x86\x92\xfa\xe8i>,+\xda\xf8/B2]s\xd4'
- '\x10}[\xfd\x19\x98\xb1\xfa*V~U\xdf\t\x02\x01\xa6\xa8\xae\x8b\x8cm\xd9n'
- '\xd5\x83\xa1%k\x16lfuY\\q\x8c\x8b')
-
-class FakeRESTClient(object):
-
- context = None
-
- def __init__(self, url):
- self.url = url
-
- def POST(self, url, data=None):
- io = cStringIO.StringIO(data) if data else None
- request = browser.TestRequest(io)
- request.method = 'POST'
- if url == '/new':
- klass = rest.NewView
- elif url == '/key':
- klass = rest.KeyView
- else:
- raise ValueError(url)
-
- view = klass(self.context, request)
- self.contents = view()
-
-
-def setupRestApi(localFacility, masterFacility):
- MyFakeRESTClient = type(
- 'FakeRESTClient', (FakeRESTClient,), {'context': masterFacility})
- localFacility.clientClass = MyFakeRESTClient
-
-
-class TestingKeyManagementFacility(facility.KeyManagementFacility):
-
- def __init__(self):
- super(TestingKeyManagementFacility, self).__init__()
- hash = md5.new()
- hash.update(KeyEncyptingKey)
- md5Key = hash.hexdigest()
- self[md5Key] = facility.Key(EncryptedEncryptionKey)
-
- def generate(self):
- return KeyEncyptingKey
-
-
-class TestingKeyHolder(object):
- implements(interfaces.IKeyHolder)
- key = KeyEncyptingKey
-
Copied: keas.kmi/tags/0.2.0/src/keas/kmi/testing.py (from rev 90842, keas.kmi/trunk/src/keas/kmi/testing.py)
===================================================================
--- keas.kmi/tags/0.2.0/src/keas/kmi/testing.py (rev 0)
+++ keas.kmi/tags/0.2.0/src/keas/kmi/testing.py 2008-09-04 21:06:25 UTC (rev 90845)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+import md5
+import cStringIO
+from zope.publisher import browser
+from zope.interface import implements
+from keas.kmi import facility, rest, interfaces
+
+KeyEncyptingKey = '''-----BEGIN RSA PRIVATE KEY-----
+MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9
+rQlU/x/NWxG0vvFCnrDn7VvQN+syb3+a0DMCAgChAkAzKw3lwPxw0VVccq1J7qeO
+4DXR1iEMIoWruiCyq0aLkHnQzrZpaHnd4w+JNKIGOVDEWItf3iZNMXkoqj2hoPmp
+AiEA5kWTkYrfH+glJUfV/GvU6jcPSNctcJCnqTfMjQU0KEUCIQDU/R3iz5Lojw1S
+R1v6C5gdY/mrQydegHVGFS/p276KFwIgDk124UnRb7IyAlDI6xteUVSDVZ4Pivd+
+fP6yEkylbQkCIDo1Ri4VvzRtDsVkmFdqhcucHhIu+UTCHN6uVjy7QmIpAiB7Gl9m
+F4a4UlXVivb82J7ew3ZABnFIC9Q+dHW7WeZhxg==
+-----END RSA PRIVATE KEY-----
+'''
+
+EncryptedEncryptionKey = (
+ '\xbc\x08\xdbo\x04\xe3\xc7G\x13\xd3\x86\x92\xfa\xe8i>,+\xda\xf8/B2]s\xd4'
+ '\x10}[\xfd\x19\x98\xb1\xfa*V~U\xdf\t\x02\x01\xa6\xa8\xae\x8b\x8cm\xd9n'
+ '\xd5\x83\xa1%k\x16lfuY\\q\x8c\x8b')
+
+class FakeRESTClient(object):
+
+ context = None
+
+ def __init__(self, url):
+ self.url = url
+
+ def post(self, url, data=None, headers={}):
+ io = cStringIO.StringIO(data) if data else None
+ request = browser.TestRequest(io)
+ request.method = 'POST'
+ if url == '/new':
+ klass = rest.NewView
+ elif url == '/key':
+ if headers.get('content-type') != 'text/plain':
+ # ensure we don't trip on
+ # http://trac.pythonpaste.org/pythonpaste/ticket/294
+ raise ValueError('bad content type')
+ klass = rest.KeyView
+ else:
+ raise ValueError(url)
+
+ view = klass(self.context, request)
+ self.contents = view()
+
+
+def setupRestApi(localFacility, masterFacility):
+ MyFakeRESTClient = type(
+ 'FakeRESTClient', (FakeRESTClient,), {'context': masterFacility})
+ localFacility.clientClass = MyFakeRESTClient
+
+
+class TestingKeyManagementFacility(facility.KeyManagementFacility):
+
+ def __init__(self):
+ super(TestingKeyManagementFacility, self).__init__()
+ hash = md5.new()
+ hash.update(KeyEncyptingKey)
+ md5Key = hash.hexdigest()
+ self[md5Key] = facility.Key(EncryptedEncryptionKey)
+
+ def generate(self):
+ return KeyEncyptingKey
+
+
+class TestingKeyHolder(object):
+ implements(interfaces.IKeyHolder)
+ key = KeyEncyptingKey
+
More information about the Checkins
mailing list