summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-10-16 13:58:50 -0400
committerSimo Sorce <simo@redhat.com>2015-10-23 14:11:07 -0400
commitcfa5aff186fe0e321d8fe1c191834d157a5e8162 (patch)
tree54d2328b1cbb949b8e6f98b2d93336051d391b64
parentedd5cd333e2465f5b79a063243afd61e65d6d82b (diff)
downloadcustodia-cfa5aff186fe0e321d8fe1c191834d157a5e8162.tar.gz
custodia-cfa5aff186fe0e321d8fe1c191834d157a5e8162.tar.xz
custodia-cfa5aff186fe0e321d8fe1c191834d157a5e8162.zip
Add a generic encrypting layer for storage
This plugin takes a nother store to use and ecnrypts all content. note: it does not encrypt key names nor the containers Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Christian Heimes <cheimes@redhat.com>
-rw-r--r--custodia.conf18
-rwxr-xr-xcustodia/custodia1
-rw-r--r--custodia/store/encgen.py55
-rw-r--r--tests/custodia.py45
4 files changed, 119 insertions, 0 deletions
diff --git a/custodia.conf b/custodia.conf
index c77cf8b..35065d1 100644
--- a/custodia.conf
+++ b/custodia.conf
@@ -95,3 +95,21 @@ forward_headers = {"CUSTODIA_AUTH_ID": "test", "CUSTODIA_AUTH_KEY": "foo-host-ke
handler = custodia.forwarder.Forwarder
forward_uri = http+unix://%2e%2fserver_socket/forwarder_loop
forward_headers = {"REMOTE_USER": "test"}
+
+# Encgen example
+[store:backing]
+handler = custodia.store.sqlite.SqliteStore
+dburi = examples/enclite.db
+table = enclite
+
+[store:overlay]
+handler = custodia.store.encgen.EncryptedOverlay
+backing_store = backing
+master_key = examples/enclite.sample.key
+master_enctype = A128CBC-HS256
+
+[authz:kemgen]
+handler = custodia.message.kem.KEMKeysStore
+path = /encgen/secrets/
+store = overlay
+
diff --git a/custodia/custodia b/custodia/custodia
index 8d69dea..36074cc 100755
--- a/custodia/custodia
+++ b/custodia/custodia
@@ -102,6 +102,7 @@ def parse_config(cfgfile):
attach_store('auth:', config['authenticators'], config['stores'])
attach_store('authz:', config['authorizers'], config['stores'])
attach_store('', config['consumers'], config['stores'])
+ attach_store('store:', config['stores'], config['stores'])
return config
diff --git a/custodia/store/encgen.py b/custodia/store/encgen.py
new file mode 100644
index 0000000..671f152
--- /dev/null
+++ b/custodia/store/encgen.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2015 Custodia Project Contributors - see LICENSE file
+
+from jwcrypto.common import json_decode, json_encode
+from jwcrypto.jwe import JWE
+from jwcrypto.jwk import JWK
+
+from custodia.store.interface import CSStore, CSStoreError
+
+
+class EncryptedOverlay(CSStore):
+
+ def __init__(self, config):
+ super(EncryptedOverlay, self).__init__(config)
+
+ if 'backing_store' not in config:
+ raise ValueError('Missing "backing_store" for Encrypted Store')
+ self.store_name = config['backing_store']
+ self.store = None
+
+ if 'master_key' not in config:
+ raise ValueError('Missing "master_key" for Encrypted Store')
+
+ with open(config['master_key']) as f:
+ data = f.read()
+ key = json_decode(data)
+ self.mkey = JWK(**key)
+
+ self.enc = config.get('master_enctype', 'A256CBC_HS512')
+
+ def get(self, key):
+ value = self.store.get(key)
+ if value is None:
+ return None
+ try:
+ jwe = JWE()
+ jwe.deserialize(value, self.mkey)
+ return jwe.payload.decode('utf-8')
+ except Exception as err:
+ self.logger.error("Error parsing key %s: [%r]" % (key, repr(err)))
+ raise CSStoreError('Error occurred while trying to parse key')
+
+ def set(self, key, value, replace=False):
+ jwe = JWE(value, json_encode({'alg': 'dir', 'enc': self.enc}))
+ jwe.add_recipient(self.mkey)
+ cvalue = jwe.serialize(compact=True)
+ return self.store.set(key, cvalue, replace)
+
+ def span(self, key):
+ return self.store.span(key)
+
+ def list(self, keyfilter=''):
+ return self.store.list(keyfilter)
+
+ def cut(self, key):
+ return self.store.cut(key)
diff --git a/tests/custodia.py b/tests/custodia.py
index 0817d81..cce1c40 100644
--- a/tests/custodia.py
+++ b/tests/custodia.py
@@ -10,6 +10,8 @@ import unittest
from string import Template
+from jwcrypto import jwk
+
from requests.exceptions import HTTPError
from custodia.client import CustodiaClient
@@ -69,6 +71,21 @@ forward_headers = {"CUSTODIA_AUTH_ID": "${TEST_AUTH_ID}", \
handler = custodia.forwarder.Forwarder
forward_uri = ${SOCKET_URL}/forwarder_loop
forward_headers = {"REMOTE_USER": "test"}
+
+# Encgen
+[store:encgen]
+handler = custodia.store.encgen.EncryptedOverlay
+backing_store = simple
+master_key = test_mkey.conf
+master_enctype = A128CBC-HS256
+
+[authz:enc]
+handler = custodia.httpd.authorizers.SimplePathAuthz
+paths = /enc
+
+[/enc]
+handler = custodia.secrets.Secrets
+store = encgen
"""
@@ -83,6 +100,12 @@ def unlink_if_exists(filename):
raise
+def generate_key(filename):
+ key = jwk.JWK(generate='oct', size=256)
+ with (open(filename, 'w+')) as keyfile:
+ keyfile.write(key.export())
+
+
class CustodiaTests(unittest.TestCase):
@classmethod
@@ -92,12 +115,14 @@ class CustodiaTests(unittest.TestCase):
pexec = env.get('CUSTODIAPYTHON', 'python')
unlink_if_exists('test_socket')
unlink_if_exists('test_secrets.db')
+ unlink_if_exists('test_mkey.conf')
unlink_if_exists('test_custodia.conf')
unlink_if_exists('test_log.txt')
unlink_if_exists('test_audit.log')
cls.socket_url = TEST_SOCKET_URL
cls.test_auth_id = "test_user"
cls.test_auth_key = "cd54b735-e756-4f12-aa18-d85509baef36"
+ generate_key('test_mkey.conf')
with (open('test_custodia.conf', 'w+')) as conffile:
t = Template(TEST_CUSTODIA_CONF)
conf = t.substitute({'SOCKET_URL': cls.socket_url,
@@ -121,6 +146,8 @@ class CustodiaTests(unittest.TestCase):
cls.fwd.headers['REMOTE_USER'] = 'test'
cls.loop = CustodiaClient(cls.socket_url + '/forwarder_loop')
cls.loop.headers['REMOTE_USER'] = 'test'
+ cls.enc = CustodiaClient(cls.socket_url + '/enc')
+ cls.enc.headers['REMOTE_USER'] = 'enc'
@classmethod
def tearDownClass(cls):
@@ -179,3 +206,21 @@ class CustodiaTests(unittest.TestCase):
self.loop.list_container('test')
except HTTPError as e:
self.assertEqual(e.response.status_code, 502)
+
+ def test_A_enc_1_create_container(self):
+ self.enc.create_container('container')
+ r = self.enc.list_container('container')
+ self.assertEqual(r.json(), [])
+ self.enc.delete_container('container')
+ try:
+ self.enc.list_container('container')
+ except HTTPError as e:
+ self.assertEqual(e.response.status_code, 404)
+
+ def test_A_enc_2_set_simple_key(self):
+ self.enc.create_container('enc')
+ self.enc.set_simple_key('enc/key', 'simple')
+ key = self.admin.get_simple_key('enc/key')
+ self.assertNotEqual(key, 'simple')
+ key = self.enc.get_simple_key('enc/key')
+ self.assertEqual(key, 'simple')