Skip to content

Commit 68f1c20

Browse files
iabdalkaderdpgeorge
authored andcommitted
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 <[email protected]>
1 parent 2644f57 commit 68f1c20

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

Diff for: extmod/extmod.mk

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ SRC_EXTMOD_C += \
4242
extmod/modsocket.c \
4343
extmod/modtls_axtls.c \
4444
extmod/modtls_mbedtls.c \
45+
extmod/mbedtls/mbedtls_alt.c \
4546
extmod/modtime.c \
4647
extmod/moductypes.c \
4748
extmod/modvfs.c \
@@ -242,6 +243,10 @@ MBEDTLS_CONFIG_FILE ?= \"mbedtls/mbedtls_config_port.h\"
242243
GIT_SUBMODULES += $(MBEDTLS_DIR)
243244
CFLAGS_EXTMOD += -DMBEDTLS_CONFIG_FILE=$(MBEDTLS_CONFIG_FILE)
244245
CFLAGS_EXTMOD += -DMICROPY_SSL_MBEDTLS=1 -I$(TOP)/$(MBEDTLS_DIR)/include
246+
ifeq ($(MICROPY_PY_SSL_ECDSA_SIGN_ALT),1)
247+
CFLAGS_EXTMOD += -DMICROPY_PY_SSL_ECDSA_SIGN_ALT=1
248+
LDFLAGS_EXTMOD += -Wl,--wrap=mbedtls_ecdsa_write_signature
249+
endif
245250
SRC_THIRDPARTY_C += lib/mbedtls_errors/mp_mbedtls_errors.c
246251
SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\
247252
aes.c \

Diff for: extmod/mbedtls/mbedtls_alt.c

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Copyright The Mbed TLS Contributors
7+
* Copyright (c) 2024 Damien P. George
8+
*
9+
* This file provides default fallback functions for use with alternate
10+
* cryptography functions implemented in Python.
11+
*/
12+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
13+
#if defined(MBEDTLS_ECP_RESTARTABLE) || defined(MBEDTLS_ECDSA_DETERMINISTIC)
14+
#error "MICROPY_PY_SSL_ECDSA_SIGN_ALT cannot be used with MBEDTLS_ECP_RESTARTABLE or MBEDTLS_ECDSA_DETERMINISTIC"
15+
#endif
16+
17+
#include <string.h>
18+
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
19+
#include "mbedtls/platform.h"
20+
#include "mbedtls/ssl.h"
21+
#include "mbedtls/error.h"
22+
#include "mbedtls/ecdsa.h"
23+
#include "mbedtls/asn1write.h"
24+
25+
extern int micropy_mbedtls_ecdsa_sign_alt(const mbedtls_mpi *d, const unsigned char *hash, size_t hlen,
26+
unsigned char *sig, size_t sig_size, size_t *slen);
27+
28+
29+
// Compute and write signature
30+
// See lib/mbedtls/library/ecdsa.c:688
31+
//
32+
// Note: To avoid duplicating a lot of code, MBEDTLS_ECDSA_SIGN_ALT is not defined,
33+
// which allows the default mbedtls_ecdsa_sign to be used as a fallback function.
34+
// However, mbedtls_ecdsa_sign cannot be wrapped because it is called internally
35+
// within its object file, so we wrap mbedtls_ecdsa_read/write_signature instead.
36+
int __wrap_mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx,
37+
mbedtls_md_type_t md_alg,
38+
const unsigned char *hash, size_t hlen,
39+
unsigned char *sig, size_t sig_size, size_t *slen,
40+
int (*f_rng)(void *, unsigned char *, size_t),
41+
void *p_rng) {
42+
43+
(void)md_alg;
44+
45+
if (f_rng == NULL) {
46+
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
47+
}
48+
49+
// Check if curve is supported for ECDSA.
50+
if (!mbedtls_ecdsa_can_do(ctx->grp.id) || ctx->grp.N.p == NULL) {
51+
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
52+
}
53+
54+
// Try signing with the alternative function first.
55+
int ret = micropy_mbedtls_ecdsa_sign_alt(&ctx->d, hash, hlen, sig, sig_size, slen);
56+
57+
// Fallback to the default mbedtls implementation if needed.
58+
if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED) {
59+
mbedtls_mpi r, s;
60+
mbedtls_mpi_init(&r);
61+
mbedtls_mpi_init(&s);
62+
63+
size_t len = 0;
64+
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = { 0 };
65+
unsigned char *p = buf + sizeof(buf);
66+
67+
MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ctx->grp, &r, &s, &ctx->d, hash, hlen, f_rng, p_rng));
68+
MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_mpi(&p, buf, &s));
69+
MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_mpi(&p, buf, &r));
70+
MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
71+
MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
72+
73+
if (len > sig_size) {
74+
ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
75+
} else {
76+
ret = 0;
77+
*slen = len;
78+
memcpy(sig, p, len);
79+
}
80+
81+
cleanup:
82+
mbedtls_mpi_free(&r);
83+
mbedtls_mpi_free(&s);
84+
}
85+
86+
return ret;
87+
}
88+
#endif

Diff for: extmod/modtls_mbedtls.c

+70
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
#else
5454
#include "mbedtls/version.h"
5555
#endif
56+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
57+
#include "mbedtls/ecdsa.h"
58+
#include "mbedtls/asn1.h"
59+
#endif
5660

5761
#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)
5862

@@ -68,6 +72,9 @@ typedef struct _mp_obj_ssl_context_t {
6872
int authmode;
6973
int *ciphersuites;
7074
mp_obj_t handler;
75+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
76+
mp_obj_t ecdsa_sign_callback;
77+
#endif
7178
} mp_obj_ssl_context_t;
7279

7380
// 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
248255
mbedtls_pk_init(&self->pkey);
249256
self->ciphersuites = NULL;
250257
self->handler = mp_const_none;
258+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
259+
self->ecdsa_sign_callback = mp_const_none;
260+
#endif
251261

252262
#ifdef MBEDTLS_DEBUG_C
253263
// 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) {
295305
dest[0] = MP_OBJ_NEW_SMALL_INT(self->authmode);
296306
} else if (attr == MP_QSTR_verify_callback) {
297307
dest[0] = self->handler;
308+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
309+
} else if (attr == MP_QSTR_ecdsa_sign_callback) {
310+
dest[0] = self->ecdsa_sign_callback;
311+
#endif
298312
} else {
299313
// Continue lookup in locals_dict.
300314
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) {
305319
self->authmode = mp_obj_get_int(dest[1]);
306320
dest[0] = MP_OBJ_NULL;
307321
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
322+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
323+
} else if (attr == MP_QSTR_ecdsa_sign_callback) {
324+
dest[0] = MP_OBJ_NULL;
325+
self->ecdsa_sign_callback = dest[1];
326+
#endif
308327
} else if (attr == MP_QSTR_verify_callback) {
309328
dest[0] = MP_OBJ_NULL;
310329
self->handler = dest[1];
@@ -786,6 +805,57 @@ static MP_DEFINE_CONST_OBJ_TYPE(
786805
/******************************************************************************/
787806
// ssl module.
788807

808+
#if MICROPY_PY_SSL_ECDSA_SIGN_ALT
809+
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) {
810+
uint8_t key[256];
811+
812+
// Check if the current context has an alternative sign function.
813+
mp_obj_ssl_context_t *ssl_ctx = MP_STATE_THREAD(tls_ssl_context);
814+
if (ssl_ctx == NULL || ssl_ctx->ecdsa_sign_callback == mp_const_none) {
815+
return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
816+
}
817+
818+
size_t klen = mbedtls_mpi_size(d);
819+
if (klen > sizeof(key)) {
820+
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
821+
}
822+
823+
// Convert the MPI private key (d) to a binary array
824+
if (mbedtls_mpi_write_binary(d, key, klen) != 0) {
825+
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
826+
}
827+
828+
nlr_buf_t nlr;
829+
mp_buffer_info_t sig_buf;
830+
if (nlr_push(&nlr) == 0) {
831+
mp_obj_t ret = mp_call_function_2(ssl_ctx->ecdsa_sign_callback,
832+
mp_obj_new_bytearray_by_ref(klen, (void *)key),
833+
mp_obj_new_bytearray_by_ref(hlen, (void *)hash));
834+
if (ret == mp_const_none) {
835+
// key couldn't be used by the alternative implementation.
836+
nlr_pop();
837+
return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
838+
}
839+
mp_get_buffer_raise(ret, &sig_buf, MP_BUFFER_READ);
840+
nlr_pop();
841+
} else {
842+
// The alternative implementation failed to sign.
843+
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
844+
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
845+
}
846+
847+
// Check if the buffer fits.
848+
if (sig_buf.len > sig_size) {
849+
return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
850+
}
851+
852+
// Copy ASN.1 signature to buffer.
853+
*slen = sig_buf.len;
854+
memcpy(sig, sig_buf.buf, sig_buf.len);
855+
return 0;
856+
}
857+
#endif
858+
789859
static const mp_rom_map_elem_t mp_module_tls_globals_table[] = {
790860
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tls) },
791861

0 commit comments

Comments
 (0)