client server using nanopb over SSL

323 Views Asked by At

I am trying to implement SSL for transport layer security for client-server communication using protobuf payload. I looked at network_server example of nanopb and also openssl / wolfssl C example clients ( like https://aticleworld.com/ssl-server-client-using-openssl-in-c/ and https://www.wolfssl.com/docs/quickstart/ ) . However SSL library provides functions like SSL_set_fd_ctx, SSL_connect,SSL_read,SSL_write for usage in socket client code. How to integrate SSL llibrary with nanopb network_server example which uses functions like pb_encode_delimited and pb_decode_delimited for send and receive ? Any help appreciated.

3

There are 3 best solutions below

0
On

Following jpa's advise added ssl read write callback in common.c.

static bool ssl_write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
    WOLFSSL * sslfd = (WOLFSSL *) stream->state;
    int ret = wolfSSL_write(sslfd, buf, count);
    return (ret == count) ; // true if success or false
}
pb_ostream_t pb_ostream_from_ssl_socket(WOLFSSL * ssl)
{

    pb_ostream_t stream = {&ssl_write_callback, (void *) (WOLFSSL *)ssl, SIZE_MAX, 0};

    return stream;
}

From client main

//initialize WOLFSSL and associate socket fd
pb_ostream_t output = pb_ostream_from_ssl_socket(ssl); // WOLFSSL * ssl;

It is working. Thanks to all who helped

0
On

wolfSSL supports custom input/output (I/O) and allows users to plug in their own callbacks for sending and receiving.

This means wolfSSL is agnostic to the underlying transport layer. By default wolfSSL assumes BSD sockets and a TCP/IP stack but a user can simply write their own send and receive functions and register them during setup to remove the TCP/IP or BSD socket default dependencies.

Below I've included some basic documentation on how to setup a custom send and receive and I've also included a link to an example where we are using two files on the same PC to exchange TLS packets between a client and server using the file system as the transport layer (no sockets, port, TCP/IP etc!)

int myReceive(WOLFSSL *ssl, char *buf, int sz, void *ctx)
{
    // ssl = the current SSL object, cast to void if unused
    // buf = the buffer to receive the message, always used
    // sz = the size in bytes to receive, always used
    // ctx = a custom user context, can be anything, a structure, char buf, variable, cast to the correct type and use as needed, cast to void if unused.

    // RULE1: Only return the amount received.
    // RULE2: In the case of a failed receive return one of the following errors as appropriate, returning 0 will
    //        trigger an automatic re-receive attempt without returning control to the calling application.
    //        WOLFSSL_CBIO_ERR_GENERAL    = -1,     /* general unexpected err */           
    //        WOLFSSL_CBIO_ERR_WANT_READ  = -2,     /* need to call read  again */         
    //        WOLFSSL_CBIO_ERR_WANT_WRITE = -2,     /* need to call write again */         
    //        WOLFSSL_CBIO_ERR_CONN_RST   = -3,     /* connection reset */                 
    //        WOLFSSL_CBIO_ERR_ISR        = -4,     /* interrupt */                        
    //        WOLFSSL_CBIO_ERR_CONN_CLOSE = -5,     /* connection closed or epipe */       
    //        WOLFSSL_CBIO_ERR_TIMEOUT    = -6      /* socket timeout */ 
    // RULE3: In the case of a partial receive, only return the amount read, call wolfSSL_read again
    //        with the exact same parameters (including sz), the state machine will internally keep
    //        track of received vs remainder and will handle the remainder appropriately.
}

int mySend(WOLFSSL *ssl, char *buf, int sz, void *ctx)
{
    // ssl = the current SSL object, cast to void if unused
    // buf = the message to send, always used
    // sz = the size in bytes to send, always used
    // ctx = a custom user context, can be anything, a structure, char buf, variable, cast to the correct type and use as needed, cast to void if unused.

    // RULE1: Only return the amount sent.
    // RULE2: In the case of a failed send return one of the following errors as appropriate, returning 0 will
    //        trigger an automatic re-send attempt without returning control to the calling application.
    //        WOLFSSL_CBIO_ERR_GENERAL    = -1,     /* general unexpected err */           
    //        WOLFSSL_CBIO_ERR_WANT_READ  = -2,     /* need to call read  again */         
    //        WOLFSSL_CBIO_ERR_WANT_WRITE = -2,     /* need to call write again */         
    //        WOLFSSL_CBIO_ERR_CONN_RST   = -3,     /* connection reset */                 
    //        WOLFSSL_CBIO_ERR_ISR        = -4,     /* interrupt */                        
    //        WOLFSSL_CBIO_ERR_CONN_CLOSE = -5,     /* connection closed or epipe */       
    //        WOLFSSL_CBIO_ERR_TIMEOUT    = -6      /* socket timeout */ 
    // RULE3: In the case of a partial send, only return the amount written, call wolfSSL_write again
    //        with the exact same parameters (including sz), the state machine will internally keep
    //        track of send vs remainder and will handle the remainder appropriately.
}

    // Register your callbacks in place of the defaults:
    wolfSSL_CTX_SetIORecv(ctx, mySend);
    wolfSSL_CTX_SetIOSend(ctx, myReceive);

https://github.com/wolfSSL/wolfssl-examples/tree/master/custom-io-callbacks

The reason I included this link is that you can checkout the custom I/O callbacks for the client at https://github.com/wolfSSL/wolfssl-examples/blob/master/custom-io-callbacks/file-client/file-client.c#L73-L119 and see how you could simply replace the read() and write() calls that use the file system to instead use your pb_encode_delimited() and pb_decode_delimited().

If you have any followup questions on how to setup custom I/O with wolfSSL or are struggling to implement it with nanopb please feel free to send an email to the wolfSSL support team at:

"support [at] wolfssl [dot] com"

Thanks!

  • KH
0
On

SSL_write and SSL_read are the functions you use to transfer data over SSL. In the network_server example, libc send() and recv() are used in examples/network_server/common.c.

You could substitute the functions there to make nanopb write directly to the SSL pipe. Alternatively, you can always encode and decode from a memory buffer (like in examples/simple/simple.c), and then separately send and receive that memory buffer.