Update P11 plugin to function & match new plugin contract.
add plugin to entry_points and more cleanup Change-Id: Ie3c742e3071912f3a22d7460cfb79b1a383efb6f Implementing: blueprint crypto-implement-hsm-plugin
This commit is contained in:
parent
5efef9fbce
commit
7d7e918953
@ -2,6 +2,8 @@
|
||||
# This code is disabled just enough to pass tox tests, but once full
|
||||
# integration into Barbican is achieved, this code should re-enabled.
|
||||
|
||||
import base64
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from barbican.common import exception
|
||||
@ -14,9 +16,28 @@ from barbican.openstack.common.gettextutils import _
|
||||
# TODO: Remove this:
|
||||
PyKCS11 = {}
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
p11_crypto_plugin_group = cfg.OptGroup(name='p11_crypto_plugin',
|
||||
title="PKCS11 Crypto Plugin Options")
|
||||
p11_crypto_plugin_opts = [
|
||||
cfg.StrOpt('library_path',
|
||||
default=None,
|
||||
help=_('Path to vendor PKCS11 library')),
|
||||
cfg.StrOpt('login',
|
||||
default=None,
|
||||
help=_('Password to login to PKCS11 session'))
|
||||
]
|
||||
CONF.register_group(p11_crypto_plugin_group)
|
||||
CONF.register_opts(p11_crypto_plugin_opts, group=p11_crypto_plugin_group)
|
||||
|
||||
|
||||
class P11CryptoPluginKeyException(exception.BarbicanException):
|
||||
message = _("More than one key found for label")
|
||||
|
||||
|
||||
class P11CryptoPluginException(exception.BarbicanException):
|
||||
message = _("TODO") # TODO
|
||||
message = _("General exception")
|
||||
|
||||
|
||||
class P11CryptoPlugin(CryptoPluginBase):
|
||||
@ -26,24 +47,29 @@ class P11CryptoPlugin(CryptoPluginBase):
|
||||
This implementation currently relies on an unreleased fork of PyKCS11.
|
||||
"""
|
||||
|
||||
def __init__(self, conf=cfg.CONF):
|
||||
self.block_size = 16
|
||||
self.kek_key_length = 32
|
||||
self.pkcs11 = PyKCS11.PyKCS11Lib()
|
||||
# TODO check if conf crypto path is none
|
||||
#self.pkcs11.load(conf.crypto.p11_crypto_plugin_lib_path)
|
||||
self.pkcs11.load('/usr/lib/libCryptoki2_64.so') # TODO: load from conf
|
||||
def __init__(self, conf=cfg.CONF, library=None):
|
||||
self.block_size = 16 # in bytes
|
||||
self.kek_key_length = 32 # in bytes (256-bit)
|
||||
self.algorithm = 0x8000011c # CKM_AES_GCM vendor prefixed.
|
||||
if library is not None:
|
||||
self.pkcs11 = library
|
||||
else:
|
||||
self.pkcs11 = PyKCS11.PyKCS11Lib()
|
||||
if conf.p11_crypto_plugin.library_path is None:
|
||||
raise ValueError(_("library_path is required"))
|
||||
else:
|
||||
self.pkcs11.load(conf.p11_crypto_plugin.library_path)
|
||||
# initialize the library. PyKCS11 does not supply this for free
|
||||
self._check_error(self.pkcs11.lib.C_Initialize())
|
||||
# TODO: check if session stays open/reopen when closed
|
||||
self.session = self.pkcs11.openSession(1)
|
||||
self.session.login(conf.p11_crypto_plugin.login)
|
||||
self.rw_session = self.pkcs11.openSession(1, PyKCS11.CKF_RW_SESSION)
|
||||
self.rw_session.login(conf.p11_crypto_plugin.login)
|
||||
|
||||
def _pad(self, unencrypted):
|
||||
"""Adds padding to unencrypted byte string."""
|
||||
pad_length = self.block_size - (
|
||||
len(unencrypted) % self.block_size
|
||||
)
|
||||
pad_length = self.block_size - (len(unencrypted) % self.block_size)
|
||||
return unencrypted + (chr(pad_length) * pad_length)
|
||||
|
||||
def _strip_pad(self, unencrypted):
|
||||
@ -65,110 +91,65 @@ class P11CryptoPlugin(CryptoPluginBase):
|
||||
if len(keys) == 1:
|
||||
return keys[0]
|
||||
elif len(keys) == 0:
|
||||
return None, None
|
||||
return None
|
||||
elif len(keys) > 1:
|
||||
raise P11CryptoPluginException() # TODO:make this a mega exception
|
||||
|
||||
def _get_current_key_label_for_tenant(self, tenant):
|
||||
key_label = self.repo.get_key(tenant) # TODO
|
||||
return key_label
|
||||
|
||||
# TODO: jwood: No longer needed...see bind_kek_metadata() below...
|
||||
# def _generate_key_for_tenant(self, tenant):
|
||||
# # TODO: uuid generation from sufficient entropy?
|
||||
# key_label = "tenant-{0}-key-{1}".format(tenant.tenant_id,
|
||||
# uuid.uuid4())
|
||||
# template = (
|
||||
# (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
|
||||
# (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
|
||||
# (PyKCS11.CKA_VALUE_LEN, self.kek_key_length),
|
||||
# (PyKCS11.CKA_LABEL, key_label),
|
||||
# (PyKCS11.CKA_PRIVATE, True),
|
||||
# (PyKCS11.CKA_SENSITIVE, True),
|
||||
# (PyKCS11.CKA_ENCRYPT, True),
|
||||
# (PyKCS11.CKA_DECRYPT, True),
|
||||
# #(PyKCS11.CKA_TOKEN, True), # TODO: enable this (saves to HSM)
|
||||
# (PyKCS11.CKA_WRAP, True),
|
||||
# (PyKCS11.CKA_UNWRAP, True),
|
||||
# # TODO: make these unextractable if feasible
|
||||
# (PyKCS11.CKA_EXTRACTABLE, True))
|
||||
# ckattr = self.session._template2ckattrlist(template)
|
||||
#
|
||||
# m = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
|
||||
# key = PyKCS11.CK_OBJECT_HANDLE()
|
||||
# self._check_error(
|
||||
# self.pkcs11.lib.C_GenerateKey(self.session.session, m, ckattr,
|
||||
# key)
|
||||
# )
|
||||
# self.repo.write_key(key_label, tenant) # TODO: write key
|
||||
# return (key, key_label)
|
||||
|
||||
def _build_kek_metadata(self, mechanism, key_label, iv):
|
||||
# TODO: CBC, default (exception?)
|
||||
encryption_type = {
|
||||
PyKCS11.CKM_AES_ECB: 'AES ECB',
|
||||
PyKCS11.CKM_AES_CBC_PAD: 'AES CBC PAD',
|
||||
# TODO: determine if PKCS11 GCM pads automatically
|
||||
PyKCS11.CKM_AES_GCM: 'AES GCM'
|
||||
}[mechanism.mechanism]
|
||||
|
||||
kek_metadata = json.dumps({
|
||||
'plugin': 'P11CryptoPlugin',
|
||||
'encryption': encryption_type,
|
||||
'kek_length': self.kek_key_length,
|
||||
'kek': key_label
|
||||
})
|
||||
return kek_metadata
|
||||
|
||||
def encrypt(self, unencrypted, kek_metadata, tenant):
|
||||
padded_data = self._pad(unencrypted)
|
||||
|
||||
key = self._get_key_by_label(kek_metadata.kek_label)
|
||||
|
||||
# TODO: jwood: No longer needed:
|
||||
# key_label = self._get_current_key_label_for_tenant(tenant)
|
||||
# if key_label:
|
||||
# key = self._get_key_by_label(key_label)
|
||||
# else:
|
||||
# key, key_label = self._generate_key_for_tenant(tenant)
|
||||
raise P11CryptoPluginKeyException()
|
||||
|
||||
def _generate_iv(self):
|
||||
iv = self.session.generateRandom(self.block_size)
|
||||
mech = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
|
||||
iv = b''.join(chr(i) for i in iv)
|
||||
if len(iv) != self.block_size:
|
||||
raise P11CryptoPluginException()
|
||||
return iv
|
||||
|
||||
def _build_gcm_params(self, iv):
|
||||
gcm = PyKCS11.LowLevel.CK_AES_GCM_PARAMS()
|
||||
gcm.pIv = iv
|
||||
gcm.ulIvLen = len(iv)
|
||||
gcm.ulIvBits = len(iv) * 8
|
||||
gcm.ulTagBits = 128
|
||||
return gcm
|
||||
|
||||
def encrypt(self, unencrypted, kek_meta_tenant, tenant):
|
||||
# TODO: GCM should not require padding.
|
||||
padded_data = self._pad(unencrypted)
|
||||
key = self._get_key_by_label(kek_meta_tenant.kek_label)
|
||||
iv = self.generate_iv()
|
||||
gcm = self._build_gcm_params(iv)
|
||||
mech = PyKCS11.Mechanism(self.algorithm, gcm)
|
||||
encrypted = self.session.encrypt(key, padded_data, mech)
|
||||
cyphertext = b''.join(chr(i) for i in encrypted)
|
||||
kek_meta_extended = json.dumps({
|
||||
'iv': base64.b64encode(iv)
|
||||
})
|
||||
|
||||
# TODO: jwood No longer needed???: kek_metadata = self
|
||||
# ._build_kek_metadata(mech, key_label, iv)
|
||||
|
||||
return cyphertext, None # TODO: jwood kek_metadata return not needed?
|
||||
return cyphertext, kek_meta_extended
|
||||
|
||||
def decrypt(self, encrypted, kek_meta_tenant, kek_meta_extended, tenant):
|
||||
# TODO: jwood Metadata coming in now...kek_info = json.loads(
|
||||
# kek_metadata)
|
||||
key, iv = self._get_key_by_label(
|
||||
kek_meta_tenant.kek_label) # TODO: jwood
|
||||
# kek_info['kek']) # TODO: get IV
|
||||
mech = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
|
||||
key = self._get_key_by_label(kek_meta_tenant.kek_label)
|
||||
meta_extended = json.loads(kek_meta_extended)
|
||||
iv = base64.b64decode(meta_extended['iv'])
|
||||
gcm = self._build_gcm_params(iv)
|
||||
mech = PyKCS11.Mechanism(self.algorithm, gcm)
|
||||
decrypted = self.session.decrypt(key, encrypted, mech)
|
||||
padded_secret = b''.join(chr(i) for i in decrypted)
|
||||
return self._strip_pad(padded_secret)
|
||||
|
||||
# TODO: jwood: This is a new method, to generate a key in the HSM for the
|
||||
# metadata kek_label.
|
||||
def bind_kek_metadata(self, kek_metadata):
|
||||
# Enforce idempotency: If we've already generated a key for the
|
||||
# kek_label, leave now.
|
||||
key, iv = self._get_key_by_label(kek_metadata.kek_label)
|
||||
key = self._get_key_by_label(kek_metadata.kek_label)
|
||||
if key:
|
||||
return
|
||||
|
||||
# To be persisted by Barbican:
|
||||
kek_metadata.algorithm = 'AES CBC PAD'
|
||||
kek_metadata.bit_length = self.kek_key_length
|
||||
kek_metadata.mode = None
|
||||
kek_metadata.algorithm = 'AES'
|
||||
kek_metadata.bit_length = self.kek_key_length * 8
|
||||
kek_metadata.mode = 'GCM'
|
||||
kek_metadata.plugin_meta = None
|
||||
|
||||
# Generate the key.
|
||||
# TODO: review template to ensure it's what we want
|
||||
template = (
|
||||
(PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
|
||||
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
|
||||
@ -178,17 +159,21 @@ class P11CryptoPlugin(CryptoPluginBase):
|
||||
(PyKCS11.CKA_SENSITIVE, True),
|
||||
(PyKCS11.CKA_ENCRYPT, True),
|
||||
(PyKCS11.CKA_DECRYPT, True),
|
||||
#(PyKCS11.CKA_TOKEN, True), # TODO: enable this (saves to HSM)
|
||||
(PyKCS11.CKA_TOKEN, True),
|
||||
(PyKCS11.CKA_WRAP, True),
|
||||
(PyKCS11.CKA_UNWRAP, True),
|
||||
# TODO: make these unextractable if feasible
|
||||
(PyKCS11.CKA_EXTRACTABLE, True))
|
||||
(PyKCS11.CKA_EXTRACTABLE, False))
|
||||
ckattr = self.session._template2ckattrlist(template)
|
||||
|
||||
m = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
|
||||
key = PyKCS11.CK_OBJECT_HANDLE()
|
||||
self._check_error(
|
||||
self.pkcs11.lib.C_GenerateKey(self.session.session, m, ckattr, key)
|
||||
self.pkcs11.lib.C_GenerateKey(
|
||||
self.rw_session.session,
|
||||
m,
|
||||
ckattr,
|
||||
key
|
||||
)
|
||||
)
|
||||
|
||||
def create(self, algorithm, bit_length):
|
||||
@ -197,7 +182,7 @@ class P11CryptoPlugin(CryptoPluginBase):
|
||||
byte_length = bit_length / 8
|
||||
rand = self.session.generateRandom(byte_length)
|
||||
if len(rand) != byte_length:
|
||||
raise P11CryptoPluginException() # TODO: revisit exceptions
|
||||
raise P11CryptoPluginException()
|
||||
return rand
|
||||
|
||||
def supports(self, secret_type):
|
||||
|
1
setup.py
1
setup.py
@ -75,6 +75,7 @@ setup(
|
||||
py_modules=[],
|
||||
entry_points="""
|
||||
[barbican.crypto.plugin]
|
||||
p11_crypto = barbican.crypto.p11plugin:P11CryptoPlugin
|
||||
simple_crypto = barbican.crypto.plugin:SimpleCryptoPlugin
|
||||
|
||||
[barbican.test.crypto.plugin]
|
||||
|
Loading…
x
Reference in New Issue
Block a user