summaryrefslogtreecommitdiffstats
path: root/drivers/crypto/imx-scc/scc-blobgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/crypto/imx-scc/scc-blobgen.c')
-rw-r--r--drivers/crypto/imx-scc/scc-blobgen.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/drivers/crypto/imx-scc/scc-blobgen.c b/drivers/crypto/imx-scc/scc-blobgen.c
new file mode 100644
index 0000000000..e1a1372420
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc-blobgen.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@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 <common.h>
+#include <dma.h>
+#include <digest.h>
+#include <driver.h>
+#include <init.h>
+#include <blobgen.h>
+#include <stdlib.h>
+#include <crypto.h>
+#include <crypto/sha.h>
+
+#include "scc.h"
+
+#define MAX_IVLEN BLOCKSIZE_BYTES
+
+static struct digest *sha256;
+
+static int sha256sum(uint8_t *src, uint8_t *dst, unsigned int size)
+{
+ if (!sha256)
+ sha256 = digest_alloc("sha256");
+
+ if (!sha256) {
+ pr_err("Unable to allocate sha256 digest\n");
+ return -EINVAL;
+ }
+
+ return digest_digest(sha256, src, size, dst);
+}
+
+static int imx_scc_blob_encrypt(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize)
+{
+ char *s;
+ int bufsiz;
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+
+ bufsiz = ALIGN(plainsize + KEYMOD_LENGTH, 8);
+
+ s = malloc(bufsiz + SHA256_DIGEST_SIZE);
+ if (!s)
+ return -ENOMEM;
+
+ memset(s, 0, bufsiz);
+
+ strncpy(s, modifier, KEYMOD_LENGTH);
+ memcpy(s + KEYMOD_LENGTH, plain, plainsize);
+
+ ret = sha256sum(s, hash, bufsiz);
+ if (ret)
+ goto out;
+
+ memcpy(s + bufsiz, hash, SHA256_DIGEST_SIZE);
+
+ bufsiz += SHA256_DIGEST_SIZE;
+
+ req.info = iv;
+ req.src = s;
+ req.dst = blob;
+ req.nbytes = bufsiz;
+
+ get_random_bytes(req.info, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_encrypt(&req);
+ if (ret)
+ goto out;
+
+ memcpy(blob + bufsiz, req.info, MAX_IVLEN);
+ *blobsize = bufsiz + MAX_IVLEN;
+
+out:
+ free(s);
+
+ return ret;
+}
+
+static int imx_scc_blob_decrypt(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize)
+{
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+ uint8_t *data;
+ int ciphersize = blobsize - MAX_IVLEN;
+
+ if (blobsize <= MAX_IVLEN + SHA256_DIGEST_SIZE + KEYMOD_LENGTH)
+ return -EINVAL;
+
+ data = malloc(ciphersize);
+ if (!data)
+ return -ENOMEM;
+
+ req.info = iv;
+ req.nbytes = ciphersize;
+ req.src = (void *)blob;
+ req.dst = data;
+
+ memcpy(req.info, blob + req.nbytes, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_decrypt(&req);
+ if (ret)
+ goto out;
+
+ ret = sha256sum(data, hash, ciphersize - SHA256_DIGEST_SIZE);
+ if (ret)
+ goto out;
+
+ if (memcmp(data + ciphersize - SHA256_DIGEST_SIZE, hash,
+ SHA256_DIGEST_SIZE)) {
+ pr_err("%s: Corrupted SHA256 digest. Can't continue.\n",
+ bg->dev.name);
+ pr_err("%s: Calculated hash:\n", bg->dev.name);
+ memory_display(hash, 0, SHA256_DIGEST_SIZE, 1, 0);
+ pr_err("%s: Received hash:\n", bg->dev.name);
+ memory_display(data + ciphersize - SHA256_DIGEST_SIZE,
+ 0, SHA256_DIGEST_SIZE, 1, 0);
+
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ *plainsize = ciphersize - SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ *plain = xmemdup(data + KEYMOD_LENGTH, *plainsize);
+out:
+ free(data);
+
+ return ret;
+}
+
+int imx_scc_blob_gen_probe(struct device_d *dev)
+{
+ struct blobgen *bg;
+ int ret;
+
+ bg = xzalloc(sizeof(*bg));
+
+ bg->max_payload_size = MAX_BLOB_LEN - MAX_IVLEN -
+ SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ bg->encrypt = imx_scc_blob_encrypt;
+ bg->decrypt = imx_scc_blob_decrypt;
+
+ ret = blob_gen_register(dev, bg);
+ if (ret)
+ free(bg);
+
+ return ret;
+}