blob: 3c236058d5e3dea10f22502e62be42a045c7f978 [file] [log] [blame]
/*
* 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;
}