Use MultiFernet to enable more than one KEK

This patch changes the `kek` option for the Simple Crypto Plugin to
allow more than one KEK to be specified.

When more than one KEK is configured, the first KEK is used to encrypt
new data and any additiona KEKs are only used to decrypt existing data.

This change allows for rotating in new KEKs on demand.

Change-Id: I0c3683e316e78478461f5f30f4f353ff43a3bb09
This commit is contained in:
Douglas Mendizábal 2025-02-13 16:42:26 -05:00 committed by Douglas Mendizabal
parent bf78873180
commit cfba1c1ba8
3 changed files with 33 additions and 8 deletions

View File

@ -32,10 +32,13 @@ LOG = utils.getLogger(__name__)
simple_crypto_plugin_group = cfg.OptGroup(name='simple_crypto_plugin',
title="Simple Crypto Plugin Options")
simple_crypto_plugin_opts = [
cfg.StrOpt('kek',
default='dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg=',
help=u._('Key encryption key to be used by Simple Crypto '
'Plugin'), secret=True),
cfg.MultiStrOpt(
'kek',
default=['dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg='],
secret=True,
help=u._('Fernet Key-Encryption Key (KEK) to be used by SimpleCrypto '
'Plugin to encrypt Project-specific KEKs.'),
),
cfg.StrOpt('plugin_name',
help=u._('User friendly plugin name'),
default='Software Only Crypto'),
@ -53,7 +56,9 @@ class SimpleCryptoPlugin(c.CryptoPluginBase):
"""Insecure implementation of the crypto plugin."""
def __init__(self, conf=CONF):
self.master_kek = conf.simple_crypto_plugin.kek
if len(conf.simple_crypto_plugin.kek) < 1:
raise ValueError(u._("SimpleCrypto KEK is undefined"))
self.master_keys = conf.simple_crypto_plugin.kek
self.plugin_name = conf.simple_crypto_plugin.plugin_name
LOG.info("{} initialized".format(self.plugin_name))
@ -64,7 +69,9 @@ class SimpleCryptoPlugin(c.CryptoPluginBase):
if not kek_meta_dto.plugin_meta:
raise ValueError(u._('KEK not yet created.'))
# the kek is stored encrypted. Need to decrypt.
encryptor = fernet.Fernet(self.master_kek)
encryptor = fernet.MultiFernet(
[fernet.Fernet(x) for x in self.master_keys]
)
# Note : If plugin_meta type is unicode, encode to byte.
if isinstance(kek_meta_dto.plugin_meta, str):
kek_meta_dto.plugin_meta = kek_meta_dto.plugin_meta.encode('utf-8')
@ -100,7 +107,7 @@ class SimpleCryptoPlugin(c.CryptoPluginBase):
kek_meta_dto.mode = 'cbc'
if not kek_meta_dto.plugin_meta:
# the kek is stored encrypted in the plugin_meta field
encryptor = fernet.Fernet(self.master_kek)
encryptor = fernet.Fernet(self.master_keys[0])
key = fernet.Fernet.generate_key()
kek_meta_dto.plugin_meta = encryptor.encrypt(key)
return kek_meta_dto

View File

@ -65,7 +65,7 @@ class WhenTestingSimpleCryptoPlugin(utils.BaseTestCase):
Compare with unencrypted
"""
project_kek = fernet.Fernet.generate_key()
encryptor = fernet.Fernet(self.plugin.master_kek)
encryptor = fernet.Fernet(self.plugin.master_keys[0])
ENC_project_kek = encryptor.encrypt(project_kek)
UENC_project_kek = ENC_project_kek
kek_meta_dto = self._get_mocked_kek_meta_dto()

View File

@ -0,0 +1,18 @@
---
security:
- |
The configuration for Simple Crypto Plugin has been updated to allow more
than one Key-Encryption-Key (KEK) to be defined. This enables the ability
to rotate in new KEKs on demand. If there is more than one KEK specified
in the config file, then the first KEK is considered "active", which means
it will be used to encrypt any new Project-specific KEKs. Any additional
KEKs will only be used to decrypt existing pKEKs when necessary. .e.g.
.. code-block::
[simple_crypto_plugin]
# First key is used for ecnrypting new data
kek = Yl1EKQ5e4VpK3X7lbWF249GDsk0mrL929P-Mnnz-bdc=
# Additionak keys used for decrypting existing data
kek = AfXmy1NEfzmtJEYVGrQJ0C2-dr8S0lFoNBX5Vb7MC44=
kek = Ua4Y8ryfamShYT_TzxSjok9Tl11OWFSk3whOSY-TIaw=