SmartAudio/lichee/linux-4.9/drivers/crypto/sunxi-ss/sunxi_ss_proc_comm.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