Allow multiple token labels for PKCS#11 driver
This patch changes the slot selection logic to look for more than one token label in the list of slots, using the first token that is found. This change is required to enable load balancing with devices that use separate tokens for this feature. This patch adds a new option in the [p11_crypto_plugin] section "token_labels", and deprecates the "token_label" option. For backwards compatibility, the "token_label" option will still be used if present. Change-Id: Ic2b85246c37e856c38cb47613313b19e653118de
This commit is contained in:
parent
fa7722bfc5
commit
1ca03610d7
@ -334,7 +334,7 @@ class HSMCommands(object):
|
||||
encryption_mechanism='CKM_AES_CBC',
|
||||
hmac_keywrap_mechanism=hmacwrap,
|
||||
token_serial_number=conf.p11_crypto_plugin.token_serial_number,
|
||||
token_label=conf.p11_crypto_plugin.token_label
|
||||
token_labels=conf.p11_crypto_plugin.token_labels
|
||||
)
|
||||
self.session = self.pkcs11.get_session()
|
||||
|
||||
|
@ -38,12 +38,17 @@ p11_crypto_plugin_opts = [
|
||||
help=u._('Path to vendor PKCS11 library')),
|
||||
cfg.StrOpt('token_serial_number',
|
||||
help=u._('Token serial number used to identify the token to be '
|
||||
'used. Required when the device has multiple tokens '
|
||||
'with the same label.')),
|
||||
'used.')),
|
||||
cfg.StrOpt('token_label',
|
||||
help=u._('Token label used to identify the token to '
|
||||
'be used. Required when token_serial_number is '
|
||||
'not specified.')),
|
||||
deprecated_for_removal=True,
|
||||
help=u._('DEPRECATED: Use token_labels instead. '
|
||||
'Token label used to identify the token to '
|
||||
'be used.')),
|
||||
cfg.ListOpt('token_labels',
|
||||
help=u._('List of labels for one or more tokens to be used. '
|
||||
'Typically this is a single label, but some HSM '
|
||||
'devices may require more than one label for Load '
|
||||
'Balancing or High Availability configurations.')),
|
||||
cfg.StrOpt('login',
|
||||
help=u._('Password to login to PKCS11 session'),
|
||||
secret=True),
|
||||
@ -128,23 +133,41 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
def __init__(self, conf=CONF, ffi=None, pkcs11=None):
|
||||
self.conf = conf
|
||||
plugin_conf = conf.p11_crypto_plugin
|
||||
if plugin_conf.library_path is None:
|
||||
raise ValueError(u._("library_path is required"))
|
||||
|
||||
# Use specified or create new pkcs11 object
|
||||
self.pkcs11 = pkcs11 or self._create_pkcs11(plugin_conf, ffi)
|
||||
|
||||
# Save conf arguments
|
||||
if plugin_conf.library_path is None:
|
||||
raise ValueError(u._("library_path is required"))
|
||||
self.library_path = plugin_conf.library_path
|
||||
|
||||
self.encryption_mechanism = plugin_conf.encryption_mechanism
|
||||
self.generate_iv = plugin_conf.aes_gcm_generate_iv
|
||||
self.cka_sensitive = plugin_conf.always_set_cka_sensitive
|
||||
self.mkek_key_type = 'CKK_AES'
|
||||
self.mkek_length = plugin_conf.mkek_length
|
||||
self.mkek_label = plugin_conf.mkek_label
|
||||
self.hmac_label = plugin_conf.hmac_label
|
||||
self.hmac_key_type = plugin_conf.hmac_key_type
|
||||
self.hmac_keygen_mechanism = plugin_conf.hmac_keygen_mechanism
|
||||
self.hmac_keywrap_mechanism = plugin_conf.hmac_keywrap_mechanism
|
||||
self.os_locking_ok = plugin_conf.os_locking_ok
|
||||
self.pkek_length = plugin_conf.pkek_length
|
||||
self.pkek_cache_ttl = plugin_conf.pkek_cache_ttl
|
||||
self.pkek_cache_limit = plugin_conf.pkek_cache_limit
|
||||
self.rw_session = plugin_conf.rw_session
|
||||
self.seed_file = plugin_conf.seed_file
|
||||
self.seed_length = plugin_conf.seed_length
|
||||
self.slot_id = plugin_conf.slot_id
|
||||
self.login = plugin_conf.login
|
||||
self.token_serial_number = plugin_conf.token_serial_number
|
||||
self.token_labels = plugin_conf.token_labels or list()
|
||||
if plugin_conf.token_label:
|
||||
LOG.warning('Using deprecated option "token_label". Please update '
|
||||
'your configuration file.')
|
||||
if plugin_conf.token_label not in self.token_labels:
|
||||
self.token_labels.append(plugin_conf.token_label)
|
||||
|
||||
# Use specified or create new pkcs11 object
|
||||
self.pkcs11 = pkcs11 or self._create_pkcs11(ffi)
|
||||
|
||||
self._configure_object_cache()
|
||||
|
||||
@ -315,25 +338,25 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
else:
|
||||
break
|
||||
|
||||
def _create_pkcs11(self, plugin_conf, ffi=None):
|
||||
def _create_pkcs11(self, ffi=None):
|
||||
seed_random_buffer = None
|
||||
if plugin_conf.seed_file:
|
||||
with open(plugin_conf.seed_file, 'rb') as f:
|
||||
seed_random_buffer = f.read(plugin_conf.seed_length)
|
||||
if self.seed_file:
|
||||
with open(self.seed_file, 'rb') as f:
|
||||
seed_random_buffer = f.read(self.seed_length)
|
||||
return pkcs11.PKCS11(
|
||||
library_path=plugin_conf.library_path,
|
||||
login_passphrase=plugin_conf.login,
|
||||
rw_session=plugin_conf.rw_session,
|
||||
slot_id=plugin_conf.slot_id,
|
||||
encryption_mechanism=plugin_conf.encryption_mechanism,
|
||||
library_path=self.library_path,
|
||||
login_passphrase=self.login,
|
||||
rw_session=self.rw_session,
|
||||
slot_id=self.slot_id,
|
||||
encryption_mechanism=self.encryption_mechanism,
|
||||
ffi=ffi,
|
||||
seed_random_buffer=seed_random_buffer,
|
||||
generate_iv=plugin_conf.aes_gcm_generate_iv,
|
||||
always_set_cka_sensitive=plugin_conf.always_set_cka_sensitive,
|
||||
hmac_keywrap_mechanism=plugin_conf.hmac_keywrap_mechanism,
|
||||
token_serial_number=plugin_conf.token_serial_number,
|
||||
token_label=plugin_conf.token_label,
|
||||
os_locking_ok=plugin_conf.os_locking_ok
|
||||
generate_iv=self.generate_iv,
|
||||
always_set_cka_sensitive=self.cka_sensitive,
|
||||
hmac_keywrap_mechanism=self.hmac_keywrap_mechanism,
|
||||
token_serial_number=self.token_serial_number,
|
||||
token_labels=self.token_labels,
|
||||
os_locking_ok=self.os_locking_ok
|
||||
)
|
||||
|
||||
def _reinitialize_pkcs11(self):
|
||||
@ -350,7 +373,7 @@ class P11CryptoPlugin(plugin.CryptoPluginBase):
|
||||
with self.mk_cache_lock:
|
||||
self.mk_cache.clear()
|
||||
|
||||
self.pkcs11 = self._create_pkcs11(self.conf.p11_crypto_plugin)
|
||||
self.pkcs11 = self._create_pkcs11()
|
||||
self._configure_object_cache()
|
||||
|
||||
def _get_session(self):
|
||||
|
@ -12,7 +12,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
import itertools
|
||||
import textwrap
|
||||
|
||||
import cffi
|
||||
@ -433,7 +432,7 @@ class PKCS11(object):
|
||||
generate_iv=None, always_set_cka_sensitive=None,
|
||||
hmac_keywrap_mechanism='CKM_SHA256_HMAC',
|
||||
token_serial_number=None,
|
||||
token_label=None,
|
||||
token_labels=None,
|
||||
os_locking_ok=False):
|
||||
if algorithm:
|
||||
LOG.warning("WARNING: Using deprecated 'algorithm' argument.")
|
||||
@ -467,7 +466,7 @@ class PKCS11(object):
|
||||
self.rw_session = rw_session
|
||||
self.slot_id = self._get_slot_id(
|
||||
token_serial_number,
|
||||
token_label,
|
||||
token_labels,
|
||||
slot_id)
|
||||
|
||||
# Algorithm options
|
||||
@ -485,10 +484,9 @@ class PKCS11(object):
|
||||
self._seed_random(session, seed_random_buffer)
|
||||
self._rng_self_test(session)
|
||||
self.return_session(session)
|
||||
LOG.debug("Connected to PCKS11 sn: %s label: %s slot: %s",
|
||||
token_serial_number, token_label, self.slot_id)
|
||||
LOG.debug("Connected to PCKS#11 Token in Slot %s", self.slot_id)
|
||||
|
||||
def _get_slot_id(self, token_serial_number, token_label, slot_id):
|
||||
def _get_slot_id(self, token_serial_number, token_labels, slot_id):
|
||||
# First find out how many slots with tokens are available
|
||||
slots_ptr = self.ffi.new("CK_ULONG_PTR")
|
||||
rv = self.lib.C_GetSlotList(CK_TRUE, self.ffi.NULL, slots_ptr)
|
||||
@ -525,10 +523,10 @@ class PKCS11(object):
|
||||
LOG.debug("Found token sn: %s in slot %s",
|
||||
token.serial_number,
|
||||
token.slot_id)
|
||||
if token_label:
|
||||
if token_labels:
|
||||
LOG.warning(
|
||||
"Ignoring token_label: %s from barbican.conf",
|
||||
token_label
|
||||
"Ignoring token_labels: %s from barbican.conf",
|
||||
token_labels
|
||||
)
|
||||
if slot_id:
|
||||
LOG.warning("Ignoring slot_id: %s from barbican.conf",
|
||||
@ -537,22 +535,16 @@ class PKCS11(object):
|
||||
raise ValueError("Token Serial Number not found.")
|
||||
|
||||
# Label match is next, raises an error if there's not exactly one match
|
||||
if token_label:
|
||||
matched = list(itertools.dropwhile(
|
||||
lambda x: x.label != token_label,
|
||||
tokens
|
||||
))
|
||||
if len(matched) > 1:
|
||||
raise ValueError("More than one matching token label found")
|
||||
if len(matched) < 1:
|
||||
raise ValueError("Token Label not found.")
|
||||
|
||||
token = matched.pop()
|
||||
LOG.debug("Found token label: %s in slot %s", token.label,
|
||||
token.slot_id)
|
||||
if slot_id:
|
||||
LOG.warning("Ignoring slot_id: %s from barbican.conf", slot_id)
|
||||
return token.slot_id
|
||||
if token_labels:
|
||||
for token in tokens:
|
||||
if token.label in token_labels:
|
||||
LOG.debug("Found token label: %s in slot %s", token.label,
|
||||
token.slot_id)
|
||||
if slot_id:
|
||||
LOG.warning("Ignoring slot_id: %s from barbican.conf",
|
||||
slot_id)
|
||||
return token.slot_id
|
||||
raise ValueError("Token Labels not found.")
|
||||
|
||||
# If we got this far, slot_id was the only param given, so we return it
|
||||
return slot_id
|
||||
|
@ -56,6 +56,7 @@ class WhenTestingP11CryptoPlugin(utils.BaseTestCase):
|
||||
self.cfg_mock.p11_crypto_plugin.slot_id = 1
|
||||
self.cfg_mock.p11_crypto_plugin.token_serial_number = None
|
||||
self.cfg_mock.p11_crypto_plugin.token_label = None
|
||||
self.cfg_mock.p11_crypto_plugin.token_labels = None
|
||||
self.cfg_mock.p11_crypto_plugin.rw_session = True
|
||||
self.cfg_mock.p11_crypto_plugin.pkek_length = 32
|
||||
self.cfg_mock.p11_crypto_plugin.pkek_cache_ttl = 900
|
||||
@ -70,7 +71,8 @@ class WhenTestingP11CryptoPlugin(utils.BaseTestCase):
|
||||
self.cfg_mock.p11_crypto_plugin.plugin_name = self.plugin_name
|
||||
|
||||
self.plugin = p11_crypto.P11CryptoPlugin(
|
||||
conf=self.cfg_mock, pkcs11=self.pkcs11
|
||||
conf=self.cfg_mock,
|
||||
pkcs11=self.pkcs11
|
||||
)
|
||||
|
||||
def test_invalid_library_path(self):
|
||||
@ -299,19 +301,18 @@ class WhenTestingP11CryptoPlugin(utils.BaseTestCase):
|
||||
ffi = pkcs11.build_ffi()
|
||||
setattr(ffi, 'dlopen', lambda x: lib)
|
||||
|
||||
p11 = self.plugin._create_pkcs11(self.cfg_mock.p11_crypto_plugin, ffi)
|
||||
p11 = self.plugin._create_pkcs11(ffi)
|
||||
self.assertIsInstance(p11, pkcs11.PKCS11)
|
||||
|
||||
# test for when plugin_conf.seed_file is not None
|
||||
self.cfg_mock.p11_crypto_plugin.seed_file = 'seed_file'
|
||||
self.plugin.seed_file = 'seed_file'
|
||||
d = '01234567' * 4
|
||||
mo = mock.mock_open(read_data=d)
|
||||
|
||||
with mock.patch(six.moves.builtins.__name__ + '.open',
|
||||
mo,
|
||||
create=True):
|
||||
p11 = self.plugin._create_pkcs11(
|
||||
self.cfg_mock.p11_crypto_plugin, ffi)
|
||||
p11 = self.plugin._create_pkcs11(ffi)
|
||||
|
||||
self.assertIsInstance(p11, pkcs11.PKCS11)
|
||||
mo.assert_called_once_with('seed_file', 'rb')
|
||||
|
@ -175,11 +175,11 @@ class WhenTestingPKCS11(utils.BaseTestCase):
|
||||
return pkcs11.CKR_OK
|
||||
|
||||
def test_get_slot_id_from_serial_number(self):
|
||||
slot_id = self.pkcs11._get_slot_id('111111', None, 1)
|
||||
slot_id = self.pkcs11._get_slot_id('111111', None, 2)
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_get_slot_id_from_label(self):
|
||||
slot_id = self.pkcs11._get_slot_id(None, 'myLabel', 1)
|
||||
slot_id = self.pkcs11._get_slot_id(None, ['myLabel'], 2)
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_get_slot_id_backwards_compatibility(self):
|
||||
@ -187,7 +187,7 @@ class WhenTestingPKCS11(utils.BaseTestCase):
|
||||
self.assertEqual(5, slot_id)
|
||||
|
||||
def test_get_slot_id_from_serial_ignores_label(self):
|
||||
slot_id = self.pkcs11._get_slot_id('111111', 'badLabel', 1)
|
||||
slot_id = self.pkcs11._get_slot_id('111111', ['badLabel'], 2)
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_get_slot_id_from_serial_ignores_given_slot(self):
|
||||
@ -195,7 +195,7 @@ class WhenTestingPKCS11(utils.BaseTestCase):
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_get_slot_id_from_label_ignores_given_slot(self):
|
||||
slot_id = self.pkcs11._get_slot_id(None, 'myLabel', 3)
|
||||
slot_id = self.pkcs11._get_slot_id(None, ['myLabel'], 3)
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_get_slot_id_serial_not_found(self):
|
||||
@ -204,14 +204,14 @@ class WhenTestingPKCS11(utils.BaseTestCase):
|
||||
|
||||
def test_get_slot_id_label_not_found(self):
|
||||
self.assertRaises(ValueError,
|
||||
self.pkcs11._get_slot_id, None, 'badLabel', 1)
|
||||
self.pkcs11._get_slot_id, None, ['myLabelbad'], 1)
|
||||
|
||||
def test_get_slot_id_two_tokens_same_label(self):
|
||||
self.lib.C_GetSlotList.side_effect = self._get_two_slot_list
|
||||
self.lib.C_GetTokenInfo.side_effect = \
|
||||
self._get_two_token_info_same_label
|
||||
self.assertRaises(ValueError,
|
||||
self.pkcs11._get_slot_id, None, 'myLabel', 1)
|
||||
slot_id = self.pkcs11._get_slot_id(None, ['myLabel'], 3)
|
||||
self.assertEqual(1, slot_id)
|
||||
|
||||
def test_public_get_session(self):
|
||||
self.lib.C_GetSessionInfo.side_effect = self._get_session_public
|
||||
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
A new "token_labels" option has been added to the PKCS#11 driver which
|
||||
supersedes the previous "token_label" option. The new option is used to
|
||||
specify a list of tokens that can be used by Barbican. This is required
|
||||
for some HSM devices that use separate tokens for load balancing. For most
|
||||
use cases the new option will just have a single token. The old option
|
||||
is deprecated, but will still be used if present.
|
||||
deprecations:
|
||||
- |
|
||||
The "token_label" option in the PKCS#11 driver is deprecated. Th new
|
||||
"token_labels" option should be used instead. If present, "token_label"
|
||||
will still be used by appending it to "token_labels".
|
Loading…
x
Reference in New Issue
Block a user