/** * @file trng.c * @brief Semidrive CRYPTO trng source file. * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. */ #include #include "trng.h" #include "sdrv_crypto_utility.h" #define TRNG_REG32(addr) ((volatile uint32_t *)(uintptr_t)(addr)) #define trng_readl(a) (*TRNG_REG32(a)) typedef uint32_t GET_RAND_WORDS(uint32_t *a, uint32_t words); /* function: get trng IP version * parameters: none * return: trng IP version * caution: */ uint32_t trng_get_version(void) { return TRNG_VERSION; } /* function: TRNG global interruption enable * parameters: none * return: none * caution: */ void trng_global_int_enable(void) { TRNG_CR |= ((0x1) << TRNG_GLOBAL_INT_OFFSET); } /* function: TRNG global interruption disable * parameters: none * return: none * caution: */ void trng_global_int_disable(void) { TRNG_CR &= ~((0x1) << TRNG_GLOBAL_INT_OFFSET); } /* function: TRNG empty-read interruption enable * parameters: none * return: none * caution: * 1. works when global interruption is enabled */ void trng_empty_read_int_enable(void) { TRNG_CR |= ((0x1) << TRNG_READ_EMPTY_INT_OFFSET); } /* function: TRNG empty-read interruption disable * parameters: none * return: none * caution: */ void trng_empty_read_int_disable(void) { TRNG_CR &= ~((0x1) << TRNG_READ_EMPTY_INT_OFFSET); } /* function: TRNG data interruption enable * parameters: none * return: none * caution: * 1. works when global interruption is enabled */ void trng_data_int_enable(void) { TRNG_CR |= ((0x1) << TRNG_DATA_INT_OFFSET); } /* function: TRNG data interruption disable * parameters: none * return: none * caution: */ void trng_data_int_disable(void) { TRNG_CR &= ~((0x1) << TRNG_DATA_INT_OFFSET); } /* function: TRNG enable * parameters: none * return: none * caution: */ void trng_enable(void) { uint32_t i; TRNG_CR |= 0x1; /* sleep for a while */ i = 0xFFF; while (i--) { ; } } /* function: TRNG disable * parameters: none * return: none * caution: */ void trng_disable(void) { TRNG_CR &= ~(0x1); } /* function: set RO entropy config * parameters: * cfg ------------------------ RO entropy config, only the low 4 bits are * valid, every bit indicates one RO entropy, the MSB is RO 0, and LSB is RO 3 * return: TRNG_SUCCESS(success), other(error) * caution: * 1. only the low 4 bits of cfg are valid * 2. if the low 4 bits of cig is 0, that means to disable all RO entropy */ uint32_t trng_ro_entropy_config(uint8_t cfg) { if (cfg > 15) { return TRNG_INVALID_INPUT; } else { ; } TRNG_CR &= ~(0x0000000F << TRNG_RO_ENTROPY_OFFSET); TRNG_CR |= (((uint32_t)cfg) & 0x0000000F) << TRNG_RO_ENTROPY_OFFSET; return TRNG_SUCCESS; } /* function: set sub RO entropy config * parameters: * sn ------------------------- input, RO entropy source series number, must * be in [0,3] value ---------------------- input, the config value of RO sn * return: TRNG_SUCCESS(success), other(error) * caution: */ uint32_t trng_ro_sub_entropy_config(uint8_t sn, uint16_t cfg) { switch (sn) { case 0: RO_SRC_EN1 &= ~0xFFFF0000; RO_SRC_EN1 |= ((uint32_t)cfg) << 16; break; case 1: RO_SRC_EN1 &= ~0x0000FFFF; RO_SRC_EN1 |= (uint32_t)cfg; break; case 2: RO_SRC_EN2 &= ~0xFFFF0000; RO_SRC_EN2 |= ((uint32_t)cfg) << 16; break; case 3: RO_SRC_EN2 &= ~0x0000FFFF; RO_SRC_EN2 |= (uint32_t)cfg; break; default: return TRNG_INVALID_INPUT; } return TRNG_SUCCESS; } /* function: set TRNG mode * parameters: * with_post_processing ------- 0:no, other:yes * return: none * caution: */ void trng_set_mode(uint8_t with_post_processing) { if (with_post_processing) { TRNG_MSEL |= 0x1; } else { TRNG_MSEL &= ~(0x1); } TRNG_SR |= 0x7; } /* function: reseed TRNG(works when DRBG is enabled) * parameters: none * return: none * caution: * 1. used for DRBG */ void trng_reseed(void) { TRNG_RESEED |= 0x1; TRNG_SR |= 0x7; } /* function: TRNG set frequency * parameters: * freq ----------------------- input, frequency config, must be in [0,3], * and 0: 1/4 of input frequency, 1: 1/8 ..., 2: 1/16 ..., 3: 1/32 ..., return: * TRNG_SUCCESS(success), other(error) caution: */ uint32_t trng_set_freq(uint8_t freq) { if (freq > 3) { return TRNG_INVALID_INPUT; } else { ; } SCLK_FREQ = freq; return TRNG_SUCCESS; } /* function: get some rand words * parameters: * a -------------------------- output, random words * words ---------------------- input, word number of output, must be in [1, * 8] return: TRNG_SUCCESS(success), other(error) caution: * 1. please make sure the two parameters are valid */ uint32_t get_rand_uint32(uint32_t *a, uint32_t words) { uint32_t i; while (0 == (TRNG_SR & 0x2)) { if (TRNG_SR & 0x1) { return TRNG_HT_ERROR; } else { ; } } for (i = 0; i < words; i++) { *(a++) = TRNG_DR; } TRNG_SR |= 0x2; return TRNG_SUCCESS; } /* function: get some rand words(with reseed) * parameters: * a -------------------------- output, random words * words ---------------------- input, word number of output, must be in [1, * 8] return: TRNG_SUCCESS(success), other(error) caution: * 1. please make sure the two parameters are valid */ uint32_t get_rand_uint32_with_reseed(uint32_t *a, uint32_t words) { uint32_t i; trng_reseed(); while (0 == (TRNG_SR & 0x2)) { if (TRNG_SR & 0x1) { trng_disable(); trng_enable(); return TRNG_HT_ERROR; } else { ; } } /*for debug printf(" 0x%08x", *(a-1));*/ for (i = 0; i < words; i++) { *(a++) = TRNG_DR; } TRNG_SR |= 0x2; return TRNG_SUCCESS; } /* function: get rand buffer(internal basis interface) * parameters: * rand ----------------------- input, byte buffer rand * bytes ---------------------- input, byte length of rand * get_rand_words ------------- input, function pointer to get some random * words(at most 8 words) return: TRNG_SUCCESS(success), other(error) caution: */ uint32_t get_rand_buffer(uint8_t *rand, uint32_t bytes, GET_RAND_WORDS get_rand_words) { uint32_t i; uint32_t tmp, tmp_len, rng_data; uint32_t count, ret; uint8_t *a = rand; /* check input parameters */ if (NULL == rand || NULL == get_rand_words) { return TRNG_BUFFER_NULL; } else if (0 == bytes) { return TRNG_SUCCESS; } else { ; } /* make sure trng and ro are enabled */ if (0 == (TRNG_CR & 0x1)) { return TRNG_INVALID_CONFIG; } else if (0 == (TRNG_CR & (0x0000000F << TRNG_RO_ENTROPY_OFFSET))) { return TRNG_INVALID_CONFIG; } else { ; } tmp_len = bytes; tmp = ((uint32_t)a) & 3; if (tmp) { i = 4 - tmp; ret = get_rand_words(&rng_data, 1); if (TRNG_SUCCESS != ret) { goto END; } else { if (tmp_len > i) { memcpy_(a, &rng_data, i); a += i; tmp_len -= i; } else { memcpy_(a, &rng_data, tmp_len); goto END; } } } else { ; } tmp = tmp_len / 4; while (tmp) { if (tmp > 8) { count = 8; } else { count = tmp; } ret = get_rand_words((uint32_t *)a, count); if (TRNG_SUCCESS != ret) { goto END; } else { a += count << 2; tmp -= count; } } tmp_len = tmp_len & 3; if (tmp_len) { ret = get_rand_words(&rng_data, 1); if (TRNG_SUCCESS != ret) { goto END; } else { memcpy_(a, &rng_data, tmp_len); } } ret = TRNG_SUCCESS; END: if (TRNG_SUCCESS != ret) { memset_(rand, 0, bytes); } else { ; } #ifdef TRNG_POKER_TEST if (TRNG_SUCCESS == ret) { poker_test(rand, bytes); } #endif return ret; } /* function: get rand(for internal test) * parameters: * rand ----------------------- input, byte buffer rand * bytes ---------------------- input, byte length of rand * return: TRNG_SUCCESS(success), other(error) * caution: */ uint32_t get_rand_internal(uint8_t *rand, uint32_t bytes) { return get_rand_buffer(rand, bytes, get_rand_uint32); } /* function: get rand with fast speed(with entropy reducing, for such as * clearing tmp buffer) parameters: rand ----------------------- input, byte * buffer rand bytes ---------------------- input, byte length of rand return: * TRNG_SUCCESS(success), other(error) caution: */ uint32_t get_rand_fast(uint8_t *rand, uint32_t bytes) { /* with post-processing */ if (0x0 == TRNG_MSEL) { trng_disable(); trng_set_mode(1); trng_enable(); } else { ; } return get_rand_buffer(rand, bytes, get_rand_uint32); } /* function: get rand(without entropy reducing) * parameters: * rand ----------------------- input, byte buffer rand * bytes ---------------------- input, byte length of rand * return: TRNG_SUCCESS(success), other(error) * caution: */ uint32_t get_rand(uint8_t *rand, uint32_t bytes) { uint32_t ret; ret = get_rand_buffer(rand, bytes, get_rand_uint32_with_reseed); return ret; } uint32_t get_hrng(uint8_t *dst, uint32_t size) { uint32_t ret = 0; uint32_t rng_value = 0; uint32_t i = 0; uint32_t index = 0; uint32_t index_max = 0; uint32_t cp_left = 0; if (dst == NULL) { return ret; } index = 0; /*4 byte one cp*/ index_max = size >> 2; cp_left = size & 0x3; for (i = 0; i < index_max; i++) { rng_value = trng_readl(APB_SEIP_BASE_ADDR + 8); memcpy_(dst + index, (void *)(&rng_value), sizeof(rng_value)); index = index + 4; ret = index; } if (cp_left > 0) { rng_value = trng_readl(APB_SEIP_BASE_ADDR + 8); memcpy_(dst + index, (void *)(&rng_value), cp_left); ret = index + cp_left; } return ret; } /*********************************** TREO ************************************/ /* function: TERO RNG enable * parameters: none * return: none * caution: */ void tero_enable(void) { uint32_t i; TERO_CR |= 0x1; i = 0xFFF; while (i--) { ; } } /* function: TERO RNG disable * parameters: none * return: none * caution: */ void tero_disable(void) { TERO_CR &= ~(0x1); } /* function: TERO RNG set the system cycle threshold of the TERO counter kept, * parameters: * threshold_value ------------ input, threshold value * return: none * caution: */ uint32_t tero_set_stop_threshold(uint8_t threshold_value) { if (0 == threshold_value) { return TRNG_INVALID_INPUT; } else { ; } TERO_CR &= ~(0x000000FF << TRNG_TERO_THRESHOLD_OFFSET); TERO_CR |= (((uint32_t)threshold_value) << TRNG_TERO_THRESHOLD_OFFSET); return TRNG_SUCCESS; } /* function: set TERO entropy config * parameters: * cfg ------------------------ RO entropy config, only the low 4 bits are * valid, every bit indicates one RO entropy, the LSB is TERO 0, and MSB is RO 3 * return: TRNG_SUCCESS(success), other(error) * caution: * 1. only the low 4 bits of cfg are valid * 2. if the low 4 bits of cig is 0, that means to disable all TERO entropy */ uint32_t tero_entropy_config(uint8_t cfg) { if (cfg > 15) { return TRNG_INVALID_INPUT; } else { ; } TERO_CR &= ~(0x0000000F << TRNG_TERO_ENTROPY_OFFSET); TERO_CR |= (((uint32_t)cfg) & 0x0000000F) << TRNG_TERO_ENTROPY_OFFSET; return TRNG_SUCCESS; } /* function: TERO RNG set output as rng * parameters: none * return: none * caution: */ void tero_set_output_rng(void) { TERO_CR &= ~((0x1) << 1); } /* function: TERO RNG set output as oscillation times * parameters: none * return: none * caution: */ void tero_set_output_osc_times(void) { TERO_CR |= ((0x1) << 1); } /* function: select TREO 1&2 or TERO 3&4 when output is oscillation times * parameters: * cfg ------------------------ input, 0:TREO 1&2, other:TREO 3&4 * return: none * caution: */ void tero_set_osc_sel(uint8_t cfg) { if (0 == cfg) { TERO_CR &= ~((0x1) << 2); } else { TERO_CR |= ((0x1) << 2); } } /* function: set lower limit of oscillation times * parameters: * value ---------------------- input, lower limit value * return: none * caution: */ void tero_set_osc_times_lower_limit(uint16_t value) { TERO_THOLD &= ~(0x000000FF << 16); TERO_THOLD |= ((uint32_t)value) << 16; } /* function: set upper limit of oscillation times * parameters: * value ---------------------- input, upper limit value * return: none * caution: */ void tero_set_osc_times_upper_limit(uint16_t value) { TERO_THOLD &= ~(0x000000FF); TERO_THOLD |= ((uint32_t)value); } /* function: get tero rand * parameters: * a -------------------------- input, byte buffer a * bytes ---------------------- input, byte length of rand * return: TRNG_SUCCESS(success), other(error) * caution: */ uint32_t get_tero_rand(uint8_t *a, uint32_t bytes) { uint32_t i; uint32_t tmp, rng_data; #ifdef TRNG_POKER_TEST uint32_t tmp_len; #endif if (NULL == a) { return TRNG_BUFFER_NULL; } else if (0 == bytes) { return TRNG_SUCCESS; } else { ; } /* make sure tero config is valid */ if (0 == (TERO_CR & 0x1)) { return TRNG_INVALID_CONFIG; } else if (TERO_CR & 0x2) { return TRNG_INVALID_CONFIG; } else if (0 == (TERO_CR & (0x0000000F << TRNG_TERO_ENTROPY_OFFSET))) { return TRNG_INVALID_CONFIG; } else { ; } #ifdef TRNG_POKER_TEST tmp_len = bytes; #endif tmp = ((uint32_t)a) & 3; if (tmp) { i = 4 - tmp; while (0 == (TERO_SR & 0x1)) { ; } rng_data = TERO_DR; if (bytes > i) { memcpy_(a, &rng_data, i); a += i; bytes -= i; } else { memcpy_(a, &rng_data, bytes); return TRNG_SUCCESS; } } tmp = bytes / 4; while (tmp) { while (0 == (TERO_SR & 0x1)) { ; } *((uint32_t *)a) = TERO_DR; a += 4; tmp--; } bytes = bytes & 3; if (bytes) { while (0 == (TERO_SR & 0x1)) { ; } rng_data = TERO_DR; memcpy_(a, &rng_data, bytes); } #ifdef TRNG_POKER_TEST poker_test(a - tmp_len, tmp_len); #endif return TRNG_SUCCESS; }