Skip to content

esp32: Add ca_cert to ussl #8874

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,23 @@ Constants
ssl.CERT_REQUIRED

Supported values for *cert_reqs* parameter.


MBEDTLS Based Ports
-------------------
This applies for the esp32

.. function:: ssl.wrap_socket(sock, server_side=False, key=None, cert=None, server_hostname=None, cert_reqs=0, ca_certs=None, do_handshake=True)

* *sock* the socket object which gets wrapped
* *server_side* if this socket is used as a server
* *key* server key
* *cert* server cert
* *server_hostname* for SNI
* *cert_reqs* Or-ed flags from x509.h https://tls.mbed.org/api/x509_8h_source.html which errors NOT to tolerate. If set to zero all cert validation errors are accapted, if set to 0xffffff all errors will raise, set to 0xffffff to be secure by default which deviates from cpython.
* *ca_certs* ca certificates
* *do_handshake* if the handshake should be performed

.. warning::

key, cert and ca_certs are byte objects which include one DER-encoded or one or more concatenated PEM-encoded certificates.
37 changes: 31 additions & 6 deletions extmod/modussl_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ typedef struct _mp_obj_ssl_socket_t {
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt cert;
mbedtls_pk_context pkey;
} mp_obj_ssl_socket_t;
Expand All @@ -63,6 +63,8 @@ struct ssl_args {
mp_arg_val_t cert;
mp_arg_val_t server_side;
mp_arg_val_t server_hostname;
mp_arg_val_t cert_reqs;
mp_arg_val_t ca_cert;
mp_arg_val_t do_handshake;
};

Expand Down Expand Up @@ -167,7 +169,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
int ret;
mbedtls_ssl_init(&o->ssl);
mbedtls_ssl_config_init(&o->conf);
mbedtls_x509_crt_init(&o->cacert);
mbedtls_x509_crt_init(&o->ca_cert);
mbedtls_x509_crt_init(&o->cert);
mbedtls_pk_init(&o->pkey);
mbedtls_ctr_drbg_init(&o->ctr_drbg);
Expand All @@ -191,7 +193,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
goto cleanup;
}

mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE);
mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg);
#ifdef MBEDTLS_DEBUG_C
mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL);
Expand Down Expand Up @@ -237,6 +239,18 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
}
}

if (args->ca_cert.u_obj != mp_const_none) {
size_t ca_cert_len;
const byte *ca_cert = (const byte *)mp_obj_str_get_data(args->ca_cert.u_obj, &ca_cert_len);
// len should include terminating null
ret = mbedtls_x509_crt_parse(&o->ca_cert, ca_cert, ca_cert_len + 1);
if (ret != 0) {
ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; // use general error for all cert errors
goto cleanup;
}
mbedtls_ssl_conf_ca_chain(&o->conf, &o->ca_cert, NULL);
}

if (args->do_handshake.u_bool) {
while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Expand All @@ -246,19 +260,28 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
MICROPY_EVENT_POLL_HOOK
#endif
}

if (args->cert_reqs.u_int != 0) {
// We might get any of the MBEDTLS_X509_BADCERT_XXX or MBEDTLS_X509_BADCRL_XXX errors see x509.h
ret = mbedtls_ssl_get_verify_result(&o->ssl);
if ((ret & args->cert_reqs.u_int) != 0){
printf("mbedtls_cert error: %x\n", ret);
goto cleanup;
}
}
}

return o;

cleanup:
mbedtls_pk_free(&o->pkey);
mbedtls_x509_crt_free(&o->cert);
mbedtls_x509_crt_free(&o->cacert);
mbedtls_x509_crt_free(&o->ca_cert);
mbedtls_ssl_free(&o->ssl);
mbedtls_ssl_config_free(&o->conf);
mbedtls_ctr_drbg_free(&o->ctr_drbg);
mbedtls_entropy_free(&o->entropy);

// ToDo: How to return cert verification Errors?
if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
mp_raise_OSError(MP_ENOMEM);
} else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) {
Expand Down Expand Up @@ -346,7 +369,7 @@ STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i
if (request == MP_STREAM_CLOSE) {
mbedtls_pk_free(&self->pkey);
mbedtls_x509_crt_free(&self->cert);
mbedtls_x509_crt_free(&self->cacert);
mbedtls_x509_crt_free(&self->ca_cert);
mbedtls_ssl_free(&self->ssl);
mbedtls_ssl_config_free(&self->conf);
mbedtls_ctr_drbg_free(&self->ctr_drbg);
Expand Down Expand Up @@ -395,6 +418,8 @@ STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_
{ 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 = 0xffffff} },
{ MP_QSTR_ca_certs, 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} },
};

Expand Down
71 changes: 71 additions & 0 deletions tests/esp32/esp32_test_cert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import network
import time
import socket
import ssl

CA = b"""-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----"""

wlan = network.WLAN(network.STA_IF) # create station interface
wlan.active(True) # activate the interface
if not wlan.isconnected():
if wlan.status() == network.STAT_CONNECTING:
wlan.disconnect()
wlan.connect('xyz', 'xyz') # connect to an AP

while True:
if wlan.isconnected():
break
time.sleep(0.2)
host = 'letsencrypt.org'

ai = socket.getaddrinfo(host, 443)
ai = ai[0]
s = socket.socket()
s.connect(ai[-1])
# s.setblocking(False)
s2 = ssl.wrap_socket(s)
cert = s2.getpeercert(True)
s2.close()

s = socket.socket()
s.connect(ai[-1])
s.setblocking(False)
print("Starting connection which should raise, probably you get a message like: \"mbedtls_cert error: 8\"")
try:
s2 = ssl.wrap_socket(s, server_hostname=host, cert_reqs=0xffffff)
except OSError:
pass
else:
raise 'This should have raised an error!'
finally:
s2.close()

s = socket.socket()
s.connect(ai[-1])
s.setblocking(False)
print("Starting connection which should work")
try:
s2 = ssl.wrap_socket(s, server_hostname=host, ca_certs=CA, cert_reqs=0xffffff)
except OSError:
raise Exception('This should not have raised an error!')
finally:
s2.close()