Import new HW_RANDOM_MSM

Change-Id: If9f43dd61d7e07f8e695455d8705ba881c672cbd
This commit is contained in:
followmsi 2023-02-27 23:07:30 +01:00
parent 5aba32ba26
commit db92d029a4
10 changed files with 2162 additions and 29 deletions

View File

@ -254,6 +254,8 @@ config UML_RANDOM
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

View File

@ -22,4 +22,4 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o
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

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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__ */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 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
@ -24,6 +24,17 @@
#include <linux/types.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#include <mach/msm_bus.h>
#include <linux/qrng.h>
#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"
@ -42,42 +53,79 @@
#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;
#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
};
static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
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)
{
long ret = 0;
pr_debug("ioctl: cmd = %d\n", cmd);
switch (cmd) {
case QRNG_IOCTL_RESET_BUS_BANDWIDTH:
pr_info("calling msm_rng_bus_scale(LOW)\n");
ret = msm_bus_scale_client_update_request(
msm_rng_device_info.qrng_perf_client, 0);
if (ret)
pr_err("failed qrng_reset_bus_bw, ret = %ld\n", ret);
break;
default:
pr_err("Unsupported IOCTL call");
break;
}
return ret;
}
/*
*
* 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) {
dev_err(&pdev->dev, "failed to enable clock in callback\n");
return 0;
}
/* read random data from h/w */
do {
/* check status bit if data is available */
@ -93,17 +141,187 @@ static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
*(retdata++) = val;
currsize += 4;
/* make sure we stay on 32bit boundary */
if ((maxsize - currsize) < 4)
break;
} while (currsize < maxsize);
} 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;
} while (currsize < maxsize);
/* 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,
@ -115,6 +333,12 @@ static int __devinit msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
unsigned long reg_val = 0;
int ret = 0;
if (msm_rng_dev->qrng_perf_client) {
ret = msm_bus_scale_client_update_request(
msm_rng_dev->qrng_perf_client, 1);
if (ret)
pr_err("bus_scale_client_update_req failed!\n");
}
/* Enable the PRNG CLK */
ret = clk_prepare_enable(msm_rng_dev->prng_clk);
if (ret) {
@ -145,18 +369,40 @@ static int __devinit msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
*/
mb();
}
clk_disable_unprepare(msm_rng_dev->prng_clk);
return 0;
}
static const struct file_operations msm_rng_fops = {
.unlocked_ioctl = msm_rng_ioctl,
};
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;
struct msm_rng_device *msm_rng_dev = NULL;
void __iomem *base = NULL;
int error = 0;
int ret = 0;
struct device *dev;
struct msm_bus_scale_pdata *qrng_platform_support = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
@ -165,7 +411,7 @@ static int __devinit msm_rng_probe(struct platform_device *pdev)
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;
@ -180,8 +426,21 @@ static int __devinit msm_rng_probe(struct platform_device *pdev)
}
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 */
msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
"qcom,msm-rng-iface-clk")))
msm_rng_dev->prng_clk = clk_get(&pdev->dev,
"iface_clk");
else
msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm_rng_dev->prng_clk)) {
dev_err(&pdev->dev, "failed to register clock source\n");
error = -EPERM;
@ -192,6 +451,17 @@ static int __devinit msm_rng_probe(struct platform_device *pdev)
msm_rng_dev->pdev = pdev;
platform_set_drvdata(pdev, msm_rng_dev);
if (pdev->dev.of_node) {
/* Register bus client */
qrng_platform_support = msm_bus_cl_get_pdata(pdev);
msm_rng_dev->qrng_perf_client = msm_bus_scale_register_client(
qrng_platform_support);
msm_rng_device_info.qrng_perf_client =
msm_rng_dev->qrng_perf_client;
if (!msm_rng_dev->qrng_perf_client)
pr_err("Unable to register bus client\n");
}
/* Enable rng h/w */
error = msm_rng_enable_hw(msm_rng_dev);
@ -206,15 +476,39 @@ static int __devinit msm_rng_probe(struct platform_device *pdev)
error = -EPERM;
goto rollback_clk;
}
ret = register_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME, &msm_rng_fops);
return 0;
msm_rng_class = class_create(THIS_MODULE, "msm-rng");
if (IS_ERR(msm_rng_class)) {
pr_err("class_create failed\n");
return PTR_ERR(msm_rng_class);
}
dev = device_create(msm_rng_class, NULL, MKDEV(QRNG_IOC_MAGIC, 0),
NULL, "msm-rng");
if (IS_ERR(dev)) {
pr_err("Device create failed\n");
error = PTR_ERR(dev);
goto unregister_chrdev;
}
cdev_init(&msm_rng_cdev, &msm_rng_fops);
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);
class_destroy(msm_rng_class);
rollback_clk:
clk_put(msm_rng_dev->prng_clk);
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;
}
@ -223,11 +517,20 @@ static int __devexit msm_rng_remove(struct platform_device *pdev)
{
struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
unregister_chrdev(QRNG_IOC_MAGIC, DRIVER_NAME);
hwrng_unregister(&msm_rng);
clk_put(msm_rng_dev->prng_clk);
iounmap(msm_rng_dev->base);
platform_set_drvdata(pdev, NULL);
kfree(msm_rng_dev);
if (msm_rng_dev->qrng_perf_client)
msm_bus_scale_unregister_client(msm_rng_dev->qrng_perf_client);
if (msm_rng_dev->drbg_ctx) {
fips_drbg_final(msm_rng_dev->drbg_ctx);
kzfree(msm_rng_dev->drbg_ctx);
msm_rng_dev->drbg_ctx = NULL;
}
kzfree(msm_rng_dev);
return 0;
}
@ -260,6 +563,10 @@ static void __exit msm_rng_exit(void)
}
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");

View File

@ -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