[PATCH 6/6] crypto: caam: add blob_gen driver

Steffen Trumtrar s.trumtrar at pengutronix.de
Wed Oct 14 06:39:39 PDT 2015


The blob_gen driver allows generating and reading of red blobs on the
i.MX6 CAAM crypto core.

Signed-off-by: Steffen Trumtrar <s.trumtrar at pengutronix.de>
Signed-off-by: Marc Kleine-Budde <mkl at pengutronix.de>
---
 drivers/crypto/caam/Kconfig    |   9 +
 drivers/crypto/caam/Makefile   |   1 +
 drivers/crypto/caam/blob_gen.c | 485 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 495 insertions(+)
 create mode 100644 drivers/crypto/caam/blob_gen.c

diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index d54754c7a89b..4466785358e7 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -35,3 +35,12 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
 		7 => 128
 		8 => 256
 		9 => 512
+
+config CRYPTO_DEV_FSL_CAAM_BLOB_GEN
+	bool "CAAM - Blob Generator (EXPERIMENTAL)"
+	depends on CRYPTO_DEV_FSL_CAAM
+	select BASE64
+	default n
+	help
+	  Selecting this will register the device /dev/blob which can en/decapsulate
+	  red blobs with the help of the CAAM.
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index cd46936fb6b4..6c126cf11dac 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -3,3 +3,4 @@
 #
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += ctrl.o error.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += jr.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.o
diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c
new file mode 100644
index 000000000000..7e5f990bc8e5
--- /dev/null
+++ b/drivers/crypto/caam/blob_gen.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel at pengutronix.de>
+ *
+ * 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 <asm/io.h>
+#include <base64.h>
+#include <common.h>
+#include <dma.h>
+#include <driver.h>
+#include <init.h>
+#include <fs.h>
+#include <fcntl.h>
+#include "intern.h"
+#include "desc.h"
+#include "desc_constr.h"
+#include "error.h"
+#include "jr.h"
+
+#define DRIVERNAME	"blob"
+
+/*
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable
+ * blob.
+ */
+#define INITIAL_DESCSZ		16
+/* 32 bytes key blob + 16 bytes HMAC identifier */
+#define BLOB_OVERHEAD		(32 + 16)
+#define KEYMOD_LENGTH		16
+#define RED_BLOB_LENGTH		64
+#define MAX_BLOB_LEN		4096
+
+enum access_rights {
+	KERNEL,
+	KERNEL_EVM,
+	USERSPACE,
+};
+
+struct blob_priv;
+
+struct blob_job_result {
+        int err;
+};
+
+struct blob_param {
+	struct param_d *param;
+	char *value;
+};
+
+struct blob_dma_blob {
+	u8 data[MAX_BLOB_LEN];
+};
+
+struct blob_dma_modifier {
+	u8 data[KEYMOD_LENGTH];
+};
+
+struct blob_priv {
+	struct device_d *jrdev;
+	struct device_d dev;
+	struct blob_param modifier_param;
+	struct blob_param payload_param;
+	struct blob_param blob_param;
+	u32 __iomem *desc;
+	bool busy;
+	enum access_rights access;
+	unsigned int payload_size;
+	struct blob_dma_blob *red_blob;
+	struct blob_dma_blob *payload;
+	struct blob_dma_modifier *modifier;
+};
+
+static void jr_jobdesc_blob_decap(u32 __iomem *desc, u8 *keymod, u8 *blob,
+				  u8 *plain_txt, u16 input_size)
+{
+	dma_addr_t dma_keymod;
+	dma_addr_t dma_plain;
+	dma_addr_t dma_blob;
+	u16 in_sz;
+	u16 out_sz;
+
+	dma_keymod = (dma_addr_t)keymod;
+	dma_plain = (dma_addr_t)plain_txt;
+	dma_blob = (dma_addr_t)blob;
+
+	in_sz = input_size;
+	out_sz = input_size - BLOB_OVERHEAD;
+
+	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);
+	append_seq_in_ptr(desc, dma_blob, in_sz, 0);
+	append_seq_out_ptr(desc, dma_plain, out_sz, 0);
+	append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void jr_jobdesc_blob_encap(u32 __iomem *desc, u8 *keymod, u8 *plain_txt,
+				  u8 *blob, u16 input_size)
+{
+	dma_addr_t dma_keymod;
+	dma_addr_t dma_plain;
+	dma_addr_t dma_blob;
+	u16 in_sz;
+	u16 out_sz;
+
+	dma_keymod = (dma_addr_t)keymod;
+	dma_plain = (dma_addr_t)plain_txt;
+	dma_blob = (dma_addr_t)blob;
+
+	in_sz = input_size;
+	out_sz = input_size + BLOB_OVERHEAD;
+
+	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);
+	append_seq_in_ptr(desc, dma_plain, in_sz, 0);
+	append_seq_out_ptr(desc, dma_blob, out_sz, 0);
+	append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void blob_job_done(struct device_d *dev, u32 *desc, u32 err, void *arg)
+{
+	struct blob_job_result *res = arg;
+
+	if (!res)
+		return;
+
+	if (err)
+		caam_jr_strstatus(dev, readl(0x2101044));
+
+	res->err = err;
+}
+
+/*
+ * 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 input_size)
+{
+	struct device_d *jrdev = priv->jrdev;
+	struct blob_job_result testres;
+	int ret;
+
+	memset(priv->desc, 0, 64);
+
+	jr_jobdesc_blob_encap(priv->desc, keymod, input, output, input_size);
+
+	pr_debug("data:\n");
+	pr_hex_dump_debug("data: ",
+			DUMP_PREFIX_OFFSET, 16, 1, input,
+			input_size, false);
+	pr_debug("jobdesc:\n");
+	pr_hex_dump_debug("jobdesc: ",
+			DUMP_PREFIX_OFFSET, 16, 1, priv->desc,
+			desc_bytes(priv->desc), false);
+	ret = caam_jr_enqueue(jrdev, priv->desc, blob_job_done, &testres);
+
+	pr_debug("output:\n");
+	pr_hex_dump_debug("",
+		       DUMP_PREFIX_OFFSET, 16, 1, priv->red_blob,
+		       input_size + BLOB_OVERHEAD, false);
+
+	return ret;
+}
+
+static int blob_decap_blob(struct blob_priv *priv, u8 *keymod, u8 *input,
+				u8 *output, u16 input_length)
+{
+	struct device_d *jrdev = priv->jrdev;
+	struct blob_job_result testres;
+	int ret;
+
+	memset(priv->desc, 0, 64);
+
+	jr_jobdesc_blob_decap(priv->desc, keymod, input, output, input_length);
+
+	pr_debug("data:\n");
+	pr_hex_dump_debug("data: ",
+			DUMP_PREFIX_OFFSET, 16, 1, input,
+			input_length, false);
+	pr_debug("jobdesc:\n");
+	pr_hex_dump_debug("jobdesc: ",
+			DUMP_PREFIX_OFFSET, 16, 1, priv->desc,
+			desc_bytes(priv->desc), false);
+
+	testres.err = 0;
+	ret = caam_jr_enqueue(jrdev, priv->desc, blob_job_done, &testres);
+	if (ret) {
+		dev_err(jrdev, "decryption error\n");
+	}
+	ret = testres.err;
+
+	pr_debug("%s: input_length %d\n", __func__, input_length);
+	pr_debug("output:\n");
+	pr_hex_dump_debug("",
+		       DUMP_PREFIX_OFFSET, 16, 1, output,
+		       input_length - BLOB_OVERHEAD, false);
+
+	return ret;
+}
+
+static int blob_gen_payload_get(struct param_d *p, void *arg)
+{
+	struct blob_priv *priv = arg;
+	struct blob_param *payload = &priv->payload_param;
+
+	if (priv->payload_size == 0)
+		return -EINVAL;
+
+	if (priv->access != USERSPACE)
+		return -EPERM;
+
+	free(payload->value);
+	payload->value = xstrndup(priv->payload->data, priv->payload_size);
+
+	return 0;
+}
+
+static int blob_gen_payload_set(struct param_d *p, void *arg)
+{
+	struct blob_priv *priv = arg;
+	struct blob_param *payload = &priv->payload_param;
+	size_t len;
+
+	if (priv->busy)
+		return -EBUSY;
+
+	len = strlen(payload->value);
+	if (len > sizeof(priv->payload->data))
+		return -EINVAL;
+
+	memcpy(priv->payload->data, payload->value, len);
+	priv->payload_size = len;
+
+	return 0;
+}
+
+static int blob_gen_blob_set(struct param_d *p, void *arg)
+{
+	struct blob_priv *priv = arg;
+	struct blob_param *blob = &priv->blob_param;
+	int length;
+	int ret;
+
+	if (priv->busy)
+		return -EBUSY;
+
+	length = strlen(blob->value);
+	if (length <= 0) {
+		dev_err(&priv->dev, "blob can't be 0\n");
+		return -EINVAL;
+	}
+
+	priv->busy = true;
+
+	pr_debug("red_blob:\n");
+	pr_hex_dump_debug("",
+		       DUMP_PREFIX_OFFSET, 16, 1, blob->value,
+		       length, false);
+
+	memset(priv->red_blob->data, 0x0, sizeof(priv->red_blob->data));
+	length = decode_base64(priv->red_blob->data, sizeof(priv->red_blob->data), blob->value);
+	if (length <= BLOB_OVERHEAD) {
+		dev_err(&priv->dev, "blob to short\n");
+		priv->busy = false;
+		return -EINVAL;
+	}
+
+	pr_debug("red_blob decoded:\n");
+	pr_hex_dump_debug("",
+		       DUMP_PREFIX_OFFSET, 16, 1, priv->red_blob,
+		       length, false);
+
+	memset(priv->payload->data, 0x0, sizeof(priv->payload->data));
+
+	ret = blob_decap_blob(priv, priv->modifier->data, priv->red_blob->data,
+			      priv->payload->data, length);
+	if (ret) {
+		priv->busy = false;
+		return -EINVAL;
+	}
+
+	priv->payload_size = length - BLOB_OVERHEAD;
+
+	priv->busy = false;
+	return 0;
+}
+
+static int blob_gen_blob_get(struct param_d *p, void *arg)
+{
+	struct blob_priv *priv = arg;
+	struct blob_param *blob = &priv->blob_param;
+	int length;
+	int ret;
+
+	if (priv->busy)
+		return -EBUSY;
+
+	if (priv->payload_size <= 0) {
+		dev_err(&priv->dev, "No payload given\n");
+		return -EINVAL;
+	}
+
+	priv->busy = true;
+
+	memset(priv->red_blob->data, 0x0, sizeof(priv->red_blob->data));
+	ret = blob_encap_blob(priv,
+			      priv->modifier->data,
+			      priv->payload->data,
+			      priv->red_blob->data,
+			      priv->payload_size);
+	if (ret) {
+		priv->busy = false;
+		return -EINVAL;
+	}
+
+	free(blob->value);
+	length = priv->payload_size + BLOB_OVERHEAD;
+	blob->value = xzalloc(BASE64_LENGTH(length) + 1);
+	uuencode(blob->value, priv->red_blob->data, length);
+
+	pr_hex_dump_debug("",
+		       DUMP_PREFIX_OFFSET, 16, 1, blob->value,
+		       strlen(blob->value), false);
+
+	priv->busy = false;
+
+	return 0;
+}
+
+static int blob_gen_modifier_set(struct param_d *p, void *arg)
+{
+	struct blob_priv *priv = arg;
+	struct blob_param *modifier = &priv->modifier_param;
+	ssize_t len;
+
+	if (priv->busy)
+		return -EBUSY;
+
+	if (!strncmp(modifier->value, "kernel:evm", 10))
+		priv->access = KERNEL_EVM;
+	else if (!strncmp(modifier->value, "kernel:", 7))
+		priv->access = KERNEL;
+	else if (!strncmp(modifier->value, "user:", 5))
+		priv->access = USERSPACE;
+
+	len = strlen(modifier->value);
+	if (len > sizeof(priv->modifier->data))
+		return -EINVAL;
+
+	memset(priv->modifier->data, 0, sizeof(priv->modifier->data));
+	memcpy(priv->modifier->data, modifier->value, len);
+
+	return 0;
+}
+
+static int blob_gen_probe(struct device_d *dev)
+{
+	struct device_node *dev_node;
+	struct device_d *jrdev;
+	struct blob_priv *priv;
+	int ret;
+
+	priv = xzalloc(sizeof(*priv));
+
+	priv->busy = false;
+
+	dev->priv = priv;
+
+	dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0-job-ring");
+	if (!dev_node) {
+		dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0-job-ring");
+		if (!dev_node)
+			return -ENODEV;
+	}
+
+	jrdev = of_find_device_by_node(dev_node);
+	if (!jrdev)
+		return -ENODEV;
+
+	priv->jrdev = jrdev;
+
+	priv->payload = dma_alloc_coherent(sizeof(*priv->payload),
+					   DMA_ADDRESS_BROKEN);
+	if (!priv->payload) {
+		dev_err(dev, "unable to allocate payload\n");
+		return -ENOMEM;
+	}
+	priv->modifier = dma_alloc_coherent(sizeof(*priv->modifier),
+					    DMA_ADDRESS_BROKEN);
+	if (!priv->modifier) {
+		dev_err(dev, "unable to allocate modifier\n");
+		ret = -ENOMEM;
+		goto out_payload;
+	}
+
+	priv->red_blob = dma_alloc_coherent(sizeof(*priv->red_blob),
+					    DMA_ADDRESS_BROKEN);
+	if (!priv->red_blob) {
+		dev_err(dev, "unable to allocate red_blob\n");
+		ret = -ENOMEM;
+		goto out_modifier;
+	}
+
+	priv->desc = dma_alloc_coherent(sizeof(*priv->desc) * 64, DMA_ADDRESS_BROKEN);
+	if (!priv->desc) {
+		dev_err(dev, "unable to allocate desc\n");
+		ret = -ENOMEM;
+		goto out_blob;
+	}
+
+	strcpy(priv->dev.name, "blob");
+	priv->dev.parent = dev;
+	register_device(&priv->dev);
+
+	priv->modifier_param.param = dev_add_param_string(&(priv->dev), "modifier",
+							  blob_gen_modifier_set,
+							  NULL,
+							  &priv->modifier_param.value,
+							  priv);
+	if (IS_ERR(priv->modifier_param.param)) {
+		ret = PTR_ERR(&priv->modifier_param.param);
+		goto out_desc;
+	}
+
+	priv->payload_param.param = dev_add_param_string(&(priv->dev), "payload",
+							 blob_gen_payload_set,
+							 blob_gen_payload_get,
+							 &priv->payload_param.value,
+							 priv);
+	if (IS_ERR(priv->payload_param.param)) {
+		ret = PTR_ERR(&priv->payload_param.param);
+		goto out_desc;
+	}
+
+	priv->blob_param.param = dev_add_param_string(&(priv->dev), "blob",
+						      blob_gen_blob_set,
+						      blob_gen_blob_get,
+						      &priv->blob_param.value,
+						      priv);
+	if (IS_ERR(priv->blob_param.param)) {
+		ret = PTR_ERR(&priv->blob_param.param);
+		goto out_desc;
+	}
+
+	return 0;
+
+out_desc:
+	dma_free_coherent(priv->desc, 0, sizeof(*priv->desc) * 64);
+out_blob:
+	dma_free_coherent(priv->red_blob, 0, sizeof(*priv->red_blob));
+out_modifier:
+	dma_free_coherent(priv->modifier, 0, sizeof(*priv->modifier));
+out_payload:
+	dma_free_coherent(priv->payload, 0, sizeof(*priv->payload));
+	return ret;
+}
+
+static struct of_device_id blob_gen_ids[] = {
+	{
+		.compatible = "caam_blob",
+	},
+	{},
+};
+
+static struct driver_d blob_gen_driver = {
+	.name = DRIVERNAME,
+	.of_compatible = DRV_OF_COMPAT(blob_gen_ids),
+	.probe = blob_gen_probe,
+};
+device_platform_driver(blob_gen_driver);
-- 
2.6.1




More information about the barebox mailing list