diff mbox series

[v3,3/3] synquacer: Add RNG pseudo TA

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

Commit Message

Sumit Garg Nov. 20, 2018, 6:42 a.m. UTC
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

Comments

Daniel Thompson Nov. 20, 2018, 3:58 p.m. UTC | #1
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

>
Sumit Garg Nov. 21, 2018, 6:44 a.m. UTC | #2
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 mbox series

Patch

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