diff options
Diffstat (limited to 'drivers/crypto/imx-scc/scc-blobgen.c')
-rw-r--r-- | drivers/crypto/imx-scc/scc-blobgen.c | 159 |
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; +} |