From bce2b6460d518affb305e49c42a2df1e94c933e8 Mon Sep 17 00:00:00 2001 From: ccli8 Date: Mon, 2 Apr 2018 15:41:12 +0800 Subject: [PATCH] Support TRNG To change TRNG security state, we need to: 1. Change CRPT/CRYPTO bit in NVIC/SCU in partition_M2351.h 2. Add/remove TRNG in device_has list in targets.json to match partition_M2351.h --- .../TARGET_NUMAKER_PFM_M2351/objects.h | 5 + .../TARGET_M2351/crypto/crypto-misc.c | 329 ++++++++++++++++++ .../TARGET_M2351/crypto/crypto-misc.h | 105 ++++++ .../TARGET_M2351/device/partition_M2351.h | 4 +- .../TARGET_NUVOTON/TARGET_M2351/trng_api.c | 89 +++++ targets/targets.json | 5 +- 6 files changed, 533 insertions(+), 4 deletions(-) create mode 100644 targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.c create mode 100644 targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.h create mode 100644 targets/TARGET_NUVOTON/TARGET_M2351/trng_api.c diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/TARGET_NUMAKER_PFM_M2351/objects.h b/targets/TARGET_NUVOTON/TARGET_M2351/TARGET_NUMAKER_PFM_M2351/objects.h index f025f883ac..0959ef25fa 100644 --- a/targets/TARGET_NUVOTON/TARGET_M2351/TARGET_NUMAKER_PFM_M2351/objects.h +++ b/targets/TARGET_NUVOTON/TARGET_M2351/TARGET_NUMAKER_PFM_M2351/objects.h @@ -112,6 +112,11 @@ struct pwmout_s { struct sleep_s { int powerdown; }; + +struct trng_s { + uint8_t dummy; +}; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.c b/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.c new file mode 100644 index 0000000000..2cd2382af1 --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.c @@ -0,0 +1,329 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "cmsis.h" +#include "mbed_assert.h" +#include "mbed_critical.h" +#include "mbed_error.h" +#include +#include "nu_modutil.h" +#include "nu_bitutil.h" +#include "crypto-misc.h" + +/* NOTE: There's inconsistency in cryptography related naming, Crpt or Crypto. For example, cryptography IRQ + * handler could be CRPT_IRQHandler or CRYPTO_IRQHandler. To override default cryptography IRQ handler, see + * device/startup_{CHIP}.c for its name or call NVIC_SetVector regardless of its name. */ +void CRPT_IRQHandler(); + +/* Track if AES H/W is available */ +static uint16_t crypto_aes_avail = 1; +/* Track if DES H/W is available */ +static uint16_t crypto_des_avail = 1; +/* Track if SHA H/W is available */ +static uint16_t crypto_sha_avail = 1; +/* Track if ECC H/W is available */ +static uint16_t crypto_ecc_avail = 1; + +/* Crypto (AES, DES, SHA, etc.) init counter. Crypto's keeps active as it is non-zero. */ +static uint16_t crypto_init_counter = 0U; + +static bool crypto_submodule_acquire(uint16_t *submodule_avail); +static void crypto_submodule_release(uint16_t *submodule_avail); + +/* Crypto done flags */ +#define CRYPTO_DONE_OK BIT0 /* Done with OK */ +#define CRYPTO_DONE_ERR BIT1 /* Done with error */ + +/* Track if PRNG H/W operation is done */ +static volatile uint16_t crypto_prng_done; +/* Track if AES H/W operation is done */ +static volatile uint16_t crypto_aes_done; +/* Track if DES H/W operation is done */ +static volatile uint16_t crypto_des_done; +/* Track if ECC H/W operation is done */ +static volatile uint16_t crypto_ecc_done; + +static void crypto_submodule_prestart(volatile uint16_t *submodule_done); +static bool crypto_submodule_wait(volatile uint16_t *submodule_done); + +/* As crypto init counter changes from 0 to 1: + * + * 1. Enable crypto clock + * 2. Enable crypto interrupt + */ +void crypto_init(void) +{ + core_util_critical_section_enter(); + if (crypto_init_counter == USHRT_MAX) { + core_util_critical_section_exit(); + error("Crypto clock enable counter would overflow (> USHRT_MAX)"); + } + core_util_atomic_incr_u16(&crypto_init_counter, 1); + if (crypto_init_counter == 1) { + /* Enable IP clock + * + * NOTE: We must call secure version (from non-secure domain) because SYS/CLK regions are secure. + */ + CLK_EnableModuleClock_S(CRPT_MODULE); + + NVIC_SetVector(CRPT_IRQn, (uint32_t) CRPT_IRQHandler); + NVIC_EnableIRQ(CRPT_IRQn); + } + core_util_critical_section_exit(); +} + +/* As crypto init counter changes from 1 to 0: + * + * 1. Disable crypto interrupt + * 2. Disable crypto clock + */ +void crypto_uninit(void) +{ + core_util_critical_section_enter(); + if (crypto_init_counter == 0) { + core_util_critical_section_exit(); + error("Crypto clock enable counter would underflow (< 0)"); + } + core_util_atomic_decr_u16(&crypto_init_counter, 1); + if (crypto_init_counter == 0) { + NVIC_DisableIRQ(CRPT_IRQn); + + /* Enable IP clock + * + * NOTE: We must call secure version (from non-secure domain) because SYS/CLK regions are secure. + */ + CLK_DisableModuleClock_S(CRPT_MODULE); + } + core_util_critical_section_exit(); +} + +/* Implementation that should never be optimized out by the compiler */ +void crypto_zeroize(void *v, size_t n) +{ + volatile unsigned char *p = (volatile unsigned char*) v; + while (n--) { + *p++ = 0; + } +} + +/* Implementation that should never be optimized out by the compiler */ +void crypto_zeroize32(uint32_t *v, size_t n) +{ + volatile uint32_t *p = (volatile uint32_t*) v; + while (n--) { + *p++ = 0; + } +} + +bool crypto_aes_acquire(void) +{ + return crypto_submodule_acquire(&crypto_aes_avail); +} + +void crypto_aes_release(void) +{ + crypto_submodule_release(&crypto_aes_avail); +} + +bool crypto_des_acquire(void) +{ + return crypto_submodule_acquire(&crypto_des_avail); +} + +void crypto_des_release(void) +{ + crypto_submodule_release(&crypto_des_avail); +} + +bool crypto_sha_acquire(void) +{ + return crypto_submodule_acquire(&crypto_sha_avail); +} + +void crypto_sha_release(void) +{ + crypto_submodule_release(&crypto_sha_avail); +} + +bool crypto_ecc_acquire(void) +{ + return crypto_submodule_acquire(&crypto_ecc_avail); +} + +void crypto_ecc_release(void) +{ + crypto_submodule_release(&crypto_ecc_avail); +} + +void crypto_prng_prestart(void) +{ + crypto_submodule_prestart(&crypto_prng_done); +} + +bool crypto_prng_wait(void) +{ + return crypto_submodule_wait(&crypto_prng_done); +} + +void crypto_aes_prestart(void) +{ + crypto_submodule_prestart(&crypto_aes_done); +} + +bool crypto_aes_wait(void) +{ + return crypto_submodule_wait(&crypto_aes_done); +} + +void crypto_des_prestart(void) +{ + crypto_submodule_prestart(&crypto_des_done); +} + +bool crypto_des_wait(void) +{ + return crypto_submodule_wait(&crypto_des_done); +} + +void crypto_ecc_prestart(void) +{ + crypto_submodule_prestart(&crypto_ecc_done); +} + +bool crypto_ecc_wait(void) +{ + return crypto_submodule_wait(&crypto_ecc_done); +} + +bool crypto_dma_buff_compat(const void *buff, size_t buff_size, size_t size_aligned_to) +{ + uint32_t buff_ = (uint32_t) buff; + + return (((buff_ & 0x03) == 0) && /* Word-aligned buffer base address */ + ((buff_size & (size_aligned_to - 1)) == 0) && /* Crypto submodule dependent buffer size alignment */ + (((buff_ >> 28) == 0x2) && (buff_size <= (0x30000000 - buff_)))); /* 0x20000000-0x2FFFFFFF */ +} + +/* Overlap cases + * + * 1. in_buff in front of out_buff: + * + * in in_end + * | | + * |||||||||||||||| + * |||||||||||||||| + * | | + * out out_end + * + * 2. out_buff in front of in_buff: + * + * in in_end + * | | + * |||||||||||||||| + * |||||||||||||||| + * | | + * out out_end + */ +bool crypto_dma_buffs_overlap(const void *in_buff, size_t in_buff_size, const void *out_buff, size_t out_buff_size) +{ + uint32_t in = (uint32_t) in_buff; + uint32_t in_end = in + in_buff_size; + uint32_t out = (uint32_t) out_buff; + uint32_t out_end = out + out_buff_size; + + bool overlap = (in <= out && out < in_end) || (out <= in && in < out_end); + + return overlap; +} + +static bool crypto_submodule_acquire(uint16_t *submodule_avail) +{ + uint16_t expectedCurrentValue = 1; + return core_util_atomic_cas_u16(submodule_avail, &expectedCurrentValue, 0); +} + +static void crypto_submodule_release(uint16_t *submodule_avail) +{ + uint16_t expectedCurrentValue = 0; + while (! core_util_atomic_cas_u16(submodule_avail, &expectedCurrentValue, 1)); +} + +static void crypto_submodule_prestart(volatile uint16_t *submodule_done) +{ + *submodule_done = 0; + + /* Ensure memory accesses above are completed before DMA is started + * + * Replacing __DSB() with __DMB() is also OK in this case. + * + * Refer to "multi-master systems" section with DMA in: + * https://static.docs.arm.com/dai0321/a/DAI0321A_programming_guide_memory_barriers_for_m_profile.pdf + */ + __DSB(); +} + +static bool crypto_submodule_wait(volatile uint16_t *submodule_done) +{ + while (! *submodule_done); + + /* Ensure while loop above and subsequent code are not reordered */ + __DSB(); + + if ((*submodule_done & CRYPTO_DONE_OK)) { + /* Done with OK */ + return true; + } else if ((*submodule_done & CRYPTO_DONE_ERR)) { + /* Done with error */ + return false; + } + + return false; +} + +/* Crypto interrupt handler */ +void CRPT_IRQHandler() +{ + uint32_t intsts; + + if ((intsts = PRNG_GET_INT_FLAG(CRYPTO_MODBASE())) != 0) { + /* Done with OK */ + crypto_prng_done |= CRYPTO_DONE_OK; + /* Clear interrupt flag */ + PRNG_CLR_INT_FLAG(CRYPTO_MODBASE()); + } else if ((intsts = AES_GET_INT_FLAG(CRYPTO_MODBASE())) != 0) { + /* Done with OK */ + crypto_aes_done |= CRYPTO_DONE_OK; + /* Clear interrupt flag */ + AES_CLR_INT_FLAG(CRYPTO_MODBASE()); + } else if ((intsts = TDES_GET_INT_FLAG(CRYPTO_MODBASE())) != 0) { + /* Done with OK */ + crypto_des_done |= CRYPTO_DONE_OK; + /* Clear interrupt flag */ + TDES_CLR_INT_FLAG(CRYPTO_MODBASE()); + } else if ((intsts = ECC_GET_INT_FLAG(CRYPTO_MODBASE())) != 0) { + /* Check interrupt flags */ + if (intsts & CRPT_INTSTS_ECCIF_Msk) { + /* Done with OK */ + crypto_ecc_done |= CRYPTO_DONE_OK; + } else if (intsts & CRPT_INTSTS_ECCEIF_Msk) { + /* Done with error */ + crypto_ecc_done |= CRYPTO_DONE_ERR; + } + /* Clear interrupt flag */ + ECC_CLR_INT_FLAG(CRYPTO_MODBASE()); + } +} diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.h b/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.h new file mode 100644 index 0000000000..5150ca66e6 --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M2351/crypto/crypto-misc.h @@ -0,0 +1,105 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_CRYPTO_MISC_H +#define MBED_CRYPTO_MISC_H + +#include +#include "partition_M2351.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Get Crypto module base dependent on security state */ +__STATIC_INLINE CRPT_T *CRYPTO_MODBASE(void) +{ +#if defined(SCU_INIT_PNSSET1_VAL) && (SCU_INIT_PNSSET1_VAL & (1<<18)) + return CRPT_NS; +#else + return CRPT; +#endif +} + +/* Init/Uninit crypto module */ +void crypto_init(void); +void crypto_uninit(void); + +/* Clear buffer to zero + * Implementation that should never be optimized out by the compiler */ +void crypto_zeroize(void *v, size_t n); +void crypto_zeroize32(uint32_t *v, size_t n); + +/* Acquire/release ownership of AES H/W */ +/* NOTE: If "acquire" succeeds, "release" must be done to pair it. */ +bool crypto_aes_acquire(void); +void crypto_aes_release(void); + +/* Acquire/release ownership of DES H/W */ +/* NOTE: If "acquire" succeeds, "release" must be done to pair it. */ +bool crypto_des_acquire(void); +void crypto_des_release(void); + +/* Acquire/release ownership of SHA H/W */ +/* NOTE: If "acquire" succeeds, "release" must be done to pair it. */ +bool crypto_sha_acquire(void); +void crypto_sha_release(void); + +/* Acquire/release ownership of ECC H/W */ +/* NOTE: If "acquire" succeeds, "release" must be done to pair it. */ +bool crypto_ecc_acquire(void); +void crypto_ecc_release(void); + +/* Flow control between crypto/xxx start and crypto/xxx ISR + * + * crypto_xxx_prestart/crypto_xxx_wait encapsulate control flow between crypto/xxx start and crypto/xxx ISR. + * + * crypto_xxx_prestart will also address synchronization issue with memory barrier instruction. + * + * On finish, return of crypto_xxx_wait indicates success or not: + * true if successful + * false if failed + * + * Example: Start AES H/W and wait for its finish + * crypto_aes_prestart(); + * AES_Start(); + * crypto_aes_wait(); + */ +void crypto_prng_prestart(void); +bool crypto_prng_wait(void); +void crypto_aes_prestart(void); +bool crypto_aes_wait(void); +void crypto_des_prestart(void); +bool crypto_des_wait(void); +void crypto_ecc_prestart(void); +bool crypto_ecc_wait(void); + + +/* Check if buffer can be used for crypto DMA. It has the following requirements: + * (1) Word-aligned buffer base address + * (2) Crypto submodule (AES, DES, SHA, etc.) dependent buffer size alignment. Must be 2 power. + * (3) Located in 0x20000000-0x2FFFFFFF region + */ +bool crypto_dma_buff_compat(const void *buff, size_t buff_size, size_t size_aligned_to); + +/* Check if input/output buffers are overlapped */ +bool crypto_dma_buffs_overlap(const void *in_buff, size_t in_buff_size, const void *out_buff, size_t out_buff_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/device/partition_M2351.h b/targets/TARGET_NUVOTON/TARGET_M2351/device/partition_M2351.h index ae9d0b1711..ba01430926 100644 --- a/targets/TARGET_NUVOTON/TARGET_M2351/device/partition_M2351.h +++ b/targets/TARGET_NUVOTON/TARGET_M2351/device/partition_M2351.h @@ -164,7 +164,7 @@ __STATIC_INLINE void FMC_NSBA_Setup(void) // CRC <0=> Secure <1=> Non-Secure // CRPT <0=> Secure <1=> Non-Secure */ -#define SCU_INIT_PNSSET1_VAL 0xFFFBFFFF +#define SCU_INIT_PNSSET1_VAL 0xFFFFFFFF /* PNSSET2 */ @@ -718,7 +718,7 @@ __STATIC_INLINE void SCU_Setup(void) // */ -#define NVIC_INIT_ITNS2_VAL 0xFFFFFF7F +#define NVIC_INIT_ITNS2_VAL 0xFFFFFFFF /* diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/trng_api.c b/targets/TARGET_NUVOTON/TARGET_M2351/trng_api.c new file mode 100644 index 0000000000..ca40f3db27 --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M2351/trng_api.c @@ -0,0 +1,89 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if DEVICE_TRNG + +#include +#include +#include "cmsis.h" +#include "us_ticker_api.h" +#include "trng_api.h" +#include "crypto-misc.h" + + +/* + * Get Random number generator. + */ + +#define PRNG_KEY_SIZE (0x20UL) + +static void trng_get(unsigned char *pConversionData) +{ + uint32_t *p32ConversionData; + + p32ConversionData = (uint32_t *)pConversionData; + + PRNG_Open(CRYPTO_MODBASE(), PRNG_KEY_SIZE_256, 1, us_ticker_read()); + crypto_prng_prestart(); + PRNG_Start(CRYPTO_MODBASE()); + crypto_prng_wait(); + + PRNG_Read(CRYPTO_MODBASE(), p32ConversionData); +} + +void trng_init(trng_t *obj) +{ + (void)obj; + + /* Init crypto module */ + crypto_init(); + + PRNG_ENABLE_INT(CRYPTO_MODBASE()); +} + +void trng_free(trng_t *obj) +{ + (void)obj; + + PRNG_DISABLE_INT(CRYPTO_MODBASE()); + + /* Uninit crypto module */ + crypto_uninit(); +} + +int trng_get_bytes(trng_t *obj, uint8_t *output, size_t length, size_t *output_length) +{ + (void)obj; + unsigned char tmpBuff[PRNG_KEY_SIZE]; + size_t cur_length = 0; + + while (length >= sizeof(tmpBuff)) { + trng_get(output); + output += sizeof(tmpBuff); + cur_length += sizeof(tmpBuff); + length -= sizeof(tmpBuff); + } + if (length > 0) { + trng_get(tmpBuff); + memcpy(output, tmpBuff, length); + cur_length += length; + crypto_zeroize(tmpBuff, sizeof(tmpBuff)); + } + *output_length = cur_length; + return 0; +} + +#endif diff --git a/targets/targets.json b/targets/targets.json index c73ff51f93..84abbe663c 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -4298,7 +4298,7 @@ "is_disk_virtual": true, "supported_toolchains": ["GCC_ARM", "IAR", "ARMC6"], "inherits": ["Target"], - "device_has": ["I2C", "I2CSLAVE", "I2C_ASYNCH", "LOWPOWERTIMER", "RTC", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "STDIO_MESSAGES", "SLEEP", "SPI", "SPISLAVE", "SPI_ASYNCH", "FLASH"], + "device_has": ["I2C", "I2CSLAVE", "I2C_ASYNCH", "LOWPOWERTIMER", "RTC", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", "STDIO_MESSAGES", "SLEEP", "SPI", "SPISLAVE", "SPI_ASYNCH", "TRNG", "FLASH"], "detect_code": ["1305"], "release_versions": ["5"], "device_name": "M2351KIAAEES", @@ -4306,7 +4306,8 @@ }, "NUMAKER_PFM_M2351_S": { "core": "Cortex-M23", - "inherits": ["NUMAKER_PFM_M2351"] + "inherits": ["NUMAKER_PFM_M2351"], + "device_has_remove": ["TRNG"] }, "NUMAKER_PFM_M2351_NS": { "core": "Cortex-M23-NS",