396 lines
9.8 KiB
C
396 lines
9.8 KiB
C
|
/*
|
||
|
* The driver of SUNXI SecuritySystem controller.
|
||
|
*
|
||
|
* Copyright (C) 2014 Allwinner.
|
||
|
*
|
||
|
* Mintow <duanmintao@allwinnertech.com>
|
||
|
*
|
||
|
* This file is licensed under the terms of the GNU General Public
|
||
|
* License version 2. This program is licensed "as is" without any
|
||
|
* warranty of any kind, whether express or implied.
|
||
|
*/
|
||
|
|
||
|
#include <linux/vmalloc.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <crypto/internal/hash.h>
|
||
|
#include <crypto/internal/rng.h>
|
||
|
#include <crypto/hash.h>
|
||
|
|
||
|
#include "sunxi_ss.h"
|
||
|
#include "sunxi_ss_proc.h"
|
||
|
#include "sunxi_ss_reg.h"
|
||
|
|
||
|
void ss_print_hex(char *_data, int _len, void *_addr)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
pr_debug("---------------- The valid len = %d ----------------\n",
|
||
|
_len);
|
||
|
for (i = 0; i < _len/8; i++) {
|
||
|
pr_debug("0x%p: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
||
|
i*8 + _addr,
|
||
|
_data[i*8+0], _data[i*8+1], _data[i*8+2], _data[i*8+3],
|
||
|
_data[i*8+4], _data[i*8+5], _data[i*8+6], _data[i*8+7]);
|
||
|
}
|
||
|
pr_debug("----------------------------------------------------\n");
|
||
|
}
|
||
|
#ifdef SS_SCATTER_ENABLE
|
||
|
void ss_print_task_info(ce_task_desc_t *task)
|
||
|
{
|
||
|
void *src_ptr = phys_to_virt(task->src[0].addr);
|
||
|
unsigned int src_len = task->src[0].len << 2;
|
||
|
void *dst_ptr = phys_to_virt(task->dst[0].addr);
|
||
|
unsigned int dst_len = task->dst[0].len << 2;
|
||
|
|
||
|
pr_debug("---------------------task_info--------------------\n");
|
||
|
ss_print_hex((char *)task, sizeof(*task), task);
|
||
|
dma_sync_single_for_cpu(&ss_dev->pdev->dev, task->src[0].addr,
|
||
|
src_len, DMA_FROM_DEVICE);
|
||
|
pr_debug("--------------src_len = 0x%x-------------\n", src_len);
|
||
|
ss_print_hex(src_ptr, src_len, src_ptr);
|
||
|
dma_sync_single_for_cpu(&ss_dev->pdev->dev, task->dst[0].addr,
|
||
|
dst_len, DMA_FROM_DEVICE);
|
||
|
pr_debug("--------------dst_len = 0x%x-------------\n", dst_len);
|
||
|
ss_print_hex(dst_ptr, dst_len, dst_ptr);
|
||
|
}
|
||
|
#endif
|
||
|
int ss_sg_cnt(struct scatterlist *sg, int total)
|
||
|
{
|
||
|
int cnt = 0;
|
||
|
int nbyte = 0;
|
||
|
struct scatterlist *cur = sg;
|
||
|
struct scatterlist *prev = sg;
|
||
|
|
||
|
while (cur != NULL) {
|
||
|
cnt++;
|
||
|
prev = cur;
|
||
|
SS_DBG("cnt: %d, cur: %p, len: %d, is_last: %ld\n", cnt, cur,
|
||
|
cur->length, sg_is_last(cur));
|
||
|
nbyte += cur->length;
|
||
|
if (nbyte >= total)
|
||
|
break;
|
||
|
|
||
|
cur = sg_next(cur);
|
||
|
}
|
||
|
|
||
|
if (!sg_is_last(prev))
|
||
|
sg_mark_end(prev);
|
||
|
return cnt;
|
||
|
}
|
||
|
|
||
|
int ss_aes_crypt(struct ablkcipher_request *req, int dir, int method, int mode)
|
||
|
{
|
||
|
ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req);
|
||
|
|
||
|
SS_DBG("nbytes: %d, dec: %d, method: %d, mode: %d\n",
|
||
|
req->nbytes, dir, method, mode);
|
||
|
if (ss_dev->suspend) {
|
||
|
SS_ERR("SS has already suspend.\n");
|
||
|
return -EAGAIN;
|
||
|
}
|
||
|
|
||
|
req_ctx->dir = dir;
|
||
|
req_ctx->type = method;
|
||
|
req_ctx->mode = mode;
|
||
|
req->base.flags |= SS_FLAG_AES;
|
||
|
|
||
|
return ss_aes_one_req(ss_dev, req);
|
||
|
}
|
||
|
|
||
|
int ss_prng_get_random(struct crypto_rng *tfm, const u8 *src, unsigned int slen,
|
||
|
u8 *dst, unsigned int dlen)
|
||
|
{
|
||
|
return ss_rng_get_random(tfm, dst, dlen, 0);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_TRNG_ENABLE
|
||
|
int ss_trng_get_random(struct crypto_rng *tfm, const u8 *src, unsigned int slen,
|
||
|
u8 *dst, unsigned int dlen)
|
||
|
{
|
||
|
return ss_rng_get_random(tfm, dst, dlen, 1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(SS_SHA_SWAP_PRE_ENABLE) || defined(SS_SHA_SWAP_FINAL_ENABLE)
|
||
|
void ss_hash_swap(char *data, int len)
|
||
|
{
|
||
|
int i;
|
||
|
int temp = 0;
|
||
|
int *cur = (int *)data;
|
||
|
|
||
|
SS_DBG("Convert the byter-order of digest. len %d\n", len);
|
||
|
for (i = 0; i < len/4; i++, cur++) {
|
||
|
temp = cpu_to_be32(*cur);
|
||
|
*cur = temp;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int ss_hash_blk_size(int type)
|
||
|
{
|
||
|
#if defined(SS_SHA384_ENABLE) || defined(SS_SHA512_ENABLE)
|
||
|
if ((type == SS_METHOD_SHA384) || (type == SS_METHOD_SHA512))
|
||
|
return SHA512_BLOCK_SIZE;
|
||
|
#endif
|
||
|
return SHA1_BLOCK_SIZE;
|
||
|
}
|
||
|
|
||
|
/* The tail data will be processed later. */
|
||
|
void ss_hash_padding_sg_prepare(struct scatterlist *last, int total)
|
||
|
{
|
||
|
if (total%SHA1_BLOCK_SIZE != 0) {
|
||
|
SS_DBG("sg len: %d, total: %d\n", sg_dma_len(last), total);
|
||
|
WARN(sg_dma_len(last) < total%SHA1_BLOCK_SIZE,
|
||
|
"sg len: %d, total: %d\n", sg_dma_len(last), total);
|
||
|
sg_dma_len(last) = sg_dma_len(last) - total%SHA1_BLOCK_SIZE;
|
||
|
}
|
||
|
WARN_ON(sg_dma_len(last) > total);
|
||
|
}
|
||
|
|
||
|
int ss_hash_hw_padding(ss_hash_ctx_t *ctx, int type)
|
||
|
{
|
||
|
SS_DBG("type: %d, n: %d, cnt: %d\n", type, ctx->tail_len, ctx->cnt);
|
||
|
return SS_HASH_PAD_SIZE;
|
||
|
}
|
||
|
|
||
|
int ss_hash_sw_padding(ss_hash_ctx_t *ctx, int type)
|
||
|
{
|
||
|
int blk_size = ss_hash_blk_size(type);
|
||
|
int len_threshold = blk_size == SHA512_BLOCK_SIZE ? 112 : 56;
|
||
|
int n = ctx->tail_len;
|
||
|
u8 *p = ctx->pad;
|
||
|
int len_l = ctx->cnt << 3; /* total len, in bits. */
|
||
|
int len_h = ctx->cnt >> 29;
|
||
|
int big_endian = type == SS_METHOD_MD5 ? 0 : 1;
|
||
|
|
||
|
SS_DBG("type = %d, n = %d, ctx->cnt = %d\n", type, n, ctx->cnt);
|
||
|
p[n] = 0x80;
|
||
|
n++;
|
||
|
|
||
|
if (n > len_threshold) { /* The pad data need two blocks. */
|
||
|
memset(p+n, 0, blk_size*2 - n);
|
||
|
p += blk_size*2 - 8;
|
||
|
} else {
|
||
|
memset(p+n, 0, blk_size - n);
|
||
|
p += blk_size - 8;
|
||
|
}
|
||
|
|
||
|
if (big_endian == 1) {
|
||
|
#if 0
|
||
|
/* The length should use bit64 in SHA384/512 case.
|
||
|
* The OpenSSL package is always small than 8K,
|
||
|
* so we use still bit32.
|
||
|
*/
|
||
|
if (blk_size == SHA512_BLOCK_SIZE) {
|
||
|
int len_hh = ctx->cnt >> 61;
|
||
|
*(int *)(p-4) = swab32(len_hh);
|
||
|
}
|
||
|
#endif
|
||
|
*(int *)p = swab32(len_h);
|
||
|
*(int *)(p+4) = swab32(len_l);
|
||
|
} else {
|
||
|
*(int *)p = len_l;
|
||
|
*(int *)(p+4) = len_h;
|
||
|
}
|
||
|
|
||
|
SS_DBG("After padding %d: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||
|
(s32)(p + 8 - ctx->pad),
|
||
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
||
|
|
||
|
return p + 8 - ctx->pad;
|
||
|
}
|
||
|
|
||
|
int ss_hash_padding(ss_hash_ctx_t *ctx, int type)
|
||
|
{
|
||
|
#ifdef SS_HASH_HW_PADDING
|
||
|
#ifdef SS_HASH_HW_PADDING_ALIGN_CASE
|
||
|
if (ctx->tail_len == 0)
|
||
|
return ss_hash_sw_padding(ctx, type);
|
||
|
#endif
|
||
|
return ss_hash_hw_padding(ctx, type);
|
||
|
#else
|
||
|
return ss_hash_sw_padding(ctx, type);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int ss_hash_one_req(sunxi_ss_t *sss, struct ahash_request *req)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
ss_aes_req_ctx_t *req_ctx = NULL;
|
||
|
ss_hash_ctx_t *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
||
|
|
||
|
SS_ENTER();
|
||
|
if (!req->src) {
|
||
|
SS_ERR("Invalid sg: src = %p\n", req->src);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ss_dev_lock();
|
||
|
|
||
|
req_ctx = ahash_request_ctx(req);
|
||
|
req_ctx->dma_src.sg = req->src;
|
||
|
|
||
|
ret = ss_hash_start(ctx, req_ctx, req->nbytes, 0);
|
||
|
if (ret < 0)
|
||
|
SS_ERR("ss_hash_start fail(%d)\n", ret);
|
||
|
|
||
|
ss_dev_unlock();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Backup the tail data to req_ctx->pad[]. */
|
||
|
void ss_hash_save_tail(struct ahash_request *req)
|
||
|
{
|
||
|
s8 *buf = NULL;
|
||
|
s32 sg_cnt = 0;
|
||
|
s32 taillen = 0;
|
||
|
ss_aes_req_ctx_t *req_ctx = ahash_request_ctx(req);
|
||
|
ss_hash_ctx_t *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
||
|
|
||
|
taillen = req->nbytes % ss_hash_blk_size(req_ctx->type);
|
||
|
SS_DBG("type: %d, mode: %d, len: %d, tail: %d\n", req_ctx->type,
|
||
|
req_ctx->mode, req->nbytes, taillen);
|
||
|
ctx->tail_len = taillen;
|
||
|
if (taillen == 0) /* The package don't need to backup. */
|
||
|
return;
|
||
|
|
||
|
buf = vmalloc(req->nbytes);
|
||
|
if (unlikely(buf == NULL)) {
|
||
|
SS_ERR("Fail to vmalloc(%d)\n", req->nbytes);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sg_cnt = ss_sg_cnt(req->src, req->nbytes);
|
||
|
sg_copy_to_buffer(req->src, sg_cnt, buf, req->nbytes);
|
||
|
|
||
|
memcpy(ctx->pad, buf + req->nbytes - taillen, taillen);
|
||
|
vfree(buf);
|
||
|
}
|
||
|
|
||
|
int ss_hash_update(struct ahash_request *req)
|
||
|
{
|
||
|
if (!req->nbytes) {
|
||
|
SS_ERR("Invalid length: %d.\n", req->nbytes);
|
||
|
return 0;
|
||
|
}
|
||
|
ss_hash_save_tail(req);
|
||
|
|
||
|
SS_DBG("Flags: %#x, len = %d\n", req->base.flags, req->nbytes);
|
||
|
if (ss_dev->suspend) {
|
||
|
SS_ERR("SS has already suspend.\n");
|
||
|
return -EAGAIN;
|
||
|
}
|
||
|
|
||
|
req->base.flags |= SS_FLAG_HASH;
|
||
|
return ss_hash_one_req(ss_dev, req);
|
||
|
}
|
||
|
|
||
|
int ss_hash_final(struct ahash_request *req)
|
||
|
{
|
||
|
int pad_len = 0;
|
||
|
ss_aes_req_ctx_t *req_ctx = ahash_request_ctx(req);
|
||
|
ss_hash_ctx_t *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
|
||
|
struct scatterlist last = {0}; /* make a sg struct for padding data. */
|
||
|
|
||
|
if (req->result == NULL) {
|
||
|
SS_ERR("Invalid result porinter.\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
SS_DBG("Method: %d, cnt: %d\n", req_ctx->type, ctx->cnt);
|
||
|
if (ss_dev->suspend) {
|
||
|
SS_ERR("SS has already suspend.\n");
|
||
|
return -EAGAIN;
|
||
|
}
|
||
|
|
||
|
/* Process the padding data. */
|
||
|
pad_len = ss_hash_padding(ctx, req_ctx->type);
|
||
|
SS_DBG("Pad len: %d\n", pad_len);
|
||
|
req_ctx->dma_src.sg = &last;
|
||
|
sg_init_table(&last, 1);
|
||
|
sg_set_buf(&last, ctx->pad, pad_len);
|
||
|
SS_DBG("Padding data:\n");
|
||
|
ss_print_hex((s8 *)ctx->pad, 128, ctx->pad);
|
||
|
|
||
|
ss_dev_lock();
|
||
|
#ifdef SS_HASH_HW_PADDING_ALIGN_CASE
|
||
|
if (ctx->tail_len == 0)
|
||
|
ss_hash_start(ctx, req_ctx, pad_len, 0);
|
||
|
else
|
||
|
#endif
|
||
|
ss_hash_start(ctx, req_ctx, pad_len, 1);
|
||
|
|
||
|
ss_sha_final();
|
||
|
|
||
|
SS_DBG("Method: %d, cnt: %d\n", req_ctx->type, ctx->cnt);
|
||
|
|
||
|
ss_check_sha_end();
|
||
|
ss_md_get(req->result, ctx->md, ctx->md_size);
|
||
|
ss_ctrl_stop();
|
||
|
ss_dev_unlock();
|
||
|
|
||
|
#ifdef SS_SHA_SWAP_FINAL_ENABLE
|
||
|
if (req_ctx->type != SS_METHOD_MD5)
|
||
|
ss_hash_swap(req->result, ctx->md_size);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int ss_hash_finup(struct ahash_request *req)
|
||
|
{
|
||
|
ss_hash_update(req);
|
||
|
return ss_hash_final(req);
|
||
|
}
|
||
|
|
||
|
int ss_hash_digest(struct ahash_request *req)
|
||
|
{
|
||
|
crypto_ahash_reqtfm(req)->init(req);
|
||
|
ss_hash_update(req);
|
||
|
return ss_hash_final(req);
|
||
|
}
|
||
|
|
||
|
#ifdef SS_TRNG_POSTPROCESS_ENABLE
|
||
|
void ss_trng_postprocess(u8 *out, u32 outlen, u8 *in, u32 inlen)
|
||
|
{
|
||
|
s32 i;
|
||
|
int ret;
|
||
|
u32 left = inlen;
|
||
|
s8 result[SHA256_DIGEST_SIZE] = "";
|
||
|
struct scatterlist sg = {0};
|
||
|
struct crypto_ahash *tfm = NULL;
|
||
|
struct ahash_request *req = NULL;
|
||
|
|
||
|
tfm = crypto_alloc_ahash("sha256", CRYPTO_ALG_TYPE_AHASH, CRYPTO_ALG_TYPE_AHASH_MASK);
|
||
|
if (IS_ERR(tfm)) {
|
||
|
SS_ERR("Fail to alloc ahash tfm!\n");
|
||
|
}
|
||
|
|
||
|
req = ahash_request_alloc(tfm, GFP_KERNEL);
|
||
|
if (!req) {
|
||
|
SS_ERR("Fail to alloc ahash request!\n");
|
||
|
}
|
||
|
|
||
|
ahash_request_set_callback(req, 0, NULL, NULL);
|
||
|
|
||
|
for (i = 0; i < inlen/SHA256_BLOCK_SIZE; i++) {
|
||
|
sg_init_one(&sg, in+i*SHA256_BLOCK_SIZE, SHA256_BLOCK_SIZE);
|
||
|
ahash_request_set_crypt(req, &sg, result, SHA256_BLOCK_SIZE);
|
||
|
ret = crypto_ahash_digest(req);
|
||
|
if (ret) {
|
||
|
SS_ERR("Fail to do %d SHA256(), inlen: %d\n", i, inlen);
|
||
|
break;
|
||
|
}
|
||
|
if (left < SHA256_DIGEST_SIZE) {
|
||
|
memcpy(&out[i*SHA256_DIGEST_SIZE], req->result, inlen);
|
||
|
break;
|
||
|
}
|
||
|
memcpy(&out[i*SHA256_DIGEST_SIZE], req->result, SHA256_DIGEST_SIZE);
|
||
|
left -= SHA256_DIGEST_SIZE;
|
||
|
}
|
||
|
|
||
|
ahash_request_free(req);
|
||
|
crypto_free_ahash(tfm);
|
||
|
}
|
||
|
#endif
|