ANDROID: block: Introduce passthrough keyslot manager

The regular keyslot manager is designed for devices that have a small
number of keyslots that need to be programmed with keys ahead of time,
and bios that are sent to the device need to be tagged with a keyslot
index.

Some inline encryption hardware may not have any limitations on the
number of keyslot, and may instead allow each bio to be tagged with
a raw key, data unit number, etc. rather than a pre-programmed keyslot's
index. These devices don't need any sort of keyslot management, and it's
better for these devices not to have to allocate a regular keyslot
manager with some fixed number of keyslots. These devices can instead
set up a passthrough keyslot manager in their request queue, which
require less resources than regular keyslot managers, as they simply
do no-ops when trying to program keys into slots.

Separately, the device mapper may map over devices that have inline
encryption hardware, and it wants to pass the key along to the
underlying hardware. While the DM layer can expose inline encryption
capabilities by setting up a regular keyslot manager with some fixed
number of keyslots in the dm device's request queue, this only wastes
memory since the keys programmed into the dm device's request queue
will never be used. Instead, it's better to set up a passthrough
keyslot manager for dm devices.

Bug: 137270441
Bug: 147814592
Change-Id: I6d91e83e86a73b0d6066873c8a9117cf2c089234
Signed-off-by: Satya Tangirala <satyat@google.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Satya Tangirala 2020-01-21 09:27:43 -08:00 committed by Eric Biggers
parent 8f48f6657d
commit e65d08ae68
2 changed files with 71 additions and 0 deletions

View file

@ -66,6 +66,11 @@ struct keyslot_manager {
struct keyslot slots[];
};
static inline bool keyslot_manager_is_passthrough(struct keyslot_manager *ksm)
{
return ksm->num_slots == 0;
}
/**
* keyslot_manager_create() - Create a keyslot manager
* @num_slots: The number of key slots to manage.
@ -211,6 +216,9 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm,
int err;
struct keyslot *idle_slot;
if (keyslot_manager_is_passthrough(ksm))
return 0;
down_read(&ksm->lock);
slot = find_and_grab_keyslot(ksm, key);
up_read(&ksm->lock);
@ -276,6 +284,9 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm,
*/
void keyslot_manager_get_slot(struct keyslot_manager *ksm, unsigned int slot)
{
if (keyslot_manager_is_passthrough(ksm))
return;
if (WARN_ON(slot >= ksm->num_slots))
return;
@ -293,6 +304,9 @@ void keyslot_manager_put_slot(struct keyslot_manager *ksm, unsigned int slot)
{
unsigned long flags;
if (keyslot_manager_is_passthrough(ksm))
return;
if (WARN_ON(slot >= ksm->num_slots))
return;
@ -352,6 +366,16 @@ int keyslot_manager_evict_key(struct keyslot_manager *ksm,
int err;
struct keyslot *slotp;
if (keyslot_manager_is_passthrough(ksm)) {
if (ksm->ksm_ll_ops.keyslot_evict) {
down_write(&ksm->lock);
err = ksm->ksm_ll_ops.keyslot_evict(ksm, key, -1);
up_write(&ksm->lock);
return err;
}
return 0;
}
down_write(&ksm->lock);
slot = find_keyslot(ksm, key);
if (slot < 0) {
@ -389,6 +413,9 @@ void keyslot_manager_reprogram_all_keys(struct keyslot_manager *ksm)
{
unsigned int slot;
if (WARN_ON(keyslot_manager_is_passthrough(ksm)))
return;
down_write(&ksm->lock);
for (slot = 0; slot < ksm->num_slots; slot++) {
const struct keyslot *slotp = &ksm->slots[slot];
@ -426,6 +453,45 @@ void keyslot_manager_destroy(struct keyslot_manager *ksm)
}
EXPORT_SYMBOL_GPL(keyslot_manager_destroy);
/**
* keyslot_manager_create_passthrough() - Create a passthrough keyslot manager
* @ksm_ll_ops: The struct keyslot_mgmt_ll_ops
* @crypto_mode_supported: Bitmasks for supported encryption modes
* @ll_priv_data: Private data passed as is to the functions in ksm_ll_ops.
*
* Allocate memory for and initialize a passthrough keyslot manager.
* Called by e.g. storage drivers to set up a keyslot manager in their
* request_queue, when the storage driver wants to manage its keys by itself.
* This is useful for inline encryption hardware that don't have a small fixed
* number of keyslots, and for layered devices.
*
* See keyslot_manager_create() for more details about the parameters.
*
* Context: This function may sleep
* Return: Pointer to constructed keyslot manager or NULL on error.
*/
struct keyslot_manager *keyslot_manager_create_passthrough(
const struct keyslot_mgmt_ll_ops *ksm_ll_ops,
const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX],
void *ll_priv_data)
{
struct keyslot_manager *ksm;
ksm = kzalloc(sizeof(*ksm), GFP_KERNEL);
if (!ksm)
return NULL;
ksm->ksm_ll_ops = *ksm_ll_ops;
memcpy(ksm->crypto_mode_supported, crypto_mode_supported,
sizeof(ksm->crypto_mode_supported));
ksm->ll_priv_data = ll_priv_data;
init_rwsem(&ksm->lock);
return ksm;
}
EXPORT_SYMBOL_GPL(keyslot_manager_create_passthrough);
/**
* keyslot_manager_derive_raw_secret() - Derive software secret from wrapped key
* @ksm: The keyslot manager

View file

@ -64,6 +64,11 @@ void *keyslot_manager_private(struct keyslot_manager *ksm);
void keyslot_manager_destroy(struct keyslot_manager *ksm);
struct keyslot_manager *keyslot_manager_create_passthrough(
const struct keyslot_mgmt_ll_ops *ksm_ops,
const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX],
void *ll_priv_data);
int keyslot_manager_derive_raw_secret(struct keyslot_manager *ksm,
const u8 *wrapped_key,
unsigned int wrapped_key_size,