Message ID | 1542696136-5240-3-git-send-email-sumit.garg@linaro.org |
---|---|
State | New |
Headers | show |
Series | [v3,1/3] libtomcrypt: Import SHA512/256 approved hash algorithm | expand |
On Tue, Nov 20, 2018 at 12:12:16PM +0530, Sumit Garg wrote: > This platform provides 7 on-chip thermal sensors accessible from secure > world only. So, using thermal noise from these sensors we have tried to > create an entropy source as a pseudo TA. This is definitely getting there but... > > Signed-off-by: Sumit Garg <sumit.garg@linaro.org> > --- > core/arch/arm/plat-synquacer/main.c | 9 + > core/arch/arm/plat-synquacer/platform_config.h | 1 + > core/arch/arm/plat-synquacer/rng_pta.c | 363 +++++++++++++++++++++++++ > core/arch/arm/plat-synquacer/rng_pta.h | 11 + > core/arch/arm/plat-synquacer/rng_pta_client.h | 30 ++ > core/arch/arm/plat-synquacer/sub.mk | 1 + > 6 files changed, 415 insertions(+) > create mode 100644 core/arch/arm/plat-synquacer/rng_pta.c > create mode 100644 core/arch/arm/plat-synquacer/rng_pta.h > create mode 100644 core/arch/arm/plat-synquacer/rng_pta_client.h > > diff --git a/core/arch/arm/plat-synquacer/main.c b/core/arch/arm/plat-synquacer/main.c > index 714becd..c91ba9b 100644 > --- a/core/arch/arm/plat-synquacer/main.c > +++ b/core/arch/arm/plat-synquacer/main.c > @@ -18,6 +18,7 @@ > #include <sm/optee_smc.h> > #include <tee/entry_fast.h> > #include <tee/entry_std.h> > +#include <rng_pta.h> > #include <timer_fiq.h> > > static void main_fiq(void); > @@ -39,6 +40,7 @@ static struct pl011_data console_data; > > register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, CORE_MMU_DEVICE_SIZE); > register_phys_mem(MEM_AREA_IO_SEC, GIC_BASE, CORE_MMU_DEVICE_SIZE); > +register_phys_mem(MEM_AREA_IO_SEC, THERMAL_SENSOR_BASE, CORE_MMU_DEVICE_SIZE); > > const struct thread_handlers *generic_boot_get_handlers(void) > { > @@ -78,6 +80,9 @@ static enum itr_return timer_itr_cb(struct itr_handler *h __unused) > /* Reset timer for next FIQ */ > generic_timer_handler(); > > + /* Collect entropy on each timer FIQ */ > + rng_collect_entropy(); > + > return ITRR_HANDLED; > } > > @@ -92,6 +97,10 @@ static TEE_Result init_timer_itr(void) > itr_add(&timer_itr); > itr_enable(IT_SEC_TIMER); > > + /* Enable timer FIQ to fetch entropy required during boot */ > + generic_timer_start(); > + timer_fiq_running = true; > + Why can't the generic timer code manage timer_fiq_running? > return TEE_SUCCESS; > } > driver_init(init_timer_itr); > diff --git a/core/arch/arm/plat-synquacer/platform_config.h b/core/arch/arm/plat-synquacer/platform_config.h > index f9b1b40..8a91ddb 100644 > --- a/core/arch/arm/plat-synquacer/platform_config.h > +++ b/core/arch/arm/plat-synquacer/platform_config.h > @@ -19,6 +19,7 @@ > #define CONSOLE_UART_CLK_IN_HZ 62500000 > #define CONSOLE_BAUDRATE 115200 > > +#define THERMAL_SENSOR_BASE 0x54190000 > #define IT_SEC_TIMER 29 > > #define DRAM0_BASE 0x80000000 > diff --git a/core/arch/arm/plat-synquacer/rng_pta.c b/core/arch/arm/plat-synquacer/rng_pta.c > new file mode 100644 > index 0000000..f9a40aa > --- /dev/null > +++ b/core/arch/arm/plat-synquacer/rng_pta.c > @@ -0,0 +1,363 @@ > +// SPDX-License-Identifier: BSD-2-Clause > +/* > + * Copyright (C) 2018, Linaro Limited > + */ > + > +/* > + * Developerbox doesn't provide a hardware based true random number > + * generator. So this pseudo TA is an effort to provide good source of This is too vague (it is not an effort... it *is*). > + * entropy using noise from 7 thermal sensors. Its suitable for entropy > + * required during boot, seeding kernel entropy pool, cryptographic use > + * etc. > + * > + * Assumption > + * ========== > + * > + * Here we have assumed a conservative entropy estimate as 4 bits per 7 In contrast this is too assertive, we shouldn't be *starting* with the statement that the estimate is conservative... we have to show that. So for this bit just "We have assumed the entropy of the sensor is better than 8 bits per 14 sensor readings" > + * sensor readings. This entropy estimate is based on our simple minimal > + * entropy estimates done on 2.1G bytes of raw samples collected from > + * thermal sensors. Now we come in with: "We believe our estimate to be conservative and have designed to health tests to trigger if a sensor does not achieve at least 8 bits in 16 sensor reading (we use 16 rather than 14 to prevent spurious failures on edge cases). > + * > + * Theory of operation > + * =================== > + * > + * This routine uses secure timer interrupt to sample raw thermal sensor > + * readings. As thermal sensor refresh rate is every 2ms, so interrupt > + * fires every 2ms. It implements continuous health test counting rising > + * and falling edges to report if sensors fail to provide entropy. > + * > + * It uses vetted conditioner as SHA512/256 (approved hash algorithm) > + * to condense entropy. As per NIST.SP.800-90B spec, to get full entropy > + * from vetted conditioner, we need to supply double of input entropy. > + * According to assumption above and requirement for vetted conditioner, > + * we need to supply 28 raw sensor readings to get 1 byte of full > + * entropy as output. So for 32 bytes of conditioner output, we need to > + * supply 896 bytes of raw sensor readings. > + * > + * Interfaces -> Input > + * ------------------- > + * > + * void rng_collect_entropy(void); > + * > + * Called as part of secure timer interrupt handler to sample raw > + * thermal sensor readings and add entropy to the pool. > + * > + * Interfaces -> Output > + * -------------------- > + * > + * TEE_Result rng_get_entropy(uint32_t types, > + * TEE_Param params[TEE_NUM_PARAMS]); > + * > + * Invoke command to expose an entropy interface to normal world. > + * > + * Testing > + * ======= > + * > + * Passes FIPS 140-2 rngtest. > + * > + * Limitation s > + * ========== > + * > + * Output rate is limited to approx. 128 bytes per second. "Our entropy estimation was not reached using any approved or published estimation framework such as NIST SP800 90B and was tests on a very small set of physical samples (instead we have adopted what we believe to be a conservative estimate and partnered it with a fairly agressive health check)." Also I didn't ask about SHA512/256 performance for fun... that needs to be here too (please also ensure the core X is *not* core 0 since that's likely to be where most of the normal world interrupt handlers run): "Generating the SHA512/256 hash takes 24uS and will be run by an interrupt handler that pre-empts the normal world. The interrupt handler runs on core X." > + * > + */ > + > +#include <crypto/crypto.h> > +#include <kernel/delay.h> > +#include <kernel/pseudo_ta.h> > +#include <kernel/spinlock.h> > +#include <mm/core_memprot.h> > +#include <io.h> > +#include <string.h> > +#include <rng_pta.h> > +#include <rng_pta_client.h> > +#include <timer_fiq.h> > + > +#define PTA_NAME "rng.pta" > + > +#define THERMAL_SENSOR_BASE0 0x54190800 > +#define THERMAL_SENSOR_OFFSET 0x80 > +#define NUM_OF_SENSORS 7 This is nitpicking but why not just NUM_SENSORS? > + > +#define TEMP_DATA_REG_OFFSET 0x34 > + > +#define ENTROPY_POOL_SIZE 4096 > + > +#define SENSOR_DATA_SIZE 128 > + > +/* > + * As per NIST.SP.800-90B, to get full entropy from vetted conditioner, we need > + * to supply double of input entropy. So with full entropy (8 bits per byte) we > + * will get yield as one byte of output data for every 28 sensor readings. > + * For 32 bytes of SHA512/256 output data, we need to supply 896 bytes of raw > + * input data. This just restates the header comments. Split out conceptual information from calculations and put each in the right place. > + */ > +#define CONDITIONER_PAYLOAD (SENSOR_DATA_SIZE * NUM_OF_SENSORS) > + > +/* > + * Used in heatlh test to check if count of bit flips 1-0 or 0-1 lies in 12.5% > + * to 50.0% of 128 bytes raw data from particular sensor reading. In ideal If you are still measuring the probability of a specific edge then a probability of 50% would mean the resulting data has almost *no* entropy at all (it can only be achieved with a sequence of repeating 01010101...). > + * scenario either of bit flips should be around 25%. Define "ideal": do you mean exactly 8 bits per 14 sensor readings or exactly 8 bits per byte? (yes, *I* know the answer the but comment is not clear). > + */ > +#define MAX_BIT_FLIP_EDGE_COUNT 64 > +#define MIN_BIT_FLIP_EDGE_COUNT 16 > + > +static uint8_t entropy_pool[ENTROPY_POOL_SIZE] = {0}; > +static uint32_t entropy_size; > + > +/* Current sensor data */ > +static uint8_t sensor_data[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > +static uint8_t sensor_idx; > + > +/* Sensor data that passed health test */ > +static uint8_t sensor_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > +static uint8_t num_sensors_pass; > + > +static uint8_t rest_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > + > +static uint32_t health_test_fail_cnt; > +static uint32_t health_test_pass_cnt; > + > +static unsigned int entropy_lock = SPINLOCK_UNLOCK; > + > +static void pool_add_entropy(uint8_t *entropy, uint32_t size) > +{ > + uint32_t copy_size; > + > + if (entropy_size >= ENTROPY_POOL_SIZE) > + return; > + > + if ((ENTROPY_POOL_SIZE - entropy_size) >= size) > + copy_size = size; > + else > + copy_size = ENTROPY_POOL_SIZE - entropy_size; > + > + memcpy((entropy_pool + entropy_size), entropy, copy_size); > + > + entropy_size += copy_size; > +} > + > +static void pool_get_entropy(uint8_t *buf, uint32_t size) > +{ > + uint32_t off; > + > + if (size > entropy_size) > + return; > + > + off = entropy_size - size; > + > + memcpy(buf, &entropy_pool[off], size); > + entropy_size -= size; > +} > + > +static bool health_test(uint8_t sensor_id) > +{ > + bool result = true; > + uint8_t bit_flip_falling = 0, bit_flip_rising = 0; > + uint8_t i; > + > + for (i = 0; i < (SENSOR_DATA_SIZE - 1); i++) { > + if ((sensor_data[sensor_id][i] ^ > + sensor_data[sensor_id][i + 1]) & 0x1) { > + bit_flip_falling += sensor_data[sensor_id][i] & 0x1; > + bit_flip_rising += sensor_data[sensor_id][i + 1] & 0x1; > + } > + } > + > + if (bit_flip_falling > bit_flip_rising) { > + if (bit_flip_rising < MIN_BIT_FLIP_EDGE_COUNT) > + result = false; > + if (bit_flip_falling > MAX_BIT_FLIP_EDGE_COUNT) > + result = false; > + } else { > + if (bit_flip_falling < MIN_BIT_FLIP_EDGE_COUNT) > + result = false; > + if (bit_flip_rising > MAX_BIT_FLIP_EDGE_COUNT) > + result = false; > + } > + > + return result; > +} > + > +static void pool_check_add_entropy(void) > +{ > + uint32_t i; > + uint8_t entropy_sha512_256[TEE_SHA256_HASH_SIZE]; > + uint8_t rest_sensors_pass = 0; > + TEE_Result res; > + > + for (i = 0; i < NUM_OF_SENSORS; i++) { > + /* Check if particular sensor data passes health test */ > + if (health_test(i) == true) { > + health_test_pass_cnt++; > + if (num_sensors_pass < NUM_OF_SENSORS) { > + memcpy(sensor_data_pass[num_sensors_pass], > + sensor_data[i], SENSOR_DATA_SIZE); > + num_sensors_pass++; > + } else { > + memcpy(rest_data_pass[rest_sensors_pass], > + sensor_data[i], SENSOR_DATA_SIZE); > + rest_sensors_pass++; Why not just make num_sensors_pass have 13 slots instead of 7? Then we don't need any special branches here. We just accumulate good entropy and send the top 7 slots to the hash algo whenever we have >= 7 slots filled. In fact you can replace all of sensor_data, sensor_data_pass and rest_sensors_pass with a single array of 13 slots and a pointer to where sensor 0 should store the data... then the health tests walk those seven slots that are being filled and can memmove() the whole array down 1 when a health test fails. > + } > + } else { > + health_test_fail_cnt++; > + } > + } > + > + /* Check if sensor_data_pass is full */ > + if (num_sensors_pass == NUM_OF_SENSORS) { > + /* > + * Use vetted conditioner SHA512/256 as per > + * NIST.SP.800-90B to condition raw data from entropy > + * source. > + */ > + res = hash_sha512_256_compute(entropy_sha512_256, > + (uint8_t *)sensor_data_pass, > + CONDITIONER_PAYLOAD); > + if (res == TEE_SUCCESS) > + pool_add_entropy(entropy_sha512_256, > + TEE_SHA256_HASH_SIZE); > + } > + > + if (rest_sensors_pass) > + memcpy((uint8_t *)sensor_data_pass, (uint8_t *)rest_data_pass, > + (rest_sensors_pass * SENSOR_DATA_SIZE)); > + > + num_sensors_pass = rest_sensors_pass; > +} > + > +void rng_collect_entropy(void) > +{ > + uint8_t i; > + void *vaddr; > + uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); > + > + cpu_spin_lock(&entropy_lock); > + > + for (i = 0; i < NUM_OF_SENSORS; i++) { > + vaddr = phys_to_virt_io(THERMAL_SENSOR_BASE0 + > + (THERMAL_SENSOR_OFFSET * i) + > + TEMP_DATA_REG_OFFSET); > + sensor_data[i][sensor_idx] = (uint8_t)read32((vaddr_t)vaddr); > + } > + > + sensor_idx++; > + > + if (sensor_idx >= SENSOR_DATA_SIZE) { > + pool_check_add_entropy(); > + sensor_idx = 0; > + } > + > + if (entropy_size >= ENTROPY_POOL_SIZE) { I think it might be better if pool_add_entropy() and poll_check_add_entropy() had return code to indicate the pool is full (checking entropy_size here feels like a layering violation). > + generic_timer_stop(); > + timer_fiq_running = false; > + } > + > + cpu_spin_unlock(&entropy_lock); > + thread_set_exceptions(exceptions); > +} > + > +static TEE_Result rng_get_entropy(uint32_t types, > + TEE_Param params[TEE_NUM_PARAMS]) > +{ > + uint8_t *e = NULL; > + uint32_t pool_size = 0, rq_size = 0; > + uint32_t exceptions; > + TEE_Result res = TEE_SUCCESS; > + > + if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, > + TEE_PARAM_TYPE_NONE, > + TEE_PARAM_TYPE_NONE, > + TEE_PARAM_TYPE_NONE)) { > + EMSG("bad parameters types: 0x%" PRIx32, types); > + return TEE_ERROR_BAD_PARAMETERS; > + } > + > + rq_size = params[0].memref.size; > + > + if ((rq_size == 0) || (rq_size > ENTROPY_POOL_SIZE)) > + return TEE_ERROR_NOT_SUPPORTED; > + > + e = (uint8_t *)params[0].memref.buffer; > + if (!e) > + return TEE_ERROR_BAD_PARAMETERS; > + > + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); > + cpu_spin_lock(&entropy_lock); > + > + /* > + * Report health test failure to normal world in case fail count > + * exceeds 1% of pass count. > + */ > + if (health_test_pass_cnt > 100) { > + if (health_test_fail_cnt > (health_test_pass_cnt / 100)) { > + res = TEE_ERROR_HEALTH_TEST_FAIL; > + params[0].memref.size = 0; > + health_test_pass_cnt = 0; > + health_test_fail_cnt = 0; > + goto exit; > + } > + } else { > + if (health_test_fail_cnt > 1) { > + res = TEE_ERROR_HEALTH_TEST_FAIL; > + params[0].memref.size = 0; > + health_test_pass_cnt = 0; > + health_test_fail_cnt = 0; > + goto exit; > + } > + } > + > + pool_size = entropy_size; > + > + if (pool_size < rq_size) { > + params[0].memref.size = pool_size; > + pool_get_entropy(e, pool_size); > + } else { > + params[0].memref.size = rq_size; > + pool_get_entropy(e, rq_size); > + } > + > +exit: > + if (timer_fiq_running == false) { > + /* Enable timer FIQ to fetch entropy */ > + generic_timer_start(); > + timer_fiq_running = true; > + } > + > + cpu_spin_unlock(&entropy_lock); > + thread_set_exceptions(exceptions); > + > + return res; > +} > + > +/* > + * Trusted Application Entry Points > + */ > +static TEE_Result open_session(uint32_t param_types __unused, > + TEE_Param params[TEE_NUM_PARAMS] __unused, > + void **session_context __unused) > +{ > + DMSG("open entry point for pseudo-TA \"%s\"", PTA_NAME); > + return TEE_SUCCESS; > +} > + > +static TEE_Result invoke_command(void *pSessionContext __unused, > + uint32_t nCommandID, uint32_t nParamTypes, > + TEE_Param pParams[TEE_NUM_PARAMS]) > +{ > + FMSG("command entry point for pseudo-TA \"%s\"", PTA_NAME); > + > + switch (nCommandID) { > + case PTA_CMD_GET_ENTROPY: > + return rng_get_entropy(nParamTypes, pParams); > + default: > + break; > + } > + > + return TEE_ERROR_NOT_IMPLEMENTED; > +} > + > +pseudo_ta_register(.uuid = PTA_RNG_UUID, .name = PTA_NAME, > + .flags = PTA_DEFAULT_FLAGS, > + .open_session_entry_point = open_session, > + .invoke_command_entry_point = invoke_command); > diff --git a/core/arch/arm/plat-synquacer/rng_pta.h b/core/arch/arm/plat-synquacer/rng_pta.h > new file mode 100644 > index 0000000..8ce2afa > --- /dev/null > +++ b/core/arch/arm/plat-synquacer/rng_pta.h > @@ -0,0 +1,11 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2018, Linaro Limited > + */ > + > +#ifndef __RNG_PTA_H > +#define __RNG_PTA_H > + > +void rng_collect_entropy(void); > + > +#endif /* __RNG_PTA_H */ > diff --git a/core/arch/arm/plat-synquacer/rng_pta_client.h b/core/arch/arm/plat-synquacer/rng_pta_client.h > new file mode 100644 > index 0000000..b7986d3 > --- /dev/null > +++ b/core/arch/arm/plat-synquacer/rng_pta_client.h > @@ -0,0 +1,30 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (C) 2018, Linaro Limited > + */ > + > +#ifndef __RNG_PTA_CLIENT_H > +#define __RNG_PTA_CLIENT_H > + > +#define PTA_RNG_UUID { 0xab7a617c, 0xb8e7, 0x4d8f, \ > + { 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64 } } > + > +#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 > + > +/* > + * PTA_CMD_GET_ENTROPY - Get Entropy from RNG using Thermal Sensor > + * > + * param[0] (inout memref) - Entropy buffer memory reference > + * param[1] unused > + * param[2] unused > + * param[3] unused > + * > + * Result: > + * TEE_SUCCESS - Invoke command success > + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param > + * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool > + * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed > + */ > +#define PTA_CMD_GET_ENTROPY 0x0 > + > +#endif /* __RNG_PTA_CLIENT_H */ > diff --git a/core/arch/arm/plat-synquacer/sub.mk b/core/arch/arm/plat-synquacer/sub.mk > index cfa1dc3..013e57d 100644 > --- a/core/arch/arm/plat-synquacer/sub.mk > +++ b/core/arch/arm/plat-synquacer/sub.mk > @@ -1,3 +1,4 @@ > global-incdirs-y += . > srcs-y += main.c > +srcs-y += rng_pta.c > srcs-y += timer_fiq.c > -- > 2.7.4 >
On Tue, 20 Nov 2018 at 21:28, Daniel Thompson <daniel.thompson@linaro.org> wrote: > > On Tue, Nov 20, 2018 at 12:12:16PM +0530, Sumit Garg wrote: > > This platform provides 7 on-chip thermal sensors accessible from secure > > world only. So, using thermal noise from these sensors we have tried to > > create an entropy source as a pseudo TA. > > This is definitely getting there but... > > > > > Signed-off-by: Sumit Garg <sumit.garg@linaro.org> > > --- > > core/arch/arm/plat-synquacer/main.c | 9 + > > core/arch/arm/plat-synquacer/platform_config.h | 1 + > > core/arch/arm/plat-synquacer/rng_pta.c | 363 +++++++++++++++++++++++++ > > core/arch/arm/plat-synquacer/rng_pta.h | 11 + > > core/arch/arm/plat-synquacer/rng_pta_client.h | 30 ++ > > core/arch/arm/plat-synquacer/sub.mk | 1 + > > 6 files changed, 415 insertions(+) > > create mode 100644 core/arch/arm/plat-synquacer/rng_pta.c > > create mode 100644 core/arch/arm/plat-synquacer/rng_pta.h > > create mode 100644 core/arch/arm/plat-synquacer/rng_pta_client.h > > > > diff --git a/core/arch/arm/plat-synquacer/main.c b/core/arch/arm/plat-synquacer/main.c > > index 714becd..c91ba9b 100644 > > --- a/core/arch/arm/plat-synquacer/main.c > > +++ b/core/arch/arm/plat-synquacer/main.c > > @@ -18,6 +18,7 @@ > > #include <sm/optee_smc.h> > > #include <tee/entry_fast.h> > > #include <tee/entry_std.h> > > +#include <rng_pta.h> > > #include <timer_fiq.h> > > > > static void main_fiq(void); > > @@ -39,6 +40,7 @@ static struct pl011_data console_data; > > > > register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, CORE_MMU_DEVICE_SIZE); > > register_phys_mem(MEM_AREA_IO_SEC, GIC_BASE, CORE_MMU_DEVICE_SIZE); > > +register_phys_mem(MEM_AREA_IO_SEC, THERMAL_SENSOR_BASE, CORE_MMU_DEVICE_SIZE); > > > > const struct thread_handlers *generic_boot_get_handlers(void) > > { > > @@ -78,6 +80,9 @@ static enum itr_return timer_itr_cb(struct itr_handler *h __unused) > > /* Reset timer for next FIQ */ > > generic_timer_handler(); > > > > + /* Collect entropy on each timer FIQ */ > > + rng_collect_entropy(); > > + > > return ITRR_HANDLED; > > } > > > > @@ -92,6 +97,10 @@ static TEE_Result init_timer_itr(void) > > itr_add(&timer_itr); > > itr_enable(IT_SEC_TIMER); > > > > + /* Enable timer FIQ to fetch entropy required during boot */ > > + generic_timer_start(); > > + timer_fiq_running = true; > > + > > Why can't the generic timer code manage timer_fiq_running? > I just want to avoid use of spin locks in generic timer code. Here it is modified during boot where only single core is involved. But during run-time, modified under spin-lock in rng_collect_entropy() and rng_get_entropy() api's. > > > return TEE_SUCCESS; > > } > > driver_init(init_timer_itr); > > diff --git a/core/arch/arm/plat-synquacer/platform_config.h b/core/arch/arm/plat-synquacer/platform_config.h > > index f9b1b40..8a91ddb 100644 > > --- a/core/arch/arm/plat-synquacer/platform_config.h > > +++ b/core/arch/arm/plat-synquacer/platform_config.h > > @@ -19,6 +19,7 @@ > > #define CONSOLE_UART_CLK_IN_HZ 62500000 > > #define CONSOLE_BAUDRATE 115200 > > > > +#define THERMAL_SENSOR_BASE 0x54190000 > > #define IT_SEC_TIMER 29 > > > > #define DRAM0_BASE 0x80000000 > > diff --git a/core/arch/arm/plat-synquacer/rng_pta.c b/core/arch/arm/plat-synquacer/rng_pta.c > > new file mode 100644 > > index 0000000..f9a40aa > > --- /dev/null > > +++ b/core/arch/arm/plat-synquacer/rng_pta.c > > @@ -0,0 +1,363 @@ > > +// SPDX-License-Identifier: BSD-2-Clause > > +/* > > + * Copyright (C) 2018, Linaro Limited > > + */ > > + > > +/* > > + * Developerbox doesn't provide a hardware based true random number > > + * generator. So this pseudo TA is an effort to provide good source of > > This is too vague (it is not an effort... it *is*). Ok will change to "So this pseudo TA provides a good source of". > > > + * entropy using noise from 7 thermal sensors. Its suitable for entropy > > + * required during boot, seeding kernel entropy pool, cryptographic use > > + * etc. > > + * > > + * Assumption > > + * ========== > > + * > > + * Here we have assumed a conservative entropy estimate as 4 bits per 7 > > In contrast this is too assertive, we shouldn't be *starting* with the > statement that the estimate is conservative... we have to show that. So > for this bit just > > "We have assumed the entropy of the sensor is better than 8 bits per 14 > sensor readings" > Will use this instead. > > > + * sensor readings. This entropy estimate is based on our simple minimal > > + * entropy estimates done on 2.1G bytes of raw samples collected from > > + * thermal sensors. > > Now we come in with: > > "We believe our estimate to be conservative and have designed to > health tests to trigger if a sensor does not achieve at least > 8 bits in 16 sensor reading (we use 16 rather than 14 to prevent > spurious failures on edge cases). > Will add this info here. > > > + * > > + * Theory of operation > > + * =================== > > + * > > + * This routine uses secure timer interrupt to sample raw thermal sensor > > + * readings. As thermal sensor refresh rate is every 2ms, so interrupt > > + * fires every 2ms. It implements continuous health test counting rising > > + * and falling edges to report if sensors fail to provide entropy. > > + * > > + * It uses vetted conditioner as SHA512/256 (approved hash algorithm) > > + * to condense entropy. As per NIST.SP.800-90B spec, to get full entropy > > + * from vetted conditioner, we need to supply double of input entropy. > > + * According to assumption above and requirement for vetted conditioner, > > + * we need to supply 28 raw sensor readings to get 1 byte of full > > + * entropy as output. So for 32 bytes of conditioner output, we need to > > + * supply 896 bytes of raw sensor readings. > > + * > > + * Interfaces -> Input > > + * ------------------- > > + * > > + * void rng_collect_entropy(void); > > + * > > + * Called as part of secure timer interrupt handler to sample raw > > + * thermal sensor readings and add entropy to the pool. > > + * > > + * Interfaces -> Output > > + * -------------------- > > + * > > + * TEE_Result rng_get_entropy(uint32_t types, > > + * TEE_Param params[TEE_NUM_PARAMS]); > > + * > > + * Invoke command to expose an entropy interface to normal world. > > + * > > + * Testing > > + * ======= > > + * > > + * Passes FIPS 140-2 rngtest. > > + * > > + * Limitation > > s Ok. > > > + * ========== > > + * > > + * Output rate is limited to approx. 128 bytes per second. > > "Our entropy estimation was not reached using any approved or published > estimation framework such as NIST SP800 90B and was tests on a very > small set of physical samples (instead we have adopted what we believe > to be a conservative estimate and partnered it with a fairly agressive > health check)." > Will add this info here. > Also I didn't ask about SHA512/256 performance for fun... that needs to > be here too (please also ensure the core X is *not* core 0 since that's > likely to be where most of the normal world interrupt handlers run): > Hmm. During boot we need to run this timer interrupt on core 0 only. Later if we decide to increase quality of hwrng driver, then we can bind the kernel thread to run on a particular core X which is *not* core 0. Otherwise this binding needs to be put on user-space "rngd" daemon. > "Generating the SHA512/256 hash takes 24uS and will be run by an > interrupt handler that pre-empts the normal world. The interrupt handler > runs on core X." Will add this info here. > > > > + * > > + */ > > + > > +#include <crypto/crypto.h> > > +#include <kernel/delay.h> > > +#include <kernel/pseudo_ta.h> > > +#include <kernel/spinlock.h> > > +#include <mm/core_memprot.h> > > +#include <io.h> > > +#include <string.h> > > +#include <rng_pta.h> > > +#include <rng_pta_client.h> > > +#include <timer_fiq.h> > > + > > +#define PTA_NAME "rng.pta" > > + > > +#define THERMAL_SENSOR_BASE0 0x54190800 > > +#define THERMAL_SENSOR_OFFSET 0x80 > > +#define NUM_OF_SENSORS 7 > > This is nitpicking but why not just NUM_SENSORS? > Ok will use NUM_SENSORS. > > > + > > +#define TEMP_DATA_REG_OFFSET 0x34 > > + > > +#define ENTROPY_POOL_SIZE 4096 > > + > > +#define SENSOR_DATA_SIZE 128 > > + > > +/* > > + * As per NIST.SP.800-90B, to get full entropy from vetted conditioner, we need > > + * to supply double of input entropy. So with full entropy (8 bits per byte) we > > + * will get yield as one byte of output data for every 28 sensor readings. > > + * For 32 bytes of SHA512/256 output data, we need to supply 896 bytes of raw > > + * input data. > > This just restates the header comments. Split out conceptual information > from calculations and put each in the right place. > Will remove this comment as its already covered in the header. > > + */ > > +#define CONDITIONER_PAYLOAD (SENSOR_DATA_SIZE * NUM_OF_SENSORS) > > + > > +/* > > + * Used in heatlh test to check if count of bit flips 1-0 or 0-1 lies in 12.5% > > + * to 50.0% of 128 bytes raw data from particular sensor reading. In ideal > > If you are still measuring the probability of a specific edge then a > probability of 50% would mean the resulting data has almost *no* > entropy at all (it can only be achieved with a sequence of repeating > 01010101...). > Yes I am still measuring probability of a specific edge. Here my another thought was repeating 01010101... isn't sensor failure but you are right it isn't entropy either. And as per header we say heath test detects when sensors fails to supply entropy. So I will change this back to 37.5%. > > > + * scenario either of bit flips should be around 25%. > > Define "ideal": do you mean exactly 8 bits per 14 sensor readings or exactly > 8 bits per byte? (yes, *I* know the answer the but comment is not clear). > Here I would rather say full bit per LSB of particular sensor. So updated comment will look like: "In ideal scenario (1 bit of entropy per LSB) either of bit flips should be around 25%". > > > + */ > > +#define MAX_BIT_FLIP_EDGE_COUNT 64 > > +#define MIN_BIT_FLIP_EDGE_COUNT 16 > > + > > +static uint8_t entropy_pool[ENTROPY_POOL_SIZE] = {0}; > > +static uint32_t entropy_size; > > + > > +/* Current sensor data */ > > +static uint8_t sensor_data[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > > +static uint8_t sensor_idx; > > + > > +/* Sensor data that passed health test */ > > +static uint8_t sensor_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > > +static uint8_t num_sensors_pass; > > + > > +static uint8_t rest_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; > > + > > +static uint32_t health_test_fail_cnt; > > +static uint32_t health_test_pass_cnt; > > + > > +static unsigned int entropy_lock = SPINLOCK_UNLOCK; > > + > > +static void pool_add_entropy(uint8_t *entropy, uint32_t size) > > +{ > > + uint32_t copy_size; > > + > > + if (entropy_size >= ENTROPY_POOL_SIZE) > > + return; > > + > > + if ((ENTROPY_POOL_SIZE - entropy_size) >= size) > > + copy_size = size; > > + else > > + copy_size = ENTROPY_POOL_SIZE - entropy_size; > > + > > + memcpy((entropy_pool + entropy_size), entropy, copy_size); > > + > > + entropy_size += copy_size; > > +} > > + > > +static void pool_get_entropy(uint8_t *buf, uint32_t size) > > +{ > > + uint32_t off; > > + > > + if (size > entropy_size) > > + return; > > + > > + off = entropy_size - size; > > + > > + memcpy(buf, &entropy_pool[off], size); > > + entropy_size -= size; > > +} > > + > > +static bool health_test(uint8_t sensor_id) > > +{ > > + bool result = true; > > + uint8_t bit_flip_falling = 0, bit_flip_rising = 0; > > + uint8_t i; > > + > > + for (i = 0; i < (SENSOR_DATA_SIZE - 1); i++) { > > + if ((sensor_data[sensor_id][i] ^ > > + sensor_data[sensor_id][i + 1]) & 0x1) { > > + bit_flip_falling += sensor_data[sensor_id][i] & 0x1; > > + bit_flip_rising += sensor_data[sensor_id][i + 1] & 0x1; > > + } > > + } > > + > > + if (bit_flip_falling > bit_flip_rising) { > > + if (bit_flip_rising < MIN_BIT_FLIP_EDGE_COUNT) > > + result = false; > > + if (bit_flip_falling > MAX_BIT_FLIP_EDGE_COUNT) > > + result = false; > > + } else { > > + if (bit_flip_falling < MIN_BIT_FLIP_EDGE_COUNT) > > + result = false; > > + if (bit_flip_rising > MAX_BIT_FLIP_EDGE_COUNT) > > + result = false; > > + } > > + > > + return result; > > +} > > + > > +static void pool_check_add_entropy(void) > > +{ > > + uint32_t i; > > + uint8_t entropy_sha512_256[TEE_SHA256_HASH_SIZE]; > > + uint8_t rest_sensors_pass = 0; > > + TEE_Result res; > > + > > + for (i = 0; i < NUM_OF_SENSORS; i++) { > > + /* Check if particular sensor data passes health test */ > > + if (health_test(i) == true) { > > + health_test_pass_cnt++; > > + if (num_sensors_pass < NUM_OF_SENSORS) { > > + memcpy(sensor_data_pass[num_sensors_pass], > > + sensor_data[i], SENSOR_DATA_SIZE); > > + num_sensors_pass++; > > + } else { > > + memcpy(rest_data_pass[rest_sensors_pass], > > + sensor_data[i], SENSOR_DATA_SIZE); > > + rest_sensors_pass++; > > Why not just make num_sensors_pass have 13 slots instead of 7? Then we > don't need any special branches here. We just accumulate good entropy > and send the top 7 slots to the hash algo whenever we have >= 7 slots > filled. > > In fact you can replace all of sensor_data, sensor_data_pass and > rest_sensors_pass with a single array of 13 slots and a pointer to where > sensor 0 should store the data... then the health tests walk those seven > slots that are being filled and can memmove() the whole array down 1 when > a health test fails. > Seems to be an interesting approach. Will try to implement it. > > > + } > > + } else { > > + health_test_fail_cnt++; > > + } > > + } > > + > > + /* Check if sensor_data_pass is full */ > > + if (num_sensors_pass == NUM_OF_SENSORS) { > > + /* > > + * Use vetted conditioner SHA512/256 as per > > + * NIST.SP.800-90B to condition raw data from entropy > > + * source. > > + */ > > + res = hash_sha512_256_compute(entropy_sha512_256, > > + (uint8_t *)sensor_data_pass, > > + CONDITIONER_PAYLOAD); > > + if (res == TEE_SUCCESS) > > + pool_add_entropy(entropy_sha512_256, > > + TEE_SHA256_HASH_SIZE); > > + } > > + > > + if (rest_sensors_pass) > > + memcpy((uint8_t *)sensor_data_pass, (uint8_t *)rest_data_pass, > > + (rest_sensors_pass * SENSOR_DATA_SIZE)); > > + > > + num_sensors_pass = rest_sensors_pass; > > +} > > + > > +void rng_collect_entropy(void) > > +{ > > + uint8_t i; > > + void *vaddr; > > + uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); > > + > > + cpu_spin_lock(&entropy_lock); > > + > > + for (i = 0; i < NUM_OF_SENSORS; i++) { > > + vaddr = phys_to_virt_io(THERMAL_SENSOR_BASE0 + > > + (THERMAL_SENSOR_OFFSET * i) + > > + TEMP_DATA_REG_OFFSET); > > + sensor_data[i][sensor_idx] = (uint8_t)read32((vaddr_t)vaddr); > > + } > > + > > + sensor_idx++; > > + > > + if (sensor_idx >= SENSOR_DATA_SIZE) { > > + pool_check_add_entropy(); > > + sensor_idx = 0; > > + } > > + > > + if (entropy_size >= ENTROPY_POOL_SIZE) { > > I think it might be better if pool_add_entropy() and > poll_check_add_entropy() had return code to indicate > the pool is full (checking entropy_size here feels like > a layering violation). > Agree, but wouldn't pool_check_add_entropy() more appropriate place for this check? -Sumit > > > + generic_timer_stop(); > > + timer_fiq_running = false; > > + } > > > + > > + cpu_spin_unlock(&entropy_lock); > > + thread_set_exceptions(exceptions); > > +} > > + > > +static TEE_Result rng_get_entropy(uint32_t types, > > + TEE_Param params[TEE_NUM_PARAMS]) > > +{ > > + uint8_t *e = NULL; > > + uint32_t pool_size = 0, rq_size = 0; > > + uint32_t exceptions; > > + TEE_Result res = TEE_SUCCESS; > > + > > + if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, > > + TEE_PARAM_TYPE_NONE, > > + TEE_PARAM_TYPE_NONE, > > + TEE_PARAM_TYPE_NONE)) { > > + EMSG("bad parameters types: 0x%" PRIx32, types); > > + return TEE_ERROR_BAD_PARAMETERS; > > + } > > + > > + rq_size = params[0].memref.size; > > + > > + if ((rq_size == 0) || (rq_size > ENTROPY_POOL_SIZE)) > > + return TEE_ERROR_NOT_SUPPORTED; > > + > > + e = (uint8_t *)params[0].memref.buffer; > > + if (!e) > > + return TEE_ERROR_BAD_PARAMETERS; > > + > > + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); > > + cpu_spin_lock(&entropy_lock); > > + > > + /* > > + * Report health test failure to normal world in case fail count > > + * exceeds 1% of pass count. > > + */ > > + if (health_test_pass_cnt > 100) { > > + if (health_test_fail_cnt > (health_test_pass_cnt / 100)) { > > + res = TEE_ERROR_HEALTH_TEST_FAIL; > > + params[0].memref.size = 0; > > + health_test_pass_cnt = 0; > > + health_test_fail_cnt = 0; > > + goto exit; > > + } > > + } else { > > + if (health_test_fail_cnt > 1) { > > + res = TEE_ERROR_HEALTH_TEST_FAIL; > > + params[0].memref.size = 0; > > + health_test_pass_cnt = 0; > > + health_test_fail_cnt = 0; > > + goto exit; > > + } > > + } > > + > > + pool_size = entropy_size; > > + > > + if (pool_size < rq_size) { > > + params[0].memref.size = pool_size; > > + pool_get_entropy(e, pool_size); > > + } else { > > + params[0].memref.size = rq_size; > > + pool_get_entropy(e, rq_size); > > + } > > + > > +exit: > > + if (timer_fiq_running == false) { > > + /* Enable timer FIQ to fetch entropy */ > > + generic_timer_start(); > > + timer_fiq_running = true; > > + } > > + > > + cpu_spin_unlock(&entropy_lock); > > + thread_set_exceptions(exceptions); > > + > > + return res; > > +} > > + > > +/* > > + * Trusted Application Entry Points > > + */ > > +static TEE_Result open_session(uint32_t param_types __unused, > > + TEE_Param params[TEE_NUM_PARAMS] __unused, > > + void **session_context __unused) > > +{ > > + DMSG("open entry point for pseudo-TA \"%s\"", PTA_NAME); > > + return TEE_SUCCESS; > > +} > > + > > +static TEE_Result invoke_command(void *pSessionContext __unused, > > + uint32_t nCommandID, uint32_t nParamTypes, > > + TEE_Param pParams[TEE_NUM_PARAMS]) > > +{ > > + FMSG("command entry point for pseudo-TA \"%s\"", PTA_NAME); > > + > > + switch (nCommandID) { > > + case PTA_CMD_GET_ENTROPY: > > + return rng_get_entropy(nParamTypes, pParams); > > + default: > > + break; > > + } > > + > > + return TEE_ERROR_NOT_IMPLEMENTED; > > +} > > + > > +pseudo_ta_register(.uuid = PTA_RNG_UUID, .name = PTA_NAME, > > + .flags = PTA_DEFAULT_FLAGS, > > + .open_session_entry_point = open_session, > > + .invoke_command_entry_point = invoke_command); > > diff --git a/core/arch/arm/plat-synquacer/rng_pta.h b/core/arch/arm/plat-synquacer/rng_pta.h > > new file mode 100644 > > index 0000000..8ce2afa > > --- /dev/null > > +++ b/core/arch/arm/plat-synquacer/rng_pta.h > > @@ -0,0 +1,11 @@ > > +/* SPDX-License-Identifier: BSD-2-Clause */ > > +/* > > + * Copyright (C) 2018, Linaro Limited > > + */ > > + > > +#ifndef __RNG_PTA_H > > +#define __RNG_PTA_H > > + > > +void rng_collect_entropy(void); > > + > > +#endif /* __RNG_PTA_H */ > > diff --git a/core/arch/arm/plat-synquacer/rng_pta_client.h b/core/arch/arm/plat-synquacer/rng_pta_client.h > > new file mode 100644 > > index 0000000..b7986d3 > > --- /dev/null > > +++ b/core/arch/arm/plat-synquacer/rng_pta_client.h > > @@ -0,0 +1,30 @@ > > +/* SPDX-License-Identifier: BSD-2-Clause */ > > +/* > > + * Copyright (C) 2018, Linaro Limited > > + */ > > + > > +#ifndef __RNG_PTA_CLIENT_H > > +#define __RNG_PTA_CLIENT_H > > + > > +#define PTA_RNG_UUID { 0xab7a617c, 0xb8e7, 0x4d8f, \ > > + { 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64 } } > > + > > +#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 > > + > > +/* > > + * PTA_CMD_GET_ENTROPY - Get Entropy from RNG using Thermal Sensor > > + * > > + * param[0] (inout memref) - Entropy buffer memory reference > > + * param[1] unused > > + * param[2] unused > > + * param[3] unused > > + * > > + * Result: > > + * TEE_SUCCESS - Invoke command success > > + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param > > + * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool > > + * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed > > + */ > > +#define PTA_CMD_GET_ENTROPY 0x0 > > + > > +#endif /* __RNG_PTA_CLIENT_H */ > > diff --git a/core/arch/arm/plat-synquacer/sub.mk b/core/arch/arm/plat-synquacer/sub.mk > > index cfa1dc3..013e57d 100644 > > --- a/core/arch/arm/plat-synquacer/sub.mk > > +++ b/core/arch/arm/plat-synquacer/sub.mk > > @@ -1,3 +1,4 @@ > > global-incdirs-y += . > > srcs-y += main.c > > +srcs-y += rng_pta.c > > srcs-y += timer_fiq.c > > -- > > 2.7.4 > >
diff --git a/core/arch/arm/plat-synquacer/main.c b/core/arch/arm/plat-synquacer/main.c index 714becd..c91ba9b 100644 --- a/core/arch/arm/plat-synquacer/main.c +++ b/core/arch/arm/plat-synquacer/main.c @@ -18,6 +18,7 @@ #include <sm/optee_smc.h> #include <tee/entry_fast.h> #include <tee/entry_std.h> +#include <rng_pta.h> #include <timer_fiq.h> static void main_fiq(void); @@ -39,6 +40,7 @@ static struct pl011_data console_data; register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, CORE_MMU_DEVICE_SIZE); register_phys_mem(MEM_AREA_IO_SEC, GIC_BASE, CORE_MMU_DEVICE_SIZE); +register_phys_mem(MEM_AREA_IO_SEC, THERMAL_SENSOR_BASE, CORE_MMU_DEVICE_SIZE); const struct thread_handlers *generic_boot_get_handlers(void) { @@ -78,6 +80,9 @@ static enum itr_return timer_itr_cb(struct itr_handler *h __unused) /* Reset timer for next FIQ */ generic_timer_handler(); + /* Collect entropy on each timer FIQ */ + rng_collect_entropy(); + return ITRR_HANDLED; } @@ -92,6 +97,10 @@ static TEE_Result init_timer_itr(void) itr_add(&timer_itr); itr_enable(IT_SEC_TIMER); + /* Enable timer FIQ to fetch entropy required during boot */ + generic_timer_start(); + timer_fiq_running = true; + return TEE_SUCCESS; } driver_init(init_timer_itr); diff --git a/core/arch/arm/plat-synquacer/platform_config.h b/core/arch/arm/plat-synquacer/platform_config.h index f9b1b40..8a91ddb 100644 --- a/core/arch/arm/plat-synquacer/platform_config.h +++ b/core/arch/arm/plat-synquacer/platform_config.h @@ -19,6 +19,7 @@ #define CONSOLE_UART_CLK_IN_HZ 62500000 #define CONSOLE_BAUDRATE 115200 +#define THERMAL_SENSOR_BASE 0x54190000 #define IT_SEC_TIMER 29 #define DRAM0_BASE 0x80000000 diff --git a/core/arch/arm/plat-synquacer/rng_pta.c b/core/arch/arm/plat-synquacer/rng_pta.c new file mode 100644 index 0000000..f9a40aa --- /dev/null +++ b/core/arch/arm/plat-synquacer/rng_pta.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2018, Linaro Limited + */ + +/* + * Developerbox doesn't provide a hardware based true random number + * generator. So this pseudo TA is an effort to provide good source of + * entropy using noise from 7 thermal sensors. Its suitable for entropy + * required during boot, seeding kernel entropy pool, cryptographic use + * etc. + * + * Assumption + * ========== + * + * Here we have assumed a conservative entropy estimate as 4 bits per 7 + * sensor readings. This entropy estimate is based on our simple minimal + * entropy estimates done on 2.1G bytes of raw samples collected from + * thermal sensors. + * + * Theory of operation + * =================== + * + * This routine uses secure timer interrupt to sample raw thermal sensor + * readings. As thermal sensor refresh rate is every 2ms, so interrupt + * fires every 2ms. It implements continuous health test counting rising + * and falling edges to report if sensors fail to provide entropy. + * + * It uses vetted conditioner as SHA512/256 (approved hash algorithm) + * to condense entropy. As per NIST.SP.800-90B spec, to get full entropy + * from vetted conditioner, we need to supply double of input entropy. + * According to assumption above and requirement for vetted conditioner, + * we need to supply 28 raw sensor readings to get 1 byte of full + * entropy as output. So for 32 bytes of conditioner output, we need to + * supply 896 bytes of raw sensor readings. + * + * Interfaces -> Input + * ------------------- + * + * void rng_collect_entropy(void); + * + * Called as part of secure timer interrupt handler to sample raw + * thermal sensor readings and add entropy to the pool. + * + * Interfaces -> Output + * -------------------- + * + * TEE_Result rng_get_entropy(uint32_t types, + * TEE_Param params[TEE_NUM_PARAMS]); + * + * Invoke command to expose an entropy interface to normal world. + * + * Testing + * ======= + * + * Passes FIPS 140-2 rngtest. + * + * Limitation + * ========== + * + * Output rate is limited to approx. 128 bytes per second. + * + */ + +#include <crypto/crypto.h> +#include <kernel/delay.h> +#include <kernel/pseudo_ta.h> +#include <kernel/spinlock.h> +#include <mm/core_memprot.h> +#include <io.h> +#include <string.h> +#include <rng_pta.h> +#include <rng_pta_client.h> +#include <timer_fiq.h> + +#define PTA_NAME "rng.pta" + +#define THERMAL_SENSOR_BASE0 0x54190800 +#define THERMAL_SENSOR_OFFSET 0x80 +#define NUM_OF_SENSORS 7 + +#define TEMP_DATA_REG_OFFSET 0x34 + +#define ENTROPY_POOL_SIZE 4096 + +#define SENSOR_DATA_SIZE 128 + +/* + * As per NIST.SP.800-90B, to get full entropy from vetted conditioner, we need + * to supply double of input entropy. So with full entropy (8 bits per byte) we + * will get yield as one byte of output data for every 28 sensor readings. + * For 32 bytes of SHA512/256 output data, we need to supply 896 bytes of raw + * input data. + */ +#define CONDITIONER_PAYLOAD (SENSOR_DATA_SIZE * NUM_OF_SENSORS) + +/* + * Used in heatlh test to check if count of bit flips 1-0 or 0-1 lies in 12.5% + * to 50.0% of 128 bytes raw data from particular sensor reading. In ideal + * scenario either of bit flips should be around 25%. + */ +#define MAX_BIT_FLIP_EDGE_COUNT 64 +#define MIN_BIT_FLIP_EDGE_COUNT 16 + +static uint8_t entropy_pool[ENTROPY_POOL_SIZE] = {0}; +static uint32_t entropy_size; + +/* Current sensor data */ +static uint8_t sensor_data[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; +static uint8_t sensor_idx; + +/* Sensor data that passed health test */ +static uint8_t sensor_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; +static uint8_t num_sensors_pass; + +static uint8_t rest_data_pass[NUM_OF_SENSORS][SENSOR_DATA_SIZE] = {0}; + +static uint32_t health_test_fail_cnt; +static uint32_t health_test_pass_cnt; + +static unsigned int entropy_lock = SPINLOCK_UNLOCK; + +static void pool_add_entropy(uint8_t *entropy, uint32_t size) +{ + uint32_t copy_size; + + if (entropy_size >= ENTROPY_POOL_SIZE) + return; + + if ((ENTROPY_POOL_SIZE - entropy_size) >= size) + copy_size = size; + else + copy_size = ENTROPY_POOL_SIZE - entropy_size; + + memcpy((entropy_pool + entropy_size), entropy, copy_size); + + entropy_size += copy_size; +} + +static void pool_get_entropy(uint8_t *buf, uint32_t size) +{ + uint32_t off; + + if (size > entropy_size) + return; + + off = entropy_size - size; + + memcpy(buf, &entropy_pool[off], size); + entropy_size -= size; +} + +static bool health_test(uint8_t sensor_id) +{ + bool result = true; + uint8_t bit_flip_falling = 0, bit_flip_rising = 0; + uint8_t i; + + for (i = 0; i < (SENSOR_DATA_SIZE - 1); i++) { + if ((sensor_data[sensor_id][i] ^ + sensor_data[sensor_id][i + 1]) & 0x1) { + bit_flip_falling += sensor_data[sensor_id][i] & 0x1; + bit_flip_rising += sensor_data[sensor_id][i + 1] & 0x1; + } + } + + if (bit_flip_falling > bit_flip_rising) { + if (bit_flip_rising < MIN_BIT_FLIP_EDGE_COUNT) + result = false; + if (bit_flip_falling > MAX_BIT_FLIP_EDGE_COUNT) + result = false; + } else { + if (bit_flip_falling < MIN_BIT_FLIP_EDGE_COUNT) + result = false; + if (bit_flip_rising > MAX_BIT_FLIP_EDGE_COUNT) + result = false; + } + + return result; +} + +static void pool_check_add_entropy(void) +{ + uint32_t i; + uint8_t entropy_sha512_256[TEE_SHA256_HASH_SIZE]; + uint8_t rest_sensors_pass = 0; + TEE_Result res; + + for (i = 0; i < NUM_OF_SENSORS; i++) { + /* Check if particular sensor data passes health test */ + if (health_test(i) == true) { + health_test_pass_cnt++; + if (num_sensors_pass < NUM_OF_SENSORS) { + memcpy(sensor_data_pass[num_sensors_pass], + sensor_data[i], SENSOR_DATA_SIZE); + num_sensors_pass++; + } else { + memcpy(rest_data_pass[rest_sensors_pass], + sensor_data[i], SENSOR_DATA_SIZE); + rest_sensors_pass++; + } + } else { + health_test_fail_cnt++; + } + } + + /* Check if sensor_data_pass is full */ + if (num_sensors_pass == NUM_OF_SENSORS) { + /* + * Use vetted conditioner SHA512/256 as per + * NIST.SP.800-90B to condition raw data from entropy + * source. + */ + res = hash_sha512_256_compute(entropy_sha512_256, + (uint8_t *)sensor_data_pass, + CONDITIONER_PAYLOAD); + if (res == TEE_SUCCESS) + pool_add_entropy(entropy_sha512_256, + TEE_SHA256_HASH_SIZE); + } + + if (rest_sensors_pass) + memcpy((uint8_t *)sensor_data_pass, (uint8_t *)rest_data_pass, + (rest_sensors_pass * SENSOR_DATA_SIZE)); + + num_sensors_pass = rest_sensors_pass; +} + +void rng_collect_entropy(void) +{ + uint8_t i; + void *vaddr; + uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); + + cpu_spin_lock(&entropy_lock); + + for (i = 0; i < NUM_OF_SENSORS; i++) { + vaddr = phys_to_virt_io(THERMAL_SENSOR_BASE0 + + (THERMAL_SENSOR_OFFSET * i) + + TEMP_DATA_REG_OFFSET); + sensor_data[i][sensor_idx] = (uint8_t)read32((vaddr_t)vaddr); + } + + sensor_idx++; + + if (sensor_idx >= SENSOR_DATA_SIZE) { + pool_check_add_entropy(); + sensor_idx = 0; + } + + if (entropy_size >= ENTROPY_POOL_SIZE) { + generic_timer_stop(); + timer_fiq_running = false; + } + + cpu_spin_unlock(&entropy_lock); + thread_set_exceptions(exceptions); +} + +static TEE_Result rng_get_entropy(uint32_t types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + uint8_t *e = NULL; + uint32_t pool_size = 0, rq_size = 0; + uint32_t exceptions; + TEE_Result res = TEE_SUCCESS; + + if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE)) { + EMSG("bad parameters types: 0x%" PRIx32, types); + return TEE_ERROR_BAD_PARAMETERS; + } + + rq_size = params[0].memref.size; + + if ((rq_size == 0) || (rq_size > ENTROPY_POOL_SIZE)) + return TEE_ERROR_NOT_SUPPORTED; + + e = (uint8_t *)params[0].memref.buffer; + if (!e) + return TEE_ERROR_BAD_PARAMETERS; + + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); + cpu_spin_lock(&entropy_lock); + + /* + * Report health test failure to normal world in case fail count + * exceeds 1% of pass count. + */ + if (health_test_pass_cnt > 100) { + if (health_test_fail_cnt > (health_test_pass_cnt / 100)) { + res = TEE_ERROR_HEALTH_TEST_FAIL; + params[0].memref.size = 0; + health_test_pass_cnt = 0; + health_test_fail_cnt = 0; + goto exit; + } + } else { + if (health_test_fail_cnt > 1) { + res = TEE_ERROR_HEALTH_TEST_FAIL; + params[0].memref.size = 0; + health_test_pass_cnt = 0; + health_test_fail_cnt = 0; + goto exit; + } + } + + pool_size = entropy_size; + + if (pool_size < rq_size) { + params[0].memref.size = pool_size; + pool_get_entropy(e, pool_size); + } else { + params[0].memref.size = rq_size; + pool_get_entropy(e, rq_size); + } + +exit: + if (timer_fiq_running == false) { + /* Enable timer FIQ to fetch entropy */ + generic_timer_start(); + timer_fiq_running = true; + } + + cpu_spin_unlock(&entropy_lock); + thread_set_exceptions(exceptions); + + return res; +} + +/* + * Trusted Application Entry Points + */ +static TEE_Result open_session(uint32_t param_types __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused, + void **session_context __unused) +{ + DMSG("open entry point for pseudo-TA \"%s\"", PTA_NAME); + return TEE_SUCCESS; +} + +static TEE_Result invoke_command(void *pSessionContext __unused, + uint32_t nCommandID, uint32_t nParamTypes, + TEE_Param pParams[TEE_NUM_PARAMS]) +{ + FMSG("command entry point for pseudo-TA \"%s\"", PTA_NAME); + + switch (nCommandID) { + case PTA_CMD_GET_ENTROPY: + return rng_get_entropy(nParamTypes, pParams); + default: + break; + } + + return TEE_ERROR_NOT_IMPLEMENTED; +} + +pseudo_ta_register(.uuid = PTA_RNG_UUID, .name = PTA_NAME, + .flags = PTA_DEFAULT_FLAGS, + .open_session_entry_point = open_session, + .invoke_command_entry_point = invoke_command); diff --git a/core/arch/arm/plat-synquacer/rng_pta.h b/core/arch/arm/plat-synquacer/rng_pta.h new file mode 100644 index 0000000..8ce2afa --- /dev/null +++ b/core/arch/arm/plat-synquacer/rng_pta.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2018, Linaro Limited + */ + +#ifndef __RNG_PTA_H +#define __RNG_PTA_H + +void rng_collect_entropy(void); + +#endif /* __RNG_PTA_H */ diff --git a/core/arch/arm/plat-synquacer/rng_pta_client.h b/core/arch/arm/plat-synquacer/rng_pta_client.h new file mode 100644 index 0000000..b7986d3 --- /dev/null +++ b/core/arch/arm/plat-synquacer/rng_pta_client.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2018, Linaro Limited + */ + +#ifndef __RNG_PTA_CLIENT_H +#define __RNG_PTA_CLIENT_H + +#define PTA_RNG_UUID { 0xab7a617c, 0xb8e7, 0x4d8f, \ + { 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64 } } + +#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 + +/* + * PTA_CMD_GET_ENTROPY - Get Entropy from RNG using Thermal Sensor + * + * param[0] (inout memref) - Entropy buffer memory reference + * param[1] unused + * param[2] unused + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool + * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed + */ +#define PTA_CMD_GET_ENTROPY 0x0 + +#endif /* __RNG_PTA_CLIENT_H */ diff --git a/core/arch/arm/plat-synquacer/sub.mk b/core/arch/arm/plat-synquacer/sub.mk index cfa1dc3..013e57d 100644 --- a/core/arch/arm/plat-synquacer/sub.mk +++ b/core/arch/arm/plat-synquacer/sub.mk @@ -1,3 +1,4 @@ global-incdirs-y += . srcs-y += main.c +srcs-y += rng_pta.c srcs-y += timer_fiq.c
This platform provides 7 on-chip thermal sensors accessible from secure world only. So, using thermal noise from these sensors we have tried to create an entropy source as a pseudo TA. Signed-off-by: Sumit Garg <sumit.garg@linaro.org> --- core/arch/arm/plat-synquacer/main.c | 9 + core/arch/arm/plat-synquacer/platform_config.h | 1 + core/arch/arm/plat-synquacer/rng_pta.c | 363 +++++++++++++++++++++++++ core/arch/arm/plat-synquacer/rng_pta.h | 11 + core/arch/arm/plat-synquacer/rng_pta_client.h | 30 ++ core/arch/arm/plat-synquacer/sub.mk | 1 + 6 files changed, 415 insertions(+) create mode 100644 core/arch/arm/plat-synquacer/rng_pta.c create mode 100644 core/arch/arm/plat-synquacer/rng_pta.h create mode 100644 core/arch/arm/plat-synquacer/rng_pta_client.h -- 2.7.4