diff --git a/extmod/extmod.mk b/extmod/extmod.mk index c132fd89ce887..514197d6b9a88 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -42,6 +42,7 @@ SRC_EXTMOD_C += \ extmod/modsocket.c \ extmod/modtls_axtls.c \ extmod/modtls_mbedtls.c \ + extmod/mbedtls/mbedtls_alt.c \ extmod/modtime.c \ extmod/moductypes.c \ extmod/modvfs.c \ @@ -242,6 +243,10 @@ MBEDTLS_CONFIG_FILE ?= \"mbedtls/mbedtls_config_port.h\" GIT_SUBMODULES += $(MBEDTLS_DIR) CFLAGS_EXTMOD += -DMBEDTLS_CONFIG_FILE=$(MBEDTLS_CONFIG_FILE) CFLAGS_EXTMOD += -DMICROPY_SSL_MBEDTLS=1 -I$(TOP)/$(MBEDTLS_DIR)/include +ifeq ($(MICROPY_PY_SSL_ECDSA_SIGN_ALT),1) +CFLAGS_EXTMOD += -DMICROPY_PY_SSL_ECDSA_SIGN_ALT=1 +LDFLAGS_EXTMOD += -Wl,--wrap=mbedtls_ecdsa_write_signature +endif SRC_THIRDPARTY_C += lib/mbedtls_errors/mp_mbedtls_errors.c SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\ aes.c \ diff --git a/extmod/mbedtls/mbedtls_alt.c b/extmod/mbedtls/mbedtls_alt.c new file mode 100644 index 0000000000000..ccfb373488709 --- /dev/null +++ b/extmod/mbedtls/mbedtls_alt.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright The Mbed TLS Contributors + * Copyright (c) 2024 Damien P. George + * + * This file provides default fallback functions for use with alternate + * cryptography functions implemented in Python. + */ +#if MICROPY_PY_SSL_ECDSA_SIGN_ALT +#if defined(MBEDTLS_ECP_RESTARTABLE) || defined(MBEDTLS_ECDSA_DETERMINISTIC) +#error "MICROPY_PY_SSL_ECDSA_SIGN_ALT cannot be used with MBEDTLS_ECP_RESTARTABLE or MBEDTLS_ECDSA_DETERMINISTIC" +#endif + +#include +#define MBEDTLS_ALLOW_PRIVATE_ACCESS +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/error.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/asn1write.h" + +extern int micropy_mbedtls_ecdsa_sign_alt(const mbedtls_mpi *d, const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen); + + +// Compute and write signature +// See lib/mbedtls/library/ecdsa.c:688 +// +// Note: To avoid duplicating a lot of code, MBEDTLS_ECDSA_SIGN_ALT is not defined, +// which allows the default mbedtls_ecdsa_sign to be used as a fallback function. +// However, mbedtls_ecdsa_sign cannot be wrapped because it is called internally +// within its object file, so we wrap mbedtls_ecdsa_read/write_signature instead. +int __wrap_mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) { + + (void)md_alg; + + if (f_rng == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + // Check if curve is supported for ECDSA. + if (!mbedtls_ecdsa_can_do(ctx->grp.id) || ctx->grp.N.p == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + // Try signing with the alternative function first. + int ret = micropy_mbedtls_ecdsa_sign_alt(&ctx->d, hash, hlen, sig, sig_size, slen); + + // Fallback to the default mbedtls implementation if needed. + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED) { + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + size_t len = 0; + unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = { 0 }; + unsigned char *p = buf + sizeof(buf); + + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ctx->grp, &r, &s, &ctx->d, hash, hlen, f_rng, p_rng)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_mpi(&p, buf, &s)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_mpi(&p, buf, &r)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + if (len > sig_size) { + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } else { + ret = 0; + *slen = len; + memcpy(sig, p, len); + } + + cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + } + + return ret; +} +#endif diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 30118e200dee0..0a1b8828af5ca 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -53,6 +53,10 @@ #else #include "mbedtls/version.h" #endif +#if MICROPY_PY_SSL_ECDSA_SIGN_ALT +#include "mbedtls/ecdsa.h" +#include "mbedtls/asn1.h" +#endif #define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) @@ -68,6 +72,9 @@ typedef struct _mp_obj_ssl_context_t { int authmode; int *ciphersuites; mp_obj_t handler; + #if MICROPY_PY_SSL_ECDSA_SIGN_ALT + mp_obj_t ecdsa_sign_callback; + #endif } mp_obj_ssl_context_t; // This corresponds to an SSLSocket object. @@ -166,6 +173,13 @@ static NORETURN void mbedtls_raise_error(int err) { #endif } +// Stores the current SSLContext for use in mbedtls callbacks where the current state is not passed. +static inline void store_active_context(mp_obj_ssl_context_t *ssl_context) { + #if MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT + MP_STATE_THREAD(tls_ssl_context) = ssl_context; + #endif +} + static void ssl_check_async_handshake_failure(mp_obj_ssl_socket_t *sslsock, int *errcode) { if ( #if MBEDTLS_VERSION_NUMBER >= 0x03000000 @@ -241,6 +255,9 @@ static mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args mbedtls_pk_init(&self->pkey); self->ciphersuites = NULL; self->handler = mp_const_none; + #if MICROPY_PY_SSL_ECDSA_SIGN_ALT + self->ecdsa_sign_callback = mp_const_none; + #endif #ifdef MBEDTLS_DEBUG_C // Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose @@ -288,6 +305,10 @@ static void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_NEW_SMALL_INT(self->authmode); } else if (attr == MP_QSTR_verify_callback) { dest[0] = self->handler; + #if MICROPY_PY_SSL_ECDSA_SIGN_ALT + } else if (attr == MP_QSTR_ecdsa_sign_callback) { + dest[0] = self->ecdsa_sign_callback; + #endif } else { // Continue lookup in locals_dict. dest[1] = MP_OBJ_SENTINEL; @@ -298,6 +319,11 @@ static void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { self->authmode = mp_obj_get_int(dest[1]); dest[0] = MP_OBJ_NULL; mbedtls_ssl_conf_authmode(&self->conf, self->authmode); + #if MICROPY_PY_SSL_ECDSA_SIGN_ALT + } else if (attr == MP_QSTR_ecdsa_sign_callback) { + dest[0] = MP_OBJ_NULL; + self->ecdsa_sign_callback = dest[1]; + #endif } else if (attr == MP_QSTR_verify_callback) { dest[0] = MP_OBJ_NULL; self->handler = dest[1]; @@ -497,6 +523,9 @@ static int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t sock, bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname) { + // Store the current SSL context. + store_active_context(ssl_context); + // Verify the socket object has the full stream protocol mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); @@ -602,6 +631,9 @@ static mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc return MP_STREAM_ERROR; } + // Store the current SSL context. + store_active_context(o->ssl_context); + int ret = mbedtls_ssl_read(&o->ssl, buf, size); if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { // end of stream @@ -643,6 +675,9 @@ static mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in return MP_STREAM_ERROR; } + // Store the current SSL context. + store_active_context(o->ssl_context); + int ret = mbedtls_ssl_write(&o->ssl, buf, size); if (ret >= 0) { return ret; @@ -680,6 +715,9 @@ static mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i mp_obj_t sock = self->sock; if (request == MP_STREAM_CLOSE) { + // Clear the SSL context. + store_active_context(NULL); + if (sock == MP_OBJ_NULL) { // Already closed socket, do nothing. return 0; @@ -767,6 +805,57 @@ static MP_DEFINE_CONST_OBJ_TYPE( /******************************************************************************/ // ssl module. +#if MICROPY_PY_SSL_ECDSA_SIGN_ALT +int micropy_mbedtls_ecdsa_sign_alt(const mbedtls_mpi *d, const unsigned char *hash, size_t hlen, unsigned char *sig, size_t sig_size, size_t *slen) { + uint8_t key[256]; + + // Check if the current context has an alternative sign function. + mp_obj_ssl_context_t *ssl_ctx = MP_STATE_THREAD(tls_ssl_context); + if (ssl_ctx == NULL || ssl_ctx->ecdsa_sign_callback == mp_const_none) { + return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; + } + + size_t klen = mbedtls_mpi_size(d); + if (klen > sizeof(key)) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + // Convert the MPI private key (d) to a binary array + if (mbedtls_mpi_write_binary(d, key, klen) != 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + nlr_buf_t nlr; + mp_buffer_info_t sig_buf; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = mp_call_function_2(ssl_ctx->ecdsa_sign_callback, + mp_obj_new_bytearray_by_ref(klen, (void *)key), + mp_obj_new_bytearray_by_ref(hlen, (void *)hash)); + if (ret == mp_const_none) { + // key couldn't be used by the alternative implementation. + nlr_pop(); + return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; + } + mp_get_buffer_raise(ret, &sig_buf, MP_BUFFER_READ); + nlr_pop(); + } else { + // The alternative implementation failed to sign. + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + // Check if the buffer fits. + if (sig_buf.len > sig_size) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + // Copy ASN.1 signature to buffer. + *slen = sig_buf.len; + memcpy(sig, sig_buf.buf, sig_buf.len); + return 0; +} +#endif + static const mp_rom_map_elem_t mp_module_tls_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tls) }, diff --git a/lib/arduino-lib b/lib/arduino-lib index 0f0993582f7f8..8312406d6cd1a 160000 --- a/lib/arduino-lib +++ b/lib/arduino-lib @@ -1 +1 @@ -Subproject commit 0f0993582f7f882f06371d21cd15748932795272 +Subproject commit 8312406d6cd1a19683500f153b33fba7a8414127 diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/manifest.py b/ports/stm32/boards/ARDUINO_NICLA_VISION/manifest.py index b7b9a2b470d02..10958c3a3971d 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/manifest.py +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/manifest.py @@ -16,3 +16,6 @@ # RPC require("msgpackrpc", library="arduino-lib") + +# SE05x driver +require("se05x", library="arduino-lib") diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk index 0e8ff0e82d388..75096ccc90941 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk @@ -22,6 +22,7 @@ MICROPY_BLUETOOTH_BTSTACK = 0 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_SSL = 1 +MICROPY_PY_SSL_ECDSA_SIGN_ALT=1 MICROPY_SSL_MBEDTLS = 1 MICROPY_PY_OPENAMP = 1 MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/ports/stm32/boards/ARDUINO_OPTA/manifest.py b/ports/stm32/boards/ARDUINO_OPTA/manifest.py index b7b9a2b470d02..375f4c3333b0a 100644 --- a/ports/stm32/boards/ARDUINO_OPTA/manifest.py +++ b/ports/stm32/boards/ARDUINO_OPTA/manifest.py @@ -14,5 +14,8 @@ # Register external library add_library("arduino-lib", "$(ARDUINO_LIB_DIR)") +# Opta modules. +require("opta", library="arduino-lib") + # RPC require("msgpackrpc", library="arduino-lib") diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/manifest.py b/ports/stm32/boards/ARDUINO_PORTENTA_H7/manifest.py index eb53df0fbdeb2..3eeba76459864 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/manifest.py +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/manifest.py @@ -15,5 +15,10 @@ add_library("arduino-lib", "$(ARDUINO_LIB_DIR)") # RPC -require("cmwx1", library="arduino-lib") require("msgpackrpc", library="arduino-lib") + +# Lora/CMWX1 driver +require("cmwx1", library="arduino-lib") + +# SE05x driver +require("se05x", library="arduino-lib") diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk index 3b173b3acdb92..50c6706dc9e17 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk @@ -22,6 +22,7 @@ MICROPY_BLUETOOTH_BTSTACK = 0 MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_SSL = 1 +MICROPY_PY_SSL_ECDSA_SIGN_ALT=1 MICROPY_SSL_MBEDTLS = 1 MICROPY_PY_OPENAMP = 1 MICROPY_PY_OPENAMP_REMOTEPROC = 1 diff --git a/py/mpconfig.h b/py/mpconfig.h index 34eafa9e5debb..7aa22d52e06c7 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1814,6 +1814,11 @@ typedef double mp_float_t; #define MICROPY_PY_SSL_FINALISER (MICROPY_ENABLE_FINALISER) #endif +// Whether to add a root pointer for the current ssl object +#ifndef MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT +#define MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT (MICROPY_PY_SSL_ECDSA_SIGN_ALT) +#endif + // Whether to provide the "vfs" module #ifndef MICROPY_PY_VFS #define MICROPY_PY_VFS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES && MICROPY_VFS) diff --git a/py/mpstate.h b/py/mpstate.h index af55e764f6cdc..54eca596daa28 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -293,6 +293,10 @@ typedef struct _mp_state_thread_t { bool prof_callback_is_executing; struct _mp_code_state_t *current_code_state; #endif + + #if MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT + struct _mp_obj_ssl_context_t *tls_ssl_context; + #endif } mp_state_thread_t; // This structure combines the above 3 structures.