/* * 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 #include #include #include #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; }