diff --git a/CHANGELOG.md b/CHANGELOG.md index 7152f01ad..2897e0ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Crypto functions on generic_unix platform now rely on MbedTLS instead of OpenSSL - Platform function providing time used by timers was changed from `sys_monotonic_millis` to `sys_monotonic_time_u64`, `sys_monotonic_time_u64_to_ms` and `sys_monotonic_time_ms_to_u64`. - Implement `atomvm:random/0` and `atomvm:rand_bytes/1` on top of `crypto:strong_rand_bytes/1` on - generic_unix platform + generic_unix, ESP32 and RP2040 platforms. ### Added @@ -41,7 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for `crypto:one_time/4,5` on Unix and Pico as well as for `crypto:hash/2` on Pico - Added ability to configure STM32 Nucleo boards onboard UART->USB-COM using the `-DBOARD=nucleo` cmake option - Added STM32 cmake option `-DAVM_CFG_CONSOLE=` to select a different uart peripheral for the system console -- Added `crypto:strong_rand_bytes/1` using Mbed-TLS +- Added `crypto:strong_rand_bytes/1` using Mbed-TLS (only on generic_unix, ESP32 and RP2040 + platforms) ### Removed diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index 448eefdcc..f773f8324 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -575,35 +576,23 @@ term nif_crypto_strong_rand_bytes(Context *ctx, int argc, term argv[]) RAISE_ERROR(BADARG_ATOM); } - static bool initialized = false; - static mbedtls_entropy_context entropy_ctx; - static mbedtls_ctr_drbg_context rnd_ctx; - - if (!initialized) { - mbedtls_entropy_init(&entropy_ctx); - - mbedtls_ctr_drbg_init(&rnd_ctx); - - const char *seed = "AtomVM Mbed-TLS initial seed."; - int seed_len = strlen(seed); - int seed_err = mbedtls_ctr_drbg_seed( - &rnd_ctx, mbedtls_entropy_func, &entropy_ctx, (const unsigned char *) seed, seed_len); - if (UNLIKELY(seed_err != 0)) { - abort(); - } - } - int ensure_size = term_binary_heap_size(out_len); if (UNLIKELY(memory_ensure_free(ctx, ensure_size) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } + mbedtls_ctr_drbg_context *rnd_ctx = sys_mbedtls_get_ctr_drbg_context_lock(ctx->global); + if (IS_NULL_PTR(rnd_ctx)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Failed CTR_DRBG init", ctx)); + } + term out_bin = term_create_uninitialized_binary(out_len, &ctx->heap, ctx->global); unsigned char *out = (unsigned char *) term_binary_data(out_bin); - int err = mbedtls_ctr_drbg_random(&rnd_ctx, out, out_len); - if (err != 0) { - abort(); + int err = mbedtls_ctr_drbg_random(rnd_ctx, out, out_len); + sys_mbedtls_ctr_drbg_context_unlock(ctx->global); + if (UNLIKELY(err != 0)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Failed random", ctx)); } return out_bin; diff --git a/src/libAtomVM/sys_mbedtls.h b/src/libAtomVM/sys_mbedtls.h new file mode 100644 index 000000000..bb016db71 --- /dev/null +++ b/src/libAtomVM/sys_mbedtls.h @@ -0,0 +1,38 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2023 Davide Bettio + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _SYS_MBEDTLS_H_ +#define _SYS_MBEDTLS_H_ + +#include +#include + +mbedtls_entropy_context *sys_mbedtls_get_entropy_context_lock(GlobalContext *global); +void sys_mbedtls_entropy_context_unlock(GlobalContext *global); +int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size); + +mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *global); + +/** + * @warning do not call this function when already owning the entropy context + */ +void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global); + +#endif diff --git a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h index cdca93f7c..0afb8d371 100644 --- a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h +++ b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h @@ -30,9 +30,17 @@ #include #endif +#include +#include + #include +#include #include +#ifndef AVM_NO_SMP +#include "smp.h" +#endif + #include "sys.h" #define REGISTER_PORT_DRIVER(NAME, INIT_CB, DESTROY_CB, CREATE_CB) \ @@ -94,6 +102,18 @@ struct ESP32PlatformData EventListener *socket_listener; struct SyncList sockets; struct ListHead ready_connections; + +#ifndef AVM_NO_SMP + Mutex *entropy_mutex; +#endif + mbedtls_entropy_context entropy_ctx; + bool entropy_is_initialized; + +#ifndef AVM_NO_SMP + Mutex *random_mutex; +#endif + mbedtls_ctr_drbg_context random_ctx; + bool random_is_initialized; }; typedef void (*port_driver_init_t)(GlobalContext *global); diff --git a/src/platforms/esp32/components/avm_sys/sys.c b/src/platforms/esp32/components/avm_sys/sys.c index 965d2d1b4..601f709db 100644 --- a/src/platforms/esp32/components/avm_sys/sys.c +++ b/src/platforms/esp32/components/avm_sys/sys.c @@ -55,6 +55,12 @@ #include "soc/soc_caps.h" #endif +#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#include +#else +#include +#endif + // Platform uses listeners #include "listeners.h" @@ -62,6 +68,14 @@ #define TAG "sys" +#ifndef AVM_NO_SMP +#define SMP_MUTEX_LOCK(mtx) smp_mutex_lock(mtx) +#define SMP_MUTEX_UNLOCK(mtx) smp_mutex_unlock(mtx) +#else +#define SMP_MUTEX_LOCK(mtx) +#define SMP_MUTEX_UNLOCK(mtx) +#endif + static Context *port_driver_create_port(const char *port_name, GlobalContext *global, term opts); static void *select_thread_loop(void *); @@ -241,6 +255,9 @@ void sys_init_platform(GlobalContext *glb) AVM_ABORT(); } #endif + + platform->entropy_is_initialized = false; + platform->random_is_initialized = false; } void sys_free_platform(GlobalContext *glb) @@ -256,6 +273,15 @@ void sys_free_platform(GlobalContext *glb) AVM_ABORT(); } } + + if (platform->random_is_initialized) { + mbedtls_ctr_drbg_free(&platform->random_ctx); + } + + if (platform->entropy_is_initialized) { + mbedtls_entropy_free(&platform->entropy_ctx); + } + free(platform); } @@ -759,3 +785,76 @@ term esp_err_to_term(GlobalContext *glb, esp_err_t status) return term_from_int(status); } } + +int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) +{ +#ifndef MBEDTLS_THREADING_C + struct ESP32PlatformData *platform + = CONTAINER_OF(entropy, struct ESP32PlatformData, entropy_ctx); + SMP_MUTEX_LOCK(platform->entropy_mutex); + int result = mbedtls_entropy_func(entropy, buf, size); + SMP_MUTEX_UNLOCK(platform->entropy_mutex); + + return result; +#else + return mbedtls_entropy_func(entropy, buf, size); +#endif +} + +mbedtls_entropy_context *sys_mbedtls_get_entropy_context_lock(GlobalContext *global) +{ + struct ESP32PlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->entropy_mutex); + + if (!platform->entropy_is_initialized) { + // mbedtls_entropy_init will gather entropy for us using esp_fill_random() + // however it will be pseudorandom when wifi/bluetooth/RF is not enabled. + // + // TODO: when radio is not active bootloader_random_enable() must be enabled for gathering + // entropy. However, "enable function is not safe to use if any other subsystem is + // accessing the RF subsystem or the ADC at the same time". + mbedtls_entropy_init(&platform->entropy_ctx); + platform->entropy_is_initialized = true; + } + + return &platform->entropy_ctx; +} + +void sys_mbedtls_entropy_context_unlock(GlobalContext *global) +{ + struct ESP32PlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->entropy_mutex); +} + +mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *global) +{ + struct ESP32PlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->random_mutex); + + if (!platform->random_is_initialized) { + mbedtls_ctr_drbg_init(&platform->random_ctx); + + mbedtls_entropy_context *entropy_ctx = sys_mbedtls_get_entropy_context_lock(global); + // Safe to unlock it now, sys_mbedtls_entropy_func will lock it again later + sys_mbedtls_entropy_context_unlock(global); + + const char *seed = "AtomVM ESP32 Mbed-TLS initial seed."; + int seed_len = strlen(seed); + int seed_err = mbedtls_ctr_drbg_seed(&platform->random_ctx, sys_mbedtls_entropy_func, + entropy_ctx, (const unsigned char *) seed, seed_len); + if (UNLIKELY(seed_err != 0)) { + abort(); + } + platform->random_is_initialized = true; + } + + return &platform->random_ctx; +} + +void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) +{ + struct ESP32PlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->random_mutex); +} diff --git a/src/platforms/generic_unix/lib/sys.c b/src/platforms/generic_unix/lib/sys.c index 3606b816d..bb0b9ba4f 100644 --- a/src/platforms/generic_unix/lib/sys.c +++ b/src/platforms/generic_unix/lib/sys.c @@ -32,7 +32,17 @@ #include "utils.h" #if ATOMVM_HAS_MBEDTLS +#include +#include + +#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#include +#else +#include +#endif + #include "otp_ssl.h" +#include "sys_mbedtls.h" #endif #include @@ -98,6 +108,20 @@ struct GenericUnixPlatformData #endif #endif #endif + +#ifdef ATOMVM_HAS_MBEDTLS +#ifndef AVM_NO_SMP + Mutex *entropy_mutex; +#endif + mbedtls_entropy_context entropy_ctx; + bool entropy_is_initialized; + +#ifndef AVM_NO_SMP + Mutex *random_mutex; +#endif + mbedtls_ctr_drbg_context random_ctx; + bool random_is_initialized; +#endif }; static void mapped_file_avm_pack_destructor(struct AVMPackData *obj, GlobalContext *global); @@ -590,6 +614,18 @@ void sys_init_platform(GlobalContext *global) otp_net_init(global); otp_socket_init(global); #if ATOMVM_HAS_MBEDTLS +#ifndef AVM_NO_SMP + platform->entropy_mutex = smp_mutex_create(); + if (IS_NULL_PTR(platform->entropy_mutex)) { + AVM_ABORT(); + } + platform->random_mutex = smp_mutex_create(); + if (IS_NULL_PTR(platform->random_mutex)) { + AVM_ABORT(); + } +#endif + platform->entropy_is_initialized = false; + platform->random_is_initialized = false; otp_ssl_init(global); #endif @@ -618,6 +654,22 @@ void sys_free_platform(GlobalContext *global) #endif #endif #endif + +#if !defined(AVM_NO_SMP) && ATOMVM_HAS_MBEDTLS + smp_mutex_destroy(platform->entropy_mutex); + smp_mutex_destroy(platform->random_mutex); +#endif + +#if ATOMVM_HAS_MBEDTLS + if (platform->random_is_initialized) { + mbedtls_ctr_drbg_free(&platform->random_ctx); + } + + if (platform->entropy_is_initialized) { + mbedtls_entropy_free(&platform->entropy_ctx); + } +#endif + free(platform); } @@ -723,3 +775,73 @@ bool event_listener_is_event(EventListener *listener, listener_event_t event) { return listener->fd == event; } + +#ifdef ATOMVM_HAS_MBEDTLS +int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) +{ +#ifndef MBEDTLS_THREADING_C + struct GenericUnixPlatformData *platform + = CONTAINER_OF(entropy, struct GenericUnixPlatformData, entropy_ctx); + SMP_MUTEX_LOCK(platform->entropy_mutex); + int result = mbedtls_entropy_func(entropy, buf, size); + SMP_MUTEX_UNLOCK(platform->entropy_mutex); + + return result; +#else + return mbedtls_entropy_func(entropy, buf, size); +#endif +} + +mbedtls_entropy_context *sys_mbedtls_get_entropy_context_lock(GlobalContext *global) +{ + struct GenericUnixPlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->entropy_mutex); + + if (!platform->entropy_is_initialized) { + mbedtls_entropy_init(&platform->entropy_ctx); + platform->entropy_is_initialized = true; + } + + return &platform->entropy_ctx; +} + +void sys_mbedtls_entropy_context_unlock(GlobalContext *global) +{ + struct GenericUnixPlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->entropy_mutex); +} + +mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *global) +{ + struct GenericUnixPlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->random_mutex); + + if (!platform->random_is_initialized) { + mbedtls_ctr_drbg_init(&platform->random_ctx); + + mbedtls_entropy_context *entropy_ctx = sys_mbedtls_get_entropy_context_lock(global); + // Safe to unlock it now, sys_mbedtls_entropy_func will lock it again later + sys_mbedtls_entropy_context_unlock(global); + + const char *seed = "AtomVM Mbed-TLS initial seed."; + int seed_len = strlen(seed); + int seed_err = mbedtls_ctr_drbg_seed(&platform->random_ctx, sys_mbedtls_entropy_func, + entropy_ctx, (const unsigned char *) seed, seed_len); + if (UNLIKELY(seed_err != 0)) { + abort(); + } + platform->random_is_initialized = true; + } + + return &platform->random_ctx; +} + +void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) +{ + struct GenericUnixPlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->random_mutex); +} + +#endif diff --git a/src/platforms/rp2040/src/lib/rp2040_sys.h b/src/platforms/rp2040/src/lib/rp2040_sys.h index 58917ce72..0b3b74c80 100644 --- a/src/platforms/rp2040/src/lib/rp2040_sys.h +++ b/src/platforms/rp2040/src/lib/rp2040_sys.h @@ -30,6 +30,9 @@ #include #include +#include +#include + #pragma GCC diagnostic pop #include "sys.h" @@ -112,6 +115,18 @@ struct RP2040PlatformData cond_t event_poll_cond; #endif queue_t event_queue; + +#ifndef AVM_NO_SMP + Mutex *entropy_mutex; +#endif + mbedtls_entropy_context entropy_ctx; + bool entropy_is_initialized; + +#ifndef AVM_NO_SMP + Mutex *random_mutex; +#endif + mbedtls_ctr_drbg_context random_ctx; + bool random_is_initialized; }; typedef void (*port_driver_init_t)(GlobalContext *global); diff --git a/src/platforms/rp2040/src/lib/sys.c b/src/platforms/rp2040/src/lib/sys.c index f2702923d..bbc7ee3dd 100644 --- a/src/platforms/rp2040/src/lib/sys.c +++ b/src/platforms/rp2040/src/lib/sys.c @@ -44,6 +44,12 @@ #include #endif +#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#include +#else +#include +#endif + // libAtomVM #include #include @@ -57,6 +63,14 @@ // Platform uses listeners #include "listeners.h" +#ifndef AVM_NO_SMP +#define SMP_MUTEX_LOCK(mtx) smp_mutex_lock(mtx) +#define SMP_MUTEX_UNLOCK(mtx) smp_mutex_unlock(mtx) +#else +#define SMP_MUTEX_LOCK(mtx) +#define SMP_MUTEX_UNLOCK(mtx) +#endif + struct PortDriverDefListItem *port_driver_list; struct NifCollectionDefListItem *nif_collection_list; @@ -75,6 +89,9 @@ void sys_init_platform(GlobalContext *glb) cyw43_arch_init(); otp_socket_init(glb); #endif + + platform->entropy_is_initialized = false; + platform->random_is_initialized = false; } void sys_free_platform(GlobalContext *glb) @@ -85,6 +102,15 @@ void sys_free_platform(GlobalContext *glb) struct RP2040PlatformData *platform = glb->platform_data; queue_free(&platform->event_queue); + + if (platform->random_is_initialized) { + mbedtls_ctr_drbg_free(&platform->random_ctx); + } + + if (platform->entropy_is_initialized) { + mbedtls_entropy_free(&platform->entropy_ctx); + } + free(platform); #ifndef AVM_NO_SMP @@ -387,3 +413,72 @@ void sys_unregister_listener_from_event(GlobalContext *global, listener_event_t } synclist_unlock(&global->listeners); } + +// TODO: enable mbedtls threading support by defining MBEDTLS_THREADING_ALT +// and remove this function. +int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) +{ +#ifndef MBEDTLS_THREADING_C + struct RP2040PlatformData *platform + = CONTAINER_OF(entropy, struct RP2040PlatformData, entropy_ctx); + SMP_MUTEX_LOCK(platform->entropy_mutex); + int result = mbedtls_entropy_func(entropy, buf, size); + SMP_MUTEX_UNLOCK(platform->entropy_mutex); + + return result; +#else + return mbedtls_entropy_func(entropy, buf, size); +#endif +} + +mbedtls_entropy_context *sys_mbedtls_get_entropy_context_lock(GlobalContext *global) +{ + struct RP2040PlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->entropy_mutex); + + if (!platform->entropy_is_initialized) { + mbedtls_entropy_init(&platform->entropy_ctx); + platform->entropy_is_initialized = true; + } + + return &platform->entropy_ctx; +} + +void sys_mbedtls_entropy_context_unlock(GlobalContext *global) +{ + struct RP2040PlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->entropy_mutex); +} + +mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *global) +{ + struct RP2040PlatformData *platform = global->platform_data; + + SMP_MUTEX_LOCK(platform->random_mutex); + + if (!platform->random_is_initialized) { + mbedtls_ctr_drbg_init(&platform->random_ctx); + + mbedtls_entropy_context *entropy_ctx = sys_mbedtls_get_entropy_context_lock(global); + // Safe to unlock it now, sys_mbedtls_entropy_func will lock it again later + sys_mbedtls_entropy_context_unlock(global); + + const char *seed = "AtomVM RP2040 Mbed-TLS initial seed."; + int seed_len = strlen(seed); + int seed_err = mbedtls_ctr_drbg_seed(&platform->random_ctx, sys_mbedtls_entropy_func, + entropy_ctx, (const unsigned char *) seed, seed_len); + if (UNLIKELY(seed_err != 0)) { + abort(); + } + platform->random_is_initialized = true; + } + + return &platform->random_ctx; +} + +void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) +{ + struct RP2040PlatformData *platform = global->platform_data; + SMP_MUTEX_UNLOCK(platform->random_mutex); +}