blob: a9ac65abadbb4dd46b7dff7974ec8f234f5d702f [file] [log] [blame]
// 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 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
* ==========
*
* We have assumed the entropy of the sensor is better than 8 bits per
* 14 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.
*
* 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.
*
* Limitations
* ===========
*
* Output rate is limited to approx. 125 bytes per second.
*
* Our entropy estimation was not reached using any approved or
* published estimation framework such as NIST.SP.800-90B and was tested
* 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.
*
* Generating the SHA512/256 hash takes 24uS and will be run by an
* interrupt handler that pre-empts the normal world.
*/
#include <crypto/crypto.h>
#include <kernel/delay.h>
#include <kernel/pseudo_ta.h>
#include <kernel/spinlock.h>
#include <kernel/timer.h>
#include <mm/core_memprot.h>
#include <io.h>
#include <string.h>
#include <rng_pta.h>
#include <rng_pta_client.h>
#define PTA_NAME "rng.pta"
#define THERMAL_SENSOR_BASE0 0x54190800
#define THERMAL_SENSOR_OFFSET 0x80
#define NUM_SENSORS 7
#define NUM_SLOTS ((NUM_SENSORS * 2) - 1)
#define TEMP_DATA_REG_OFFSET 0x34
#define ENTROPY_POOL_SIZE 4096
#define SENSOR_DATA_SIZE 128
#define CONDITIONER_PAYLOAD (SENSOR_DATA_SIZE * NUM_SENSORS)
/*
* The health test monitors each sensor's least significant bit and counts
* the number of rising and falling edges. It verifies that both counts
* lie within interval of between 12.5% and 37.5% of the samples.
* For true random data with 8 bits of entropy per byte, both counts would
* be close to 25%.
*/
#define MAX_BIT_FLIP_EDGE_COUNT ((3 * SENSOR_DATA_SIZE) / 8)
#define MIN_BIT_FLIP_EDGE_COUNT (SENSOR_DATA_SIZE / 8)
static uint8_t entropy_pool[ENTROPY_POOL_SIZE] = {0};
static uint32_t entropy_size;
static uint8_t sensors_data[NUM_SLOTS][SENSOR_DATA_SIZE] = {0};
static uint8_t sensors_data_slot_idx;
static uint8_t sensors_data_idx;
static uint32_t health_test_fail_cnt;
static uint32_t health_test_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)
{
uint32_t falling_edge_count = 0, rising_edge_count = 0;
uint32_t lo_edge_count, hi_edge_count;
uint32_t i;
for (i = 0; i < (SENSOR_DATA_SIZE - 1); i++) {
if ((sensors_data[sensor_id][i] ^
sensors_data[sensor_id][i + 1]) & 0x1) {
falling_edge_count += (sensors_data[sensor_id][i] &
0x1);
rising_edge_count += (sensors_data[sensor_id][i + 1] &
0x1);
}
}
lo_edge_count = rising_edge_count < falling_edge_count ?
rising_edge_count : falling_edge_count;
hi_edge_count = rising_edge_count < falling_edge_count ?
falling_edge_count : rising_edge_count;
return (lo_edge_count >= MIN_BIT_FLIP_EDGE_COUNT) &&
(hi_edge_count <= MAX_BIT_FLIP_EDGE_COUNT);
}
static uint8_t pool_check_add_entropy(void)
{
uint32_t i;
uint8_t entropy_sha512_256[TEE_SHA256_HASH_SIZE];
uint8_t pool_status = 0;
TEE_Result res;
for (i = 0; i < NUM_SENSORS; i++) {
/* Check if particular sensor data passes health test */
if (health_test(sensors_data_slot_idx) == true) {
sensors_data_slot_idx++;
} else {
health_test_fail_cnt++;
memmove(sensors_data[sensors_data_slot_idx],
sensors_data[sensors_data_slot_idx + 1],
(SENSOR_DATA_SIZE * (NUM_SENSORS - i - 1)));
}
}
health_test_cnt += NUM_SENSORS;
/* Check if sensors_data have enough pass data for conditioning */
if (sensors_data_slot_idx >= NUM_SENSORS) {
/*
* Use vetted conditioner SHA512/256 as per
* NIST.SP.800-90B to condition raw data from entropy
* source.
*/
sensors_data_slot_idx -= NUM_SENSORS;
res = hash_sha512_256_compute(entropy_sha512_256,
sensors_data[sensors_data_slot_idx],
CONDITIONER_PAYLOAD);
if (res == TEE_SUCCESS)
pool_add_entropy(entropy_sha512_256,
TEE_SHA256_HASH_SIZE);
}
if (entropy_size >= ENTROPY_POOL_SIZE)
pool_status = 1;
return pool_status;
}
void rng_collect_entropy(void)
{
uint8_t i, pool_full = 0;
void *vaddr;
uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
cpu_spin_lock(&entropy_lock);
for (i = 0; i < NUM_SENSORS; i++) {
vaddr = phys_to_virt_io(THERMAL_SENSOR_BASE0 +
(THERMAL_SENSOR_OFFSET * i) +
TEMP_DATA_REG_OFFSET);
sensors_data[sensors_data_slot_idx + i][sensors_data_idx] =
(uint8_t)io_read32((vaddr_t)vaddr);
}
sensors_data_idx++;
if (sensors_data_idx >= SENSOR_DATA_SIZE) {
pool_full = pool_check_add_entropy();
sensors_data_idx = 0;
}
if (pool_full)
generic_timer_stop();
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_fail_cnt > ((health_test_cnt + 100) / 100)) {
res = TEE_ERROR_HEALTH_TEST_FAIL;
params[0].memref.size = 0;
health_test_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:
/* Enable timer FIQ to fetch entropy */
generic_timer_start(TIMER_PERIOD_MS);
cpu_spin_unlock(&entropy_lock);
thread_set_exceptions(exceptions);
return res;
}
static TEE_Result rng_get_info(uint32_t types,
TEE_Param params[TEE_NUM_PARAMS])
{
if (types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE)) {
EMSG("bad parameters types: 0x%" PRIx32, types);
return TEE_ERROR_BAD_PARAMETERS;
}
/* Output RNG rate (per second) */
params[0].value.a = 125;
/*
* Quality/entropy per 1024 bit of output data. As we have used
* a vetted conditioner as per NIST.SP.800-90B to provide full
* entropy given our assumption of entropy estimate for raw sensor
* data.
*/
params[0].value.b = 1024;
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);
case PTA_CMD_GET_RNG_INFO:
return rng_get_info(nParamTypes, pParams);
default:
break;
}
return TEE_ERROR_NOT_IMPLEMENTED;
}
pseudo_ta_register(.uuid = PTA_RNG_UUID, .name = PTA_NAME,
.flags = PTA_DEFAULT_FLAGS | TA_FLAG_DEVICE_ENUM,
.invoke_command_entry_point = invoke_command);