Repository navigation
extmod/modssl: Add SSLContext class. - #11862
Conversation
|
Code size report: |
Codecov Report
@@ Coverage Diff @@
## master #11862 +/- ##
==========================================
+ Coverage 98.40% 98.41% +0.01%
==========================================
Files 155 155
Lines 20543 20564 +21
==========================================
+ Hits 20215 20238 +23
+ Misses 328 326 -2
|
|
Code size change on bare metal ports that enable mbedTLS: For axtls: |
|
I've just rebased #8968 on this PR and so far it looks promising, saved around 1kB-2kB of memory usage in the application I'm testing, which includes some endurance/stress testing using asyncio+TLS in unix/esp32 ports (i.e restarting the mqtt broker repeatedly to see how it deals with wrapping new ssl sockets or testing a server (microdot) doing multiple calls with So when this is merged I can submit following PRs that:
Here is the patch if you want to add anything of that in this PR: diff --git a/extmod/modssl_mbedtls.c b/extmod/modssl_mbedtls.c
index 9b56907a5..5a0afdf78 100644
--- a/extmod/modssl_mbedtls.c
+++ b/extmod/modssl_mbedtls.c
@@ -37,7 +37,6 @@
#include "py/stream.h"
#include "py/objstr.h"
-// mbedtls_time_t
#include "mbedtls/platform.h"
#include "mbedtls/ssl.h"
#include "mbedtls/x509_crt.h"
@@ -46,6 +45,16 @@
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#include "mbedtls/build_info.h"
+#include "mbedtls/platform_time.h"
+#else
+#include "mbedtls/version.h"
+#endif
+#ifdef MICROPY_MBEDTLS_PLATFORM_TIME_ALT
+#include "mbedtls/mbedtls_config.h"
+
+#endif
#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)
@@ -59,6 +68,7 @@ typedef struct _mp_obj_ssl_context_t {
mbedtls_x509_crt cert;
mbedtls_pk_context pkey;
int authmode;
+ int cipherid [];
} mp_obj_ssl_context_t;
// This corresponds to an SSLSocket object.
@@ -74,9 +84,10 @@ typedef struct _mp_obj_ssl_socket_t {
STATIC const mp_obj_type_t ssl_context_type;
STATIC const mp_obj_type_t ssl_socket_type;
+STATIC const MP_DEFINE_STR_OBJ(mbedtls_version_obj, MBEDTLS_VERSION_STRING_FULL);
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);
+ bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname, int cert_reqs);
/******************************************************************************/
// Helper functions.
@@ -156,6 +167,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
mp_obj_ssl_context_t *self = m_new_obj(mp_obj_ssl_context_t);
#endif
self->base.type = type_in;
+ self->cipherid[0] = 0;
// Initialise mbedTLS state.
mbedtls_ssl_config_init(&self->conf);
@@ -169,6 +181,16 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
mbedtls_debug_set_threshold(3);
#endif
+ #ifdef MICROPY_MBEDTLS_PLATFORM_TIME_ALT
+ mbedtls_platform_set_time(platform_mbedtls_time);
+ #endif
+ // DEBUG MBEDTLS_PLATFORM
+ // time_t mbt;
+ // time_t mbtz;
+ // mbt = mbedtls_time(NULL);
+ // mbtz = platform_mbedtls_time(NULL);
+ // printf("secs mbt : %lu \n\n", mbt);
+ // printf("secs mbtz: %lu \n\n", mbtz);
const byte seed[] = "upy";
int ret = mbedtls_ctr_drbg_seed(&self->ctr_drbg, mbedtls_entropy_func, &self->entropy, seed, sizeof(seed));
@@ -236,6 +258,57 @@ STATIC mp_obj_t ssl_context___del__(mp_obj_t self_in) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(ssl_context___del___obj, ssl_context___del__);
#endif
+// Ciphersuites
+
+STATIC mp_obj_t mod_ssl_get_ciphers() {
+ mp_obj_t list = mp_obj_new_list(0, NULL);
+ const int *cipher_list = mbedtls_ssl_list_ciphersuites();
+ while (*cipher_list) {
+ const char *cipher_name = mbedtls_ssl_get_ciphersuite_name(*cipher_list);
+ mp_obj_list_append(list,
+ MP_OBJ_FROM_PTR(mp_obj_new_str(cipher_name,
+ strlen(cipher_name))));
+ cipher_list++;
+ if (!*cipher_list) {
+ break;
+ }
+
+ }
+
+ return list;
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ssl_get_ciphers_obj, mod_ssl_get_ciphers);
+
+
+STATIC mp_obj_t mod_ssl_set_ciphers(mp_obj_t self_in, mp_obj_t ciphersuite) {
+ mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
+ // check that ciphersuite is a list of str?
+ mp_obj_list_t *ciphers = MP_OBJ_TO_PTR(ciphersuite);
+
+ for (int i = 0, n = ciphers->len; i < n; i++) {
+
+ if (ciphers->items[i] != mp_const_none) {
+ const char *ciphername = mp_obj_str_get_str(ciphers->items[i]);
+ const int id = mbedtls_ssl_get_ciphersuite_id(ciphername);
+ self->cipherid[i] = id;
+ if (id == 0) {
+ mbedtls_raise_error(MBEDTLS_ERR_SSL_BAD_CONFIG);
+ }
+
+ }
+ }
+
+ self->cipherid[ciphers->len + 1] = 0;
+
+ return mp_const_none;
+
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_set_ciphers_obj, mod_ssl_set_ciphers);
+
+// SSLContext.load_certchain
+
STATIC void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, mp_obj_t cert_obj) {
size_t key_len;
const byte *key = (const byte *)mp_obj_str_get_data(key_obj, &key_len);
@@ -264,6 +337,37 @@ STATIC void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, m
}
}
+STATIC mp_obj_t mod_ssl_load_certchain(size_t n_args, const mp_obj_t *pos_args,
+ mp_map_t *kw_args) {
+
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ };
+
+ mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ // struct cert_chain_args args;
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args,
+ args);
+
+ if (args[0].u_obj != mp_const_none) {
+ ssl_context_load_key(self, args[0].u_obj, args[1].u_obj);
+
+ }
+
+ return mp_const_none;
+
+
+
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_load_certchain_obj, 1, mod_ssl_load_certchain);
+
+
+
+// SSLContext.load_cadata
+
STATIC void ssl_context_load_cadata(mp_obj_ssl_context_t *self, mp_obj_t cadata_obj) {
size_t cacert_len;
const byte *cacert = (const byte *)mp_obj_str_get_data(cadata_obj, &cacert_len);
@@ -276,12 +380,26 @@ STATIC void ssl_context_load_cadata(mp_obj_ssl_context_t *self, mp_obj_t cadata_
mbedtls_ssl_conf_ca_chain(&self->conf, &self->cacert, NULL);
}
+STATIC mp_obj_t mod_ssl_load_cadata(mp_obj_t self_in, mp_obj_t cadata) {
+ mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
+ if (cadata != mp_const_none) {
+ ssl_context_load_cadata(self, cadata);
+ }
+
+ return mp_const_none;
+
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_load_cadata_obj, mod_ssl_load_cadata);
+
+
STATIC mp_obj_t ssl_context_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_server_side, ARG_do_handshake_on_connect, ARG_server_hostname };
+ enum { ARG_server_side, ARG_do_handshake_on_connect, ARG_server_hostname, ARG_cert_reqs };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_do_handshake_on_connect, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_cert_reqs, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MBEDTLS_SSL_VERIFY_NONE}},
};
// Parse arguments.
@@ -292,14 +410,19 @@ STATIC mp_obj_t ssl_context_wrap_socket(size_t n_args, const mp_obj_t *pos_args,
// Create and return the new SSLSocket object.
return ssl_socket_make_new(self, sock, args[ARG_server_side].u_bool,
- args[ARG_do_handshake_on_connect].u_bool, args[ARG_server_hostname].u_obj);
+ args[ARG_do_handshake_on_connect].u_bool, args[ARG_server_hostname].u_obj, args[ARG_cert_reqs].u_int);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ssl_context_wrap_socket_obj, 2, ssl_context_wrap_socket);
+// add load_certchain, load_cadata, get/set ciphersuites
STATIC const mp_rom_map_elem_t ssl_context_locals_dict_table[] = {
#if MICROPY_PY_SSL_FINALISER
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ssl_context___del___obj) },
#endif
+ { MP_ROM_QSTR(MP_QSTR_get_ciphers), MP_ROM_PTR(&mod_ssl_get_ciphers_obj)},
+ { MP_ROM_QSTR(MP_QSTR_set_ciphers), MP_ROM_PTR(&mod_ssl_set_ciphers_obj)},
+ { MP_ROM_QSTR(MP_QSTR_load_cadata), MP_ROM_PTR(&mod_ssl_load_cadata_obj)},
+ { MP_ROM_QSTR(MP_QSTR_load_certchain), MP_ROM_PTR(&mod_ssl_load_certchain_obj)},
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&ssl_context_wrap_socket_obj) },
};
STATIC MP_DEFINE_CONST_DICT(ssl_context_locals_dict, ssl_context_locals_dict_table);
@@ -351,12 +474,12 @@ 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) {
+ bool server_side, bool do_handshake_on_connect, mp_obj_t server_hostname, int cert_reqs) {
// 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);
-
#if MICROPY_PY_SSL_FINALISER
mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t);
#else
@@ -367,8 +490,22 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
o->poll_mask = 0;
o->last_error = 0;
+
int ret;
+ uint32_t flags = 0;
+ int *cid = &ssl_context->cipherid[0];
+ /* printf("cipherid: %d\n", *cid); */
+
+ // Ciphersuite Config
+ if (*cid != 0) {
+ mbedtls_ssl_conf_ciphersuites(&ssl_context->conf, (const int *)&ssl_context->cipherid);
+ }
+
+
mbedtls_ssl_init(&o->ssl);
+ // Authmode
+ mbedtls_ssl_conf_authmode(&ssl_context->conf, cert_reqs);
+
ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf);
if (ret != 0) {
@@ -385,6 +522,7 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL);
+
if (do_handshake_on_connect) {
while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
@@ -399,10 +537,29 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
return MP_OBJ_FROM_PTR(o);
cleanup:
+ if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
+ flags = mbedtls_ssl_get_verify_result(&o->ssl);
+
+ }
+
mbedtls_ssl_free(&o->ssl);
- mbedtls_raise_error(ret);
+
+ if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
+ char xcbuf[512];
+ ret = mbedtls_x509_crt_verify_info(xcbuf, sizeof(xcbuf), "\n", flags);
+ // The length of the string written (not including the terminated nul byte),
+ // or a negative err code.
+ if (ret > 0) {
+ mp_raise_ValueError(MP_ERROR_TEXT(xcbuf));
+ } else {
+ mbedtls_raise_error(ret);
+ }
+ } else {
+ mbedtls_raise_error(ret);
+ }
}
+
STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
if (!mp_obj_is_true(binary_form)) {
@@ -416,6 +573,17 @@ STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ssl_getpeercert_obj, mod_ssl_getpeercert);
+STATIC mp_obj_t mod_ssl_cipher(mp_obj_t o_in) {
+ mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
+ const char *cipher_suite = mbedtls_ssl_get_ciphersuite(&o->ssl);
+ const char *tls_version = mbedtls_ssl_get_version(&o->ssl);
+ mp_obj_t tuple[2] = {mp_obj_new_str(cipher_suite, strlen(cipher_suite)),
+ mp_obj_new_str(tls_version, strlen(tls_version))};
+
+ return mp_obj_new_tuple(2, tuple);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ssl_cipher_obj, mod_ssl_cipher);
+
STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in);
o->poll_mask = 0;
@@ -486,6 +654,14 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
+STATIC mp_obj_t socket_ssl_pending(mp_obj_t self_in) {
+ mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in);
+ int check_pending = 0;
+ check_pending = mbedtls_ssl_check_pending(&o->ssl);
+ return mp_obj_new_int(check_pending);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_ssl_pending_obj, socket_ssl_pending);
+
STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in);
mp_uint_t ret = 0;
@@ -548,6 +724,8 @@ STATIC const mp_rom_map_elem_t ssl_socket_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) },
+ { MP_ROM_QSTR(MP_QSTR_cipher), MP_ROM_PTR(&mod_ssl_cipher_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ssl_pending), MP_ROM_PTR(&socket_ssl_pending_obj) },
};
STATIC MP_DEFINE_CONST_DICT(ssl_socket_locals_dict, ssl_socket_locals_dict_table);
@@ -614,7 +792,7 @@ STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_
// Create and return the new SSLSocket object.
return ssl_socket_make_new(ssl_context, sock, args[ARG_server_side].u_bool,
- args[ARG_do_handshake].u_bool, args[ARG_server_hostname].u_obj);
+ args[ARG_do_handshake].u_bool, args[ARG_server_hostname].u_obj, args[ARG_cert_reqs].u_int);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
@@ -628,6 +806,7 @@ STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SSLContext), MP_ROM_PTR(&ssl_context_type) },
// Constants.
+ { MP_ROM_QSTR(MP_QSTR_MBEDTLS_VERSION), MP_ROM_PTR(&mbedtls_version_obj)},
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_CLIENT), MP_ROM_INT(MBEDTLS_SSL_IS_CLIENT) },
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_SERVER), MP_ROM_INT(MBEDTLS_SSL_IS_SERVER) },
{ MP_ROM_QSTR(MP_QSTR_CERT_NONE), MP_ROM_INT(MBEDTLS_SSL_VERIFY_NONE) },
And next would follow the TLS support for |
This commit adds the SSLContext class to the ssl module, and retains the
existing ssl.wrap_socket() function to maintain backwards compatibility.
CPython deprecated the ssl.wrap_socket() function since CPython 3.7 and
instead one should use ssl.SSLContext().wrap_socket(). This commit makes
that possible.
For the axtls implementation:
- ssl.SSLContext is added, although it doesn't hold much state because
axtls requires calling ssl_ctx_new() for each new socket
- ssl.SSLContext.wrap_socket() is added
- ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER are added
For the mbedtls implementation:
- ssl.SSLContext is added, and holds most of the mbedtls state
- ssl.verify_mode is added (getter and setter)
- ssl.SSLContext.wrap_socket() is added
- ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER are added
The signatures match CPython:
- SSLContext(protocol)
- SSLContext.wrap_socket(sock, *, server_side=False,
do_handshake_on_connect=True, server_hostname=None)
The existing ssl.wrap_socket() functions retain their existing signature.
Signed-off-by: Damien George <[email protected]>
Signed-off-by: Damien George <[email protected]>
For coverage. Signed-off-by: Damien George <[email protected]>
Signed-off-by: Damien George <[email protected]>
64e5a91 to
713a451
Compare
|
Thanks @Carglglz for testing this PR. It's now merged, so feel free to follow up with your changes. (Note that I didn't add any of the above patches.) |
This PR adds the SSLContext class to the ssl module, and retains the existing ssl.wrap_socket() function to maintain backwards compatibility.
CPython deprecated the ssl.wrap_socket() function since CPython 3.7 and instead one should use ssl.SSLContext().wrap_socket(). This PR makes that possible.
For the axtls implementation:
For the mbedtls implementation:
The signatures match CPython:
The existing ssl.wrap_socket() functions retain their existing signature.