diff options
Diffstat (limited to 'src/s390_drbg.c')
-rw-r--r-- | src/s390_drbg.c | 882 |
1 files changed, 882 insertions, 0 deletions
diff --git a/src/s390_drbg.c b/src/s390_drbg.c new file mode 100644 index 0000000..75f9bc6 --- /dev/null +++ b/src/s390_drbg.c @@ -0,0 +1,882 @@ +/* + * This program is released under the Common Public License V1.0 + * + * You should have received a copy of Common Public License V1.0 along with + * with this program. + * + * DRBG conforming to NIST SP800-90A + * + * Author(s): Patrick Steuer <patrick.steuer@de.ibm.com> + * + * Copyright IBM Corp. 2015 + */ + +#include <errno.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <syslog.h> + +#include "fips.h" +#include "s390_crypto.h" +#include "s390_drbg.h" +#include "s390_sha.h" + +#define MAX_NO_OF_BYTES (255 * DRBG_OUT_LEN) /* limit for hash_df */ + +/* + * Test DRBG mechanisms + */ +static ica_drbg_mech_t DRBG_TESTMECH1 = {.error_state = DRBG_HEALTH_TEST_FAIL}; +static ica_drbg_mech_t DRBG_TESTMECH2 = {.error_state = 0}; + +/* + * Auxiliary functions + */ +static int test_uninstantiate(ica_drbg_mech_t *mech); + +static int test_instantiate_error_handling(ica_drbg_mech_t *mech); + +static int test_reseed_error_handling(ica_drbg_mech_t *mech); + +static int test_generate_error_handling(ica_drbg_mech_t *mech); + +static int set_error_state(ica_drbg_mech_t *mech, + int error); + +/* + * DRBG mechanism list. Add new DRBG mechanism here: + */ +ica_drbg_mech_t *const DRBG_MECH_LIST[] = {&DRBG_SHA512, + &DRBG_TESTMECH1, + &DRBG_TESTMECH2}; +const size_t DRBG_MECH_LIST_LEN = sizeof(DRBG_MECH_LIST) + / sizeof(DRBG_MECH_LIST[0]); + +/* + * DRBG SEI list. The first string (element 0) has the highest priority. + */ +const char *const DRBG_SEI_LIST[] = {"/dev/hwrng", + "/dev/prandom"}; +const size_t DRBG_SEI_LIST_LEN = sizeof(DRBG_SEI_LIST) + / sizeof(DRBG_SEI_LIST[0]); + +/* + * DRBG mechanism functions + */ +int drbg_instantiate(ica_drbg_t **sh, + int sec, + bool pr, + ica_drbg_mech_t *mech, + const unsigned char *pers, + size_t pers_len, + bool test_mode, + const unsigned char *test_nonce, + size_t test_nonce_len, + const unsigned char *test_entropy, + size_t test_entropy_len) +{ + void *init_ws; + int status; + + /* 9.1 Instantiate Process */ + + if(!sh || *sh) + return DRBG_SH_INV; + status = drbg_mech_valid(mech); + if(status) + return status; + + /* step 1 */ + if(sec > mech->highest_supp_sec) + return DRBG_SEC_NOTSUPP; + + /* step 2: pr is supported. */ + + /* step 3 */ + if(!pers) + pers_len = 0; + else if(pers_len <= 0) + pers = NULL; + if(pers_len > mech->max_pers_len) + return DRBG_PERS_INV; + + /* step 4 */ + if(sec <= DRBG_SEC_112) + sec = DRBG_SEC_112; + else + if(sec <= DRBG_SEC_128) + sec = DRBG_SEC_128; + else + if(sec <= DRBG_SEC_192) + sec = DRBG_SEC_192; + else + sec = DRBG_SEC_256; + + /* step 5: Null step. */ + + const size_t entropy_len = !test_mode ? + (sec + 7) / 8 + DRBG_ADD_ENTROPY_LEN : + test_entropy_len; + const size_t nonce_len = !test_mode ? DRBG_NONCE_LEN : test_nonce_len; + unsigned char entropy[entropy_len]; + unsigned char nonce[nonce_len]; + + /* step 6 */ + if(!test_mode) /* use entropy from SEI */ + status = drbg_get_entropy_input(pr, sec, mech->max_len, + entropy, entropy_len); + else{ + if(test_entropy){ /* use test entropy */ + memcpy(entropy, test_entropy, entropy_len); + status = 0; + } + else{ /* test for entropy source failure */ + status = drbg_get_entropy_input(pr, sec, mech->max_len, + entropy, entropy_len); + } + } + + /* step 7 */ + if(status){ + status = DRBG_ENTROPY_SOURCE_FAIL; + goto _exit_; + } + + /* step 8 */ + if(!test_mode){ /* use thread id + timestamp + counter */ + status = drbg_get_nonce(nonce, nonce_len); + } + else{ /* use test nonce */ + memcpy(nonce, test_nonce, nonce_len); + } + if(status){ + status = DRBG_NONCE_INV; + goto _exit_; + } + + /* step 9 */ + status = mech->instantiate(&init_ws, sec, pers, pers_len, entropy, + entropy_len, nonce, nonce_len); + if(status){ + if(0 > status) + set_error_state(mech, status); + goto _exit_; + } + + /* step 10 */ + *sh = malloc(sizeof(ica_drbg_t)); + if(!*sh){ + status = DRBG_NOMEM; + goto _exit_; + } + + /* step 11 */ + drbg_recursive_mutex_init(&(*sh)->lock); + (*sh)->mech = mech; + (*sh)->ws = init_ws; + (*sh)->sec = sec; + (*sh)->pr = pr; + + /* step 12 */ +_exit_: + drbg_zmem(entropy, entropy_len); + drbg_zmem(nonce, nonce_len); + return status; +} + +int drbg_reseed(ica_drbg_t *sh, + bool pr, + const unsigned char *add, + size_t add_len, + bool test_mode, + const unsigned char *test_entropy, + size_t test_entropy_len) +{ + int status; + + /* 9.2 Reseed Process */ + + /* step 1 */ + if(!sh || !sh->ws) + return DRBG_SH_INV; + status = drbg_mech_valid(sh->mech); + if(status) + return status; + + /* step 2 */ + if(pr && !sh->pr) + return DRBG_PR_NOTSUPP; + + /* step 3 */ + if(!add) + add_len = 0; + else if(add_len <= 0) + add = NULL; + if(add_len > sh->mech->max_add_len) + return DRBG_ADD_INV; + + const size_t entropy_len = !test_mode ? + (sh->sec + 7) / 8 + DRBG_ADD_ENTROPY_LEN : + test_entropy_len; + unsigned char entropy[entropy_len]; + + /* step 4 */ + if(!test_mode) /* use entropy from SEI */ + status = drbg_get_entropy_input(pr, sh->sec, sh->mech->max_len, + entropy, entropy_len); + else{ + if(test_entropy){ /* use test entropy */ + memcpy(entropy, test_entropy, entropy_len); + status = 0; + } + else{ /* test for entropy source failure */ + status = drbg_get_entropy_input(pr, sh->sec, + sh->mech->max_len, + entropy, entropy_len); + } + } + + /* step 5 */ + if(status){ + status = DRBG_ENTROPY_SOURCE_FAIL; + goto _exit_; + } + + /* steps 6 and 7 */ + pthread_mutex_lock(&sh->lock); + status = sh->mech->reseed(sh->ws, add, add_len, entropy, entropy_len); + pthread_mutex_unlock(&sh->lock); + if(0 > status) + set_error_state(sh->mech, status); + + /* step 8 */ +_exit_: + drbg_zmem(entropy, entropy_len); + return status; /* return reseed status */ +} + +int drbg_generate(ica_drbg_t *sh, + int sec, + bool pr, + const unsigned char *add, + size_t add_len, + bool test_mode, + const unsigned char *test_entropy, + size_t test_entropy_len, + unsigned char *prnd, + size_t prnd_len) +{ + int status; + bool reseed_required; + + /* 9.3 Generate Process */ + + /* step 1 */ + if(!sh || !sh->ws) + return DRBG_SH_INV; + status = drbg_mech_valid(sh->mech); + if(status) + return status; + + /* step 2 */ + if(prnd_len > sh->mech->max_no_of_bytes_per_req) + return DRBG_REQUEST_INV; + + /* step 3 */ + if(sec > sh->sec) + return DRBG_SEC_NOTSUPP; + + /* step 4 */ + if(!add) + add_len = 0; + else if(add_len <= 0) + add = NULL; + if(add_len > sh->mech->max_add_len) + return DRBG_ADD_INV; + + /* step 5 */ + if(pr && !sh->pr) + return DRBG_PR_NOTSUPP; + + /* step 6 */ + reseed_required = false; + + /* step 7 */ +_reseed_req_: + pthread_mutex_lock(&sh->lock); + if(pr || reseed_required){ + /* steps 7.1 and 7.3 */ + status = drbg_reseed(sh, pr, add, add_len, test_mode, + test_entropy, test_entropy_len); + /* step 7.2 */ + if(status){ + pthread_mutex_unlock(&sh->lock); + return status; /* return reseed status */ + } + /* step 7.4 */ + add = NULL; + add_len = 0; + /* step 7.5 */ + reseed_required = false; + } + + /* steps 8 and 10 */ + status = sh->mech->generate(sh->ws, add, add_len, prnd, prnd_len); + pthread_mutex_unlock(&sh->lock); + + /* step 9 */ + if(DRBG_RESEED_REQUIRED == status){ + /* step 9.1 */ + reseed_required = true; + /* step 9.2 */ + if(sh->pr) + pr = true; + /* step 9.3 */ + goto _reseed_req_; + } + else if(0 > status) + set_error_state(sh->mech, status); + + /* step 11 */ + return status; +} + +int drbg_uninstantiate(ica_drbg_t **sh, + bool test_mode) +{ + int status; + + /* 9.4 Uninstantiate Process */ + + /* step 1 */ + if(!sh || !(*sh) || !(*sh)->ws) + return DRBG_SH_INV; + status = drbg_mech_valid((*sh)->mech); + if(status > 0) /* uninst. is possible in error state (< 0) */ + return status; + + /* step 2 */ + pthread_mutex_lock(&(*sh)->lock); + status = (*sh)->mech->uninstantiate(&(*sh)->ws, test_mode); + if(status){ + if(0 > status) + set_error_state((*sh)->mech, status); + return status; /* return uninstantiate status */ + } + pthread_mutex_unlock(&(*sh)->lock); + pthread_mutex_destroy(&(*sh)->lock); + drbg_zmem(*sh, sizeof(ica_drbg_t)); + if(test_mode) + status = drbg_check_zmem(*sh, sizeof(ica_drbg_t)); + free(*sh); + *sh = NULL; + + /* step 3 */ + return status; +} + +int drbg_health_test(const void *func, + int sec, + bool pr, + ica_drbg_mech_t *mech) +{ + int status, i; + const int SEC[] = {DRBG_SEC_112, DRBG_SEC_128, DRBG_SEC_192, + DRBG_SEC_256}; + + status = drbg_mech_valid(mech); + if(status) + return status; + + if(drbg_instantiate == func){ + /* Test vectors. */ + status = mech->health_test(drbg_instantiate, sec, pr); + if(status){ + if(0 > status) + set_error_state(mech, status); + return status; + } + + /* Error handling test. */ + status = test_instantiate_error_handling(mech); + if(status) + return set_error_state(mech, DRBG_HEALTH_TEST_FAIL); + + /* Uninstantiate test. */ + status = test_uninstantiate(mech); + if(status) + return set_error_state(mech, DRBG_HEALTH_TEST_FAIL); + + return 0; + } + else if(drbg_reseed == func){ + /* Test vectors. */ + status = mech->health_test(drbg_reseed, sec, pr); + if(status){ + if(0 > status) + return set_error_state(mech, status); + return status; + } + + /* Error handling test. */ + status = test_reseed_error_handling(mech); + if(status) + return set_error_state(mech, status); + + /* Uninstantiate test. */ + status = test_uninstantiate(mech); + if(status) + return set_error_state(mech, status); + + return 0; + } + else if(drbg_generate == func){ + /* Test vectors: test all combinations sec, pr supp, pr req */ + for(i = 0; i < sizeof(SEC) / sizeof(SEC[0]); i++){ + if(SEC[i] > mech->highest_supp_sec) + break; + + status = mech->health_test(drbg_generate, SEC[i], + false); + if(status){ + if(0 > status) + set_error_state(mech, status); + return status; + } + + status = mech->health_test(drbg_generate, SEC[i], + true); + if(status){ + if(0 > status) + set_error_state(mech, status); + return status; + } + } + + /* Error handling test. */ + status = test_generate_error_handling(mech); + if(status) + return set_error_state(mech, status); + + /* Uninstantiate test.*/ + status = test_uninstantiate(mech); + if(status) + return set_error_state(mech, status); + + return 0; + } + else + return DRBG_REQUEST_INV; +} + +/* + * Auxiliary functions + */ +int drbg_get_entropy_input(bool pr, + int min_entropy, + size_t max_len, + unsigned char *entropy, + size_t entropy_len) +{ + size_t min_len; + size_t priority; + FILE *fd; + int status; + + /* NIST SP800-90C Get_entropy_input */ + + if(!entropy) + return DRBG_REQUEST_INV; + if(0 > min_entropy) + min_entropy = 0; + + min_len = ((min_entropy + 7) / 8); + + if(min_len > max_len) + return DRBG_REQUEST_INV; + + if(entropy_len < min_len || entropy_len > max_len) + return DRBG_REQUEST_INV; + + for(priority = 0; priority < DRBG_SEI_LIST_LEN; priority++){ + fd = fopen(DRBG_SEI_LIST[priority], "r"); + if(fd){ + status = fread(entropy, entropy_len, 1, fd); + fclose(fd); + if(status == 1) + return 0; + } + } + + return DRBG_ENTROPY_SOURCE_FAIL; +} + +int drbg_get_nonce(unsigned char *nonce, + size_t nonce_len) +{ + size_t i; + static uint16_t ctr; + + /* The buffer for nonce must hold a 16 byte timestamp. */ + if(DRBG_NONCE_LEN != nonce_len) + return DRBG_NONCE_INV; + + /* Get timestamp from TOD clock. */ + s390_stcke_hw(nonce); + /* The value in the bits 72 - 111 is non-zero when the clock is + * running. */ + const unsigned char zero_buff[(111 - 72 + 1) / 8] = {0}; + int status = !memcmp(nonce + (72 / 8), &zero_buff, + (111 - 72 + 1) / 8); + if(status) + return DRBG_NONCE_INV; + + /* Get thread id. */ + pthread_t thread_id = pthread_self(); + + /* Store bytewise XOR of the thread id in first byte. */ + for(i = 0; i < sizeof(thread_id); i++) + *nonce ^= *((unsigned char *)&thread_id + i); + + /* Store counter in the last two bytes. Since TOD clock is thread-save, + * this counter is chosen not to be thread-safe. */ + *((uint16_t *)(nonce + DRBG_NONCE_LEN - 2)) = ctr; + ctr++; + + return 0; +} + +int drbg_hash_df(const unsigned char *input, + size_t input_len, + unsigned char *req_bytes, + size_t req_bytes_len) +{ + uint64_t shabuff[2]; + size_t i; + int status; + unsigned char counter; + + /* 10.4.1 Hash_df Process */ + + if(!req_bytes_len) + return 0; /* no bytes requested: do nothing */ + if(!req_bytes || !input) + return DRBG_REQUEST_INV; + if (MAX_NO_OF_BYTES < req_bytes_len) + return DRBG_REQUEST_INV; + + const uint32_t no_of_bits_to_return = req_bytes_len * 8; + + /* steps 1 and 2 */ + const size_t len = (req_bytes_len + DRBG_OUT_LEN - 1) / DRBG_OUT_LEN; + unsigned char temp[len * DRBG_OUT_LEN]; + + /* step 3 */ + counter = 0x01; + + /* step 4 */ + const size_t _tmp_len = 1 + sizeof(no_of_bits_to_return) + input_len; + unsigned char _tmp[_tmp_len]; + memcpy(_tmp + 1, &no_of_bits_to_return, sizeof(no_of_bits_to_return)); + memcpy(_tmp + 1 + sizeof(no_of_bits_to_return), input, input_len); + for(i = 1; i <= len; i++){ + /* step 4.1 */ + _tmp[0] = counter; + status = s390_sha_hw(SHA_512_DEFAULT_IV, _tmp, _tmp_len, + temp + (i - 1) * DRBG_OUT_LEN, + SHA_MSG_PART_ONLY, &shabuff[0], + &shabuff[1], SHA_512); + if(status){ + status = DRBG_HEALTH_TEST_FAIL; + goto _exit_; + } + /* step 4.2 */ + counter++; + } + + /* step 5 */ + memcpy(req_bytes, temp, req_bytes_len); + + /* step 6 */ +_exit_: + drbg_zmem(_tmp, _tmp_len); + drbg_zmem(temp, len * DRBG_OUT_LEN); + return status; +} + +static int test_uninstantiate(ica_drbg_mech_t *mech) +{ + /* Error handling test. */ + int status; + + status = drbg_uninstantiate(NULL, false); + if(DRBG_SH_INV != status) + return DRBG_HEALTH_TEST_FAIL; + + /* Test if internal state is zeroised. */ + ica_drbg_t *sh = NULL; + status = drbg_instantiate(&sh, mech->highest_supp_sec, true, mech, + NULL, 0, false, NULL, 0, NULL, 0); + if(status) + return status; + + status = drbg_uninstantiate(&sh, true); + if(status) + return status; + + return 0; +} + +static int test_instantiate_error_handling(ica_drbg_mech_t *mech) +{ + int test_no = 0, status; + + /* Pointer to state handle is NULL. */ + test_no++; + status = drbg_instantiate(NULL, 0, true, mech, NULL, 0, false, NULL, + 0, NULL, 0); + if(DRBG_SH_INV != status) + return test_no; + + /* State handle is already in use. */ + test_no++; + ica_drbg_t *sh = NULL; + ica_drbg_t test_sh = {.lock = PTHREAD_MUTEX_INITIALIZER}; + drbg_recursive_mutex_init(&test_sh.lock); + sh = &test_sh; + test_sh.mech = mech; + test_sh.ws = (void *)"ws"; + status = drbg_instantiate(&sh, 0, true, mech, NULL, 0, false, NULL, 0, + NULL, 0); + if(DRBG_SH_INV != status) + return test_no; + test_sh.mech = NULL; + sh = NULL; + + /* Mechanism is not supported. */ + test_no++; + ica_drbg_mech_t test_mech = {.lock = PTHREAD_RWLOCK_INITIALIZER}; + status = drbg_instantiate(&sh, 0, true, &test_mech, NULL, 0, false, + NULL, 0, NULL, 0); + if(DRBG_MECH_INV != status) + return test_no; + + /* Mechanism in error state. */ + test_no++; + status = drbg_instantiate(&sh, 0, true, &DRBG_TESTMECH1, NULL, 0, + false, NULL, 0, NULL, 0); + if(0 <= status) + return test_no; + + /* Security strength is not supported. */ + test_no++; + status = drbg_instantiate(&sh, mech->highest_supp_sec + 1, true, mech, + NULL, 0, true, NULL, 0, NULL, 0); + if(DRBG_SEC_NOTSUPP != status) + return test_no; + + /* Personalization string is too long. */ + test_no++; + status = drbg_instantiate(&sh, 0, true, mech, (unsigned char *)"pers", + mech->max_pers_len + 1, false, NULL, 0, NULL, + 0); + if(DRBG_PERS_INV != status) + return test_no; + + /* Entropy source failed. */ + test_no++; + status = drbg_instantiate(&sh, 0, true, mech, NULL, 0, true, NULL, 0, + NULL, 0); + if(DRBG_ENTROPY_SOURCE_FAIL != status) + return test_no; + + return 0; +} + +static int test_reseed_error_handling(ica_drbg_mech_t *mech) +{ + int test_no = 0, status; + + /* Invalid state handle. */ + status = drbg_reseed(NULL, true, NULL, 0, false, NULL, 0); + if(DRBG_SH_INV != status) + return test_no; + + /* Mechanism is not supported. */ + test_no++; + ica_drbg_mech_t test_mech = {.lock = PTHREAD_RWLOCK_INITIALIZER}; + ica_drbg_t test_sh = {.lock = PTHREAD_MUTEX_INITIALIZER}; + drbg_recursive_mutex_init(&test_sh.lock); + test_sh.mech = &test_mech; + test_sh.ws = (void *)"ws"; + status = drbg_reseed(&test_sh, true, NULL, 0, false, NULL, 0); + if(DRBG_MECH_INV != status) + return test_no; + test_sh.mech = NULL; + + /* Mechanism is in error state */ + test_no++; + test_sh.mech = &DRBG_TESTMECH1; + status = drbg_reseed(&test_sh, true, NULL, 0, false, NULL, 0); + if(0 <= status) + return test_no; + test_sh.mech = NULL; + + /* Prediction resistance is requested but not supported. */ + test_no++; + test_sh.mech = &DRBG_TESTMECH2; + status = drbg_reseed(&test_sh, true, NULL, 0, false, NULL, 0); + if(DRBG_PR_NOTSUPP != status) + return test_no; + test_sh.mech = NULL; + + /* Additional input is too long. */ + test_no++; + test_sh.mech = mech; + status = drbg_reseed(&test_sh, false, (unsigned char *)"add", + mech->max_add_len + 1, false, NULL, 0); + if(DRBG_ADD_INV != status) + return test_no; + test_sh.mech = NULL; + + /* Entropy source failed. */ + test_no++; + test_sh.mech = mech; + status = drbg_reseed(&test_sh, false, NULL, 0, true, NULL, 0); + if(DRBG_ENTROPY_SOURCE_FAIL != status) + return test_no; + test_sh.mech = NULL; + + return 0; +} + +static int test_generate_error_handling(ica_drbg_mech_t *mech) +{ + const int SEC[] = {DRBG_SEC_112, DRBG_SEC_128, DRBG_SEC_192, + DRBG_SEC_256}; + int test_no = 0, status, i; + unsigned char prnd; + + /* Invalid state handle. */ + test_no++; + status = drbg_generate(NULL, mech->highest_supp_sec, false, NULL, 0, + false, NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_SH_INV != status) + return test_no; + + /* Mechanism is not supported. */ + test_no++; + ica_drbg_mech_t test_mech = {.lock = PTHREAD_RWLOCK_INITIALIZER}; + ica_drbg_t test_sh = {.lock = PTHREAD_MUTEX_INITIALIZER}; + drbg_recursive_mutex_init(&test_sh.lock); + test_sh.mech = &test_mech; + test_sh.ws = (void *)"ws"; + status = drbg_generate(&test_sh, mech->highest_supp_sec, false, NULL, 0, + false, NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_MECH_INV != status) + return test_no; + test_sh.mech = NULL; + + /* Mechanism is in error state. */ + test_no++; + test_sh.mech = &DRBG_TESTMECH1; + status = drbg_generate(&test_sh, mech->highest_supp_sec, false, NULL, 0, + false, NULL, 0, &prnd, sizeof(prnd)); + if(0 <= status) + return test_no; + test_sh.mech = NULL; + + /* Too many pseudorandom bytes requested. */ + test_no++; + test_sh.mech = mech; + + status = drbg_generate(&test_sh, mech->highest_supp_sec, false, NULL, + 0, false, NULL, 0, &prnd, + mech->max_no_of_bytes_per_req + 1); + if(DRBG_REQUEST_INV != status) + return test_no; + test_sh.mech = NULL; + + /* Requested security strength is too high. */ + test_no++; + test_sh.mech = mech; + test_sh.sec = DRBG_SEC_112; + + status = drbg_generate(&test_sh, DRBG_SEC_112 + 1, false, NULL, 0, + true, NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_SEC_NOTSUPP != status) + return test_no; + test_sh.mech = NULL; + test_sh.sec = 0; + + /* Additional input is too long. */ + test_no++; + test_sh.mech = mech; + test_sh.sec = mech->highest_supp_sec; + + status = drbg_generate(&test_sh, mech->highest_supp_sec, false, + (unsigned char *)"add", mech->max_add_len + 1, + false, NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_ADD_INV != status) + return test_no; + test_sh.mech = NULL; + test_sh.sec = 0; + + /* Prediction resistance is requested but not supported. */ + test_no++; + test_sh.mech = mech; + test_sh.sec = mech->highest_supp_sec; + for(i = 0; i < sizeof(SEC); i++){ + if(SEC[i] > mech->highest_supp_sec) + break; + status = drbg_generate(&test_sh, SEC[i], true, NULL, 0, true, + NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_PR_NOTSUPP != status) + return test_no; + } + test_sh.mech = NULL; + test_sh.sec = 0; + + /* Entropy source failed. */ + test_no++; + test_sh.mech = mech; + test_sh.sec = mech->highest_supp_sec; + test_sh.pr = true; + + status = drbg_generate(&test_sh, mech->highest_supp_sec, true, NULL, 0, + true, NULL, 0, &prnd, sizeof(prnd)); + if(DRBG_ENTROPY_SOURCE_FAIL != status) + return test_no; + test_sh.mech = NULL; + test_sh.sec = 0; + test_sh.pr = false; + + return 0; +} + +static int set_error_state(ica_drbg_mech_t *mech, + int error) +{ +#ifdef ICA_FIPS + fips |= ICA_FIPS_RNG; + + /* Write to syslog in FIPS-enabled built. The DRBG failure is critical + * here since the old PRNG code is diasbled at compile time. */ + switch (error) { + case DRBG_HEALTH_TEST_FAIL: + syslog(LOG_ERR, "Libica DRBG-%s test failed.", mech->id); + break; + case DRBG_ENTROPY_SOURCE_FAIL: + syslog(LOG_ERR, "Libica DRBG-%s entropy source failed.", + mech->id); + break; + default: + break; /* unreachable */ + } +#endif /* ICA_FIPS */ + + return mech->error_state = error; +} |