-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
extmod/modssl: Add SSLContext class. #11862
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
For coverage. Signed-off-by: Damien George <damien@micropython.org>
Signed-off-by: Damien George <damien@micropython.org>
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.