diff options
-rw-r--r-- | core/arch/arm/kernel/tee_time_arm_cntpct.c | 17 | ||||
-rw-r--r-- | core/arch/arm/kernel/thread.c | 12 | ||||
-rw-r--r-- | core/arch/arm/mm/tee_pager.c | 2 | ||||
-rw-r--r-- | core/arch/arm/pta/system.c | 8 | ||||
-rw-r--r-- | core/arch/arm/tee/entry_std.c | 8 | ||||
-rw-r--r-- | core/crypto.mk | 3 | ||||
-rw-r--r-- | core/crypto/rng_fortuna.c | 531 | ||||
-rw-r--r-- | core/crypto/rng_hw.c | 26 | ||||
-rw-r--r-- | core/crypto/sub.mk | 8 | ||||
-rw-r--r-- | core/drivers/imx_snvs.c | 4 | ||||
-rw-r--r-- | core/include/crypto/crypto.h | 51 | ||||
-rw-r--r-- | core/include/tee/tee_cryp_utl.h | 16 | ||||
-rw-r--r-- | core/lib/libtomcrypt/src/sub.mk | 1 | ||||
-rw-r--r-- | core/lib/libtomcrypt/src/tee_ltc_provider.c | 204 | ||||
-rw-r--r-- | core/tee/tee_cryp_utl.c | 55 |
15 files changed, 708 insertions, 238 deletions
diff --git a/core/arch/arm/kernel/tee_time_arm_cntpct.c b/core/arch/arm/kernel/tee_time_arm_cntpct.c index ea36b65d..bf738e8a 100644 --- a/core/arch/arm/kernel/tee_time_arm_cntpct.c +++ b/core/arch/arm/kernel/tee_time_arm_cntpct.c @@ -3,18 +3,17 @@ * Copyright (c) 2014, 2015 Linaro Limited */ +#include <arm.h> +#include <crypto/crypto.h> #include <kernel/misc.h> #include <kernel/tee_time.h> -#include <trace.h> #include <kernel/time_source.h> #include <mm/core_mmu.h> -#include <utee_defines.h> - -#include <tee/tee_cryp_utl.h> - -#include <stdint.h> #include <mpa.h> -#include <arm.h> +#include <stdint.h> +#include <tee/tee_cryp_utl.h> +#include <trace.h> +#include <utee_defines.h> static TEE_Result arm_cntpct_get_sys_time(TEE_Time *time) { @@ -49,7 +48,7 @@ REGISTER_TIME_SOURCE(arm_cntpct_time_source) * and adding one byte of entropy when we reach 8 rotated bits. */ -void plat_prng_add_jitter_entropy(void) +void plat_prng_add_jitter_entropy(enum crypto_rng_src sid, unsigned int *pnum) { uint64_t tsc = read_cntpct(); int bytes = 0, n; @@ -73,6 +72,6 @@ void plat_prng_add_jitter_entropy(void) if (bytes) { FMSG("%s: 0x%02X\n", __func__, (int)acc & ((1 << (bytes * 8)) - 1)); - tee_prng_add_entropy((uint8_t *)&acc, bytes); + crypto_rng_add_event(sid, pnum, (uint8_t *)&acc, bytes); } } diff --git a/core/arch/arm/kernel/thread.c b/core/arch/arm/kernel/thread.c index 1e67e2c5..ed8cb13d 100644 --- a/core/arch/arm/kernel/thread.c +++ b/core/arch/arm/kernel/thread.c @@ -143,6 +143,8 @@ static uint8_t thread_user_kdata_page[ static unsigned int thread_global_lock = SPINLOCK_UNLOCK; static bool thread_prealloc_rpc_cache; +static unsigned int thread_rpc_pnum; + static void init_canaries(void) { #ifdef CFG_WITH_STACK_CANARIES @@ -1442,13 +1444,9 @@ uint32_t thread_rpc_cmd(uint32_t cmd, size_t num_params, uint64_t carg; size_t n; - /* - * Break recursion in case plat_prng_add_jitter_entropy_norpc() - * sleeps on a mutex or unlocks a mutex with a sleeper (contended - * mutex). - */ - if (cmd != OPTEE_MSG_RPC_CMD_WAIT_QUEUE) - plat_prng_add_jitter_entropy_norpc(); + /* The source CRYPTO_RNG_SRC_JITTER_RPC is safe to use here */ + plat_prng_add_jitter_entropy(CRYPTO_RNG_SRC_JITTER_RPC, + &thread_rpc_pnum); if (!get_rpc_arg(cmd, num_params, &arg, &carg)) return TEE_ERROR_OUT_OF_MEMORY; diff --git a/core/arch/arm/mm/tee_pager.c b/core/arch/arm/mm/tee_pager.c index d1caa139..ed74156b 100644 --- a/core/arch/arm/mm/tee_pager.c +++ b/core/arch/arm/mm/tee_pager.c @@ -371,7 +371,7 @@ void tee_pager_generate_authenc_key(void) { uint8_t key[PAGER_AE_KEY_BITS / 8]; - if (rng_generate(key, sizeof(key)) != TEE_SUCCESS) + if (crypto_rng_read(key, sizeof(key)) != TEE_SUCCESS) panic("failed to generate random"); if (internal_aes_gcm_expand_enc_key(key, sizeof(key), &pager_ae_key)) diff --git a/core/arch/arm/pta/system.c b/core/arch/arm/pta/system.c index dbb019fc..1186407d 100644 --- a/core/arch/arm/pta/system.c +++ b/core/arch/arm/pta/system.c @@ -6,11 +6,13 @@ #include <kernel/pseudo_ta.h> #include <kernel/user_ta.h> #include <pta_system.h> -#include <tee/tee_cryp_utl.h> +#include <crypto/crypto.h> #include <util.h> #define MAX_ENTROPY_IN 32u +static unsigned int system_pnum; + static TEE_Result system_rng_reseed(struct tee_ta_session *s __unused, uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]) @@ -33,7 +35,9 @@ static TEE_Result system_rng_reseed(struct tee_ta_session *s __unused, entropy_sz = MIN(entropy_sz, MAX_ENTROPY_IN); - return tee_prng_add_entropy(entropy_input, entropy_sz); + crypto_rng_add_event(CRYPTO_RNG_SRC_NONSECURE, &system_pnum, + entropy_input, entropy_sz); + return TEE_SUCCESS; } static TEE_Result open_session(uint32_t param_types __unused, diff --git a/core/arch/arm/tee/entry_std.c b/core/arch/arm/tee/entry_std.c index 5a7fef92..47c97406 100644 --- a/core/arch/arm/tee/entry_std.c +++ b/core/arch/arm/tee/entry_std.c @@ -35,6 +35,8 @@ static struct mobj *shm_mobj; static struct mobj **sdp_mem_mobjs; #endif +static unsigned int session_pnum; + static bool param_mem_from_mobj(struct param_mem *mem, struct mobj *mobj, const paddr_t pa, const size_t sz) { @@ -295,7 +297,8 @@ static void entry_open_session(struct thread_smc_args *smc_args, * un-predictable, using this property to increase randomness * of prng */ - plat_prng_add_jitter_entropy(); + plat_prng_add_jitter_entropy(CRYPTO_RNG_SRC_JITTER_SESSION, + &session_pnum); cleanup_params: cleanup_params(arg->params + num_meta, saved_attr, @@ -322,7 +325,8 @@ static void entry_close_session(struct thread_smc_args *smc_args, goto out; } - plat_prng_add_jitter_entropy(); + plat_prng_add_jitter_entropy(CRYPTO_RNG_SRC_JITTER_SESSION, + &session_pnum); s = (struct tee_ta_session *)(vaddr_t)arg->session; res = tee_ta_close_session(s, &tee_open_sessions, NSAPP_IDENTITY); diff --git a/core/crypto.mk b/core/crypto.mk index 74536139..b0a50d54 100644 --- a/core/crypto.mk +++ b/core/crypto.mk @@ -48,6 +48,8 @@ CFG_CRYPTO_SHA256:=y endif endif +$(eval $(call cryp-enable-all-depends,CFG_WITH_SOFTWARE_PRNG, AES ECB SHA256)) + ifeq ($(CFG_CRYPTO_WITH_CE),y) $(call force,CFG_AES_GCM_TABLE_BASED,n,conflicts with CFG_CRYPTO_WITH_CE) @@ -135,4 +137,3 @@ _CFG_CRYPTO_WITH_HASH := $(call cryp-one-enabled, MD5 SHA1 SHA224 SHA256 SHA384 _CFG_CRYPTO_WITH_MAC := $(call cryp-one-enabled, HMAC CMAC CBC_MAC) _CFG_CRYPTO_WITH_CBC := $(call cryp-one-enabled, CBC CBC_MAC) _CFG_CRYPTO_WITH_ASN1 := $(call cryp-one-enabled, RSA DSA ECC) -_CFG_CRYPTO_WITH_FORTUNA_PRNG := $(call cryp-all-enabled, AES SHA256) diff --git a/core/crypto/rng_fortuna.c b/core/crypto/rng_fortuna.c new file mode 100644 index 00000000..b9ce2d22 --- /dev/null +++ b/core/crypto/rng_fortuna.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* Copyright (c) 2018, Linaro Limited */ + +/* + * This is an implementation of the Fortuna cryptographic PRNG as defined in + * https://www.schneier.com/academic/paperfiles/fortuna.pdf + * There's one small exception, see comment in restart_pool() below. + */ + +#include <assert.h> +#include <crypto/crypto.h> +#include <kernel/mutex.h> +#include <kernel/refcount.h> +#include <kernel/spinlock.h> +#include <kernel/tee_time.h> +#include <string.h> +#include <types_ext.h> +#include <utee_defines.h> +#include <util.h> + +#define NUM_POOLS 32 +#define BLOCK_SIZE 16 +#define KEY_SIZE 32 +#define CIPHER_ALGO TEE_ALG_AES_ECB_NOPAD +#define HASH_ALGO TEE_ALG_SHA256 +#define MIN_POOL_SIZE 64 +#define MAX_EVENT_DATA_LEN 32U +#define RING_BUF_DATA_SIZE 4U + +/* + * struct fortuna_state - state of the Fortuna PRNG + * @ctx: Cipher context used to produce the random numbers + * @counter: Counter which is encrypted to produce the random numbers + * @pool0_length: Amount of data added to pool0 + * @pool_ctx: One hash context for each pool + * @reseed_ctx: Hash context used while reseeding + * @reseed_count: Number of time we've reseeded the PRNG, used to tell + * which pools should be used in the reseed process + * @next_reseed_time: If we have a secure time, the earliest next time we + * may reseed + * + * To minimize the delay in crypto_rng_add_event() there's @pool_spin_lock + * which protects everything needed by this function. + * + * @next_reseed_time is used as a rate limiter for reseeding. + */ +static struct fortuna_state { + void *ctx; + uint64_t counter[2]; + unsigned int pool0_length; + void *pool_ctx[NUM_POOLS]; + void *reseed_ctx; + uint32_t reseed_count; +#ifndef CFG_SECURE_TIME_SOURCE_REE + TEE_Time next_reseed_time; +#endif +} state; + +static struct mutex state_mu = MUTEX_INITIALIZER; + +static struct { + struct { + uint8_t snum; + uint8_t pnum; + uint8_t dlen; + uint8_t data[RING_BUF_DATA_SIZE]; + } elem[8]; + unsigned int begin; + unsigned int end; +} ring_buffer; + +unsigned int ring_buffer_spin_lock; + +static void inc_counter(uint64_t counter[2]) +{ + counter[0]++; + if (!counter[0]) + counter[1]++; +} + +static TEE_Result hash_init(void *ctx) +{ + return crypto_hash_init(ctx, HASH_ALGO); +} + +static TEE_Result hash_update(void *ctx, const void *data, size_t dlen) +{ + return crypto_hash_update(ctx, HASH_ALGO, data, dlen); +} + +static TEE_Result hash_final(void *ctx, uint8_t digest[KEY_SIZE]) +{ + return crypto_hash_final(ctx, HASH_ALGO, digest, KEY_SIZE); +} + +static TEE_Result key_from_data(void *ctx, const void *data, size_t dlen, + uint8_t key[KEY_SIZE]) +{ + TEE_Result res; + + res = hash_init(ctx); + if (res) + return res; + res = hash_update(ctx, data, dlen); + if (res) + return res; + return hash_final(ctx, key); +} + +static TEE_Result cipher_init(void *ctx, uint8_t key[KEY_SIZE]) +{ + return crypto_cipher_init(ctx, CIPHER_ALGO, TEE_MODE_ENCRYPT, + key, KEY_SIZE, NULL, 0, NULL, 0); +} + +static void fortuna_done(void) +{ + size_t n; + + for (n = 0; n < NUM_POOLS; n++) { + crypto_hash_free_ctx(state.pool_ctx[n], HASH_ALGO); + state.pool_ctx[n] = NULL; + } + crypto_hash_free_ctx(state.reseed_ctx, HASH_ALGO); + state.reseed_ctx = NULL; + crypto_cipher_free_ctx(state.ctx, CIPHER_ALGO); + state.ctx = NULL; +} + +TEE_Result crypto_rng_init(const void *data, size_t dlen) +{ + TEE_Result res; + uint8_t key[KEY_SIZE]; + void *ctx; + size_t n; + + COMPILE_TIME_ASSERT(sizeof(state.counter) == BLOCK_SIZE); + + if (state.ctx) + return TEE_ERROR_BAD_STATE; + + memset(&state, 0, sizeof(state)); + + for (n = 0; n < NUM_POOLS; n++) { + res = crypto_hash_alloc_ctx(&state.pool_ctx[n], HASH_ALGO); + if (res) + goto err; + res = crypto_hash_init(state.pool_ctx[n], HASH_ALGO); + if (res) + goto err; + } + + res = crypto_hash_alloc_ctx(&state.reseed_ctx, HASH_ALGO); + if (res) + goto err; + + res = key_from_data(state.reseed_ctx, data, dlen, key); + if (res) + return res; + + res = crypto_cipher_alloc_ctx(&ctx, CIPHER_ALGO); + if (res) + return res; + res = cipher_init(ctx, key); + if (res) + return res; + inc_counter(state.counter); + state.ctx = ctx; + return TEE_SUCCESS; +err: + fortuna_done(); + return res; +} + +static void push_ring_buffer(uint8_t snum, uint8_t pnum, const void *data, + size_t dlen) +{ + uint8_t dl = MIN(RING_BUF_DATA_SIZE, dlen); + unsigned int next_begin; + uint32_t old_itr_status; + + /* Spinlock to serialize writers */ + old_itr_status = cpu_spin_lock_xsave(&ring_buffer_spin_lock); + + next_begin = (ring_buffer.begin + 1) % ARRAY_SIZE(ring_buffer.elem); + if (next_begin == atomic_load_uint(&ring_buffer.end)) + goto out; /* buffer is full */ + + ring_buffer.elem[next_begin].snum = snum; + ring_buffer.elem[next_begin].pnum = pnum; + ring_buffer.elem[next_begin].dlen = dl; + memcpy(ring_buffer.elem[next_begin].data, data, dl); + + atomic_store_uint(&ring_buffer.begin, next_begin); + +out: + cpu_spin_unlock_xrestore(&ring_buffer_spin_lock, old_itr_status); +} + +static size_t pop_ring_buffer(uint8_t *snum, uint8_t *pnum, + uint8_t data[RING_BUF_DATA_SIZE]) +{ + unsigned int next_end; + size_t dlen; + + if (atomic_load_uint(&ring_buffer.begin) == ring_buffer.end) + return 0; + + next_end = (ring_buffer.end + 1) % ARRAY_SIZE(ring_buffer.elem); + + *snum = ring_buffer.elem[ring_buffer.end].snum; + *pnum = ring_buffer.elem[ring_buffer.end].pnum; + dlen = MIN(ring_buffer.elem[ring_buffer.end].dlen, RING_BUF_DATA_SIZE); + assert(ring_buffer.elem[ring_buffer.end].dlen == dlen); + memcpy(data, ring_buffer.elem[ring_buffer.end].data, dlen); + + atomic_store_uint(&ring_buffer.end, next_end); + + return dlen; +} + +static TEE_Result add_event(uint8_t snum, uint8_t pnum, + const void *data, size_t dlen) +{ + TEE_Result res; + size_t dl = MIN(MAX_EVENT_DATA_LEN, dlen); + uint8_t v[] = { snum, dl }; + + if (pnum >= NUM_POOLS) + return TEE_ERROR_BAD_PARAMETERS; + + res = hash_update(state.pool_ctx[pnum], v, sizeof(v)); + if (res) + return res; + res = hash_update(state.pool_ctx[pnum], data, dl); + if (res) + return res; + if (!pnum) { + unsigned int l; + + if (!ADD_OVERFLOW(state.pool0_length, dl, &l)) + state.pool0_length = l; + } + + return TEE_SUCCESS; +} + +static TEE_Result drain_ring_buffer(void) +{ + while (true) { + TEE_Result res; + uint8_t snum; + uint8_t pnum; + uint8_t data[RING_BUF_DATA_SIZE]; + size_t dlen; + + dlen = pop_ring_buffer(&snum, &pnum, data); + if (!dlen) + return TEE_SUCCESS; + + res = add_event(snum, pnum, data, dlen); + if (res) + return res; + } +} + +static unsigned int get_next_pnum(unsigned int *pnum) +{ + unsigned int nval; + unsigned int oval = atomic_load_uint(pnum); + + while (true) { + nval = (oval + 1) % NUM_POOLS; + + if (atomic_cas_uint(pnum, &oval, nval)) { + /* + * *pnum is normally initialized to 0 and we'd like + * to start feeding pool number 0 as that's the + * most important one. + * + * If we where to take just *pnum and increase it + * later multiple updaters could end up with the + * same number. + * + * By increasing first we get the number unique for + * next update and by subtracting one (using + * modulus) we get the number for this update. + */ + return (nval + NUM_POOLS - 1) % NUM_POOLS; + } + /* + * At this point atomic_cas_uint() has updated oval to the + * current *pnum. + */ + } +} + +void crypto_rng_add_event(enum crypto_rng_src sid, unsigned int *pnum, + const void *data, size_t dlen) +{ + unsigned int pn = get_next_pnum(pnum); + uint8_t snum = sid >> 1; + + if (CRYPTO_RNG_SRC_IS_QUICK(sid)) { + push_ring_buffer(snum, pn, data, dlen); + } else { + mutex_lock(&state_mu); + add_event(snum, pn, data, dlen); + drain_ring_buffer(); + mutex_unlock(&state_mu); + } +} + +/* GenerateBlocks */ +static TEE_Result generate_blocks(void *block, size_t nblocks) +{ + uint8_t *b = block; + size_t n; + + for (n = 0; n < nblocks; n++) { + TEE_Result res = crypto_cipher_update(state.ctx, CIPHER_ALGO, + TEE_MODE_ENCRYPT, false, + (void *)state.counter, + BLOCK_SIZE, + b + n * BLOCK_SIZE); + + /* + * Make sure to increase the counter before returning an + * eventual errors, we must never re-use the counter with + * the same key. + */ + inc_counter(state.counter); + if (res) + return res; + } + + return TEE_SUCCESS; +} + +/* GenerateRandomData */ +static TEE_Result generate_random_data(void *buf, size_t blen) +{ + TEE_Result res; + + res = generate_blocks(buf, blen / BLOCK_SIZE); + if (res) + return res; + if (blen % BLOCK_SIZE) { + uint8_t block[BLOCK_SIZE]; + uint8_t *b = (uint8_t *)buf + ROUNDDOWN(blen, BLOCK_SIZE); + + res = generate_blocks(block, 1); + if (res) + return res; + memcpy(b, block, blen % BLOCK_SIZE); + } + + return TEE_SUCCESS; +} + +#ifdef CFG_SECURE_TIME_SOURCE_REE +static bool reseed_rate_limiting(void) +{ + /* + * There's no point in checking REE time for reseed rate limiting, + * and also it makes it less complicated if we can avoid doing RPC + * here. + */ + return false; +} +#else +static bool reseed_rate_limiting(void) +{ + TEE_Result res; + TEE_Time time; + const TEE_Time time_100ms = { 0, 100 }; + + res = tee_time_get_sys_time(&time); + /* + * Failure to read time must result in allowing reseed or we could + * block reseeding forever. + */ + if (res) + return false; + + if (TEE_TIME_LT(time, state.next_reseed_time)) + return true; + + /* Time to reseed, calculate next time reseed is OK */ + TEE_TIME_ADD(time, time_100ms, state.next_reseed_time); + return false; +} +#endif + +static TEE_Result restart_pool(void *pool_ctx, uint8_t pool_digest[KEY_SIZE]) +{ + TEE_Result res = hash_final(pool_ctx, pool_digest); + + if (res) + return res; + + res = hash_init(pool_ctx); + if (res) + return res; + + /* + * Restart the pool with the digest of the old pool. This is an + * extension to Fortuna. In the original Fortuna all pools was + * restarted from scratch. This extension is one more defense + * against spamming of the pools with known data which could lead + * to the spammer knowing the state of the pools. + * + * This extra precaution could be useful since OP-TEE sometimes + * have very few sources of good entropy and at the same time has + * sources that could quite easily be predicted by an attacker. + */ + return hash_update(pool_ctx, pool_digest, KEY_SIZE); +} + +static bool reseed_from_pool(uint32_t reseed_count, size_t pool_num) +{ + /* + * Specification says: use pool if + * 2^pool_num is a divisor of reseed_count + * + * in order to avoid an expensive modulus operation we're + * optimizing this below. + */ + return !pool_num || !((reseed_count >> (pool_num - 1)) & 1); +} + +static TEE_Result maybe_reseed(void) +{ + TEE_Result res; + size_t n; + uint8_t pool_digest[KEY_SIZE]; + + if (state.pool0_length < MIN_POOL_SIZE) + return TEE_SUCCESS; + + if (reseed_rate_limiting()) + return TEE_SUCCESS; + + state.reseed_count++; + + res = hash_init(state.reseed_ctx); + if (res) + return res; + + for (n = 0; + n < NUM_POOLS && reseed_from_pool(state.reseed_count, n); n++) { + res = restart_pool(state.pool_ctx[n], pool_digest); + if (res) + return res; + if (!n) + state.pool0_length = 0; + + res = hash_update(state.reseed_ctx, pool_digest, KEY_SIZE); + if (res) + return res; + } + res = hash_final(state.reseed_ctx, pool_digest); + if (res) + return res; + + crypto_cipher_final(state.ctx, CIPHER_ALGO); + res = crypto_cipher_init(state.ctx, CIPHER_ALGO, TEE_MODE_ENCRYPT, + pool_digest, KEY_SIZE, NULL, 0, NULL, 0); + if (res) + return res; + inc_counter(state.counter); + + return TEE_SUCCESS; +} + +static TEE_Result fortuna_read(void *buf, size_t blen) +{ + TEE_Result res; + + if (!state.ctx) + return TEE_ERROR_BAD_STATE; + + mutex_lock(&state_mu); + + res = maybe_reseed(); + if (res) + goto out; + + if (blen) { + uint8_t new_key[KEY_SIZE]; + + res = generate_random_data(buf, blen); + if (res) + goto out; + + res = generate_blocks(new_key, KEY_SIZE / BLOCK_SIZE); + if (res) + goto out; + crypto_cipher_final(state.ctx, CIPHER_ALGO); + res = cipher_init(state.ctx, new_key); + if (res) + goto out; + } + + res = drain_ring_buffer(); +out: + if (res) + fortuna_done(); + mutex_unlock(&state_mu); + + return res; +} + +TEE_Result crypto_rng_read(void *buf, size_t blen) +{ + size_t offs = 0; + + while (true) { + TEE_Result res; + size_t n; + + /* Draw at most 1 MiB of random on a single key */ + n = MIN(blen - offs, SIZE_1M); + if (!n) + return TEE_SUCCESS; + res = fortuna_read((uint8_t *)buf + offs, n); + if (res) + return res; + offs += n; + } +} diff --git a/core/crypto/rng_hw.c b/core/crypto/rng_hw.c new file mode 100644 index 00000000..577eacaa --- /dev/null +++ b/core/crypto/rng_hw.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* Copyright (c) 2018, Linaro Limited */ + +#include <compiler.h> +#include <crypto/crypto.h> +#include <tee/tee_cryp_utl.h> +#include <types_ext.h> + +TEE_Result __weak crypto_rng_init(const void *data __unused, + size_t dlen __unused) +{ + return TEE_SUCCESS; +} + +void __weak crypto_rng_add_event(enum crypto_rng_src sid __unused, + unsigned int *pnum __unused, + const void *data __unused, + size_t dlen __unused) +{ +} + +TEE_Result __weak crypto_rng_read(void *buf, size_t blen) +{ + return get_rng_array(buf, blen); +} + diff --git a/core/crypto/sub.mk b/core/crypto/sub.mk index f21eface..bb16a3eb 100644 --- a/core/crypto/sub.mk +++ b/core/crypto/sub.mk @@ -6,4 +6,10 @@ srcs-y += aes-gcm-ghash-tbl.c else srcs-y += aes-gcm-ghash.c endif -srcs-$(CFG_WITH_USER_TA) += signed_hdr.c
\ No newline at end of file +srcs-$(CFG_WITH_USER_TA) += signed_hdr.c + +ifeq ($(CFG_WITH_SOFTWARE_PRNG),y) +srcs-y += rng_fortuna.c +else +srcs-y += rng_hw.c +endif diff --git a/core/drivers/imx_snvs.c b/core/drivers/imx_snvs.c index bf5a7df6..278d42f9 100644 --- a/core/drivers/imx_snvs.c +++ b/core/drivers/imx_snvs.c @@ -157,7 +157,7 @@ TEE_Result snvs_srtc_enable(void) } /* Reused from tee_time_arm_cntpct.c */ -void plat_prng_add_jitter_entropy(void) +void plat_prng_add_jitter_entropy(enum crypto_rng_src sid, unsigned int *pnum) { uint64_t tsc = snvs_srtc_read_lp_counter(); int bytes = 0, n; @@ -181,6 +181,6 @@ void plat_prng_add_jitter_entropy(void) if (bytes) { FMSG("%s: 0x%02" PRIX16, __func__, acc & GENMASK_32(bytes * 8, 0)); - tee_prng_add_entropy((uint8_t *)&acc, bytes); + crypto_rng_add_event(sid, pnum, (uint8_t *)&acc, bytes); } } diff --git a/core/include/crypto/crypto.h b/core/include/crypto/crypto.h index 38cf752d..2018d3cf 100644 --- a/core/include/crypto/crypto.h +++ b/core/include/crypto/crypto.h @@ -256,13 +256,54 @@ TEE_Result crypto_acipher_ecc_shared_secret(struct ecc_keypair *private_key, TEE_Result hash_sha256_check(const uint8_t *hash, const uint8_t *data, size_t data_size); -/* Add entropy to PRNG entropy pool. */ -TEE_Result crypto_rng_add_entropy(const uint8_t *inbuf, size_t len); +#define CRYPTO_RNG_SRC_IS_QUICK(sid) (!!((sid) & 1)) -/* To read random data from PRNG implementation. */ -TEE_Result crypto_rng_read(void *buf, size_t blen); +/* + * enum crypto_rng_src - RNG entropy source + * + * Identifiers for different RNG entropy sources. The lowest bit indicates + * if the source is to be merely queued (bit is 1) or if it's delivered + * directly to the pool. The difference is that in the latter case RPC to + * normal world can be performed and in the former it must not. + */ +enum crypto_rng_src { + CRYPTO_RNG_SRC_JITTER_SESSION = (0 << 1 | 0), + CRYPTO_RNG_SRC_JITTER_RPC = (1 << 1 | 1), + CRYPTO_RNG_SRC_NONSECURE = (1 << 1 | 0), +}; + +/* + * crypto_rng_init() - initialize the RNG + * @data: buffer with initial seed + * @dlen: length of @data + */ +TEE_Result crypto_rng_init(const void *data, size_t dlen); -TEE_Result rng_generate(void *buffer, size_t len); +/* + * crypto_rng_add_event() - supply entropy to RNG from a source + * @sid: Source identifier, should be unique for a specific source + * @pnum: Pool number, acquired using crypto_rng_get_next_pool_num() + * @data: Data associated with the event + * @dlen: Length of @data + * + * @sid controls whether the event is merly queued in a ring buffer or if + * it's added to one of the pools directly. If CRYPTO_RNG_SRC_IS_QUICK() is + * true (lowest bit set) events are queue otherwise added to corresponding + * pool. If CRYPTO_RNG_SRC_IS_QUICK() is false, eventual queued events are + * added to their queues too. + */ +void crypto_rng_add_event(enum crypto_rng_src sid, unsigned int *pnum, + const void *data, size_t dlen); + +/* + * crypto_rng_read() - read cryptograhically secure RNG + * @buf: Buffer to hold the data + * @len: Length of buffer. + * + * Eventual queued events are also added to their pools during this + * function call. + */ +TEE_Result crypto_rng_read(void *buf, size_t len); TEE_Result crypto_aes_expand_enc_key(const void *key, size_t key_len, void *enc_key, unsigned int *rounds); diff --git a/core/include/tee/tee_cryp_utl.h b/core/include/tee/tee_cryp_utl.h index 92534b3c..5ad15e6f 100644 --- a/core/include/tee/tee_cryp_utl.h +++ b/core/include/tee/tee_cryp_utl.h @@ -7,6 +7,7 @@ #define TEE_CRYP_UTL_H #include <tee_api_types.h> +#include <crypto/crypto.h> #if !defined(CFG_WITH_SOFTWARE_PRNG) TEE_Result get_rng_array(void *buffer, int len); @@ -26,12 +27,17 @@ TEE_Result tee_aes_cbc_cts_update(void *cbc_ctx, void *ecb_ctx, const uint8_t *data, size_t len, uint8_t *dst); -TEE_Result tee_prng_add_entropy(const uint8_t *in, size_t len); -void plat_prng_add_jitter_entropy(void); /* - * The _norpc version must not invoke Normal World, or infinite recursion - * may occur. As an exception however, using mutexes is allowed. + * plat_prng_add_jitter_entropy() - Adds jitter to RNG entropy pool + * @sid: source ID, normally unique per location of the call + * @pnum: pointer where the pool number for this @sid is stored + * + * Note that the supplied @sid controls (CRYPTO_RNG_SRC_IS_QUICK()) whether + * RPC is allowed to be performed or the event just will be queued for later + * consumption. */ -void plat_prng_add_jitter_entropy_norpc(void); +void plat_prng_add_jitter_entropy(enum crypto_rng_src sid, unsigned int *pnum); + +void plat_rng_init(void); #endif diff --git a/core/lib/libtomcrypt/src/sub.mk b/core/lib/libtomcrypt/src/sub.mk index 89e74bd9..ca88fb1f 100644 --- a/core/lib/libtomcrypt/src/sub.mk +++ b/core/lib/libtomcrypt/src/sub.mk @@ -16,4 +16,3 @@ subdirs-$(_CFG_CRYPTO_WITH_ACIPHER) += math subdirs-y += misc subdirs-y += modes subdirs-$(_CFG_CRYPTO_WITH_ACIPHER) += pk -subdirs-$(CFG_WITH_SOFTWARE_PRNG) += prngs diff --git a/core/lib/libtomcrypt/src/tee_ltc_provider.c b/core/lib/libtomcrypt/src/tee_ltc_provider.c index 4cec762a..86000421 100644 --- a/core/lib/libtomcrypt/src/tee_ltc_provider.c +++ b/core/lib/libtomcrypt/src/tee_ltc_provider.c @@ -25,8 +25,6 @@ #include <kernel/thread.h> #endif -#if !defined(CFG_WITH_SOFTWARE_PRNG) - /* Random generator */ static int prng_mpa_start(union Prng_state *prng __unused) { @@ -49,10 +47,10 @@ static int prng_mpa_ready(union Prng_state *prng __unused) static unsigned long prng_mpa_read(unsigned char *out, unsigned long outlen, union Prng_state *prng __unused) { - if (TEE_SUCCESS == get_rng_array(out, outlen)) - return outlen; - else + if (crypto_rng_read(out, outlen)) return 0; + + return outlen; } static int prng_mpa_done(union Prng_state *prng __unused) @@ -92,69 +90,6 @@ static const struct ltc_prng_descriptor prng_mpa_desc = { .test = &prng_mpa_test, }; -#endif /* !CFG_WITH_SOFTWARE_PRNG */ - -struct tee_ltc_prng { - int index; - const char *name; - prng_state state; - bool inited; -}; - -static struct tee_ltc_prng _tee_ltc_prng = -#if defined(CFG_WITH_SOFTWARE_PRNG) - { -#if defined(_CFG_CRYPTO_WITH_FORTUNA_PRNG) - .name = "fortuna", -#else - /* - * we need AES and SHA256 for fortuna PRNG, - * if the system configuration can't provide those, - * fallback to RC4 - */ - .name = "rc4", -#endif - }; -#else - { - .name = "prng_mpa", - }; -#endif - -static struct tee_ltc_prng *tee_ltc_get_prng(void) -{ - return &_tee_ltc_prng; -} - -static TEE_Result tee_ltc_prng_init(struct tee_ltc_prng *prng) -{ - int res; - int prng_index; - - assert(prng); - - prng_index = find_prng(prng->name); - if (prng_index == -1) - return TEE_ERROR_BAD_PARAMETERS; - - if (!prng->inited) { - res = prng_descriptor[prng_index]->start(&prng->state); - if (res != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - - plat_prng_add_jitter_entropy_norpc(); - - res = prng_descriptor[prng_index]->ready(&prng->state); - if (res != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - - prng->index = prng_index; - prng->inited = true; - } - - return TEE_SUCCESS; -} - /* * tee_ltc_reg_algs(): Registers * - algorithms @@ -189,16 +124,7 @@ static void tee_ltc_reg_algs(void) #if defined(CFG_CRYPTO_SHA512) register_hash(&sha512_desc); #endif - -#if defined(CFG_WITH_SOFTWARE_PRNG) -#if defined(_CFG_CRYPTO_WITH_FORTUNA_PRNG) - register_prng(&fortuna_desc); -#else - register_prng(&rc4_desc); -#endif -#else register_prng(&prng_mpa_desc); -#endif } @@ -692,13 +618,12 @@ TEE_Result crypto_acipher_gen_rsa_key(struct rsa_keypair *key, size_t key_size) rsa_key ltc_tmp_key; int ltc_res; long e; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); /* get the public exponent */ e = mp_get_int(key->e); /* Generate a temporary RSA key */ - ltc_res = rsa_make_key(&prng->state, prng->index, key_size/8, e, + ltc_res = rsa_make_key(NULL, find_prng("prng_mpa"), key_size / 8, e, <c_tmp_key); if (ltc_res != CRYPT_OK) { res = TEE_ERROR_BAD_PARAMETERS; @@ -934,7 +859,6 @@ TEE_Result crypto_acipher_rsaes_encrypt(uint32_t algo, .e = key->e, .N = key->n }; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); mod_size = ltc_mp.unsigned_size((void *)(ltc_key.N)); if (*dst_len < mod_size) { @@ -956,7 +880,7 @@ TEE_Result crypto_acipher_rsaes_encrypt(uint32_t algo, ltc_res = rsa_encrypt_key_ex(src, src_len, dst, (unsigned long *)(dst_len), label, - label_len, &prng->state, prng->index, + label_len, NULL, find_prng("prng_mpa"), ltc_hashindex, ltc_rsa_algo, <c_key); switch (ltc_res) { case CRYPT_PK_INVALID_PADDING: @@ -988,7 +912,6 @@ TEE_Result crypto_acipher_rsassa_sign(uint32_t algo, struct rsa_keypair *key, int ltc_res, ltc_rsa_algo, ltc_hashindex; unsigned long ltc_sig_len; rsa_key ltc_key = { 0, }; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); ltc_key.type = PK_PRIVATE; ltc_key.e = key->e; @@ -1050,7 +973,7 @@ TEE_Result crypto_acipher_rsassa_sign(uint32_t algo, struct rsa_keypair *key, ltc_sig_len = mod_size; ltc_res = rsa_sign_hash_ex(msg, msg_len, sig, <c_sig_len, - ltc_rsa_algo, &prng->state, prng->index, + ltc_rsa_algo, NULL, find_prng("prng_mpa"), ltc_hashindex, salt_len, <c_key); *sig_len = ltc_sig_len; @@ -1187,7 +1110,6 @@ TEE_Result crypto_acipher_gen_dsa_key(struct dsa_keypair *key, size_t key_size) dsa_key ltc_tmp_key; size_t group_size, modulus_size = key_size/8; int ltc_res; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); if (modulus_size <= 128) group_size = 20; @@ -1199,7 +1121,7 @@ TEE_Result crypto_acipher_gen_dsa_key(struct dsa_keypair *key, size_t key_size) group_size = 40; /* Generate the DSA key */ - ltc_res = dsa_make_key(&prng->state, prng->index, group_size, + ltc_res = dsa_make_key(NULL, find_prng("prng_mpa"), group_size, modulus_size, <c_tmp_key); if (ltc_res != CRYPT_OK) { res = TEE_ERROR_BAD_PARAMETERS; @@ -1238,7 +1160,6 @@ TEE_Result crypto_acipher_dsa_sign(uint32_t algo, struct dsa_keypair *key, .y = key->y, .x = key->x, }; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); if (algo != TEE_ALG_DSA_SHA1 && algo != TEE_ALG_DSA_SHA224 && @@ -1270,8 +1191,8 @@ TEE_Result crypto_acipher_dsa_sign(uint32_t algo, struct dsa_keypair *key, goto err; } - ltc_res = dsa_sign_hash_raw(msg, msg_len, r, s, &prng->state, - prng->index, <c_key); + ltc_res = dsa_sign_hash_raw(msg, msg_len, r, s, NULL, + find_prng("prng_mpa"), <c_key); if (ltc_res == CRYPT_OK) { *sig_len = 2 * mp_unsigned_bin_size(ltc_key.q); @@ -1363,12 +1284,11 @@ TEE_Result crypto_acipher_gen_dh_key(struct dh_keypair *key, struct bignum *q, TEE_Result res; dh_key ltc_tmp_key; int ltc_res; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); /* Generate the DH key */ ltc_tmp_key.g = key->g; ltc_tmp_key.p = key->p; - ltc_res = dh_make_key(&prng->state, prng->index, q, xbits, + ltc_res = dh_make_key(NULL, find_prng("prng_mpa"), q, xbits, <c_tmp_key); if (ltc_res != CRYPT_OK) { res = TEE_ERROR_BAD_PARAMETERS; @@ -1521,7 +1441,6 @@ TEE_Result crypto_acipher_gen_ecc_key(struct ecc_keypair *key) TEE_Result res; ecc_key ltc_tmp_key; int ltc_res; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); size_t key_size_bytes = 0; size_t key_size_bits = 0; @@ -1531,7 +1450,7 @@ TEE_Result crypto_acipher_gen_ecc_key(struct ecc_keypair *key) } /* Generate the ECC key */ - ltc_res = ecc_make_key(&prng->state, prng->index, + ltc_res = ecc_make_key(NULL, find_prng("prng_mpa"), key_size_bytes, <c_tmp_key); if (ltc_res != CRYPT_OK) { return TEE_ERROR_BAD_PARAMETERS; @@ -1647,7 +1566,6 @@ TEE_Result crypto_acipher_ecc_sign(uint32_t algo, struct ecc_keypair *key, void *r, *s; size_t key_size_bytes; ecc_key ltc_key; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); if (algo == 0) { res = TEE_ERROR_BAD_PARAMETERS; @@ -1672,7 +1590,7 @@ TEE_Result crypto_acipher_ecc_sign(uint32_t algo, struct ecc_keypair *key, } ltc_res = ecc_sign_hash_raw(msg, msg_len, r, s, - &prng->state, prng->index, <c_key); + NULL, find_prng("prng_mpa"), <c_key); if (ltc_res == CRYPT_OK) { *sig_len = 2 * key_size_bytes; @@ -2867,69 +2785,6 @@ void crypto_aes_gcm_final(void *ctx) } #endif /*CFG_CRYPTO_AES_GCM_FROM_CRYPTOLIB*/ -/****************************************************************************** - * Pseudo Random Number Generator - ******************************************************************************/ -TEE_Result crypto_rng_read(void *buf, size_t blen) -{ - int err; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); - - err = prng_is_valid(prng->index); - - if (err != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - - if (prng_descriptor[prng->index]->read(buf, blen, &prng->state) != - (unsigned long)blen) - return TEE_ERROR_BAD_STATE; - - return TEE_SUCCESS; -} - -/* Called as a result of rng_generate() below */ -static TEE_Result _tee_ltc_prng_add_entropy( - const uint8_t *inbuf __maybe_unused, size_t len __maybe_unused) -{ -#if defined(CFG_WITH_SOFTWARE_PRNG) - int err; -#ifdef _CFG_CRYPTO_WITH_FORTUNA_PRNG - int (*add_entropy)(const unsigned char *, unsigned long, - prng_state *) = fortuna_add_entropy; -#else - int (*add_entropy)(const unsigned char *, unsigned long, - prng_state *) = rc4_add_entropy; -#endif - - err = add_entropy(inbuf, len, &_tee_ltc_prng.state); - if (err != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - - return TEE_SUCCESS; -#else - return TEE_ERROR_BAD_STATE; -#endif -} - -TEE_Result crypto_rng_add_entropy(const uint8_t *inbuf, size_t len) -{ - int err; - struct tee_ltc_prng *prng = tee_ltc_get_prng(); - - err = prng_is_valid(prng->index); - - if (err != CRYPT_OK) - return _tee_ltc_prng_add_entropy(inbuf, len); - - err = prng_descriptor[prng->index]->add_entropy( - inbuf, len, &prng->state); - - if (err != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - - return TEE_SUCCESS; -} - TEE_Result crypto_init(void) { #if defined(_CFG_CRYPTO_WITH_ACIPHER) @@ -2937,7 +2792,7 @@ TEE_Result crypto_init(void) #endif tee_ltc_reg_algs(); - return tee_ltc_prng_init(tee_ltc_get_prng()); + return TEE_SUCCESS; } #if defined(CFG_WITH_VFP) @@ -2971,39 +2826,6 @@ TEE_Result hash_sha256_check(const uint8_t *hash, const uint8_t *data, } #endif -TEE_Result rng_generate(void *buffer, size_t len) -{ -#if defined(CFG_WITH_SOFTWARE_PRNG) -#ifdef _CFG_CRYPTO_WITH_FORTUNA_PRNG - int (*start)(prng_state *) = fortuna_start; - int (*ready)(prng_state *) = fortuna_ready; - unsigned long (*read)(unsigned char *, unsigned long, prng_state *) = - fortuna_read; -#else - int (*start)(prng_state *) = rc4_start; - int (*ready)(prng_state *) = rc4_ready; - unsigned long (*read)(unsigned char *, unsigned long, prng_state *) = - rc4_read; -#endif - - if (!_tee_ltc_prng.inited) { - if (start(&_tee_ltc_prng.state) != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - plat_prng_add_jitter_entropy_norpc(); - if (ready(&_tee_ltc_prng.state) != CRYPT_OK) - return TEE_ERROR_BAD_STATE; - _tee_ltc_prng.inited = true; - } - if (read(buffer, len, &_tee_ltc_prng.state) != len) - return TEE_ERROR_BAD_STATE; - return TEE_SUCCESS; - - -#else - return get_rng_array(buffer, len); -#endif -} - TEE_Result crypto_aes_expand_enc_key(const void *key, size_t key_len, void *enc_key, unsigned int *rounds) { diff --git a/core/tee/tee_cryp_utl.c b/core/tee/tee_cryp_utl.c index 78277f35..a7dfe342 100644 --- a/core/tee/tee_cryp_utl.c +++ b/core/tee/tee_cryp_utl.c @@ -5,12 +5,14 @@ #include <crypto/crypto.h> #include <initcall.h> +#include <kernel/panic.h> #include <kernel/tee_time.h> #include <rng_support.h> #include <stdlib.h> #include <string_ext.h> #include <string.h> #include <tee/tee_cryp_utl.h> +#include <trace.h> #include <utee_defines.h> #if !defined(CFG_WITH_SOFTWARE_PRNG) @@ -329,34 +331,65 @@ TEE_Result tee_aes_cbc_cts_update(void *cbc_ctx, void *ecb_ctx, return TEE_SUCCESS; } -TEE_Result tee_prng_add_entropy(const uint8_t *in, size_t len) -{ - return crypto_rng_add_entropy(in, len); -} - /* * Override this in your platform code to feed the PRNG platform-specific * jitter entropy. This implementation does not efficiently deliver entropy * and is here for backwards-compatibility. */ -__weak void plat_prng_add_jitter_entropy(void) +__weak void plat_prng_add_jitter_entropy(enum crypto_rng_src sid, + unsigned int *pnum) { TEE_Time current; +#ifdef CFG_SECURE_TIME_SOURCE_REE + if (CRYPTO_RNG_SRC_IS_QUICK(sid)) + return; /* Can't read REE time here */ +#endif + if (tee_time_get_sys_time(¤t) == TEE_SUCCESS) - tee_prng_add_entropy((uint8_t *)¤t, sizeof(current)); + crypto_rng_add_event(sid, pnum, ¤t, sizeof(current)); } -__weak void plat_prng_add_jitter_entropy_norpc(void) +__weak void plat_rng_init(void) { + TEE_Result res = TEE_SUCCESS; + TEE_Time t; + #ifndef CFG_SECURE_TIME_SOURCE_REE - plat_prng_add_jitter_entropy(); + /* + * This isn't much of a seed. Ideally we should either get a seed from + * a hardware RNG or from a previously saved seed. + * + * Seeding with hardware RNG is currently up to the platform to + * override this function. + * + * Seeding with a saved seed will require cooperation from normal + * world, this is still TODO. + */ + res = tee_time_get_sys_time(&t); +#else + EMSG("Warning: seeding RNG with zeroes"); + memset(&t, 0, sizeof(t)); #endif + if (!res) + res = crypto_rng_init(&t, sizeof(t)); + if (res) { + EMSG("Failed to initialize RNG: %#" PRIx32, res); + panic(); + } } static TEE_Result tee_cryp_init(void) { - return crypto_init(); -} + TEE_Result res = crypto_init(); + res = crypto_init(); + if (res) { + EMSG("Failed to initialize crypto API: %#" PRIx32, res); + panic(); + } + plat_rng_init(); + + return TEE_SUCCESS; +} service_init(tee_cryp_init); |