/* * Copyright (C) 2016 Pengutronix, Steffen Trumtrar * * 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 #include #include #include #include #include #include #include #include #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; }