From f3f215e9bdf138fa6f94fb376ed72c25641aa298 Mon Sep 17 00:00:00 2001 From: Carlosgg Date: Tue, 27 Jun 2023 03:00:00 +0100 Subject: [PATCH] extmod/modssl_mbedtls: Add SSLContext certificate methods. This commit adds: 1) Methods to SSLContext class that match CPython signature: - `SSLContext.load_cert_chain(certfile, keyfile)` - `SSLContext.load_verify_locations(cafile=, cadata=)` - `SSLContext.get_ciphers()` --> ["CIPHERSUITE"] - `SSLContext.set_ciphers(["CIPHERSUITE"])` 2) `sslsocket.cipher()` to get current ciphersuite and protocol version. 3) `ssl.MBEDTLS_VERSION` string constant. 4) Certificate verification errors info instead of `MBEDTLS_ERR_X509_CERT_VERIFY_FAILED`. 5) Tests in `net_inet` and `multi_net` to test these new methods. `SSLContext.load_cert_chain` method allows loading key and cert from disk passing a filepath in `certfile` or `keyfile` options. `SSLContext.load_verify_locations`'s `cafile` option enables the same functionality for ca files. Signed-off-by: Carlos Gil --- extmod/modssl_mbedtls.c | 164 ++++++++++++++++++ tests/README.md | 18 ++ tests/extmod/ssl_sslcontext_ciphers.py | 29 ++++ tests/extmod/ssl_sslcontext_ciphers.py.exp | 6 + tests/multi_net/expired_cert.der | Bin 0 -> 1331 bytes tests/multi_net/rsa_cert.der | Bin 0 -> 1421 bytes tests/multi_net/rsa_key.der | Bin 0 -> 2347 bytes tests/multi_net/ssl_cert_rsa.py | 130 ++------------ .../sslcontext_check_hostname_error.py | 59 +++++++ .../sslcontext_check_hostname_error.py.exp | 4 + tests/multi_net/sslcontext_getpeercert.py | 55 ++++++ tests/multi_net/sslcontext_getpeercert.py.exp | 5 + tests/multi_net/sslcontext_server_client.py | 60 +++++++ .../multi_net/sslcontext_server_client.py.exp | 4 + .../sslcontext_server_client_ciphers.py | 58 +++++++ .../sslcontext_server_client_ciphers.py.exp | 4 + .../sslcontext_server_client_files.py | 54 ++++++ .../sslcontext_server_client_files.py.exp | 4 + tests/multi_net/sslcontext_verify_error.py | 58 +++++++ .../multi_net/sslcontext_verify_error.py.exp | 6 + .../multi_net/sslcontext_verify_time_error.py | 58 +++++++ .../sslcontext_verify_time_error.py.exp | 6 + tests/net_inet/mpycert.der | Bin 0 -> 1306 bytes tests/net_inet/test_sslcontext_client.py | 52 ++++++ tests/net_inet/test_sslcontext_client.py.exp | 1 + 25 files changed, 722 insertions(+), 113 deletions(-) create mode 100644 tests/extmod/ssl_sslcontext_ciphers.py create mode 100644 tests/extmod/ssl_sslcontext_ciphers.py.exp create mode 100644 tests/multi_net/expired_cert.der create mode 100644 tests/multi_net/rsa_cert.der create mode 100644 tests/multi_net/rsa_key.der create mode 100644 tests/multi_net/sslcontext_check_hostname_error.py create mode 100644 tests/multi_net/sslcontext_check_hostname_error.py.exp create mode 100644 tests/multi_net/sslcontext_getpeercert.py create mode 100644 tests/multi_net/sslcontext_getpeercert.py.exp create mode 100644 tests/multi_net/sslcontext_server_client.py create mode 100644 tests/multi_net/sslcontext_server_client.py.exp create mode 100644 tests/multi_net/sslcontext_server_client_ciphers.py create mode 100644 tests/multi_net/sslcontext_server_client_ciphers.py.exp create mode 100644 tests/multi_net/sslcontext_server_client_files.py create mode 100644 tests/multi_net/sslcontext_server_client_files.py.exp create mode 100644 tests/multi_net/sslcontext_verify_error.py create mode 100644 tests/multi_net/sslcontext_verify_error.py.exp create mode 100644 tests/multi_net/sslcontext_verify_time_error.py create mode 100644 tests/multi_net/sslcontext_verify_time_error.py.exp create mode 100644 tests/net_inet/mpycert.der create mode 100644 tests/net_inet/test_sslcontext_client.py create mode 100644 tests/net_inet/test_sslcontext_client.py.exp diff --git a/extmod/modssl_mbedtls.c b/extmod/modssl_mbedtls.c index 1d7705c17f..3d420759da 100644 --- a/extmod/modssl_mbedtls.c +++ b/extmod/modssl_mbedtls.c @@ -36,6 +36,8 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/objstr.h" +#include "py/reader.h" +#include "extmod/vfs.h" // mbedtls_time_t #include "mbedtls/platform.h" @@ -46,6 +48,11 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/debug.h" #include "mbedtls/error.h" +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#include "mbedtls/build_info.h" +#else +#include "mbedtls/version.h" +#endif #define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) @@ -59,6 +66,7 @@ typedef struct _mp_obj_ssl_context_t { mbedtls_x509_crt cert; mbedtls_pk_context pkey; int authmode; + int *ciphersuites; } mp_obj_ssl_context_t; // This corresponds to an SSLSocket object. @@ -75,12 +83,32 @@ 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); /******************************************************************************/ // 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; @@ -162,6 +190,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args mbedtls_x509_crt_init(&self->cacert); mbedtls_x509_crt_init(&self->cert); mbedtls_pk_init(&self->pkey); + self->ciphersuites = NULL; #ifdef MBEDTLS_DEBUG_C // Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose @@ -236,6 +265,54 @@ 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 +// SSLContext.get_ciphers() +STATIC mp_obj_t ssl_context_get_ciphers(mp_obj_t self_in) { + mp_obj_t list = mp_obj_new_list(0, NULL); + for (const int *cipher_list = mbedtls_ssl_list_ciphersuites(); *cipher_list; ++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(ssl_context_get_ciphers_obj, ssl_context_get_ciphers); + +// SSLContext.set_ciphers(ciphersuite) +STATIC mp_obj_t ssl_context_set_ciphers(mp_obj_t self_in, mp_obj_t ciphersuite) { + mp_obj_ssl_context_t *ssl_context = MP_OBJ_TO_PTR(self_in); + + // Check that ciphersuite is a list or tuple. + size_t len = 0; + mp_obj_t *ciphers; + mp_obj_get_array(ciphersuite, &len, &ciphers); + if (len == 0) { + mbedtls_raise_error(MBEDTLS_ERR_SSL_BAD_CONFIG); + } + + // Parse list of ciphers. + ssl_context->ciphersuites = m_new(int, len + 1); + for (int i = 0, n = len; i < n; i++) { + if (ciphers[i] != mp_const_none) { + const char *ciphername = mp_obj_str_get_str(ciphers[i]); + const int id = mbedtls_ssl_get_ciphersuite_id(ciphername); + ssl_context->ciphersuites[i] = id; + if (id == 0) { + mbedtls_raise_error(MBEDTLS_ERR_SSL_BAD_CONFIG); + } + } + } + ssl_context->ciphersuites[len + 1] = 0; + + // Configure ciphersuite. + mbedtls_ssl_conf_ciphersuites(&ssl_context->conf, (const int *)ssl_context->ciphersuites); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_ciphers_obj, ssl_context_set_ciphers); + 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 +341,30 @@ 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) { + 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); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(ssl_context_load_cert_chain_obj, ssl_context_load_cert_chain); + 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,6 +377,30 @@ 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) { + + 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); + } + 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_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 }; static const mp_arg_t allowed_args[] = { @@ -300,6 +425,10 @@ 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(&ssl_context_get_ciphers_obj)}, + { MP_ROM_QSTR(MP_QSTR_set_ciphers), MP_ROM_PTR(&ssl_context_set_ciphers_obj)}, + { MP_ROM_QSTR(MP_QSTR_load_cert_chain), MP_ROM_PTR(&ssl_context_load_cert_chain_obj)}, + { MP_ROM_QSTR(MP_QSTR_load_verify_locations), MP_ROM_PTR(&ssl_context_load_verify_locations_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); @@ -369,6 +498,8 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t o->last_error = 0; int ret; + uint32_t flags = 0; + mbedtls_ssl_init(&o->ssl); ret = mbedtls_ssl_setup(&o->ssl, &ssl_context->conf); @@ -382,6 +513,11 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t if (ret != 0) { goto cleanup; } + } else if (ssl_context->authmode == MBEDTLS_SSL_VERIFY_REQUIRED && server_side == false) { + + o->sock = MP_OBJ_NULL; + mbedtls_ssl_free(&o->ssl); + mp_raise_ValueError(MP_ERROR_TEXT("CERT_REQUIRED requires server_hostname")); } mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); @@ -398,8 +534,23 @@ 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); + } + o->sock = MP_OBJ_NULL; mbedtls_ssl_free(&o->ssl); + + if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + char xcbuf[256]; + int ret_info = 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_info > 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("%s"), xcbuf); + } + } + mbedtls_raise_error(ret); } @@ -416,6 +567,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; @@ -565,6 +727,7 @@ 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) }, }; STATIC MP_DEFINE_CONST_DICT(ssl_socket_locals_dict, ssl_socket_locals_dict_table); @@ -645,6 +808,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) }, diff --git a/tests/README.md b/tests/README.md index c89930d0c0..083269d6e8 100644 --- a/tests/README.md +++ b/tests/README.md @@ -174,3 +174,21 @@ internal_bench/bytebuf: 0.177s (+87.78%) internal_bench/bytebuf-3-bytarray_map.py 1 tests performed (3 individual testcases) ``` + +## Test key/certificates + +SSL/TLS tests in `multi_net` and `net_inet` use a +self-signed key/cert pair that is randomly generated and to be used for +testing/demonstration only. You should always generate your own key/cert. + +To generate a new self-signed key/cert pair with openssl do: +``` +$ openssl req -x509 -newkey rsa:4096 -keyout rsa_key.pem -out rsa_cert.pem -days 365 -nodes +``` +In this case CN is: micropython.local + +Convert them to DER format: +``` +$ openssl rsa -in rsa_key.pem -out rsa_key.der -outform DER +$ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER +``` diff --git a/tests/extmod/ssl_sslcontext_ciphers.py b/tests/extmod/ssl_sslcontext_ciphers.py new file mode 100644 index 0000000000..d87e96afdf --- /dev/null +++ b/tests/extmod/ssl_sslcontext_ciphers.py @@ -0,0 +1,29 @@ +# Basic test of ssl.SSLContext get_ciphers() and set_ciphers() methods. + +try: + import ssl +except ImportError: + print("SKIP") + raise SystemExit + + +ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + +ciphers = ctx.get_ciphers() + +for ci in ciphers: + print(ci) + +ctx.set_ciphers(ciphers[:1]) + +# Test error cases. + +try: + ctx.set_ciphers(ciphers[0]) +except TypeError as e: + print(e) + +try: + ctx.set_ciphers(["BAR"]) +except OSError as e: + print(e) diff --git a/tests/extmod/ssl_sslcontext_ciphers.py.exp b/tests/extmod/ssl_sslcontext_ciphers.py.exp new file mode 100644 index 0000000000..4d243a7883 --- /dev/null +++ b/tests/extmod/ssl_sslcontext_ciphers.py.exp @@ -0,0 +1,6 @@ +TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 +TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 +TLS-RSA-WITH-AES-256-CBC-SHA256 +TLS-RSA-WITH-AES-128-CBC-SHA256 +object 'str' isn't a tuple or list +(-24192, 'MBEDTLS_ERR_SSL_BAD_CONFIG') diff --git a/tests/multi_net/expired_cert.der b/tests/multi_net/expired_cert.der new file mode 100644 index 0000000000000000000000000000000000000000..8e1db02aee5ef883dc1e1ae326db234fa3db6fd5 GIT binary patch literal 1331 zcmXqLV%0ZjViso-vATIXIMid#AN85K^Mn-N{27?eo zZUas>=1>+kVJ62=Ltz6!5Ql?@D>y$lRX4aKu_V<{#6Son$i>6$o0(jcA5dA6k)LNM zWgr0(W#$pg1&J1bMfGy>lM{0cZl;k>TDu^9sH_OX~O?y%!k2em3dS9&<0gjbBv5_N!%GXHjSrd&k?Y ze_^WxyVr#oGX58hbYF-~JM*f!qh&*W*yYzx=6nbf2>ABn+|}=2KH73*Yn0EL=Dz#a zv%{a)W-Q&*_hR9>O$j&Z7Bd*8&OCB=enQiai78tr+;5w@_Ns*O{B(nRM?OT$eKYki zn$6|WWMI0<-2AhFb=WFZ*&BC09OBE7)f6r{*t#yY+GLJGkxUKa!p)OU=K92IpLlft zRPE}{JpC2E?}9Jw&fO~zHl2TAqq69WcXKCgK0W!t*5pei``PbSCh&b>-YM(zLd3CK z;W9ar2%2v~Bm~uW0yAD*0uxVeR}s=lcJp zrCUEbZ{YWEQ%%j9RbNh(eSF~}`RoOM2-jV^iD%trGQT{xZLhYB@u{V=PKSz}>pH+! ze0^!)?$pb(udQcslxsZ1mwlz=`t${RuFQCpuCO;@o&N;xjq8`rsXD&cOx>p3FlH(pKSzNOs((Ci0LTOPkqv$GLgT?!1Tan>x!8gn)w?ipF3vC(V}9_us_gA>%#o) zjM_I(T`A09cG7-uQSjcHm%_hJoYsE$GJl23gbZiC5S6{$7nztD85ogM2rvTxQwSr& zQZvEDr!R^ho9Y;^w`RxOpe5|-g(83EiOV#W-25+p^VfdaMCR|R9P>(_+*&5T&DnD5 zyWgDZxoRJ^c3Hay&Pg*a)@=G978!bL<%3N!j=y5|>uhH9k2w<@H!-zs!@R=zcCwe5 zH%2(HZ|~VxeKbO?!Tn{R8<$V>t@9R7(mUD2zd3)tzyI9ToYdy8UuL&vM4z`z`EfY6 zQD*W1t?*?NXI6!D@_y9*x4}X7#^ddUGB?a}HLjiXdONl5&Btf&`po4eJudFu;v*F0 zby)Yys+Di@k6k|NK8s^^jdg9ZK;fB_n_Ss4u5MVIvS!vCE#Xw&qpD}`Ec>w7Uizni z=JKa{oyIo$mwT6)>^zG@_uEc*pd3xQso=o#`qfE__0RIC{-lna(f2Pt_{`?qgsUDW}m!#D1YYP2}fT{ z`nl)i7he0zKb{oZD28lVm!GzK@8|O(=FuE_y1Et}hf`lR+?f(Ix$wr_+YLt>L<42H zn!*q7d*|28D3mXp;vM1dd*17Rcj*fSv%Z^0-m9=>9<9)s65^e=RehV%KZAJNiiji5 zzZ|=(mm~@rG}d0tlKkC$N>-Y+)!|i3$$O`=`pxg&Ech?0)%njfZQ+3{C$|N1#`kK@ Yzij;UV)*=%%POz9aC?9Fe6pz>0Lynz{r~^~ literal 0 HcmV?d00001 diff --git a/tests/multi_net/rsa_cert.der b/tests/multi_net/rsa_cert.der new file mode 100644 index 0000000000000000000000000000000000000000..24ed957eb2c5a21ec4e351abce0c802645462b02 GIT binary patch literal 1421 zcmXqLV(m0&VlG_3%*4pVBqD9Qpj(t7?v+|BcleH7GaI!wm7F%6esW@tft)z6k+Gqnp{0S5k)@G&6p(8Hn3tJ`Fy)Mj<@xN%K`$BBmnODsnEgSN~F28;<=R=r4z_%agu73aW(Uv1y zqkPsh_uapq9sayFW9g>87YomAO1M$Cn87f0=8?Pe6PkWZOxZf&e%sWwS0#++ryJZm z@*!I8o2iGuWNH`}Zk~KH z*C$^4#H0JCYFBsW>96p87kp`V?p}eg>HG^Dl|^5?n>%Uq>B$eaCSNMq&wjTuf$t0R zPFbH9B97e}=hto8e#1d7<=d6dlbY`vv3&Z+`0204{8)vGoA30eZM!FbMZq`T7r(T|YZ9R*lT;n0W>?Ne#jry@#TCI9X_-oCm(!O=x`q0}Vp_3`_5zI)|A-}AJ5>g+HHwo77j_g$!Q4(*xH zGv#5*#D7vsXD99WY$MoWYJF!$Oh=J^>XQbQiTp(drUx!tSIpGV%-=Zq+%Z#*78PrT z{eeze7v^tg)V_J@N?``GllFs)g7?Wr zAkaV-m~v(LSj1RFYAv;zR=atc{jDugN}CdzrRn;&*FYX5t;`}}Al86g0Y6BAFeBrC z7FGjhAcdUlfVm2o>=+r=IUEpu{OVrs>JxsC!`IBs^sBix=gGoNsU1wk7n8yTXD!#? zw5q#BPGjc2#V;}$-9GtFvoddrnY_Ow#QjdzhS~9&?&-3pgf~8ykFj%Rlr1@XtmCba z=w`-*!gWQa*Xt{7tG4V^zIuHo>-Qhq7WOzjN&9z9zh&FgskN^jO!HmcVz%MI zSw9X&wZvV&r*{1QTafWLhhuf6fRy+BN3v7T@;z8}{ce}tMGbZtzC#z1@8teUmY5f~ oKXy{&^Rnwn_9aopUmohkXixC8eR#|{+jyP`L#Ldb>0yCz0E4|}w*UYD literal 0 HcmV?d00001 diff --git a/tests/multi_net/rsa_key.der b/tests/multi_net/rsa_key.der new file mode 100644 index 0000000000000000000000000000000000000000..5a4666402a6a5e7b34bded6c3dd5b59bd49e4d18 GIT binary patch literal 2347 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 R=4H2VfSVY8Uv66Pf#LLHc!2-_ literal 0 HcmV?d00001 diff --git a/tests/multi_net/ssl_cert_rsa.py b/tests/multi_net/ssl_cert_rsa.py index 6454ad83aa..71c9a34797 100644 --- a/tests/multi_net/ssl_cert_rsa.py +++ b/tests/multi_net/ssl_cert_rsa.py @@ -2,127 +2,29 @@ # This test won't run under CPython because CPython doesn't have key/cert try: - import binascii, socket, ssl + import binascii, os, socket, ssl except ImportError: print("SKIP") raise SystemExit PORT = 8000 +# These are test certificates. See tests/README.md for details. +certfile = "multi_net/rsa_cert.der" +keyfile = "multi_net/rsa_key.der" -# This self-signed key/cert pair is randomly generated and to be used for -# testing/demonstration only. You should always generate your own key/cert. +try: + os.stat(certfile) + os.stat(keyfile) +except OSError: + print("SKIP") + raise SystemExit -# To generate a new self-signed key/cert pair with openssl do: -# $ openssl req -x509 -newkey rsa:4096 -keyout rsa_key.pem -out rsa_cert.pem -days 1825 -nodes -# -# Convert them to DER format: -# $ openssl rsa -in rsa_key.pem -out rsa_key.der -outform DER -# $ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER -# -# Then convert to hex format, eg using binascii.hexlify(data). +with open(certfile, "rb") as cf: + cert = cadata = cf.read() -cert = binascii.unhexlify( - b"308205b53082039da00302010202090090195a9382cbcbef300d06092a864886f70d01010b050030" - b"71310b3009060355040613024155310c300a06035504080c03466f6f310c300a06035504070c0342" - b"617231143012060355040a0c0b4d6963726f507974686f6e31143012060355040b0c0b4d6963726f" - b"507974686f6e311a301806035504030c116d6963726f707974686f6e2e6c6f63616c301e170d3233" - b"303731353136323034395a170d3238303731333136323034395a3071310b30090603550406130241" - b"55310c300a06035504080c03466f6f310c300a06035504070c0342617231143012060355040a0c0b" - b"4d6963726f507974686f6e31143012060355040b0c0b4d6963726f507974686f6e311a3018060355" - b"04030c116d6963726f707974686f6e2e6c6f63616c30820222300d06092a864886f70d0101010500" - b"0382020f003082020a0282020100944fdb40b587af0cf7e9696c355d24a70936874e6a3bd2598166" - b"ce2495aaf9b4af01b54471f7cbf3626ae0720bf0bfd520507f79ec553c62898bfd2598385f56061b" - b"0e8f452625c82d3c83e2a0d070ab9be2db21faf88c58e4a61d62f8ff43960aa1ffdadaad41f7cb2e" - b"b337070a39f08ff9fe20c09b19926cbbc4a5154b796ff7e7ce11334e090d360c81072af08758f6cd" - b"7bad75bc7b95b6dcc801c85de81d72806ca3ce0782bfcbdffce707f9fb1572a7db0d74445dc32d5f" - b"bea12a3ab1d47edf668ebfa60ed8b51e654e76292e3894ee574ea851064956906aa8afe00e67664e" - b"110b5a6ff7db51f7944463cdd626ff2ec7886c229f4ca5985168f20f8f210972b5ff9181d4f3beb8" - b"914ec5b24a0953253b3d42ab55e98bd70cb25e7a24c603b27ec83e1ce31c90b728b47a5f606ff2a1" - b"0ff784a016894c28f7e71f51a78b0a7601bbbc8c1b132b04e567394a327a7aa4674e8e4c0bfaec4b" - b"eeccf0ed09d1660933d718a2f34ff91d79d875a73fbac07182a9531ca52bd360e2678f95ff9b4ba2" - b"1490d7456548364b2eb335c207d6e1e48ccd7d8cb43868a334c095bd9673be7403f3b69b545ee904" - b"a3f513d2b2a2dd46f06820cd394819551dd05d9b34a8a3238a521f6c1c3592f76d5ef29e181c60ee" - b"bcaf4c63098794c15d4f82e7425e75ff8f5430247ecc0e7f2983b715506012f187d54a7b6729bc61" - b"fa4d10a9f22b0203010001a350304e301d0603551d0e041604147a6d126931b58fa1c3dff3c9215f" - b"6202e61fa8da301f0603551d230418301680147a6d126931b58fa1c3dff3c9215f6202e61fa8da30" - b"0c0603551d13040530030101ff300d06092a864886f70d01010b0500038202010051b3a4834d2bf1" - b"95ac645bca61e95289eff38d80ab2ee732c6ebd1370781b66955a7583c78831f9fb7d8b236a1b5ff" - b"c9183b4e17225951e1fb2c39f7218af30ec3fd8e4f697e0d10ecd05eb14208535dc55bc1e25d8a43" - b"050670d4de3e4cb8c884e6cbb6b884320d496b354acf5258bcb0ddaefd065ee8fccbddf3a2bfa10d" - b"bfeb8ab6b2580b50f0678760599269b612f81ba1310bfcd39427fec49211769c514cdd0305081d8a" - b"11ebe705496d4dcc31ac9fab96a2d298ee4423789baffbfa0fa82ee1b5113f9cf597647a36640cad" - b"abf535205c322e16153d6ab04b0817f57d8a9a6ca2db2ab10986ae9eab343547e52c78a641868bb5" - b"e2981182fcc55d86cdc6aa8478b226318a3be72fb726dd0b90f30df810c4d6c6b5a0ecb3c6cc375b" - b"8d3d244a07d8517ad390929be7b75f679beb63d8c1028905af2383144a4ed560e45907d301846acc" - b"9dbec86bcdd7fbf8a805b59f359c8bd997f5eb7b8aea6f7a538f9663ec2c12e07d4b37650e92b783" - b"74356daee4a501eeb27fef79b472b2fcce4363a9ff4d80f96a3b47dc4c4ef380ef231d193a517071" - b"b31078fa9f9a80cfd943f7e99e4ed8548c9ea80fd845ecc2c89726be273fa8b36680d645998fd1e6" - b"2367638f4953e9af68531aedb2ee49dffaaed07a4a5b97551712058219ac6f8da71710949f761271" - b"5273a348dcce40c556bdab00a4ae3a7b23a5934ac88b7640df" -) - -key = binascii.unhexlify( - b"308209280201000282020100944fdb40b587af0cf7e9696c355d24a70936874e6a3bd2598166ce24" - b"95aaf9b4af01b54471f7cbf3626ae0720bf0bfd520507f79ec553c62898bfd2598385f56061b0e8f" - b"452625c82d3c83e2a0d070ab9be2db21faf88c58e4a61d62f8ff43960aa1ffdadaad41f7cb2eb337" - b"070a39f08ff9fe20c09b19926cbbc4a5154b796ff7e7ce11334e090d360c81072af08758f6cd7bad" - b"75bc7b95b6dcc801c85de81d72806ca3ce0782bfcbdffce707f9fb1572a7db0d74445dc32d5fbea1" - b"2a3ab1d47edf668ebfa60ed8b51e654e76292e3894ee574ea851064956906aa8afe00e67664e110b" - b"5a6ff7db51f7944463cdd626ff2ec7886c229f4ca5985168f20f8f210972b5ff9181d4f3beb8914e" - b"c5b24a0953253b3d42ab55e98bd70cb25e7a24c603b27ec83e1ce31c90b728b47a5f606ff2a10ff7" - b"84a016894c28f7e71f51a78b0a7601bbbc8c1b132b04e567394a327a7aa4674e8e4c0bfaec4beecc" - b"f0ed09d1660933d718a2f34ff91d79d875a73fbac07182a9531ca52bd360e2678f95ff9b4ba21490" - b"d7456548364b2eb335c207d6e1e48ccd7d8cb43868a334c095bd9673be7403f3b69b545ee904a3f5" - b"13d2b2a2dd46f06820cd394819551dd05d9b34a8a3238a521f6c1c3592f76d5ef29e181c60eebcaf" - b"4c63098794c15d4f82e7425e75ff8f5430247ecc0e7f2983b715506012f187d54a7b6729bc61fa4d" - b"10a9f22b0203010001028202000b41080520013cc242299f0b4bfd5663aa6a4dd8206d8ba7a90f11" - b"036babfea8bc42e7eb5aae8ff656f87f3188406b7e13a6a815ab5e4867bdc236a25caba26857ac43" - b"ed9134b4d73cbf83ce759f7b7d3a25fbb4d76376dae3f6caf210ace60703a58951a51852922803d2" - b"2b91c82fdf563d85101d2d67c259a7e1e318fb922a71e85015b40beed9e6c90a1d6e1fb45586dcce" - b"ceb9c964a356ade82b6275e5c01e492a753f940852df788eab454aadc7d1dc74ddcf7dc493a3e4c9" - b"0557bbfe747e701b4b27b5c518a29dbcd8385525a1bb835e72a489096e15387e2f70b112c6bbd79e" - b"a97ae2562f7947cd2367635e25b5656a54aac7f1c892243dc135e5025a44d724884b244e8fe4abb4" - b"c67bbd2e652d5fc5942b55c24b7f642f65b9b6d37110a955c63eb4f26435be056effbd777f14db8d" - b"3d8073f7583b24656edb19911e1307101443a50717c32dbb80b6212e6f0ee43f629b1e718a958a5c" - b"fdcd99762f5bff821ac49b0e77c9d1426f8bb31142df030549330dde5cc92fa20d09744ceac6ae02" - b"fb354e9b930173e08488375f7c795b3b934c72b58a3353332d5129d56151b57a793d99868885ebd4" - b"aac11ca03e09f5b6bd9dda5322a0ab81e468839ea373ecd2b5ac4ffc99740581b35add07f83ff18e" - b"c2111555ead17783294b2330ad874bd966c1d60b44e5f379650910a8a05eb92cb7550191c13251f5" - b"0a11afa7510282010100c5a4aa380f6bdd4b4524deb44425aa7ef61039a46ad0d09e2ca2cd7fb757" - b"ff325f81eaf3a2e790afb3ffb0d71f3ffa52db1a24d3149839f03d1acfe33ef721fe310895986c5a" - b"fe88ceb82318ed540456b8aa7e07dc7b982345c4f040b1544bd2ee1e4cb0315bd8db3794ea93d705" - b"f41cc1c06badf72de36d2b4a4399846d6c851260e5044e9495be8225307edb97071bdea08c99ccfe" - b"54219f6a785db47864e03cf2851abcb62941d3efeea7cdf136d9e23845cf9ea0323b156c686c6d30" - b"1cbb5a8c7f1db23a998bf549874b2c13685b20d200d2d91be92c40480a0cca18c28f654dd644c60d" - b"e8e03824c0ff83e7cbfc44b2aa16ad537a09565ed4afbe63b8930282010100c01a5e6108420c3d2e" - b"ccd0b559e08680f47b3e7271ee4ea9bf4740cc5c418a53225778eddb716447b02d234909f8291581" - b"a45be0591952bacda55e774338962502c1d73f2d5383259aaa69f2603fde216ca9557d8b4e629888" - b"c697fec1aaf9f99ebd223c06399cc13cd21bd01e3660acc148ba841e5c89b3f8f04efac07f8072a5" - b"bacb4f5cfece528496bb35e906361efdb89a17fe4999f47508d5e48914ac651172ddc994993b4672" - b"7ec62810d6c204af4b5fd52ba4f8cb3c8720fbd469b219868e28294e60276bc2483e78d96a0edf29" - b"e237fe6f1660705d5cd3590c476e37c5d367b19bfb0a1c29ef296dfd3e9fabf5b37e1fb7357a3032" - b"c8a641b467d7090282010100bc6d55bf66ac6e69017dba38e0b38c4dc8a8055c845d9a5702b51ff8" - b"4042cbd1298f0201cf70b7d75b634d247aed92e9056c72692f3c46188d190fd35647648824154c11" - b"ea54025149cbf1e224f9b1bd4007836a594117f5a0e1b62fe72037bddc38d4e231dc9fedb79ae8dd" - b"93e5602b3e6905fff02536aaf0d7b78517e4fece0b8c872ac9040d93781e9e92832604a80462ca49" - b"234fe1c3c0695061fdd9be4aaeb08447ce5c590f2250a01629586bf3e421c424c1d576ae2fa99010" - b"b7346460165ed61de8bac782d0928e4313bd59037051e6691e85e692c2a22bbaafbe555742bca7a8" - b"1fae4933e332df317b7f3551c7e91211d6a33c38c4b85a4b46d769b3028201003884497a00a4f5d6" - b"d63af9b830fe06744ff926512345ba2ce49280f4debb858799d5e4450e4798fa2251d54cbabb20d3" - b"2bf5fff5cc20d01f173b6cc467a9713ae849c11adc29f2ae90874c6e3b74eed42494d90afb7e0f31" - b"d323a23a181e4636f345af99bb371df01805b49b11186c6ec6daafcd08e5aeb99d268e05e5b65d42" - b"dd914c194841cacfaa24726594edf7e43c3f204ea8c85c9bf806a66efb097302b514773dc41324c6" - b"400f1e1b5180ed49d58cb6600fdc143a2ecf8e9ba84d8451502de890e6771181f981a9a782475aa2" - b"bb3ecbbc76503e0530e28b676a5e6585d114b63021b4c4afae82a74cadb1cbe61a7e393ff975a942" - b"1edebb531f51618902820100214d9f1efa774b9d4e0a996442c2744560c84b133045b1af9241d60f" - b"c2f82043ac169dc9496ebb5f26b5cb8a6636c57d44e06843bf1f082be42fe5933a7ab7a6878dccf3" - b"58606a9fd6984ea525fe34f9e86f7bae33e707be0dec8fbef2deed253c822f6b812e7bd8c64bc302" - b"5c9a9e58811d30981a329f7b130148b0eb2ac62cec516942f7530963edab832bd0bacf344b183b9d" - b"ba9d54535dceff640f94d79599edf8dd0c32029950ede63f2f579b0d3c9a13c04df73fec03c4bcbe" - b"ff7ecf69ba082445673a263685475b91390963e2d42705ba89ff107e96bbb7a887daa016f282f1e6" - b"bdd7b9bb14579166f8c13be876cdef07e13c6ef08ff49d4207c7c7ff" -) +with open(keyfile, "rb") as kf: + key = kf.read() # Server @@ -146,7 +48,9 @@ def instance1(): multitest.next() s = socket.socket() s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) - s = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED, cadata=cert) + s = ssl.wrap_socket( + s, cert_reqs=ssl.CERT_REQUIRED, server_hostname="micropython.local", cadata=cadata + ) s.write(b"client to server") print(s.read(16)) s.close() diff --git a/tests/multi_net/sslcontext_check_hostname_error.py b/tests/multi_net/sslcontext_check_hostname_error.py new file mode 100644 index 0000000000..94bd472767 --- /dev/null +++ b/tests/multi_net/sslcontext_check_hostname_error.py @@ -0,0 +1,59 @@ +# Test creating an SSL connection when server_hostname is required but not specified. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/rsa_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + multitest.broadcast("ready") + try: + s2 = server_ctx.wrap_socket(s2, server_side=True) + except Exception as e: + print(e) + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + # The client will reject the SSL connection so wait until the server is ready + # so that it can see the rejection (otherwise it sees a closed socket). + multitest.wait("ready") + try: + s = client_ctx.wrap_socket(s) + except Exception as e: + print(e) + s.close() diff --git a/tests/multi_net/sslcontext_check_hostname_error.py.exp b/tests/multi_net/sslcontext_check_hostname_error.py.exp new file mode 100644 index 0000000000..d29279f860 --- /dev/null +++ b/tests/multi_net/sslcontext_check_hostname_error.py.exp @@ -0,0 +1,4 @@ +--- instance0 --- +(-29312, 'MBEDTLS_ERR_SSL_CONN_EOF') +--- instance1 --- +CERT_REQUIRED requires server_hostname diff --git a/tests/multi_net/sslcontext_getpeercert.py b/tests/multi_net/sslcontext_getpeercert.py new file mode 100644 index 0000000000..b95b13f0fc --- /dev/null +++ b/tests/multi_net/sslcontext_getpeercert.py @@ -0,0 +1,55 @@ +# Test creating an SSL connection and getting the peer certificate. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/rsa_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + s2 = server_ctx.wrap_socket(s2, server_side=True) + print(s2.read(16)) + s2.write(b"server to client") + s2.close() + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + s = client_ctx.wrap_socket(s, server_hostname="micropython.local") + print(s.getpeercert(True)) + s.write(b"client to server") + print(s.read(16)) + s.close() diff --git a/tests/multi_net/sslcontext_getpeercert.py.exp b/tests/multi_net/sslcontext_getpeercert.py.exp new file mode 100644 index 0000000000..24ab0883e3 --- /dev/null +++ b/tests/multi_net/sslcontext_getpeercert.py.exp @@ -0,0 +1,5 @@ +--- instance0 --- +b'client to server' +--- instance1 --- +None +b'server to client' diff --git a/tests/multi_net/sslcontext_server_client.py b/tests/multi_net/sslcontext_server_client.py new file mode 100644 index 0000000000..4d2ac06c58 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client.py @@ -0,0 +1,60 @@ +# Test creating an SSL connection with certificates as bytes objects. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +certfile = "multi_net/rsa_cert.der" +keyfile = "multi_net/rsa_key.der" + +try: + os.stat(certfile) + os.stat(keyfile) +except OSError: + print("SKIP") + raise SystemExit + +with open(certfile, "rb") as cf: + cert = cadata = cf.read() + +with open(keyfile, "rb") as kf: + key = kf.read() + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + s2 = server_ctx.wrap_socket(s2, server_side=True) + print(s2.read(16)) + s2.write(b"server to client") + s2.close() + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cadata=cadata) + s = client_ctx.wrap_socket(s, server_hostname="micropython.local") + s.write(b"client to server") + print(s.read(16)) + s.close() diff --git a/tests/multi_net/sslcontext_server_client.py.exp b/tests/multi_net/sslcontext_server_client.py.exp new file mode 100644 index 0000000000..909c496d01 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client.py.exp @@ -0,0 +1,4 @@ +--- instance0 --- +b'client to server' +--- instance1 --- +b'server to client' diff --git a/tests/multi_net/sslcontext_server_client_ciphers.py b/tests/multi_net/sslcontext_server_client_ciphers.py new file mode 100644 index 0000000000..e2de4e6ad4 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client_ciphers.py @@ -0,0 +1,58 @@ +# Test creating an SSL connection with specified ciphers. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/rsa_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + s2 = server_ctx.wrap_socket(s2, server_side=True) + assert isinstance(s2.cipher(), tuple) + print(s2.read(16)) + s2.write(b"server to client") + s2.close() + s.close() + + +# Client +def instance1(): + multitest.next() + s = socket.socket() + s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) + client_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ciphers = client_ctx.get_ciphers() + assert "TLS-RSA-WITH-AES-256-CBC-SHA256" in ciphers + client_ctx.set_ciphers(["TLS-RSA-WITH-AES-256-CBC-SHA256"]) + client_ctx.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + s = client_ctx.wrap_socket(s, server_hostname="micropython.local") + s.write(b"client to server") + print(s.read(16)) + s.close() diff --git a/tests/multi_net/sslcontext_server_client_ciphers.py.exp b/tests/multi_net/sslcontext_server_client_ciphers.py.exp new file mode 100644 index 0000000000..909c496d01 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client_ciphers.py.exp @@ -0,0 +1,4 @@ +--- instance0 --- +b'client to server' +--- instance1 --- +b'server to client' diff --git a/tests/multi_net/sslcontext_server_client_files.py b/tests/multi_net/sslcontext_server_client_files.py new file mode 100644 index 0000000000..2c8af21d23 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client_files.py @@ -0,0 +1,54 @@ +# Test creating an SSL connection with certificates from a file. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/rsa_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + s2 = server_ctx.wrap_socket(s2, server_side=True) + print(s2.read(16)) + s2.write(b"server to client") + s2.close() + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + s = client_ctx.wrap_socket(s, server_hostname="micropython.local") + s.write(b"client to server") + print(s.read(16)) + s.close() diff --git a/tests/multi_net/sslcontext_server_client_files.py.exp b/tests/multi_net/sslcontext_server_client_files.py.exp new file mode 100644 index 0000000000..909c496d01 --- /dev/null +++ b/tests/multi_net/sslcontext_server_client_files.py.exp @@ -0,0 +1,4 @@ +--- instance0 --- +b'client to server' +--- instance1 --- +b'server to client' diff --git a/tests/multi_net/sslcontext_verify_error.py b/tests/multi_net/sslcontext_verify_error.py new file mode 100644 index 0000000000..4feb4ce983 --- /dev/null +++ b/tests/multi_net/sslcontext_verify_error.py @@ -0,0 +1,58 @@ +# Test creating an SSL connection with an invalid certificate. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/rsa_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + try: + s2 = server_ctx.wrap_socket(s2, server_side=True) + except Exception as e: + print(e) + multitest.broadcast("finished") + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + try: + s = client_ctx.wrap_socket(s, server_hostname="foobar.local") + except Exception as e: + print(e) + # Don't close the socket until the server has seen our SSL rejection. + multitest.wait("finished") + s.close() diff --git a/tests/multi_net/sslcontext_verify_error.py.exp b/tests/multi_net/sslcontext_verify_error.py.exp new file mode 100644 index 0000000000..900c7c2308 --- /dev/null +++ b/tests/multi_net/sslcontext_verify_error.py.exp @@ -0,0 +1,6 @@ +--- instance0 --- +(-30592, 'MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE') +--- instance1 --- + +The certificate Common Name (CN) does not match with the expected CN + diff --git a/tests/multi_net/sslcontext_verify_time_error.py b/tests/multi_net/sslcontext_verify_time_error.py new file mode 100644 index 0000000000..36711acfc8 --- /dev/null +++ b/tests/multi_net/sslcontext_verify_time_error.py @@ -0,0 +1,58 @@ +# Test creating an SSL connection with an expired certificate. + +try: + import os + import socket + import ssl +except ImportError: + print("SKIP") + raise SystemExit + +PORT = 8000 + +# These are test certificates. See tests/README.md for details. +cert = cafile = "multi_net/expired_cert.der" +key = "multi_net/rsa_key.der" + +try: + os.stat(cafile) + os.stat(key) +except OSError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + multitest.globals(IP=multitest.get_network_ip()) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen(1) + multitest.next() + s2, _ = s.accept() + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_ctx.load_cert_chain(cert, key) + try: + s2 = server_ctx.wrap_socket(s2, server_side=True) + except Exception as e: + print(e) + multitest.broadcast("finished") + s.close() + + +# Client +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.verify_mode = ssl.CERT_REQUIRED + client_ctx.load_verify_locations(cafile=cafile) + try: + s = client_ctx.wrap_socket(s, server_hostname="micropython.local") + except Exception as e: + print(e) + # Don't close the socket until the server has seen our SSL rejection. + multitest.wait("finished") + s.close() diff --git a/tests/multi_net/sslcontext_verify_time_error.py.exp b/tests/multi_net/sslcontext_verify_time_error.py.exp new file mode 100644 index 0000000000..25f754c97e --- /dev/null +++ b/tests/multi_net/sslcontext_verify_time_error.py.exp @@ -0,0 +1,6 @@ +--- instance0 --- +(-30592, 'MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE') +--- instance1 --- + +The certificate validity has expired + diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der new file mode 100644 index 0000000000000000000000000000000000000000..2d66ea723ea4f983874b3ffacdf612bd90e150d7 GIT binary patch literal 1306 zcmXqLVihxJV*0m$nTe5!NswWpHiy@F9*O0_->&JYE{~5g;AP{~YV&CO&dbQi&B|cl zZ^&)H$;KSY!Y0fV8f>U(pbp}22`hN!m82HsrIsiJrzV#cWtLPb1f>?ICKe@UD7Y8p zmlha`8VG}wG7Ix~1_!w-1m)+KC`1?<$cghB85md^m>7UT6p(9bU}i>{#klr@pXRpDI%e!;XU(K zEV7jR+GOLj(l76;^&6z(8 z#AA0A<^OIxy7p3Agsu4T=bXDgYJYFLbMRJS>=n1iXV$77?AVi#UYfS~qr~?`G0%ek zTXk%6U;1BI;?)e!a{IZ#KhHBh{kp6`Tx5OnlK(R|Po8@xcsbiYk5<`*nd?+bcMG2h zV*dIzaAEgtQ6^?a2FArrj2yraVKLwX2B<7QBjbM-7GNT1Gmr)GRarnG&7sZ4$jZvj z%mimK8VG@;g+aGrR#$eo~%5Jf`OcY%mS$e637aZkrgU|Y*%2BHjp&nU}H;f0^%a4a^@zWr&>?>x!W!N-s;l2=W2SzrWwg=OMT_0*&3%7h3Gae zcy;*g4~6~lXSNqGY|pd)7B}VI6NUN9-gj?ee!gg{n9amW8&X z%-(q}=2Oav1N$lu`1j?y@Wf5pt@piK;Nc5d7tPy|3U8BlD*g~sn=(0kfov+vK`y0r z0=&-C7fQ(2J$TS&zBOv&UW5JZdD>e475$`H4}X=I{vmG7;iWsWKIrYSHs1Np``(#9 zPu90^x7i;EbvFB!@z6{>t8eDT|4SW~n`}RDA=%Q@vNZ40uCf!8nO?5+&JjPy!*p|R z$I5B||t$-I`!kiAsP`9|pUq9dAo-;c!loml7AVsQOaYrMq5%H7Z7 z3cA@JwoN{~v;R(Fp{myU`)^ePf-<@%-FbR#>*HIs7us`L6b;ukef_<2^@&b#+lM|+ zE%?6e)!sX;QRMa2+qMeJ>mn~d`VsLndWXl^e=+`In*ZcNmDisT+|c`~X7U7a{l9A# zak{(Ne|WiJ`+p7J45Mr5adMf9C-3+=w_Bh4Qjqhqe53GGU!%tR7QwBtb+KuhuXfyh uGIi_Otzkk=XOH+DQ?+mj$bEB;AyneuOV5-mey66-*%E!Ac*W`+?uP)U>;Po| literal 0 HcmV?d00001 diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py new file mode 100644 index 0000000000..860b053d5b --- /dev/null +++ b/tests/net_inet/test_sslcontext_client.py @@ -0,0 +1,52 @@ +import os +import socket +import ssl + +# This certificate was obtained from micropython.org using openssl: +# $ openssl s_client -showcerts -connect micropython.org:443 /dev/null +# The certificate is from Let's Encrypt: +# 1 s:/C=US/O=Let's Encrypt/CN=R3 +# i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1 +# Validity +# Not Before: Sep 4 00:00:00 2020 GMT +# Not After : Sep 15 16:00:00 2025 GMT +# Copy PEM content to a file (certmpy.pem) and convert to DER e.g. +# $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER +# Then convert to hex format, eg using binascii.hexlify(data). + + +ca_cert_chain = "mpycert.der" +try: + os.stat(ca_cert_chain) +except OSError: + print("SKIP") + raise SystemExit + + +def main(use_stream=True): + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + context.verify_mode = ssl.CERT_REQUIRED + assert context.verify_mode == ssl.CERT_REQUIRED + + context.load_verify_locations(cafile=ca_cert_chain) + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + addr = socket.getaddrinfo("micropython.org", 443)[0][-1] + + # CPython can wrap the socket even if not connected yet. + # ssl_sock = context.wrap_socket(s, server_hostname='micropython.org') + # ssl_sock.connect(addr) + + # MicroPython needs to connect first, CPython can do this too. + s.connect(addr) + # server_hostname must match CN (Common Name) in the certificate + # presented by the server + ssl_sock = context.wrap_socket(s, server_hostname="micropython.org") + ssl_sock.write(b"GET / HTTP/1.0\r\n\r\n") + print(ssl_sock.read(17)) + assert isinstance(ssl_sock.cipher(), tuple) + ssl_sock.close() + + +main() diff --git a/tests/net_inet/test_sslcontext_client.py.exp b/tests/net_inet/test_sslcontext_client.py.exp new file mode 100644 index 0000000000..fb69ecf1a6 --- /dev/null +++ b/tests/net_inet/test_sslcontext_client.py.exp @@ -0,0 +1 @@ +b'HTTP/1.1 200 OK\r\n'