Mbedtls with LwIP and k64f

Hello,
I looking for some help with creating https client. I saw many examples, all like this: mbedtls/ssl_client1.c at development · Mbed-TLS/mbedtls · GitHub
Porting Mbed TLS to a new environment or OS — Mbed TLS documentation etc.
( I based on this example but still have some problem with it)

I’m using k64f board, LwIP and mbedtls.
I have some connection with my server. I sent from my client the “Client Hello” message. Then my server answered “Server hello”, with certificate etc.

Unfortunately my client isn’t receiving this data and handshaking stops.

To make connections I use tcp_pcb *pcb, and tcp_connect function:

tcp_connect(pcb, &dest, HTTPS_PORT, client_connected);

I initialize of course (I think) all mbed ssl functions (among others):
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);

After making connection i setting the callbacks:
mbedtls_ssl_set_bio(&ssl, pcb, my_mbedtls_net_send, my_mbedtls_net_recv, NULL);
With my “my_mbedtls_net_recv” and “my_mbedtls_net_send” functions.

Now my questions:

  1. Why my my_mbedtls_net_recv isn’t receiving data?
    ( if I add my extra callback function like this:
    tcp_recv(hs->pcb, http_recv);
    where:
    static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
    {
    tcp_recved(hs->pcb, hs->req->tot_len);
    printf(“p->tot_len = %d”,p->tot_len);
    return ERR_OK;
    }
    “Server Hello” message is receiving by this function, but too late and not while handshaking (handshaking is already stopped)
    )

  2. How to direct data from liwp to the my_mbedtls_net_recv function?

I know it is little complicated,
Could You help me with it?

@Bon_X Thank you for your question!

Have you considered using mbed OS? Mbed Os is supplied with LWIP stack, and there is a tls-client example which runs over Mbed-OS, using Mbed TLS on Mbed supported devices. K64F being one of them.

As for your problem, it sounds to me that your socket is working asynchronous, but it is treated as synchronous, since according to the wireshark, the server receives the ClientHello message, and you say that the ServerHello message is received to late. Meaning, that Mbed TLS is calling your receive callback, and you return immediately without an error, but with an empty receive buffer.
Could this be the case?

Have you considered setting the socket to be blocking?
On the other hand, have you considered calling mbedtls_net_set_nonblock(), adding support to your receive callback to return MBEDTLS_ERR_SSL_WANT_READ in case the socket is non blocking, and haven’t read anything?
Regards,
Mbed TLS Team member
Ron

Hello roneld01! Nice to meet You!
I cant use the mbed OS (this project),but I saw many examples with this OS (also with k64f)

Meaning, that Mbed TLS is calling your receive callback, and you return immediately without an error, but with an empty receive buffer.

No, there my receive callback (calling by mbedtls_ssl_set_bio and by mbed_tls_handshake) isn’t receiving data, and then handshaking function returns an error (for example -0x7280 - MBEDTLS_ERR_SSL_CONN_EOF )
After that http_recv function (which I called for my testing only) is receiving this data (exactly “server hello”).

I’m not sure how to receive data (e.g. “server hello”) inside receiving callback if I use the tcp_pcb and pbuf.
Without mbed tls I know how to do it, but there it’s not working well.

Could You give me some tips, how should I receive data (I have to use tcp_pcb socket)?

On the other hand, have you considered calling mbedtls_net_set_nonblock() , adding support to your receive callback to return MBEDTLS_ERR_SSL_WANT_READ in case the socket is non blocking, and haven’t read anything?

I read about this somewhere. I have to call the mbedtls_net_set_nonblock() before I connect to my server or when?
And how check status of my tcp_pcb socket (blocking or nonblocking)?

I’m newbie in mbedtls and lwIP and have so many questions…

Thank You very much for Your repply. :slight_smile:
Regards,
Bon_X

Hi @Bon_X
mbedtls_ssl_set_bio() is only called at the begining to set your send \ recv callbacks.

Is your socket configured to work in asynchronous matter?
If yes, then you should call mbedtls_net_set_nonblock() at the begining, when you set you socket.
According to your description, calling directly http_recv() works.
How did you implement my_mbedtls_net_recv()? Do you call there http_recv(), setting the output buffer to be the output buffer of your pcb buffer? You may need to copy the data, if setting the buffer is not possible.
I can give you assistance on the TLS part.
Regards,
Mbed TLS Team member
Ron

Hi roneld01,

mbedtls_ssl_set_bio() is only called at the begining to set your send \ recv callbacks.

Yes, You’re right.

I tried to use “mbedtls_net_set_nonblock()” or “mbedtls_net_set_block”. Both of this functions (and many others) belong to “net_sockets.h” library. I can’t use this, cause “This module only works on Unix and Windows”.

How did you implement my_mbedtls_net_recv() ? Do you call there http_recv() , setting the output buffer to be the output buffer of your pcb buffer? You may need to copy the data, if setting the buffer is not possible.

Yes, I tried call my http_recv function (and many others). The length of “server hello” is 1184. But if I use my function, I received not this length of data.

int my_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len, struct pbuf *p)
{
printf("my_mbedtls_net_recv\n\r");
struct tcp_pcb *pcb;

pcb = (struct tcp_pcb *)ctx;
tcp_recved(pcb,p->tot_len);

 printf("receive  len = %d\n\r", len);
 printf("receive  p->tot_len = %d\n\r", p->tot_len);
 printf("receive  p->len = %d\n\r", p->len);
 //writting data to the buf and returning the length of receiving data
 // i not working on it, now, cause I'm not receiving "server hello" 

And I have (console):

Starting the TLS handshake...
start my_mbedtls_net_send
sent data length = 392          // "client hello"
end my_mbedtls_net_send

start my_mbedtls_net_recv
receive  len = 5
receive  p->tot_len = 146   // don't know what what I'm receiving
receive  p->len = 11667     //
end my_mbedtls_net_recv

start my_mbedtls_net_send
sent data length = 7
end my_mbedtls_net_send    

mbedtls_ssl_handshake() returned -0x7200

p->tot_len = 1189   //  this is receiving by http_recv function after handshaking error  and this is the "server hello"
p->tot_len = 593    //  this is receiving by http_recv function after handshaking error (some other data)

Hi @Bon_X
The function signature of mbedtls_ssl_recv() is:

typedef int mbedtls_ssl_recv_t( void *ctx,
                                unsigned char *buf,
                                size_t len );

I am having trouble understanding how you set my_mbedtls_net_recv as this callback, as it doesn`t get the same parameters.
Note that Mbed TLS first reads the first 5 bytes (the TLS header), and then tries to read the rest of the packet. Is it possible that since the tot_len is more, the rest of the buffer is flushed and ignored, when you only read the first five bytes? What do you do with the rest of the buffer.

Both of this functions (and many others) belong to “net_sockets.h” library.

Yes, this is an example module, that works on Windows and Linux. /you should probably implement the API to fit your network layer

Hi, roneld01

typedef int mbedtls_ssl_recv_t( void *ctx,
                                unsigned char *buf,
                                size_t len );

Yes, I know it now. It’s one of my test functions, I tried many solutions :smiley:

Is it possible that since the tot_len is more, the rest of the buffer is flushed and ignored, when you only read the first five bytes? 

I don’t know, I was sure that the procedure looks like:

  1. Starting handshaking
  2. Sending Client hello ( by my_mbedtls_net_send callback)
  3. Receiving Server hello (by http_recv callback)
  4. “transmit” data (server hello) from http_rev to the my_mbedtls_net_send callback (received data is inside the pbuf *p. its why I added the “struct pbuf *p” into arguments of my_mbedtls_net_recv() )
  5. write data (server hello) to the buf (agrument of my_mbedtls_net_send callback) and returning len or MBEDTLS_ERR_SSL_WANT_READ or some error.

But, as I see, http_recv callback is not called while handshaking(). (but after that)
So I have a problem with using the received data, (dont know how extract it from pbuf when server hello comes - inside my_mbedtls_net_recv function)

Regards!

Hi @Bon_X
One correction to your flow. 5 should describe your mbedtls_net_recv callback.

According to your description, if http_recv is called after, it may be a matter of asynchronous operation. You should probably either return MBEDTLS_ERR_SSL_WANT_READ, which will make the handshake operation loop until the buffer is filled, or block, until http_recv is acgtually called.
I am less familiar with the LwIP functions, but there probably are some flags to set the socket to be blocking, or events to listen to for when the recv is called.
Regards,
Mbed TLS Team member
Ron

Hello after the break,
I changed my recv function and now 5. is (with recv callback function :wink: ):

int my_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) 
{
    printf("my_mbedtls_net_recv\n\r");

    struct tcp_struct_state *conn = (struct tcp_struct_state *)ctx;

    struct pbuf* p;
    u16_t ret;
    u16_t copy_len = (u16_t)LWIP_MIN(len, 0x7FFF);
    err_t err;

    if (conn == NULL)
    {
		printf("receiving end return 0\n\r");
		return 0;
	}

    p = es->p;

    printf("len = %d\n\r",len);

    LWIP_ASSERT("len is too big", len <= 0xFFFF);

    if (p == NULL)
    {
    	printf("receiving end p==NULL\n\r");
    	return MBEDTLS_ERR_SSL_WANT_READ;
    }

    ret = pbuf_copy_partial(p, buf, copy_len, 0);
    LWIP_ASSERT("ret <= p->len", ret <= p->len);
    err = pbuf_header(p, -(s16_t)ret);
    LWIP_ASSERT("error", err == ERR_OK);
    if(p->len == 0)
    {
    	printf("len == 0");
    	es->p = p->next;
    	p->next = NULL;
    	pbuf_free(p);
    }
    printf("receiving end return received len\n\rret = %d\n\r",ret);
    return ret;
}

I’m still inside handshaing (console):

 Starting the TLS handshake...
 my_mbedtls_net_send                   // send callback starts
 len = 392                             // send client hello (but not visible in wireshark)
 write_len = 392            
 size left = 0
 writing end
 my_mbedtls_net_recv                   // recv callback starts
 len = 5
 receiving end p==NULL                 //  returns MBEDTLS_ERR_SSL_WANT_READ
 my_mbedtls_net_recv                   // recv callback starts
 len = 5
 receiving end p==NULL                 //  returns MBEDTLS_ERR_SSL_WANT_READ
(...) e.t.c.

And if I did some test if my recv callvack looks like:

int my_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
{
    return -1;
}

Console reply looks like:

Starting the TLS handshake...
my_mbedtls_net_send                    // send callback starts
len = 392                              // send client hello (and visible in wireshark)       
write_len = 392                                                   
size left = 0
writing end
mbedtls_ssl_handshake returned -0x1    // end of handshaking   (as i want, "return -1" in recv callback finction)             
callback http_recv                     //http_recv from tcp_recv(pcb, http_recv); - receiving data works after handshaking.
p->tot_len = 1189
callback http_recv
p->tot_len = 593   

I think, that my recv and send callback functions works fine, but problem is with the pcb and pbuf, and using the received data.

Regards!

Hi @Bon_X
Your full flow is not clear to me, but according to your code and log, it seems that you are stick in an infinite loop of MBEDTLS_ERR_SSL_WANT_READ.
What is es? Where do you initialize it? Where do you allocate its member p?
I am guessing, that since p is NULL, you are missing some initialization \ allocation of this buffer, causing your recv call back to return MBEDTLS_ERR_SSL_WANT_READ.
Regards,
Mbed TLS Team member
Ron