2015-11-09 15:19:02

by Steffen Trumtrar

[permalink] [raw]
Subject: [RFC] i.MX6 CAAM blob generator for IMA/EVM initialization

Hi!

The RFC Patch attached after this cover letter is mostly for illustration
purposes, so please don't waste too much time reviewing the code ;-)

For context I'll try to describe the problem that this patch tries to solve.

I need to be able to boot an EVM signed (and dongled) rootfs. The CAAM on
the i.MX6 has support for an OTP key and can en/decrypt data.
It also has a feature for generating red blobs: basically a chunk of data,
that is encrypted with the OTP key, which can be saved on some medium as a
secret to decrypt the EVM HMAC secret for one specific device.

To open the rootfs, the secret is handed from the bootloader to the kernel
as a base64 encoded string via the cmdline to an initramfs.
In the initramfs the sysfs file "modifier" is set to something starting with
"kernel:evm" and the base64 string is written to the sysfs file "blob".
The CAAM than decodes the red blob and, in case of "kernel:evm", initializes
the EVM or otherwise writes the result to "payload" if the modifier starts
with "user:". Therefore a blob that was generated for EVM never leaves the
kernel on decryption.
Generation of blobs goes like: echoing "modifier" to something and echoing
the payload to "payload". The red blob can than be read from "blob".


So, the sysfs interface is not the best option, I guess. The question is:
What is the right approach for a setup like this?
I need to:
- be able to encrypt the secret and store it somewhere
- to load the stored secret and decrypt it later
- initialize IMA/EVM with the secret

Would something like
- security/keys/encrypted-keys/encrypted.c
be the correct approach?

Thanks,
Steffen


Steffen Trumtrar (1):
crypto: caam - add red blobifier

drivers/crypto/caam/Kconfig | 9 +
drivers/crypto/caam/Makefile | 1 +
drivers/crypto/caam/blob_gen.c | 528 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 538 insertions(+)
create mode 100644 drivers/crypto/caam/blob_gen.c

--
2.6.1


2015-11-09 15:19:03

by Steffen Trumtrar

[permalink] [raw]
Subject: [RFC] crypto: caam - add red blobifier

Signed-off-by: Steffen Trumtrar <[email protected]>
---
drivers/crypto/caam/Kconfig | 9 +
drivers/crypto/caam/Makefile | 1 +
drivers/crypto/caam/blob_gen.c | 528 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 538 insertions(+)
create mode 100644 drivers/crypto/caam/blob_gen.c

diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index e7555ff4cafd..329fab7ba4f7 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -99,6 +99,15 @@ config CRYPTO_DEV_FSL_CAAM_AHASH_API
To compile this as a module, choose M here: the module
will be called caamhash.

+config CRYPTO_DEV_FSL_CAAM_BLOB_GEN
+ tristate "CAAM Blob Generator (EXPERIMENTAL)"
+ depends on CRYPTO_DEV_FSL_CAAM
+ default n
+ help
+ Selecting this will enable the CAAM red blob generator.
+ This module will take a chunk of data via sysfs and returns
+ an encrypted red blob. The inverse is also possible.
+
config CRYPTO_DEV_FSL_CAAM_RNG_API
tristate "Register caam device for hwrng API"
depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 550758a333e7..359ef3b97fdf 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.o

caam-objs := ctrl.o
caam_jr-objs := jr.o key_gen.o error.o
diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c
new file mode 100644
index 000000000000..9c2a02c2882a
--- /dev/null
+++ b/drivers/crypto/caam/blob_gen.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/evm.h>
+#include "compat.h"
+#include "intern.h"
+#include "desc.h"
+#include "desc_constr.h"
+#include "error.h"
+#include "jr.h"
+
+enum access_rights {
+ KERNEL,
+ KERNEL_EVM,
+ USERSPACE,
+};
+
+struct blob_priv {
+ struct device *jrdev;
+ struct device dev;
+ u32 *desc;
+ u8 __iomem *red_blob;
+ u8 __iomem *modifier;
+ u8 __iomem *output;
+ bool busy;
+ enum access_rights access;
+ unsigned int payload_size;
+ struct bin_attribute *blob;
+ struct bin_attribute *payload;
+ struct caam_drv_private *ctrlpriv;
+};
+
+struct blob_job_result {
+ int err;
+ struct completion completion;
+};
+
+static struct platform_device *pdev;
+
+static void blob_job_done(struct device *dev, u32 *desc, u32 err, void *context)
+{
+ struct blob_job_result *res = context;
+
+#ifdef DEBUG
+ dev_err(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ if (err)
+ caam_jr_strstatus(dev, err);
+
+ res->err = err;
+
+ complete(&res->completion);
+}
+
+/*
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable
+ * blob.
+ */
+#define INITIAL_DESCSZ 16
+#define BLOB_OVERHEAD (32 + 16)
+#define KEYMOD_LENGTH 16
+#define RED_BLOB_LENGTH 64
+#define MAX_BLOB_LEN 4096
+
+/*
+ * Generate a blob with the following format:
+ * Format: Normal format (Test format only for testing)
+ * Contents: General data (aka red blob)
+ * Security State: Secure State (Non-Secure for testing)
+ * Memory Types: General memory
+ */
+static int blob_encap_blob(struct blob_priv *priv, u8 *keymod, u8 *input,
+ u8 *output, u16 length)
+{
+ u32 *desc;
+ struct device *jrdev = priv->jrdev;
+ dma_addr_t dma_keymod;
+ dma_addr_t dma_in;
+ dma_addr_t dma_out;
+ struct blob_job_result testres;
+ int ret;
+
+ desc = kmalloc(CAAM_CMD_SZ * 5 + CAAM_PTR_SZ * 5, GFP_KERNEL | GFP_DMA);
+ if (!desc) {
+ dev_err(jrdev, "unable to allocate desc\n");
+ return -ENOMEM;
+ }
+
+ dma_keymod = dma_map_single(jrdev, keymod, KEYMOD_LENGTH, DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, dma_keymod)) {
+ dev_err(jrdev, "unable to map keymod buffer\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ dma_in = dma_map_single(jrdev, input, length - BLOB_OVERHEAD,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, dma_in)) {
+ dev_err(jrdev, "unable to map keymod buffer\n");
+ ret = -ENOMEM;
+ goto out_unmap_key;
+ }
+
+ dma_out = dma_map_single(jrdev, output, length, DMA_FROM_DEVICE);
+ if (dma_mapping_error(jrdev, dma_out)) {
+ dev_err(jrdev, "unable to map output DMA buffer\n");
+ ret = -ENOMEM;
+ goto out_unmap_in;
+ }
+
+ /*
+ * A data blob is encrypted using a blob key (BK); a random number.
+ * The BK is used as an AES-CCM key. The initial block (B0) and the
+ * initial counter (Ctr0) are generated automatically and stored in
+ * Class 1 Context DWords 0+1+2+3. The random BK is stored in the
+ * Class 1 Key Register. Operation Mode is set to AES-CCM.
+ */
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, dma_keymod, KEYMOD_LENGTH, CLASS_2 | KEY_DEST_CLASS_REG);
+ append_seq_in_ptr(desc, dma_in, length - BLOB_OVERHEAD, 0);
+ append_seq_out_ptr(desc, dma_out, length, 0);
+ append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
+
+#ifdef DEBUG
+ printk("%s: keymod %s\n", __func__, keymod);
+ print_hex_dump(KERN_ERR, "data@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, input,
+ length - BLOB_OVERHEAD, false);
+ print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, desc,
+ desc_bytes(desc), false);
+#endif
+
+ testres.err = 0;
+ init_completion(&testres.completion);
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (!ret) {
+ wait_for_completion_interruptible(&testres.completion);
+ ret = testres.err;
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "output@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, output,
+ length, false);
+#endif
+ }
+
+ dma_unmap_single(jrdev, dma_out, length,
+ DMA_FROM_DEVICE);
+out_unmap_in:
+ dma_unmap_single(jrdev, dma_keymod, KEYMOD_LENGTH, DMA_TO_DEVICE);
+out_unmap_key:
+ dma_unmap_single(jrdev, dma_in, length - BLOB_OVERHEAD, DMA_TO_DEVICE);
+out_free:
+ kfree(desc);
+
+ return ret;
+}
+
+static int blob_decap_blob(struct blob_priv *priv, u8 *keymod, u8 *input,
+ u8 *output, u16 length)
+{
+ u32 *desc;
+ struct device *jrdev = priv->jrdev;
+ dma_addr_t dma_keymod;
+ dma_addr_t dma_in;
+ dma_addr_t dma_out;
+ struct blob_job_result testres;
+ int ret;
+
+ desc = kzalloc(CAAM_CMD_SZ * 5 + CAAM_PTR_SZ * 5, GFP_KERNEL | GFP_DMA);
+ if (!desc) {
+ dev_err(jrdev, "unable to allocate desc\n");
+ return -ENOMEM;
+ }
+
+ dma_keymod = dma_map_single(jrdev, keymod, KEYMOD_LENGTH, DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, dma_keymod)) {
+ dev_err(jrdev, "unable to map keymod buffer\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ dma_in = dma_map_single(jrdev, input, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, dma_in)) {
+ dev_err(jrdev, "unable to map keymod buffer\n");
+ ret = -ENOMEM;
+ goto out_unmap_key;
+ }
+
+ dma_out = dma_map_single(jrdev, output, length - BLOB_OVERHEAD, DMA_FROM_DEVICE);
+ if (dma_mapping_error(jrdev, dma_out)) {
+ dev_err(jrdev, "unable to map output DMA buffer\n");
+ ret = -ENOMEM;
+ goto out_unmap_in;
+ }
+
+ /*
+ * A data blob is encrypted using a blob key (BK); a random number.
+ * The BK is used as an AES-CCM key. The initial block (B0) and the
+ * initial counter (Ctr0) are generated automatically and stored in
+ * Class 1 Context DWords 0+1+2+3. The random BK is stored in the
+ * Class 1 Key Register. Operation Mode is set to AES-CCM.
+ */
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, dma_keymod, KEYMOD_LENGTH, CLASS_2 | KEY_DEST_CLASS_REG);
+ append_seq_in_ptr(desc, dma_in, length, 0);
+ append_seq_out_ptr(desc, dma_out, length - BLOB_OVERHEAD, 0);
+ append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "data@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, input,
+ length, false);
+ print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, desc,
+ desc_bytes(desc), false);
+#endif
+
+ testres.err = 0;
+ init_completion(&testres.completion);
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (!ret) {
+ wait_for_completion_interruptible(&testres.completion);
+ ret = testres.err;
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "output@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, output,
+ length - BLOB_OVERHEAD, false);
+#endif
+ }
+
+ dma_unmap_single(jrdev, dma_out, length - BLOB_OVERHEAD, DMA_FROM_DEVICE);
+out_unmap_in:
+ dma_unmap_single(jrdev, dma_keymod, KEYMOD_LENGTH, DMA_TO_DEVICE);
+out_unmap_key:
+ dma_unmap_single(jrdev, dma_in, length, DMA_TO_DEVICE);
+out_free:
+ kfree(desc);
+
+ return ret;
+}
+
+static void blob_gen_init_evm(struct blob_priv *priv)
+{
+ evm_init((const char *)priv->output, priv->payload_size);
+}
+
+static ssize_t
+blob_gen_blob_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t size)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct blob_priv *priv = dev_get_drvdata(dev);
+ int length = min((int)size, (int) (priv->payload_size + BLOB_OVERHEAD - off));
+ int ret;
+
+ if (length > 0) {
+ priv->busy = true;
+
+ ret = blob_encap_blob(priv, priv->modifier, priv->output, priv->red_blob, length);
+ if (ret) {
+ priv->busy = false;
+ return -EINVAL;
+ }
+
+ memcpy(buf, priv->red_blob, length);
+
+ priv->busy = false;
+ }
+
+ return length;
+}
+
+static ssize_t
+blob_gen_blob_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t size)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct blob_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ if (size > 0) {
+ priv->busy = true;
+
+ memcpy(priv->red_blob, buf, size);
+
+ memset(priv->output, 0, MAX_BLOB_LEN - BLOB_OVERHEAD);
+
+ ret = blob_decap_blob(priv, priv->modifier, priv->red_blob,
+ priv->output, size);
+ if (ret) {
+ priv->busy = false;
+ return -EINVAL;
+ }
+
+ priv->payload_size = size - BLOB_OVERHEAD;
+
+ if (priv->access == KERNEL_EVM)
+ blob_gen_init_evm(priv);
+
+ priv->busy = false;
+ }
+
+ return size;
+}
+
+static ssize_t
+blob_gen_payload_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t size)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct blob_priv *priv = dev_get_drvdata(dev);
+ int length = min((int) size, (int) (priv->payload_size - off));
+
+ if (priv->payload_size == 0)
+ return -EINVAL;
+
+ if (priv->access != USERSPACE)
+ return -EPERM;
+
+ memcpy(buf, priv->output + off, length);
+
+ return length;
+}
+
+static ssize_t
+blob_gen_payload_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t size)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct blob_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->busy)
+ return -EPERM;
+
+ memcpy(priv->output, buf, size);
+
+ priv->payload_size = size;
+
+ return size;
+}
+
+static ssize_t
+blob_gen_show_modifier(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct blob_priv *priv = dev_get_drvdata(dev);
+
+ memcpy(buf, priv->modifier, KEYMOD_LENGTH);
+
+ return KEYMOD_LENGTH;
+}
+
+static ssize_t
+blob_gen_set_modifier(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct blob_priv *priv = dev_get_drvdata(dev);
+ char *buf_ptr;
+
+ if (n > KEYMOD_LENGTH)
+ return -EINVAL;
+
+ if (priv->busy)
+ return -EPERM;
+
+ buf_ptr = (char *)buf;
+ if (!strncmp(buf_ptr, "kernel:evm", 10))
+ priv->access = KERNEL_EVM;
+ else if (!strncmp(buf_ptr, "kernel:", 7))
+ priv->access = KERNEL;
+ else if (!strncmp(buf_ptr, "user:", 5))
+ priv->access = USERSPACE;
+
+ memset(priv->output, 0, MAX_BLOB_LEN - BLOB_OVERHEAD);
+ memset(priv->red_blob, 0, MAX_BLOB_LEN);
+
+ memcpy(priv->modifier, buf, n);
+
+ return n;
+}
+static DEVICE_ATTR(modifier, S_IRUSR | S_IWUSR,
+ blob_gen_show_modifier, blob_gen_set_modifier);
+
+static struct attribute *blob_attrs[] = {
+ &dev_attr_modifier.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(blob);
+
+static int __init blob_gen_init(void)
+{
+ struct device_node *dev_node;
+ struct device *ctrldev;
+ struct caam_drv_private *ctrlpriv;
+ struct blob_priv *priv;
+ int ret;
+
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+ if (!dev_node) {
+ dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+ if (!dev_node)
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev) {
+ of_node_put(dev_node);
+ return -ENODEV;
+ }
+
+ ctrldev = &pdev->dev;
+ ctrlpriv = dev_get_drvdata(ctrldev);
+
+ pdev = platform_device_alloc("blob_gen", -1);
+ if (!pdev)
+ return -ENOMEM;
+ pdev->dev.parent = ctrldev;
+ pdev->dev.groups = blob_groups;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ priv->blob = devm_kzalloc(&pdev->dev, sizeof(struct bin_attribute),
+ GFP_KERNEL);
+ priv->payload = devm_kzalloc(&pdev->dev, sizeof(struct bin_attribute),
+ GFP_KERNEL);
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto pdev_add_failed;
+
+ priv->modifier = devm_kzalloc(&pdev->dev, sizeof(*priv->modifier)*KEYMOD_LENGTH,
+ GFP_KERNEL | GFP_DMA);
+
+ priv->red_blob = devm_kzalloc(&pdev->dev, sizeof(*priv->red_blob)*
+ MAX_BLOB_LEN, GFP_KERNEL | GFP_DMA);
+
+ priv->output = devm_kzalloc(&pdev->dev, sizeof(*priv->output)*
+ MAX_BLOB_LEN - BLOB_OVERHEAD, GFP_KERNEL | GFP_DMA);
+
+ priv->desc = devm_kzalloc(&pdev->dev, sizeof(*priv->desc), GFP_KERNEL | GFP_DMA);
+ if (priv->desc == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ priv->busy = false;
+
+ priv->jrdev = &ctrlpriv->jrpdev[0]->dev;
+
+ priv->blob->attr.name = "blob";
+ priv->blob->attr.mode = S_IRUSR | S_IWUSR;
+
+ sysfs_bin_attr_init(priv->blob);
+ priv->blob->read = blob_gen_blob_read;
+ priv->blob->write = blob_gen_blob_write;
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, priv->blob);
+ if (ret)
+ dev_err(&pdev->dev, "unable to create sysfs file %s\n",
+ priv->blob->attr.name);
+ else
+ dev_info(&pdev->dev, "added sysfs binary attribute\n");
+
+ priv->payload->attr.name = "payload";
+ priv->payload->attr.mode = S_IRUSR | S_IWUSR;
+
+ sysfs_bin_attr_init(priv->payload);
+ priv->payload->read = blob_gen_payload_read;
+ priv->payload->write = blob_gen_payload_write;
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, priv->payload);
+ if (ret)
+ dev_err(&pdev->dev, "unable to create sysfs file %s\n",
+ priv->payload->attr.name);
+ else
+ dev_info(&pdev->dev, "added sysfs binary attribute\n");
+
+ return 0;
+
+pdev_add_failed:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+static void __exit blob_gen_exit(void)
+{
+ struct blob_priv *priv;
+
+ priv = dev_get_drvdata(&pdev->dev);
+ if (!priv)
+ return;
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, priv->payload);
+ sysfs_remove_bin_file(&pdev->dev.kobj, priv->blob);
+
+ platform_device_unregister(pdev);
+}
+
+module_init(blob_gen_init);
+module_exit(blob_gen_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Blob Generator Example");
+MODULE_AUTHOR("Steffen Trumtrar <[email protected]>");
--
2.6.1

2015-11-09 20:29:54

by Mimi Zohar

[permalink] [raw]
Subject: Re: [Linux-ima-user] [RFC] i.MX6 CAAM blob generator for IMA/EVM initialization

On Mon, 2015-11-09 at 16:18 +0100, Steffen Trumtrar wrote:
> Hi!
>
> The RFC Patch attached after this cover letter is mostly for illustration
> purposes, so please don't waste too much time reviewing the code ;-)
>
> For context I'll try to describe the problem that this patch tries to solve.
>
> I need to be able to boot an EVM signed (and dongled) rootfs. The CAAM on
> the i.MX6 has support for an OTP key and can en/decrypt data.
> It also has a feature for generating red blobs: basically a chunk of data,
> that is encrypted with the OTP key, which can be saved on some medium as a
> secret to decrypt the EVM HMAC secret for one specific device.
>
> To open the rootfs, the secret is handed from the bootloader to the kernel
> as a base64 encoded string via the cmdline to an initramfs.
> In the initramfs the sysfs file "modifier" is set to something starting with
> "kernel:evm" and the base64 string is written to the sysfs file "blob".
> The CAAM than decodes the red blob and, in case of "kernel:evm", initializes
> the EVM or otherwise writes the result to "payload" if the modifier starts
> with "user:". Therefore a blob that was generated for EVM never leaves the
> kernel on decryption.
> Generation of blobs goes like: echoing "modifier" to something and echoing
> the payload to "payload". The red blob can than be read from "blob".
>
>
> So, the sysfs interface is not the best option, I guess. The question is:
> What is the right approach for a setup like this?
> I need to:
> - be able to encrypt the secret and store it somewhere
> - to load the stored secret and decrypt it later
> - initialize IMA/EVM with the secret
>
> Would something like
> - security/keys/encrypted-keys/encrypted.c
> be the correct approach?

Instead of using the CAAM for OTP encrypting/decrypting, can it be used
to load the EVM key directly? Dmitry's patches, which will be
upstreamed in 4.5
https://git.kernel.org/cgit/linux/kernel/git/zohar/linux-integrity.git/log/?h=for-next-4.5? adds support for a crypto device to directly load the EVM key.

FYI, the EVM key is an encrypted key, which encrypts/decrypts either a
trusted or user type key.

Mimi

2016-01-27 10:04:59

by Steffen Trumtrar

[permalink] [raw]
Subject: Re: [Linux-ima-user] [RFC] i.MX6 CAAM blob generator for IMA/EVM initialization


Hi!

Mimi Zohar writes:

> On Mon, 2015-11-09 at 16:18 +0100, Steffen Trumtrar wrote:
>> Hi!
>>
>> The RFC Patch attached after this cover letter is mostly for illustration
>> purposes, so please don't waste too much time reviewing the code ;-)
>>
>> For context I'll try to describe the problem that this patch tries to solve.
>>
>> I need to be able to boot an EVM signed (and dongled) rootfs. The CAAM on
>> the i.MX6 has support for an OTP key and can en/decrypt data.
>> It also has a feature for generating red blobs: basically a chunk of data,
>> that is encrypted with the OTP key, which can be saved on some medium as a
>> secret to decrypt the EVM HMAC secret for one specific device.
>>
>> To open the rootfs, the secret is handed from the bootloader to the kernel
>> as a base64 encoded string via the cmdline to an initramfs.
>> In the initramfs the sysfs file "modifier" is set to something starting with
>> "kernel:evm" and the base64 string is written to the sysfs file "blob".
>> The CAAM than decodes the red blob and, in case of "kernel:evm", initializes
>> the EVM or otherwise writes the result to "payload" if the modifier starts
>> with "user:". Therefore a blob that was generated for EVM never leaves the
>> kernel on decryption.
>> Generation of blobs goes like: echoing "modifier" to something and echoing
>> the payload to "payload". The red blob can than be read from "blob".
>>
>>
>> So, the sysfs interface is not the best option, I guess. The question is:
>> What is the right approach for a setup like this?
>> I need to:
>> - be able to encrypt the secret and store it somewhere
>> - to load the stored secret and decrypt it later
>> - initialize IMA/EVM with the secret
>>
>> Would something like
>> - security/keys/encrypted-keys/encrypted.c
>> be the correct approach?
>
> Instead of using the CAAM for OTP encrypting/decrypting, can it be used
> to load the EVM key directly? Dmitry's patches, which will be
> upstreamed in 4.5
> https://git.kernel.org/cgit/linux/kernel/git/zohar/linux-integrity.git/log/?h=for-next-4.5? adds support for a crypto device to directly load the EVM key.
>

The patches look good and I use them for loading the EVM key from the
CAAM driver. But I still need the OTP decryption functionality.

The key data that I hand to evm_set_key must be device specific but I
don't want to use the fused OTP in the CAAM directly.
The OTP is used to protect multiple random keys. Therefore I need to
generate encrypted blobs that I can store on some unsecure memory
(EEPROM, NAND,...) and be able to hand that later back to the CAAM
module, to then get back an IMA/EVM, ecryptfs, $something key.

> FYI, the EVM key is an encrypted key, which encrypts/decrypts either a
> trusted or user type key.
>
So the normal approach would be to have a key in the kernel keyring
and decrypt it with the key loaded with evm_set_key?

Can I somehow use the keyring framework as an abstraction around my
blobbing/deblobbing functionality?
So that the "keyring" calls into the crypto driver to decrypt the data
and uses the crypto driver to encrypt the keys when I want to "dump"
them?


Thanks,
Steffen

--
Pengutronix e.K. | Steffen Trumtrar |
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 |

2016-01-28 15:44:45

by Mimi Zohar

[permalink] [raw]
Subject: Re: [Linux-ima-user] [RFC] i.MX6 CAAM blob generator for IMA/EVM initialization

On Wed, 2016-01-27 at 11:04 +0100, Steffen Trumtrar wrote:
> Hi!
>
> Mimi Zohar writes:
>
> > On Mon, 2015-11-09 at 16:18 +0100, Steffen Trumtrar wrote:
> >> Hi!
> >>
> >> The RFC Patch attached after this cover letter is mostly for illustration
> >> purposes, so please don't waste too much time reviewing the code ;-)
> >>
> >> For context I'll try to describe the problem that this patch tries to solve.
> >>
> >> I need to be able to boot an EVM signed (and dongled) rootfs. The CAAM on
> >> the i.MX6 has support for an OTP key and can en/decrypt data.
> >> It also has a feature for generating red blobs: basically a chunk of data,
> >> that is encrypted with the OTP key, which can be saved on some medium as a
> >> secret to decrypt the EVM HMAC secret for one specific device.
> >>
> >> To open the rootfs, the secret is handed from the bootloader to the kernel
> >> as a base64 encoded string via the cmdline to an initramfs.
> >> In the initramfs the sysfs file "modifier" is set to something starting with
> >> "kernel:evm" and the base64 string is written to the sysfs file "blob".
> >> The CAAM than decodes the red blob and, in case of "kernel:evm", initializes
> >> the EVM or otherwise writes the result to "payload" if the modifier starts
> >> with "user:". Therefore a blob that was generated for EVM never leaves the
> >> kernel on decryption.
> >> Generation of blobs goes like: echoing "modifier" to something and echoing
> >> the payload to "payload". The red blob can than be read from "blob".
> >>
> >>
> >> So, the sysfs interface is not the best option, I guess. The question is:
> >> What is the right approach for a setup like this?
> >> I need to:
> >> - be able to encrypt the secret and store it somewhere
> >> - to load the stored secret and decrypt it later
> >> - initialize IMA/EVM with the secret
> >>
> >> Would something like
> >> - security/keys/encrypted-keys/encrypted.c
> >> be the correct approach?
> >
> > Instead of using the CAAM for OTP encrypting/decrypting, can it be used
> > to load the EVM key directly? Dmitry's patches, which will be
> > upstreamed in 4.5
> >
> https://git.kernel.org/cgit/linux/kernel/git/zohar/linux-integrity.git/log/?h=for-next-4.5? adds support for a crypto device to directly load the EVM key.
> >
>
> The patches look good and I use them for loading the EVM key from the
> CAAM driver. But I still need the OTP decryption functionality.

> The key data that I hand to evm_set_key must be device specific but I
> don't want to use the fused OTP in the CAAM directly.
> The OTP is used to protect multiple random keys. Therefore I need to
> generate encrypted blobs that I can store on some unsecure memory
> (EEPROM, NAND,...) and be able to hand that later back to the CAAM
> module, to then get back an IMA/EVM, ecryptfs, $something key.
>
> > FYI, the EVM key is an encrypted key, which encrypts/decrypts either a
> > trusted or user type key.
> >
> So the normal approach would be to have a key in the kernel keyring
> and decrypt it with the key loaded with evm_set_key?

Sorry, I should have said the encrypted key is encrypted/decrypted using
the trusted or user type key.

> Can I somehow use the keyring framework as an abstraction around my
> blobbing/deblobbing functionality?
> So that the "keyring" calls into the crypto driver to decrypt the data
> and uses the crypto driver to encrypt the keys when I want to "dump"
> them?

Definitely. It sounds like you want the equivalent functionality as
the TPM based trusted keys using OTP on the CAAM.

>From Documentation/security/keys-trusted-encrypted.txt:

"Trusted Keys use a TPM both to generate and to seal the keys. Keys are
sealed
under a 2048 bit RSA key in the TPM, and optionally sealed to specified
PCR
(integrity measurement) values, and only unsealed by the TPM, if PCRs
and blob
integrity verifications match."

Mimi

2016-01-28 16:28:02

by Jan Lübbe

[permalink] [raw]
Subject: Re: [Linux-ima-user] [RFC] i.MX6 CAAM blob generator for IMA/EVM initialization

Hi!

On Do, 2016-01-28 at 10:41 -0500, Mimi Zohar wrote:
> On Wed, 2016-01-27 at 11:04 +0100, Steffen Trumtrar wrote:
> > Can I somehow use the keyring framework as an abstraction around my
> > blobbing/deblobbing functionality?
> > So that the "keyring" calls into the crypto driver to decrypt the data
> > and uses the crypto driver to encrypt the keys when I want to "dump"
> > them?
>
> Definitely. It sounds like you want the equivalent functionality as
> the TPM based trusted keys using OTP on the CAAM.
>
> From Documentation/security/keys-trusted-encrypted.txt:
>
> "Trusted Keys use a TPM both to generate and to seal the keys. Keys are
> sealed under a 2048 bit RSA key in the TPM, and optionally sealed to specified
> PCR (integrity measurement) values, and only unsealed by the TPM, if PCRs
> and blob integrity verifications match."

OK. The implementation is in security/keys/trusted.c, right? This talks
directly to the TPM. Also the code in security/keys/encrypted-keys/
explicitly uses the interface in keys/trusted-type.h.

Now I'm not sure how to best allow using the CAAM as an alternative to a
TPM. Would we have different "backends" in the trusted keys
implementation so that for example encrypted keys don't need to know
about the difference?

Otherwise we would need to add another key type ("trusted-caam?"). Then
we'd need to add code to support trusted-caam keys wherever we already
support trusted(-tpm) keys?

Do you have a suggestion on how we should proceed?

Best regards,
Jan Lübbe
--
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 |