NuMaker connection with AWS IoT thru MQTT/HTTPS (Mbed OS 6)
Dependencies: MQTT
targets/TARGET_NUVOTON/platform_entropy.cpp
- Committer:
- ccli8
- Date:
- 2020-03-27
- Revision:
- 41:b878d7cd7035
- Parent:
- 33:c3a985807206
- Child:
- 42:fa6f7f79a112
File content as of revision 41:b878d7cd7035:
/* * Copyright (c) 2019 Nuvoton Technology Corporation * * SPDX-License-Identifier: Apache-2.0 * * 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 && !TARGET_PSA #include "mbed.h" #include "mbedtls/config.h" #if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) /* Support entropy source with EADC seeded random on non-PSA targets without TRNG * * Follow the steps below to replace TRNG with EADC seeded random: * * 1. Seed random generator with EADC band gap * 2. Define MBEDTLS_ENTROPY_HARDWARE_ALT and provide custom mbedtls_hardware_poll(...) * * Reference configuration in mbed_app.json: * * For Pelion/mbedtls: * * "target.macros_add": [ * "MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\"", * "MBEDTLS_ENTROPY_HARDWARE_ALT" * ], * * For non-Pelion/mbedtls: * * "target.macros_add": [ * "MBEDTLS_ENTROPY_HARDWARE_ALT" * ], * * For both Pelion/non-Pelion (skip when done in targets.json): * * "target.device_has_remove": ["TRNG"], * * WARNING: If the security level of EADC seeded random generator cannot meet requirements, replace it with another entropy source. * * Select random generator between PRNG and S/W random: * * For targets with PRNG: * "crypto-prng-present": true, * For targets without PRNG (e.g. M482): * "crypto-prng-present": false, */ #if NU_CRYPTO_PRNG_PRESENT #include "crypto-misc.h" #else #include <stdlib.h> #endif extern "C" { int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen); } /* Support EADC band gap * * Mbed OS defines analog-in HAL for normal purposes, but EADC band gap is not defined. * To avoid EADC code conflict and fit into existent analog-in HAL, we: * * 1. Hijack AnalogIn driver to involve analog-in HAL protection and EADC initialization. * This needs one dedicated EADC pin EADC_AUX_PINNAME. * 2. Run EADC band gap conversion, with EADC module already initialized via above. This needs * one dedicated sample module and one dedicated channel. */ #if TARGET_NUC472 #define EADC_AUX_PINNAME A0 #define EADC_BANDGAP_SMPLMOD 7 #define EADC_BANDGAP_CHN 8 #if NU_CRYPTO_PRNG_PRESENT #define PRNG_KEYSIZE_ID PRNG_KEY_SIZE_128 #define PRNG_KEYSIZE 16 #endif #elif TARGET_M480 #define EADC_AUX_PINNAME A0 #define EADC_BANDGAP_SMPLMOD 16 #define EADC_BANDGAP_CHN 16 #if NU_CRYPTO_PRNG_PRESENT #define PRNG_KEYSIZE_ID PRNG_KEY_SIZE_128 #define PRNG_KEYSIZE 16 #endif #else #error("Target not support") #endif class NuBandGap : public mbed::AnalogIn { public: NuBandGap(); ~NuBandGap(); /* Generate bitstream based on EADC band gap * * @returns 1/0 bitstream */ uint16_t read_bitstream(); }; class NuEADCSeedRandom : private mbed::NonCopyable<NuEADCSeedRandom> { public: NuEADCSeedRandom(); ~NuEADCSeedRandom(); /* Get random data * * @param output The pointer to an output array * @param len The size of output data, to avoid buffer overwrite * @param olen The length of generated data */ int get_bytes(unsigned char *output, size_t len, size_t *olen); private: NuBandGap band_gap; }; int mbedtls_hardware_poll(MBED_UNUSED void *data, unsigned char *output, size_t len, size_t *olen) { static NuEADCSeedRandom eadc_seed_random; return eadc_seed_random.get_bytes(output, len, olen); } NuBandGap::NuBandGap() : mbed::AnalogIn(EADC_AUX_PINNAME) { EADC_T *eadc_base = (EADC_T *) EADC_BASE; EADC_ConfigSampleModule(eadc_base, EADC_BANDGAP_SMPLMOD, EADC_SOFTWARE_TRIGGER, EADC_BANDGAP_CHN); } NuBandGap::~NuBandGap() { } uint16_t NuBandGap::read_bitstream() { uint16_t one_or_zero; lock(); EADC_T *eadc_base = (EADC_T *) EADC_BASE; EADC_START_CONV(eadc_base, 1 << EADC_BANDGAP_SMPLMOD); while (EADC_GET_DATA_VALID_FLAG(eadc_base, 1 << EADC_BANDGAP_SMPLMOD) != (1 << EADC_BANDGAP_SMPLMOD)); uint16_t conv_res_12 = EADC_GET_CONV_DATA(eadc_base, EADC_BANDGAP_SMPLMOD); /* 1 as number of 'one' is odd; 0 otherwise */ unsigned i; uint16_t count_one = 0; for (i = 0; i < 12; i ++) { if (conv_res_12 & 1) { count_one ++; } conv_res_12 >>= 1; } one_or_zero = count_one & 1; unlock(); return one_or_zero; } NuEADCSeedRandom::NuEADCSeedRandom() { #if NU_CRYPTO_PRNG_PRESENT crypto_init(); #if TARGET_NUC472 || (MBED_MAJOR_VERSION < 6) PRNG_ENABLE_INT(); #else PRNG_ENABLE_INT(CRPT); #endif #endif uint32_t seed = 0; unsigned i = 32; /* Get seed from EADC band gap */ while (i --) { seed <<= 1; seed |= band_gap.read_bitstream(); } #if NU_CRYPTO_PRNG_PRESENT /* PRNG reload seed */ #if TARGET_NUC472 || (MBED_MAJOR_VERSION < 6) PRNG_Open(PRNG_KEYSIZE_ID, 1, seed); #else PRNG_Open(CRPT, PRNG_KEYSIZE_ID, 1, seed); #endif #else srand(seed); #endif /* #if NU_CRYPTO_PRNG_PRESENT */ } NuEADCSeedRandom::~NuEADCSeedRandom() { #if NU_CRYPTO_PRNG_PRESENT #if TARGET_NUC472 || (MBED_MAJOR_VERSION < 6) PRNG_DISABLE_INT(); #else PRNG_DISABLE_INT(CRPT); #endif crypto_uninit(); #endif /* #if NU_CRYPTO_PRNG_PRESENT */ } int NuEADCSeedRandom::get_bytes(unsigned char *output, size_t len, size_t *olen) { /* Check argument validity */ if (!output && len) { return -1; } #if NU_CRYPTO_PRNG_PRESENT unsigned char *output_ind = output; size_t rmn = len; uint32_t rand_data[PRNG_KEYSIZE / sizeof(uint32_t)]; while (rmn) { crypto_prng_prestart(); #if TARGET_NUC472 || (MBED_MAJOR_VERSION < 6) PRNG_Start(); #else PRNG_Start(CRPT); #endif crypto_prng_wait(); #if TARGET_NUC472 || (MBED_MAJOR_VERSION < 6) PRNG_Read(rand_data); #else PRNG_Read(CRPT, rand_data); #endif size_t n = (rmn >= PRNG_KEYSIZE) ? PRNG_KEYSIZE : rmn; memcpy(output_ind, rand_data, n); output_ind += n; rmn -= n; } #else unsigned char *output_ind = output; size_t rmn = len; while (rmn) { *output_ind ++ = rand() % 256; rmn --; } #endif /* #if NU_CRYPTO_PRNG_PRESENT */ if (olen) { *olen = len; } return 0; } #else /* Support entropy source with mbedtls NV seed on non-PSA targets without TRNG * * Follow the steps below to replace TRNG with mbedtls NV seed: * * 1. Define MBEDTLS_ENTROPY_NV_SEED * 2. Define MBEDTLS_PLATFORM_NV_SEED_READ_MACRO/MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO and provide custom mbedtls_nv_seed_read(...)/mbedtls_nv_seed_write(...). * 3. Don't define MBEDTLS_PSA_INJECT_ENTROPY. Meet mbedtls_psa_inject_entropy(...) undefined and then provide custom one, which must be compatible with mbedtls_nv_seed_read(...)/mbedtls_nv_seed_write(...) above. * 4. For development, simulating partial provision process, inject entropy seed via mbedtls_psa_inject_entropy(...) pre-main. * * Reference configuration in mbed_app.json: * * For Pelion/mbedtls, don't define MBEDTLS_ENTROPY_NV_SEED because it has defined in: * https://github.com/ARMmbed/mbed-cloud-client/blob/master/mbed-client-pal/Configs/mbedTLS/mbedTLSConfig_mbedOS_SW_TRNG.h * * "target.macros_add": [ * "MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS_SW_TRNG.h\"", * "PAL_USE_HW_TRNG=0", * "MBEDTLS_PLATFORM_NV_SEED_READ_MACRO=mbedtls_platform_seed_read", * "MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO=mbedtls_platform_seed_write" * ], * * For non-Pelion/mbedtls: * * "target.macros_add": [ * "MBEDTLS_ENTROPY_NV_SEED", * "MBEDTLS_PLATFORM_NV_SEED_READ_MACRO=mbedtls_platform_seed_read", * "MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO=mbedtls_platform_seed_write" * ], * * For both Pelion/non-Pelion (skip when done in targets.json): * * "target.device_has_remove": ["TRNG"], * * WARNING: The injection of mbedtls NV seed pre-main is only for development. Run provision process for mass production. */ #include "entropy_poll.h" #include "psa/crypto.h" #include "KVStore.h" #include "TDBStore.h" #include "KVMap.h" #include "kv_config.h" extern "C" { psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed, size_t seed_size); int mbedtls_platform_seed_read(unsigned char *buf, size_t buf_len); int mbedtls_platform_seed_write(unsigned char *buf, size_t buf_len); } /* Requirement of seed size * * 1. >= MBEDTLS_ENTROPY_MIN_PLATFORM * 2. >= MBEDTLS_ENTROPY_BLOCK_SIZE * 3. <= MBEDTLS_ENTROPY_MAX_SEED_SIZE */ #define SEED_SIZE 64 MBED_STATIC_ASSERT(SEED_SIZE >= MBEDTLS_ENTROPY_MIN_PLATFORM, "Seed size must be larger than or equal to MBEDTLS_ENTROPY_MIN_PLATFORM"); MBED_STATIC_ASSERT(SEED_SIZE >= MBEDTLS_ENTROPY_BLOCK_SIZE, "Seed size must be larger than or equal to MBEDTLS_ENTROPY_BLOCK_SIZE"); MBED_STATIC_ASSERT(SEED_SIZE <= MBEDTLS_ENTROPY_MAX_SEED_SIZE, "Seed size must be smaller than or equal to MBEDTLS_ENTROPY_MAX_SEED_SIZE"); /* Seed key name in kvstore */ #define KV_KEY_SEED "seed" /* Inject an initial entropy seed for the random generator into secure storage * * See reference below for its prototype: * https://github.com/ARMmbed/mbed-os/blob/master/features/mbedtls/mbed-crypto/inc/psa/crypto_extra.h */ psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed, size_t seed_size) { /* Check seed size requirement */ if ((( seed_size < MBEDTLS_ENTROPY_MIN_PLATFORM) || (seed_size < MBEDTLS_ENTROPY_BLOCK_SIZE)) || (seed_size > MBEDTLS_ENTROPY_MAX_SEED_SIZE)) { return PSA_ERROR_INVALID_ARGUMENT; } /* Get kvstore internal storage where seed is injected */ KVMap &kv_map = KVMap::get_instance(); KVStore *inner_store = kv_map.get_internal_kv_instance(NULL); if (inner_store == NULL) { return PSA_ERROR_STORAGE_FAILURE; } /* Check if seed has injected */ KVStore::info_t kv_info; int kv_status = inner_store->get_info(KV_KEY_SEED, &kv_info); if (kv_status == MBED_SUCCESS) { return PSA_ERROR_NOT_PERMITTED; } else if (kv_status == MBED_ERROR_ITEM_NOT_FOUND) { /* No seed injected, inject it below */ } else { return PSA_ERROR_STORAGE_FAILURE; } /* Inject seed into kvstore internal storage */ kv_status = inner_store->set(KV_KEY_SEED, seed, seed_size, 0); if (kv_status == MBED_SUCCESS) { return PSA_SUCCESS; } else { return PSA_ERROR_STORAGE_FAILURE; } } int mbedtls_platform_seed_read(unsigned char *buf, size_t buf_len) { /* Get kvstore internal storage where seed is injected */ KVMap &kv_map = KVMap::get_instance(); KVStore *inner_store = kv_map.get_internal_kv_instance(NULL); if (inner_store == NULL) { return -1; } /* Read seed from kvstore internal storage */ size_t actual_size = 0; int kv_status = inner_store->get(KV_KEY_SEED, buf, buf_len, &actual_size, 0); if (kv_status != MBED_SUCCESS || actual_size != buf_len) { return -1; } else { return buf_len; } } int mbedtls_platform_seed_write(unsigned char *buf, size_t buf_len) { /* Get kvstore internal storage where seed is injected */ KVMap &kv_map = KVMap::get_instance(); KVStore *inner_store = kv_map.get_internal_kv_instance(NULL); if (inner_store == NULL) { return -1; } /* Write seed into kvstore internal storage */ int kv_status = inner_store->set(KV_KEY_SEED, buf, buf_len, 0); if (kv_status != MBED_SUCCESS) { return -1; } else { return buf_len; } } #endif /* #if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) */ #endif /* !DEVICE_TRNG && !TARGET_PSA */