This RFC aims to provide better security of users using CAAM block allowing
them to encrypt their disks with a hardware key. A hardware key is a
key only know by the hardware hence not known neither by the userspace nor
the kernelspace.
The use of hardware keys allows to mitigate attacks against systems using
such type of keys as the key is more complicated to obtain. In effect, even
a compromised kernel could not divulge the key used to encrypt the data.
The user or the kernel can only configure a device mapper using the target
crypt and provide the HW key to encrypt a disk.
The CAAM possesses a mechanism to generate HW keys called "black key".
The CAAM has the capacity to encrypt a plain key ("red key") with a key
only accessible by the hardware hence creating a "black key". The
encryption key used change at each reset of the CAAM: shutdown, reboot.
generate a black key: <plain key> -> CAAM -> <black key>
To keep a black key between reset, it must be stored in filesystem in a
structure called "black blob". This structure contains the "black key" but
also encryption information. It is also generated by the CAAM using a key
only accessible by the hardware but which doesn't change between reset of
the CAAM.
generate a black blob: <black key> -> CAAM -> <black blob> -> FS
obtain the black key: FS -> <black blob> -> CAAM -> <black key>
The creation of such structures and its use was not exposed to userspace so
it was complicated to use and required custom development. We would like to
ease this using interface which are known and used:
- Linux key retention service : Allow to generate or load keys in a
keyring which can be used by applications.
- dm-crypt : device mapper allowing to encrypt data.
The capacity to generate or load keys already available in the Linux key
retention service does not allows to exploit CAAM capabilities hence we
need to create a new key_type. The new key type "caam_tk" allows to:
- Create a black key from random
- Create a black key from a red key
- Load a black blob to retrieve the black key
The patch 01 presents the implementation of the key type is available in
the RFC. This implementation is presented to give context but cannot be
compiled as it requires other components not presented in this RFC.
We target dm-crypt to perform the disk encryption. It can retrieve keys
from the Linux key retention service however it only supports the key
type "user" and "logon". In order to use keys from our new key type, we
need to either:
- add support for our new key type : dm-crypt need a reference on the key
type structure.
- support any key type : the reference on the key type is retrieved from
the key subsystem based on the name of the key type
The patch 02 present the implementation of the second solution. The main
advantage of this implementation are:
- The reference on the key type is no more needed at compile time
- All future key type will be supported
- Only the key type registered can be obtained (key type as module)
We think that this modification of dm-crypt and the key subsystem will ease
the use of dm-crypt and the development of key type.
Franck LENORMAND (2):
drivers: crypto: caam: key: Add caam_tk key type
dm-crypt: Use any key type which is registered
drivers/crypto/caam/caam_key.c | 623 +++++++++++++++++++++++++++++++++++++++++
drivers/crypto/caam/caam_key.h | 58 ++++
drivers/md/dm-crypt.c | 11 +-
include/linux/key-type.h | 2 +
security/keys/key.c | 42 +++
5 files changed, 731 insertions(+), 5 deletions(-)
create mode 100644 drivers/crypto/caam/caam_key.c
create mode 100644 drivers/crypto/caam/caam_key.h
--
2.7.4
This patch adds a module which creates a new key type which
can be used by the user with the linux key retention service.
The key created by this module are black keys appended with
a tag to create a tag key.
Such a key can be passed to the linux crypto API for the
transforms:
- tk(cbc(aes))
The configuration string passed to the key service has 3
forms:
- new <black key encryption> <size in bytes>
- set <black key encryption> <hex of a key>
- load <black key encryption> <hex of a blob>
with <black key encryption> = ecb | ccm
When reading or printing a key, it will return a binary blob
which can be saved to a file through powercycle. The blob
can then be loaded.
V2: Expect the data to be loaded to be prepended by ':hex:'
Signed-off-by: Franck LENORMAND <[email protected]>
---
drivers/crypto/caam/caam_key.c | 623 +++++++++++++++++++++++++++++++++++++++++
drivers/crypto/caam/caam_key.h | 58 ++++
2 files changed, 681 insertions(+)
create mode 100644 drivers/crypto/caam/caam_key.c
create mode 100644 drivers/crypto/caam/caam_key.h
diff --git a/drivers/crypto/caam/caam_key.c b/drivers/crypto/caam/caam_key.c
new file mode 100644
index 0000000..5d89c9d
--- /dev/null
+++ b/drivers/crypto/caam/caam_key.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018 NXP
+ * caam key is generated using NXP CAAM hardware block. CAAM generates the
+ * random number (used as a key) and creates its blob for the user.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/string.h>
+#include <linux/key-type.h>
+#include <linux/rcupdate.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+
+#include "desc.h"
+#include "desc_constr.h"
+#include "caam_desc.h"
+#include "caam_key.h"
+#include "caam_util.h"
+
+/* Key modifier for CAAM key blobbing */
+static const char caam_key_modifier[KEYMOD_SIZE_GM] = {
+ 'C', 'A', 'A', 'M', '_', 'K', 'E', 'Y',
+ '_', 'T', 'Y', 'P', 'E', '_', 'V', '1',
+};
+
+/* Operation supported */
+enum caam_key_op {
+ OP_ERROR = -1,
+ OP_NEW_KEY,
+ OP_SET_KEY,
+ OP_LOAD_BLOB,
+};
+
+/* Tokens for the operation to do */
+static const match_table_t key_cmd_tokens = {
+ {OP_NEW_KEY, "new"},
+ {OP_SET_KEY, "set"},
+ {OP_LOAD_BLOB, "load"},
+ {OP_ERROR, NULL}
+};
+
+enum caam_key_fmt {
+ FMT_ERROR = -1,
+ FMT_ECB,
+ FMT_CCM,
+};
+
+/* Tokens for the type of encryption of the black key */
+static const char FMT_ECB_txt[] = "ecb";
+static const char FMT_CCM_txt[] = "ccm";
+
+static const match_table_t key_fmt_tokens = {
+ {FMT_ECB, FMT_ECB_txt},
+ {FMT_CCM, FMT_CCM_txt},
+ {FMT_ERROR, NULL}
+};
+
+int caam_key_tag_black_key(struct caam_key_payload *ckpayload,
+ size_t black_key_max_len, u8 auth, u8 trusted)
+{
+ struct tag_object_conf tag;
+ enum tag_type type;
+ int ret;
+ u32 size_tagged = black_key_max_len;
+
+ if (!ckpayload)
+ return -EINVAL;
+
+ if (!is_auth(auth) || !is_trusted_key(trusted))
+ return -EINVAL;
+
+ if (auth == KEY_COVER_ECB) {
+ if (trusted == UNTRUSTED_KEY)
+ type = TAG_TYPE_BLACK_KEY_ECB;
+ else
+ type = TAG_TYPE_BLACK_KEY_ECB_TRUSTED;
+ } else {
+ if (trusted == UNTRUSTED_KEY)
+ type = TAG_TYPE_BLACK_KEY_CCM;
+ else
+ type = TAG_TYPE_BLACK_KEY_CCM_TRUSTED;
+ }
+
+ /* Prepare the tag */
+ init_tag_object_header(&tag.header, type);
+ init_blackey_conf(&tag.conf.bk_conf, ckpayload->key_len,
+ auth == KEY_COVER_CCM,
+ trusted == TRUSTED_KEY);
+
+ ret = set_tag_object_conf(&tag, ckpayload->black_key,
+ ckpayload->black_key_len, &size_tagged);
+ if (ret) {
+ pr_err("Tagging fail: %d\n", ret);
+ goto exit;
+ }
+
+ /* Update the size of the black key tagged */
+ ckpayload->black_key_len = size_tagged;
+
+exit:
+ return ret;
+}
+
+static int caam_transform(enum caam_key_op key_cmd,
+ struct caam_key_payload *ckpayload)
+{
+ int ret;
+ struct device *jrdev;
+ u8 key_cover;
+
+ /* Allocate caam job ring for operation to be performed from CAAM */
+ jrdev = caam_jr_alloc();
+ if (!jrdev) {
+ pr_info("caam_jr_alloc failed\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (ckpayload->key_fmt_val == FMT_ECB)
+ key_cover = KEY_COVER_ECB;
+ else
+ key_cover = KEY_COVER_CCM;
+
+ switch (key_cmd) {
+ case OP_LOAD_BLOB:
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "input blob: ",
+ DUMP_PREFIX_OFFSET, 16, 4, ckpayload->blob,
+ ckpayload->blob_len, 0);
+#endif
+ /* Decapsulate the black blob into a black key */
+ ret = caam_blob_decap(jrdev,
+ ckpayload->blob, ckpayload->blob_len,
+ DATA_GENMEM, BLACK_BLOB,
+ ckpayload->key_mod,
+ &ckpayload->key_mod_len, DATA_GENMEM,
+ ckpayload->black_key,
+ &ckpayload->black_key_len, DATA_GENMEM,
+ BLACK_KEY, &ckpayload->key_len,
+ key_cover, UNTRUSTED_KEY);
+ if (ret) {
+ pr_info("key_blob decap fail: %d\n", ret);
+ goto free_jr;
+ }
+
+ break;
+ case OP_SET_KEY:
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "input key: ",
+ DUMP_PREFIX_OFFSET, 16, 4, ckpayload->key,
+ ckpayload->key_len, 0);
+#endif
+
+ /* Cover the input key */
+ ret = caam_black_key(jrdev,
+ ckpayload->key, ckpayload->key_len,
+ DATA_GENMEM,
+ ckpayload->black_key,
+ &ckpayload->black_key_len, DATA_GENMEM,
+ key_cover, UNTRUSTED_KEY);
+ /*
+ * Clear the input key
+ * TODO: Make it secure to not be removed by compiler
+ */
+ memset(ckpayload->key, 0, ckpayload->key_len);
+
+ if (ret) {
+ pr_info("key covering fail: (%d)\n", ret);
+ goto free_jr;
+ }
+
+ /* Encapsulate the key */
+ ret = caam_blob_encap(jrdev,
+ ckpayload->black_key,
+ ckpayload->black_key_len, DATA_GENMEM,
+ BLACK_KEY, ckpayload->key_len, key_cover,
+ UNTRUSTED_KEY,
+ ckpayload->key_mod,
+ &ckpayload->key_mod_len, DATA_GENMEM,
+ ckpayload->blob, &ckpayload->blob_len,
+ DATA_GENMEM, BLACK_BLOB);
+ if (ret) {
+ pr_info("Blob encapsulation of key fail: %d\n", ret);
+ goto free_jr;
+ }
+
+ break;
+ case OP_NEW_KEY:
+ /*
+ * We need random data to create a key however we do not
+ * want
+ */
+ ret = caam_random_black_key(jrdev,
+ ckpayload->key_len,
+ ckpayload->black_key,
+ &ckpayload->black_key_len,
+ DATA_GENMEM, key_cover,
+ UNTRUSTED_KEY);
+
+ if (ret) {
+ pr_info("Random key covering fail: %d\n", ret);
+ goto free_jr;
+ }
+
+ /* Encapsulate the key */
+ ret = caam_blob_encap(jrdev,
+ ckpayload->black_key,
+ ckpayload->black_key_len, DATA_GENMEM,
+ BLACK_KEY, ckpayload->key_len, key_cover,
+ UNTRUSTED_KEY,
+ ckpayload->key_mod,
+ &ckpayload->key_mod_len, DATA_GENMEM,
+ ckpayload->blob, &ckpayload->blob_len,
+ DATA_GENMEM, BLACK_BLOB);
+ if (ret) {
+ pr_info("Blob encapsulation of random fail: %d\n", ret);
+ goto free_jr;
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "black key: ",
+ DUMP_PREFIX_OFFSET, 16, 4, ckpayload->black_key,
+ ckpayload->black_key_len, 0);
+ print_hex_dump(KERN_ERR, "blob: ",
+ DUMP_PREFIX_OFFSET, 16, 4, ckpayload->blob,
+ ckpayload->blob_len, 0);
+#endif
+
+ /* Tag the black key so it can be passed to CAAM crypto API */
+ ret = caam_key_tag_black_key(ckpayload,
+ ARRAY_SIZE(ckpayload->black_key),
+ key_cover, UNTRUSTED_KEY);
+ if (ret) {
+ pr_info("Black key tagging fail: %d\n", ret);
+ goto free_jr;
+ }
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "tagged black key: ",
+ DUMP_PREFIX_OFFSET, 16, 4, ckpayload->black_key,
+ ckpayload->black_key_len, 0);
+#endif
+
+ /* Update the aliased user_key_payload */
+ ckpayload->upayload.datalen = ckpayload->black_key_len;
+ memcpy(ckpayload->upayload.data, ckpayload->black_key,
+ ckpayload->upayload.datalen);
+
+free_jr:
+ caam_jr_free(jrdev);
+
+out:
+ if (ret)
+ pr_err("Operation %s(%d) failed\n",
+ key_cmd_tokens[key_cmd].pattern, key_cmd);
+
+ return ret;
+}
+
+/*
+ * parse_inputdata - parse the keyctl input data and fill in the
+ * payload structure for key or its blob.
+ * param[in]: data pointer to the data to be parsed for creating key.
+ * param[in]: p pointer to caam key payload structure to fill parsed data
+ * On success returns 0, otherwise -EINVAL.
+ */
+static enum caam_key_op parse_inputdata(char *data,
+ struct caam_key_payload *ckpayload)
+{
+ substring_t args[MAX_OPT_ARGS];
+ long keylen = 0;
+ int ret = 0;
+ enum caam_key_op op_to_do = OP_ERROR;
+ int key_cmd = -EINVAL;
+ int key_fmt = -EINVAL;
+ char *c = NULL;
+ const char *hex_format = ":hex:";
+ u32 hex_format_size;
+
+ c = strsep(&data, " \t");
+ if (!c) {
+ ret = -EINVAL;
+ pr_err("Failed to find 1st arg\n");
+ goto out;
+ }
+
+ /* Get the keyctl command i.e. new_key or load_blob etc */
+ key_cmd = match_token(c, key_cmd_tokens, args);
+
+ /* Skip spaces to get the 1st argument */
+ c = strsep(&data, " \t");
+ if (!c) {
+ ret = -EINVAL;
+ pr_err("Failed to find 2nd arg\n");
+ goto out;
+ }
+
+ /* Get the keyctl format i.e. ecb or ccm etc */
+ key_fmt = match_token(c, key_fmt_tokens, args);
+
+ /* Skip spaces to get second argument */
+ c = strsep(&data, " \t");
+ if (!c) {
+ ret = -EINVAL;
+ pr_err("Failed to find 3rd arg\n");
+ goto out;
+ }
+
+ switch (key_fmt) {
+ case FMT_ECB:
+ ckpayload->key_fmt_val = KEY_COVER_ECB;
+ break;
+ case FMT_CCM:
+ ckpayload->key_fmt_val = KEY_COVER_CCM;
+ break;
+ case FMT_ERROR:
+ ret = -EINVAL;
+ pr_err("Format %d not supported\n", key_fmt);
+ goto out;
+ }
+
+ /* Prepare arguments */
+ switch (key_cmd) {
+ case OP_NEW_KEY:
+ /* Second argument is key size */
+ ret = kstrtol(c, 10, &keylen);
+ if (ret < 0 || keylen < MIN_KEY_SIZE ||
+ keylen > MAX_KEY_SIZE) {
+ ret = -EINVAL;
+ pr_err("Failed to retrieve key length\n");
+ goto out;
+ }
+
+ ckpayload->key_len = keylen;
+
+ ckpayload->black_key_len = ARRAY_SIZE(ckpayload->black_key);
+ ckpayload->blob_len = ARRAY_SIZE(ckpayload->blob);
+
+ op_to_do = OP_NEW_KEY;
+
+ break;
+ case OP_SET_KEY:
+ /* Second argument is key data for CAAM*/
+
+ /* key_len = No of characters in key/2 */
+ ckpayload->key_len = strlen(c) / 2;
+ if (ckpayload->blob_len > MAX_KEY_SIZE) {
+ ret = -EINVAL;
+ pr_err("Failed to compute key length\n");
+ goto out;
+ }
+
+ ret = hex2bin(ckpayload->key, c, ckpayload->key_len);
+ if (ret < 0) {
+ ret = -EINVAL;
+ pr_err("Failed to retrieve key data\n");
+ goto out;
+ }
+
+ ckpayload->black_key_len = ARRAY_SIZE(ckpayload->black_key);
+ ckpayload->blob_len = ARRAY_SIZE(ckpayload->blob);
+
+ op_to_do = OP_SET_KEY;
+
+ break;
+ case OP_LOAD_BLOB:
+ /* Second argument is blob data for CAAM */
+ hex_format_size = strlen(hex_format);
+
+ /* The blob is prepended by the format */
+ if (strncmp(c, hex_format, hex_format_size) != 0) {
+ ret = -EINVAL;
+ pr_err("Failed to match blob format\n");
+ goto out;
+ }
+
+ /* Advance the pointer */
+ c += hex_format_size;
+
+ /* Blob_len = No of characters in blob/2 */
+ ckpayload->blob_len = strlen(c) / 2;
+ if (ckpayload->blob_len > MAX_BLOB_SIZE) {
+ ret = -EINVAL;
+ pr_err("Failed to compute blob length\n");
+ goto out;
+ }
+
+ ret = hex2bin(ckpayload->blob, c, ckpayload->blob_len);
+ if (ret < 0) {
+ ret = -EINVAL;
+ pr_err("Failed to retrieve blob data\n");
+ goto out;
+ }
+
+ ckpayload->key_len = ARRAY_SIZE(ckpayload->key);
+ ckpayload->black_key_len = ARRAY_SIZE(ckpayload->black_key);
+
+ op_to_do = OP_LOAD_BLOB;
+
+ break;
+ case OP_ERROR:
+ ret = -EINVAL;
+ pr_err("Command %d not supported\n", key_cmd);
+ break;
+ }
+
+ ckpayload->key_mod = caam_key_modifier;
+ ckpayload->key_mod_len = ARRAY_SIZE(caam_key_modifier);
+
+out:
+ return (ret == 0) ? op_to_do : OP_ERROR;
+}
+
+static struct caam_key_payload *caam_payload_alloc(struct key *key)
+{
+ struct caam_key_payload *ckpayload = NULL;
+ int ret = 0;
+
+ ret = key_payload_reserve(key, sizeof(*ckpayload));
+ if (ret < 0) {
+ pr_err("Failed to reserve payload\n");
+ goto out;
+ }
+
+ ckpayload = kzalloc(sizeof(*ckpayload), GFP_KERNEL);
+ if (!ckpayload)
+ goto out;
+
+out:
+ return ckpayload;
+}
+
+/*
+ * caam_destroy - clear and free the key's payload
+ */
+static void caam_destroy(struct key *key)
+{
+ struct caam_key_payload *ckpayload = NULL;
+
+ /* Retrieve the payload */
+ ckpayload = dereference_key_locked(key);
+ if (!ckpayload)
+ pr_err("Fail to retrieve key payload\n");
+
+ kzfree(ckpayload);
+}
+
+/*
+ * caam_instantiate - create a new caam type key.
+ * Supports the operation to generate a new key. A random number
+ * is generated from CAAM as key data and the corresponding red blob
+ * is formed and stored as key_blob.
+ * Also supports the operation to load the blob and key is derived using
+ * that blob from CAAM.
+ * On success, return 0. Otherwise return errno.
+ */
+static int caam_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
+{
+ struct caam_key_payload *ckpayload;
+ size_t datalen;
+ char *data = NULL;
+ int key_cmd = 0;
+ int ret = 0;
+
+ if (!key || !prep) {
+ ret = -EINVAL;
+ pr_err("Input data incorrect\n");
+ goto out;
+ }
+
+ datalen = prep->datalen;
+
+ if (datalen <= 0 || datalen > 32767) {
+ ret = -EINVAL;
+ pr_err("Payload data size incorrect\n");
+ goto out;
+ }
+
+ /* Allocate memory to get a parsable string */
+ data = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(data, prep->data, datalen);
+ data[datalen] = '\0';
+
+ ckpayload = caam_payload_alloc(key);
+ if (!ckpayload) {
+ pr_err("Fail to allocate payload\n");
+ ret = -ENOMEM;
+ goto free_data;
+ }
+
+ /* Initialize and fill the payload */
+ key_cmd = parse_inputdata(data, ckpayload);
+ if (key_cmd == OP_ERROR) {
+ pr_err("Fail to parse data\n");
+ ret = key_cmd;
+ goto free_payload;
+ }
+
+ /* Create the black key and/or the blob */
+ caam_transform(key_cmd, ckpayload);
+ if (ret != 0) {
+ pr_info("transform fail (%d)\n", ret);
+ goto free_payload;
+ }
+
+ /* Store the payload to the key */
+ rcu_assign_keypointer(key, ckpayload);
+
+ goto out;
+
+free_payload:
+ kzfree(ckpayload);
+
+free_data:
+ kzfree(data);
+
+out:
+ return ret;
+}
+
+/*
+ * caam_read - copy the blob data to userspace.
+ * param[in]: key pointer to key struct
+ * param[in]: buffer pointer to user data for creating key
+ * param[in]: buflen is the length of the buffer
+ * On success, return to userspace the caam key data size.
+ */
+static long caam_read(const struct key *key, char __user *buffer, size_t buflen)
+{
+ const struct caam_key_payload *ckpayload = NULL;
+ size_t size_to_copy;
+ size_t size_copied = 0;
+ unsigned long not_copied;
+ char *to = buffer;
+
+ /* Retrieve the payload */
+ ckpayload = dereference_key_locked(key);
+ if (!ckpayload) {
+ pr_err("Fail to retrieve key payload\n");
+ return -EINVAL;
+ }
+
+ /* Check all the data can be copied */
+ size_to_copy = ckpayload->blob_len;
+
+ /* If buflen == 0, the user request the size needed */
+ if (buflen == 0)
+ return size_to_copy;
+
+ /* Check the buffer */
+ if (!buffer) {
+ pr_err("Buffer not set\n");
+ return -EINVAL;
+ }
+
+ /* Check the buffer is big enough */
+ if (size_to_copy > buflen) {
+ pr_err("Buffer length too short\n");
+ return -ENOMEM;
+ }
+
+ /* Copy blob */
+ not_copied = copy_to_user(to, ckpayload->blob, ckpayload->blob_len);
+ if (not_copied != 0) {
+ pr_err("Copy of black blob failed\n");
+ return -EIO;
+ }
+ size_copied += ckpayload->blob_len;
+
+ if (size_to_copy != size_copied)
+ pr_info("Mismatch between size computed and copied\n");
+
+ return size_copied;
+}
+
+/* Description of the key type for CAAM keys */
+struct key_type key_type_caam_tk = {
+ .name = "caam_tk",
+ .instantiate = caam_instantiate,
+ .destroy = caam_destroy,
+ .read = caam_read,
+};
+EXPORT_SYMBOL_GPL(key_type_caam_tk);
+
+static int __init init_caam_key(void)
+{
+ int ret;
+
+ ret = register_key_type(&key_type_caam_tk);
+ if (ret) {
+ pr_err("Failed to register key storage %s\n",
+ key_type_caam_tk.name);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static void __exit cleanup_caam_key(void)
+{
+ unregister_key_type(&key_type_caam_tk);
+}
+
+late_initcall(init_caam_key);
+module_exit(cleanup_caam_key);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/caam/caam_key.h b/drivers/crypto/caam/caam_key.h
new file mode 100644
index 0000000..93273ea
--- /dev/null
+++ b/drivers/crypto/caam/caam_key.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 NXP.
+ *
+ */
+
+#ifndef _KEYS_caam_TYPE_H
+#define _KEYS_caam_TYPE_H
+
+#include <linux/rcupdate.h>
+#include <linux/key-type.h>
+#include <keys/user-type.h>
+#include "caam_desc.h"
+#include "tag_object.h"
+
+extern struct key_type key_type_caam_tk;
+
+/* Minimum key size to be used is 32 bytes and maximum key size fixed
+ * is 128 bytes.
+ * Blob size to be kept is Maximum key size + blob header added by CAAM.
+ */
+
+#define MIN_KEY_SIZE 16
+#define MAX_KEY_SIZE 128
+
+#define MAX_BLACK_KEY_SIZE (MAX_KEY_SIZE + CCM_OVERHEAD +\
+ TAG_OVERHEAD)
+
+#define MAX_BLOB_SIZE (MAX_KEY_SIZE + BLOB_OVERHEAD)
+
+struct caam_key_payload {
+ /*
+ * The aliasing of the structure allow user to see this payload
+ * as a user defined payload
+ *
+ * The structure has to be set during execution
+ */
+ struct aliased_user_key_payload {
+ struct rcu_head rcu;
+ unsigned short datalen;
+ char data[MAX_BLACK_KEY_SIZE];
+ } upayload;
+
+ size_t key_len;
+ unsigned char key[MAX_KEY_SIZE + 1];
+ int key_fmt_val;
+
+ size_t black_key_len;
+ unsigned char black_key[MAX_BLACK_KEY_SIZE];
+
+ size_t blob_len;
+ unsigned char blob[MAX_BLOB_SIZE];
+
+ size_t key_mod_len;
+ const void *key_mod;
+};
+
+#endif
--
2.7.4
There was only 2 key_type supported by dm-crypt which limits other
implementations.
This patch allows to use any key_type which is registered obtaining
the key_type from key framework.
This also remove the compilation dependency between dm-crypt and
key implementations.
Signed-off-by: Franck LENORMAND <[email protected]>
---
drivers/md/dm-crypt.c | 11 ++++++-----
include/linux/key-type.h | 2 ++
security/keys/key.c | 42 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index dd538e6..e25efc2 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -35,6 +35,7 @@
#include <crypto/authenc.h>
#include <linux/rtnetlink.h> /* for struct rtattr and RTA macros only */
#include <keys/user-type.h>
+#include <linux/key-type.h>
#include <linux/device-mapper.h>
@@ -2010,6 +2011,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string
int ret;
struct key *key;
const struct user_key_payload *ukp;
+ struct key_type *type;
/*
* Reject key_string with whitespace. dm core currently lacks code for
@@ -2025,16 +2027,15 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string
if (!key_desc || key_desc == key_string || !strlen(key_desc + 1))
return -EINVAL;
- if (strncmp(key_string, "logon:", key_desc - key_string + 1) &&
- strncmp(key_string, "user:", key_desc - key_string + 1))
- return -EINVAL;
+ type = get_key_type(key_string, key_desc - key_string);
+ if (!type)
+ return -ENOENT;
new_key_string = kstrdup(key_string, GFP_KERNEL);
if (!new_key_string)
return -ENOMEM;
- key = request_key(key_string[0] == 'l' ? &key_type_logon : &key_type_user,
- key_desc + 1, NULL);
+ key = request_key(type, key_desc + 1, NULL);
if (IS_ERR(key)) {
kzfree(new_key_string);
return PTR_ERR(key);
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index bc9af55..2b2167b 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -176,6 +176,8 @@ extern struct key_type key_type_keyring;
extern int register_key_type(struct key_type *ktype);
extern void unregister_key_type(struct key_type *ktype);
+extern struct key_type *get_key_type(const char *type_name, size_t string_size);
+
extern int key_payload_reserve(struct key *key, size_t datalen);
extern int key_instantiate_and_link(struct key *key,
const void *data,
diff --git a/security/keys/key.c b/security/keys/key.c
index 44a80d6..ef76114 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -1156,6 +1156,48 @@ void unregister_key_type(struct key_type *ktype)
}
EXPORT_SYMBOL(unregister_key_type);
+/**
+ * get_key_type - Get the type of key using its name
+ * @type_name: Name of the key type to get
+ * @string_size: Size of the string to match
+ *
+ * The functions support null ended string (string_size == 0) as well as
+ * pointer on a string matching a number of characters (string_size > 0)
+ *
+ * Returns a pointer on the key type if successful, -ENOENT if the key type
+ * is not registered.
+ */
+struct key_type *get_key_type(const char *type_name, size_t string_size)
+{
+ struct key_type *p;
+ struct key_type *ktype = ERR_PTR(-ENOENT);
+
+ if (!type_name)
+ return ktype;
+
+ down_write(&key_types_sem);
+
+ /* Search the key type in the list */
+ list_for_each_entry(p, &key_types_list, link) {
+ if (string_size) {
+ if (strncmp(p->name, type_name, string_size) == 0) {
+ ktype = p;
+ break;
+ }
+ } else {
+ if (strcmp(p->name, type_name) == 0) {
+ ktype = p;
+ break;
+ }
+ }
+ }
+
+ up_read(&key_types_sem);
+
+ return ktype;
+}
+EXPORT_SYMBOL(get_key_type);
+
/*
* Initialise the key management state.
*/
--
2.7.4
Franck LENORMAND <[email protected]> wrote:
> The capacity to generate or load keys already available in the Linux key
> retention service does not allows to exploit CAAM capabilities hence we
> need to create a new key_type. The new key type "caam_tk" allows to:
> - Create a black key from random
> - Create a black key from a red key
> - Load a black blob to retrieve the black key
Is it possible that this could be done through an existing key type, such as
the asymmetric, trusted or encrypted key typed?
David
Hi Franck,
thanks for working on this!
On Fri, 2019-03-01 at 17:09 +0100, Franck LENORMAND wrote:
> The creation of such structures and its use was not exposed to userspace so
> it was complicated to use and required custom development. We would like to
> ease this using interface which are known and used:
> - Linux key retention service : Allow to generate or load keys in a
> keyring which can be used by applications.
> - dm-crypt : device mapper allowing to encrypt data.
>
> The capacity to generate or load keys already available in the Linux key
> retention service does not allows to exploit CAAM capabilities hence we
> need to create a new key_type. The new key type "caam_tk" allows to:
> - Create a black key from random
> - Create a black key from a red key
> - Load a black blob to retrieve the black key
On 2018-07-23, Udit Agarwal <[email protected]> sent a series which
seems related to this:
[PATCH v2 1/2] security/keys/secure_key: Adds the secure key support
based on CAAM.
[PATCH v2 2/2] encrypted_keys: Adds support for secure key-type as
master key.
Is this series intended to continue that work and cover the same uses-
cases?
If I remember correctly, the CAAM also supports marking blobs to allow
or disallow exporting the encapsulated key from the hardware. Or is
this unneeded and we could encrypt/decrypt other (less critical) key
material against the tk(cbc(aes)) CAAM key via the keyring mechanisms?
Best regards,
Jan
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
> -----Original Message-----
> From: Jan Lübbe <[email protected]>
> Sent: Wednesday, March 6, 2019 5:48 PM
> To: Franck Lenormand <[email protected]>; linux-
> [email protected]; [email protected];
> [email protected]
> Cc: Horia Geanta <[email protected]>; Silvano Di Ninno
> <[email protected]>; [email protected]; [email protected]; dm-
> [email protected]; [email protected]; [email protected];
> [email protected]; David Gstir <[email protected]>
> Subject: Re: [RFC PATCH 0/2] Create CAAM HW key in linux keyring and use in
> dmcrypt
>
> Hi Franck,
>
> thanks for working on this!
>
> On Fri, 2019-03-01 at 17:09 +0100, Franck LENORMAND wrote:
> > The creation of such structures and its use was not exposed to
> > userspace so it was complicated to use and required custom
> > development. We would like to ease this using interface which are known
> and used:
> > - Linux key retention service : Allow to generate or load keys in a
> > keyring which can be used by applications.
> > - dm-crypt : device mapper allowing to encrypt data.
> >
> > The capacity to generate or load keys already available in the Linux
> > key retention service does not allows to exploit CAAM capabilities
> > hence we need to create a new key_type. The new key type "caam_tk"
> allows to:
> > - Create a black key from random
> > - Create a black key from a red key
> > - Load a black blob to retrieve the black key
>
> On 2018-07-23, Udit Agarwal <[email protected]> sent a series which
> seems related to this:
> [PATCH v2 1/2] security/keys/secure_key: Adds the secure key support based
> on CAAM.
> [PATCH v2 2/2] encrypted_keys: Adds support for secure key-type as master
> key.
>
> Is this series intended to continue that work and cover the same uses- cases?
>
> If I remember correctly, the CAAM also supports marking blobs to allow or
> disallow exporting the encapsulated key from the hardware. Or is this
> unneeded and we could encrypt/decrypt other (less critical) key material
> against the tk(cbc(aes)) CAAM key via the keyring mechanisms?
>
> Best regards,
> Jan
> --
> Pengutronix e.K. | |
> Industrial Linux Solutions |
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fww
> w.pengutronix.de%2F&data=02%7C01%7Cfranck.lenormand%40nxp.co
> m%7C29ab89e99f9040e30aa908d6a2537967%7C686ea1d3bc2b4c6fa92cd99c
> 5c301635%7C0%7C0%7C636874876732153093&sdata=byKD4SmYfzMTs
> FK6cX5L81%2B9hxxqTbnOLf7%2BAJgXylU%3D&reserved=0 |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Hello Jan,
The current series is aimed to change which key type is acceptable in dm-crypt. Without
this change, the key type cannot be integrated properly.
About the key type implementation:
The implementation of the key type is an improvement related to the work of Udit Agarwal.
The transform tk(cbc(aes)) is intended to receive key material whose format can change
hence the "tagging" to handle it properly. It could, for example, handle plain keys, black keys or blobs as key material and this material would be stored in keyring.
Regards,
Franck
> -----Original Message-----
> From: David Howells <[email protected]>
> Sent: Wednesday, March 6, 2019 6:30 PM
> To: Franck Lenormand <[email protected]>
> Cc: [email protected]; [email protected]; linux-security-
> [email protected]; [email protected]; Horia Geanta
> <[email protected]>; Silvano Di Ninno <[email protected]>;
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]
> Subject: Re: [RFC PATCH 0/2] Create CAAM HW key in linux keyring and use in
> dmcrypt
>
> Franck LENORMAND <[email protected]> wrote:
>
> > The capacity to generate or load keys already available in the Linux
> > key retention service does not allows to exploit CAAM capabilities
> > hence we need to create a new key_type. The new key type "caam_tk"
> allows to:
> > - Create a black key from random
> > - Create a black key from a red key
> > - Load a black blob to retrieve the black key
>
> Is it possible that this could be done through an existing key type, such as the
> asymmetric, trusted or encrypted key typed?
>
> David
Hello David,
I didn't know about asymmetric key type so I looked it up, from my
observation, it would not be possible to use it for the caam_tk as
we must perform operations on the data provided.
The name " asymmetric " is also misleading for the use we would have.
The trusted and encrypted does not provides the necessary
callbacks to do what we would need or require huge modifications.
I would like, for this series to focus on the change related to
dm-crypt. In effect, it is currently not possible to pass a key
from the asymmetric key type to it.
Franck
On Thu, 2019-03-07 at 13:17 +0000, Franck Lenormand wrote:
> > -----Original Message-----
> > From: David Howells <[email protected]>
> > Sent: Wednesday, March 6, 2019 6:30 PM
> > To: Franck Lenormand <[email protected]>
> > Cc: [email protected]; [email protected]; linux-
> > security-
> > [email protected]; [email protected]; Horia Geanta
> > <[email protected]>; Silvano Di Ninno <[email protected]>;
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]
> > Subject: Re: [RFC PATCH 0/2] Create CAAM HW key in linux keyring
> > and use in
> > dmcrypt
> >
> > Franck LENORMAND <[email protected]> wrote:
> >
> > > The capacity to generate or load keys already available in the
> > > Linux
> > > key retention service does not allows to exploit CAAM
> > > capabilities
> > > hence we need to create a new key_type. The new key type
> > > "caam_tk"
> >
> > allows to:
> > > - Create a black key from random
> > > - Create a black key from a red key
> > > - Load a black blob to retrieve the black key
> >
> > Is it possible that this could be done through an existing key
> > type, such as the
> > asymmetric, trusted or encrypted key typed?
> >
> > David
>
> Hello David,
>
> I didn't know about asymmetric key type so I looked it up, from my
> observation, it would not be possible to use it for the caam_tk as
> we must perform operations on the data provided.
> The name " asymmetric " is also misleading for the use we would have.
>
> The trusted and encrypted does not provides the necessary
> callbacks to do what we would need or require huge modifications.
>
> I would like, for this series to focus on the change related to
> dm-crypt. In effect, it is currently not possible to pass a key
> from the asymmetric key type to it.
What you're performing are all bog standard key wrapping operations
which is why we're asking the question: do we have to expose any of
this to the user? Can this whole thing not be encapsulated in such a
way that the kernel automatically selects the best key
escrow/accelerator technology on boot and uses it (if there are > 1
there would have to be an interface for the user to choose). We make
all the accelerator device key formats distinguishable so the kernel
can figure out on load what is supposed to be handling them. That way
the user never need worry about naming the actual key handler at all,
it would all be taken care of under the covers.
The one key type per escrow/accelerator has us all going ... "aren't
there hundreds of these on the market?" which would seem to be a huge
usability explosion because a user will likely only have one, but they
have to figure out the type instructions for that one. I really think
a better way is to have a more generic key type that's capable of
interfacing to the wrap/unwrap and crypto functions in such a way that
the end user doesn't have to know which they're using: most platforms
only have one thing installed, so you use that thing.
James
On Fri, 2019-03-01 at 17:09 +0100, Franck LENORMAND wrote:
> @@ -2025,16 +2027,15 @@ static int crypt_set_keyring_key(struct
> crypt_config *cc, const char *key_string
> if (!key_desc || key_desc == key_string || !strlen(key_desc
> + 1))
> return -EINVAL;
>
> - if (strncmp(key_string, "logon:", key_desc - key_string + 1)
> &&
> - strncmp(key_string, "user:", key_desc - key_string + 1))
> - return -EINVAL;
> + type = get_key_type(key_string, key_desc - key_string);
> + if (!type)
> + return -ENOENT;
You can't do this. This check ensures that the key responds correctly
to user_key_payload_locked() lower down. To do that, the payload has
to be in a specific form. You ensured that yours are, but dm-crypt
will now accept any key type, load the user payload blindly and create
all sorts of mayhem in the kernel because of the structural differences
in payload types.
James