Multiple HTTPS instances with mbedTLS on multithread/multitask system - ESP8266

Hi.

Please delete the other topic that I created: https://forums.mbed.com/t/mbedtls-on-esp8266-rtos-sdk-multithreading-processor-reseting/7805

I 'm testing the ESP8266 RTOS SDK (which uses freeRTOS), latest version, which includes mbedTLS. I am working with Firebase database. I am able to do these things correctly using mbedTLS:
(1) connect to the host with a persistent sock and keep listening for changes inside a given resource.
(2) write data to a given resource of the same database.

Both these things are only working separately. I did one task for each purpose and I can only create only one of the tasks at the main(), if I create both tasks the processor keeps reseting.
My plan is to be able to keep a socket open to keep monitoring (1), when needed, be able to write (2), I want both thing can wok in parallel.

On config.h MBEDTLS_THREADING_C and MBEDTLS_THREADING_PTHREAD are defined.
I have made mbedtls_ssl_config conf; global, and I only call mbedtls_ssl_config_init(&conf); one time in setup of main. That is, I am using an unique conf for both tasks.

Here is my current code: esp8266_freeRTOS_firebase/https_mbedtls_firebase_v2.c at master Ā· jefersonpehls/esp8266_freeRTOS_firebase Ā· GitHub

I want to use mutiple instances of mbedTLS as HTTPS client. As I was reading on web about this, I will need to use mutexes, in which places of my code I will need to add these mutexes?

Regards.

EDIT

Based on the code above, I edited it in order the following structs be shared between the tasks, to not declare one on each task:
mbedtls_ssl_config conf;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt cacert;

But anyway, the code only works if I create just one of the tasks (or just monitor task or just write task). Creating both makes the processor stay resetting.

If you want you can check it here: esp8266_freeRTOS_firebase/https_mbedtls_firebase_v3.c at master Ā· jefersonpehls/esp8266_freeRTOS_firebase Ā· GitHub

Which resources of mbedTLS are making my program crash?

Hi @abomin3v3l

Mbed TLS is supposed to be thread safe. All the shared resources should bwe protected with the Mbed TLS mutex.
I would suggest you read the following articles:

Of course, there might always be some issues that were missed, which should be fixed.
Any other global resource that you are using in your program, should also be protected.
You could also look at the ssl_client example for reference.
Regards,
Mbed Support
Ron

1 Like

Hi @roneld01

I did a test here and it seems that mbedtls_ssl_read is a blocking function. Is this correct?

I have this:

do
    {
        len = sizeof(buf) - 1;
        bzero(buf, sizeof(buf));
        ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);

        ESP_LOGI(TAG, "#### READ @@@@");

        if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
            continue;

        if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) 
        {
            //ESP_LOGI(TAG, "#### PEER CLOSE NOTIFY ####");
            ret = 0;
            break;
        }

        if(ret < 0)
        {
            //ESP_LOGE(TAG, "mbedtls_ssl_read returned -0x%x", -ret);
            break;
        }

        if(ret == 0)
        {
            //ESP_LOGI(TAG, "connection closed");
            break;
        }

        len = ret;
        //ESP_LOGD(TAG, "%d bytes read", len);

        /* Print response directly to stdout as it is read */
        for(int i = 0; i < len; i++) 
        {
            putchar(buf[i]);
        }

    } while(1);

And the string is being printed only when I receive the keep-alive message from FIrebase each 30 seconds.

Hi @abomin3v3l
Yes, mbedtls_ssl_read() is blocking. To be precise, the f_rcv callback is blocking, which blocks mbedtls_ssl_read(), unless you set your recv calllback to non_blocking, and return MBEDTLS_ERR_SSL_WANT_READ in case your socket would block the operation, as in the BSD sockets example in the code
Regards

1 Like

Hi @roneld01

I did some tests and realized the following.

In my program, these things are global:

extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[]   asm("_binary_server_root_cert_pem_end");
mbedtls_ssl_config conf;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt cacert;

At the setup I do that:

int ret;
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_ssl_config_init(&conf);

mbedtls_entropy_init(&entropy);
if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)) != 0)
{
    ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
    abort();
}

ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start, server_root_cert_pem_end-server_root_cert_pem_start);
if(ret < 0)
{
    ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
    abort();
}

if((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
    ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
    abort();
}

mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);

xTaskCreate(https_monitor_task, "https_monitor_task", 8192, NULL, 6, NULL);
xTaskCreate(https_write_task, "https_write_task", 8192, NULL, 5, NULL);

The task ā€œhttps_monitor_taskā€ is created first and has higher priority. then I started to test how far I can go inside ā€œhttps_write_taskā€ in order the program does not crash.
Look below were shows ā€œHere there is a problemā€, the string printed via debug is being ā€œmbedtls_ssl_setup returned -0x7f00ā€. The task ā€œhttps_monitor_taskā€ does not print that kind of message at the same point. If I dont lock the program with for ( ; ; ) the program crashes. Maybe this is crashing my program when it calls ā€œgoto exit;ā€ Do you have idea of what is this error and how to solve?

static void https_write_task (void *pvParameters)
{
    char writeREQUEST[2048];
    char buf[512];
    int ret, flags, len;
    
    mbedtls_ssl_context ssl;
    mbedtls_net_context server_fd;

    mbedtls_ssl_init(&ssl);

     /* Hostname set here should match CN in server certificate */
    if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
    {
        ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
        abort();
    }

    //Until here is all OK...

    //Here there is a problem
    if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0)
    {
        ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);

        //Lock here
        for (;;) 
        {
            vTaskDelay(100/portTICK_PERIOD_MS);
        }

        goto exit;
    }

    while (1)
    {
        mbedtls_net_init(&server_fd);
        //...
    }
}

Regards.

Hi @abomin3v3l
If you use the utility program strerror, you will see:

./strerror 0x7f00
Last error was: -0x7f00 - SSL - Memory allocation failed

This error is returned when calloc fails to allocate memory in your platform.
From mbedtls_ssl_setup() it is returned when the library tries to allocate the in transport buffer and the out transport buffer.
I assume you left for MBEDTLS_SSL_MAX_CONTENT_LEN the default value of 16KB, which you means that you are trying to allocate 32 KB for these two buffers.
Note you can tweak the values to be different for in and out buffers using MBEDTLS_SSL_IN_CONTENT_LEN and MBEDTLS_SSL_OUT_CONTENT_LEN.
I would suggest you read the following articles that realtes to this:

You can tweak the buffer sizes to length that could fit the certificates that ou send \ receive

As for the crash, yes it is probably happening on some resource cleaning, for resource that hasnā€™t been allocated, and it is not NULL
Regards

1 Like

Hi @roneld01 Thanks!

I solved this yesterday by setting on the menuconfig of my application the maximum length of incoming and outgoing fragments to 2.5KB. Now the application is working correctly doing both things ā€œin parallelā€.

You said on previous post:
Yes, mbedtls_ssl_read() is blocking. To be precise, the f_rcv callback is blocking, which blocks mbedtls_ssl_read() , unless you set your recv calllback to non_blocking, and return MBEDTLS_ERR_SSL_WANT_READ in case your socket would block the operation, as in the BSD sockets example in the code

I did not understood your answer very well, could you explain more?
I want it work this way: if inside the read function there is no reply yet, then its returns a specific value to my task and the task will block for around 50ms then will call the receive function again.

How I would do this: unless you set your recv calllback to non_blocking, and return MBEDTLS_ERR_SSL_WANT_READ. How to set to non-blocking?

I think this is the only thing is missing on my program regarding the mbedTLS, I just want to make it non-blocking, and if the write function is also blocking, also want to make it non-blocking too.

I did not found f_rcv inside the read function.

And thanks for you help again!

Regards!

H @abomin3v3l
You are responsible for the bio callbacks you set through the mbedtls_ssl_set_bio() function.

mbedtls_ssl_read() calls the f_recv function through mbedtls_ssl_fetch_input()

The socket you are setting and using should be implemented as non-blocking, if this is how you want.
Perhaps for your needs, you should use f_recv_timeout, with 50 ms
Regards

1 Like

Hi @roneld01

Doing this way?

mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, NULL, mbedtls_net_recv_timeout);

How to set the timeout value? in this case to 50ms.

Hi @abomin3v3l

Doing this way?

You can also call mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);

this assuming your platfomr supports the given example bio callbacks. If it does, then to set it to non blocking, you could just call mbedtls_net_set_nonblock(), however, if you want the timeout, then keep it blocking for 50 ms.

How to set the timeout value? in this case to 50ms.

You could set it with mbedtls_ssl_conf_read_timeout()
Regards

1 Like

Hi @roneld01

Thanks, it worked. Now it is really non-blocking, I have tested. I did this way:

mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_net_set_nonblock(&server_fd);

But now I have another ā€˜problemā€™, maybe the last.
Is is about the certificate, I am getting the following messages:

Failed to verify peer certificate!
The certificate is not correctly signed by the trusted CA.

Currently, for incoming/outgoing fragment length, I am using both at 2.5KB, but I also tested with both at 4KB and got the same messages. I have just suspected on this, but is seems to not be the issue.

Here is my current code:
I suspect on the NULL of line 488ā€¦

The code shows on line 487: mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
If I change to MBEDTLS_SSL_VERIFY_REQUIRED, the connection to the server does not work.

And to say, there is a file server_root_cert.pem on the main folder of my application. And in my application I am only HTTPS client always, never a server.

Do you have idea on how to solve this?

Regards.