extmod/modtls: Move the native ssl module to tls.

The current `ssl` module has quite a few differences to the CPython
implementation.  This change moves the MicroPython variant to a new `tls`
module and provides a wrapper module for `ssl` (in micropython-lib).

Users who only rely on implemented comparible behavior can continue to use
`ssl`, while users that rely on non-compatible behavior should switch to
`tls`.  Then we can make the facade in `ssl` more strictly adhere to
CPython.

Signed-off-by: Felix Dörre <felix@dogcraft.de>
pull/12259/head
Felix Dörre 2024-02-01 12:07:06 +00:00 zatwierdzone przez Damien George
rodzic f8f1f29ac0
commit b802f0f8ab
14 zmienionych plików z 49 dodań i 183 usunięć

Wyświetl plik

@ -39,8 +39,8 @@ set(MICROPY_SOURCE_EXTMOD
${MICROPY_EXTMOD_DIR}/modre.c
${MICROPY_EXTMOD_DIR}/modselect.c
${MICROPY_EXTMOD_DIR}/modsocket.c
${MICROPY_EXTMOD_DIR}/modssl_axtls.c
${MICROPY_EXTMOD_DIR}/modssl_mbedtls.c
${MICROPY_EXTMOD_DIR}/modtls_axtls.c
${MICROPY_EXTMOD_DIR}/modtls_mbedtls.c
${MICROPY_EXTMOD_DIR}/modtime.c
${MICROPY_EXTMOD_DIR}/modwebsocket.c
${MICROPY_EXTMOD_DIR}/modwebrepl.c

Wyświetl plik

@ -36,8 +36,8 @@ SRC_EXTMOD_C += \
extmod/modre.c \
extmod/modselect.c \
extmod/modsocket.c \
extmod/modssl_axtls.c \
extmod/modssl_mbedtls.c \
extmod/modtls_axtls.c \
extmod/modtls_mbedtls.c \
extmod/modtime.c \
extmod/moductypes.c \
extmod/modwebrepl.c \

Wyświetl plik

@ -422,48 +422,8 @@ STATIC MP_DEFINE_CONST_OBJ_TYPE(
/******************************************************************************/
// ssl module.
STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_key,
ARG_cert,
ARG_server_side,
ARG_server_hostname,
ARG_do_handshake,
};
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_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
};
// Parse arguments.
mp_obj_t sock = pos_args[0];
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);
// Create SSLContext.
mp_int_t protocol = args[ARG_server_side].u_bool ? PROTOCOL_TLS_SERVER : PROTOCOL_TLS_CLIENT;
mp_obj_t ssl_context_args[1] = { MP_OBJ_NEW_SMALL_INT(protocol) };
mp_obj_ssl_context_t *ssl_context = MP_OBJ_TO_PTR(ssl_context_make_new(&ssl_context_type, 1, 0, ssl_context_args));
// Load key and cert if given.
if (args[ARG_key].u_obj != mp_const_none) {
ssl_context_load_key(ssl_context, args[ARG_key].u_obj, args[ARG_cert].u_obj);
}
// 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);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ssl) },
// Functions.
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
STATIC const mp_rom_map_elem_t mp_module_tls_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tls) },
// Classes.
{ MP_ROM_QSTR(MP_QSTR_SSLContext), MP_ROM_PTR(&ssl_context_type) },
@ -472,13 +432,13 @@ STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_CLIENT), MP_ROM_INT(PROTOCOL_TLS_CLIENT) },
{ MP_ROM_QSTR(MP_QSTR_PROTOCOL_TLS_SERVER), MP_ROM_INT(PROTOCOL_TLS_SERVER) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
STATIC MP_DEFINE_CONST_DICT(mp_module_tls_globals, mp_module_tls_globals_table);
const mp_obj_module_t mp_module_ssl = {
const mp_obj_module_t mp_module_tls = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_ssl_globals,
.globals = (mp_obj_dict_t *)&mp_module_tls_globals,
};
MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_ssl, mp_module_ssl);
MP_REGISTER_MODULE(MP_QSTR_tls, mp_module_tls);
#endif // MICROPY_PY_SSL && MICROPY_SSL_AXTLS

Wyświetl plik

@ -91,24 +91,6 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
/******************************************************************************/
// Helper functions.
STATIC mp_obj_t read_file(mp_obj_t self_in) {
// file = open(args[0], "rb")
mp_obj_t f_args[2] = {
self_in,
MP_OBJ_NEW_QSTR(MP_QSTR_rb),
};
mp_obj_t file = mp_vfs_open(2, &f_args[0], (mp_map_t *)&mp_const_empty_map);
// data = file.read()
mp_obj_t dest[2];
mp_load_method(file, MP_QSTR_read, dest);
mp_obj_t data = mp_call_method_n_kw(0, 0, dest);
// file.close()
mp_stream_close(file);
return data;
}
#ifdef MBEDTLS_DEBUG_C
STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) {
(void)ctx;
@ -256,9 +238,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
}
if (endpoint == MBEDTLS_SSL_IS_CLIENT) {
// The CPython default is MBEDTLS_SSL_VERIFY_REQUIRED, but to maintain
// backwards compatibility we use MBEDTLS_SSL_VERIFY_NONE for now.
self->authmode = MBEDTLS_SSL_VERIFY_NONE;
self->authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
} else {
self->authmode = MBEDTLS_SSL_VERIFY_NONE;
}
@ -376,25 +356,9 @@ STATIC void ssl_context_load_key(mp_obj_ssl_context_t *self, mp_obj_t key_obj, m
}
// SSLContext.load_cert_chain(certfile, keyfile)
STATIC mp_obj_t ssl_context_load_cert_chain(mp_obj_t self_in, mp_obj_t certfile, mp_obj_t keyfile) {
STATIC mp_obj_t ssl_context_load_cert_chain(mp_obj_t self_in, mp_obj_t cert, mp_obj_t pkey) {
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_t pkey;
mp_obj_t cert;
if (certfile != mp_const_none) {
// check if key is a string/path
if (!(mp_obj_is_type(keyfile, &mp_type_bytes))) {
pkey = read_file(keyfile);
} else {
pkey = keyfile;
}
// check if cert is a string/path
if (!(mp_obj_is_type(certfile, &mp_type_bytes))) {
cert = read_file(certfile);
} else {
cert = certfile;
}
ssl_context_load_key(self, pkey, cert);
}
ssl_context_load_key(self, pkey, cert);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(ssl_context_load_cert_chain_obj, ssl_context_load_cert_chain);
@ -411,29 +375,14 @@ 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);
}
// SSLContext.load_verify_locations(cafile=None, *, cadata=None)
STATIC mp_obj_t ssl_context_load_verify_locations(size_t n_args, const mp_obj_t *pos_args,
mp_map_t *kw_args) {
// SSLContext.load_verify_locations(cadata)
STATIC mp_obj_t ssl_context_load_verify_locations(mp_obj_t self_in, mp_obj_t cadata) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_cafile, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_cadata, 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]);
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);
// cafile
if (args[0].u_obj != mp_const_none) {
ssl_context_load_cadata(self, read_file(args[0].u_obj));
}
// cadata
if (args[1].u_obj != mp_const_none) {
ssl_context_load_cadata(self, args[1].u_obj);
}
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
ssl_context_load_cadata(self, cadata);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ssl_context_load_verify_locations_obj, 1, ssl_context_load_verify_locations);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_load_verify_locations_obj, ssl_context_load_verify_locations);
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 };
@ -788,61 +737,8 @@ STATIC MP_DEFINE_CONST_OBJ_TYPE(
/******************************************************************************/
// ssl module.
STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_key,
ARG_cert,
ARG_server_side,
ARG_server_hostname,
ARG_cert_reqs,
ARG_cadata,
ARG_do_handshake,
};
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_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ 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}},
{ MP_QSTR_cadata, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_do_handshake, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
};
// Parse arguments.
mp_obj_t sock = pos_args[0];
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);
// Create SSLContext.
mp_int_t protocol = args[ARG_server_side].u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT;
mp_obj_t ssl_context_args[1] = { MP_OBJ_NEW_SMALL_INT(protocol) };
mp_obj_ssl_context_t *ssl_context = MP_OBJ_TO_PTR(ssl_context_make_new(&ssl_context_type, 1, 0, ssl_context_args));
// Load key and cert if given.
if (args[ARG_key].u_obj != mp_const_none) {
ssl_context_load_key(ssl_context, args[ARG_key].u_obj, args[ARG_cert].u_obj);
}
// Set the verify_mode.
mp_obj_t dest[2] = { MP_OBJ_SENTINEL, MP_OBJ_NEW_SMALL_INT(args[ARG_cert_reqs].u_int) };
ssl_context_attr(MP_OBJ_FROM_PTR(ssl_context), MP_QSTR_verify_mode, dest);
// Load cadata if given.
if (args[ARG_cadata].u_obj != mp_const_none) {
ssl_context_load_cadata(ssl_context, args[ARG_cadata].u_obj);
}
// 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);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ssl) },
// Functions.
{ MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&mod_ssl_wrap_socket_obj) },
STATIC const mp_rom_map_elem_t mp_module_tls_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tls) },
// Classes.
{ MP_ROM_QSTR(MP_QSTR_SSLContext), MP_ROM_PTR(&ssl_context_type) },
@ -855,13 +751,13 @@ STATIC const mp_rom_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_CERT_OPTIONAL), MP_ROM_INT(MBEDTLS_SSL_VERIFY_OPTIONAL) },
{ MP_ROM_QSTR(MP_QSTR_CERT_REQUIRED), MP_ROM_INT(MBEDTLS_SSL_VERIFY_REQUIRED) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_ssl_globals, mp_module_ssl_globals_table);
STATIC MP_DEFINE_CONST_DICT(mp_module_tls_globals, mp_module_tls_globals_table);
const mp_obj_module_t mp_module_ssl = {
const mp_obj_module_t mp_module_tls = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_ssl_globals,
.globals = (mp_obj_dict_t *)&mp_module_tls_globals,
};
MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_ssl, mp_module_ssl);
MP_REGISTER_MODULE(MP_QSTR_tls, mp_module_tls);
#endif // MICROPY_PY_SSL && MICROPY_SSL_MBEDTLS

Wyświetl plik

@ -1,3 +1,4 @@
freeze_as_str("frzstr")
freeze_as_mpy("frzmpy")
freeze_mpy("$(MPY_DIR)/tests/frozen")
require("ssl")

Wyświetl plik

@ -1 +1,2 @@
require("mip-cmdline")
require("ssl")

Wyświetl plik

@ -0,0 +1 @@
require("ssl")

Wyświetl plik

@ -1,3 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<FrozenManifest>$(PyWinDir)\variants\manifest.py</FrozenManifest>
</PropertyGroup>
</Project>

Wyświetl plik

@ -1,13 +1,13 @@
# Basic test of ssl.SSLContext get_ciphers() and set_ciphers() methods.
# Basic test of tls.SSLContext get_ciphers() and set_ciphers() methods.
try:
import ssl
import tls
except ImportError:
print("SKIP")
raise SystemExit
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
ciphers = ctx.get_ciphers()

Wyświetl plik

@ -1,20 +1,20 @@
# Test MicroPython-specific behaviour of ssl.SSLContext.
# Test MicroPython-specific behaviour of tls.SSLContext.
try:
import ssl
import tls
except ImportError:
print("SKIP")
raise SystemExit
# Test constructing without any arguments (in CPython it's a DeprecationWarning).
try:
ssl.SSLContext()
tls.SSLContext()
except TypeError:
print("TypeError")
# Test attributes that don't exist (in CPython new attributes can be added).
# This test is needed for coverage because SSLContext implements a custom attr handler.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
try:
ctx.does_not_exist
except AttributeError:

Wyświetl plik

@ -3,7 +3,7 @@
try:
import os
import socket
import ssl
import tls
except ImportError:
print("SKIP")
raise SystemExit
@ -13,6 +13,10 @@ PORT = 8000
# These are test certificates. See tests/README.md for details.
cert = cafile = "ec_cert.der"
key = "ec_key.der"
with open(cafile, "rb") as f:
cadata = f.read()
with open(key, "rb") as f:
keydata = f.read()
try:
os.stat(cafile)
@ -31,8 +35,8 @@ def instance0():
s.listen(1)
multitest.next()
s2, _ = s.accept()
server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_ctx.load_cert_chain(cert, key)
server_ctx = tls.SSLContext(tls.PROTOCOL_TLS_SERVER)
server_ctx.load_cert_chain(cadata, keydata)
s2 = server_ctx.wrap_socket(s2, server_side=True)
assert isinstance(s2.cipher(), tuple)
print(s2.read(16))
@ -46,12 +50,12 @@ def instance1():
multitest.next()
s = socket.socket()
s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
client_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client_ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
ciphers = client_ctx.get_ciphers()
assert "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256" in ciphers
client_ctx.set_ciphers(["TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256"])
client_ctx.verify_mode = ssl.CERT_REQUIRED
client_ctx.load_verify_locations(cafile=cafile)
client_ctx.verify_mode = tls.CERT_REQUIRED
client_ctx.load_verify_locations(cadata)
s = client_ctx.wrap_socket(s, server_hostname="micropython.local")
s.write(b"client to server")
print(s.read(16))

Wyświetl plik

@ -57,8 +57,8 @@ deflate errno example_package
ffi framebuf gc hashlib
heapq io json machine
math os platform random
re select socket ssl
struct sys termios time
re select socket struct
sys termios time tls
uctypes websocket
me