mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
Import new HW_RANDOM_MSM
Change-Id: If9f43dd61d7e07f8e695455d8705ba881c672cbd
This commit is contained in:
parent
5aba32ba26
commit
db92d029a4
10 changed files with 2162 additions and 29 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
938
drivers/char/hw_random/ctr_drbg.c
Normal file
938
drivers/char/hw_random/ctr_drbg.c
Normal 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;
|
||||
}
|
||||
|
114
drivers/char/hw_random/ctr_drbg.h
Normal file
114
drivers/char/hw_random/ctr_drbg.h
Normal 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__ */
|
290
drivers/char/hw_random/fips_drbg.c
Normal file
290
drivers/char/hw_random/fips_drbg.c
Normal 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;
|
||||
}
|
||||
|
58
drivers/char/hw_random/fips_drbg.h
Normal file
58
drivers/char/hw_random/fips_drbg.h
Normal 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__ */
|
346
drivers/char/hw_random/msm_fips_selftest.c
Normal file
346
drivers/char/hw_random/msm_fips_selftest.c
Normal 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;
|
||||
|
||||
}
|
||||
|
31
drivers/char/hw_random/msm_fips_selftest.h
Normal file
31
drivers/char/hw_random/msm_fips_selftest.h
Normal 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__ */
|
|
@ -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");
|
||||
|
|
47
drivers/char/hw_random/msm_rng.h
Normal file
47
drivers/char/hw_random/msm_rng.h
Normal 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
|
Loading…
Reference in a new issue