From 9c77833933734cb57c0553304cfc30455e78c91e Mon Sep 17 00:00:00 2001 From: Levi Rak Date: Fri, 2 Jun 2017 16:52:09 -0600 Subject: [PATCH] README: callback and ecdhe --- tls/README.md | 288 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 286 insertions(+), 2 deletions(-) diff --git a/tls/README.md b/tls/README.md index b29088ce..c7898bdc 100644 --- a/tls/README.md +++ b/tls/README.md @@ -72,6 +72,16 @@ into. 2. [Client](#client-tls) 3. [Running](#run-tls) +4. [Using callbacks](#callback) + + 1. [Running](#run-callback) + +4. [Using ECC](#ecc) + + 1. [Server](#server-ecc) + 2. [Client](#client-ecc) + 3. [Running](#run-ecc) + ## Running these examples @@ -882,7 +892,6 @@ And we're done. We should now have a fully functional TLS client. * `client-tls` * `client-tls-callback` -* `client-tls-ecdhe` * `client-tls-nonblocking` * `client-tls-resume` * `client-tls-writedup` @@ -891,12 +900,281 @@ And we're done. We should now have a fully functional TLS client. * `server-tls` * `server-tls-callback` -* `server-tls-ecdhe` * `server-tls-nonblocking` * `server-tls-threaded` +## Using callbacks + +The edits required to make wolfSSL use custom functions for getting I/O from a +socket are identical between the client and server code. As such, pick +whichever `*-tls.c` you want and copy it to `*-tls-callback.c`. The finished +version of the server can be found [here][s-tls-c], and the finished version of +the client can be found [here][c-tls-c]. + +The first step is to add `errno.h` to the list of "usual suspects". This block +should now look like this: + +```c +/* the usual suspects */ +#include +#include +#include +#include +``` + +Next, we're going to need write our callbacks functions. These functions will +be called by wolfSSL whenever it need to read or write data through the +sockets. These callbacks can be quite sophisticated, but we're going to write +simple callbacks that tell us how many bytes they read or write. + +First, the read callback: + +```c +int my_IORecv(WOLFSSL* ssl, char* buff, int sz, void* ctx) +{ + /* By default, ctx will be a pointer to the file descriptor to read from. + * This can be changed by calling wolfSSL_SetIOReadCtx(). */ + int sockfd = *(int*)ctx; + int recvd; + + + /* Receive message from socket */ + if ((recvd = recv(sockfd, buff, sz, 0)) == -1) { + /* error encountered. Be responsible and report it in wolfSSL terms */ + + fprintf(stderr, "IO RECEIVE ERROR: "); + switch (errno) { + #if EAGAIN != EWOULDBLOCK + case EAGAIN: /* EAGAIN == EWOULDBLOCK on some systems, but not others */ + #endif + case EWOULDBLOCK: + if (!wolfSSL_dtls(ssl) || wolfSSL_get_using_nonblock(ssl)) { + fprintf(stderr, "would block\n"); + return WOLFSSL_CBIO_ERR_WANT_READ; + } + else { + fprintf(stderr, "socket timeout\n"); + return WOLFSSL_CBIO_ERR_TIMEOUT; + } + case ECONNRESET: + fprintf(stderr, "connection reset\n"); + return WOLFSSL_CBIO_ERR_CONN_RST; + case EINTR: + fprintf(stderr, "socket interrupted\n"); + return WOLFSSL_CBIO_ERR_ISR; + case ECONNREFUSED: + fprintf(stderr, "connection refused\n"); + return WOLFSSL_CBIO_ERR_WANT_READ; + case ECONNABORTED: + fprintf(stderr, "connection aborted\n"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + default: + fprintf(stderr, "general error\n"); + return WOLFSSL_CBIO_ERR_GENERAL; + } + } + else if (recvd == 0) { + printf("Connection closed\n"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + } + + /* successful receive */ + printf("my_IORecv: received %d bytes from %d\n", sz, sockfd); + return recvd; +} +``` + +Next the write callback: + +```c +int my_IOSend(WOLFSSL* ssl, char* buff, int sz, void* ctx) +{ + /* By default, ctx will be a pointer to the file descriptor to write to. + * This can be changed by calling wolfSSL_SetIOWriteCtx(). */ + int sockfd = *(int*)ctx; + int sent; + + + /* Receive message from socket */ + if ((sent = send(sockfd, buff, sz, 0)) == -1) { + /* error encountered. Be responsible and report it in wolfSSL terms */ + + fprintf(stderr, "IO SEND ERROR: "); + switch (errno) { + #if EAGAIN != EWOULDBLOCK + case EAGAIN: /* EAGAIN == EWOULDBLOCK on some systems, but not others */ + #endif + case EWOULDBLOCK: + fprintf(stderr, "would block\n"); + return WOLFSSL_CBIO_ERR_WANT_READ; + case ECONNRESET: + fprintf(stderr, "connection reset\n"); + return WOLFSSL_CBIO_ERR_CONN_RST; + case EINTR: + fprintf(stderr, "socket interrupted\n"); + return WOLFSSL_CBIO_ERR_ISR; + case EPIPE: + fprintf(stderr, "socket EPIPE\n"); + return WOLFSSL_CBIO_ERR_CONN_CLOSE; + default: + fprintf(stderr, "general error\n"); + return WOLFSSL_CBIO_ERR_GENERAL; + } + } + else if (sent == 0) { + printf("Connection closed\n"); + return 0; + } + + /* successful send */ + printf("my_IOSend: sent %d bytes to %d\n", sz, sockfd); + return sent; +} +``` + +The most complex part of these callbacks are probably their error reporting. +wolfSSL expects more error information that we've been doing in these examples +until now. As such, to be responsible we have to translate the `errno` value +into terms that wolfSSL expects form its I/O functions. + +But that's the hard part. Now all that's left is to register these functions +with wolfSSL. We can do this at any time between calling `wolfSSL_CTX_new()` +to get `ctx` and `wolfSSL_new()` to get `ssl`. After we've handled loading +certificate or key files but before the "Initialize the server address struct +with zeros" block is a good place. After this, add these lines: + +```c + /* Register callbacks */ + wolfSSL_SetIORecv(ctx, my_IORecv); + wolfSSL_SetIOSend(ctx, my_IOSend); +``` + +And just like that wolfSSL will use our functions to send and receive data. Now +when this program is run we should see a number of "my\_OISend: sent" and +"my\_IORecv: recieved" lines in our output. + +#### Running + +`server-tls-callback` can be connected to by the following: + +* `client-tls` +* `client-tls-callback` +* `client-tls-nonblocking` +* `client-tls-resume` +* `client-tls-writedup` + +`client-tls-callback` can connect to the following: + +* `server-tls` +* `server-tls-callback` +* `server-tls-nonblocking` +* `server-tls-threaded` + + + +## Using ECC + +We can also have wolfSSL use ECC keys. + +#### Server + +We'll modify the server first. Copy `server-tls.c` to a new file, +`server-tls-ecdhe.c`, that we will modify. The finished version can be found +[here][s-tls-e]. + +The first change will be to our file locations. Change the defines for +`CERT_FILE` and `KEY_FILE` to the following: + +```c +#define CERT_FILE "../certs/server-ecc.pem" +#define KEY_FILE "../certs/ecc-key.pem" +``` + +Furthermore, we'll want to define `CIPHER_LIST`. This will be a list of all +ciphers we support on this server. The define will look like this: + +```c +#define CIPHER_LIST "ECDHE-ECDSA-CHACHA20-POLY1305" +``` + +Finally, all we need to do is set this cipher list. Just after the "Load server +key into `WOLFSSL_CTX`" block, add these lines: + +```c + /* Set cipher list */ + if (wolfSSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set cipher list\n"); + return -1; + } +``` + +And just like that, we're done! + +#### Client + +This is one of those cases where we have more to change about the client than +the server. Copy `client-tls.c` to a new file, `client-tls-ecdhe.c`, that we +will modify. The finished version can be found [here][c-tls-e]. + +We start by changing the definition of `CERT_FILE`. It should now look like +this: + +```c +#define CERT_FILE "../certs/server-ecc.pem" +``` + +After this, we need two more files and a cipher list. In total, these new +defines will look like this: + +```c +#define ECC_FILE "../certs/client-ecc-cert.pem" +#define KEY_FILE "../certs/ecc-client-key.pem" +#define CIPHER_LIST "ECDHE-ECDSA-CHACHA20-POLY1305" +``` + +With these defined, we just need to load them into `ctx`. We're already doing +this for `CERT_FILE` in the "Load client certificates into `WOLFSSL_CTX`" +block, so add these blocks just after that: + +```c + /* Load client ecc certificates into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_certificate_chain_file(ctx, ECC_FILE) != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + ECC_FILE); + return -1; + } + + /* Load client ecc key into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM) + != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + KEY_FILE); + return -1; + } + + /* Set cipher list */ + if (wolfSSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != SSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to set cipher list\n"); + return -1; + } +``` + +And now the client is set up. + +#### Running + +`server-tls-ecdhe` can be connected to by the following: + +* `client-tls-ecdhe` + +`client-tls-ecdhe` can connect to the following: + +* `server-tls-ecdhe` + + + [make]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/Makefile @@ -905,3 +1183,9 @@ And we're done. We should now have a fully functional TLS client. [s-tls]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/server-tls.c [c-tls]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/client-tls.c + +[s-tls-c]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/server-tls-callback.c +[c-tls-c]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/client-tls-callback.c + +[s-tls-e]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/server-tls-ecdhe.c +[c-tls-e]: https://github.com/wolfssl/wolfssl-examples/blob/master/tls/client-tls-ecdhe.c