1932 lines
47 KiB
C
1932 lines
47 KiB
C
/*
|
|
* drivers/misc/tegra-cryptodev.c
|
|
*
|
|
* crypto dev node for NVIDIA tegra aes hardware
|
|
*
|
|
* Copyright (c) 2010-2021, NVIDIA Corporation. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/nospec.h>
|
|
#include <soc/tegra/chip-id.h>
|
|
#include <linux/mutex.h>
|
|
#include <crypto/rng.h>
|
|
#include <crypto/hash.h>
|
|
#include <linux/platform/tegra/common.h>
|
|
#include <soc/tegra/fuse.h>
|
|
#include <crypto/akcipher.h>
|
|
#include <crypto/internal/skcipher.h>
|
|
|
|
#include <uapi/misc/tegra-cryptodev.h>
|
|
#include <asm/barrier.h>
|
|
|
|
#define NBUFS 2
|
|
#define XBUFSIZE 8
|
|
#define RNG_DRBG 1
|
|
#define RNG 0
|
|
#define NUM_RSA_ALGO 4
|
|
#define ECC_MODE_MIN_INDEX 7
|
|
#define ECC_MODE_MAX_INDEX 13
|
|
#define MAX_RSA_MSG_LEN 256
|
|
#define MAX_RSA1_MSG_LEN 512
|
|
|
|
enum tegra_se_pka1_ecc_type {
|
|
ECC_POINT_MUL,
|
|
ECC_POINT_ADD,
|
|
ECC_POINT_DOUBLE,
|
|
ECC_POINT_VER,
|
|
ECC_SHAMIR_TRICK,
|
|
ECC_INVALID,
|
|
};
|
|
|
|
struct tegra_crypto_ctx {
|
|
/*ecb, cbc, ofb, ctr */
|
|
struct crypto_skcipher *aes_tfm[TEGRA_CRYPTO_MAX];
|
|
/* rsa512, rsa1024, rsa1536, rsa2048 */
|
|
struct crypto_akcipher *rsa_tfm[4];
|
|
/* rsa512, rsa768, rsa1024, rsa1536, rsa2048, rsa3072, rsa4096 */
|
|
struct crypto_akcipher *pka1_rsa_tfm;
|
|
/* sha1, sha224, sha256, sha384, sha512, cmac */
|
|
struct crypto_ahash *sha_tfm[6];
|
|
struct crypto_shash *shash_tfm[6];
|
|
struct crypto_rng *rng;
|
|
struct crypto_rng *rng_drbg;
|
|
u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
|
|
int use_ssk;
|
|
bool skip_exit;
|
|
struct mutex lock;
|
|
};
|
|
|
|
struct tegra_crypto_completion {
|
|
struct completion restart;
|
|
int req_err;
|
|
};
|
|
|
|
static inline unsigned int crypto_shash_reqsize(struct crypto_shash *stfm)
|
|
{
|
|
return stfm->descsize;
|
|
}
|
|
|
|
static inline void shash_request_set_tfm(struct shash_desc *desc,
|
|
struct crypto_shash *stfm)
|
|
{
|
|
desc->tfm = (struct crypto_shash *)crypto_shash_tfm(stfm);
|
|
}
|
|
|
|
static inline struct shash_desc *shash_request_alloc(
|
|
struct crypto_shash *stfm, gfp_t gfp)
|
|
{
|
|
struct shash_desc *desc;
|
|
|
|
desc = kmalloc(sizeof(struct shash_desc) +
|
|
crypto_shash_reqsize(stfm), gfp);
|
|
|
|
if (likely(desc))
|
|
shash_request_set_tfm(desc, stfm);
|
|
|
|
return desc;
|
|
}
|
|
|
|
static inline void shash_request_free(struct shash_desc *desc)
|
|
{
|
|
kzfree(desc);
|
|
}
|
|
|
|
static int alloc_bufs(unsigned long *buf[NBUFS])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NBUFS; i++) {
|
|
buf[i] = (void *)__get_free_page(GFP_KERNEL);
|
|
if (!buf[i])
|
|
goto err_free_buf;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_free_buf:
|
|
while (i-- > 0)
|
|
free_page((unsigned long)buf[i]);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void free_bufs(unsigned long *buf[NBUFS])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NBUFS; i++)
|
|
free_page((unsigned long)buf[i]);
|
|
}
|
|
|
|
static int tegra_crypto_dev_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct tegra_crypto_ctx *ctx;
|
|
int ret = 0;
|
|
|
|
ctx = kzalloc(sizeof(struct tegra_crypto_ctx), GFP_KERNEL);
|
|
if (!ctx) {
|
|
pr_err("no memory for context\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* CBC tfm is allocated during device_open itself
|
|
* for (LP0) CTX_SAVE test that is performed using CBC
|
|
*/
|
|
ctx->aes_tfm[TEGRA_CRYPTO_CBC] =
|
|
crypto_alloc_skcipher("cbc-aes-tegra",
|
|
CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
|
|
if (IS_ERR(ctx->aes_tfm[TEGRA_CRYPTO_CBC])) {
|
|
pr_err("Failed to load transform for cbc-aes-tegra: %ld\n",
|
|
PTR_ERR(ctx->aes_tfm[TEGRA_CRYPTO_CBC]));
|
|
ret = PTR_ERR(ctx->aes_tfm[TEGRA_CRYPTO_CBC]);
|
|
kfree(ctx);
|
|
return ret;
|
|
}
|
|
mutex_init(&ctx->lock);
|
|
|
|
filp->private_data = ctx;
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypto_dev_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct tegra_crypto_ctx *ctx = filp->private_data;
|
|
int i = 0;
|
|
static int tfm_index;
|
|
int ret = 0;
|
|
|
|
/* store_tfm is needed to store the tfms in order to free them
|
|
* later when skip_exit becomes false
|
|
*/
|
|
static struct crypto_skcipher *store_tfm[
|
|
TEGRA_CRYPTO_AES_TEST_KEYSLOTS];
|
|
|
|
/* Only when skip_exit is false, the concerned tfm is freed,
|
|
* else it is just saved in store_tfm that is freed later
|
|
*/
|
|
if (tfm_index >= TEGRA_CRYPTO_AES_TEST_KEYSLOTS) {
|
|
pr_err("Invalid key slot index usage: %d\n", tfm_index);
|
|
ret = -ERANGE;
|
|
goto out;
|
|
}
|
|
|
|
if (ctx->aes_tfm[TEGRA_CRYPTO_CBC] && ctx->skip_exit)
|
|
store_tfm[tfm_index++] = ctx->aes_tfm[TEGRA_CRYPTO_CBC];
|
|
|
|
if (ctx->aes_tfm[TEGRA_CRYPTO_CBC] && !ctx->skip_exit) {
|
|
crypto_free_skcipher(ctx->aes_tfm[TEGRA_CRYPTO_CBC]);
|
|
|
|
for (i = tfm_index - 1; i >= 0; i--)
|
|
crypto_free_skcipher(store_tfm[i]);
|
|
tfm_index = 0;
|
|
}
|
|
out:
|
|
mutex_destroy(&ctx->lock);
|
|
kfree(ctx);
|
|
filp->private_data = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void tegra_crypt_complete(struct crypto_async_request *req, int err)
|
|
{
|
|
struct tegra_crypto_completion *done = req->data;
|
|
|
|
if (err != -EINPROGRESS) {
|
|
done->req_err = err;
|
|
complete(&done->restart);
|
|
}
|
|
}
|
|
|
|
static int process_crypt_req(struct file *filp, struct tegra_crypto_ctx *ctx,
|
|
struct tegra_crypt_req *crypt_req)
|
|
{
|
|
struct crypto_skcipher *tfm;
|
|
struct skcipher_request *req = NULL;
|
|
struct scatterlist in_sg;
|
|
struct scatterlist out_sg;
|
|
unsigned long *xbuf[NBUFS];
|
|
int ret = 0, size = 0;
|
|
unsigned long total = 0;
|
|
const u8 *key = NULL;
|
|
struct tegra_crypto_completion tcrypt_complete;
|
|
char aes_algo[5][10] = {"ecb(aes)", "cbc(aes)", "ofb(aes)", "ctr(aes)",
|
|
"xts(aes)"};
|
|
const char *algo;
|
|
|
|
if (crypt_req->op != TEGRA_CRYPTO_CBC) {
|
|
if (crypt_req->op >= TEGRA_CRYPTO_MAX)
|
|
return -EINVAL;
|
|
|
|
crypt_req->op = array_index_nospec(crypt_req->op,
|
|
TEGRA_CRYPTO_MAX);
|
|
|
|
tfm = crypto_alloc_skcipher(aes_algo[crypt_req->op],
|
|
CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to load transform for %s: %ld\n",
|
|
aes_algo[crypt_req->op], PTR_ERR(tfm));
|
|
ret = PTR_ERR(tfm);
|
|
goto out;
|
|
}
|
|
|
|
ctx->aes_tfm[crypt_req->op] = tfm;
|
|
filp->private_data = ctx;
|
|
} else {
|
|
tfm = ctx->aes_tfm[TEGRA_CRYPTO_CBC];
|
|
ctx->skip_exit = crypt_req->skip_exit;
|
|
filp->private_data = ctx;
|
|
}
|
|
|
|
req = skcipher_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("%s: Failed to allocate request\n", __func__);
|
|
ret = -ENOMEM;
|
|
goto free_tfm;
|
|
}
|
|
|
|
if (((crypt_req->keylen &
|
|
CRYPTO_KEY_LEN_MASK) != TEGRA_CRYPTO_KEY_128_SIZE) &&
|
|
((crypt_req->keylen &
|
|
CRYPTO_KEY_LEN_MASK) != TEGRA_CRYPTO_KEY_192_SIZE) &&
|
|
((crypt_req->keylen &
|
|
CRYPTO_KEY_LEN_MASK) != TEGRA_CRYPTO_KEY_256_SIZE) &&
|
|
((crypt_req->keylen &
|
|
CRYPTO_KEY_LEN_MASK) != TEGRA_CRYPTO_KEY_512_SIZE)) {
|
|
ret = -EINVAL;
|
|
pr_err("crypt_req keylen invalid");
|
|
goto process_req_out;
|
|
}
|
|
|
|
crypto_skcipher_clear_flags(tfm, ~0);
|
|
|
|
if (!ctx->use_ssk)
|
|
key = crypt_req->key;
|
|
|
|
if (!crypt_req->skip_key) {
|
|
algo = crypto_tfm_alg_driver_name(crypto_skcipher_tfm(tfm));
|
|
if (!algo) {
|
|
pr_err("Not a avilable algo");
|
|
ret = -EINVAL;
|
|
goto process_req_out;
|
|
}
|
|
|
|
/* Null key is only allowed in SE driver */
|
|
if (!strstr(algo, "tegra")) {
|
|
ret = -EINVAL;
|
|
goto process_req_out;
|
|
}
|
|
|
|
ret = crypto_skcipher_setkey(tfm, key, crypt_req->keylen);
|
|
if (ret < 0) {
|
|
pr_err("setkey failed");
|
|
goto process_req_out;
|
|
}
|
|
}
|
|
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto process_req_out;
|
|
}
|
|
|
|
init_completion(&tcrypt_complete.restart);
|
|
|
|
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
tegra_crypt_complete, &tcrypt_complete);
|
|
|
|
total = crypt_req->plaintext_sz;
|
|
while (total > 0) {
|
|
size = min(total, PAGE_SIZE);
|
|
ret = copy_from_user((void *)xbuf[0],
|
|
(void __user *)crypt_req->plaintext, size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto process_req_buf_out;
|
|
}
|
|
sg_init_one(&in_sg, xbuf[0], size);
|
|
sg_init_one(&out_sg, xbuf[1], size);
|
|
|
|
if (!crypt_req->skip_iv) {
|
|
skcipher_request_set_crypt(req, &in_sg,
|
|
&out_sg, size, crypt_req->iv);
|
|
/*
|
|
* Setting IV for the first block only. AES CBC
|
|
* should use updated IV generated from the last block.
|
|
* Which is already being maintained by SE.
|
|
*/
|
|
crypt_req->skip_iv = true;
|
|
} else {
|
|
skcipher_request_set_crypt(req, &in_sg,
|
|
&out_sg, size, NULL);
|
|
}
|
|
|
|
reinit_completion(&tcrypt_complete.restart);
|
|
|
|
tcrypt_complete.req_err = 0;
|
|
|
|
ret = crypt_req->encrypt ?
|
|
crypto_skcipher_encrypt(req) :
|
|
crypto_skcipher_decrypt(req);
|
|
if ((ret == -EINPROGRESS) || (ret == -EBUSY)) {
|
|
/* crypto driver is asynchronous */
|
|
ret = wait_for_completion_timeout(&tcrypt_complete.restart,
|
|
msecs_to_jiffies(5000));
|
|
if (ret == 0)
|
|
goto process_req_buf_out;
|
|
|
|
if (tcrypt_complete.req_err < 0) {
|
|
ret = tcrypt_complete.req_err;
|
|
goto process_req_buf_out;
|
|
}
|
|
} else if (ret < 0) {
|
|
pr_debug("%scrypt failed (%d)\n",
|
|
crypt_req->encrypt ? "en" : "de", ret);
|
|
goto process_req_buf_out;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)crypt_req->result,
|
|
(const void *)xbuf[1], size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_to_user failed (%d)\n", __func__,
|
|
ret);
|
|
goto process_req_buf_out;
|
|
}
|
|
|
|
total -= size;
|
|
crypt_req->result += size;
|
|
crypt_req->plaintext += size;
|
|
}
|
|
|
|
process_req_buf_out:
|
|
free_bufs(xbuf);
|
|
process_req_out:
|
|
skcipher_request_free(req);
|
|
free_tfm:
|
|
if (crypt_req->op != TEGRA_CRYPTO_CBC)
|
|
crypto_free_skcipher(tfm);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int wait_async_op(struct tegra_crypto_completion *tr, int ret)
|
|
{
|
|
if (ret == -EINPROGRESS || ret == -EBUSY) {
|
|
wait_for_completion(&tr->restart);
|
|
reinit_completion(&tr->restart);
|
|
ret = tr->req_err;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int sha_shash_hash_op(struct shash_desc *desc,
|
|
struct tegra_crypto_completion *tr,
|
|
int ret)
|
|
{
|
|
if (ret == -EINPROGRESS || ret == -EBUSY) {
|
|
ret = wait_for_completion_interruptible(&tr->restart);
|
|
if (!ret)
|
|
ret = tr->req_err;
|
|
reinit_completion(&tr->restart);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_cryptodev_rsa_set_key(struct crypto_akcipher *tfm,
|
|
char *key, unsigned int keylen,
|
|
unsigned int max_rsa_key_len,
|
|
enum tegra_rsa_op_mode op_mode)
|
|
{
|
|
unsigned int total_key_len;
|
|
char *key_mem;
|
|
int ret = 0;
|
|
|
|
if (!keylen)
|
|
return -EINVAL;
|
|
|
|
if ((((keylen >> 16) & 0xFFFF) > max_rsa_key_len) ||
|
|
((keylen & 0xFFFF) > max_rsa_key_len)) {
|
|
pr_err("Invalid rsa key length\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
total_key_len = (((keylen >> 16) & 0xFFFF) +
|
|
(keylen & 0xFFFF));
|
|
key_mem = kzalloc(total_key_len, GFP_KERNEL);
|
|
if (!key_mem)
|
|
return -ENOMEM;
|
|
|
|
ret = copy_from_user(key_mem, (void __user *)key,
|
|
total_key_len);
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
kfree(key_mem);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (op_mode == RSA_SET_PUB)
|
|
ret = crypto_akcipher_set_pub_key(tfm,
|
|
key_mem,
|
|
keylen);
|
|
else
|
|
ret = crypto_akcipher_set_priv_key(tfm,
|
|
key_mem,
|
|
keylen);
|
|
kfree(key_mem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypt_rsa(struct file *filp, struct tegra_crypto_ctx *ctx,
|
|
struct tegra_rsa_req *rsa_req)
|
|
{
|
|
struct crypto_akcipher *tfm = NULL;
|
|
struct akcipher_request *req;
|
|
struct scatterlist sg[2];
|
|
void *src_buff, *dst_buff;
|
|
int ret = 0;
|
|
unsigned long *xbuf[XBUFSIZE];
|
|
struct tegra_crypto_completion rsa_complete;
|
|
|
|
if (rsa_req->op_mode == RSA_INIT) {
|
|
tfm = crypto_alloc_akcipher("rsa-pka0",
|
|
CRYPTO_ALG_TYPE_AKCIPHER, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to load transform for rsa-pka0: %ld\n",
|
|
PTR_ERR(tfm));
|
|
return PTR_ERR(tfm);
|
|
}
|
|
|
|
ctx->rsa_tfm[rsa_req->algo] = tfm;
|
|
filp->private_data = ctx;
|
|
return 0;
|
|
} else {
|
|
ctx = filp->private_data;
|
|
tfm = ctx->rsa_tfm[rsa_req->algo];
|
|
}
|
|
|
|
if ((rsa_req->op_mode == RSA_SET_PUB) ||
|
|
(rsa_req->op_mode == RSA_SET_PRIV)) {
|
|
if (rsa_req->skip_key) {
|
|
pr_err("RSA skip key is set\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = tegra_cryptodev_rsa_set_key(tfm, rsa_req->key,
|
|
rsa_req->keylen,
|
|
MAX_RSA_MSG_LEN,
|
|
rsa_req->op_mode);
|
|
if (ret < 0) {
|
|
pr_err("alg: rsa: set key failed\n");
|
|
return ret;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_ENCRYPT ||
|
|
rsa_req->op_mode == RSA_DECRYPT || rsa_req->op_mode == RSA_SIGN
|
|
|| rsa_req->op_mode == RSA_VERIFY) {
|
|
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("alg: rsa: Failed to allocate request: %s\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto buf_fail;
|
|
}
|
|
|
|
init_completion(&rsa_complete.restart);
|
|
rsa_complete.req_err = 0;
|
|
|
|
src_buff = xbuf[0];
|
|
dst_buff = xbuf[1];
|
|
|
|
ret = copy_from_user(src_buff, (void __user *)rsa_req->message,
|
|
rsa_req->msg_len);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user failed\n", __func__);
|
|
goto rsa_fail;
|
|
}
|
|
|
|
memset(dst_buff, 0, rsa_req->msg_len);
|
|
|
|
sg_init_one(&sg[0], src_buff, rsa_req->msg_len);
|
|
sg_init_one(&sg[1], dst_buff, rsa_req->msg_len);
|
|
|
|
akcipher_request_set_crypt(req, &sg[0], &sg[1],
|
|
rsa_req->msg_len, rsa_req->msg_len);
|
|
|
|
if (rsa_req->op_mode == RSA_ENCRYPT) {
|
|
ret = crypto_akcipher_encrypt(req);
|
|
if (ret) {
|
|
pr_err("alg: rsa: encrypt failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_DECRYPT) {
|
|
ret = crypto_akcipher_decrypt(req);
|
|
if (ret) {
|
|
pr_err("alg: rsa: decrypt failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_SIGN) {
|
|
ret = crypto_akcipher_sign(req);
|
|
if (ret) {
|
|
pr_err("alg: rsa: sign failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_VERIFY) {
|
|
ret = crypto_akcipher_verify(req);
|
|
if (ret) {
|
|
pr_err("alg: rsa: verification failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)rsa_req->result,
|
|
(const void *)xbuf[1], rsa_req->msg_len);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("alg: rsa: copy_to_user failed (%d)\n", ret);
|
|
}
|
|
rsa_fail:
|
|
free_bufs(xbuf);
|
|
buf_fail:
|
|
akcipher_request_free(req);
|
|
} else if (rsa_req->op_mode == RSA_EXIT) {
|
|
crypto_free_akcipher(tfm);
|
|
} else {
|
|
pr_err("alg: rsa: invalid rsa operation\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypt_rsa_ahash(struct file *filp,
|
|
struct tegra_crypto_ctx *ctx,
|
|
struct tegra_rsa_req_ahash *rsa_req_ah)
|
|
{
|
|
struct crypto_ahash *tfm = NULL;
|
|
struct ahash_request *req = NULL;
|
|
struct scatterlist sg[1];
|
|
char *result = NULL;
|
|
void *hash_buff;
|
|
int ret = 0;
|
|
unsigned long *xbuf[XBUFSIZE];
|
|
struct tegra_crypto_completion rsa_complete;
|
|
char rsa_algo[4][10] = {"rsa512", "rsa1024", "rsa1536", "rsa2048"};
|
|
unsigned int total_key_len;
|
|
char *key_mem;
|
|
|
|
if ((((rsa_req_ah->keylen >> 16) & 0xFFFF) >
|
|
MAX_RSA_MSG_LEN) ||
|
|
((rsa_req_ah->keylen & 0xFFFF) >
|
|
MAX_RSA_MSG_LEN)) {
|
|
pr_err("Invalid rsa key length\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
total_key_len = (((rsa_req_ah->keylen >> 16) & 0xFFFF) +
|
|
(rsa_req_ah->keylen & 0xFFFF));
|
|
|
|
key_mem = kzalloc(total_key_len, GFP_KERNEL);
|
|
if (!key_mem)
|
|
return -ENOMEM;
|
|
|
|
ret = copy_from_user(key_mem, (void __user *)rsa_req_ah->key,
|
|
total_key_len);
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
kfree(key_mem);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rsa_req_ah->key = key_mem;
|
|
tfm = crypto_alloc_ahash(rsa_algo[rsa_req_ah->algo],
|
|
CRYPTO_ALG_TYPE_AHASH, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to load transform for %s: %ld\n",
|
|
rsa_algo[rsa_req_ah->algo], PTR_ERR(tfm));
|
|
ret = PTR_ERR(tfm);
|
|
goto out;
|
|
}
|
|
|
|
filp->private_data = ctx;
|
|
|
|
req = ahash_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("alg: hash: Failed to allocate request: %s\n", __func__);
|
|
ret = -ENOMEM;
|
|
goto req_fail;
|
|
}
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto buf_fail;
|
|
}
|
|
|
|
init_completion(&rsa_complete.restart);
|
|
rsa_complete.req_err = 0;
|
|
|
|
result = kzalloc(rsa_req_ah->keylen >> 16, GFP_KERNEL);
|
|
if (!result)
|
|
goto result_fail;
|
|
|
|
hash_buff = xbuf[0];
|
|
|
|
ret = copy_from_user(hash_buff, (void __user *)rsa_req_ah->message,
|
|
rsa_req_ah->msg_len);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user failed\n", __func__);
|
|
goto rsa_fail;
|
|
}
|
|
|
|
sg_init_one(&sg[0], hash_buff, rsa_req_ah->msg_len);
|
|
if (!(rsa_req_ah->keylen))
|
|
goto rsa_fail;
|
|
|
|
if (!rsa_req_ah->skip_key) {
|
|
ret = crypto_ahash_setkey(tfm,
|
|
rsa_req_ah->key, rsa_req_ah->keylen);
|
|
if (ret) {
|
|
pr_err("alg: hash: setkey failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
}
|
|
|
|
ahash_request_set_crypt(req, sg, result, rsa_req_ah->msg_len);
|
|
|
|
ret = crypto_ahash_digest(req);
|
|
|
|
if (ret == -EINPROGRESS || ret == -EBUSY) {
|
|
ret = wait_for_completion_interruptible(&rsa_complete.restart);
|
|
if (!ret)
|
|
ret = rsa_complete.req_err;
|
|
reinit_completion(&rsa_complete.restart);
|
|
}
|
|
|
|
if (ret) {
|
|
pr_err("alg: hash: digest failed\n");
|
|
goto rsa_fail;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)rsa_req_ah->result,
|
|
(const void *)result,
|
|
crypto_ahash_digestsize(tfm));
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("alg: hash: copy_to_user failed (%d)\n", ret);
|
|
}
|
|
|
|
rsa_fail:
|
|
kfree(result);
|
|
result_fail:
|
|
free_bufs(xbuf);
|
|
buf_fail:
|
|
ahash_request_free(req);
|
|
req_fail:
|
|
crypto_free_ahash(tfm);
|
|
out:
|
|
kfree(key_mem);
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypt_pka1_eddsa(struct tegra_pka1_eddsa_request *eddsa_req)
|
|
{
|
|
struct crypto_akcipher *tfm = NULL;
|
|
struct akcipher_request *req = NULL;
|
|
u8 *m_str = NULL;
|
|
struct scatterlist src, dst;
|
|
struct scatterlist src_tab[3];
|
|
struct tegra_crypto_completion result;
|
|
unsigned int outbuf_maxlen;
|
|
void *outbuf = NULL;
|
|
void *key = NULL;
|
|
u8 *key_buf = NULL;
|
|
unsigned int nbytes;
|
|
int err;
|
|
u8 *keymem = NULL;
|
|
u8 *output = NULL;
|
|
u8 *public_key = NULL;
|
|
|
|
tfm = crypto_alloc_akcipher("eddsa",
|
|
CRYPTO_ALG_TYPE_AKCIPHER, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to load transform for eddsa: %ld\n",
|
|
PTR_ERR(tfm));
|
|
return PTR_ERR(tfm);
|
|
}
|
|
|
|
/* Alloc akcipher request */
|
|
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("Failed to allocate akcipher request\n");
|
|
err = -ENOMEM;
|
|
goto free_tfm;
|
|
}
|
|
|
|
keymem = kzalloc(eddsa_req->keylen, GFP_KERNEL);
|
|
if (!keymem) {
|
|
err = -ENOMEM;
|
|
goto free_req;
|
|
}
|
|
|
|
err = copy_from_user(keymem, (void __user *)eddsa_req->key,
|
|
eddsa_req->keylen);
|
|
if (err) {
|
|
err = -EFAULT;
|
|
pr_err("copy_from_user failed (%d) for eddsa key\n", err);
|
|
goto free_mem;
|
|
}
|
|
|
|
/* Set private key */
|
|
err = crypto_akcipher_set_priv_key(tfm, keymem, eddsa_req->keylen);
|
|
if (err) {
|
|
pr_err("eddsa set priv key failed\n");
|
|
goto free_mem;
|
|
}
|
|
|
|
nbytes = eddsa_req->nbytes;
|
|
|
|
key = kzalloc(nbytes, GFP_KERNEL);
|
|
if (!key) {
|
|
err = -ENOMEM;
|
|
goto free_mem;
|
|
}
|
|
|
|
/* Set up result callback */
|
|
init_completion(&result.restart);
|
|
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
tegra_crypt_complete, &result);
|
|
|
|
/* Generate pub key */
|
|
err = wait_async_op(&result,
|
|
crypto_akcipher_set_pub_key(tfm, key, nbytes));
|
|
if (err) {
|
|
pr_err("alg:eddsa set_pub_key test failed\n");
|
|
goto free_key;
|
|
}
|
|
|
|
key_buf = (u8 *)key;
|
|
|
|
public_key = kzalloc(nbytes, GFP_KERNEL);
|
|
if (!public_key) {
|
|
err = -ENOMEM;
|
|
goto free_key;
|
|
}
|
|
|
|
err = copy_from_user(public_key, (void __user *)eddsa_req->public_key,
|
|
nbytes);
|
|
if (err) {
|
|
pr_err("copy_from_user failed (%d) for eddsa public key\n",
|
|
err);
|
|
err = -EFAULT;
|
|
goto free_pub_key;
|
|
}
|
|
|
|
if (memcmp(key, public_key, nbytes)) {
|
|
err = -EINVAL;
|
|
pr_err("alg:eddsa set_pub_key test failed. Invalid Output\n");
|
|
goto free_pub_key;
|
|
}
|
|
|
|
m_str = kzalloc(eddsa_req->msize, GFP_KERNEL);
|
|
if (!m_str) {
|
|
err = -ENOMEM;
|
|
goto free_pub_key;
|
|
}
|
|
|
|
err = copy_from_user(m_str, (void __user *)eddsa_req->message,
|
|
eddsa_req->msize);
|
|
if (err) {
|
|
pr_err("copy_from_user failed (%d) for eddsa message\n", err);
|
|
err = -EFAULT;
|
|
goto free_msg;
|
|
}
|
|
|
|
output = kzalloc(nbytes * 2, GFP_KERNEL);
|
|
if (!output) {
|
|
err = -ENOMEM;
|
|
goto free_msg;
|
|
}
|
|
|
|
sg_init_one(&src, m_str, eddsa_req->msize);
|
|
sg_init_one(&dst, output, nbytes * 2);
|
|
|
|
akcipher_request_set_crypt(req, &src, &dst,
|
|
eddsa_req->msize, nbytes * 2);
|
|
|
|
/* Set up result callback */
|
|
init_completion(&result.restart);
|
|
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
tegra_crypt_complete, &result);
|
|
|
|
/* Run eddsa sign operation on message digest */
|
|
err = wait_async_op(&result, crypto_akcipher_sign(req));
|
|
if (err) {
|
|
pr_err("alg:eddsa sign test failed\n");
|
|
goto free_output;
|
|
}
|
|
|
|
/* verify that signature (r,s) is valid */
|
|
if (req->dst_len != 2 * nbytes) {
|
|
err = -EINVAL;
|
|
goto free_output;
|
|
}
|
|
|
|
err = copy_to_user((void __user *)eddsa_req->signature,
|
|
output, 2 * nbytes);
|
|
if (err) {
|
|
err = -EFAULT;
|
|
pr_debug("%s: copy_to_user failed (%d)\n", __func__, err);
|
|
goto free_output;
|
|
}
|
|
|
|
outbuf_maxlen = crypto_akcipher_maxsize(tfm);
|
|
outbuf = kzalloc(outbuf_maxlen, GFP_KERNEL);
|
|
if (!outbuf) {
|
|
pr_err("Failed to allocate outbuf memory\n");
|
|
err = -ENOMEM;
|
|
goto free_output;
|
|
}
|
|
|
|
/* Set src and dst buffers */
|
|
sg_init_table(src_tab, 3);
|
|
sg_set_buf(&src_tab[0], m_str, eddsa_req->msize);
|
|
sg_set_buf(&src_tab[1], output, nbytes);
|
|
sg_set_buf(&src_tab[2], output + nbytes, nbytes);
|
|
sg_init_one(&dst, outbuf, outbuf_maxlen);
|
|
|
|
akcipher_request_set_crypt(req, src_tab, &dst,
|
|
eddsa_req->msize + 2 * nbytes,
|
|
outbuf_maxlen);
|
|
|
|
/* Set up result callback */
|
|
init_completion(&result.restart);
|
|
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
tegra_crypt_complete, &result);
|
|
|
|
/* Run eddsa verify operation on sig (r,s) */
|
|
err = wait_async_op(&result, crypto_akcipher_verify(req));
|
|
|
|
kfree(outbuf);
|
|
free_output:
|
|
kfree(output);
|
|
free_msg:
|
|
kfree(m_str);
|
|
free_pub_key:
|
|
kfree(public_key);
|
|
free_key:
|
|
kfree(key);
|
|
free_mem:
|
|
kfree(keymem);
|
|
free_req:
|
|
akcipher_request_free(req);
|
|
free_tfm:
|
|
crypto_free_akcipher(tfm);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tegra_crypt_pka1_ecc(struct tegra_se_pka1_ecc_request *ecc_req)
|
|
{
|
|
struct tegra_se_pka1_ecc_request temp_ecc_req;
|
|
int ret;
|
|
|
|
if ((ecc_req->op_mode < ECC_MODE_MIN_INDEX) ||
|
|
(ecc_req->op_mode > ECC_MODE_MAX_INDEX)) {
|
|
pr_err("Invalid value of ecc opmode index %d\n",
|
|
ecc_req->op_mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
temp_ecc_req.op_mode = ecc_req->op_mode;
|
|
temp_ecc_req.size = ecc_req->size;
|
|
temp_ecc_req.type = ecc_req->type;
|
|
|
|
temp_ecc_req.modulus = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.modulus) {
|
|
ret = -ENOMEM;
|
|
goto mod_fail;
|
|
}
|
|
temp_ecc_req.curve_param_a = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.curve_param_a) {
|
|
ret = -ENOMEM;
|
|
goto param_a_fail;
|
|
}
|
|
temp_ecc_req.curve_param_b = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.curve_param_b) {
|
|
ret = -ENOMEM;
|
|
goto param_b_fail;
|
|
}
|
|
temp_ecc_req.base_pt_x = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.base_pt_x) {
|
|
ret = -ENOMEM;
|
|
goto base_px_fail;
|
|
}
|
|
temp_ecc_req.base_pt_y = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.base_pt_y) {
|
|
ret = -ENOMEM;
|
|
goto base_py_fail;
|
|
}
|
|
temp_ecc_req.res_pt_x = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.res_pt_x) {
|
|
ret = -ENOMEM;
|
|
goto res_px_fail;
|
|
}
|
|
temp_ecc_req.res_pt_y = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.res_pt_y) {
|
|
ret = -ENOMEM;
|
|
goto res_py_fail;
|
|
}
|
|
temp_ecc_req.key = kzalloc(ecc_req->size, GFP_KERNEL);
|
|
if (!temp_ecc_req.key) {
|
|
ret = -ENOMEM;
|
|
goto key_fail;
|
|
}
|
|
|
|
ret = copy_from_user(temp_ecc_req.modulus,
|
|
(void __user *)ecc_req->modulus, ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
ret = copy_from_user(temp_ecc_req.curve_param_a,
|
|
(void __user *)ecc_req->curve_param_a,
|
|
ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
if ((ecc_req->type == ECC_POINT_VER) ||
|
|
(ecc_req->type == ECC_SHAMIR_TRICK)) {
|
|
ret = copy_from_user(temp_ecc_req.curve_param_b,
|
|
(void __user *)ecc_req->curve_param_b,
|
|
ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n",
|
|
__func__, ret);
|
|
goto free_all;
|
|
}
|
|
}
|
|
|
|
if (ecc_req->type != ECC_POINT_DOUBLE) {
|
|
ret = copy_from_user(temp_ecc_req.base_pt_x,
|
|
(void __user *)ecc_req->base_pt_x,
|
|
ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n",
|
|
__func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
ret = copy_from_user(temp_ecc_req.base_pt_y,
|
|
(void __user *)ecc_req->base_pt_y,
|
|
ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n",
|
|
__func__, ret);
|
|
goto free_all;
|
|
}
|
|
}
|
|
|
|
ret = copy_from_user(temp_ecc_req.res_pt_x,
|
|
(void __user *)ecc_req->res_pt_x, ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
ret = copy_from_user(temp_ecc_req.res_pt_y,
|
|
(void __user *)ecc_req->res_pt_y, ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
if ((ecc_req->type == ECC_POINT_MUL) ||
|
|
(ecc_req->type == ECC_SHAMIR_TRICK)) {
|
|
ret = copy_from_user(temp_ecc_req.key,
|
|
(void __user *)ecc_req->key,
|
|
ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n",
|
|
__func__, ret);
|
|
goto free_all;
|
|
}
|
|
}
|
|
|
|
ret = tegra_se_pka1_ecc_op(&temp_ecc_req);
|
|
if (ret) {
|
|
pr_debug("\ntegra_se_pka1_ecc_op failed(%d) for ECC\n", ret);
|
|
goto free_all;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)ecc_req->res_pt_x,
|
|
temp_ecc_req.res_pt_x, ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_to_user failed (%d)\n", __func__, ret);
|
|
goto free_all;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)ecc_req->res_pt_y,
|
|
temp_ecc_req.res_pt_y, ecc_req->size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_to_user failed (%d)\n", __func__, ret);
|
|
}
|
|
free_all:
|
|
kfree(temp_ecc_req.key);
|
|
key_fail:
|
|
kfree(temp_ecc_req.res_pt_y);
|
|
res_py_fail:
|
|
kfree(temp_ecc_req.res_pt_x);
|
|
res_px_fail:
|
|
kfree(temp_ecc_req.base_pt_y);
|
|
base_py_fail:
|
|
kfree(temp_ecc_req.base_pt_x);
|
|
base_px_fail:
|
|
kfree(temp_ecc_req.curve_param_b);
|
|
param_b_fail:
|
|
kfree(temp_ecc_req.curve_param_a);
|
|
param_a_fail:
|
|
kfree(temp_ecc_req.modulus);
|
|
mod_fail:
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypt_pka1_rsa(struct file *filp, struct tegra_crypto_ctx *ctx,
|
|
struct tegra_pka1_rsa_request *rsa_req)
|
|
{
|
|
struct crypto_akcipher *tfm = NULL;
|
|
struct akcipher_request *req;
|
|
struct scatterlist sg[2];
|
|
int ret = 0;
|
|
int len;
|
|
unsigned long *xbuf[XBUFSIZE];
|
|
struct tegra_crypto_completion rsa_complete;
|
|
|
|
if (rsa_req->op_mode == RSA_INIT) {
|
|
tfm = crypto_alloc_akcipher("rsa-pka1",
|
|
CRYPTO_ALG_TYPE_AKCIPHER, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to load transform for rsa: %ld\n",
|
|
PTR_ERR(tfm));
|
|
return PTR_ERR(tfm);
|
|
}
|
|
ctx->pka1_rsa_tfm = tfm;
|
|
filp->private_data = ctx;
|
|
|
|
return 0;
|
|
}
|
|
|
|
ctx = filp->private_data;
|
|
tfm = ctx->pka1_rsa_tfm;
|
|
|
|
if ((rsa_req->op_mode == RSA_SET_PUB) ||
|
|
(rsa_req->op_mode == RSA_SET_PRIV)) {
|
|
ret = tegra_cryptodev_rsa_set_key(tfm, rsa_req->key,
|
|
rsa_req->keylen,
|
|
MAX_RSA1_MSG_LEN,
|
|
rsa_req->op_mode);
|
|
if (ret < 0) {
|
|
pr_err("alg: pka1:rsa: set key failed\n");
|
|
return ret;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_ENCRYPT ||
|
|
rsa_req->op_mode == RSA_DECRYPT ||
|
|
rsa_req->op_mode == RSA_SIGN ||
|
|
rsa_req->op_mode == RSA_VERIFY) {
|
|
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("alg: rsa: Failed to allocate request: %s\n",
|
|
__func__);
|
|
ret = -ENOMEM;
|
|
goto out_tfm;
|
|
}
|
|
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto buf_fail;
|
|
}
|
|
|
|
init_completion(&rsa_complete.restart);
|
|
rsa_complete.req_err = 0;
|
|
|
|
len = crypto_akcipher_maxsize(tfm);
|
|
if (len < 0) {
|
|
ret = -EINVAL;
|
|
goto buf_fail;
|
|
}
|
|
|
|
ret = copy_from_user((void *)xbuf[0],
|
|
(void __user *)rsa_req->message, len);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_debug("%s: copy_from_user failed (%d)\n",
|
|
__func__, ret);
|
|
goto copy_fail;
|
|
}
|
|
|
|
sg_init_one(&sg[0], xbuf[0], len);
|
|
sg_init_one(&sg[1], xbuf[1], len);
|
|
|
|
akcipher_request_set_crypt(req, &sg[0], &sg[1], len, len);
|
|
|
|
if (rsa_req->op_mode == RSA_ENCRYPT) {
|
|
ret = crypto_akcipher_encrypt(req);
|
|
if (ret) {
|
|
pr_err("alg: pka1:rsa: encrypt failed\n");
|
|
goto copy_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_DECRYPT) {
|
|
ret = crypto_akcipher_decrypt(req);
|
|
if (ret) {
|
|
pr_err("alg: pka1:rsa: decrypt failed\n");
|
|
goto copy_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_SIGN) {
|
|
ret = crypto_akcipher_sign(req);
|
|
if (ret) {
|
|
pr_err("alg: pka1:rsa: sign failed\n");
|
|
goto copy_fail;
|
|
}
|
|
} else if (rsa_req->op_mode == RSA_VERIFY) {
|
|
ret = crypto_akcipher_verify(req);
|
|
if (ret) {
|
|
pr_err("alg: pka1:rsa: verification failed\n");
|
|
goto copy_fail;
|
|
}
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)rsa_req->result,
|
|
(const void *)xbuf[1], len);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("alg: pka1:rsa: copy_to_user failed (%d)\n",
|
|
ret);
|
|
}
|
|
copy_fail:
|
|
free_bufs(xbuf);
|
|
buf_fail:
|
|
akcipher_request_free(req);
|
|
} else if (rsa_req->op_mode == RSA_EXIT) {
|
|
crypto_free_akcipher(tfm);
|
|
} else {
|
|
pr_err("alg: pka1:rsa: invalid rsa operation\n");
|
|
}
|
|
out_tfm:
|
|
if (ret)
|
|
crypto_free_akcipher(tfm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypto_sha(struct file *filp, struct tegra_crypto_ctx *ctx,
|
|
struct tegra_sha_req *sha_req)
|
|
{
|
|
|
|
struct crypto_ahash *tfm;
|
|
struct scatterlist sg[1];
|
|
char result[64];
|
|
char algo[64];
|
|
struct ahash_request *req;
|
|
struct tegra_crypto_completion sha_complete;
|
|
void *hash_buff;
|
|
unsigned long *xbuf[XBUFSIZE];
|
|
int ret = -ENOMEM;
|
|
|
|
if (sha_req->plaintext_sz > PAGE_SIZE) {
|
|
pr_err("alg:hash: invalid plaintext_sz for sha_req\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (strncpy_from_user(algo, sha_req->algo, sizeof(algo) - 1) < 0) {
|
|
pr_err("alg:hash: invalid algo for sha_req\n");
|
|
return -EFAULT;
|
|
}
|
|
algo[sizeof(algo) - 1] = '\0';
|
|
|
|
tfm = crypto_alloc_ahash(algo, 0, 0);
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("alg:hash:Failed to load transform for %s:%ld\n",
|
|
algo, PTR_ERR(tfm));
|
|
goto out_alloc;
|
|
}
|
|
|
|
req = ahash_request_alloc(tfm, GFP_KERNEL);
|
|
if (!req) {
|
|
pr_err("alg:hash:Failed to allocate request for %s\n", algo);
|
|
goto out_noreq;
|
|
}
|
|
|
|
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
|
tegra_crypt_complete, &sha_complete);
|
|
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto out_buf;
|
|
}
|
|
|
|
init_completion(&sha_complete.restart);
|
|
sha_complete.req_err = 0;
|
|
|
|
memset(result, 0, 64);
|
|
|
|
hash_buff = xbuf[0];
|
|
|
|
ret = copy_from_user((void *)hash_buff,
|
|
(void __user *)sha_req->plaintext,
|
|
sha_req->plaintext_sz);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
sg_init_one(&sg[0], hash_buff, sha_req->plaintext_sz);
|
|
|
|
if (sha_req->keylen) {
|
|
crypto_ahash_clear_flags(tfm, ~0);
|
|
ret = crypto_ahash_setkey(tfm, sha_req->key,
|
|
sha_req->keylen);
|
|
if (ret) {
|
|
pr_err("alg:hash:setkey failed on %s:ret=%d\n",
|
|
sha_req->algo, ret);
|
|
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ahash_request_set_crypt(req, sg, result, sha_req->plaintext_sz);
|
|
|
|
ret = wait_async_op(&sha_complete, crypto_ahash_init(req));
|
|
if (ret) {
|
|
pr_err("alg: hash: init failed for %s: ret=%d\n",
|
|
sha_req->algo, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = wait_async_op(&sha_complete, crypto_ahash_update(req));
|
|
if (ret) {
|
|
pr_err("alg: hash: update failed for %s: ret=%d\n",
|
|
sha_req->algo, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = wait_async_op(&sha_complete, crypto_ahash_final(req));
|
|
if (ret) {
|
|
pr_err("alg: hash: final failed for %s: ret=%d\n",
|
|
sha_req->algo, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)sha_req->result,
|
|
(const void *)result, crypto_ahash_digestsize(tfm));
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("alg: hash: copy_to_user failed (%d) for %s\n",
|
|
ret, sha_req->algo);
|
|
}
|
|
|
|
out:
|
|
free_bufs(xbuf);
|
|
|
|
out_buf:
|
|
ahash_request_free(req);
|
|
|
|
out_noreq:
|
|
crypto_free_ahash(tfm);
|
|
|
|
out_alloc:
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_crypto_sha_shash(struct file *filp,
|
|
struct tegra_crypto_ctx *ctx,
|
|
struct tegra_sha_req_shash *sha_req_shash)
|
|
{
|
|
struct crypto_shash *stfm;
|
|
char result[64];
|
|
struct shash_desc *desc;
|
|
struct tegra_crypto_completion sha_complete;
|
|
void *shash_buff;
|
|
unsigned long *xbuf[XBUFSIZE];
|
|
int ret = -ENOMEM;
|
|
char sha_algo[5][10] = {"sha1", "sha224", "sha256",
|
|
"sha384", "sha512"};
|
|
|
|
if (sha_req_shash->plaintext_sz > PAGE_SIZE) {
|
|
pr_err("alg:shash: invalid plaintext_sz for sha_req_shash\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (sha_req_shash->algo > SHA512 || sha_req_shash->algo < 0) {
|
|
pr_err("alg: shash: invalid index algo in sha_req_shash\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sha_req_shash->algo = array_index_nospec(sha_req_shash->algo, SHA512);
|
|
|
|
stfm = crypto_alloc_shash(sha_algo[sha_req_shash->algo], 0, 0);
|
|
if (IS_ERR(stfm)) {
|
|
pr_err("alg:shash:Failed to load transform for %s:%ld\n",
|
|
sha_algo[sha_req_shash->algo], PTR_ERR(stfm));
|
|
goto out_alloc;
|
|
}
|
|
|
|
ctx->shash_tfm[sha_req_shash->algo] = stfm;
|
|
|
|
filp->private_data = ctx;
|
|
desc = shash_request_alloc(stfm, GFP_KERNEL);
|
|
if (!desc) {
|
|
pr_err("alg:shash:Failed to allocate request for %s\n",
|
|
sha_algo[sha_req_shash->algo]);
|
|
goto out_noreq;
|
|
}
|
|
|
|
ret = alloc_bufs(xbuf);
|
|
if (ret < 0) {
|
|
pr_err("alloc_bufs failed");
|
|
goto out_buf;
|
|
}
|
|
|
|
init_completion(&sha_complete.restart);
|
|
sha_complete.req_err = 0;
|
|
shash_buff = xbuf[0];
|
|
ret = copy_from_user((void *)shash_buff,
|
|
(void __user *)sha_req_shash->plaintext,
|
|
sha_req_shash->plaintext_sz);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user failed (%d)\n", __func__, ret);
|
|
goto out;
|
|
}
|
|
desc->tfm = stfm;
|
|
ret = sha_shash_hash_op(desc, &sha_complete, crypto_shash_init(desc));
|
|
if (ret) {
|
|
pr_err("alg: shash: init failed for %s: ret=%d\n",
|
|
sha_algo[sha_req_shash->algo], ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = sha_shash_hash_op(desc, &sha_complete,
|
|
crypto_shash_update(desc, shash_buff,
|
|
sha_req_shash->plaintext_sz));
|
|
if (ret) {
|
|
pr_err("alg: shash: update failed for %s: ret=%d\n",
|
|
sha_algo[sha_req_shash->algo], ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = sha_shash_hash_op(desc, &sha_complete,
|
|
crypto_shash_final(desc, result));
|
|
if (ret) {
|
|
pr_err("alg: shash: final failed for %s: ret=%d\n",
|
|
sha_algo[sha_req_shash->algo], ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)sha_req_shash->result,
|
|
(const void *)result, crypto_shash_digestsize(stfm));
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("alg: shash: copy_to_user failed (%d) for %s\n",
|
|
ret, sha_algo[sha_req_shash->algo]);
|
|
}
|
|
|
|
out:
|
|
free_bufs(xbuf);
|
|
|
|
out_buf:
|
|
shash_request_free(desc);
|
|
|
|
out_noreq:
|
|
crypto_free_shash(stfm);
|
|
|
|
out_alloc:
|
|
return ret;
|
|
}
|
|
|
|
static long tegra_crypto_dev_ioctl(struct file *filp,
|
|
unsigned int ioctl_num, unsigned long arg)
|
|
{
|
|
struct tegra_crypto_ctx *ctx = filp->private_data;
|
|
struct crypto_rng *tfm = NULL;
|
|
struct tegra_pka1_rsa_request pka1_rsa_req;
|
|
struct tegra_se_pka1_ecc_request pka1_ecc_req;
|
|
struct tegra_pka1_eddsa_request pka1_eddsa_req;
|
|
struct tegra_crypt_req crypt_req;
|
|
struct tegra_rng_req rng_req;
|
|
struct tegra_sha_req sha_req;
|
|
struct tegra_sha_req_shash sha_req_shash;
|
|
struct tegra_rsa_req rsa_req;
|
|
struct tegra_rsa_req_ahash rsa_req_ah;
|
|
#ifdef CONFIG_COMPAT
|
|
struct tegra_crypt_req_32 crypt_req_32;
|
|
struct tegra_rng_req_32 rng_req_32;
|
|
struct tegra_sha_req_32 sha_req_32;
|
|
int i = 0;
|
|
#endif
|
|
char *rng;
|
|
int ret = 0;
|
|
|
|
/*
|
|
* Avoid processing ioctl if the file has been closed.
|
|
* This will prevent crashes caused by NULL pointer dereference.
|
|
*/
|
|
if (!ctx) {
|
|
pr_err("%s: ctx not allocated\n", __func__);
|
|
return -EPERM;
|
|
}
|
|
|
|
mutex_lock(&ctx->lock);
|
|
|
|
switch (ioctl_num) {
|
|
case TEGRA_CRYPTO_IOCTL_NEED_SSK:
|
|
ctx->use_ssk = (int)arg;
|
|
break;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
case TEGRA_CRYPTO_IOCTL_PROCESS_REQ_32:
|
|
ret = copy_from_user(&crypt_req_32, (void __user *)arg,
|
|
sizeof(struct tegra_crypt_req_32));
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
if (crypt_req_32.keylen > TEGRA_CRYPTO_MAX_KEY_SIZE) {
|
|
pr_err("key length %d exceeds max value %d\n",
|
|
crypt_req_32.keylen, TEGRA_CRYPTO_MAX_KEY_SIZE);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
crypt_req.op = crypt_req_32.op;
|
|
crypt_req.encrypt = crypt_req_32.encrypt;
|
|
crypt_req.skip_key = crypt_req_32.skip_key;
|
|
crypt_req.skip_iv = crypt_req_32.skip_iv;
|
|
for (i = 0; i < crypt_req_32.keylen; i++)
|
|
crypt_req.key[i] = crypt_req_32.key[i];
|
|
crypt_req.keylen = crypt_req_32.keylen;
|
|
for (i = 0; i < TEGRA_CRYPTO_IV_SIZE; i++)
|
|
crypt_req.iv[i] = crypt_req_32.iv[i];
|
|
crypt_req.ivlen = crypt_req_32.ivlen;
|
|
crypt_req.plaintext =
|
|
(u8 __user *)(void *)(__u64)(crypt_req_32.plaintext);
|
|
crypt_req.plaintext_sz = crypt_req_32.plaintext_sz;
|
|
crypt_req.result =
|
|
(u8 __user *)(void *)(__u64)(crypt_req_32.result);
|
|
|
|
ret = process_crypt_req(filp, ctx, &crypt_req);
|
|
break;
|
|
#endif
|
|
case TEGRA_CRYPTO_IOCTL_PROCESS_REQ:
|
|
ret = copy_from_user(&crypt_req, (void __user *)arg,
|
|
sizeof(crypt_req));
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
ret = process_crypt_req(filp, ctx, &crypt_req);
|
|
break;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
case TEGRA_CRYPTO_IOCTL_SET_SEED_32:
|
|
if (copy_from_user(&rng_req_32, (void __user *)arg,
|
|
sizeof(rng_req_32))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < TEGRA_CRYPTO_RNG_SEED_SIZE; i++)
|
|
rng_req.seed[i] = rng_req_32.seed[i];
|
|
rng_req.type = rng_req_32.type;
|
|
/* fall through */
|
|
#endif
|
|
|
|
case TEGRA_CRYPTO_IOCTL_SET_SEED:
|
|
if (ioctl_num == TEGRA_CRYPTO_IOCTL_SET_SEED) {
|
|
if (copy_from_user(&rng_req, (void __user *)arg,
|
|
sizeof(rng_req))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n",
|
|
__func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
memcpy(ctx->seed, rng_req.seed, TEGRA_CRYPTO_RNG_SEED_SIZE);
|
|
|
|
if (rng_req.type == RNG_DRBG) {
|
|
if (tegra_get_chip_id() == TEGRA20 ||
|
|
tegra_get_chip_id() == TEGRA30) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
ctx->rng_drbg = crypto_alloc_rng("rng_drbg-aes-tegra",
|
|
CRYPTO_ALG_TYPE_RNG, 0);
|
|
if (IS_ERR(ctx->rng_drbg)) {
|
|
pr_err("Failed to load transform for "
|
|
"rng_drbg tegra: %ld\n",
|
|
PTR_ERR(ctx->rng_drbg));
|
|
ret = PTR_ERR(ctx->rng_drbg);
|
|
goto out;
|
|
}
|
|
tfm = ctx->rng_drbg;
|
|
filp->private_data = ctx;
|
|
ret = crypto_rng_reset(ctx->rng_drbg, ctx->seed,
|
|
crypto_rng_seedsize(ctx->rng_drbg));
|
|
} else {
|
|
ctx->rng = crypto_alloc_rng("rng-aes-tegra",
|
|
CRYPTO_ALG_TYPE_RNG, 0);
|
|
if (IS_ERR(ctx->rng)) {
|
|
pr_err("Failed to load transform for "
|
|
"tegra rng: %ld\n", PTR_ERR(ctx->rng));
|
|
ret = PTR_ERR(ctx->rng);
|
|
goto out;
|
|
}
|
|
tfm = ctx->rng;
|
|
filp->private_data = ctx;
|
|
ret = crypto_rng_reset(ctx->rng, ctx->seed,
|
|
crypto_rng_seedsize(ctx->rng));
|
|
}
|
|
if (ret)
|
|
pr_err("crypto_rng_reset failed: %d\n", ret);
|
|
crypto_free_rng(tfm);
|
|
break;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
case TEGRA_CRYPTO_IOCTL_GET_RANDOM_32:
|
|
if (copy_from_user(&rng_req_32, (void __user *)arg,
|
|
sizeof(rng_req_32))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
rng_req.nbytes = rng_req_32.nbytes;
|
|
rng_req.type = rng_req_32.type;
|
|
rng_req.rdata = (u8 __user *)(void *)(__u64)rng_req_32.rdata;
|
|
/* fall through */
|
|
#endif
|
|
|
|
case TEGRA_CRYPTO_IOCTL_GET_RANDOM:
|
|
if (ioctl_num == TEGRA_CRYPTO_IOCTL_GET_RANDOM) {
|
|
if (copy_from_user(&rng_req, (void __user *)arg,
|
|
sizeof(rng_req))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n",
|
|
__func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rng = kzalloc(rng_req.nbytes, GFP_KERNEL);
|
|
if (!rng) {
|
|
if (rng_req.type == RNG_DRBG)
|
|
pr_err("mem alloc for rng_drbg fail");
|
|
else
|
|
pr_err("mem alloc for rng fail");
|
|
|
|
ret = -ENODATA;
|
|
goto out;
|
|
}
|
|
|
|
if (rng_req.type == RNG_DRBG) {
|
|
if (tegra_get_chip_id() == TEGRA20 ||
|
|
tegra_get_chip_id() == TEGRA30) {
|
|
ret = -EINVAL;
|
|
goto rng_out;
|
|
}
|
|
ctx->rng_drbg = crypto_alloc_rng("rng_drbg-aes-tegra",
|
|
CRYPTO_ALG_TYPE_RNG, 0);
|
|
if (IS_ERR(ctx->rng_drbg)) {
|
|
pr_err("Failed to load transform for "
|
|
"rng_drbg tegra: %ld\n",
|
|
PTR_ERR(ctx->rng_drbg));
|
|
ret = PTR_ERR(ctx->rng_drbg);
|
|
goto rng_out;
|
|
}
|
|
tfm = ctx->rng_drbg;
|
|
filp->private_data = ctx;
|
|
ret = crypto_rng_get_bytes(ctx->rng_drbg, rng,
|
|
rng_req.nbytes);
|
|
} else {
|
|
ctx->rng = crypto_alloc_rng("rng-aes-tegra",
|
|
CRYPTO_ALG_TYPE_RNG, 0);
|
|
if (IS_ERR(ctx->rng)) {
|
|
pr_err("Failed to load transform for "
|
|
"tegra rng: %ld\n", PTR_ERR(ctx->rng));
|
|
ret = PTR_ERR(ctx->rng);
|
|
goto rng_out;
|
|
}
|
|
tfm = ctx->rng;
|
|
filp->private_data = ctx;
|
|
ret = crypto_rng_get_bytes(ctx->rng, rng,
|
|
rng_req.nbytes);
|
|
}
|
|
|
|
if (ret != rng_req.nbytes) {
|
|
if (rng_req.type == RNG_DRBG)
|
|
pr_err("rng_drbg failed");
|
|
else
|
|
pr_err("rng failed");
|
|
ret = -ENODATA;
|
|
goto free_tfm;
|
|
}
|
|
ret = copy_to_user((void __user *)rng_req.rdata,
|
|
(const void *)rng, rng_req.nbytes);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_to_user fail(%d)\n", __func__, ret);
|
|
goto free_tfm;
|
|
}
|
|
free_tfm:
|
|
crypto_free_rng(tfm);
|
|
rng_out:
|
|
kfree(rng);
|
|
break;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
case TEGRA_CRYPTO_IOCTL_GET_SHA_32:
|
|
ret = copy_from_user(&sha_req_32, (void __user *)arg,
|
|
sizeof(sha_req_32));
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
if (sha_req_32.keylen > TEGRA_CRYPTO_MAX_KEY_SIZE) {
|
|
pr_err("key length %d not within the range [0,%d]\n",
|
|
sha_req_32.keylen, TEGRA_CRYPTO_MAX_KEY_SIZE);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
for (i = 0; i < sha_req_32.keylen; i++)
|
|
sha_req.key[i] = sha_req_32.key[i];
|
|
sha_req.keylen = sha_req_32.keylen;
|
|
sha_req.algo =
|
|
(unsigned char __user *)(void *)(__u64)(sha_req_32.algo);
|
|
sha_req.plaintext =
|
|
(unsigned char __user *)(void *)(__u64)(sha_req_32.plaintext);
|
|
sha_req.plaintext_sz = sha_req_32.plaintext_sz;
|
|
sha_req.result =
|
|
(unsigned char __user *)(void *)(__u64)(sha_req_32.result);
|
|
|
|
ret = tegra_crypto_sha(filp, ctx, &sha_req);
|
|
break;
|
|
#endif
|
|
|
|
case TEGRA_CRYPTO_IOCTL_GET_SHA:
|
|
if (tegra_get_chip_id() == TEGRA20) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (copy_from_user(&sha_req, (void __user *)arg,
|
|
sizeof(sha_req))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
if (sha_req.keylen > TEGRA_CRYPTO_MAX_KEY_SIZE) {
|
|
pr_err("key length %d not within the range [0,%d]\n",
|
|
sha_req.keylen, TEGRA_CRYPTO_MAX_KEY_SIZE);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
ret = tegra_crypto_sha(filp, ctx, &sha_req);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_GET_SHA_SHASH:
|
|
if (copy_from_user(&sha_req_shash, (void __user *)arg,
|
|
sizeof(sha_req_shash))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
ret = tegra_crypto_sha_shash(filp, ctx, &sha_req_shash);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_RSA_REQ_AHASH:
|
|
if (copy_from_user(&rsa_req_ah, (void __user *)arg,
|
|
sizeof(rsa_req_ah))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
if (rsa_req_ah.algo >= NUM_RSA_ALGO) {
|
|
pr_err("Invalid value of algo index %d\n",
|
|
rsa_req_ah.algo);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
rsa_req_ah.algo = array_index_nospec(rsa_req_ah.algo,
|
|
NUM_RSA_ALGO);
|
|
if (rsa_req_ah.msg_len > MAX_RSA_MSG_LEN) {
|
|
pr_err("Illegal message from user of length = %d\n",
|
|
rsa_req_ah.msg_len);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
rsa_req_ah.msg_len = array_index_nospec(rsa_req_ah.msg_len,
|
|
MAX_RSA_MSG_LEN + 1);
|
|
ret = tegra_crypt_rsa_ahash(filp, ctx, &rsa_req_ah);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_RSA_REQ:
|
|
if (copy_from_user(&rsa_req, (void __user *)arg,
|
|
sizeof(rsa_req))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
if (rsa_req.msg_len > MAX_RSA_MSG_LEN) {
|
|
pr_err("Illegal message from user of length = %d\n",
|
|
rsa_req.msg_len);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
rsa_req.msg_len = array_index_nospec(rsa_req.msg_len,
|
|
MAX_RSA_MSG_LEN + 1);
|
|
if (rsa_req.algo >= NUM_RSA_ALGO) {
|
|
pr_err("Invalid value of algo index %d\n",
|
|
rsa_req.algo);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
rsa_req.algo = array_index_nospec(rsa_req.algo,
|
|
NUM_RSA_ALGO);
|
|
|
|
ret = tegra_crypt_rsa(filp, ctx, &rsa_req);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_PKA1_RSA_REQ:
|
|
if (copy_from_user(&pka1_rsa_req, (void __user *)arg,
|
|
sizeof(pka1_rsa_req))) {
|
|
pr_err("%s: copy_from_user fail(%d) for pka1_rsa_req\n",
|
|
__func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
ret = tegra_crypt_pka1_rsa(filp, ctx, &pka1_rsa_req);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_PKA1_ECC_REQ:
|
|
if (copy_from_user(&pka1_ecc_req, (void __user *)arg,
|
|
sizeof(pka1_ecc_req))) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user fail(%d) for pka1_ecc_req\n",
|
|
__func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = tegra_crypt_pka1_ecc(&pka1_ecc_req);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_PKA1_EDDSA_REQ:
|
|
if (copy_from_user(&pka1_eddsa_req, (void __user *)arg,
|
|
sizeof(pka1_eddsa_req))) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_from_user fail(%d) for pka1_eddsa_req\n",
|
|
__func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = tegra_crypt_pka1_eddsa(&pka1_eddsa_req);
|
|
break;
|
|
|
|
case TEGRA_CRYPTO_IOCTL_RNG1_REQ:
|
|
if (copy_from_user(&rng_req, (void __user *)arg,
|
|
sizeof(rng_req))) {
|
|
pr_err("%s: copy_from_user fail(%d)\n",
|
|
__func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
rng = kzalloc(rng_req.nbytes, GFP_KERNEL);
|
|
if (!rng) {
|
|
ret = -ENODATA;
|
|
goto out;
|
|
}
|
|
|
|
ctx->rng = crypto_alloc_rng("rng1-elp-tegra",
|
|
CRYPTO_ALG_TYPE_RNG, 0);
|
|
if (IS_ERR(ctx->rng)) {
|
|
pr_err("Failed to alloc rng1: %ld\n",
|
|
PTR_ERR(ctx->rng));
|
|
ret = PTR_ERR(ctx->rng);
|
|
goto rng1_out;
|
|
}
|
|
|
|
tfm = ctx->rng;
|
|
filp->private_data = ctx;
|
|
ret = crypto_rng_get_bytes(ctx->rng, rng,
|
|
rng_req.nbytes);
|
|
|
|
if (ret != rng_req.nbytes) {
|
|
pr_err("rng failed");
|
|
ret = -ENODATA;
|
|
goto free_rng1_tfm;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)rng_req.rdata,
|
|
(const void *)rng, rng_req.nbytes);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
pr_err("%s: copy_to_user fail(%d)\n", __func__, ret);
|
|
goto free_rng1_tfm;
|
|
}
|
|
free_rng1_tfm:
|
|
crypto_free_rng(tfm);
|
|
rng1_out:
|
|
kfree(rng);
|
|
break;
|
|
|
|
default:
|
|
pr_debug("invalid ioctl code(%d)", ioctl_num);
|
|
ret = -EINVAL;
|
|
}
|
|
out:
|
|
mutex_unlock(&ctx->lock);
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations tegra_crypto_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tegra_crypto_dev_open,
|
|
.release = tegra_crypto_dev_release,
|
|
.unlocked_ioctl = tegra_crypto_dev_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = tegra_crypto_dev_ioctl,
|
|
#endif
|
|
};
|
|
|
|
static struct miscdevice tegra_crypto_device = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "tegra-crypto",
|
|
.fops = &tegra_crypto_fops,
|
|
};
|
|
|
|
static int __init tegra_crypto_dev_init(void)
|
|
{
|
|
return misc_register(&tegra_crypto_device);
|
|
}
|
|
|
|
late_initcall(tegra_crypto_dev_init);
|
|
|
|
static void __exit tegra_crypto_module_exit(void)
|
|
{
|
|
misc_deregister(&tegra_crypto_device);
|
|
}
|
|
module_exit(tegra_crypto_module_exit);
|
|
|
|
MODULE_DESCRIPTION("Tegra AES hw device node.");
|
|
MODULE_AUTHOR("NVIDIA Corporation");
|
|
MODULE_LICENSE("GPL v2");
|