misc: tegra-cryptodev: Enhancement to support user space tests
vjagadish [Fri, 9 Dec 2011 04:15:56 +0000 (09:15 +0530)]
Enchancement to support user space tests such as OFB, CTR, sha1,
sha224,sha256, sha384, sha512.

BUG 903375

Change-Id: I52767978bd3758671ec6fff988223ac046f5579c
Reviewed-on: http://git-master/r/84296
Tested-by: Venkata Jagadish <vjagadish@nvidia.com>
Reviewed-by: Mallikarjun Kasoju <mkasoju@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

drivers/misc/tegra-cryptodev.c
drivers/misc/tegra-cryptodev.h

index d5ed6a2..d767ea0 100644 (file)
 #include <linux/scatterlist.h>
 #include <linux/uaccess.h>
 #include <crypto/rng.h>
+#include <crypto/hash.h>
+#include <mach/hardware.h>
 
 #include "tegra-cryptodev.h"
 
 #define NBUFS 2
+#define XBUFSIZE 8
 
 struct tegra_crypto_ctx {
        struct crypto_ablkcipher *ecb_tfm;
        struct crypto_ablkcipher *cbc_tfm;
+       struct crypto_ablkcipher *ofb_tfm;
+       struct crypto_ablkcipher *ctr_tfm;
+       struct crypto_ablkcipher *cmac_tfm;
        struct crypto_rng *rng;
        u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
        int use_ssk;
@@ -105,6 +111,26 @@ static int tegra_crypto_dev_open(struct inode *inode, struct file *filp)
                goto fail_cbc;
        }
 
+       if (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2) {
+               ctx->ofb_tfm = crypto_alloc_ablkcipher("ofb-aes-tegra",
+                       CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
+               if (IS_ERR(ctx->ofb_tfm)) {
+                       pr_err("Failed to load transform for ofb-aes-tegra: %ld\n",
+                               PTR_ERR(ctx->ofb_tfm));
+                       ret = PTR_ERR(ctx->ofb_tfm);
+                       goto fail_ofb;
+               }
+
+               ctx->ctr_tfm = crypto_alloc_ablkcipher("ctr-aes-tegra",
+                       CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
+               if (IS_ERR(ctx->ctr_tfm)) {
+                       pr_err("Failed to load transform for ctr-aes-tegra: %ld\n",
+                               PTR_ERR(ctx->ctr_tfm));
+                       ret = PTR_ERR(ctx->ctr_tfm);
+                       goto fail_ctr;
+               }
+       }
+
        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",
@@ -117,6 +143,12 @@ static int tegra_crypto_dev_open(struct inode *inode, struct file *filp)
        return ret;
 
 fail_rng:
+       if (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2)
+               crypto_free_ablkcipher(ctx->ctr_tfm);
+fail_ctr:
+       if (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2)
+               crypto_free_ablkcipher(ctx->ofb_tfm);
+fail_ofb:
        crypto_free_ablkcipher(ctx->cbc_tfm);
 
 fail_cbc:
@@ -133,6 +165,12 @@ static int tegra_crypto_dev_release(struct inode *inode, struct file *filp)
 
        crypto_free_ablkcipher(ctx->ecb_tfm);
        crypto_free_ablkcipher(ctx->cbc_tfm);
+
+       if (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2) {
+               crypto_free_ablkcipher(ctx->ofb_tfm);
+               crypto_free_ablkcipher(ctx->ctr_tfm);
+       }
+
        crypto_free_rng(ctx->rng);
        kfree(ctx);
        filp->private_data = NULL;
@@ -158,16 +196,27 @@ static int process_crypt_req(struct tegra_crypto_ctx *ctx, struct tegra_crypt_re
        unsigned long *xbuf[NBUFS];
        int ret = 0, size = 0;
        unsigned long total = 0;
-       struct tegra_crypto_completion tcrypt_complete;
        const u8 *key = NULL;
+       struct tegra_crypto_completion tcrypt_complete;
 
        if (crypt_req->op & TEGRA_CRYPTO_ECB) {
                req = ablkcipher_request_alloc(ctx->ecb_tfm, GFP_KERNEL);
                tfm = ctx->ecb_tfm;
-       } else {
+       } else if (crypt_req->op & TEGRA_CRYPTO_CBC) {
                req = ablkcipher_request_alloc(ctx->cbc_tfm, GFP_KERNEL);
                tfm = ctx->cbc_tfm;
+       } else if ((crypt_req->op & TEGRA_CRYPTO_OFB) &&
+                       (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2)) {
+
+               req = ablkcipher_request_alloc(ctx->ofb_tfm, GFP_KERNEL);
+               tfm = ctx->ofb_tfm;
+       } else if ((crypt_req->op & TEGRA_CRYPTO_CTR) &&
+                       (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2)) {
+
+               req = ablkcipher_request_alloc(ctx->ctr_tfm, GFP_KERNEL);
+               tfm = ctx->ctr_tfm;
        }
+
        if (!req) {
                pr_err("%s: Failed to allocate request\n", __func__);
                return -ENOMEM;
@@ -254,12 +303,121 @@ process_req_out:
        return ret;
 }
 
+static int sha_async_hash_op(struct ahash_request *req,
+                               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;
+               INIT_COMPLETION(tr->restart);
+       }
+       return ret;
+}
+
+static int tegra_crypto_sha(struct tegra_sha_req *sha_req)
+{
+
+       struct crypto_ahash *tfm;
+       struct scatterlist sg[1];
+       char result[64];
+       struct ahash_request *req;
+       struct tegra_crypto_completion sha_complete;
+       void *hash_buff;
+       unsigned long *xbuf[XBUFSIZE];
+       int ret = -ENOMEM;
+
+       tfm = crypto_alloc_ahash(sha_req->algo, 0, 0);
+       if (IS_ERR(tfm)) {
+               printk(KERN_ERR "alg: hash: Failed to load transform for %s: "
+                      "%ld\n", sha_req->algo, PTR_ERR(tfm));
+               goto out_alloc;
+       }
+
+       req = ahash_request_alloc(tfm, GFP_KERNEL);
+       if (!req) {
+               printk(KERN_ERR "alg: hash: Failed to allocate request for "
+                      "%s\n", sha_req->algo);
+               goto out_noreq;
+       }
+
+       ret = alloc_bufs(xbuf);
+       if (ret < 0) {
+               pr_err("alloc_bufs failed");
+               goto out_buf;
+       }
+
+       init_completion(&sha_complete.restart);
+
+       memset(result, 0, 64);
+
+       hash_buff = xbuf[0];
+
+       memcpy(hash_buff, sha_req->plaintext, sha_req->plaintext_sz);
+       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) {
+                       printk(KERN_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 = sha_async_hash_op(req, &sha_complete, crypto_ahash_init(req));
+       if (ret) {
+               pr_err("alg: hash: init failed on "
+                      "for %s: ret=%d\n", sha_req->algo, -ret);
+               goto out;
+       }
+
+       ret = sha_async_hash_op(req, &sha_complete, crypto_ahash_update(req));
+       if (ret) {
+               pr_err("alg: hash: update failed on "
+                      "for %s: ret=%d\n", sha_req->algo, -ret);
+               goto out;
+       }
+
+       ret = sha_async_hash_op(req, &sha_complete, crypto_ahash_final(req));
+       if (ret) {
+               pr_err("alg: hash: final failed on "
+                      "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 < 0)
+               goto out;
+
+out:
+       free_bufs(xbuf);
+
+out_buf:
+       ahash_request_free(req);
+
+out_noreq:
+       crypto_free_ahash(tfm);
+
+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 tegra_crypt_req crypt_req;
        struct tegra_rng_req rng_req;
+       struct tegra_sha_req sha_req;
        char *rng;
        int ret = 0;
 
@@ -316,6 +474,17 @@ rng_out:
                        kfree(rng);
                break;
 
+       case TEGRA_CRYPTO_IOCTL_GET_SHA:
+               if (tegra_get_chipid() != TEGRA_CHIPID_TEGRA2) {
+                       if (copy_from_user(&sha_req, (void __user *)arg,
+                               sizeof(sha_req)))
+                               return -EFAULT;
+                       ret = tegra_crypto_sha(&sha_req);
+               } else {
+                       ret = -EINVAL;
+               }
+               break;
+
        default:
                pr_debug("invalid ioctl code(%d)", ioctl_num);
                ret = -EINVAL;
index ed62a52..34ec9c1 100644 (file)
@@ -28,6 +28,7 @@
 #define TEGRA_CRYPTO_IOCTL_PROCESS_REQ _IOWR(0x98, 101, int*)
 #define TEGRA_CRYPTO_IOCTL_SET_SEED    _IOWR(0x98, 102, int*)
 #define TEGRA_CRYPTO_IOCTL_GET_RANDOM  _IOWR(0x98, 103, int*)
+#define TEGRA_CRYPTO_IOCTL_GET_SHA     _IOWR(0x98, 104, int*)
 
 #define TEGRA_CRYPTO_MAX_KEY_SIZE      AES_MAX_KEY_SIZE
 #define TEGRA_CRYPTO_IV_SIZE   AES_BLOCK_SIZE
 /* encrypt/decrypt operations */
 #define TEGRA_CRYPTO_ECB       BIT(0)
 #define TEGRA_CRYPTO_CBC       BIT(1)
-#define TEGRA_CRYPTO_RNG       BIT(2)
+#define TEGRA_CRYPTO_OFB       BIT(2)
+#define TEGRA_CRYPTO_CTR       BIT(3)
+#define TEGRA_CRYPTO_CMAC      BIT(4)
+#define TEGRA_CRYPTO_RNG       BIT(5)
 
 /* a pointer to this struct needs to be passed to:
  * TEGRA_CRYPTO_IOCTL_PROCESS_REQ
@@ -67,4 +71,13 @@ struct tegra_rng_req {
        int nbytes; /* random data length */
 };
 
+struct tegra_sha_req {
+       char key[TEGRA_CRYPTO_MAX_KEY_SIZE];
+       int keylen;
+       unsigned char *algo;
+       unsigned char *plaintext;
+       int plaintext_sz;
+       unsigned char *result;
+};
+
 #endif