From 2644f577f1562a641c62d223dfb1fd80dd541ac9 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 16 Oct 2024 14:08:43 +0200 Subject: [PATCH 1/6] extmod/modtls_mbedtls: Add a thread-global ptr for current SSL context. This is necessary for mbedTLS callbacks that do not carry any user state, so those callbacks can be customised per SSL context. Signed-off-by: iabdalkader --- extmod/modtls_mbedtls.c | 19 +++++++++++++++++++ py/mpconfig.h | 5 +++++ py/mpstate.h | 4 ++++ 3 files changed, 28 insertions(+) diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c index 30118e200dee0..b261e7a70cc86 100644 --- a/extmod/modtls_mbedtls.c +++ b/extmod/modtls_mbedtls.c @@ -166,6 +166,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 @@ -497,6 +504,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 +612,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 +656,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 +696,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; 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. From 68f1c2014514191415cda46f7c5264dce4799b9b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 24 Sep 2024 09:37:59 +0200 Subject: [PATCH 2/6] extmod/modtls_mbedtls: Support alternate sign callbacks in Python. This commit enables the implementation of alternative mbedTLS cryptography functions, such as ECDSA sign and verify, in pure Python. Alternative functions are implemented in Python callbacks, that get invoked from wrapper functions when needed. The callback can return None to fall back to the default mbedTLS function. A common use case for this feature is with secure elements that have drivers implemented in Python. Currently, only the ECDSA alternate sign function wrapper is implemented. Tested signing with a private EC key stored on an NXP SE05x secure element. Signed-off-by: iabdalkader --- extmod/extmod.mk | 5 ++ extmod/mbedtls/mbedtls_alt.c | 88 ++++++++++++++++++++++++++++++++++++ extmod/modtls_mbedtls.c | 70 ++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 extmod/mbedtls/mbedtls_alt.c 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 b261e7a70cc86..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. @@ -248,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 @@ -295,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; @@ -305,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]; @@ -786,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) }, From bab6a016b3e719d8e96d2c88cd25e53f779e6b62 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 26 Sep 2024 11:53:34 +0200 Subject: [PATCH 3/6] lib/arduino-lib: Update submodule. Changes: - Add ISO7816, APDU and SE05x package. - Add support for Opta Expansion protocol. Signed-off-by: iabdalkader --- lib/arduino-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 28009a78cf5060e6e6b2dd9c35751994e8bde645 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 24 Sep 2024 15:00:04 +0200 Subject: [PATCH 4/6] stm32/boards/ARDUINO_PORTENTA_H7: Add SE05x driver. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_PORTENTA_H7/manifest.py | 7 ++++++- ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) 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 From 57bc98f499572237fa97bad5dbc5eebb1063ebda Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 24 Sep 2024 15:01:06 +0200 Subject: [PATCH 5/6] stm32/boards/ARDUINO_NICLA_VISION: Add SE05x driver. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_NICLA_VISION/manifest.py | 3 +++ ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk | 1 + 2 files changed, 4 insertions(+) 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 From 4c54335195d1c0f21cc6936365dba6b595097f52 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 16 Oct 2024 10:16:16 +0200 Subject: [PATCH 6/6] stm32/boards/ARDUINO_OPTA: Add Opta expansion module. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_OPTA/manifest.py | 3 +++ 1 file changed, 3 insertions(+) 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")