Adding script for rewrapping p11 KEKs

This script pulls all project available KEKs and rewraps them
with a MKEK specified in the barbican config file.

Change-Id: I5f130b8f6d744195e3ed6c708e96b23b200eea2b
This commit is contained in:
John Vrbanac 2015-06-26 11:59:02 -05:00
parent a2003a9c10
commit 9d5b06ba7e
4 changed files with 211 additions and 1 deletions

143
barbican/cmd/pkcs11_kek_rewrap.py Executable file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import json
import traceback
import sqlalchemy
from sqlalchemy import orm
from sqlalchemy.orm import scoping
from barbican.common import utils
from barbican.model import models
from barbican.plugin.crypto import p11_crypto
# Use config values from p11_crypto
CONF = p11_crypto.CONF
class KekRewrap(object):
def __init__(self, db_connection, library_path, login, slot_id,
new_mkek_label):
self.dry_run = False
self.new_label = new_mkek_label
self.db_engine = sqlalchemy.create_engine(db_connection)
self._session_creator = scoping.scoped_session(
orm.sessionmaker(
bind=self.db_engine,
autocommit=True
)
)
self.crypto_plugin = p11_crypto.P11CryptoPlugin(CONF)
self.pkcs11 = self.crypto_plugin.pkcs11
self.plugin_name = utils.generate_fullname_for(self.crypto_plugin)
def rewrap_kek(self, project, kek):
with self.db_session.begin():
meta_dict = json.loads(kek.plugin_meta)
if self.dry_run:
msg = 'Would have unwrapped key with {} and rewrapped with {}'
print(msg.format(meta_dict['mkek_label'], self.new_label))
print('Would have updated KEKDatum in db {}'.format(kek.id))
else:
hsm_session = self.pkcs11.create_working_session()
print('Rewrapping KEK {}'.format(kek.id))
print('Pre-change IV: {}, Wrapped Key: {}'.format(
meta_dict['iv'], meta_dict['wrapped_key']))
updated_meta = self.pkcs11.rewrap_kek(
iv=meta_dict['iv'],
wrapped_key=meta_dict['wrapped_key'],
hmac=meta_dict['hmac'],
mkek_label=meta_dict['mkek_label'],
hmac_label=meta_dict['hmac_label'],
key_length=32,
session=hsm_session
)
self.pkcs11.close_session(hsm_session)
print('Post-change IV: {}, Wrapped Key: {}'.format(
updated_meta['iv'], updated_meta['wrapped_key']))
# Update KEK metadata in DB
kek.plugin_meta = json.dumps(updated_meta)
def get_keks_for_project(self, project):
keks = []
with self.db_session.begin() as transaction:
print('Retrieving KEKs for Project {}'.format(project.id))
query = transaction.session.query(models.KEKDatum)
query = query.filter_by(project_id=project.id)
query = query.filter_by(plugin_name=self.plugin_name)
keks = query.all()
return keks
def get_projects(self):
print('Retrieving all available projects')
projects = []
with self.db_session.begin() as transaction:
projects = transaction.session.query(models.Project).all()
return projects
@property
def db_session(self):
return self._session_creator()
def execute(self, dry_run=True):
self.dry_run = dry_run
if self.dry_run:
print('-- Running in dry-run mode --')
projects = self.get_projects()
for project in projects:
keks = self.get_keks_for_project(project)
for kek in keks:
try:
self.rewrap_kek(project, kek)
except Exception:
print('Error occurred! SQLAlchemy automatically rolled-'
'back the transaction')
traceback.print_exc()
def main():
script_desc = ('Utility to re-wrap project KEKs after rotating an MKEK.')
parser = argparse.ArgumentParser(description=script_desc)
parser.add_argument(
'--dry-run',
action='store_true',
help='Displays changes that will be made (Non-destructive)'
)
args = parser.parse_args()
rewrapper = KekRewrap(
db_connection=CONF.sql_connection,
library_path=CONF.p11_crypto_plugin.library_path,
login=CONF.p11_crypto_plugin.login,
slot_id=CONF.p11_crypto_plugin.slot_id,
new_mkek_label=CONF.p11_crypto_plugin.mkek_label
)
rewrapper.execute(args.dry_run)
if __name__ == '__main__':
main()

View File

@ -485,7 +485,7 @@ class PKCS11(object):
ck_attributes = self.build_attributes([
Attribute(CKA_CLASS, CKO_SECRET_KEY),
Attribute(CKA_KEY_TYPE, CKK_AES),
Attribute(CKA_LABEL, mkek_label)
Attribute(CKA_LABEL, str(mkek_label))
])
rv = self.lib.C_FindObjectsInit(
session, ck_attributes.template, len(ck_attributes.template)
@ -542,6 +542,44 @@ class PKCS11(object):
self.check_error(rv)
return object_handle_ptr[0]
def rewrap_kek(self, iv, wrapped_key, hmac, mkek_label, hmac_label,
key_length, session):
unwrapped_kek = self.unwrap_key(iv, hmac, wrapped_key, mkek_label,
hmac_label, session)
mkek = self.key_handles[self.current_mkek_label]
iv = self.generate_random(16, session)
mech = self.ffi.new("CK_MECHANISM *")
mech.mechanism = CKM_AES_CBC_PAD
mech.parameter = iv
mech.parameter_len = 16
padded_length = key_length + self.block_size
buf = self.ffi.new("CK_BYTE[{0}]".format(padded_length))
buf_len = self.ffi.new("CK_ULONG *", padded_length)
rv = self.lib.C_WrapKey(
session,
mech,
mkek,
unwrapped_kek,
buf,
buf_len
)
self.check_error(rv)
wrapped_kek = self.ffi.buffer(buf, buf_len[0])[:]
hmac = self.compute_hmac(wrapped_kek, session)
return {
'iv': base64.b64encode(self.ffi.buffer(iv)[:]),
'wrapped_key': base64.b64encode(wrapped_kek),
'hmac': base64.b64encode(hmac),
'mkek_label': self.current_mkek_label,
'hmac_label': self.current_hmac_label
}
def generate_wrapped_kek(self, kek_label, key_length, session):
# generate a non-persistent key that is extractable
ck_attributes = self.build_attributes([

View File

@ -232,6 +232,34 @@ class WhenTestingP11CryptoPlugin(utils.BaseTestCase):
self.assertEqual(self.lib.C_UnwrapKey.call_count, 1)
self.assertEqual(self.lib.C_Verify.call_count, 1)
def test_rewrap_kek(self):
plugin_meta = {
'iv': base64.b64encode(b"\x00" * 16),
'hmac': base64.b64encode(b"\x00" * 32),
'wrapped_key': base64.b64encode(b"\x00" * 48),
'mkek_label': 'mkek',
'hmac_label': 'hmac',
}
self.lib.C_WrapKey.return_value = pkcs11.CKR_OK
self.lib.C_UnwrapKey.return_value = pkcs11.CKR_OK
self.lib.C_VerifyInit.return_value = pkcs11.CKR_OK
self.lib.C_Verify.return_value = pkcs11.CKR_OK
self.lib.C_SignInit.return_value = pkcs11.CKR_OK
self.lib.C_Sign.return_value = pkcs11.CKR_OK
self.plugin.pkcs11.rewrap_kek(
plugin_meta['iv'],
plugin_meta['wrapped_key'],
plugin_meta['hmac'],
plugin_meta['mkek_label'],
plugin_meta['hmac'],
32,
self.test_session
)
self.assertEqual(self.lib.C_UnwrapKey.call_count, 1)
self.assertEqual(self.lib.C_WrapKey.call_count, 1)
self.assertEqual(self.lib.C_Verify.call_count, 1)
def test_generate_asymmetric_raises_error(self):
self.assertRaises(NotImplementedError,
self.plugin.generate_asymmetric,

View File

@ -27,6 +27,7 @@ console_scripts =
barbican-keystone-listener = barbican.cmd.keystone_listener:main
barbican-worker = barbican.cmd.worker:main
barbican-worker-retry-scheduler = barbican.cmd.worker_retry_scheduler:main
pkcs11-kek-rewrap = barbican.cmd.pkcs11_kek_rewrap:main
barbican.secretstore.plugin =
store_crypto = barbican.plugin.store_crypto:StoreCryptoAdapterPlugin