Merge "crypto: msm: Add support for FIPS complience"
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 68616b8..87938e0 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -254,6 +254,8 @@
config HW_RANDOM_MSM
tristate "Qualcomm MSM Random Number Generator support"
depends on HW_RANDOM && ARCH_MSM
+ select CRYPTO_AES
+ select CRYPTO_ECB
default n
---help---
This driver provides kernel-side support for the Random Number
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index c24305d..d0369fd 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -22,4 +22,4 @@
obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
-obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o
+obj-$(CONFIG_HW_RANDOM_MSM) += msm_rng.o fips_drbg.o ctr_drbg.o msm_fips_selftest.o
diff --git a/drivers/char/hw_random/ctr_drbg.c b/drivers/char/hw_random/ctr_drbg.c
new file mode 100644
index 0000000..d8da08e
--- /dev/null
+++ b/drivers/char/hw_random/ctr_drbg.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <mach/msm_bus.h>
+#include <linux/qrng.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/string.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+
+#include "ctr_drbg.h"
+#include "fips_drbg.h"
+
+#define E_FAILURE 0Xffff
+#define E_SUCCESS 0
+
+#define AES128_KEY_SIZE (16)
+#define AES128_BLOCK_SIZE (16)
+
+#define AES_TEXT_LENGTH (64)
+#define MAX_TEXT_LENGTH (2048)
+
+uint8_t df_initial_k[16] = "\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf";
+
+static void _crypto_cipher_test_complete(struct crypto_async_request *req,
+ int err)
+{
+ struct msm_ctr_tcrypt_result_s *res = NULL;
+
+ if (!req)
+ return;
+
+ res = req->data;
+ if (!res)
+ return;
+
+ if (err == -EINPROGRESS)
+ return;
+ res->err = err;
+ complete(&res->completion);
+}
+
+static int ctr_aes_init(struct ctr_drbg_ctx_s *ctx)
+{
+ int status = 0;
+
+ ctx->aes_ctx.tfm = crypto_alloc_ablkcipher("qcom-ecb(aes)", 0, 0);
+ if (IS_ERR(ctx->aes_ctx.tfm) || (NULL == ctx->aes_ctx.tfm)) {
+ pr_info("%s: qcom-ecb(aes) failed", __func__);
+ ctx->aes_ctx.tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0);
+ pr_info("ctx->aes_ctx.tfm = %p\n", ctx->aes_ctx.tfm);
+ if (IS_ERR(ctx->aes_ctx.tfm) || (NULL == ctx->aes_ctx.tfm)) {
+ pr_err("%s: qcom-ecb(aes) failed\n", __func__);
+ status = -E_FAILURE;
+ goto out;
+ }
+ }
+
+ ctx->aes_ctx.req = ablkcipher_request_alloc(ctx->aes_ctx.tfm,
+ GFP_KERNEL);
+ if (IS_ERR(ctx->aes_ctx.req) || (NULL == ctx->aes_ctx.req)) {
+ pr_info("%s: Failed to allocate request.\n", __func__);
+ status = -E_FAILURE;
+ goto clr_tfm;
+ }
+
+ ablkcipher_request_set_callback(ctx->aes_ctx.req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _crypto_cipher_test_complete,
+ &ctx->aes_ctx.result);
+
+ memset(&ctx->aes_ctx.input, 0, sizeof(struct msm_ctr_buffer_s));
+ memset(&ctx->aes_ctx.output, 0, sizeof(struct msm_ctr_buffer_s));
+
+ /* Allocate memory. */
+ ctx->aes_ctx.input.virt_addr = kmalloc(AES128_BLOCK_SIZE,
+ GFP_KERNEL | __GFP_DMA);
+ if (NULL == ctx->aes_ctx.input.virt_addr) {
+ pr_debug("%s: Failed to input memory.\n", __func__);
+ status = -E_FAILURE;
+ goto clr_req;
+ }
+ ctx->aes_ctx.output.virt_addr = kmalloc(AES128_BLOCK_SIZE,
+ GFP_KERNEL | __GFP_DMA);
+ if (NULL == ctx->aes_ctx.output.virt_addr) {
+ pr_debug("%s: Failed to output memory.\n", __func__);
+ status = -E_FAILURE;
+ goto clr_input;
+ }
+
+ /*--------------------------------------------------------------------
+ Set DF AES mode
+ ----------------------------------------------------------------------*/
+ ctx->df_aes_ctx.tfm = crypto_alloc_ablkcipher("qcom-ecb(aes)", 0, 0);
+ if ((NULL == ctx->df_aes_ctx.tfm) || IS_ERR(ctx->df_aes_ctx.tfm)) {
+ pr_info("%s: qcom-ecb(aes) failed", __func__);
+ ctx->df_aes_ctx.tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, 0);
+ if (IS_ERR(ctx->df_aes_ctx.tfm) ||
+ (NULL == ctx->df_aes_ctx.tfm)) {
+ pr_err("%s: ecb(aes) failed", __func__);
+ status = -E_FAILURE;
+ goto clr_output;
+ }
+ }
+
+ ctx->df_aes_ctx.req = ablkcipher_request_alloc(ctx->df_aes_ctx.tfm,
+ GFP_KERNEL);
+ if (IS_ERR(ctx->df_aes_ctx.req) || (NULL == ctx->df_aes_ctx.req)) {
+ pr_debug(": Failed to allocate request.\n");
+ status = -E_FAILURE;
+ goto clr_df_tfm;
+ }
+
+ ablkcipher_request_set_callback(ctx->df_aes_ctx.req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _crypto_cipher_test_complete,
+ &ctx->df_aes_ctx.result);
+
+ memset(&ctx->df_aes_ctx.input, 0, sizeof(struct msm_ctr_buffer_s));
+ memset(&ctx->df_aes_ctx.output, 0, sizeof(struct msm_ctr_buffer_s));
+
+ ctx->df_aes_ctx.input.virt_addr = kmalloc(AES128_BLOCK_SIZE,
+ GFP_KERNEL | __GFP_DMA);
+ if (NULL == ctx->df_aes_ctx.input.virt_addr) {
+ pr_debug(": Failed to input memory.\n");
+ status = -E_FAILURE;
+ goto clr_df_req;
+ }
+
+ ctx->df_aes_ctx.output.virt_addr = kmalloc(AES128_BLOCK_SIZE,
+ GFP_KERNEL | __GFP_DMA);
+ if (NULL == ctx->df_aes_ctx.output.virt_addr) {
+ pr_debug(": Failed to output memory.\n");
+ status = -E_FAILURE;
+ goto clr_df_input;
+ }
+
+ goto out;
+
+clr_df_input:
+ if (ctx->df_aes_ctx.input.virt_addr) {
+ kzfree(ctx->df_aes_ctx.input.virt_addr);
+ ctx->df_aes_ctx.input.virt_addr = NULL;
+ }
+clr_df_req:
+ if (ctx->df_aes_ctx.req) {
+ ablkcipher_request_free(ctx->df_aes_ctx.req);
+ ctx->df_aes_ctx.req = NULL;
+ }
+clr_df_tfm:
+ if (ctx->df_aes_ctx.tfm) {
+ crypto_free_ablkcipher(ctx->df_aes_ctx.tfm);
+ ctx->df_aes_ctx.tfm = NULL;
+ }
+clr_output:
+ if (ctx->aes_ctx.output.virt_addr) {
+ kzfree(ctx->aes_ctx.output.virt_addr);
+ ctx->aes_ctx.output.virt_addr = NULL;
+ }
+clr_input:
+ if (ctx->aes_ctx.input.virt_addr) {
+ kzfree(ctx->aes_ctx.input.virt_addr);
+ ctx->aes_ctx.input.virt_addr = NULL;
+ }
+clr_req:
+ if (ctx->aes_ctx.req) {
+ ablkcipher_request_free(ctx->aes_ctx.req);
+ ctx->aes_ctx.req = NULL;
+ }
+clr_tfm:
+ if (ctx->aes_ctx.tfm) {
+ crypto_free_ablkcipher(ctx->aes_ctx.tfm);
+ ctx->aes_ctx.tfm = NULL;
+ }
+out:
+ return status;
+}
+
+/*
+ * Increments the V field in *ctx
+ */
+static void increment_V(struct ctr_drbg_ctx_s *ctx)
+{
+ unsigned sum = 1;
+ int i;
+ uint8_t *p = &ctx->seed.key_V.V[0];
+
+ /*
+ * To make known answer tests work, this has to be done big_endian.
+ * So we just do it by bytes.
+ * since we are using AES-128, the key size is 16 bytes.
+ */
+ for (i = 15; sum != 0 && i >= 0; --i) {
+ sum += p[i];
+ p[i] = (sum & 0xff);
+ sum >>= 8;
+ }
+
+ return;
+}
+
+/*
+ * The NIST update function. It updates the key and V to new values
+ * (to prevent backtracking) and optionally stirs in data. data may
+ * be null, otherwise *data is from 0 to 256 bits long.
+ * keysched is an optional keyschedule to use as an optimization. It
+ * must be consistent with the key in *ctx. No changes are made to
+ * *ctx until it is assured that there will be no failures. Note that
+ * data_len is in bytes. (That may not be offical NIST
+ * recommendation, but I do it anyway; they say "or equivalent" and
+ * this is equivalent enough.)
+ */
+static enum ctr_drbg_status_t
+update(struct ctr_drbg_ctx_s *ctx, const uint8_t *data, size_t data_len)
+{
+ uint8_t temp[32];
+ unsigned int i;
+ int rc;
+ struct scatterlist sg_in, sg_out;
+
+ for (i = 0; i < 2; ++i) {
+ increment_V(ctx);
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ /*
+ * Note: personalize these called routines for
+ * specific testing.
+ */
+ memcpy(ctx->aes_ctx.input.virt_addr,
+ ctx->seed.key_V.V,
+ CTR_DRBG_BLOCK_LEN_BYTES);
+
+ crypto_ablkcipher_clear_flags(ctx->aes_ctx.tfm, ~0);
+
+ /* Encrypt some clear text! */
+
+ sg_init_one(&sg_in,
+ ctx->aes_ctx.input.virt_addr,
+ AES128_BLOCK_SIZE);
+ sg_init_one(&sg_out,
+ ctx->aes_ctx.output.virt_addr,
+ AES128_BLOCK_SIZE);
+ ablkcipher_request_set_crypt(ctx->aes_ctx.req,
+ &sg_in,
+ &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES,
+ NULL);
+
+ rc = crypto_ablkcipher_encrypt(ctx->aes_ctx.req);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->aes_ctx.result.completion);
+ if (!rc && !ctx->aes_ctx.result.err) {
+ INIT_COMPLETION(ctx->aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ pr_debug("crypto_ablkcipher_encrypt returned");
+ pr_debug(" with %d result %d on iteration\n",
+ rc,
+ ctx->aes_ctx.result.err);
+ break;
+ }
+
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ memcpy(temp + AES128_BLOCK_SIZE * i,
+ ctx->aes_ctx.output.virt_addr,
+ AES128_BLOCK_SIZE);
+ }
+
+ if (data_len > 0)
+ pr_debug("in upadte, data_len = %zu\n", data_len);
+
+ for (i = 0; i < data_len; ++i)
+ ctx->seed.as_bytes[i] = temp[i] ^ data[i];
+
+ /* now copy the rest of temp to key and V */
+ if (32 > data_len) {
+ memcpy(ctx->seed.as_bytes + data_len,
+ temp + data_len,
+ 32 - data_len);
+ }
+
+ memset(temp, 0, 32);
+ return CTR_DRBG_SUCCESS;
+}
+
+/*
+ * Reseeds the CTR_DRBG instance with entropy. entropy_len_bits must
+ * be exactly 256.
+ */
+enum ctr_drbg_status_t ctr_drbg_reseed(struct ctr_drbg_ctx_s *ctx,
+ const void *entropy,
+ size_t entropy_len_bits)
+{
+ enum ctr_drbg_status_t update_rv;
+ uint8_t seed_material[32];
+ int rc;
+
+ if (ctx == NULL || entropy == NULL)
+ return CTR_DRBG_INVALID_ARG;
+
+ update_rv = block_cipher_df(ctx,
+ (uint8_t *)entropy,
+ (entropy_len_bits / 8),
+ seed_material,
+ 32
+ );
+ if (CTR_DRBG_SUCCESS != update_rv) {
+ memset(seed_material, 0, 32);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+
+ rc = crypto_ablkcipher_setkey(ctx->aes_ctx.tfm,
+ ctx->seed.key_V.key,
+ AES128_KEY_SIZE
+ );
+ if (rc) {
+ memset(seed_material, 0, 32);
+ pr_debug("set-key in Instantiate failed, returns with %d", rc);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+
+ pr_debug("ctr_drbg_reseed, to call update\n");
+ update_rv = update(ctx, (const uint8_t *)seed_material, 32);
+ pr_debug("ctr_drbg_reseed, after called update\n");
+ if (update_rv != CTR_DRBG_SUCCESS) {
+ memset(seed_material, 0, 32);
+ return update_rv;
+ }
+ ctx->reseed_counter = 1; /* think 0 but SP 800-90 says 1 */
+
+ memset(seed_material, 0, 32);
+
+ return CTR_DRBG_SUCCESS;
+
+}
+
+/*
+ * The NIST instantiate function. entropy_len_bits must be exactly
+ * 256. After reseed_interval generate requests, generated requests
+ * will fail until the CTR_DRBG instance is reseeded. As per NIST SP
+ * 800-90, an error is returned if reseed_interval > 2^48.
+ */
+
+enum ctr_drbg_status_t
+ctr_drbg_instantiate(struct ctr_drbg_ctx_s *ctx,
+ const uint8_t *entropy,
+ size_t entropy_len_bits,
+ const uint8_t *nonce,
+ size_t nonce_len_bits,
+ unsigned long long reseed_interval)
+{
+
+ enum ctr_drbg_status_t update_rv;
+ uint8_t seed_material[32];
+ uint8_t df_input[32];
+ int rc;
+
+ if (ctx == NULL || entropy == NULL || nonce == NULL)
+ return CTR_DRBG_INVALID_ARG;
+ if (((nonce_len_bits / 8) + (entropy_len_bits / 8)) > 32) {
+ pr_info("\nentropy_len_bits + nonce_len_bits is too long!");
+ pr_info("\nnonce len: %zu, entropy: %zu\n",
+ nonce_len_bits, entropy_len_bits);
+ return CTR_DRBG_INVALID_ARG + 1;
+ }
+
+ if (reseed_interval > (1ULL << 48))
+ return CTR_DRBG_INVALID_ARG + 2;
+
+ ctr_aes_init(ctx);
+
+ memset(ctx->seed.as_bytes, 0, sizeof(ctx->seed.as_bytes));
+ memcpy(df_input, (uint8_t *)entropy, entropy_len_bits / 8);
+ memcpy(df_input + (entropy_len_bits / 8), nonce, nonce_len_bits / 8);
+
+ update_rv = block_cipher_df(ctx, df_input, 24, seed_material, 32);
+ memset(df_input, 0, 32);
+
+ if (CTR_DRBG_SUCCESS != update_rv) {
+ pr_debug("block_cipher_df failed, returns %d", update_rv);
+ memset(seed_material, 0, 32);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+
+ rc = crypto_ablkcipher_setkey(ctx->aes_ctx.tfm,
+ ctx->seed.key_V.key,
+ AES128_KEY_SIZE);
+ if (rc) {
+ pr_debug("crypto_ablkcipher_setkey API failed: %d", rc);
+ memset(seed_material, 0, 32);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+ update_rv = update(ctx, (const uint8_t *)seed_material, 32);
+ if (update_rv != CTR_DRBG_SUCCESS) {
+ memset(seed_material, 0, 32);
+ return update_rv;
+ }
+
+ ctx->reseed_counter = 1; /* think 0 but SP 800-90 says 1 */
+ ctx->reseed_interval = reseed_interval;
+
+ memset(seed_material, 0, 32);
+
+ pr_debug(" return from ctr_drbg_instantiate\n");
+
+ return CTR_DRBG_SUCCESS;
+}
+
+/*
+ * Generate random bits. len_bits is specified in bits, as required by
+ * NIST SP800-90. It fails with CTR_DRBG_NEEDS_RESEED if the number
+ * of generates since instantiation or the last reseed >= the
+ * reseed_interval supplied at instantiation. len_bits must be a
+ * multiple of 8. len_bits must not exceed 2^19, as per NIST SP
+ * 800-90. Optionally stirs in additional_input which is
+ * additional_input_len_bits long, and is silently rounded up to a
+ * multiple of 8. CTR_DRBG_INVALID_ARG is returned if any pointer arg
+ * is null and the corresponding length is non-zero or if
+ * additioanl_input_len_bits > 256.
+ */
+enum ctr_drbg_status_t
+ctr_drbg_generate_w_data(struct ctr_drbg_ctx_s *ctx,
+ void *additional_input,
+ size_t additional_input_len_bits,
+ void *buffer,
+ size_t len_bits)
+{
+ size_t total_blocks = (len_bits + 127) / 128;
+ enum ctr_drbg_status_t update_rv;
+ int rv = 0;
+ size_t i;
+ int rc;
+ struct scatterlist sg_in, sg_out;
+
+ if (ctx == NULL)
+ return CTR_DRBG_INVALID_ARG;
+ if (buffer == NULL && len_bits > 0)
+ return CTR_DRBG_INVALID_ARG;
+ if (len_bits % 8 != 0)
+ return CTR_DRBG_INVALID_ARG;
+ if (len_bits > (1<<19))
+ return CTR_DRBG_INVALID_ARG;
+
+ if ((additional_input == NULL && additional_input_len_bits > 0) ||
+ additional_input_len_bits > CTR_DRBG_SEED_LEN_BITS)
+ return CTR_DRBG_INVALID_ARG;
+ if (ctx->reseed_counter > ctx->reseed_interval)
+ return CTR_DRBG_NEEDS_RESEED;
+
+ rc = crypto_ablkcipher_setkey(ctx->aes_ctx.tfm,
+ ctx->seed.key_V.key,
+ AES128_KEY_SIZE);
+ if (rc) {
+ pr_debug("crypto_ablkcipher_setkey API failed: %d", rc);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+ if (rv < 0)
+ return CTR_DRBG_GENERAL_ERROR;
+
+ if (!ctx->continuous_test_started) {
+ increment_V(ctx);
+ init_completion(&ctx->aes_ctx.result.completion);
+ crypto_ablkcipher_clear_flags(ctx->aes_ctx.tfm, ~0);
+ memcpy(ctx->aes_ctx.input.virt_addr, ctx->seed.key_V.V, 16);
+ sg_init_one(&sg_in, ctx->aes_ctx.input.virt_addr, 16);
+ sg_init_one(&sg_out, ctx->aes_ctx.output.virt_addr, 16);
+ ablkcipher_request_set_crypt(ctx->aes_ctx.req, &sg_in, &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES, NULL);
+ rc = crypto_ablkcipher_encrypt(ctx->aes_ctx.req);
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->aes_ctx.result.completion);
+ if (!rc && !ctx->aes_ctx.result.err) {
+ INIT_COMPLETION(ctx->aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ pr_debug(":crypto_ablkcipher_encrypt returned with %d result %d on iteration\n",
+ rc,
+ ctx->aes_ctx.result.err);
+ break;
+ }
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ memcpy(ctx->prev_drn, ctx->aes_ctx.output.virt_addr, 16);
+ ctx->continuous_test_started = 1;
+ }
+
+ /* Generate the output */
+ for (i = 0; i < total_blocks; ++i) {
+ /* Increment the counter */
+ increment_V(ctx);
+ if (((len_bits % 128) != 0) && (i == (total_blocks - 1))) {
+ /* last block and it's a fragment */
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ /*
+ * Note: personalize these called routines for
+ * specific testing.
+ */
+
+ crypto_ablkcipher_clear_flags(ctx->aes_ctx.tfm, ~0);
+
+ /* Encrypt some clear text! */
+
+ memcpy(ctx->aes_ctx.input.virt_addr,
+ ctx->seed.key_V.V,
+ 16);
+ sg_init_one(&sg_in,
+ ctx->aes_ctx.input.virt_addr,
+ 16);
+ sg_init_one(&sg_out,
+ ctx->aes_ctx.output.virt_addr,
+ 16);
+ ablkcipher_request_set_crypt(ctx->aes_ctx.req,
+ &sg_in,
+ &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES,
+ NULL);
+
+ rc = crypto_ablkcipher_encrypt(ctx->aes_ctx.req);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->aes_ctx.result.completion);
+ if (!rc && !ctx->aes_ctx.result.err) {
+ INIT_COMPLETION(
+ ctx->aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ if (!memcmp(ctx->prev_drn,
+ ctx->aes_ctx.output.virt_addr,
+ 16))
+ return CTR_DRBG_GENERAL_ERROR;
+ else
+ memcpy(ctx->prev_drn,
+ ctx->aes_ctx.output.virt_addr,
+ 16);
+ rv = 0;
+ memcpy((uint8_t *)buffer + 16*i,
+ ctx->aes_ctx.output.virt_addr,
+ (len_bits % 128)/8);
+ } else {
+ /* normal case: encrypt direct to target buffer */
+
+ init_completion(&ctx->aes_ctx.result.completion);
+
+ /*
+ * Note: personalize these called routines for
+ * specific testing.
+ */
+
+ crypto_ablkcipher_clear_flags(ctx->aes_ctx.tfm, ~0);
+
+ /* Encrypt some clear text! */
+
+ memcpy(ctx->aes_ctx.input.virt_addr,
+ ctx->seed.key_V.V,
+ 16);
+ sg_init_one(&sg_in,
+ ctx->aes_ctx.input.virt_addr,
+ 16);
+ sg_init_one(&sg_out,
+ ctx->aes_ctx.output.virt_addr,
+ 16);
+ ablkcipher_request_set_crypt(ctx->aes_ctx.req,
+ &sg_in,
+ &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES,
+ NULL);
+
+ rc = crypto_ablkcipher_encrypt(ctx->aes_ctx.req);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->aes_ctx.result.completion);
+ if (!rc && !ctx->aes_ctx.result.err) {
+ INIT_COMPLETION(
+ ctx->aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+
+ if (!memcmp(ctx->prev_drn,
+ ctx->aes_ctx.output.virt_addr,
+ 16))
+ return CTR_DRBG_GENERAL_ERROR;
+ else
+ memcpy(ctx->prev_drn,
+ ctx->aes_ctx.output.virt_addr,
+ 16);
+
+ memcpy((uint8_t *)buffer + 16*i,
+ ctx->aes_ctx.output.virt_addr,
+ 16);
+ rv = 0;
+ }
+ }
+
+ update_rv = update(ctx,
+ additional_input,
+ (additional_input_len_bits + 7) / 8); /* round up */
+ if (update_rv != CTR_DRBG_SUCCESS)
+ return update_rv;
+
+ ctx->reseed_counter += 1;
+
+ return CTR_DRBG_SUCCESS;
+}
+
+/*
+ * Generate random bits, but with no provided data. See notes on
+ * ctr_drbg_generate_w_data()
+ */
+enum ctr_drbg_status_t
+ctr_drbg_generate(struct ctr_drbg_ctx_s *ctx,
+ void *buffer,
+ size_t len_bits)
+
+{
+ return ctr_drbg_generate_w_data(ctx, NULL, 0, buffer, len_bits);
+}
+
+void ctr_aes_deinit(struct ctr_drbg_ctx_s *ctx)
+{
+ if (ctx->aes_ctx.req) {
+ ablkcipher_request_free(ctx->aes_ctx.req);
+ ctx->aes_ctx.req = NULL;
+ }
+ if (ctx->aes_ctx.tfm) {
+ crypto_free_ablkcipher(ctx->aes_ctx.tfm);
+ ctx->aes_ctx.tfm = NULL;
+ }
+ if (ctx->aes_ctx.input.virt_addr) {
+ kzfree(ctx->aes_ctx.input.virt_addr);
+ ctx->aes_ctx.input.virt_addr = NULL;
+ }
+ if (ctx->aes_ctx.output.virt_addr) {
+ kzfree(ctx->aes_ctx.output.virt_addr);
+ ctx->aes_ctx.output.virt_addr = NULL;
+ }
+ if (ctx->df_aes_ctx.req) {
+ ablkcipher_request_free(ctx->df_aes_ctx.req);
+ ctx->df_aes_ctx.req = NULL;
+ }
+ if (ctx->df_aes_ctx.tfm) {
+ crypto_free_ablkcipher(ctx->df_aes_ctx.tfm);
+ ctx->df_aes_ctx.tfm = NULL;
+ }
+ if (ctx->df_aes_ctx.input.virt_addr) {
+ kzfree(ctx->df_aes_ctx.input.virt_addr);
+ ctx->df_aes_ctx.input.virt_addr = NULL;
+ }
+ if (ctx->df_aes_ctx.output.virt_addr) {
+ kzfree(ctx->df_aes_ctx.output.virt_addr);
+ ctx->df_aes_ctx.output.virt_addr = NULL;
+ }
+
+}
+
+/*
+ * Zeroizes the context structure. In some future implemenation it
+ * could also free resources. So do call it.
+ */
+void
+ctr_drbg_uninstantiate(struct ctr_drbg_ctx_s *ctx)
+{
+ ctr_aes_deinit(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+/*
+ * the derivation functions to handle biased entropy input.
+ */
+enum ctr_drbg_status_t df_bcc_func(struct ctr_drbg_ctx_s *ctx,
+ uint8_t *key,
+ uint8_t *input,
+ uint32_t input_size,
+ uint8_t *output)
+{
+ enum ctr_drbg_status_t ret_val = CTR_DRBG_SUCCESS;
+ uint8_t *p;
+ int rc;
+ int i;
+ int n;
+ struct scatterlist sg_in, sg_out;
+
+ if (0 != (input_size % CTR_DRBG_BLOCK_LEN_BYTES))
+ return CTR_DRBG_INVALID_ARG;
+
+ n = input_size / CTR_DRBG_BLOCK_LEN_BYTES;
+
+ for (i = 0; i < CTR_DRBG_BLOCK_LEN_BYTES; i++)
+ ctx->df_aes_ctx.output.virt_addr[i] = 0;
+
+ rc = crypto_ablkcipher_setkey(ctx->df_aes_ctx.tfm,
+ key,
+ AES128_KEY_SIZE);
+ if (rc) {
+ pr_debug("crypto_ablkcipher_setkey API failed: %d\n", rc);
+ return CTR_DRBG_GENERAL_ERROR;
+ }
+
+ p = input;
+ while (n > 0) {
+ for (i = 0; i < CTR_DRBG_BLOCK_LEN_BYTES; i++, p++)
+ ctx->df_aes_ctx.input.virt_addr[i] =
+ ctx->df_aes_ctx.output.virt_addr[i] ^ (*p);
+
+ init_completion(&ctx->df_aes_ctx.result.completion);
+
+ /*
+ * Note: personalize these called routines for
+ * specific testing.
+ */
+
+ crypto_ablkcipher_clear_flags(ctx->df_aes_ctx.tfm, ~0);
+
+ /* Encrypt some clear text! */
+
+ sg_init_one(&sg_in, ctx->df_aes_ctx.input.virt_addr, 16);
+ sg_init_one(&sg_out, ctx->df_aes_ctx.output.virt_addr, 16);
+
+ ablkcipher_request_set_crypt(ctx->df_aes_ctx.req,
+ &sg_in,
+ &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES,
+ NULL);
+
+ rc = crypto_ablkcipher_encrypt(ctx->df_aes_ctx.req);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->df_aes_ctx.result.completion);
+ if (!rc && !ctx->df_aes_ctx.result.err) {
+ INIT_COMPLETION(
+ ctx->df_aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+
+ init_completion(&ctx->df_aes_ctx.result.completion);
+ n--;
+ }
+
+ for (i = 0; i < CTR_DRBG_BLOCK_LEN_BYTES; i++)
+ output[i] = ctx->df_aes_ctx.output.virt_addr[i];
+
+ return ret_val;
+}
+
+/* output_size must <= 512 bits (<= 64) */
+enum ctr_drbg_status_t
+block_cipher_df(struct ctr_drbg_ctx_s *ctx,
+ const uint8_t *input,
+ uint32_t input_size,
+ uint8_t *output,
+ uint32_t output_size)
+{
+ enum ctr_drbg_status_t ret_val = CTR_DRBG_SUCCESS;
+ uint32_t s_len = 0;
+ uint32_t s_pad_len = 0;
+ uint8_t temp[32];
+ uint32_t out_len = 0;
+ uint8_t siv_string[64];
+ uint8_t *p_s_string = NULL;
+ int rc;
+ struct scatterlist sg_in, sg_out;
+
+ if (output_size > 64)
+ return CTR_DRBG_INVALID_ARG;
+
+ s_len = input_size + 9;
+
+ s_pad_len = s_len % 16;
+
+ if (0 != s_pad_len)
+ s_len += (16 - s_pad_len);
+
+ /* add the length of IV */
+ s_len += 16;
+
+ if (s_len > 64)
+ pr_debug("error! s_len is too big!!!!!!!!!!!!\n");
+
+ memset(siv_string, 0, 64);
+
+ p_s_string = siv_string + 16;
+
+ p_s_string[3] = input_size;
+ p_s_string[7] = output_size;
+ memcpy(p_s_string + 8, input, input_size);
+ p_s_string[8 + input_size] = 0x80;
+ if (0 < s_pad_len)
+ memset(p_s_string + 9 + input_size, '\0', s_pad_len);
+
+ ret_val = df_bcc_func(ctx, df_initial_k, siv_string, s_len, temp);
+
+ if (CTR_DRBG_SUCCESS != ret_val) {
+ pr_debug("df_bcc_func failed, returned %d", ret_val);
+ goto out;
+ }
+
+ siv_string[3] = 0x1;
+ ret_val = df_bcc_func(ctx, df_initial_k, siv_string, s_len, temp + 16);
+
+ if (CTR_DRBG_SUCCESS != ret_val)
+ goto out;
+
+ out_len = 0;
+ rc = crypto_ablkcipher_setkey(ctx->df_aes_ctx.tfm,
+ temp,
+ AES128_KEY_SIZE);
+ if (rc) {
+ pr_debug("crypto_ablkcipher_setkey API failed: %d", rc);
+ goto out;
+ }
+ memcpy(ctx->df_aes_ctx.input.virt_addr, temp + 16, 16);
+
+ while (out_len < output_size) {
+
+ init_completion(&ctx->df_aes_ctx.result.completion);
+
+ /*
+ * Note: personalize these called routines for
+ * specific testing.
+ */
+
+ crypto_ablkcipher_clear_flags(ctx->df_aes_ctx.tfm, ~0);
+
+ /* Encrypt some clear text! */
+
+ sg_init_one(&sg_in, ctx->df_aes_ctx.input.virt_addr, 16);
+ sg_init_one(&sg_out, ctx->df_aes_ctx.output.virt_addr, 16);
+ ablkcipher_request_set_crypt(ctx->df_aes_ctx.req,
+ &sg_in,
+ &sg_out,
+ CTR_DRBG_BLOCK_LEN_BYTES,
+ NULL);
+
+ rc = crypto_ablkcipher_encrypt(ctx->df_aes_ctx.req);
+
+ switch (rc) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ rc = wait_for_completion_interruptible(
+ &ctx->df_aes_ctx.result.completion);
+ if (!rc && !ctx->df_aes_ctx.result.err) {
+ INIT_COMPLETION(
+ ctx->df_aes_ctx.result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+
+
+ init_completion(&ctx->df_aes_ctx.result.completion);
+
+ memcpy(output + out_len, ctx->df_aes_ctx.output.virt_addr, 16);
+ memcpy(ctx->df_aes_ctx.input.virt_addr, output + out_len, 16);
+ out_len += 16;
+ }
+
+out:
+ memset(siv_string, 0, 64);
+ memset(temp, 0, 32);
+ return ret_val;
+}
+
diff --git a/drivers/char/hw_random/ctr_drbg.h b/drivers/char/hw_random/ctr_drbg.h
new file mode 100644
index 0000000..55a9988
--- /dev/null
+++ b/drivers/char/hw_random/ctr_drbg.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MSM_CTR_DRBG_H__
+#define __MSM_CTR_DRBG_H__
+
+/* This is the module that is actually follows the details of NIST SP
+ * 800-90 so it can claim to use a FIPS-approved algorithm.
+ */
+
+/* Added ctr_drbg_generate_w_data which supplies
+ * additional input to the generate operation.
+ */
+
+
+#define CTR_DRBG_MAX_REQ_LEN_BITS (1 << 19)
+#define CTR_DRBG_SEED_LEN_BITS 256
+#define CTR_DRBG_BLOCK_LEN_BITS 128
+#define CTR_DRBG_BLOCK_LEN_BYTES (CTR_DRBG_BLOCK_LEN_BITS/8)
+#define CTR_DRBG_MAX_RESEED_INTERVAL (1ULL << 48)
+
+#define MSM_AES128_BLOCK_SIZE (16)
+#define MSM_ENTROPY_BUFFER_SIZE (16)
+#define MSM_NONCE_BUFFER_SIZE (8)
+
+enum ctr_drbg_status_t {
+ CTR_DRBG_SUCCESS = 0,
+ CTR_DRBG_NEEDS_RESEED,
+ CTR_DRBG_INVALID_ARG,
+ CTR_DRBG_GENERAL_ERROR = 0xFF,
+};
+
+union ctr_drbg_seed_t {
+ uint8_t as_bytes[32];
+ uint32_t as_words[8];
+ uint64_t as_64[4];
+ struct {
+ uint8_t key[16];
+ uint8_t V[16];
+ } key_V;
+};
+
+struct msm_ctr_tcrypt_result_s {
+ struct completion completion;
+ int err;
+};
+
+struct msm_ctr_buffer_s {
+ unsigned char *virt_addr;
+};
+
+struct aes_struct_s {
+ struct crypto_ablkcipher *tfm;
+ struct ablkcipher_request *req;
+ struct msm_ctr_buffer_s input;
+ struct msm_ctr_buffer_s output;
+ struct msm_ctr_tcrypt_result_s result;
+};
+
+struct ctr_drbg_ctx_s {
+ unsigned long long reseed_counter; /* starts at 1 as per SP
+ * 800-90
+ */
+ unsigned long long reseed_interval;
+ union ctr_drbg_seed_t seed;
+ struct aes_struct_s aes_ctx;
+ struct aes_struct_s df_aes_ctx;
+ uint8_t prev_drn[MSM_AES128_BLOCK_SIZE];
+ uint8_t continuous_test_started;
+};
+
+enum ctr_drbg_status_t ctr_drbg_instantiate(struct ctr_drbg_ctx_s *ctx,
+ const uint8_t *entropy,
+ size_t entropy_len_bits,
+ const uint8_t *nonce,
+ size_t nonce_len_bits,
+ unsigned long long reseed_interval);
+
+enum ctr_drbg_status_t ctr_drbg_reseed(struct ctr_drbg_ctx_s *ctx,
+ const void *entropy,
+ size_t entropy_len);
+
+enum ctr_drbg_status_t ctr_drbg_generate_w_data(struct ctr_drbg_ctx_s *ctx,
+ void *additional_input,
+ size_t additional_input_len_bits,
+ void *buffer,
+ size_t len_bits);
+
+enum ctr_drbg_status_t ctr_drbg_generate(struct ctr_drbg_ctx_s *ctx,
+ void *buffer,
+ size_t len);
+
+void ctr_drbg_uninstantiate(struct ctr_drbg_ctx_s *ctx);
+
+enum ctr_drbg_status_t block_cipher_df(struct ctr_drbg_ctx_s *ctx,
+ const uint8_t *input,
+ uint32_t input_size,
+ uint8_t *output,
+ uint32_t output_size
+ );
+void ctr_aes_deinit(struct ctr_drbg_ctx_s *ctx);
+
+#endif /* __MSM_CTR_DRBG_H__ */
diff --git a/drivers/char/hw_random/fips_drbg.c b/drivers/char/hw_random/fips_drbg.c
new file mode 100644
index 0000000..7b4225e
--- /dev/null
+++ b/drivers/char/hw_random/fips_drbg.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <mach/msm_bus.h>
+#include <linux/qrng.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/string.h>
+
+#include "msm_rng.h"
+#include "fips_drbg.h"
+
+/* The fips-140 random number generator is a wrapper around the CTR_DRBG
+ * random number generator, which is built according to the
+ * specifications in NIST SP 800-90 using AES-128.
+ *
+ * This wrapper has the following functionality
+ * a. Entropy collection is via a callback.
+ * b. A failure of CTR_DRBG because reseeding is needed invisibly
+ * causes the underlying CTR_DRBG instance to be reseeded with
+ * new random data and then the generate request is retried.
+ * c. Limitations in CTR_DRBG (like not allowed more than 65536 bytes
+ * to be genrated in one request) are worked around. At this level
+ * it just works.
+ * d. On success the return value is zero. If the callback was invoked
+ * and returned a non-zero value, that value is returned. On all other
+ * errors -1 is returned.
+ */
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+/* 32 bytes = 256 bits = seed length */
+#define MAGIC 0xab10d161
+
+#define RESEED_INTERVAL (1 << 31)
+
+int get_entropy_callback(void *ctx, void *buf)
+{
+ struct msm_rng_device *msm_rng_dev = (struct msm_rng_device *)ctx;
+ int ret_val = -1;
+
+ if (NULL == ctx)
+ return FIPS140_PRNG_ERR;
+
+ if (NULL == buf)
+ return FIPS140_PRNG_ERR;
+
+ ret_val = msm_rng_direct_read(msm_rng_dev, buf);
+ if ((size_t)ret_val != Q_HW_DRBG_BLOCK_BYTES)
+ return ret_val;
+
+ return 0;
+}
+
+/* Initialize *ctx. Automatically reseed after reseed_interval calls
+ * to fips_drbg_gen. The underlying CTR_DRBG will automatically be
+ * reseeded every reseed_interval requests. Values over
+ * CTR_DRBG_MAX_RESEED_INTERVAL (2^48) or that are zero are silently
+ * converted to CTR_DRBG_MAX_RESEED_INTERVAL. (It is easy to justify
+ * lowering values that are too large to CTR_DRBG_MAX_RESEED_INTERVAL
+ * (the NIST SP800-90 limit): just silently enforcing the rules.
+ * Silently converted 0 to to CTR_DRBG_MAX_RESEED_INTERVAL is harder.
+ * The alternative is to return an error. But since
+ * CTR_DRBG_MAX_RESEED is safe, we relieve the caller of one more
+ * error to worry about.)
+ */
+static int
+do_fips_drbg_init(struct fips_drbg_ctx_s *ctx,
+ get_entropy_callback_t callback,
+ void *callback_ctx,
+ unsigned long long reseed_interval)
+{
+ uint8_t entropy_pool[Q_HW_DRBG_BLOCK_BYTES];
+ enum ctr_drbg_status_t init_rv;
+ int rv = -1;
+
+ if (ctx == NULL)
+ return FIPS140_PRNG_ERR;
+ if (callback == NULL)
+ return FIPS140_PRNG_ERR;
+ if (reseed_interval == 0 ||
+ reseed_interval > CTR_DRBG_MAX_RESEED_INTERVAL)
+ reseed_interval = CTR_DRBG_MAX_RESEED_INTERVAL;
+
+ /* fill in callback related fields in ctx */
+ ctx->get_entropy_callback = callback;
+ ctx->get_entropy_callback_ctx = callback_ctx;
+
+ if (!ctx->fips_drbg_started) {
+ rv = (*ctx->get_entropy_callback)(ctx->get_entropy_callback_ctx,
+ ctx->prev_hw_drbg_block
+ );
+ if (rv != 0)
+ return FIPS140_PRNG_ERR;
+ ctx->fips_drbg_started = 1;
+ }
+
+ rv = (*ctx->get_entropy_callback)(ctx->get_entropy_callback_ctx,
+ entropy_pool
+ );
+ if (rv != 0) {
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+ return FIPS140_PRNG_ERR;
+ }
+
+ if (!memcmp(entropy_pool,
+ ctx->prev_hw_drbg_block,
+ Q_HW_DRBG_BLOCK_BYTES)) {
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+ return FIPS140_PRNG_ERR;
+ } else
+ memcpy(ctx->prev_hw_drbg_block,
+ entropy_pool,
+ Q_HW_DRBG_BLOCK_BYTES);
+
+
+ init_rv = ctr_drbg_instantiate(&ctx->ctr_drbg_ctx,
+ entropy_pool,
+ 8 * MSM_ENTROPY_BUFFER_SIZE,
+ entropy_pool + MSM_ENTROPY_BUFFER_SIZE,
+ 8 * 8,
+ reseed_interval);
+
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+
+ if (init_rv == 0)
+ ctx->magic = MAGIC;
+
+ return 0;
+}
+
+int fips_drbg_init(struct msm_rng_device *msm_rng_ctx)
+{
+ uint32_t ret_val = 0;
+
+ ret_val = do_fips_drbg_init(msm_rng_ctx->drbg_ctx,
+ get_entropy_callback,
+ msm_rng_ctx,
+ RESEED_INTERVAL
+ );
+ if (ret_val != 0)
+ ret_val = FIPS140_PRNG_ERR;
+
+ return ret_val;
+}
+
+/* Push new entropy into the CTR_DRBG instance in ctx, combining
+ * it with the entropy already there. On success, 0 is returned. If
+ * the callback returns a non-zero value, that value is returned.
+ * Other errors return -1.
+ */
+static int
+fips_drbg_reseed(struct fips_drbg_ctx_s *ctx)
+{
+ uint8_t entropy_pool[Q_HW_DRBG_BLOCK_BYTES];
+ int rv;
+ enum ctr_drbg_status_t init_rv;
+
+ if (ctx == NULL)
+ return FIPS140_PRNG_ERR;
+
+ if (!ctx->fips_drbg_started) {
+ rv = (*ctx->get_entropy_callback)(ctx->get_entropy_callback_ctx,
+ ctx->prev_hw_drbg_block
+ );
+ if (rv != 0)
+ return FIPS140_PRNG_ERR;
+ ctx->fips_drbg_started = 1;
+ }
+
+ rv = (*ctx->get_entropy_callback)(ctx->get_entropy_callback_ctx,
+ entropy_pool
+ );
+ if (rv != 0) {
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+ return FIPS140_PRNG_ERR;
+ }
+
+ if (!memcmp(entropy_pool,
+ ctx->prev_hw_drbg_block,
+ Q_HW_DRBG_BLOCK_BYTES)) {
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+ return FIPS140_PRNG_ERR;
+ } else
+ memcpy(ctx->prev_hw_drbg_block,
+ entropy_pool,
+ Q_HW_DRBG_BLOCK_BYTES);
+
+ init_rv = ctr_drbg_reseed(&ctx->ctr_drbg_ctx,
+ entropy_pool,
+ 8 * MSM_ENTROPY_BUFFER_SIZE);
+
+ /* Zeroize the buffer for security. */
+ memset(entropy_pool, 0, Q_HW_DRBG_BLOCK_BYTES);
+
+ return (init_rv == CTR_DRBG_SUCCESS ?
+ FIPS140_PRNG_OK :
+ FIPS140_PRNG_ERR);
+}
+
+/* generate random bytes. len is in bytes On success returns 0. If
+ * the callback returns a non-zero value, that is returned. Other
+ * errors return -1. */
+int
+fips_drbg_gen(struct fips_drbg_ctx_s *ctx, void *tgt, size_t len)
+{
+
+ /* The contorted flow in this function is so that the CTR_DRBG
+ stuff can follow NIST SP 800-90, which has the generate function
+ fail and return a special code if a reseed is needed. We also work
+ around the CTR_DRBG limitation of the maximum request sized being
+ 2^19 bits. */
+
+ enum ctr_drbg_status_t gen_rv;
+ int rv;
+
+ if (ctx == NULL || ctx->magic != MAGIC)
+ return FIPS140_PRNG_ERR;
+ if (tgt == NULL && len > 0)
+ return FIPS140_PRNG_ERR;
+ while (len > 0) {
+ size_t req_len;
+
+ if (len < (CTR_DRBG_MAX_REQ_LEN_BITS / 8))
+ req_len = len;
+ else
+ req_len = CTR_DRBG_MAX_REQ_LEN_BITS / 8;
+
+ gen_rv = ctr_drbg_generate(&ctx->ctr_drbg_ctx,
+ tgt,
+ 8*req_len);
+ switch (gen_rv) {
+ case CTR_DRBG_SUCCESS:
+ tgt = (uint8_t *)tgt + req_len;
+ len -= req_len;
+ break;
+ case CTR_DRBG_NEEDS_RESEED:
+ rv = fips_drbg_reseed(ctx);
+ if (rv != 0)
+ return rv;
+ break;
+ default:
+ return FIPS140_PRNG_ERR;
+ }
+ }
+
+ return 0;
+}
+
+/* free resources and zeroize state */
+void
+fips_drbg_final(struct fips_drbg_ctx_s *ctx)
+{
+ ctr_drbg_uninstantiate(&ctx->ctr_drbg_ctx);
+ ctx->get_entropy_callback = 0;
+ ctx->get_entropy_callback_ctx = 0;
+ ctx->fips_drbg_started = 0;
+ memset(ctx->prev_hw_drbg_block, 0, Q_HW_DRBG_BLOCK_BYTES);
+ ctx->magic = 0;
+}
+
diff --git a/drivers/char/hw_random/fips_drbg.h b/drivers/char/hw_random/fips_drbg.h
new file mode 100644
index 0000000..06da362
--- /dev/null
+++ b/drivers/char/hw_random/fips_drbg.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MSM_FIPS_DRBG_H__
+#define __MSM_FIPS_DRBG_H__
+
+#include "ctr_drbg.h"
+#include "msm_rng.h"
+
+#define FIPS140_PRNG_OK (0)
+#define FIPS140_PRNG_ERR (-1)
+
+typedef int (*get_entropy_callback_t)(void *ctx, void *buf);
+
+struct fips_drbg_ctx_s {
+ uint32_t magic; /* for checking that ctx is likely valid */
+ get_entropy_callback_t get_entropy_callback;
+ void *get_entropy_callback_ctx;
+ struct ctr_drbg_ctx_s ctr_drbg_ctx;
+ uint8_t fips_drbg_started;
+ uint8_t prev_hw_drbg_block[Q_HW_DRBG_BLOCK_BYTES];
+};
+
+/*
+ * initialize *ctx, requesting automatic reseed after reseed_interval
+ * calls to qpsi_rng_gen. callback is a function to get entropy.
+ * callback_ctx is a pointer to any context structure that function
+ * may need. (Pass NULL if no context structure is needed.) callback
+ * must return zero or a positive number on success, and a
+ * negative number on an error.
+ */
+int fips_drbg_init(struct msm_rng_device *msm_rng_ctx);
+
+/* generated random data. Returns 0 on success, -1 on failures */
+int fips_drbg_gen(struct fips_drbg_ctx_s *ctx, void *tgt, size_t len);
+
+
+/* free resources and zeroize state */
+/* Failure to call fips_drbg_final is not a security issue, since
+ CTR_DRBG provides backtracking resistance by updating Key and V
+ immediately after the data has been generated but before the
+ generate function returns. But it is a resource issue (except at
+ program termination), as it abandons a FILE structure and a file
+ descriptor. */
+void fips_drbg_final(struct fips_drbg_ctx_s *ctx);
+
+#endif /* __MSM_FIPS_DRBG_H__ */
diff --git a/drivers/char/hw_random/msm_fips_selftest.c b/drivers/char/hw_random/msm_fips_selftest.c
new file mode 100644
index 0000000..3c23605
--- /dev/null
+++ b/drivers/char/hw_random/msm_fips_selftest.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "fips_drbg.h"
+#include "ctr_drbg.h"
+#include "msm_rng.h"
+#include "msm_fips_selftest.h"
+
+#define CTRAES128_ENTROPY_BYTES (16)
+#define CTRAES128_NONCE_BYTES (8)
+#define CTRAES128_MAX_OUTPUT_BYTES (64)
+
+struct ctr_drbg_testcase_s {
+ char *name;
+ char *entropy_string;
+ char *nonce_string;
+ char *reseed_entropy_string;
+ char *expected_string;
+};
+
+static struct ctr_drbg_testcase_s t0 = {
+ .name = "use_pr_0",
+ .entropy_string = "\x8f\xb9\x57\x3a\x54\x62\x53\xcd"
+ "\xbf\x62\x15\xa1\x80\x5a\x41\x38",
+ .nonce_string = "\x7c\x2c\xe6\x54\x02\xbc\xa6\x83",
+ .reseed_entropy_string = "\xbc\x5a\xd8\x9a\xe1\x8c\x49\x1f"
+ "\x90\xa2\xae\x9e\x7e\x2c\xf9\x9d",
+ .expected_string = "\x07\x62\x82\xe8\x0e\x65\xd7\x70"
+ "\x1a\x35\xb3\x44\x63\x68\xb6\x16"
+ "\xf8\xd9\x62\x23\xb9\xb5\x11\x64"
+ "\x23\xa3\xa2\x32\xc7\x2c\xea\xbf"
+ "\x4a\xcc\xc4\x0a\xc6\x19\xd6\xaa"
+ "\x68\xae\xdb\x8b\x26\x70\xb8\x07"
+ "\xcc\xe9\x9f\xc2\x1b\x8f\xa5\x16"
+ "\xef\x75\xb6\x8f\xc0\x6c\x87\xc7",
+};
+
+static struct ctr_drbg_testcase_s t1 = {
+ .name = "use_pr_1",
+ .entropy_string = "\xa3\x56\xf3\x9a\xce\x48\x59\xb1"
+ "\xe1\x99\x49\x40\x22\x8e\xa4\xeb",
+ .nonce_string = "\xff\x33\xe9\x51\x39\xf7\x67\xf1",
+ .reseed_entropy_string = "\x66\x8f\x0f\xe2\xd8\xa9\xa9\x29"
+ "\x20\xfc\xb9\xf3\x55\xd6\xc3\x4c",
+ .expected_string = "\xa1\x06\x61\x65\x7b\x98\x0f\xac"
+ "\xce\x77\x91\xde\x7f\x6f\xe6\x1e"
+ "\x88\x15\xe5\xe2\x4c\xce\xb8\xa6"
+ "\x63\xf2\xe8\x2f\x5b\xfb\x16\x92"
+ "\x06\x2a\xf3\xa8\x59\x05\xe0\x5a"
+ "\x92\x9a\x07\x65\xc7\x41\x29\x3a"
+ "\x4b\x1d\x15\x3e\x02\x14\x7b\xdd"
+ "\x74\x5e\xbd\x70\x07\x4d\x6c\x08",
+};
+
+static struct ctr_drbg_testcase_s *testlist[] = {
+ &t0, &t1
+};
+
+static int allzeroP(void *p, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i)
+ if (((uint8_t *)p)[i] != 0)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * basic test. return value is error count.
+ */
+int fips_ctraes128_df_known_answer_test(struct ctr_debg_test_inputs_s *tcase)
+{
+ struct ctr_drbg_ctx_s ctx;
+ enum ctr_drbg_status_t rv;
+
+ if (tcase->observed_string_len > CTRAES128_MAX_OUTPUT_BYTES) {
+ pr_debug("known answer test output is bigger than 64!\n");
+ return 1;
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ ctx.continuous_test_started = 1;
+
+ rv = ctr_drbg_instantiate(&ctx,
+ tcase->entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ tcase->nonce_string,
+ 8 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (rv != CTR_DRBG_SUCCESS) {
+ pr_err("test instantiate failed with code %d\n", rv);
+ return 1;
+ }
+
+ rv = ctr_drbg_reseed(&ctx,
+ tcase->reseed_entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES);
+ if (rv != CTR_DRBG_SUCCESS) {
+ pr_err("test reseed failed with code %d\n", rv);
+ return 1;
+ }
+
+ rv = ctr_drbg_generate(&ctx,
+ tcase->observed_string,
+ tcase->observed_string_len * 8);
+ if (rv != CTR_DRBG_SUCCESS) {
+ pr_err("test generate (2) failed with code %d\n", rv);
+ return 1;
+ }
+
+ rv = ctr_drbg_generate(&ctx,
+ tcase->observed_string,
+ tcase->observed_string_len * 8);
+ if (rv != CTR_DRBG_SUCCESS) {
+ pr_err("test generate (2) failed with code %d\n", rv);
+ return 1;
+ }
+
+ ctr_drbg_uninstantiate(&ctx);
+
+ if (!allzeroP(&ctx.seed, sizeof(ctx.seed))) {
+ pr_err("test Final failed to zeroize the context\n");
+ return 1;
+ }
+
+ pr_info("\n DRBG counter test done");
+ return 0;
+
+}
+
+static int fips_drbg_healthcheck_sanitytest(void)
+{
+ struct ctr_drbg_ctx_s *p_ctx = NULL;
+ enum ctr_drbg_status_t rv = CTR_DRBG_SUCCESS;
+ char entropy_string[MSM_ENTROPY_BUFFER_SIZE];
+ char nonce[MSM_NONCE_BUFFER_SIZE];
+ char buffer[32];
+
+ pr_info("start DRBG health check sanity test.\n");
+ p_ctx = kzalloc(sizeof(struct ctr_drbg_ctx_s), GFP_KERNEL);
+ if (NULL == p_ctx) {
+ rv = CTR_DRBG_GENERAL_ERROR;
+ pr_err("p_ctx kzalloc fail\n");
+ goto outbuf;
+ }
+
+ /*
+ * test DRGB Instantiaion function error handling.
+ * Sends a NULL pointer as DTR-DRBG context.
+ */
+ rv = ctr_drbg_instantiate(NULL,
+ entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ nonce,
+ 8 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (CTR_DRBG_SUCCESS == rv) {
+ rv = CTR_DRBG_INVALID_ARG;
+ pr_err("failed to handle NULL pointer of CTR context\n");
+ goto outbuf;
+ }
+
+ /*
+ * test DRGB Instantiaion function error handling.
+ * Sends a NULL pointer as entropy input.
+ */
+ rv = ctr_drbg_instantiate(p_ctx,
+ NULL,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ nonce,
+ 8 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (CTR_DRBG_SUCCESS == rv) {
+ rv = CTR_DRBG_INVALID_ARG;
+ pr_err("failed to handle NULL pointer of entropy string\n");
+ goto outbuf;
+ }
+
+ rv = ctr_drbg_instantiate(p_ctx,
+ entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ NULL,
+ 8 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (CTR_DRBG_SUCCESS == rv) {
+ rv = CTR_DRBG_INVALID_ARG;
+ pr_err("failed to handle NULL pointer of nonce string\n");
+ goto outbuf;
+ }
+
+ /*
+ * test DRGB Instantiaion function error handling.
+ * Sends very long seed length.
+ */
+ rv = ctr_drbg_instantiate(p_ctx,
+ entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ nonce,
+ 32 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (CTR_DRBG_SUCCESS == rv) {
+ rv = CTR_DRBG_INVALID_ARG;
+ pr_err("failed to handle incorrect seed size\n");
+ goto outbuf;
+ }
+
+
+ rv = ctr_drbg_instantiate(p_ctx,
+ entropy_string,
+ 8 * CTRAES128_ENTROPY_BYTES,
+ nonce,
+ 8 * CTRAES128_NONCE_BYTES,
+ 1<<19);
+ if (CTR_DRBG_SUCCESS != rv) {
+ pr_err("Instantiation failed to handle CTR-DRBG instance\n");
+ goto outbuf;
+ }
+
+ /*
+ * test DRGB generator function error handling.
+ * set output string as NULL.
+ */
+ rv = ctr_drbg_generate(p_ctx, NULL, 256);
+ if (CTR_DRBG_SUCCESS == rv) {
+ pr_err("failed to handle incorrect buffer pointer\n");
+ rv = CTR_DRBG_INVALID_ARG;
+ goto outdrbg;
+ }
+
+ rv = ctr_drbg_generate(p_ctx, &buffer, 1 << 20);
+ if (CTR_DRBG_SUCCESS == rv) {
+ pr_err("failed to handle too long output length\n");
+ rv = CTR_DRBG_INVALID_ARG;
+ goto outdrbg;
+ }
+
+ rv = ctr_drbg_generate(p_ctx, &buffer, 177);
+ if (CTR_DRBG_SUCCESS == rv) {
+ pr_err("failed to handle incorrect output length\n");
+ rv = CTR_DRBG_INVALID_ARG;
+ goto outdrbg;
+ }
+
+ pr_info("DRBG health check sanity test passed.\n");
+ rv = CTR_DRBG_SUCCESS;
+
+outdrbg:
+ ctr_drbg_uninstantiate(p_ctx);
+
+outbuf:
+ if (p_ctx)
+ kzfree(p_ctx);
+ p_ctx = NULL;
+
+ memset(buffer, 0, 32);
+ memset(nonce, 0, MSM_NONCE_BUFFER_SIZE);
+ memset(entropy_string, 0, MSM_ENTROPY_BUFFER_SIZE);
+
+ return rv;
+}
+
+int fips_self_test(void)
+{
+ struct ctr_debg_test_inputs_s cavs_input;
+ uint8_t entropy[CTRAES128_ENTROPY_BYTES];
+ uint8_t nonce[CTRAES128_NONCE_BYTES];
+ uint8_t reseed_entropy[CTRAES128_ENTROPY_BYTES];
+ uint8_t expected[CTRAES128_MAX_OUTPUT_BYTES];
+ uint8_t observed[CTRAES128_MAX_OUTPUT_BYTES];
+ unsigned int i;
+ int errors = 0;
+ int ret;
+
+ cavs_input.entropy_string = entropy;
+ cavs_input.nonce_string = nonce;
+ cavs_input.reseed_entropy_string = reseed_entropy;
+ cavs_input.observed_string = observed;
+ cavs_input.observed_string_len = CTRAES128_MAX_OUTPUT_BYTES;
+
+
+ ret = fips_drbg_healthcheck_sanitytest();
+ if (CTR_DRBG_SUCCESS != ret) {
+ pr_err("DRBG health check fail\n");
+ errors++;
+ return errors;
+ }
+
+ for (i = 0;
+ i < sizeof(testlist)/sizeof(struct ctr_drbg_testcase_s *);
+ ++i) {
+ memcpy(entropy,
+ testlist[i]->entropy_string,
+ CTRAES128_ENTROPY_BYTES);
+ memcpy(nonce,
+ testlist[i]->nonce_string,
+ CTRAES128_NONCE_BYTES);
+ memcpy(reseed_entropy,
+ testlist[i]->reseed_entropy_string,
+ CTRAES128_ENTROPY_BYTES);
+ memcpy(expected,
+ testlist[i]->expected_string,
+ CTRAES128_MAX_OUTPUT_BYTES);
+
+ pr_debug("starting test %s\n", testlist[i]->name);
+ ret = fips_ctraes128_df_known_answer_test(&cavs_input);
+ pr_debug("completed test %s\n\n", testlist[i]->name);
+ if (0 != ret) {
+ pr_debug("got error from drbg known answer test!\n");
+ return 1;
+ }
+
+ if (memcmp(expected,
+ cavs_input.observed_string,
+ CTRAES128_MAX_OUTPUT_BYTES) != 0) {
+ errors++;
+ pr_info("%s: generate failed\n", testlist[i]->name);
+ return 1;
+ } else
+ pr_info("%s: generate PASSED!\n", testlist[i]->name);
+ }
+
+ if (errors == 0)
+ pr_debug("All tests passed\n");
+ else
+ pr_debug("%d tests failed\n", errors);
+
+ return errors;
+
+}
+
diff --git a/drivers/char/hw_random/msm_fips_selftest.h b/drivers/char/hw_random/msm_fips_selftest.h
new file mode 100644
index 0000000..090ae01
--- /dev/null
+++ b/drivers/char/hw_random/msm_fips_selftest.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MSM_FIPS_SELFTEST_H__
+#define __MSM_FIPS_SELFTEST_H__
+
+struct ctr_debg_test_inputs_s {
+ char *entropy_string; /* must by 16 bytes */
+ char *nonce_string; /* must be 8 bytes */
+ char *reseed_entropy_string; /* must be 16 bytes */
+ char *observed_string; /* lenth is defined
+ in observed_string_len */
+ int observed_string_len;
+};
+
+int fips_ctraes128_df_known_answer_test(struct ctr_debg_test_inputs_s *tcase);
+
+int fips_self_test(void);
+
+#endif /* __MSM_FIPS_SELFTEST_H__ */
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 4118a7a..e7b9a27 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, The Linux Foundation. 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 version 2 and
@@ -29,6 +29,13 @@
#include <linux/fs.h>
#include <linux/cdev.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+
+#include "msm_rng.h"
+#include "ctr_drbg.h"
+#include "fips_drbg.h"
+#include "msm_fips_selftest.h"
+
#define DRIVER_NAME "msm_rng"
/* Device specific register offsets */
@@ -46,16 +53,34 @@
#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
+/* Global FIPS status */
+#ifdef CONFIG_FIPS_ENABLE
+enum fips_status g_fips140_status = FIPS140_STATUS_FAIL;
+EXPORT_SYMBOL(g_fips140_status);
-struct msm_rng_device {
- struct platform_device *pdev;
- void __iomem *base;
- struct clk *prng_clk;
- uint32_t qrng_perf_client;
+#else
+enum fips_status g_fips140_status = FIPS140_STATUS_NA;
+EXPORT_SYMBOL(g_fips140_status);
+
+#endif
+
+/*FIPS140-2 call back for DRBG self test */
+void *drbg_call_back;
+EXPORT_SYMBOL(drbg_call_back);
+
+
+
+enum {
+ FIPS_NOT_STARTED = 0,
+ DRBG_FIPS_STARTED
};
struct msm_rng_device msm_rng_device_info;
+#ifdef CONFIG_FIPS_ENABLE
+static int fips_mode_enabled = FIPS_NOT_STARTED;
+#endif
+
static long msm_rng_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -77,28 +102,24 @@
return ret;
}
-static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+/*
+ *
+ * This function calls hardware random bit generator directory and retuns it
+ * back to caller
+ *
+ */
+int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev, void *data)
{
- struct msm_rng_device *msm_rng_dev;
struct platform_device *pdev;
void __iomem *base;
- size_t maxsize;
size_t currsize = 0;
unsigned long val;
unsigned long *retdata = data;
int ret;
- msm_rng_dev = (struct msm_rng_device *)rng->priv;
pdev = msm_rng_dev->pdev;
base = msm_rng_dev->base;
- /* calculate max size bytes to transfer back to caller */
- maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
-
- /* no room for word data */
- if (maxsize < 4)
- return 0;
-
/* enable PRNG clock */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
@@ -120,6 +141,74 @@
*(retdata++) = val;
currsize += 4;
+ } while (currsize < Q_HW_DRBG_BLOCK_BYTES);
+
+ /* vote to turn off clock */
+ clk_disable_unprepare(msm_rng_dev->prng_clk);
+
+ val = 0L;
+ return currsize;
+
+}
+
+static int msm_rng_drbg_read(struct hwrng *rng,
+ void *data, size_t max, bool wait)
+{
+ struct msm_rng_device *msm_rng_dev;
+ struct platform_device *pdev;
+ void __iomem *base;
+ size_t maxsize;
+ size_t currsize = 0;
+ unsigned long val;
+ unsigned long *retdata = data;
+ int ret, ret1;
+
+ msm_rng_dev = (struct msm_rng_device *)rng->priv;
+ pdev = msm_rng_dev->pdev;
+ base = msm_rng_dev->base;
+
+
+ down(&msm_rng_dev->drbg_sem);
+
+ /* calculate max size bytes to transfer back to caller */
+ maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
+
+ /* no room for word data */
+ if (maxsize < 4)
+ return 0;
+
+ /* read random data from CTR-AES based DRBG */
+ if (FIPS140_DRBG_ENABLED == msm_rng_dev->fips140_drbg_enabled) {
+ ret1 = fips_drbg_gen(msm_rng_dev->drbg_ctx, data, maxsize);
+ if (FIPS140_PRNG_ERR == ret1)
+ panic("random number generator generator error.\n");
+ } else
+ ret1 = 1;
+
+ /* read random data from h/w */
+ /* enable PRNG clock */
+ ret = clk_prepare_enable(msm_rng_dev->prng_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock in callback\n");
+ up(&msm_rng_dev->drbg_sem);
+ return 0;
+ }
+ /* read random data from h/w */
+ do {
+ /* check status bit if data is available */
+ if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001))
+ break; /* no data to read so just bail */
+
+ /* read FIFO */
+ val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
+ if (!val)
+ break; /* no data to read so just bail */
+
+ /* write data back to callers pointer */
+ if (0 != ret1)
+ *(retdata++) = val;
+ currsize += 4;
+
/* make sure we stay on 32bit boundary */
if ((maxsize - currsize) < 4)
break;
@@ -127,9 +216,112 @@
/* vote to turn off clock */
clk_disable_unprepare(msm_rng_dev->prng_clk);
+ up(&msm_rng_dev->drbg_sem);
+
return currsize;
}
+#ifdef CONFIG_FIPS_ENABLE
+static void _fips_drbg_init_error(struct msm_rng_device *msm_rng_dev)
+{
+ unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
+ clk_put(msm_rng_dev->prng_clk);
+ iounmap(msm_rng_dev->base);
+ kzfree(msm_rng_dev->drbg_ctx);
+ kzfree(msm_rng_dev);
+ panic("software random number generator initialization error.\n");
+}
+#else
+static inline void _fips_drbg_init_error(struct msm_rng_device *msm_rng_dev)
+{
+ return;
+}
+
+#endif
+
+#ifdef CONFIG_FIPS_ENABLE
+int _do_msm_fips_drbg_init(void *rng_dev)
+{
+ struct msm_rng_device *msm_rng_dev = (struct msm_rng_device *) rng_dev;
+
+ int ret;
+
+ if (NULL == msm_rng_dev)
+ return 1;
+
+ ret = fips_drbg_init(msm_rng_dev);
+ if (0 == ret) {
+ pr_debug("start fips self test\n");
+ ret = fips_self_test();
+ if (ret) {
+ msm_rng_dev->fips140_drbg_enabled =
+ FIPS140_DRBG_DISABLED;
+ _fips_drbg_init_error(msm_rng_dev);
+ } else {
+ msm_rng_dev->fips140_drbg_enabled =
+ FIPS140_DRBG_ENABLED;
+ }
+ } else {
+ msm_rng_dev->fips140_drbg_enabled = FIPS140_DRBG_DISABLED;
+ _fips_drbg_init_error(msm_rng_dev);
+ }
+
+ return ret;
+}
+#else
+int _do_msm_fips_drbg_init(void *rng_dev)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_FIPS_ENABLE
+static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ struct msm_rng_device *msm_rng_dev = (struct msm_rng_device *)rng->priv;
+ unsigned char a[Q_HW_DRBG_BLOCK_BYTES];
+ int read_size;
+ unsigned char *p = data;
+
+ switch (fips_mode_enabled) {
+ case DRBG_FIPS_STARTED:
+ return msm_rng_drbg_read(rng, data, max, wait);
+ break;
+ case FIPS_NOT_STARTED:
+ if (g_fips140_status != FIPS140_STATUS_PASS) {
+ do {
+ read_size = msm_rng_direct_read(msm_rng_dev, a);
+ if (read_size <= 0)
+ break;
+ if ((max - read_size > 0)) {
+ memcpy(p, a, read_size);
+ p += read_size;
+ max -= read_size;
+ } else {
+ memcpy(p, a, max);
+ break;
+ }
+ } while (1);
+ return p - (unsigned char *)data;
+ } else {
+ fips_mode_enabled = DRBG_FIPS_STARTED;
+ return msm_rng_drbg_read(rng, data, max, wait);
+ }
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return 0;
+}
+#else
+static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+ return msm_rng_drbg_read(rng, data, max, wait);
+}
+#endif
+
static struct hwrng msm_rng = {
.name = DRIVER_NAME,
.read = msm_rng_read,
@@ -187,6 +379,20 @@
static struct class *msm_rng_class;
static struct cdev msm_rng_cdev;
+#ifdef CONFIG_FIPS_ENABLE
+
+static void _first_msm_drbg_init(struct msm_rng_device *msm_rng_dev)
+{
+ fips_reg_drbg_callback((void *)msm_rng_dev);
+ return;
+}
+#else
+static void _first_msm_drbg_init(struct msm_rng_device *msm_rng_dev)
+{
+ _do_msm_fips_drbg_init(msm_rng_dev);
+}
+#endif
+
static int __devinit msm_rng_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -205,7 +411,7 @@
goto err_exit;
}
- msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
+ msm_rng_dev = kzalloc(sizeof(struct msm_rng_device), GFP_KERNEL);
if (!msm_rng_dev) {
dev_err(&pdev->dev, "cannot allocate memory\n");
error = -ENOMEM;
@@ -220,6 +426,14 @@
}
msm_rng_dev->base = base;
+ msm_rng_dev->drbg_ctx = kzalloc(sizeof(struct fips_drbg_ctx_s),
+ GFP_KERNEL);
+ if (!msm_rng_dev->drbg_ctx) {
+ dev_err(&pdev->dev, "cannot allocate memory\n");
+ error = -ENOMEM;
+ goto err_clk_get;
+ }
+
/* create a handle for clock control */
if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
"qcom,msm-rng-iface-clk")))
@@ -279,7 +493,11 @@
}
cdev_init(&msm_rng_cdev, &msm_rng_fops);
- return ret;
+ sema_init(&msm_rng_dev->drbg_sem, 1);
+
+ _first_msm_drbg_init(msm_rng_dev);
+
+ return error;
unregister_chrdev:
unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
@@ -288,7 +506,8 @@
err_clk_get:
iounmap(msm_rng_dev->base);
err_iomap:
- kfree(msm_rng_dev);
+ kzfree(msm_rng_dev->drbg_ctx);
+ kzfree(msm_rng_dev);
err_exit:
return error;
}
@@ -296,6 +515,9 @@
static int __devexit msm_rng_remove(struct platform_device *pdev)
{
struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
+
+ fips_drbg_final(msm_rng_dev->drbg_ctx);
+
unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
hwrng_unregister(&msm_rng);
clk_put(msm_rng_dev->prng_clk);
@@ -303,7 +525,11 @@
platform_set_drvdata(pdev, NULL);
if (msm_rng_dev->qrng_perf_client)
msm_bus_scale_unregister_client(msm_rng_dev->qrng_perf_client);
- kfree(msm_rng_dev);
+ if (msm_rng_dev->drbg_ctx) {
+ kzfree(msm_rng_dev->drbg_ctx);
+ msm_rng_dev->drbg_ctx = NULL;
+ }
+ kzfree(msm_rng_dev);
return 0;
}
@@ -336,6 +562,10 @@
}
module_exit(msm_rng_exit);
+#ifdef CONFIG_FIPS_ENABLE
+EXPORT_SYMBOL(fips_ctraes128_df_known_answer_test);
+#endif
+EXPORT_SYMBOL(_do_msm_fips_drbg_init);
MODULE_AUTHOR("The Linux Foundation");
MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
diff --git a/drivers/char/hw_random/msm_rng.h b/drivers/char/hw_random/msm_rng.h
new file mode 100644
index 0000000..b79ba46
--- /dev/null
+++ b/drivers/char/hw_random/msm_rng.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#ifndef __MSM_RNG_HEADER__
+#define __MSM_RNG_HEADER__
+
+#include <linux/semaphore.h>
+#include <linux/qcedev.h>
+
+struct _fips_drbg_ctx;
+
+#define FIPS140_DRBG_ENABLED (1)
+#define FIPS140_DRBG_DISABLED (0)
+
+#define Q_HW_DRBG_BLOCK_BYTES (32)
+
+extern void fips_reg_drbg_callback(void *src);
+
+struct msm_rng_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+ struct clk *prng_clk;
+ uint32_t qrng_perf_client;
+ struct semaphore drbg_sem;
+ struct fips_drbg_ctx_s *drbg_ctx;
+ int fips140_drbg_enabled;
+};
+
+/*
+ *
+ * This function calls hardware random bit generator
+ * directory and retuns it back to caller.
+ *
+ */
+int msm_rng_direct_read(struct msm_rng_device *msm_rng_dev, void *data);
+
+#endif
diff --git a/drivers/crypto/msm/Makefile b/drivers/crypto/msm/Makefile
index df9acf2..ee8bc93 100644
--- a/drivers/crypto/msm/Makefile
+++ b/drivers/crypto/msm/Makefile
@@ -1,3 +1,6 @@
+ifeq ($(CONFIG_FIPS_ENABLE), y)
+ obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev_fips.o
+endif
obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev.o
ifeq ($(CONFIG_CRYPTO_DEV_QCE50), y)
obj-$(CONFIG_CRYPTO_DEV_QCE) += qce50.o
@@ -8,5 +11,8 @@
obj-$(CONFIG_CRYPTO_DEV_QCE) += qce.o
endif
endif
+ifeq ($(CONFIG_FIPS_ENABLE), y)
+ obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto_fips.o
+endif
obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto.o
obj-$(CONFIG_CRYPTO_DEV_OTA_CRYPTO) += ota_crypto.o
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index d1e35ec..9839279 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1,6 +1,6 @@
/* Qualcomm CE device driver.
*
- * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2014, The Linux Foundation. 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 version 2 and
@@ -33,12 +33,16 @@
#include <mach/scm.h>
#include <mach/msm_bus.h>
#include <linux/qcedev.h>
+#include "qcedevi.h"
#include "qce.h"
#define CACHE_LINE_SIZE 32
#define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
+/* are FIPS integrity tests done ?? */
+bool is_fips_qcedev_integritytest_done;
+
static uint8_t _std_init_vector_sha1_uint8[] = {
0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
@@ -52,98 +56,8 @@
0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
};
-enum qcedev_crypto_oper_type {
- QCEDEV_CRYPTO_OPER_CIPHER = 0,
- QCEDEV_CRYPTO_OPER_SHA = 1,
- QCEDEV_CRYPTO_OPER_LAST
-};
-
-struct qcedev_handle;
-
-struct qcedev_cipher_req {
- struct ablkcipher_request creq;
- void *cookie;
-};
-
-struct qcedev_sha_req {
- struct ahash_request sreq;
- void *cookie;
-};
-
-struct qcedev_sha_ctxt {
- uint32_t auth_data[4];
- uint8_t digest[QCEDEV_MAX_SHA_DIGEST];
- uint32_t diglen;
- uint8_t trailing_buf[64];
- uint32_t trailing_buf_len;
- uint8_t first_blk;
- uint8_t last_blk;
- uint8_t authkey[QCEDEV_MAX_SHA_BLOCK_SIZE];
- bool init_done;
-};
-
-struct qcedev_async_req {
- struct list_head list;
- struct completion complete;
- enum qcedev_crypto_oper_type op_type;
- union {
- struct qcedev_cipher_op_req cipher_op_req;
- struct qcedev_sha_op_req sha_op_req;
- };
- union{
- struct qcedev_cipher_req cipher_req;
- struct qcedev_sha_req sha_req;
- };
- struct qcedev_handle *handle;
- int err;
-};
-
static DEFINE_MUTEX(send_cmd_lock);
static DEFINE_MUTEX(qcedev_sent_bw_req);
-/**********************************************************************
- * Register ourselves as a misc device to be able to access the dev driver
- * from userspace. */
-
-
-#define QCEDEV_DEV "qcedev"
-
-struct qcedev_control{
-
- /* CE features supported by platform */
- struct msm_ce_hw_support platform_support;
-
- uint32_t ce_lock_count;
- uint32_t high_bw_req_count;
-
- /* CE features/algorithms supported by HW engine*/
- struct ce_hw_support ce_support;
-
- uint32_t bus_scale_handle;
-
- /* misc device */
- struct miscdevice miscdevice;
-
- /* qce handle */
- void *qce;
-
- /* platform device */
- struct platform_device *pdev;
-
- unsigned magic;
-
- struct list_head ready_commands;
- struct qcedev_async_req *active_command;
- spinlock_t lock;
- struct tasklet_struct done_tasklet;
-};
-
-struct qcedev_handle {
- /* qcedev control handle */
- struct qcedev_control *cntl;
- /* qce internal sha context*/
- struct qcedev_sha_ctxt sha_ctxt;
-};
-
/*-------------------------------------------------------------------------
* Resource Locking Service
* ------------------------------------------------------------------------*/
@@ -345,6 +259,12 @@
struct qcedev_handle *handle;
struct qcedev_control *podev;
+ /* IF FIPS tests not passed, return error */
+ if (((g_fips140_status == FIPS140_STATUS_FAIL) ||
+ (g_fips140_status == FIPS140_STATUS_PASS_CRYPTO)) &&
+ is_fips_qcedev_integritytest_done)
+ return -ENXIO;
+
podev = qcedev_minor_to_control(MINOR(inode->i_rdev));
if (podev == NULL) {
pr_err("%s: no such device %d\n", __func__,
@@ -427,7 +347,7 @@
return;
}
-static void qcedev_sha_req_cb(void *cookie, unsigned char *digest,
+void qcedev_sha_req_cb(void *cookie, unsigned char *digest,
unsigned char *authdata, int ret)
{
struct qcedev_sha_req *areq;
@@ -454,7 +374,7 @@
};
-static void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
+void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
unsigned char *iv, int ret)
{
struct qcedev_cipher_req *areq;
@@ -825,7 +745,7 @@
if (user_src && __copy_from_user(k_src,
(void __user *)user_src,
qcedev_areq->sha_op_req.data[0].len)) {
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return -EFAULT;
}
k_src += qcedev_areq->sha_op_req.data[0].len;
@@ -834,7 +754,7 @@
if (user_src && __copy_from_user(k_src,
(void __user *)user_src,
qcedev_areq->sha_op_req.data[i].len)) {
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return -EFAULT;
}
k_src += qcedev_areq->sha_op_req.data[i].len;
@@ -865,7 +785,7 @@
handle->sha_ctxt.last_blk = 0;
handle->sha_ctxt.first_blk = 0;
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return err;
}
@@ -979,7 +899,7 @@
}
sreq->entries = saved_req->entries;
sreq->data_len = saved_req->data_len;
- kfree(saved_req);
+ kzfree(saved_req);
} else
err = qcedev_sha_update_max_xfer(qcedev_areq, handle, sg_src);
@@ -1038,7 +958,7 @@
handle->sha_ctxt.init_done = false;
memset(&handle->sha_ctxt.trailing_buf[0], 0, 64);
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return err;
}
@@ -1090,7 +1010,7 @@
(void __user *)qcedev_areq->sha_op_req.data[i].vaddr;
if (user_src && __copy_from_user(k_src, (void __user *)user_src,
qcedev_areq->sha_op_req.data[i].len)) {
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return -EFAULT;
}
k_src += qcedev_areq->sha_op_req.data[i].len;
@@ -1104,7 +1024,7 @@
handle->sha_ctxt.diglen = qcedev_areq->sha_op_req.diglen;
err = submit_req(qcedev_areq, handle);
- kfree(k_buf_src);
+ kzfree(k_buf_src);
return err;
}
@@ -1224,7 +1144,7 @@
handle->sha_ctxt.last_blk = 0;
handle->sha_ctxt.first_blk = 0;
- kfree(k_src);
+ kzfree(k_src);
return err;
}
@@ -1507,8 +1427,8 @@
err = qcedev_vbuf_ablk_cipher_max_xfer(areq,
&di, handle, k_align_src);
if (err < 0) {
- kfree(k_buf_src);
- kfree(saved_req);
+ kzfree(k_buf_src);
+ kzfree(saved_req);
return err;
}
@@ -1549,8 +1469,8 @@
err = qcedev_vbuf_ablk_cipher_max_xfer(areq,
&di, handle, k_align_src);
if (err < 0) {
- kfree(k_buf_src);
- kfree(saved_req);
+ kzfree(k_buf_src);
+ kzfree(saved_req);
return err;
}
@@ -1593,8 +1513,8 @@
creq->data_len = saved_req->data_len;
creq->byteoffset = saved_req->byteoffset;
- kfree(saved_req);
- kfree(k_buf_src);
+ kzfree(saved_req);
+ kzfree(k_buf_src);
return err;
}
@@ -1703,6 +1623,18 @@
goto error;
}
+ /* Ensure IV size */
+ if (req->ivlen > QCEDEV_MAX_IV_SIZE) {
+ pr_err("%s: ivlen is not correct: %u\n", __func__, req->ivlen);
+ goto error;
+ }
+
+ /* Ensure Key size */
+ if (req->encklen > QCEDEV_MAX_KEY_SIZE) {
+ pr_err("%s: Klen is not correct: %u\n", __func__, req->encklen);
+ goto error;
+ }
+
/* Ensure zer ivlen for ECB mode */
if (req->ivlen > 0) {
if ((req->mode == QCEDEV_AES_MODE_ECB) ||
@@ -1718,8 +1650,8 @@
}
}
/* Check for sum of all dst length is equal to data_len */
- for (i = 0; (i < QCEDEV_MAX_BUFFERS) && (total < req->data_len); i++) {
- if (req->vbuf.dst[i].len > ULONG_MAX - total) {
+ for (i = 0; i < req->entries; i++) {
+ if (req->vbuf.dst[i].len >= ULONG_MAX - total) {
pr_err("%s: Integer overflow on total req dst vbuf length\n",
__func__);
goto error;
@@ -2001,6 +1933,58 @@
}
break;
+ /* This IOCTL call can be called only once
+ by FIPS Integrity test */
+ case QCEDEV_IOCTL_UPDATE_FIPS_STATUS:
+ {
+ enum fips_status status;
+ if (is_fips_qcedev_integritytest_done)
+ return -EPERM;
+
+ if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+ sizeof(enum fips_status)))
+ return -EFAULT;
+
+ if (__copy_from_user(&status, (void __user *)arg,
+ sizeof(enum fips_status)))
+ return -EFAULT;
+
+ g_fips140_status = _fips_update_status(status);
+ pr_info("qcedev: FIPS140-2 Global status flag: %d\n",
+ g_fips140_status);
+ is_fips_qcedev_integritytest_done = true;
+
+ if (g_fips140_status == FIPS140_STATUS_FAIL) {
+ pr_info("qcedev: FIPS140-2 Integrity test failed\n");
+ break;
+ }
+
+ if (!(_do_msm_fips_drbg_init(drbg_call_back)) &&
+ (g_fips140_status != FIPS140_STATUS_NA))
+ g_fips140_status = FIPS140_STATUS_PASS;
+ }
+
+ pr_info("qcedev: FIPS140-2 Global status flag: %d\n",
+ g_fips140_status);
+
+ break;
+
+ /* Read only IOCTL call to read the
+ current FIPS140-2 Status */
+ case QCEDEV_IOCTL_QUERY_FIPS_STATUS:
+ {
+ enum fips_status status;
+ if (!access_ok(VERIFY_WRITE, (void __user *)arg,
+ sizeof(enum fips_status)))
+ return -EFAULT;
+
+ status = g_fips140_status;
+ if (__copy_to_user((void __user *)arg, &status,
+ sizeof(enum fips_status)))
+ return -EFAULT;
+
+ }
+ break;
default:
return -ENOTTY;
}
@@ -2076,6 +2060,25 @@
goto err;
}
}
+
+/*
+ * FIPS140-2 Known Answer Tests:
+ * IN case of any failure, do not Init the module
+ */
+ is_fips_qcedev_integritytest_done = false;
+ if (g_fips140_status != FIPS140_STATUS_NA) {
+ if (_fips_qcedev_cipher_selftest(&qce_dev[0]) ||
+ _fips_qcedev_sha_selftest(&qce_dev[0])) {
+ pr_err("qcedev: FIPS140-2 Known Answer Tests : Failed\n");
+ panic("SYSTEM CAN NOT BOOT !!!");
+ rc = -1;
+ } else {
+ pr_info("qcedev: FIPS140-2 Known Answer Tests : Successful\n");
+ rc = 0;
+ }
+ } else
+ pr_info("qcedev: FIPS140-2 Known Answer Tests : Skipped\n");
+
if (rc >= 0)
return 0;
else
diff --git a/drivers/crypto/msm/qcedev_fips.c b/drivers/crypto/msm/qcedev_fips.c
new file mode 100644
index 0000000..fde1b88
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_fips.c
@@ -0,0 +1,489 @@
+/* FIPS Known answer tests for QCEDEV / FIPS-non-FIPS separation .
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/mman.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/qcedev.h>
+#include "qcedevi.h"
+#include "qcedev_fips.h"
+
+/*
+ * Initiate the session handle (like open /dev/qce)
+ */
+static int _fips_initiate_qcedev_handle(struct qcedev_control *podev,
+ struct qcedev_async_req *qcedev_areq)
+{
+ struct qcedev_handle *handle;
+
+ handle = kzalloc(sizeof(struct qcedev_handle), GFP_KERNEL);
+ if (handle == NULL) {
+ pr_err("Failed to allocate memory %ld\n", PTR_ERR(handle));
+ return -ENOMEM;
+ }
+
+ handle->cntl = podev;
+ qcedev_areq->handle = handle;
+ return 0;
+}
+
+/*
+ *Initiate QCEDEV request for sha/hmac
+ */
+static
+int _fips_initiate_qcedev_async_req_sha(struct qcedev_async_req *qcedev_areq,
+ struct scatterlist *fips_sg,
+ int tv_index)
+{
+ qcedev_areq->sha_op_req.alg =
+ fips_test_vector_sha_hmac[tv_index].hash_alg;
+
+ /* If HMAC setup key else make key length zero */
+ if ((qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA1_HMAC) ||
+ (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_SHA256_HMAC) ||
+ (qcedev_areq->sha_op_req.alg == QCEDEV_ALG_AES_CMAC)) {
+ qcedev_areq->sha_op_req.authkey =
+ &fips_test_vector_sha_hmac[tv_index].key[0];
+ qcedev_areq->sha_op_req.authklen =
+ fips_test_vector_sha_hmac[tv_index].klen;
+ } else
+ qcedev_areq->sha_op_req.authklen = 0;
+
+ /* Setup input and digest */
+ qcedev_areq->sha_op_req.data[0].vaddr =
+ &fips_test_vector_sha_hmac[tv_index].input[0];
+ qcedev_areq->sha_op_req.data[0].len =
+ fips_test_vector_sha_hmac[tv_index].ilen;
+ qcedev_areq->sha_op_req.data_len =
+ fips_test_vector_sha_hmac[tv_index].ilen;
+
+ /* Setup sha context and other parameters */
+ qcedev_areq->sha_op_req.entries = 1;
+ qcedev_areq->op_type = QCEDEV_CRYPTO_OPER_SHA;
+ memset(&qcedev_areq->handle->sha_ctxt, 0,
+ sizeof(struct qcedev_sha_ctxt));
+ qcedev_areq->handle->sha_ctxt.first_blk = 1;
+
+ /* Initialize digest and digest length */
+ memset(&qcedev_areq->sha_op_req.digest[0], 0, QCEDEV_MAX_SHA_DIGEST);
+ qcedev_areq->sha_op_req.diglen =
+ fips_test_vector_sha_hmac[tv_index].diglen;
+ switch (qcedev_areq->sha_op_req.alg) {
+ case QCEDEV_ALG_SHA1:
+ case QCEDEV_ALG_SHA1_HMAC:
+ memcpy(&qcedev_areq->handle->sha_ctxt.digest[0],
+ &_std_init_vector_sha1_uint8[0],
+ SHA1_DIGEST_SIZE);
+ break;
+ case QCEDEV_ALG_SHA256:
+ case QCEDEV_ALG_SHA256_HMAC:
+ memcpy(&qcedev_areq->handle->sha_ctxt.digest[0],
+ &_std_init_vector_sha256_uint8[0],
+ SHA256_DIGEST_SIZE);
+ break;
+ case QCEDEV_ALG_AES_CMAC:
+ qcedev_areq->handle->sha_ctxt.diglen =
+ fips_test_vector_sha_hmac[tv_index].diglen;
+ break;
+ default:
+ pr_err(" _fips_initiate_qcedev_async_req_sha : Invalid algo");
+ return -EINVAL;
+ }
+
+ qcedev_areq->handle->sha_ctxt.init_done = true;
+ qcedev_areq->handle->sha_ctxt.trailing_buf_len =
+ qcedev_areq->sha_op_req.data_len;
+ memcpy(&qcedev_areq->handle->sha_ctxt.trailing_buf[0],
+ fips_test_vector_sha_hmac[tv_index].input,
+ fips_test_vector_sha_hmac[tv_index].ilen);
+ qcedev_areq->handle->sha_ctxt.last_blk = 1;
+ qcedev_areq->sha_req.sreq.nbytes = qcedev_areq->sha_op_req.data_len;
+ qcedev_areq->sha_req.cookie = qcedev_areq->handle;
+ qcedev_areq->sha_req.sreq.src = fips_sg;
+ sg_set_buf(qcedev_areq->sha_req.sreq.src,
+ &qcedev_areq->handle->sha_ctxt.trailing_buf[0],
+ qcedev_areq->sha_op_req.data_len);
+ sg_mark_end(qcedev_areq->sha_req.sreq.src);
+ return 0;
+}
+
+/*
+ * Clean up of sha context after request completion
+ */
+static void _fips_clear_qcedev_handle(struct qcedev_sha_ctxt *sha_ctxt)
+{
+ sha_ctxt->first_blk = 0;
+ sha_ctxt->last_blk = 0;
+ sha_ctxt->auth_data[0] = 0;
+ sha_ctxt->auth_data[1] = 0;
+ sha_ctxt->trailing_buf_len = 0;
+ sha_ctxt->init_done = false;
+ memset(&sha_ctxt->trailing_buf[0], 0, 64);
+}
+
+/*
+ * Self test for SHA / HMAC
+ */
+int _fips_qcedev_sha_selftest(struct qcedev_control *podev)
+{
+ int ret = 0, tv_index, num_tv;
+ struct qce_sha_req sreq;
+ struct qcedev_async_req qcedev_areq;
+ struct scatterlist fips_sg;
+
+ /* Initiate handle */
+ if (_fips_initiate_qcedev_handle(podev, &qcedev_areq))
+ return -ENOMEM;
+
+ num_tv = (sizeof(fips_test_vector_sha_hmac))/
+ (sizeof(struct _fips_test_vector_sha_hmac));
+
+ /* Tests one by one */
+ for (tv_index = 0; tv_index < num_tv; tv_index++) {
+ init_completion(&qcedev_areq.complete);
+
+ /* Initiate the qcedev request */
+ if (_fips_initiate_qcedev_async_req_sha(&qcedev_areq,
+ &fips_sg, tv_index))
+ return -EINVAL;
+
+ podev->active_command = &qcedev_areq;
+
+ /* Initiate qce hash request */
+ sreq.qce_cb = qcedev_sha_req_cb;
+ if (qcedev_areq.sha_op_req.alg != QCEDEV_ALG_AES_CMAC) {
+ sreq.digest = &qcedev_areq.handle->sha_ctxt.digest[0];
+ sreq.first_blk = qcedev_areq.handle->sha_ctxt.first_blk;
+ sreq.last_blk = qcedev_areq.handle->sha_ctxt.last_blk;
+ sreq.auth_data[0] =
+ qcedev_areq.handle->sha_ctxt.auth_data[0];
+ sreq.auth_data[1] =
+ qcedev_areq.handle->sha_ctxt.auth_data[1];
+ sreq.auth_data[2] =
+ qcedev_areq.handle->sha_ctxt.auth_data[2];
+ sreq.auth_data[3] =
+ qcedev_areq.handle->sha_ctxt.auth_data[3];
+ }
+
+ sreq.size = qcedev_areq.sha_req.sreq.nbytes;
+ sreq.src = qcedev_areq.sha_req.sreq.src;
+ sreq.areq = (void *)&qcedev_areq.sha_req;
+ sreq.flags = 0;
+ switch (qcedev_areq.sha_op_req.alg) {
+ case QCEDEV_ALG_SHA1:
+ sreq.alg = QCE_HASH_SHA1;
+ break;
+ case QCEDEV_ALG_SHA256:
+ sreq.alg = QCE_HASH_SHA256;
+ break;
+ case QCEDEV_ALG_SHA1_HMAC:
+ sreq.alg = QCE_HASH_SHA1_HMAC;
+ sreq.authkey = &qcedev_areq.sha_op_req.authkey[0];
+ sreq.authklen = qcedev_areq.sha_op_req.authklen;
+ break;
+ case QCEDEV_ALG_SHA256_HMAC:
+ sreq.alg = QCE_HASH_SHA256_HMAC;
+ sreq.authkey =
+ &qcedev_areq.sha_op_req.authkey[0];
+ sreq.authklen =
+ qcedev_areq.sha_op_req.authklen;
+ break;
+ case QCEDEV_ALG_AES_CMAC:
+ sreq.alg = QCE_HASH_AES_CMAC;
+ sreq.authkey =
+ &qcedev_areq.sha_op_req.authkey[0];
+ sreq.authklen =
+ qcedev_areq.sha_op_req.authklen;
+ break;
+ default:
+ ret = -EINVAL;
+ goto handle_free;
+ }
+
+ /*qce call */
+ ret = qce_process_sha_req(podev->qce, &sreq);
+ if (ret == 0)
+ wait_for_completion(&qcedev_areq.complete);
+ else
+ goto handle_free;
+
+ /* Known answer test */
+ if (memcmp(&qcedev_areq.handle->sha_ctxt.digest[0],
+ fips_test_vector_sha_hmac[tv_index].digest,
+ fips_test_vector_sha_hmac[tv_index].diglen)) {
+ ret = -1;
+ goto handle_free;
+ }
+ _fips_clear_qcedev_handle(&qcedev_areq.handle->sha_ctxt);
+ }
+
+handle_free:
+ kzfree(qcedev_areq.handle);
+ return ret;
+}
+
+/*
+ * Initiate QCEDEV request for cipher (Encryption/ Decryption requests)
+ */
+static
+void _fips_initiate_qcedev_async_req_cipher(
+ struct qcedev_async_req *qcedev_areq,
+ enum qcedev_oper_enum qcedev_oper,
+ struct scatterlist *fips_sg,
+ uint8_t *k_align_src,
+ int tv_index)
+{
+ uint8_t *k_align_dst = k_align_src;
+
+ /* Setup Key */
+ memset(qcedev_areq->cipher_op_req.enckey, 0,
+ fips_test_vector_cipher[tv_index].klen);
+ memcpy(qcedev_areq->cipher_op_req.enckey,
+ fips_test_vector_cipher[tv_index].key,
+ fips_test_vector_cipher[tv_index].klen);
+ qcedev_areq->cipher_op_req.encklen =
+ fips_test_vector_cipher[tv_index].klen;
+
+ /* Setup IV */
+ memset(qcedev_areq->cipher_op_req.iv, 0,
+ fips_test_vector_cipher[tv_index].ivlen);
+ memcpy(qcedev_areq->cipher_op_req.iv,
+ fips_test_vector_cipher[tv_index].iv,
+ fips_test_vector_cipher[tv_index].ivlen);
+ qcedev_areq->cipher_op_req.ivlen =
+ fips_test_vector_cipher[tv_index].ivlen;
+
+ /* Setup other parameters */
+ qcedev_areq->cipher_op_req.byteoffset = 0;
+ qcedev_areq->cipher_op_req.alg =
+ fips_test_vector_cipher[tv_index].enc_alg;
+ qcedev_areq->cipher_op_req.mode =
+ fips_test_vector_cipher[tv_index].mode;
+ qcedev_areq->cipher_op_req.use_pmem = 0;
+ qcedev_areq->cipher_op_req.in_place_op = 1;
+ qcedev_areq->cipher_op_req.entries = 1;
+ qcedev_areq->cipher_op_req.op = qcedev_oper;
+ qcedev_areq->op_type = QCEDEV_CRYPTO_OPER_CIPHER;
+
+ /* Setup Input and output buffers */
+ if (qcedev_oper == QCEDEV_OPER_ENC) {
+ qcedev_areq->cipher_op_req.data_len =
+ fips_test_vector_cipher[tv_index].pln_txt_len;
+ qcedev_areq->cipher_op_req.vbuf.src[0].len =
+ fips_test_vector_cipher[tv_index].pln_txt_len;
+ } else {
+ qcedev_areq->cipher_op_req.data_len =
+ fips_test_vector_cipher[tv_index].enc_txt_len;
+ qcedev_areq->cipher_op_req.vbuf.src[0].len =
+ fips_test_vector_cipher[tv_index].enc_txt_len;
+ }
+
+ qcedev_areq->cipher_op_req.vbuf.src[0].vaddr =
+ &k_align_src[0];
+ qcedev_areq->cipher_op_req.vbuf.dst[0].vaddr =
+ &k_align_dst[0];
+ qcedev_areq->cipher_op_req.vbuf.dst[0].len =
+ fips_test_vector_cipher[tv_index].enc_txt_len;
+
+ qcedev_areq->cipher_req.creq.src = fips_sg;
+ qcedev_areq->cipher_req.creq.dst = fips_sg;
+ sg_set_buf(qcedev_areq->cipher_req.creq.src,
+ k_align_src,
+ qcedev_areq->cipher_op_req.data_len);
+ sg_mark_end(qcedev_areq->cipher_req.creq.src);
+
+ qcedev_areq->cipher_req.creq.nbytes =
+ qcedev_areq->cipher_op_req.data_len;
+ qcedev_areq->cipher_req.creq.info =
+ qcedev_areq->cipher_op_req.iv;
+ qcedev_areq->cipher_req.cookie = qcedev_areq->handle;
+}
+
+/*
+ * Initiate QCE request for cipher (Encryption/ Decryption requests)
+ */
+static int _fips_initiate_qce_req_cipher(struct qcedev_async_req *qcedev_areq,
+ struct qce_req *creq,
+ enum qce_cipher_dir_enum cipher_dir)
+{
+ creq->dir = cipher_dir;
+ creq->iv = &qcedev_areq->cipher_op_req.iv[0];
+ creq->ivsize = qcedev_areq->cipher_op_req.ivlen;
+ creq->enckey = &qcedev_areq->cipher_op_req.enckey[0];
+ creq->encklen = qcedev_areq->cipher_op_req.encklen;
+ creq->cryptlen = qcedev_areq->cipher_op_req.data_len;
+ creq->op = QCE_REQ_ABLK_CIPHER;
+ creq->qce_cb = qcedev_cipher_req_cb;
+ creq->areq = (void *)&qcedev_areq->cipher_req;
+ creq->flags = 0;
+ switch (qcedev_areq->cipher_op_req.alg) {
+ case QCEDEV_ALG_3DES:
+ creq->alg = CIPHER_ALG_3DES;
+ break;
+ case QCEDEV_ALG_AES:
+ creq->alg = CIPHER_ALG_AES;
+ break;
+ default:
+ pr_err(" _fips_initiate_qce_req_cipher : Invalid algo");
+ return -EINVAL;
+ }
+
+ switch (qcedev_areq->cipher_op_req.mode) {
+ case QCEDEV_AES_MODE_CBC:
+ case QCEDEV_DES_MODE_CBC:
+ creq->mode = QCE_MODE_CBC;
+ break;
+ case QCEDEV_AES_MODE_ECB:
+ case QCEDEV_DES_MODE_ECB:
+ creq->mode = QCE_MODE_ECB;
+ break;
+ case QCEDEV_AES_MODE_CTR:
+ creq->mode = QCE_MODE_CTR;
+ break;
+ case QCEDEV_AES_MODE_XTS:
+ creq->mode = QCE_MODE_XTS;
+ break;
+ case QCEDEV_AES_MODE_CCM:
+ creq->mode = QCE_MODE_CCM;
+ break;
+ default:
+ pr_err(" _fips_initiate_qce_req_cipher : Invalid algo");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Self test for Cipher algorithms
+ */
+int _fips_qcedev_cipher_selftest(struct qcedev_control *podev)
+{
+ int ret = 0, tv_index = 0, num_tv;
+ struct qcedev_async_req qcedev_areq;
+ struct qce_req creq;
+ struct scatterlist fips_sg;
+ uint8_t *k_align_src = NULL;
+
+ /* initiate handle */
+ if (_fips_initiate_qcedev_handle(podev, &qcedev_areq))
+ return -ENOMEM;
+
+ num_tv = (sizeof(fips_test_vector_cipher)) /
+ (sizeof(struct _fips_test_vector_cipher));
+
+ /* tests one by one */
+ for (tv_index = 0; tv_index < num_tv; tv_index++) {
+
+ /* Allocate single buffer for in-place operation */
+ k_align_src = kzalloc(QCE_MAX_OPER_DATA, GFP_KERNEL);
+ if (k_align_src == NULL) {
+ pr_err("qcedev: Failed to allocate memory for k_align_src %ld\n",
+ PTR_ERR(k_align_src));
+ kzfree(qcedev_areq.handle);
+ return -ENOMEM;
+ }
+
+ /**************** Encryption Tests *****************/
+ init_completion(&qcedev_areq.complete);
+ memcpy(&k_align_src[0],
+ fips_test_vector_cipher[tv_index].pln_txt,
+ fips_test_vector_cipher[tv_index].pln_txt_len);
+
+ /* Initiate qcedev request */
+ _fips_initiate_qcedev_async_req_cipher(&qcedev_areq,
+ QCEDEV_OPER_ENC, &fips_sg,
+ k_align_src, tv_index);
+ podev->active_command = &qcedev_areq;
+
+ /* Initiate qce cipher request */
+ if (_fips_initiate_qce_req_cipher(&qcedev_areq,
+ &creq, QCE_ENCRYPT)) {
+ ret = -EINVAL;
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+
+ /* qce call */
+ ret = qce_ablk_cipher_req(podev->qce, &creq);
+ if (ret == 0)
+ wait_for_completion(&qcedev_areq.complete);
+ else {
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+
+ /* Known answer test for encryption */
+ if (memcmp(k_align_src,
+ fips_test_vector_cipher[tv_index].enc_txt,
+ fips_test_vector_cipher[tv_index].enc_txt_len)) {
+ ret = -1;
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+
+ /**************** Decryption Tests *****************/
+ init_completion(&qcedev_areq.complete);
+ memset(&k_align_src[0], 0,
+ fips_test_vector_cipher[tv_index].pln_txt_len);
+ memcpy(&k_align_src[0],
+ fips_test_vector_cipher[tv_index].enc_txt,
+ fips_test_vector_cipher[tv_index].enc_txt_len);
+
+ /* Initiate qcedev request */
+ _fips_initiate_qcedev_async_req_cipher(&qcedev_areq,
+ QCEDEV_OPER_DEC, &fips_sg,
+ k_align_src, tv_index);
+ podev->active_command = &qcedev_areq;
+
+ /*Initiate qce cipher request */
+ if (_fips_initiate_qce_req_cipher(&qcedev_areq,
+ &creq, QCE_DECRYPT)) {
+ ret = -EINVAL;
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+
+ /* qce call */
+ ret = qce_ablk_cipher_req(podev->qce, &creq);
+ if (ret == 0)
+ wait_for_completion(&qcedev_areq.complete);
+ else {
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+
+ /* Known answer test for Decryption */
+ if (memcmp(k_align_src,
+ fips_test_vector_cipher[tv_index].pln_txt,
+ fips_test_vector_cipher[tv_index].pln_txt_len)) {
+ ret = -1;
+ kzfree(k_align_src);
+ goto free_handle;
+ }
+ podev->active_command = NULL;
+ kzfree(k_align_src);
+ }
+
+free_handle:
+ kzfree(qcedev_areq.handle);
+ return ret;
+}
+
+void fips_reg_drbg_callback(void *src)
+{
+ drbg_call_back = src;
+}
+EXPORT_SYMBOL(fips_reg_drbg_callback);
diff --git a/drivers/crypto/msm/qcedev_fips.h b/drivers/crypto/msm/qcedev_fips.h
new file mode 100644
index 0000000..6f8ae7c
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_fips.h
@@ -0,0 +1,438 @@
+/* Test vectors : FIPS Known answer tests for QCEDEV .
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __CRYPTO_MSM_QCEDEV_FIPS_H
+#define __CRYPTO_MSM_QCEDEV_FIPS_H
+
+#include "qce.h"
+
+static uint8_t _std_init_vector_sha1_uint8[] = {
+ 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
+ 0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
+ 0xC3, 0xD2, 0xE1, 0xF0
+};
+
+/* standard initialization vector for SHA-256, source: FIPS 180-2 */
+static uint8_t _std_init_vector_sha256_uint8[] = {
+ 0x6A, 0x09, 0xE6, 0x67, 0xBB, 0x67, 0xAE, 0x85,
+ 0x3C, 0x6E, 0xF3, 0x72, 0xA5, 0x4F, 0xF5, 0x3A,
+ 0x51, 0x0E, 0x52, 0x7F, 0x9B, 0x05, 0x68, 0x8C,
+ 0x1F, 0x83, 0xD9, 0xAB, 0x5B, 0xE0, 0xCD, 0x19
+};
+
+/*
+ * For Hashing / HMAC algorithms
+ */
+struct _fips_test_vector_sha_hmac {
+ char *key;
+ unsigned char klen;
+ char *input;
+ unsigned char ilen;
+ char *digest;
+ unsigned char diglen;
+ enum qcedev_sha_alg_enum hash_alg;
+};
+
+/*
+ * For cipher algorithms
+ */
+struct _fips_test_vector_cipher {
+ char *key;
+ unsigned char klen;
+ char *iv;
+ unsigned char ivlen;
+ char *pln_txt;
+ unsigned int pln_txt_len;
+ char *enc_txt;
+ unsigned int enc_txt_len;
+ enum qcedev_cipher_alg_enum enc_alg;
+ enum qcedev_cipher_mode_enum mode;
+};
+
+
+/*
+ * Test vectors sha and hmac algorothms
+ */
+static struct _fips_test_vector_sha_hmac fips_test_vector_sha_hmac[] = {
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf */
+ {/* SHA1 */
+ .hash_alg = QCEDEV_ALG_SHA1,
+ .input = "abc",
+ .ilen = 3,
+ .digest = "\xa9\x99\x3e\x36\x47\x06\x81\x6a"
+ "\xba\x3e\x25\x71\x78\x50\xc2\x6c"
+ "\x9c\xd0\xd8\x9d",
+ .diglen = SHA1_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf */
+ {/* SHA256 */
+ .hash_alg = QCEDEV_ALG_SHA256,
+ .input = "abc",
+ .ilen = 3,
+ .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea"
+ "\x41\x41\x40\xde\x5d\xae\x22\x23"
+ "\xb0\x03\x61\xa3\x96\x17\x7a\x9c"
+ "\xb4\x10\xff\x61\xf2\x00\x15\xad",
+ .diglen = SHA256_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/HMAC_SHA1.pdf */
+ {/* HMAC-SHA1 */
+ .hash_alg = QCEDEV_ALG_SHA1_HMAC,
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13",
+ .klen = 20,
+ .input = "Sample message for keylen<blocklen",
+ .ilen = 34,
+ .digest = "\x4C\x99\xFF\x0C\xB1\xB3\x1B\xD3"
+ "\x3F\x84\x31\xDB\xAF\x4D\x17\xFC"
+ "\xD3\x56\xA8\x07",
+ .diglen = SHA1_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/HMAC_SHA256.pdf */
+ {/* HMAC-SHA256 */
+ .hash_alg = QCEDEV_ALG_SHA256_HMAC,
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F",
+ .klen = 64,
+ .input = "Sample message for keylen=blocklen",
+ .ilen = 34,
+ .digest = "\x8B\xB9\xA1\xDB\x98\x06\xF2\x0D"
+ "\xF7\xF7\x7B\x82\x13\x8C\x79\x14"
+ "\xD1\x74\xD5\x9E\x13\xDC\x4D\x01"
+ "\x69\xC9\x05\x7B\x13\x3E\x1D\x62",
+ .diglen = SHA256_DIGEST_SIZE,
+ },
+/* From NIST Special Publication 800-38B Appendix D.1 */
+ {/* AES 128-CMAC */
+ .hash_alg = QCEDEV_ALG_AES_CMAC,
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ .ilen = 16,
+ .digest = "\x07\x0a\x16\xb4\x6b\x4d\x41\x44"
+ "\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c",
+ .diglen = 16,
+ },
+ /* From NIST Special Publication 800-38B Appendix D.1 */
+ {/* AES 256-CMAC */
+ .hash_alg = QCEDEV_ALG_AES_CMAC,
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .input = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ .ilen = 16,
+ .digest = "\x28\xa7\x02\x3f\x45\x2e\x8f\x82"
+ "\xbd\x4b\xf2\x8d\x8c\x37\xc3\x5c",
+ .diglen = 16,
+ },
+};
+
+ /*
+ *Test vectors for cipher algorithms
+ */
+static struct _fips_test_vector_cipher fips_test_vector_cipher[] = {
+ /* From NIST Special Publication 800-38A, Appendix F.1 */
+ {/* AES-128 ECB */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_ECB,
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .ivlen = 0,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x3a\xd7\x7b\xb4\x0d\x7a\x36\x60"
+ "\xa8\x9e\xca\xf3\x24\x66\xef\x97"
+ "\xf5\xd3\xd5\x85\x03\xb9\x69\x9d"
+ "\xe7\x85\x89\x5a\x96\xfd\xba\xaf"
+ "\x43\xb1\xcd\x7f\x59\x8e\xce\x23"
+ "\x88\x1b\x00\xe3\xed\x03\x06\x88"
+ "\x7b\x0c\x78\x5e\x27\xe8\xad\x3f"
+ "\x82\x23\x20\x71\x04\x72\x5d\xd4",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.1 */
+ {/* AES-256 ECB */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_ECB,
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .ivlen = 0,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xf3\xee\xd1\xbd\xb5\xd2\xa0\x3c"
+ "\x06\x4b\x5a\x7e\x3d\xb1\x81\xf8"
+ "\x59\x1c\xcb\x10\xd4\x10\xed\x26"
+ "\xdc\x5b\xa7\x4a\x31\x36\x28\x70"
+ "\xb6\xed\x21\xb9\x9c\xa6\xf4\xf9"
+ "\xf1\x53\xe7\xb1\xbe\xaf\xed\x1d"
+ "\x23\x30\x4b\x7a\x39\xf9\xf3\xff"
+ "\x06\x7d\x8d\x8f\x9e\x24\xec\xc7",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.2 */
+ {/* AES-128 CBC */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_CBC,
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x76\x49\xab\xac\x81\x19\xb2\x46"
+ "\xce\xe9\x8e\x9b\x12\xe9\x19\x7d"
+ "\x50\x86\xcb\x9b\x50\x72\x19\xee"
+ "\x95\xdb\x11\x3a\x91\x76\x78\xb2"
+ "\x73\xbe\xd6\xb8\xe3\xc1\x74\x3b"
+ "\x71\x16\xe6\x9e\x22\x22\x95\x16"
+ "\x3f\xf1\xca\xa1\x68\x1f\xac\x09"
+ "\x12\x0e\xca\x30\x75\x86\xe1\xa7",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.2 */
+ {/* AES-256 CBC */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_CBC,
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba"
+ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6"
+ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d"
+ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d"
+ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf"
+ "\xa5\x30\xe2\x63\x04\x23\x14\x61"
+ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc"
+ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.5 */
+ {/* AES-128 CTR */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_CTR,
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x87\x4d\x61\x91\xb6\x20\xe3\x26"
+ "\x1b\xef\x68\x64\x99\x0d\xb6\xce"
+ "\x98\x06\xf6\x6b\x79\x70\xfd\xff"
+ "\x86\x17\x18\x7b\xb9\xff\xfd\xff"
+ "\x5a\xe4\xdf\x3e\xdb\xd5\xd3\x5e"
+ "\x5b\x4f\x09\x02\x0d\xb0\x3e\xab"
+ "\x1e\x03\x1d\xda\x2f\xbe\x03\xd1"
+ "\x79\x21\x70\xa0\xf3\x00\x9c\xee",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.5 */
+ {/* AES-256 CTR */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_CTR,
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x60\x1e\xc3\x13\x77\x57\x89\xa5"
+ "\xb7\xa7\xf5\x04\xbb\xf3\xd2\x28"
+ "\xf4\x43\xe3\xca\x4d\x62\xb5\x9a"
+ "\xca\x84\xe9\x90\xca\xca\xf5\xc5"
+ "\x2b\x09\x30\xda\xa2\x3d\xe9\x4c"
+ "\xe8\x70\x17\xba\x2d\x84\x98\x8d"
+ "\xdf\xc9\xc5\x8d\xb6\x7a\xad\xa6"
+ "\x13\xc2\xdd\x08\x45\x79\x41\xa6",
+ .enc_txt_len = 64,
+ },
+ /* Derived From From NIST Special Publication 800-38A */
+ {/* AES-128 XTS requires 2 keys and thus length of key is twice. */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_XTS,
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xba\x2a\x7d\x50\x7b\x60\x63\x3e"
+ "\xf3\x1b\x06\x14\xb4\x45\xb5\xb5"
+ "\x42\x0d\x12\x57\x28\x15\x2e\x5d"
+ "\x5a\x54\xbe\x46\x5c\x9d\x1f\x2e"
+ "\x18\x8e\x79\x07\xc7\xdf\xe7\xf8"
+ "\x78\xa6\x53\x2a\x80\xb4\xd9\xce"
+ "\x1d\xbe\x75\x7e\xb6\x11\xef\x1e"
+ "\x51\x5d\xd6\x70\x03\x51\xcc\x94",
+ .enc_txt_len = 64,
+ },
+ /* Derived From From NIST Special Publication 800-38A */
+ {/* AES-256 XTS requires 2 keys and thus length of key is twice */
+ .enc_alg = QCEDEV_ALG_AES,
+ .mode = QCEDEV_AES_MODE_XTS,
+ .key = "\x3a\xd7\x7b\xb4\x0d\x7a\x36\x60"
+ "\xa8\x9e\xca\xf3\x24\x66\xef\x97"
+ "\xf5\xd3\xd5\x85\x03\xb9\x69\x9d"
+ "\xe7\x85\x89\x5a\x96\xfd\xba\xaf"
+ "\x43\xb1\xcd\x7f\x59\x8e\xce\x23"
+ "\x88\x1b\x00\xe3\xed\x03\x06\x88"
+ "\x7b\x0c\x78\x5e\x27\xe8\xad\x3f"
+ "\x82\x23\x20\x71\x04\x72\x5d\xd4",
+ .klen = 64,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xd7\x2b\x90\x02\x6f\xf0\xd2\x39"
+ "\x7b\x1a\x57\x92\xd0\x1e\xc1\xb6"
+ "\x04\x8c\x08\x8e\xa4\x1f\xa0\x0f"
+ "\x5e\xd8\xaf\xda\x6e\xd2\x4e\x5b"
+ "\x23\xde\x09\xa4\x19\x79\xda\xd4"
+ "\xe9\x4b\xbc\x05\x2e\xca\x20\x7d"
+ "\xd5\x0f\x89\x88\xa3\xda\x46\x1f"
+ "\x1e\xde\x53\x78\x90\xb2\x9a\x2c",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-67, Appendix B.1 */
+ {/* 3DES ECB */
+ .enc_alg = QCEDEV_ALG_3DES,
+ .mode = QCEDEV_DES_MODE_ECB,
+ .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+ "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+ .klen = 24,
+ .ivlen = 0,
+ .pln_txt = "\x54\x68\x65\x20\x71\x75\x66\x63"
+ "\x6B\x20\x62\x72\x6F\x77\x6E\x20"
+ "\x66\x6F\x78\x20\x6A\x75\x6D\x70",
+ .pln_txt_len = 24,
+ .enc_txt = "\xA8\x26\xFD\x8C\xE5\x3B\x85\x5F"
+ "\xCC\xE2\x1C\x81\x12\x25\x6F\xE6"
+ "\x68\xD5\xC0\x5D\xD9\xB6\xB9\x00",
+ .enc_txt_len = 24,
+ },
+ /* Derived From From NIST Special Publication 800-38A and 800-67 */
+ {/* 3DES CBC */
+ .enc_alg = QCEDEV_ALG_3DES,
+ .mode = QCEDEV_DES_MODE_CBC,
+ .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+ "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+ .klen = 24,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07",
+ .ivlen = 8,
+ .pln_txt = "\x54\x68\x65\x20\x71\x75\x66\x63"
+ "\x6B\x20\x62\x72\x6F\x77\x6E\x20"
+ "\x66\x6F\x78\x20\x6A\x75\x6D\x70",
+ .pln_txt_len = 24,
+ .enc_txt = "\xf3\x68\xd0\x6f\x3b\xbd\x61\x4e"
+ "\x60\xf2\xd0\x24\x5c\xad\x3f\x81"
+ "\x8d\x5c\x69\xf2\xcb\x3f\xd5\xc7",
+ .enc_txt_len = 24,
+ },
+};
+#endif /* __CRYPTO_MSM_QCEDEV_FIPS_H */
+
diff --git a/drivers/crypto/msm/qcedevi.h b/drivers/crypto/msm/qcedevi.h
new file mode 100644
index 0000000..361050a
--- /dev/null
+++ b/drivers/crypto/msm/qcedevi.h
@@ -0,0 +1,173 @@
+/* QTI crypto Driver
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __CRYPTO_MSM_QCEDEVI_H
+#define __CRYPTO_MSM_QCEDEVI_H
+
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <crypto/hash.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+#include <linux/fips_status.h>
+#include "qce.h"
+
+#define CACHE_LINE_SIZE 32
+#define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
+
+/* FIPS global status variable */
+extern enum fips_status g_fips140_status;
+
+/*FIPS140-2 call back for DRBG self test */
+extern void *drbg_call_back;
+
+enum qcedev_crypto_oper_type {
+ QCEDEV_CRYPTO_OPER_CIPHER = 0,
+ QCEDEV_CRYPTO_OPER_SHA = 1,
+ QCEDEV_CRYPTO_OPER_LAST
+};
+
+struct qcedev_handle;
+
+struct qcedev_cipher_req {
+ struct ablkcipher_request creq;
+ void *cookie;
+};
+
+struct qcedev_sha_req {
+ struct ahash_request sreq;
+ void *cookie;
+};
+
+struct qcedev_sha_ctxt {
+ uint32_t auth_data[4];
+ uint8_t digest[QCEDEV_MAX_SHA_DIGEST];
+ uint32_t diglen;
+ uint8_t trailing_buf[64];
+ uint32_t trailing_buf_len;
+ uint8_t first_blk;
+ uint8_t last_blk;
+ uint8_t authkey[QCEDEV_MAX_SHA_BLOCK_SIZE];
+ bool init_done;
+};
+
+struct qcedev_async_req {
+ struct list_head list;
+ struct completion complete;
+ enum qcedev_crypto_oper_type op_type;
+ union {
+ struct qcedev_cipher_op_req cipher_op_req;
+ struct qcedev_sha_op_req sha_op_req;
+ };
+
+ union {
+ struct qcedev_cipher_req cipher_req;
+ struct qcedev_sha_req sha_req;
+ };
+ struct qcedev_handle *handle;
+ int err;
+};
+
+/**********************************************************************
+ * Register ourselves as a misc device to be able to access the dev driver
+ * from userspace. */
+
+#define QCEDEV_DEV "qcedev"
+
+struct qcedev_control {
+
+ /* CE features supported by platform */
+ struct msm_ce_hw_support platform_support;
+
+ uint32_t ce_lock_count;
+ uint32_t high_bw_req_count;
+
+ /* CE features/algorithms supported by HW engine*/
+ struct ce_hw_support ce_support;
+
+ uint32_t bus_scale_handle;
+
+ /* misc device */
+ struct miscdevice miscdevice;
+
+ /* qce handle */
+ void *qce;
+
+ /* platform device */
+ struct platform_device *pdev;
+
+ unsigned magic;
+
+ struct list_head ready_commands;
+ struct qcedev_async_req *active_command;
+ spinlock_t lock;
+ struct tasklet_struct done_tasklet;
+};
+
+struct qcedev_handle {
+ /* qcedev control handle */
+ struct qcedev_control *cntl;
+ /* qce internal sha context*/
+ struct qcedev_sha_ctxt sha_ctxt;
+};
+
+void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
+ unsigned char *iv, int ret);
+
+void qcedev_sha_req_cb(void *cookie, unsigned char *digest,
+ unsigned char *authdata, int ret);
+
+extern int _do_msm_fips_drbg_init(void *rng_dev);
+
+#ifdef CONFIG_FIPS_ENABLE
+
+/*
+ * Self test for Cipher algorithms
+ */
+int _fips_qcedev_cipher_selftest(struct qcedev_control *podev);
+
+/*
+ * Self test for SHA / HMAC
+ */
+
+int _fips_qcedev_sha_selftest(struct qcedev_control *podev);
+
+/*
+ * Update FIPs Global status Status
+ */
+static inline enum fips_status _fips_update_status(enum fips_status status)
+{
+ return (status == FIPS140_STATUS_PASS) ?
+ FIPS140_STATUS_QCRYPTO_ALLOWED :
+ FIPS140_STATUS_FAIL;
+}
+
+#else
+
+static inline int _fips_qcedev_cipher_selftest(struct qcedev_control *podev)
+{
+ return 0;
+}
+static inline int _fips_qcedev_sha_selftest(struct qcedev_control *podev)
+{
+ return 0;
+}
+
+static inline enum fips_status _fips_update_status(enum fips_status status)
+{
+ return FIPS140_STATUS_NA;
+}
+
+#endif /* CONFIG_FIPS_ENABLE */
+
+#endif /* __CRYPTO_MSM_QCEDEVI_H */
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index c247189..b8975aa 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -45,9 +45,10 @@
#include <linux/platform_data/qcom_crypto_device.h>
#include <mach/msm_bus.h>
#include <mach/qcrypto.h>
+#include <linux/fips_status.h>
+#include "qcryptoi.h"
#include "qce.h"
-
#define DEBUG_MAX_FNAME 16
#define DEBUG_MAX_RW_BUF 2048
@@ -58,6 +59,9 @@
#define QCRYPTO_HIGH_BANDWIDTH_TIMEOUT 1000
+/* are FIPS self tests done ?? */
+static bool is_fips_qcrypto_tests_done;
+
enum qcrypto_bus_state {
BUS_NO_BANDWIDTH = 0,
BUS_HAS_BANDWIDTH,
@@ -683,6 +687,12 @@
struct qcrypto_alg *q_alg;
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+ /* IF FIPS tests not passed, return error */
+ if (((g_fips140_status == FIPS140_STATUS_FAIL) ||
+ (g_fips140_status == FIPS140_STATUS_PASS_CRYPTO)) &&
+ is_fips_qcrypto_tests_done)
+ return -ENXIO;
+
q_alg = container_of(alg, struct qcrypto_alg, cipher_alg);
ctx->flags = 0;
@@ -710,6 +720,12 @@
struct qcrypto_alg *q_alg = container_of(alg, struct qcrypto_alg,
sha_alg);
+ /* IF FIPS tests not passed, return error */
+ if (((g_fips140_status == FIPS140_STATUS_FAIL) ||
+ (g_fips140_status == FIPS140_STATUS_PASS_CRYPTO)) &&
+ is_fips_qcrypto_tests_done)
+ return -ENXIO;
+
crypto_ahash_set_reqsize(ahash, sizeof(struct qcrypto_sha_req_ctx));
/* update context with ptr to cp */
sha_ctx->cp = q_alg->cp;
@@ -959,7 +975,7 @@
if (q_alg->alg_type == QCRYPTO_ALG_SHA)
crypto_unregister_ahash(&q_alg->sha_alg);
list_del(&q_alg->entry);
- kfree(q_alg);
+ kzfree(q_alg);
}
}
@@ -978,7 +994,7 @@
mutex_unlock(&cp->engine_lock);
if (pengine->qce)
qce_close(pengine->qce);
- kfree(pengine);
+ kzfree(pengine);
return 0;
}
@@ -1282,7 +1298,7 @@
if (bytes != areq->nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x", bytes,
areq->nbytes);
- kfree(rctx->data);
+ kzfree(rctx->data);
}
if (cp->platform_support.ce_shared)
@@ -1329,7 +1345,7 @@
if (bytes != nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x",
bytes, nbytes);
- kfree(rctx->data);
+ kzfree(rctx->data);
}
kzfree(rctx->assoc);
areq->assoc = rctx->assoc_sg;
@@ -1363,7 +1379,7 @@
if (bytes != nbytes)
pr_warn("bytes copied=0x%x bytes to copy= 0x%x",
bytes, nbytes);
- kfree(rctx->data);
+ kzfree(rctx->data);
}
if (ret == 0) {
@@ -3163,7 +3179,7 @@
rctx->trailing_buf_len);
memcpy((rctx->data2 + rctx->trailing_buf_len),
rctx->data, req->src->length);
- kfree(rctx->data);
+ kzfree(rctx->data);
rctx->data = rctx->data2;
sg_set_buf(&rctx->sg[0], rctx->data,
(rctx->trailing_buf_len +
@@ -3351,7 +3367,7 @@
INIT_COMPLETION(sha_ctx->ahash_req_complete);
}
- kfree(in_buf);
+ kzfree(in_buf);
ahash_request_free(ahash_req);
return ret;
@@ -3628,6 +3644,23 @@
return 0;
}
+/*
+ * Fill up fips_selftest_data structure
+ */
+
+static void _qcrypto_fips_selftest_d(struct fips_selftest_data *selftest_d,
+ struct ce_hw_support *ce_support,
+ char *prefix)
+{
+ strlcpy(selftest_d->algo_prefix, prefix, CRYPTO_MAX_ALG_NAME);
+ selftest_d->prefix_ahash_algo = ce_support->use_sw_ahash_algo;
+ selftest_d->prefix_hmac_algo = ce_support->use_sw_hmac_algo;
+ selftest_d->prefix_aes_xts_algo = ce_support->use_sw_aes_xts_algo;
+ selftest_d->prefix_aes_cbc_ecb_ctr_algo =
+ ce_support->use_sw_aes_cbc_ecb_ctr_algo;
+ selftest_d->prefix_aead_algo = ce_support->use_sw_aead_algo;
+ selftest_d->ce_device = ce_support->ce_device;
+}
int qcrypto_cipher_set_device(struct ablkcipher_request *req, unsigned int dev)
{
@@ -4235,6 +4268,10 @@
struct crypto_engine *pengine;
unsigned long flags;
+ /* For FIPS140-2 Power on self tests */
+ struct fips_selftest_data selftest_d;
+ char prefix[10] = "";
+
pengine = kzalloc(sizeof(*pengine), GFP_KERNEL);
if (!pengine) {
pr_err("qcrypto Memory allocation of q_alg FAIL, error %ld\n",
@@ -4245,7 +4282,7 @@
/* open qce */
handle = qce_open(pdev, &rc);
if (handle == NULL) {
- kfree(pengine);
+ kzfree(pengine);
platform_set_drvdata(pdev, NULL);
return rc;
}
@@ -4328,7 +4365,7 @@
if (cp->total_units != 1) {
mutex_unlock(&cp->engine_lock);
- return 0;
+ goto fips_selftest;
}
/* register crypto cipher algorithms the device supports */
@@ -4357,7 +4394,7 @@
if (rc) {
dev_err(&pdev->dev, "%s alg registration failed\n",
q_alg->cipher_alg.cra_driver_name);
- kfree(q_alg);
+ kzfree(q_alg);
} else {
list_add_tail(&q_alg->entry, &cp->alg_list);
dev_info(&pdev->dev, "%s\n",
@@ -4391,7 +4428,7 @@
if (rc) {
dev_err(&pdev->dev, "%s alg registration failed\n",
q_alg->cipher_alg.cra_driver_name);
- kfree(q_alg);
+ kzfree(q_alg);
} else {
list_add_tail(&q_alg->entry, &cp->alg_list);
dev_info(&pdev->dev, "%s\n",
@@ -4428,7 +4465,7 @@
if (rc) {
dev_err(&pdev->dev, "%s alg registration failed\n",
q_alg->sha_alg.halg.base.cra_driver_name);
- kfree(q_alg);
+ kzfree(q_alg);
} else {
list_add_tail(&q_alg->entry, &cp->alg_list);
dev_info(&pdev->dev, "%s\n",
@@ -4505,7 +4542,7 @@
dev_err(&pdev->dev,
"%s alg registration failed\n",
q_alg->sha_alg.halg.base.cra_driver_name);
- kfree(q_alg);
+ kzfree(q_alg);
} else {
list_add_tail(&q_alg->entry, &cp->alg_list);
dev_info(&pdev->dev, "%s\n",
@@ -4541,7 +4578,7 @@
if (rc) {
dev_err(&pdev->dev, "%s alg registration failed\n",
q_alg->cipher_alg.cra_driver_name);
- kfree(q_alg);
+ kzfree(q_alg);
} else {
list_add_tail(&q_alg->entry, &cp->alg_list);
dev_info(&pdev->dev, "%s\n",
@@ -4580,13 +4617,42 @@
}
mutex_unlock(&cp->engine_lock);
+
+fips_selftest:
+ /*
+ * FIPS140-2 Known Answer Tests :
+ * IN case of any failure, do not Init the module
+ */
+ is_fips_qcrypto_tests_done = false;
+
+ if (g_fips140_status != FIPS140_STATUS_NA) {
+
+ _qcrypto_prefix_alg_cra_name(prefix, 0);
+ _qcrypto_fips_selftest_d(&selftest_d, &cp->ce_support, prefix);
+ if (_fips_qcrypto_sha_selftest(&selftest_d) ||
+ _fips_qcrypto_cipher_selftest(&selftest_d) ||
+ _fips_qcrypto_aead_selftest(&selftest_d)) {
+ pr_err("qcrypto: FIPS140-2 Known Answer Tests : Failed\n");
+ panic("SYSTEM CAN NOT BOOT!!!");
+ rc = -1;
+ goto err;
+ } else
+ pr_info("qcrypto: FIPS140-2 Known Answer Tests: Successful\n");
+ if (g_fips140_status != FIPS140_STATUS_PASS)
+ g_fips140_status = FIPS140_STATUS_PASS_CRYPTO;
+
+ } else
+ pr_info("qcrypto: FIPS140-2 Known Answer Tests: Skipped\n");
+
+ is_fips_qcrypto_tests_done = true;
+
return 0;
err:
_qcrypto_remove_engine(pengine);
mutex_unlock(&cp->engine_lock);
if (pengine->qce)
qce_close(pengine->qce);
- kfree(pengine);
+ kzfree(pengine);
return rc;
};
diff --git a/drivers/crypto/msm/qcrypto_fips.c b/drivers/crypto/msm/qcrypto_fips.c
new file mode 100644
index 0000000..a53690f
--- /dev/null
+++ b/drivers/crypto/msm/qcrypto_fips.c
@@ -0,0 +1,511 @@
+/* Qcrypto: FIPS 140-2 Selftests
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <crypto/hash.h>
+#include <crypto/ctr.h>
+#include <crypto/des.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/hash.h>
+#include <crypto/algapi.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/hash.h>
+#include <linux/platform_data/qcom_crypto_device.h>
+#include <mach/qcrypto.h>
+#include "qcryptoi.h"
+#include "qcrypto_fips.h"
+
+/*
+ * Callback function
+ */
+static void _fips_cb(struct crypto_async_request *crypto_async_req, int err)
+{
+ struct _fips_completion *fips_completion = crypto_async_req->data;
+ if (err == -EINPROGRESS)
+ return;
+
+ fips_completion->err = err;
+ complete(&fips_completion->completion);
+}
+
+/*
+ * Function to prefix if needed
+ */
+static int _fips_get_alg_cra_name(char cra_name[],
+ char *prefix, unsigned int size)
+{
+ char new_cra_name[CRYPTO_MAX_ALG_NAME];
+ strlcpy(new_cra_name, prefix, CRYPTO_MAX_ALG_NAME);
+ if (CRYPTO_MAX_ALG_NAME < size + strlen(prefix))
+ return -EINVAL;
+
+ strlcat(new_cra_name, cra_name, CRYPTO_MAX_ALG_NAME);
+ strlcpy(cra_name, new_cra_name, CRYPTO_MAX_ALG_NAME);
+ return 0;
+}
+
+/*
+ * Sha/HMAC self tests
+ */
+int _fips_qcrypto_sha_selftest(struct fips_selftest_data *selftest_d)
+{
+ int rc = 0, err, tv_index = 0, num_tv;
+ char *k_out_buf = NULL;
+ struct scatterlist fips_sg;
+ struct crypto_ahash *tfm;
+ struct ahash_request *ahash_req;
+ struct _fips_completion fips_completion;
+ struct _fips_test_vector_sha_hmac tv_sha_hmac;
+
+ num_tv = (sizeof(fips_test_vector_sha_hmac)) /
+ (sizeof(struct _fips_test_vector_sha_hmac));
+
+ /* One-by-one testing */
+ for (tv_index = 0; tv_index < num_tv; tv_index++) {
+ memcpy(&tv_sha_hmac, &fips_test_vector_sha_hmac[tv_index],
+ (sizeof(struct _fips_test_vector_sha_hmac)));
+ k_out_buf = kzalloc(tv_sha_hmac.diglen, GFP_KERNEL);
+ if (k_out_buf == NULL) {
+ pr_err("qcrypto: Failed to allocate memory for k_out_buf %ld\n",
+ PTR_ERR(k_out_buf));
+ return -ENOMEM;
+ }
+
+ memset(k_out_buf, 0, tv_sha_hmac.diglen);
+ init_completion(&fips_completion.completion);
+
+ /* use_sw flags are set in dtsi file which makes
+ default Linux API calls to go to s/w crypto instead
+ of h/w crypto. This code makes sure that all selftests
+ calls always go to h/w, independent of DTSI flags. */
+ if (tv_sha_hmac.klen == 0) {
+ if (selftest_d->prefix_ahash_algo)
+ if (_fips_get_alg_cra_name(tv_sha_hmac
+ .hash_alg, selftest_d->algo_prefix,
+ strlen(tv_sha_hmac.hash_alg))) {
+ rc = -1;
+ pr_err("Algo Name is too long for tv %d\n",
+ tv_index);
+ goto clr_buf;
+ }
+ } else {
+ if (selftest_d->prefix_hmac_algo)
+ if (_fips_get_alg_cra_name(tv_sha_hmac
+ .hash_alg, selftest_d->algo_prefix,
+ strlen(tv_sha_hmac.hash_alg))) {
+ rc = -1;
+ pr_err("Algo Name is too long for tv %d\n",
+ tv_index);
+ goto clr_buf;
+ }
+ }
+
+ tfm = crypto_alloc_ahash(tv_sha_hmac.hash_alg, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("qcrypto: %s algorithm not found\n",
+ tv_sha_hmac.hash_alg);
+ rc = PTR_ERR(tfm);
+ goto clr_buf;
+ }
+
+ ahash_req = ahash_request_alloc(tfm, GFP_KERNEL);
+ if (!ahash_req) {
+ pr_err("qcrypto: ahash_request_alloc failed\n");
+ rc = -ENOMEM;
+ goto clr_tfm;
+ }
+ rc = qcrypto_ahash_set_device(ahash_req, selftest_d->ce_device);
+ if (rc != 0) {
+ pr_err("%s qcrypto_cipher_set_device failed with err %d\n",
+ __func__, rc);
+ goto clr_ahash_req;
+ }
+ ahash_request_set_callback(ahash_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _fips_cb, &fips_completion);
+
+ sg_init_one(&fips_sg, &tv_sha_hmac.input[0], tv_sha_hmac.ilen);
+
+ crypto_ahash_clear_flags(tfm, ~0);
+ if (tv_sha_hmac.klen != 0) {
+ rc = crypto_ahash_setkey(tfm, tv_sha_hmac.key,
+ tv_sha_hmac.klen);
+ if (rc) {
+ pr_err("qcrypto: crypto_ahash_setkey failed\n");
+ goto clr_ahash_req;
+ }
+ }
+
+ ahash_request_set_crypt(ahash_req, &fips_sg, k_out_buf,
+ tv_sha_hmac.ilen);
+ rc = crypto_ahash_digest(ahash_req);
+ if (rc == -EINPROGRESS || rc == -EBUSY) {
+ rc = wait_for_completion_interruptible(
+ &fips_completion.completion);
+ err = fips_completion.err;
+ if (!rc && !err) {
+ INIT_COMPLETION(fips_completion.completion);
+ } else {
+ pr_err("qcrypto:SHA: wait_for_completion failed\n");
+ goto clr_ahash_req;
+ }
+
+ }
+
+ if (memcmp(k_out_buf, tv_sha_hmac.digest,
+ tv_sha_hmac.diglen))
+ rc = -1;
+
+clr_ahash_req:
+ ahash_request_free(ahash_req);
+clr_tfm:
+ crypto_free_ahash(tfm);
+clr_buf:
+ kzfree(k_out_buf);
+
+ /* For any failure, return error */
+ if (rc)
+ return rc;
+
+ }
+ return rc;
+}
+
+/*
+* Cipher algorithm self tests
+*/
+int _fips_qcrypto_cipher_selftest(struct fips_selftest_data *selftest_d)
+{
+ int rc = 0, err, tv_index, num_tv;
+ struct crypto_ablkcipher *tfm;
+ struct ablkcipher_request *ablkcipher_req;
+ struct _fips_completion fips_completion;
+ char *k_align_src = NULL;
+ struct scatterlist fips_sg;
+ struct _fips_test_vector_cipher tv_cipher;
+
+ num_tv = (sizeof(fips_test_vector_cipher)) /
+ (sizeof(struct _fips_test_vector_cipher));
+
+ /* One-by-one testing */
+ for (tv_index = 0; tv_index < num_tv; tv_index++) {
+
+ memcpy(&tv_cipher, &fips_test_vector_cipher[tv_index],
+ (sizeof(struct _fips_test_vector_cipher)));
+
+ /* Single buffer allocation for in place operation */
+ k_align_src = kzalloc(tv_cipher.pln_txt_len, GFP_KERNEL);
+ if (k_align_src == NULL) {
+ pr_err("qcrypto:, Failed to allocate memory for k_align_src %ld\n",
+ PTR_ERR(k_align_src));
+ return -ENOMEM;
+ }
+
+ memcpy(&k_align_src[0], tv_cipher.pln_txt,
+ tv_cipher.pln_txt_len);
+
+ /* use_sw flags are set in dtsi file which makes
+ default Linux API calls to go to s/w crypto instead
+ of h/w crypto. This code makes sure that all selftests
+ calls always go to h/w, independent of DTSI flags. */
+ if (!strcmp(tv_cipher.mod_alg, "xts(aes)")) {
+ if (selftest_d->prefix_aes_xts_algo)
+ if (_fips_get_alg_cra_name(
+ tv_cipher.mod_alg,
+ selftest_d->algo_prefix,
+ strlen(tv_cipher.mod_alg))) {
+ rc = -1;
+ pr_err("Algo Name is too long for tv %d\n",
+ tv_index);
+ goto clr_buf;
+ }
+ } else {
+ if (selftest_d->prefix_aes_cbc_ecb_ctr_algo)
+ if (_fips_get_alg_cra_name(
+ tv_cipher.mod_alg,
+ selftest_d->algo_prefix,
+ strlen(tv_cipher.mod_alg))) {
+ rc = -1;
+ pr_err("Algo Name is too long for tv %d\n",
+ tv_index);
+ goto clr_buf;
+ }
+ }
+
+ tfm = crypto_alloc_ablkcipher(tv_cipher.mod_alg, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("qcrypto: %s algorithm not found\n",
+ tv_cipher.mod_alg);
+ rc = -ENOMEM;
+ goto clr_buf;
+ }
+
+ ablkcipher_req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!ablkcipher_req) {
+ pr_err("qcrypto: ablkcipher_request_alloc failed\n");
+ rc = -ENOMEM;
+ goto clr_tfm;
+ }
+ rc = qcrypto_cipher_set_device(ablkcipher_req,
+ selftest_d->ce_device);
+ if (rc != 0) {
+ pr_err("%s qcrypto_cipher_set_device failed with err %d\n",
+ __func__, rc);
+ goto clr_ablkcipher_req;
+ }
+ ablkcipher_request_set_callback(ablkcipher_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _fips_cb, &fips_completion);
+
+ crypto_ablkcipher_clear_flags(tfm, ~0);
+ rc = crypto_ablkcipher_setkey(tfm, tv_cipher.key,
+ tv_cipher.klen);
+ if (rc) {
+ pr_err("qcrypto: crypto_ablkcipher_setkey failed\n");
+ goto clr_ablkcipher_req;
+ }
+ sg_set_buf(&fips_sg, k_align_src, tv_cipher.enc_txt_len);
+ sg_mark_end(&fips_sg);
+ ablkcipher_request_set_crypt(ablkcipher_req,
+ &fips_sg, &fips_sg, tv_cipher.pln_txt_len,
+ tv_cipher.iv);
+
+ /**** Encryption Test ****/
+ init_completion(&fips_completion.completion);
+ rc = crypto_ablkcipher_encrypt(ablkcipher_req);
+ if (rc == -EINPROGRESS || rc == -EBUSY) {
+ rc = wait_for_completion_interruptible(
+ &fips_completion.completion);
+ err = fips_completion.err;
+ if (!rc && !err) {
+ INIT_COMPLETION(fips_completion.completion);
+ } else {
+ pr_err("qcrypto:cipher:ENC, wait_for_completion failed\n");
+ goto clr_ablkcipher_req;
+ }
+
+ }
+
+ if (memcmp(k_align_src, tv_cipher.enc_txt,
+ tv_cipher.enc_txt_len)) {
+ rc = -1;
+ goto clr_ablkcipher_req;
+ }
+
+ /**** Decryption test ****/
+ init_completion(&fips_completion.completion);
+ rc = crypto_ablkcipher_decrypt(ablkcipher_req);
+ if (rc == -EINPROGRESS || rc == -EBUSY) {
+ rc = wait_for_completion_interruptible(
+ &fips_completion.completion);
+ err = fips_completion.err;
+ if (!rc && !err) {
+ INIT_COMPLETION(fips_completion.completion);
+ } else {
+ pr_err("qcrypto:cipher:DEC, wait_for_completion failed\n");
+ goto clr_ablkcipher_req;
+ }
+
+ }
+
+ if (memcmp(k_align_src, tv_cipher.pln_txt,
+ tv_cipher.pln_txt_len))
+ rc = -1;
+
+clr_ablkcipher_req:
+ ablkcipher_request_free(ablkcipher_req);
+clr_tfm:
+ crypto_free_ablkcipher(tfm);
+clr_buf:
+ kzfree(k_align_src);
+
+ if (rc)
+ return rc;
+
+ }
+ return rc;
+}
+
+/*
+ * AEAD algorithm self tests
+ */
+int _fips_qcrypto_aead_selftest(struct fips_selftest_data *selftest_d)
+{
+ int rc = 0, err, tv_index, num_tv, authsize, buf_length;
+ struct crypto_aead *tfm;
+ struct aead_request *aead_req;
+ struct _fips_completion fips_completion;
+ struct scatterlist fips_sg, fips_assoc_sg;
+ char *k_align_src = NULL;
+ struct _fips_test_vector_aead tv_aead;
+
+ num_tv = (sizeof(fips_test_vector_aead)) /
+ (sizeof(struct _fips_test_vector_aead));
+
+ /* One-by-one testing */
+ for (tv_index = 0; tv_index < num_tv; tv_index++) {
+
+ memcpy(&tv_aead, &fips_test_vector_aead[tv_index],
+ (sizeof(struct _fips_test_vector_aead)));
+
+ if (tv_aead.pln_txt_len > tv_aead.enc_txt_len)
+ buf_length = tv_aead.pln_txt_len;
+ else
+ buf_length = tv_aead.enc_txt_len;
+
+ /* Single buffer allocation for in place operation */
+ k_align_src = kzalloc(buf_length, GFP_KERNEL);
+ if (k_align_src == NULL) {
+ pr_err("qcrypto:, Failed to allocate memory for k_align_src %ld\n",
+ PTR_ERR(k_align_src));
+ return -ENOMEM;
+ }
+ memcpy(&k_align_src[0], tv_aead.pln_txt,
+ tv_aead.pln_txt_len);
+
+ /* use_sw flags are set in dtsi file which makes
+ default Linux API calls to go to s/w crypto instead
+ of h/w crypto. This code makes sure that all selftests
+ calls always go to h/w, independent of DTSI flags. */
+ if (selftest_d->prefix_aead_algo) {
+ if (_fips_get_alg_cra_name(tv_aead.mod_alg,
+ selftest_d->algo_prefix,
+ strlen(tv_aead.mod_alg))) {
+ rc = -1;
+ pr_err("Algo Name is too long for tv %d\n",
+ tv_index);
+ goto clr_buf;
+ }
+ }
+ tfm = crypto_alloc_aead(tv_aead.mod_alg, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("qcrypto: %s algorithm not found\n",
+ tv_aead.mod_alg);
+ rc = -ENOMEM;
+ goto clr_buf;
+ }
+ aead_req = aead_request_alloc(tfm, GFP_KERNEL);
+ if (!aead_req) {
+ pr_err("qcrypto:aead_request_alloc failed\n");
+ rc = -ENOMEM;
+ goto clr_tfm;
+ }
+ rc = qcrypto_aead_set_device(aead_req, selftest_d->ce_device);
+ if (rc != 0) {
+ pr_err("%s qcrypto_cipher_set_device failed with err %d\n",
+ __func__, rc);
+ goto clr_aead_req;
+ }
+ init_completion(&fips_completion.completion);
+ aead_request_set_callback(aead_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _fips_cb, &fips_completion);
+ crypto_aead_clear_flags(tfm, ~0);
+ rc = crypto_aead_setkey(tfm, tv_aead.key, tv_aead.klen);
+ if (rc) {
+ pr_err("qcrypto:crypto_aead_setkey failed\n");
+ goto clr_aead_req;
+ }
+ authsize = abs(tv_aead.enc_txt_len - tv_aead.pln_txt_len);
+ rc = crypto_aead_setauthsize(tfm, authsize);
+ if (rc) {
+ pr_err("qcrypto:crypto_aead_setauthsize failed\n");
+ goto clr_aead_req;
+ }
+ sg_init_one(&fips_sg, k_align_src,
+ tv_aead.pln_txt_len + authsize);
+ aead_request_set_crypt(aead_req, &fips_sg, &fips_sg,
+ tv_aead.pln_txt_len , tv_aead.iv);
+ sg_init_one(&fips_assoc_sg, tv_aead.assoc, tv_aead.alen);
+ aead_request_set_assoc(aead_req, &fips_assoc_sg, tv_aead.alen);
+ /**** Encryption test ****/
+ rc = crypto_aead_encrypt(aead_req);
+ if (rc == -EINPROGRESS || rc == -EBUSY) {
+ rc = wait_for_completion_interruptible(
+ &fips_completion.completion);
+ err = fips_completion.err;
+ if (!rc && !err) {
+ INIT_COMPLETION(fips_completion.completion);
+ } else {
+ pr_err("qcrypto:aead:ENC, wait_for_completion failed\n");
+ goto clr_aead_req;
+ }
+
+ }
+ if (memcmp(k_align_src, tv_aead.enc_txt, tv_aead.enc_txt_len)) {
+ rc = -1;
+ goto clr_aead_req;
+ }
+
+ /** Decryption test **/
+ init_completion(&fips_completion.completion);
+ aead_request_set_callback(aead_req,
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ _fips_cb, &fips_completion);
+ crypto_aead_clear_flags(tfm, ~0);
+ rc = crypto_aead_setkey(tfm, tv_aead.key, tv_aead.klen);
+ if (rc) {
+ pr_err("qcrypto:aead:DEC, crypto_aead_setkey failed\n");
+ goto clr_aead_req;
+ }
+
+ authsize = abs(tv_aead.enc_txt_len - tv_aead.pln_txt_len);
+ rc = crypto_aead_setauthsize(tfm, authsize);
+ if (rc) {
+ pr_err("qcrypto:aead:DEC, crypto_aead_setauthsize failed\n");
+ goto clr_aead_req;
+ }
+
+ sg_init_one(&fips_sg, k_align_src,
+ tv_aead.enc_txt_len + authsize);
+ aead_request_set_crypt(aead_req, &fips_sg, &fips_sg,
+ tv_aead.enc_txt_len, tv_aead.iv);
+ sg_init_one(&fips_assoc_sg, tv_aead.assoc, tv_aead.alen);
+ aead_request_set_assoc(aead_req, &fips_assoc_sg,
+ tv_aead.alen);
+ rc = crypto_aead_decrypt(aead_req);
+ if (rc == -EINPROGRESS || rc == -EBUSY) {
+ rc = wait_for_completion_interruptible(
+ &fips_completion.completion);
+ err = fips_completion.err;
+ if (!rc && !err) {
+ INIT_COMPLETION(fips_completion.completion);
+ } else {
+ pr_err("qcrypto:aead:DEC, wait_for_completion failed\n");
+ goto clr_aead_req;
+ }
+
+ }
+
+ if (memcmp(k_align_src, tv_aead.pln_txt, tv_aead.pln_txt_len)) {
+ rc = -1;
+ goto clr_aead_req;
+ }
+clr_aead_req:
+ aead_request_free(aead_req);
+clr_tfm:
+ crypto_free_aead(tfm);
+clr_buf:
+ kzfree(k_align_src);
+ /* In case of any failure, return error */
+ if (rc)
+ return rc;
+ }
+ return rc;
+}
+
diff --git a/drivers/crypto/msm/qcrypto_fips.h b/drivers/crypto/msm/qcrypto_fips.h
new file mode 100644
index 0000000..9624adc
--- /dev/null
+++ b/drivers/crypto/msm/qcrypto_fips.h
@@ -0,0 +1,446 @@
+/* FIPS Known answer tests for Qcrypto.
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __CRYPTO_MSM_QCRYPTO_FIPS_H
+#define __CRYPTO_MSM_QCRYPTO_FIPS_H
+
+struct _fips_completion {
+ struct completion completion;
+ int err;
+};
+
+/*
+ * For cipher algorithms.
+ */
+struct _fips_test_vector_cipher {
+ char *key;
+ unsigned char klen;
+ char iv[16];
+ unsigned char ivlen;
+ char *pln_txt;
+ unsigned int pln_txt_len;
+ char *enc_txt;
+ unsigned int enc_txt_len;
+ char mod_alg[CRYPTO_MAX_ALG_NAME];
+};
+
+/*
+ * For Hashing / HMAC algorithms.
+ */
+struct _fips_test_vector_sha_hmac {
+ char *key;
+ unsigned char klen;
+ char *input;
+ unsigned char ilen;
+ char *digest;
+ unsigned char diglen;
+ char hash_alg[CRYPTO_MAX_ALG_NAME];
+};
+
+/*
+ *For AEAD algorithms
+ */
+struct _fips_test_vector_aead {
+ char *key;
+ unsigned char klen;
+ char iv[16];
+ unsigned char ivlen;
+ char assoc[32];
+ unsigned char alen;
+ char *pln_txt;
+ unsigned int pln_txt_len;
+ char *enc_txt;
+ unsigned int enc_txt_len;
+ char mod_alg[CRYPTO_MAX_ALG_NAME];
+};
+
+/*
+ *Test vectors for sha/hmac tests
+ */
+static struct _fips_test_vector_sha_hmac fips_test_vector_sha_hmac[] = {
+/*http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf*/
+ { /* SHA1 */
+ .hash_alg = "sha1",
+ .klen = 0,
+ .input = "abc",
+ .ilen = 3,
+ .digest = "\xa9\x99\x3e\x36\x47\x06\x81\x6a"
+ "\xba\x3e\x25\x71\x78\x50\xc2\x6c"
+ "\x9c\xd0\xd8\x9d",
+ .diglen = SHA1_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf */
+ {/* SHA256 */
+ .hash_alg = "sha256",
+ .klen = 0,
+ .input = "abc",
+ .ilen = 3,
+ .digest = "\xba\x78\x16\xbf\x8f\x01\xcf\xea"
+ "\x41\x41\x40\xde\x5d\xae\x22\x23"
+ "\xb0\x03\x61\xa3\x96\x17\x7a\x9c"
+ "\xb4\x10\xff\x61\xf2\x00\x15\xad",
+ .diglen = SHA256_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/HMAC_SHA1.pdf */
+ {/* HMAC-SHA1 */
+ .hash_alg = "hmac(sha1)",
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13",
+ .klen = 20,
+ .input = "Sample message for keylen<blocklen",
+ .ilen = 34,
+ .digest = "\x4C\x99\xFF\x0C\xB1\xB3\x1B\xD3"
+ "\x3F\x84\x31\xDB\xAF\x4D\x17\xFC"
+ "\xD3\x56\xA8\x07",
+ .diglen = SHA1_DIGEST_SIZE,
+ },
+/* http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/HMAC_SHA256.pdf */
+ {/* HMAC-SHA256 */
+ .hash_alg = "hmac(sha256)",
+ .key = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+ "\x20\x21\x22\x23\x24\x25\x26\x27"
+ "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
+ "\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F",
+ .klen = 64,
+ .input = "Sample message for keylen=blocklen",
+ .ilen = 34,
+ .digest = "\x8B\xB9\xA1\xDB\x98\x06\xF2\x0D"
+ "\xF7\xF7\x7B\x82\x13\x8C\x79\x14"
+ "\xD1\x74\xD5\x9E\x13\xDC\x4D\x01"
+ "\x69\xC9\x05\x7B\x13\x3E\x1D\x62",
+ .diglen = SHA256_DIGEST_SIZE,
+ },
+};
+
+/*
+ *Test vectors For cipher algorithms
+ */
+static struct _fips_test_vector_cipher fips_test_vector_cipher[] = {
+ /* From NIST Special Publication 800-38A, Appendix F.1 */
+ {/* AES-128 ECB */
+ .mod_alg = "ecb(aes)",
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .ivlen = 0,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x3a\xd7\x7b\xb4\x0d\x7a\x36\x60"
+ "\xa8\x9e\xca\xf3\x24\x66\xef\x97"
+ "\xf5\xd3\xd5\x85\x03\xb9\x69\x9d"
+ "\xe7\x85\x89\x5a\x96\xfd\xba\xaf"
+ "\x43\xb1\xcd\x7f\x59\x8e\xce\x23"
+ "\x88\x1b\x00\xe3\xed\x03\x06\x88"
+ "\x7b\x0c\x78\x5e\x27\xe8\xad\x3f"
+ "\x82\x23\x20\x71\x04\x72\x5d\xd4",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.1 */
+ {/* AES-256 ECB */
+ .mod_alg = "ecb(aes)",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .ivlen = 0,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xf3\xee\xd1\xbd\xb5\xd2\xa0\x3c"
+ "\x06\x4b\x5a\x7e\x3d\xb1\x81\xf8"
+ "\x59\x1c\xcb\x10\xd4\x10\xed\x26"
+ "\xdc\x5b\xa7\x4a\x31\x36\x28\x70"
+ "\xb6\xed\x21\xb9\x9c\xa6\xf4\xf9"
+ "\xf1\x53\xe7\xb1\xbe\xaf\xed\x1d"
+ "\x23\x30\x4b\x7a\x39\xf9\xf3\xff"
+ "\x06\x7d\x8d\x8f\x9e\x24\xec\xc7",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.2 */
+ {/* AES-128 CBC */
+ .mod_alg = "cbc(aes)",
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x76\x49\xab\xac\x81\x19\xb2\x46"
+ "\xce\xe9\x8e\x9b\x12\xe9\x19\x7d"
+ "\x50\x86\xcb\x9b\x50\x72\x19\xee"
+ "\x95\xdb\x11\x3a\x91\x76\x78\xb2"
+ "\x73\xbe\xd6\xb8\xe3\xc1\x74\x3b"
+ "\x71\x16\xe6\x9e\x22\x22\x95\x16"
+ "\x3f\xf1\xca\xa1\x68\x1f\xac\x09"
+ "\x12\x0e\xca\x30\x75\x86\xe1\xa7",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.2 */
+ {/* AES-256 CBC */
+ .mod_alg = "cbc(aes)",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba"
+ "\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6"
+ "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d"
+ "\x67\x9f\x77\x7b\xc6\x70\x2c\x7d"
+ "\x39\xf2\x33\x69\xa9\xd9\xba\xcf"
+ "\xa5\x30\xe2\x63\x04\x23\x14\x61"
+ "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc"
+ "\xda\x6c\x19\x07\x8c\x6a\x9d\x1b",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.5 */
+ {/* AES-128 CTR */
+ .mod_alg = "ctr(aes)",
+ .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
+ "\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ .klen = 16,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x87\x4d\x61\x91\xb6\x20\xe3\x26"
+ "\x1b\xef\x68\x64\x99\x0d\xb6\xce"
+ "\x98\x06\xf6\x6b\x79\x70\xfd\xff"
+ "\x86\x17\x18\x7b\xb9\xff\xfd\xff"
+ "\x5a\xe4\xdf\x3e\xdb\xd5\xd3\x5e"
+ "\x5b\x4f\x09\x02\x0d\xb0\x3e\xab"
+ "\x1e\x03\x1d\xda\x2f\xbe\x03\xd1"
+ "\x79\x21\x70\xa0\xf3\x00\x9c\xee",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-38A, Appendix F.5 */
+ {/* AES-256 CTR */
+ .mod_alg = "ctr(aes)",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\x60\x1e\xc3\x13\x77\x57\x89\xa5"
+ "\xb7\xa7\xf5\x04\xbb\xf3\xd2\x28"
+ "\xf4\x43\xe3\xca\x4d\x62\xb5\x9a"
+ "\xca\x84\xe9\x90\xca\xca\xf5\xc5"
+ "\x2b\x09\x30\xda\xa2\x3d\xe9\x4c"
+ "\xe8\x70\x17\xba\x2d\x84\x98\x8d"
+ "\xdf\xc9\xc5\x8d\xb6\x7a\xad\xa6"
+ "\x13\xc2\xdd\x08\x45\x79\x41\xa6",
+ .enc_txt_len = 64,
+ },
+ /* Derived From From NIST Special Publication 800-38A */
+ {/* AES-128 XTS requires 2 keys and thus length of key is twice. */
+ .mod_alg = "xts(aes)",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xba\x2a\x7d\x50\x7b\x60\x63\x3e"
+ "\xf3\x1b\x06\x14\xb4\x45\xb5\xb5"
+ "\x42\x0d\x12\x57\x28\x15\x2e\x5d"
+ "\x5a\x54\xbe\x46\x5c\x9d\x1f\x2e"
+ "\x18\x8e\x79\x07\xc7\xdf\xe7\xf8"
+ "\x78\xa6\x53\x2a\x80\xb4\xd9\xce"
+ "\x1d\xbe\x75\x7e\xb6\x11\xef\x1e"
+ "\x51\x5d\xd6\x70\x03\x51\xcc\x94",
+ .enc_txt_len = 64,
+ },
+ /* Derived From From NIST Special Publication 800-38A */
+ {/* AES-256 XTS requires 2 keys and thus length of key is twice */
+ .mod_alg = "xts(aes)",
+ .key = "\x3a\xd7\x7b\xb4\x0d\x7a\x36\x60"
+ "\xa8\x9e\xca\xf3\x24\x66\xef\x97"
+ "\xf5\xd3\xd5\x85\x03\xb9\x69\x9d"
+ "\xe7\x85\x89\x5a\x96\xfd\xba\xaf"
+ "\x43\xb1\xcd\x7f\x59\x8e\xce\x23"
+ "\x88\x1b\x00\xe3\xed\x03\x06\x88"
+ "\x7b\x0c\x78\x5e\x27\xe8\xad\x3f"
+ "\x82\x23\x20\x71\x04\x72\x5d\xd4",
+ .klen = 64,
+ .iv = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+ "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ .ivlen = 16,
+ .pln_txt = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
+ "\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
+ "\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
+ "\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
+ "\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ .pln_txt_len = 64,
+ .enc_txt = "\xd7\x2b\x90\x02\x6f\xf0\xd2\x39"
+ "\x7b\x1a\x57\x92\xd0\x1e\xc1\xb6"
+ "\x04\x8c\x08\x8e\xa4\x1f\xa0\x0f"
+ "\x5e\xd8\xaf\xda\x6e\xd2\x4e\x5b"
+ "\x23\xde\x09\xa4\x19\x79\xda\xd4"
+ "\xe9\x4b\xbc\x05\x2e\xca\x20\x7d"
+ "\xd5\x0f\x89\x88\xa3\xda\x46\x1f"
+ "\x1e\xde\x53\x78\x90\xb2\x9a\x2c",
+ .enc_txt_len = 64,
+ },
+ /* From NIST Special Publication 800-67, Appendix B.1 */
+ {/* 3DES ECB */
+ .mod_alg = "ecb(des3_ede)",
+ .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+ "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+ .klen = 24,
+ .ivlen = 0,
+ .pln_txt = "\x54\x68\x65\x20\x71\x75\x66\x63"
+ "\x6B\x20\x62\x72\x6F\x77\x6E\x20"
+ "\x66\x6F\x78\x20\x6A\x75\x6D\x70",
+ .pln_txt_len = 24,
+ .enc_txt = "\xA8\x26\xFD\x8C\xE5\x3B\x85\x5F"
+ "\xCC\xE2\x1C\x81\x12\x25\x6F\xE6"
+ "\x68\xD5\xC0\x5D\xD9\xB6\xB9\x00",
+ .enc_txt_len = 24,
+ },
+ /* Derived From From NIST Special Publication 800-38A and 800-67 */
+ {/* 3DES CBC */
+ .mod_alg = "cbc(des3_ede)",
+ .key = "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+ "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+ "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+ .klen = 24,
+ .iv = "\x00\x01\x02\x03\x04\x05\x06\x07",
+ .ivlen = 8,
+ .pln_txt = "\x54\x68\x65\x20\x71\x75\x66\x63"
+ "\x6B\x20\x62\x72\x6F\x77\x6E\x20"
+ "\x66\x6F\x78\x20\x6A\x75\x6D\x70",
+ .pln_txt_len = 24,
+ .enc_txt = "\xf3\x68\xd0\x6f\x3b\xbd\x61\x4e"
+ "\x60\xf2\xd0\x24\x5c\xad\x3f\x81"
+ "\x8d\x5c\x69\xf2\xcb\x3f\xd5\xc7",
+ .enc_txt_len = 24,
+ },
+};
+
+/*
+ *Test vectors For AEAD algorithms
+ */
+static struct _fips_test_vector_aead fips_test_vector_aead[] = {
+ /* From NIST Special Publication 800-38C: Appendix C.1 */
+ { /*AES 128-CCM */
+ .mod_alg = "ccm(aes)",
+ .key = "\x40\x41\x42\x43\x44\x45\x46\x47"
+ "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+ .klen = 16,
+ .iv = "\x07\x10\x11\x12\x13\x14\x15\x16"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .ivlen = 16,
+ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07",
+ .alen = 8,
+ .pln_txt = "\x20\x21\x22\x23",
+ .pln_txt_len = 4,
+ .enc_txt = "\x71\x62\x01\x5b\x4d\xac\x25\x5d",
+ .enc_txt_len = 8,
+ },
+ /* Derived From NIST Special Publication 800-38C: Appendix C.1 */
+ { /*AES 256-CCM */
+ .mod_alg = "ccm(aes)",
+ .key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
+ "\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
+ "\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ .klen = 32,
+ .iv = "\x07\x10\x11\x12\x13\x14\x15\x16"
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ .ivlen = 16,
+ .assoc = "\x00\x01\x02\x03\x04\x05\x06\x07",
+ .alen = 8,
+ .pln_txt = "\x20\x21\x22\x23",
+ .pln_txt_len = 4,
+ .enc_txt = "\xa8\xc7\xa9\x6a\x3a\x5b\x15\xe1",
+ .enc_txt_len = 8,
+ },
+};
+
+#endif /* __CRYPTO_MSM_QCRYPTO_FIPS_H */
diff --git a/drivers/crypto/msm/qcryptoi.h b/drivers/crypto/msm/qcryptoi.h
new file mode 100644
index 0000000..64963ad
--- /dev/null
+++ b/drivers/crypto/msm/qcryptoi.h
@@ -0,0 +1,74 @@
+/* QTI Crypto driver
+ *
+ * Copyright (c) 2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __CRYPTO_MSM_QCRYPTOI_H
+#define __CRYPTO_MSM_QCRYPTOI_H
+
+/* FIPS global status variable */
+extern enum fips_status g_fips140_status;
+
+/* The structure to hold data
+ * that selftests require
+ */
+struct fips_selftest_data {
+
+ char algo_prefix[10];
+ unsigned int ce_device;
+ bool prefix_ahash_algo;
+ bool prefix_hmac_algo;
+ bool prefix_aes_xts_algo;
+ bool prefix_aes_cbc_ecb_ctr_algo;
+ bool prefix_aead_algo;
+};
+
+#ifdef CONFIG_FIPS_ENABLE
+/*
+ * Sha/HMAC self tests
+ */
+int _fips_qcrypto_sha_selftest(struct fips_selftest_data *selftest_d);
+
+/*
+* Cipher algorithm self tests
+*/
+int _fips_qcrypto_cipher_selftest(struct fips_selftest_data *selftest_d);
+
+/*
+ * AEAD algorithm self tests
+ */
+int _fips_qcrypto_aead_selftest(struct fips_selftest_data *selftest_d);
+
+#else
+
+static inline
+int _fips_qcrypto_sha_selftest(struct fips_selftest_data *selftest_d)
+{
+ return 0;
+}
+
+static inline
+int _fips_qcrypto_cipher_selftest(struct fips_selftest_data *selftest_d)
+{
+ return 0;
+}
+
+static
+inline int _fips_qcrypto_aead_selftest(struct fips_selftest_data *selftest_d)
+{
+ return 0;
+}
+
+#endif /* CONFIG_FIPS_ENABLE*/
+
+#endif /* __CRYPTO_MSM_QCRYPTOI_H */
+
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
old mode 100755
new mode 100644
index c5adf38..117c5de85
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -133,6 +133,7 @@
header-y += fib_rules.h
header-y += fiemap.h
header-y += filter.h
+header-y += fips_status.h
header-y += firewire-cdev.h
header-y += firewire-constants.h
header-y += flat.h
diff --git a/include/linux/fips_status.h b/include/linux/fips_status.h
new file mode 100644
index 0000000..7daf27b
--- /dev/null
+++ b/include/linux/fips_status.h
@@ -0,0 +1,33 @@
+#ifndef _UAPI_FIPS_STATUS__H
+#define _UAPI_FIPS_STATUS__H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/**
+* fips_status: global FIPS140-2 status
+* @FIPS140_STATUS_NA:
+* Not a FIPS140-2 compliant Build.
+* The flag status won't
+* change throughout
+* the lifetime
+* @FIPS140_STATUS_PASS_CRYPTO:
+* KAT self tests are passed.
+* @FIPS140_STATUS_QCRYPTO_ALLOWED:
+* Integrity test is passed.
+* @FIPS140_STATUS_PASS:
+* All tests are passed and build
+* is in FIPS140-2 mode
+* @FIPS140_STATUS_FAIL:
+* One of the test is failed.
+* This will block all requests
+* to crypto modules
+*/
+enum fips_status {
+ FIPS140_STATUS_NA = 0,
+ FIPS140_STATUS_PASS_CRYPTO = 1,
+ FIPS140_STATUS_QCRYPTO_ALLOWED = 2,
+ FIPS140_STATUS_PASS = 3,
+ FIPS140_STATUS_FAIL = 0xFF
+};
+#endif /* _UAPI_FIPS_STATUS__H */
diff --git a/include/linux/qcedev.h b/include/linux/qcedev.h
index 87040df..655f2ce 100644
--- a/include/linux/qcedev.h
+++ b/include/linux/qcedev.h
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
+#include "fips_status.h"
#define QCEDEV_MAX_SHA_BLOCK_SIZE 64
#define QCEDEV_MAX_BEARER 31
@@ -217,6 +218,16 @@
enum qcedev_sha_alg_enum alg;
};
+/**
+* struct qfips_verify_t - Holds data for FIPS Integrity test
+* @kernel_size (IN): Size of kernel Image
+* @kernel (IN): pointer to buffer containing the kernel Image
+*/
+struct qfips_verify_t {
+ unsigned kernel_size;
+ void *kernel;
+};
+
#define QCEDEV_IOC_MAGIC 0x87
@@ -238,4 +249,8 @@
_IO(QCEDEV_IOC_MAGIC, 8)
#define QCEDEV_IOCTL_GET_CMAC_REQ \
_IOWR(QCEDEV_IOC_MAGIC, 9, struct qcedev_cipher_op_req)
+#define QCEDEV_IOCTL_UPDATE_FIPS_STATUS \
+ _IOWR(QCEDEV_IOC_MAGIC, 10, enum fips_status)
+#define QCEDEV_IOCTL_QUERY_FIPS_STATUS \
+ _IOR(QCEDEV_IOC_MAGIC, 11, enum fips_status)
#endif /* _QCEDEV__H */