diff --git a/README.md b/README.md index 5a436a348..00579a7c3 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ App|Description [picow_http_client](pico_w/wifi/http_client) | Demonstrates how to make http and https requests [picow_http_client_verify](pico_w/wifi/http_client) | Demonstrates how to make a https request with server authentication [picow_mqtt_client](pico_w/wifi/mqtt) | Demonstrates how to implement a MQTT client application +[picow_dtls](pico_w/wifi/dtls) | Demonstrates how to implement a simple DTLS client and server #### FreeRTOS examples diff --git a/pico_w/wifi/CMakeLists.txt b/pico_w/wifi/CMakeLists.txt index 31797084b..45e5f7d18 100644 --- a/pico_w/wifi/CMakeLists.txt +++ b/pico_w/wifi/CMakeLists.txt @@ -19,6 +19,7 @@ else() add_subdirectory_exclude_platforms(udp_beacon) add_subdirectory_exclude_platforms(http_client) add_subdirectory_exclude_platforms(mqtt) + add_subdirectory_exclude_platforms(dtls) if (NOT PICO_MBEDTLS_PATH) message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined") diff --git a/pico_w/wifi/dtls/CMakeLists.txt b/pico_w/wifi/dtls/CMakeLists.txt new file mode 100644 index 000000000..939bd25cc --- /dev/null +++ b/pico_w/wifi/dtls/CMakeLists.txt @@ -0,0 +1,83 @@ +# Pick DTLS server from environment +if (DEFINED ENV{DTLS_SERVER} AND (NOT DTLS_SERVER)) + set(DTLS_SERVER $ENV{DTLS_SERVER}) + message("Using DTLS_SERVER from environment ('${DTLS_SERVER}')") +endif() +if (NOT DTLS_SERVER) + message("Skipping DTLS example as DTLS_SERVER is not defined") + return() +endif() +set(DTLS_SERVER "${DTLS_SERVER}" CACHE INTERNAL "DTLS server for examples") +if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/certs/${DTLS_SERVER}") + message("Generate DTLS certs by running ${CMAKE_CURRENT_LIST_DIR}/certs/makecerts.sh") + return() +endif() + +set(RANDOM_DATA_LEN 32) +string(RANDOM LENGTH ${RANDOM_DATA_LEN} RANDOM_DATA) + +pico_add_library(pico_dtls) +target_include_directories(pico_dtls_headers INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ) +target_sources(pico_dtls INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/dtls_common.c + ) +pico_mirrored_target_link_libraries(pico_dtls INTERFACE + pico_lwip_mbedtls + pico_mbedtls + ) +target_compile_definitions(pico_dtls INTERFACE + CUSTOM_MBEDTLS_ENTROPY_PTR=\"${RANDOM_DATA}\" + CUSTOM_MBEDTLS_ENTROPY_LEN=${RANDOM_DATA_LEN} + ) + +set(TARGET_NAME dtls_echo_server) + +add_executable(${TARGET_NAME} + dtls_echo_server.c + ) +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. + ) +target_link_libraries(${TARGET_NAME} PRIVATE + pico_cyw43_arch_lwip_threadsafe_background + pico_lwip_nosys + pico_stdlib + pico_dtls + ) +target_compile_definitions(${TARGET_NAME} PRIVATE + DTLS_CERT_INC=\"certs/${DTLS_SERVER}/dtls_server.inc\" + ) +target_compile_definitions(${TARGET_NAME} PRIVATE + CYW43_HOST_NAME=\"pico_dtls_example\" + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +pico_add_extra_outputs(${TARGET_NAME}) + +set(TARGET_NAME dtls_echo_client) + +add_executable(${TARGET_NAME} + dtls_echo_client.c + ) +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. + ) +target_link_libraries(${TARGET_NAME} PRIVATE + pico_cyw43_arch_lwip_threadsafe_background + pico_lwip_nosys + pico_stdlib + pico_dtls + ) +target_compile_definitions(${TARGET_NAME} PRIVATE + DTLS_SERVER=\"${DTLS_SERVER}\" + DTLS_CERT_INC=\"certs/${DTLS_SERVER}/dtls_client.inc\" + ) +target_compile_definitions(${TARGET_NAME} PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +pico_add_extra_outputs(${TARGET_NAME}) diff --git a/pico_w/wifi/dtls/README.md b/pico_w/wifi/dtls/README.md new file mode 100644 index 000000000..9bf2f069d --- /dev/null +++ b/pico_w/wifi/dtls/README.md @@ -0,0 +1,63 @@ +# Setup + +These examples demonstrate how to use dtls via mbedtls on a Pico W device. +You need to define DTLS_SERVER and run the `makecerts.sh` script to generate the certificates and keys needed for the server and client. +``` +export DTLS_SERVER=myserver +cd dtls/certs +./makecerts.sh +``` +The examples should now build. + +# Running the dtls examples + +The client connects to a server and sends it a few lines of text which it expects to be sent back. +You can build and run the client and server examples on two Pico W devices, or to test with just one Pico W device, you can run the server or client on a Linux host. + +## Using openssl + +The `host/server.sh` and `host/client.sh` scripts demonstrate how to use DTLS with openssl, although you will have to echo text manually. +For example, run dtls_echo_client on a Pico W device and the `server.sh` on a linux host. +``` +export DTLS_SERVER=myserver +cd host +./server.sh +``` +The scripts use the keys in certs/myserver + +Or run dtls_echo_server on a Pico W device and `client.sh` on a linux host. The host name for the server on Pico W is set to `pico_dtls_example`. Make sure you build the code for the Pico W and run the client with the right DTLS_SERVER name (and matching keys in the client and server) or else the SSL handshake will fail. +``` +export DTLS_SERVER=pico_dtls_example +ping pico_dtls_example # make sure you can reach it! +cd host +./client.sh +``` +The scripts use the keys in certs/pico_dtls_example. Type a sentence into the `client.sh` console and the server should send it back as a reply. + +## Using mbedtls + +The host folder contains C versions of the examples that can be compiled natively for the host. They are modified versions of mbedtls examples. +If you are building the server or client on a linux host, the mbedtls library in PICO_SDK_PATH will be used to build the code. + +For example, run dtls_echo_client on a Pico W device and the dtls_host_echo_server on a linux host. +``` +export DTLS_SERVER=myserver +cd host +mkdir build +cd build +cmake .. +make -j8 +./dtls_host_echo_server + +``` +Or run dtls_echo_server on a Pico W device and dtls_host_echo_client on a linux host. +``` +export DTLS_SERVER=pico_dtls_example +cd host +mkdir build +cd build +cmake .. +make -j8 +./dtls_host_echo_client +``` +Remember to build the client and server for the linux host and Pico W with the correct value of DTLS_SERVER or else the handshake will fail. \ No newline at end of file diff --git a/pico_w/wifi/dtls/certs/.gitignore b/pico_w/wifi/dtls/certs/.gitignore new file mode 100644 index 000000000..355164c12 --- /dev/null +++ b/pico_w/wifi/dtls/certs/.gitignore @@ -0,0 +1 @@ +*/ diff --git a/pico_w/wifi/dtls/certs/makecerts.sh b/pico_w/wifi/dtls/certs/makecerts.sh new file mode 100755 index 000000000..c68ec7566 --- /dev/null +++ b/pico_w/wifi/dtls/certs/makecerts.sh @@ -0,0 +1,59 @@ +#!/usr/bin/bash +set -e + +if [ "${PWD##*/}" != "certs" ]; then + echo Run this in the certs folder + exit 1 +fi +if [ -z "$DTLS_SERVER" ]; then + echo Define DTLS_SERVER + exit 1 +fi +if ! command -v openssl 2>&1 >/dev/null; then + echo openssl could not be found + exit 1 +fi + +SERVER_NAME=$DTLS_SERVER + +if [ -d "$SERVER_NAME" ]; then + echo Run \"rm -fr $SERVER_NAME\" to regenerate these keys + exit 1 +fi +mkdir $SERVER_NAME +echo Generating keys in $PWD/$SERVER_NAME + +openssl genrsa -out $SERVER_NAME/ca.key 2048 +openssl req -new -x509 -days 99999 -key $SERVER_NAME/ca.key -out $SERVER_NAME/ca.crt -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=rpiroot" + +openssl genrsa -out $SERVER_NAME/server.key 2048 +openssl req -new -out $SERVER_NAME/server.csr -key $SERVER_NAME/server.key -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=$SERVER_NAME" +openssl x509 -req -in $SERVER_NAME/server.csr -CA $SERVER_NAME/ca.crt -CAkey $SERVER_NAME/ca.key -CAcreateserial -out $SERVER_NAME/server.crt -days 9999 + +openssl genrsa -out $SERVER_NAME/client.key 2048 +openssl req -new -out $SERVER_NAME/client.csr -key $SERVER_NAME/client.key -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=$SERVER_NAME" +openssl x509 -req -in $SERVER_NAME/client.csr -CA $SERVER_NAME/ca.crt -CAkey $SERVER_NAME/ca.key -CAcreateserial -out $SERVER_NAME/client.crt -days 999 + +echo -n \#define DTLS_ROOT_CERT \" > $SERVER_NAME/dtls_client.inc +cat $SERVER_NAME/ca.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_client.inc +echo -e "\"\n" >> $SERVER_NAME/dtls_client.inc + +echo -n \#define DTLS_KEY \" >> $SERVER_NAME/dtls_client.inc +cat $SERVER_NAME/client.key | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_client.inc +echo -e "\"\n" >> $SERVER_NAME/dtls_client.inc + +echo -n \#define DTLS_CERT \" >> $SERVER_NAME/dtls_client.inc +cat $SERVER_NAME/client.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_client.inc +echo -e "\"\n" >> $SERVER_NAME/dtls_client.inc + +echo -n \#define DTLS_ROOT_CERT \" > $SERVER_NAME/dtls_server.inc +cat $SERVER_NAME/ca.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_server.inc +echo -e "\"\n" >> $SERVER_NAME/dtls_server.inc + +echo -n \#define DTLS_KEY \" >> $SERVER_NAME/dtls_server.inc +cat $SERVER_NAME/server.key | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_server.inc +echo -e "\"\n" >> $SERVER_NAME/dtls_server.inc + +echo -n \#define DTLS_CERT \" >> $SERVER_NAME/dtls_server.inc +cat $SERVER_NAME/server.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/dtls_server.inc +echo "\"" >> $SERVER_NAME/dtls_server.inc diff --git a/pico_w/wifi/dtls/dtls_common.c b/pico_w/wifi/dtls/dtls_common.c new file mode 100644 index 000000000..cebdd81b1 --- /dev/null +++ b/pico_w/wifi/dtls/dtls_common.c @@ -0,0 +1,378 @@ +#include +#include "dtls_common.h" + +#ifndef DTLS_CERT_INC +#error Need to define DTLS_CERT_INC +#endif +#include DTLS_CERT_INC + +static const uint8_t dtls_root_cert[] = DTLS_ROOT_CERT; +static const uint8_t dtls_key[] = DTLS_KEY; +static const uint8_t dtls_cert[] = DTLS_CERT; + +#include "mbedtls/net_sockets.h" + +// If the timeout is zero the dtls connection will not timeout +// If the timeout is set then the dtls connection will be dropped if no data is received within this time +// Having a timeout is useful as there's no way for the client/server to know if the other end disappears +// Having a timeout implies you need some sort of keep alive from the client to the server? +#ifndef DTLS_READ_TIMEOUT_MS +#define DTLS_READ_TIMEOUT_MS 30000 +#endif + +// 0 No debug +// 1 Error +// 2 State change +// 3 Informational +// 4 Verbose +#ifndef MBEDTLS_DEBUG_LEVEL +#define MBEDTLS_DEBUG_LEVEL 1 +#endif + +static void dtls_timer_callback(__unused async_context_t *context, async_at_time_worker_t *worker) { + DTLS_DEBUG("dtls_timer_callback\n"); + dtls_processing((dtls_session_t*)worker->user_data, true); +} + +static void dtls_timer_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms) +{ + pico_mbedtls_timer_context_t *ctx = (pico_mbedtls_timer_context_t*)data; + if (fin_ms == 0) { + async_context_remove_at_time_worker(ctx->async_context, &ctx->worker); + ctx->fin_time = nil_time; + DTLS_DEBUGV("dtls_timer_set_delay cancelled\n"); + return; + } + // The async worker will get called + DTLS_DEBUGV("dtls_timer_set_delay fin_ms=%u\n", fin_ms); + async_context_add_at_time_worker_in_ms(ctx->async_context, &ctx->worker, fin_ms); + ctx->fin_time = make_timeout_time_ms(fin_ms); + ctx->int_time = make_timeout_time_ms(int_ms); +} + +static int dtls_timer_get_delay(void *data) +{ + pico_mbedtls_timer_context_t *ctx = (pico_mbedtls_timer_context_t*)data; + if (is_nil_time(ctx->fin_time)) { + return -1 ; + } + if (time_reached(ctx->fin_time)) { + DTLS_DEBUG("dtls_timer_get_delay FIN\n"); + return 2; + } + if (time_reached(ctx->int_time)) { + DTLS_DEBUG("dtls_timer_get_delay INT\n"); + return 1; + } + return 0; +} + +static void init_dtls_timer_context(pico_mbedtls_timer_context_t *timer_ctx, async_context_t *async_context, dtls_session_t *session) { + timer_ctx->async_context = async_context; + timer_ctx->worker.do_work = dtls_timer_callback; + timer_ctx->worker.user_data = session; + timer_ctx->fin_time = nil_time; + mbedtls_ssl_set_timer_cb(&session->ssl, timer_ctx, dtls_timer_set_delay, dtls_timer_get_delay); +} + +static void ssl_debug(void *ctx, int level, const char *file, int line, const char *str) +{ + (void)ctx; + (void)level; + DTLS_DEBUG("%s:%04d: %s", file, line, str); +} + +static int bio_send(void *arg, const unsigned char *dataptr, size_t size) +{ + DTLS_DEBUG("bio_send %d\n", size); + dtls_session_t *session = (dtls_session_t*)arg; + assert(session && session->udp_pcb); + assert(session->port != 0 && !ip_addr_isany_val(session->addr)); + assert(session->active); + + int written = 0; + size_t size_left = size; + + while (size_left) { + u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF); + struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, write_len, PBUF_RAM); + if (!p) { + DTLS_ERROR("pbuf alloc failed\n"); + break; + } + memcpy(p->payload, dataptr, write_len); + int err = udp_sendto(session->udp_pcb, p, &session->addr, session->port); + DTLS_DEBUG("udp_sendto %d\n", err); + pbuf_free(p); + assert(err == ERR_OK); + if (err == ERR_OK) { + written += write_len; + size_left -= write_len; + dataptr += write_len; + } else if (err == ERR_MEM) { + break; + } else { + return MBEDTLS_ERR_NET_SEND_FAILED; + } + } + return written; +} + +static int bio_recv(void *arg, unsigned char *buf, size_t len) +{ + dtls_session_t *session = (dtls_session_t*)arg; + assert(session && session->udp_pcb); + + struct pbuf *p = session->rx; + if (!p || (p->len == 0 && !p->next)) { + if (p) { + pbuf_free(p); + } + session->rx = NULL; + return MBEDTLS_ERR_SSL_WANT_READ; + } + + uint16_t copy_len = (u16_t)LWIP_MIN(len, p->tot_len); + DTLS_DEBUG("bio_recv %d/%d\n", copy_len, len); + uint16_t ret = pbuf_copy_partial(p, buf, copy_len, 0); + p = pbuf_free_header(p, ret); + if (p && p->len == 0) { + session->rx = p->next; + p->next = NULL; + pbuf_free(p); + } + session->rx = p; + return ret; +} + +int dtls_base_init(dtls_base_t *base) +{ + int ret = ERR_OK; +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); +#endif + mbedtls_ssl_config_init(&base->conf); + mbedtls_ctr_drbg_init(&base->ctr_drbg); + mbedtls_ssl_conf_rng(&base->conf, mbedtls_ctr_drbg_random, &base->ctr_drbg); + mbedtls_ssl_conf_dbg(&base->conf, ssl_debug, NULL); + mbedtls_ssl_conf_read_timeout(&base->conf, DTLS_READ_TIMEOUT_MS); + mbedtls_entropy_init(&base->entropy); + if ((ret = mbedtls_ctr_drbg_seed(&base->ctr_drbg, mbedtls_entropy_func, &base->entropy, (const unsigned char *)CUSTOM_MBEDTLS_ENTROPY_PTR, CUSTOM_MBEDTLS_ENTROPY_LEN)) != 0) { + DTLS_ERROR("Failed to seed rnd %d", ret); + return ret; + } + mbedtls_x509_crt_init(&base->cert); + ret = mbedtls_x509_crt_parse(&base->cert, dtls_cert, sizeof(dtls_cert)); + if (ret != 0) { + DTLS_ERROR("Failed to parse client cert %d", ret); + return ret; + } + ret = mbedtls_x509_crt_parse(&base->cert, dtls_root_cert, sizeof(dtls_root_cert)); + if (ret != 0) { + DTLS_ERROR("Failed to parse ca cert %d", ret); + return ret; + } + mbedtls_ssl_conf_ca_chain(&base->conf, base->cert.next, NULL); + mbedtls_pk_init(&base->pkey); +#if MBEDTLS_VERSION_MAJOR < 3 + ret = mbedtls_pk_parse_key(&base->pkey, dtls_key, sizeof(dtls_key), NULL, 0); +#else + ret = mbedtls_pk_parse_key(&base->pkey, dtls_key, sizeof(dtls_key), NULL, 0, NULL, NULL); +#endif + if (ret != 0) { + DTLS_ERROR("Failed to parse key"); + return ret; + } + if ((ret = mbedtls_ssl_conf_own_cert(&base->conf, &base->cert, &base->pkey)) != 0) { + DTLS_ERROR("failed to load own cert %d", ret); + return ret; + } + mbedtls_ssl_conf_authmode(&base->conf, MBEDTLS_SSL_VERIFY_REQUIRED); +#ifdef DTLS_SERVER + if ((ret = mbedtls_ssl_config_defaults(&base->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + DTLS_ERROR("ssl client config failed %d", ret); + return ret; + } +#else + if ((ret = mbedtls_ssl_config_defaults(&base->conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + DTLS_ERROR("ssl server config failed %d", ret); + return ret; + } + mbedtls_ssl_cookie_init(&base->cookie_ctx); + if ((ret = mbedtls_ssl_cookie_setup(&base->cookie_ctx, mbedtls_ctr_drbg_random, &base->ctr_drbg)) != 0) { + DTLS_ERROR("cookie setup failed %d", ret); + return ret; + } + mbedtls_ssl_conf_dtls_cookies(&base->conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &base->cookie_ctx); +#endif + return ret; +} + +void dtls_base_deinit(dtls_base_t *base) +{ + mbedtls_x509_crt_free(&base->cert); + mbedtls_pk_free(&base->pkey); +#ifndef DTLS_SERVER + mbedtls_ssl_cookie_free(&base->cookie_ctx); +#endif + mbedtls_ssl_config_free(&base->conf); + mbedtls_ctr_drbg_free(&base->ctr_drbg); + mbedtls_entropy_free(&base->entropy); +} + +int dtls_session_init(dtls_session_t *session, dtls_base_t *base, async_context_t *context, const char *hostname) +{ + int ret; + mbedtls_ssl_init(&session->ssl); + if ((ret = mbedtls_ssl_setup(&session->ssl, &base->conf)) != 0) { + DTLS_ERROR("ssl setup failed %d", ret); + return ret; + } + mbedtls_ssl_set_bio(&session->ssl, session, bio_send, bio_recv, NULL); + init_dtls_timer_context(&base->timer_ctx, context, session); + if (hostname) { + if ((ret = mbedtls_ssl_set_hostname(&session->ssl, hostname)) != 0) { // server hostname + DTLS_ERROR("Failed to set host name %d", ret); + return ret; + } + } + return ret; +} + +void dtls_session_deinit(dtls_session_t *session) +{ + mbedtls_ssl_free(&session->ssl); +} + +// receive tls data +static int recv_data(dtls_session_t *session) +{ + DTLS_DEBUG("recv_data\n"); + int ret; + do { + struct pbuf *buf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL); + if (buf == NULL) { + DTLS_ERROR("pbuf_alloc failed\n"); + assert(0); // todo + return ERR_OK; + } + ret = mbedtls_ssl_read(&session->ssl, (unsigned char *)buf->payload, PBUF_POOL_BUFSIZE); + if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + pbuf_free(buf); + return ERR_OK; + } + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { // the other end is closing the connection + DTLS_INFO("Peer closed the connection\n"); + pbuf_free(buf); + return dtls_session_close(session); + } + if (ret < 0) { + DTLS_ERROR("mbedtls_ssl_read error -0x%X\n", -ret); + pbuf_free(buf); + return dtls_session_close(session); + } + if (ret > 0) { + assert(ret <= PBUF_POOL_BUFSIZE); + pbuf_realloc(buf, (u16_t)ret); + if (session->handle_data_rx_cb) { + int err = session->handle_data_rx_cb(session, buf); + if (err != ERR_OK) { + if (err == ERR_ABRT) { + return ERR_ABRT; + } + return ERR_OK; + } + } else { + pbuf_free(buf); + } + } else { + pbuf_free(buf); + } + } while (ret > 0 && session->active); + return ERR_OK; +} + +int dtls_processing(dtls_session_t *session, bool timeout) +{ + DTLS_DEBUG("dtls_processing timeout=%d\n", timeout); + int ret; + if (session->close_queued) { + dtls_session_close(session); + } + if (!session->handshake_done) { + ret = mbedtls_ssl_handshake(&session->ssl); + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // handshake not done, wait for more recv calls + return ERR_OK; + } + if (ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) { + // Handshake ok, client should reconnect with correct cookie + return dtls_session_close(session); + } + if (ret != 0) { + DTLS_ERROR("mbedtls_ssl_handshake failed: -0x%X\n", -ret); + dtls_session_close(session); + return ret; + } + if (session->handle_handshake_done_cb) { + session->handle_handshake_done_cb(session); // todo: handle error? + } + session->handshake_done = true; + } + if (session->rx == NULL && !timeout) { + DTLS_DEBUG("No data\n"); + return ERR_OK; + } + return recv_data(session); +} + +int dtls_session_close(dtls_session_t *session) { + // send notification + int ret = mbedtls_ssl_close_notify(&session->ssl); + DTLS_DEBUG("close_notify %d\n", ret); + if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + session->close_queued = true; + return ret; + } + if (session->handle_session_closed_cb) { + session->handle_session_closed_cb(session); + } + mbedtls_ssl_session_reset(&session->ssl); + session->close_queued = false; + session->handshake_done = false; + if (session->rx) { + pbuf_free(session->rx); + session->rx = NULL; + } + // clear client details + session->port = 0; + ip_addr_set_zero(&session->addr); + return ERR_OK; +} + +int dtls_write(dtls_session_t *session, const uint8_t *data, size_t data_len) { + int ret = mbedtls_ssl_write(&session->ssl, data, data_len); + assert(ret > 0); + if (ret < 0) { + DTLS_ERROR("mbedtls_ssl_write failed %d", ret); + } + return ret; +} + +int dtls_set_client(dtls_session_t *session, const ip_addr_t *addr, u16_t port) { + ip_addr_copy(session->addr, *addr); + session->port = port; + + char client_ip[48]; + size_t client_ip_len; + ipaddr_ntoa_r(addr, client_ip, sizeof(client_ip)); + client_ip_len = strlen(client_ip); + client_ip_len += snprintf(client_ip + client_ip_len, sizeof(client_ip) - client_ip_len, ":%u", port); + + // For HelloVerifyRequest cookies + int ret = mbedtls_ssl_set_client_transport_id(&session->ssl, (unsigned char *)client_ip, client_ip_len); + if (ret == ERR_OK) { + DTLS_DEBUG("New client %s\n", client_ip); + } + return ret; +} \ No newline at end of file diff --git a/pico_w/wifi/dtls/dtls_common.h b/pico_w/wifi/dtls/dtls_common.h new file mode 100644 index 000000000..1e40b1fc5 --- /dev/null +++ b/pico_w/wifi/dtls/dtls_common.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2023 Raspberry Pi Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _DTLS_COMMON_H_ +#define _DTLS_COMMON_H_ + +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/ssl_cookie.h" +#include "mbedtls/debug.h" + +#include "lwip/udp.h" +#include "pico/async_context.h" +#include "pico/time.h" + +#ifndef DTLS_PRINT +#define DTLS_PRINT printf +#endif +#define DTLS_DEBUGV(...) +#ifdef NDEBUG +#define DTLS_DEBUG(...) +#define DTLS_INFO(...) +#else +#define DTLS_DEBUG DTLS_PRINT +#define DTLS_INFO DTLS_PRINT +#endif +#define DTLS_ERROR DTLS_PRINT + +typedef struct pico_mbedtls_timer_context_t_ { + async_context_t *async_context; + async_at_time_worker_t worker; + absolute_time_t fin_time; + absolute_time_t int_time; +} pico_mbedtls_timer_context_t; + +typedef struct dtls_base_t_ { + mbedtls_ssl_config conf; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + pico_mbedtls_timer_context_t timer_ctx; + mbedtls_x509_crt cert; +#ifndef DTLS_SERVER + mbedtls_ssl_cookie_ctx cookie_ctx; +#endif + mbedtls_pk_context pkey; +} dtls_base_t; + +typedef struct dtls_session_t_ { + mbedtls_ssl_context ssl; + struct udp_pcb *udp_pcb; + struct pbuf *rx; + ip_addr_t addr; + u16_t port; + bool close_queued; + bool handshake_done; + bool active; + void *user_data; + // callbacks to the user + int (*handle_data_rx_cb)(struct dtls_session_t_*, struct pbuf*); + int (*handle_handshake_done_cb)(struct dtls_session_t_*); + void (*handle_session_closed_cb)(struct dtls_session_t_*); +} dtls_session_t; + + +int dtls_base_init(dtls_base_t *base); +void dtls_base_deinit(dtls_base_t *base); + +int dtls_session_init(dtls_session_t *session, dtls_base_t *base, async_context_t *context, const char *hostname); +int dtls_set_client(dtls_session_t *session, const ip_addr_t *addr, u16_t port); +int dtls_write(dtls_session_t *session, const uint8_t *data, size_t data_len); +int dtls_processing(dtls_session_t *session, bool timeout); +int dtls_session_close(dtls_session_t *session); +void dtls_session_deinit(dtls_session_t *session); + +#endif diff --git a/pico_w/wifi/dtls/dtls_echo_client.c b/pico_w/wifi/dtls/dtls_echo_client.c new file mode 100644 index 000000000..781a758d2 --- /dev/null +++ b/pico_w/wifi/dtls/dtls_echo_client.c @@ -0,0 +1,241 @@ +#include +#include +#include "pico/stdlib.h" + +#include "pico/cyw43_arch.h" +#include "pico/lwip_nosys.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/dns.h" + +#include "dtls_common.h" + +typedef struct client_state_t_ { + dtls_base_t base; + dtls_session_t session; + bool complete; + int test_string_id; + int expected_len; +} client_state_t; + +static const char *test_data[] = { + "if you can see this, it worked!", + "This is the second request", + "The quick brown fox jumped over the lazy dog", +}; +static bool test_passed = false; + +#ifndef DTLS_SERVER_PORT +#define DTLS_SERVER_PORT 4433 +#endif +#ifndef DTLS_SERVER +#error Need to define DTLS_SERVER +#endif + +// Forward declarations +static int client_data_rx(dtls_session_t *session, struct pbuf *p); +static int client_handshake_done(dtls_session_t *session); +static void client_session_closed(dtls_session_t *session); + +// Callback to receive data +static void client_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + client_state_t *state = (client_state_t*)arg; + assert(state && pcb && state->session.udp_pcb == pcb); + if (p == NULL) { + return; + } + if (state->session.port == 0 || ip_addr_isany_val(state->session.addr)) { + return; + } + + assert(port == state->session.port); + assert(ip_addr_cmp(addr, &state->session.addr)); + + if (state->session.rx == NULL) { + state->session.rx = p; + } else { + assert(p->tot_len + (int)p->len <= 0xFFFF); + pbuf_cat(state->session.rx, p); + } + + DTLS_DEBUG("client_udp_recv_cb %d\n", p->tot_len); + dtls_processing(&state->session, false); +} + +static int start_client(client_state_t *state, const ip_addr_t *addr, int port) +{ + assert(ip_addr_isany_val(state->session.addr) && !state->session.active); + // initialise the session + ip_addr_copy(state->session.addr, *addr); + state->session.port = port; + state->session.active = true; + state->session.user_data = state; + state->session.udp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + + // set the callbacks + state->session.handle_data_rx_cb = client_data_rx; + state->session.handle_handshake_done_cb = client_handshake_done; + state->session.handle_session_closed_cb = client_session_closed; + + if (!state->session.udp_pcb) { + DTLS_ERROR("Failed to allocate udp pcb"); + return ERR_MEM; + } + int ret = udp_connect(state->session.udp_pcb, addr, port); + if (ret != ERR_OK) { + DTLS_ERROR("udp_connect failed %d", ret); + return ret; + } + // set receive callback + udp_recv(state->session.udp_pcb, client_udp_recv_cb, state); + // start handshake + ret = dtls_processing(&state->session, false); + if (ret != ERR_OK) { + DTLS_ERROR("dtls_processing failed %d", ret); + return ret; + } + return ERR_OK; +} + +// Call back with a DNS result +static void server_address_found(const char *hostname, const ip_addr_t *addr, void *arg) { + client_state_t *state = (client_state_t*)arg; + if (addr) { + DTLS_DEBUG("server address %s\n", ipaddr_ntoa(addr)); + if (start_client(state, addr, DTLS_SERVER_PORT) != 0) { + panic("Client request failed"); + } + } else { + panic("dns request failed"); // todo: retry? + } +} + +// Send a string to the server +static int send_request(dtls_session_t *session, const char *string) { + // send some data to the server + DTLS_DEBUG("CLIENT OUT: %s\n", string); + int ret = dtls_write(session, (uint8_t*)string, strlen(string)); + if (ret < 0) { + return ret; + } + assert(ret == strlen(string)); + DTLS_DEBUG("client sent: %d bytes\n", ret); + client_state_t *state = (client_state_t*)session->user_data; + state->expected_len = ret; + return ERR_OK; +} + +// Handle received data +static int client_data_rx(dtls_session_t *session, struct pbuf *p) +{ + client_state_t *state = (client_state_t*)session->user_data; + assert(state->expected_len > 0); + uint16_t tot_len = p->tot_len; + // print data received + int offset = 0; + while(offset < tot_len) { + char data_in[32]; + uint16_t req_len = LWIP_MIN(tot_len - offset, sizeof(data_in)); + assert(req_len > 0); + uint16_t data_in_len = pbuf_copy_partial(p, data_in, sizeof(data_in), offset); + assert(data_in_len > 0); + DTLS_DEBUG("CLIENT IN: (got %d need %d) %.*s\n", data_in_len, state->expected_len, data_in_len, data_in); + // check the echo is correct + const uint16_t string_len = (uint16_t)strlen(test_data[state->test_string_id]); + if (strncmp(test_data[state->test_string_id] + string_len - state->expected_len, data_in, LWIP_MIN(state->expected_len, data_in_len)) != 0) { + DTLS_ERROR("Echo mismatch want \"%s\" got \"%s\"\n", test_data[state->test_string_id] + string_len - state->expected_len, data_in); + assert(false); + } + offset += data_in_len; + state->expected_len -= data_in_len; + } + pbuf_free(p); + if (state->expected_len <= 0) { + // Send the next request or close the session + client_state_t *state = (client_state_t*)session->user_data; + if (++state->test_string_id < count_of(test_data)) { + return send_request(session, test_data[state->test_string_id]); + } else { + test_passed = true; + return dtls_session_close(session); + } + } + return ERR_OK; +} + +// Called when handshake has completed +static int client_handshake_done(dtls_session_t *session) +{ + client_state_t *state = (client_state_t*)session->user_data; + return send_request(session, test_data[state->test_string_id]); +} + +// Called when session is actually ready to close +static void client_session_closed(dtls_session_t *session) +{ + client_state_t *state = (client_state_t*)session->user_data; + state->complete = true; + session->active = false; + // Get rid of udp pcb + if (session->udp_pcb) { + udp_remove(session->udp_pcb); + session->udp_pcb = NULL; + } +} + +int main() { + stdio_init_all(); + + // Create a context + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect\n"); + return 1; + } + DTLS_INFO("Client ip address %s\n", ipaddr_ntoa(&(netif_list->ip_addr))); + + client_state_t state; + memset(&state, 0, sizeof(state)); + + int ret = dtls_base_init(&state.base); + if (ret != ERR_OK) { + panic("Failed to initialise dtls"); + } + ret = dtls_session_init(&state.session, &state.base, cyw43_arch_async_context(), DTLS_SERVER); + if (ret != ERR_OK) { + panic("Failed to initialise dtls session"); + } + + cyw43_arch_lwip_begin(); + ip_addr_t addr; + ret = dns_gethostbyname(DTLS_SERVER, &addr, server_address_found, &state); + cyw43_arch_lwip_end(); + if (ret == ERR_OK) { + cyw43_arch_lwip_begin(); + ret = start_client(&state, &addr, DTLS_SERVER_PORT); + cyw43_arch_lwip_end(); + if (ret != 0) { + panic("client request failed %d", ret); + } + } else if (ret != ERR_INPROGRESS) { + panic("dns request failed"); + } + + while(!state.complete) { + cyw43_arch_poll(); + cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000)); + } + lwip_nosys_deinit(cyw43_arch_async_context()); + cyw43_arch_deinit(); + + dtls_session_deinit(&state.session); + dtls_base_deinit(&state.base); + + DTLS_INFO("All done. Test %s\n", test_passed ? "passed" : "failed"); + sleep_ms(100); +} diff --git a/pico_w/wifi/dtls/dtls_echo_server.c b/pico_w/wifi/dtls/dtls_echo_server.c new file mode 100755 index 000000000..ec5f367fc --- /dev/null +++ b/pico_w/wifi/dtls/dtls_echo_server.c @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2023 Raspberry Pi Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" + +#include "pico/cyw43_arch.h" +#include "pico/lwip_nosys.h" +#include "lwip/netif.h" + +#include "dtls_common.h" + +typedef struct server_state_t_ { + dtls_base_t base; + dtls_session_t session; + bool complete; +} server_state_t; + +#define LISTEN_PORT 4433 + +static int server_result(server_state_t *state, int status) { + if (status == 0) { + DTLS_DEBUG("test success\n"); + } else { + DTLS_DEBUG("test failed %d\n", status); + } + state->complete = true; + return dtls_session_close(&state->session); +} + +static int server_data_rx(dtls_session_t *session, struct pbuf *p) +{ + int ret = ERR_OK; + uint16_t tot_len = p->tot_len; + // Print data received + int offset = 0; + while(offset < tot_len) { + char data_in[32]; + uint16_t req_len = LWIP_MIN(tot_len - offset, sizeof(data_in)); + assert(req_len > 0); + (void)req_len; + uint16_t data_in_len = pbuf_copy_partial(p, data_in, sizeof(data_in), offset); + assert(data_in_len > 0); + DTLS_DEBUG("SERVER IN: %.*s\n", data_in_len, data_in); + offset += data_in_len; + } + // echo it back as a reply + offset = 0; + while(offset < tot_len) { + char data_out[32]; + uint16_t resp_len = LWIP_MIN(tot_len - offset, sizeof(data_out)); + assert(resp_len > 0); + (void)resp_len; + uint16_t data_out_len = pbuf_copy_partial(p, data_out, sizeof(data_out), offset); + assert(data_out_len > 0); + DTLS_DEBUG("SERVER OUT: %.*s\n", data_out_len, data_out); + int ret = dtls_write(session, (uint8_t*)data_out, data_out_len); + if (ret < 0) { + break; + } + DTLS_DEBUG("server sent: %d bytes\n", ret); + offset += data_out_len; + } + pbuf_free(p); + return ret; +} + +// Callback to receive data +static void server_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) +{ + server_state_t *state = (server_state_t*)arg; + assert(state && pcb && state->session.udp_pcb == pcb); + if (p == NULL) { + return; + } + // Ignore? + if (((state->session.port != 0) && (port != state->session.port)) || + (!ip_addr_isany_val(state->session.addr) && !ip_addr_eq(&state->session.addr, addr))) { + pbuf_free(p); + return; + } + // New client connected? + if (state->session.port == 0 && ip_addr_isany_val(state->session.addr)) { + int ret = dtls_set_client(&state->session, addr, port); + if (ret != ERR_OK) { + DTLS_ERROR("failed to set client details %d", ret); + pbuf_free(p); + return; + } + } + if (state->session.rx == NULL) { + state->session.rx = p; + } else { + assert(p->tot_len + (int)p->len <= 0xFFFF); + pbuf_cat(state->session.rx, p); + } + DTLS_DEBUG("server_udp_recv_cb %d\n", p->tot_len); + dtls_processing(&state->session, false); +} + +int main() { + stdio_init_all(); + + // Create a context + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect\n"); + return 1; + } + + server_state_t state; + memset(&state, 0, sizeof(state)); + + int ret = dtls_base_init(&state.base); + if (ret != ERR_OK) { + panic("Failed to initialise dtls"); + } + ret = dtls_session_init(&state.session, &state.base, cyw43_arch_async_context(), NULL); + if (ret != ERR_OK) { + panic("Failed to initialise dtls session"); + } + + DTLS_INFO("Waiting for remote connection on %s:%d\n", ipaddr_ntoa(&(netif_list->ip_addr)), LISTEN_PORT); + state.session.udp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (!state.session.udp_pcb) { + panic("Failed to allocate pcb"); + } + ret = udp_bind(state.session.udp_pcb, &(netif_list->ip_addr), LISTEN_PORT); + if (ret != ERR_OK) { + panic("bind failed"); + } + udp_recv(state.session.udp_pcb, server_udp_recv_cb, &state); + state.session.handle_data_rx_cb = server_data_rx; + state.session.active = true; + + while(!state.complete) { + cyw43_arch_poll(); + cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000)); + } + lwip_nosys_deinit(cyw43_arch_async_context()); + cyw43_arch_deinit(); + + dtls_base_deinit(&state.base); + dtls_session_deinit(&state.session); + + DTLS_INFO("All done\n"); + sleep_ms(100); +} diff --git a/pico_w/wifi/dtls/host/CMakeLists.txt b/pico_w/wifi/dtls/host/CMakeLists.txt new file mode 100644 index 000000000..dcfca7e5e --- /dev/null +++ b/pico_w/wifi/dtls/host/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.12) +project(pico_mbedtls_host_test C) + +option(ENABLE_TESTING "Build mbed TLS tests." OFF) +option(ENABLE_PROGRAMS "Build mbed TLS programs." OFF) + +# Pick DTLS server from environment +if (DEFINED ENV{DTLS_SERVER} AND (NOT DTLS_SERVER)) + set(DTLS_SERVER $ENV{DTLS_SERVER}) + message("Using DTLS_SERVER from environment ('${DTLS_SERVER}')") +endif() +if (NOT DTLS_SERVER) + message("Skipping DTLS example as DTLS_SERVER is not defined") + return() +endif() +set(DTLS_SERVER "${DTLS_SERVER}" CACHE INTERNAL "DTLS server for examples") +if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/../certs/${DTLS_SERVER}") + message("Generate DTLS certs by running certs/makecerts.sh") + return() +endif() + +if (DEFINED ENV{MBEDTLS_DIR} AND NOT MBEDTLS_DIR) + set(MBEDTLS_DIR $ENV{MBEDTLS_DIR}) +endif() +if (NOT MBEDTLS_DIR) + if (DEFINED ENV{PICO_SDK_PATH} AND NOT PICO_SDK_PATH) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + endif() + if (PICO_SDK_PATH) + set(MBEDTLS_DIR ${PICO_SDK_PATH}/lib/mbedtls) + else() + message(FATAL_ERROR "Need to set MBEDTLS_DIR to mbedtls source location") + endif() +endif() +add_subdirectory(${MBEDTLS_DIR} mbedtls) + +add_executable(dtls_host_echo_client + dtls_host_echo_client.c + ) +target_link_libraries(dtls_host_echo_client PRIVATE + mbedtls + ) +target_include_directories(dtls_host_echo_client PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/.. + ) +target_compile_definitions(dtls_host_echo_client PRIVATE + DTLS_SERVER=\"${DTLS_SERVER}\" + DTLS_CERT_INC=\"certs/${DTLS_SERVER}/dtls_client.inc\" + ) + +add_executable(dtls_host_echo_server + dtls_host_echo_server.c + ) +target_link_libraries(dtls_host_echo_server PRIVATE + mbedtls + ) +target_include_directories(dtls_host_echo_server PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/.. + ) +target_compile_definitions(dtls_host_echo_server PRIVATE + DTLS_CERT_INC=\"certs/${DTLS_SERVER}/dtls_server.inc\" + ) diff --git a/pico_w/wifi/dtls/host/client.sh b/pico_w/wifi/dtls/host/client.sh new file mode 100755 index 000000000..a1eaea720 --- /dev/null +++ b/pico_w/wifi/dtls/host/client.sh @@ -0,0 +1,16 @@ +#!/usr/bin/bash + +SERVER_PORT=4433 +SERVER_ADDR=${DTLS_SERVER:-$1} +if [ -z "$SERVER_ADDR" ]; then + echo Pass dtls server address as a parameter or set DTLS_SERVER + exit 1 +fi +CERT_FOLDER=../certs/$SERVER_ADDR +if [ ! -e $CERT_FOLDER/client.crt ]; then + echo Cannot find client certificate + exit 2 +fi +echo Connecting to $SERVER_ADDR +echo Enter some text to send. Enter \"Q\" to exit +openssl s_client -dtls -cert $CERT_FOLDER/client.crt -key $CERT_FOLDER/client.key -verifyCAfile $CERT_FOLDER/ca.crt -timeout -connect $SERVER_ADDR:${SERVER_PORT} diff --git a/pico_w/wifi/dtls/host/dtls_host_echo_client.c b/pico_w/wifi/dtls/host/dtls_host_echo_client.c new file mode 100755 index 000000000..16652bfd3 --- /dev/null +++ b/pico_w/wifi/dtls/host/dtls_host_echo_client.c @@ -0,0 +1,410 @@ +/* + * Simple DTLS client demonstration program + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#include +#define mbedtls_printf printf +#define mbedtls_fprintf fprintf +#define mbedtls_exit exit +#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS +#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE +#endif + +#if !defined(MBEDTLS_SSL_CLI_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) || \ + !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_TIMING_C) || \ + !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_CERTS_C) || !defined(MBEDTLS_PEM_PARSE_C) +int main( void ) +{ + mbedtls_printf( "MBEDTLS_SSL_CLI_C and/or MBEDTLS_SSL_PROTO_DTLS and/or " + "MBEDTLS_NET_C and/or MBEDTLS_TIMING_C and/or " + "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or " + "MBEDTLS_X509_CRT_PARSE_C and/or MBEDTLS_RSA_C and/or " + "MBEDTLS_CERTS_C and/or MBEDTLS_PEM_PARSE_C not defined.\n" ); + mbedtls_exit( 0 ); +} +#else + +#include + +#include "mbedtls/net_sockets.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" +#include "mbedtls/timing.h" + +#ifndef DTLS_CERT_INC +#error Need to define DTLS_CERT_INC +#endif +#include DTLS_CERT_INC + +static const uint8_t dtls_root_cert[] = DTLS_ROOT_CERT; +static const uint8_t dtls_key[] = DTLS_KEY; +static const uint8_t dtls_cert[] = DTLS_CERT; + +/* Uncomment out the following line to default to IPv4 and disable IPv6 */ +//#define FORCE_IPV4 + +#define SERVER_PORT "4433" + +#define MESSAGE "The quick brown fox jumped over the lazy dog" + +#define READ_TIMEOUT_MS 1000 +#define MAX_RETRY 5 + +#define DEBUG_LEVEL 0 + + +static void my_debug( void *ctx, int level, + const char *file, int line, + const char *str ) +{ + ((void) level); + + mbedtls_fprintf( (FILE *) ctx, "%s:%04d: %s", file, line, str ); + fflush( (FILE *) ctx ); +} + +int main( int argc, char *argv[] ) +{ + int ret, len; + mbedtls_net_context server_fd; + uint32_t flags; + unsigned char buf[1024]; + const char *pers = "dtls_client"; + int retry_left = MAX_RETRY; + const char *server_addr = DTLS_SERVER; + + if (argc >= 2) { + server_addr = argv[1]; + } + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt clicert; + mbedtls_pk_context pkey; + mbedtls_timing_delay_context timer; + + ((void) argc); + ((void) argv); + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold( DEBUG_LEVEL ); +#endif + + /* + * 0. Initialize the RNG and the session data + */ + mbedtls_net_init( &server_fd ); + mbedtls_ssl_init( &ssl ); + mbedtls_ssl_config_init( &conf ); + mbedtls_x509_crt_init( &clicert ); + mbedtls_pk_init( &pkey ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + + mbedtls_printf( "\n . Seeding the random number generator..." ); + fflush( stdout ); + + mbedtls_entropy_init( &entropy ); + if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen( pers ) ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* + * 0. Load the certificates and private RSA key + */ + printf( "\n . Loading the client cert. and key..." ); + fflush( stdout ); + + /* + * This demonstration program uses embedded test certificates. + * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the + * client and CA certificates, as well as mbedtls_pk_parse_keyfile(). + */ + ret = mbedtls_x509_crt_parse( &clicert, (const unsigned char *) dtls_cert, sizeof(dtls_cert)); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_x509_crt_parse( &clicert, (const unsigned char *) dtls_root_cert, sizeof(dtls_root_cert)); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_pk_parse_key( &pkey, (const unsigned char *) dtls_key, sizeof(dtls_key), NULL, 0 ); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_pk_parse_key returned %d\n\n", ret ); + goto exit; + } + + printf( " ok\n" ); + + /* + * 1. Start the connection + */ + mbedtls_printf( " . Connecting to udp/%s/%s...", DTLS_SERVER, SERVER_PORT ); + fflush( stdout ); + + if( ( ret = mbedtls_net_connect( &server_fd, server_addr, + SERVER_PORT, MBEDTLS_NET_PROTO_UDP ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_net_connect returned %d\n\n", ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* + * 2. Setup stuff + */ + mbedtls_printf( " . Setting up the DTLS structure..." ); + fflush( stdout ); + + if( ( ret = mbedtls_ssl_config_defaults( &conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_DATAGRAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret ); + goto exit; + } + + /* OPTIONAL is usually a bad choice for security, but makes interop easier + * in this simplified example, in which the ca chain is hardcoded. + * Production code should set a proper ca chain and use REQUIRED. */ + mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_REQUIRED ); + mbedtls_ssl_conf_ca_chain( &conf, clicert.next, NULL ); + if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &clicert, &pkey ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret ); + goto exit; + } + mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg ); + mbedtls_ssl_conf_dbg( &conf, my_debug, stdout ); + mbedtls_ssl_conf_read_timeout( &conf, READ_TIMEOUT_MS ); + + if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret ); + goto exit; + } + + if( ( ret = mbedtls_ssl_set_hostname( &ssl, DTLS_SERVER ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); + goto exit; + } + + mbedtls_ssl_set_bio( &ssl, &server_fd, + mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout ); + + mbedtls_ssl_set_timer_cb( &ssl, &timer, mbedtls_timing_set_delay, + mbedtls_timing_get_delay ); + + mbedtls_printf( " ok\n" ); + + /* + * 4. Handshake + */ + mbedtls_printf( " . Performing the DTLS handshake..." ); + fflush( stdout ); + + do ret = mbedtls_ssl_handshake( &ssl ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", (unsigned int) -ret ); + goto exit; + } + + mbedtls_printf( " ok\n" ); + + /* + * 5. Verify the server certificate + */ + mbedtls_printf( " . Verifying peer X.509 certificate..." ); + + /* In real life, we would have used MBEDTLS_SSL_VERIFY_REQUIRED so that the + * handshake would not succeed if the peer's cert is bad. Even if we used + * MBEDTLS_SSL_VERIFY_OPTIONAL, we would bail out here if ret != 0 */ + if( ( flags = mbedtls_ssl_get_verify_result( &ssl ) ) != 0 ) + { + char vrfy_buf[512]; + + mbedtls_printf( " failed\n" ); + + mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags ); + + mbedtls_printf( "%s\n", vrfy_buf ); + } + else + mbedtls_printf( " ok\n" ); + + /* + * 6. Write the echo request + */ +send_request: + mbedtls_printf( " > Write to server:" ); + fflush( stdout ); + + len = sizeof( MESSAGE ) - 1; + + do ret = mbedtls_ssl_write( &ssl, (unsigned char *) MESSAGE, len ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret < 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); + goto exit; + } + + len = ret; + mbedtls_printf( " %d bytes written\n\n%s\n\n", len, MESSAGE ); + + /* + * 7. Read the echo response + */ + mbedtls_printf( " < Read from server:" ); + fflush( stdout ); + + memset( buf, 0, sizeof( buf ) ); + size_t read_so_far = 0; + + while(read_so_far < sizeof(MESSAGE) - 1) + { + len = sizeof( buf ) - 1 - read_so_far; + do ret = mbedtls_ssl_read( &ssl, buf + read_so_far, len ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_TIMEOUT: + mbedtls_printf( " timeout\n\n" ); + if( retry_left-- > 0 ) + goto send_request; + goto exit; + + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + mbedtls_printf( " connection was closed gracefully\n" ); + ret = 0; + goto close_notify; + + default: + mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n\n", (unsigned int) -ret ); + goto exit; + } + } + + len = ret; + mbedtls_printf( " %d bytes read\n\n%s\n\n", len, buf + read_so_far); + read_so_far += len; + } + + /* + * print result + */ + if (strcmp(buf, MESSAGE) == 0) { + mbedtls_printf( "TEST SUCCESS" ); + } else { + mbedtls_printf( "TEST FAIL %s", buf ); + } + + /* + * 8. Done, cleanly close the connection + */ +close_notify: + mbedtls_printf( " . Closing the connection..." ); + + /* No error checking, the connection might be closed already */ + do ret = mbedtls_ssl_close_notify( &ssl ); + while( ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + ret = 0; + + mbedtls_printf( " done\n" ); + + /* + * 9. Final clean-ups and exit + */ +exit: + +#ifdef MBEDTLS_ERROR_C + if( ret != 0 ) + { + char error_buf[100]; + mbedtls_strerror( ret, error_buf, 100 ); + mbedtls_printf( "Last error was: %d - %s\n\n", ret, error_buf ); + } +#endif + + mbedtls_net_free( &server_fd ); + + mbedtls_x509_crt_free( &clicert ); + mbedtls_pk_free( &pkey ); + mbedtls_ssl_free( &ssl ); + mbedtls_ssl_config_free( &conf ); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + +#if defined(_WIN32) + mbedtls_printf( " + Press Enter to exit this program.\n" ); + fflush( stdout ); getchar(); +#endif + + /* Shell can not handle large exit numbers -> 1 for errors */ + if( ret < 0 ) + ret = 1; + + mbedtls_exit( ret ); +} +#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_PROTO_DTLS && MBEDTLS_NET_C && + MBEDTLD_TIMING_C && MBEDTLS_ENTROPY_C && MBEDTLS_CTR_DRBG_C && + MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_RSA_C && MBEDTLS_CERTS_C && + MBEDTLS_PEM_PARSE_C */ diff --git a/pico_w/wifi/dtls/host/dtls_host_echo_server.c b/pico_w/wifi/dtls/host/dtls_host_echo_server.c new file mode 100755 index 000000000..e9328ecbc --- /dev/null +++ b/pico_w/wifi/dtls/host/dtls_host_echo_server.c @@ -0,0 +1,471 @@ +/* + * Simple DTLS server demonstration program + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#include +#define mbedtls_printf printf +#define mbedtls_fprintf fprintf +#define mbedtls_time_t time_t +#define mbedtls_exit exit +#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS +#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE +#endif + +/* Uncomment out the following line to default to IPv4 and disable IPv6 */ +//#define FORCE_IPV4 + +#ifdef FORCE_IPV4 +#define BIND_IP "0.0.0.0" /* Forces IPv4 */ +#else +#define BIND_IP "::" +#endif + +#if !defined(MBEDTLS_SSL_SRV_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) || \ + !defined(MBEDTLS_SSL_COOKIE_C) || !defined(MBEDTLS_NET_C) || \ + !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_CERTS_C) || !defined(MBEDTLS_PEM_PARSE_C) || \ + !defined(MBEDTLS_TIMING_C) + +int main( void ) +{ + printf( "MBEDTLS_SSL_SRV_C and/or MBEDTLS_SSL_PROTO_DTLS and/or " + "MBEDTLS_SSL_COOKIE_C and/or MBEDTLS_NET_C and/or " + "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or " + "MBEDTLS_X509_CRT_PARSE_C and/or MBEDTLS_RSA_C and/or " + "MBEDTLS_CERTS_C and/or MBEDTLS_PEM_PARSE_C and/or " + "MBEDTLS_TIMING_C not defined.\n" ); + mbedtls_exit( 0 ); +} +#else + +#if defined(_WIN32) +#include +#endif + +#include +#include +#include + +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/certs.h" +#include "mbedtls/x509.h" +#include "mbedtls/ssl.h" +#include "mbedtls/ssl_cookie.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/error.h" +#include "mbedtls/debug.h" +#include "mbedtls/timing.h" + +#if defined(MBEDTLS_SSL_CACHE_C) +#include "mbedtls/ssl_cache.h" +#endif + +#ifndef DTLS_CERT_INC +#error Need to define DTLS_CERT_INC +#endif +#include DTLS_CERT_INC + +static const uint8_t dtls_root_cert[] = DTLS_ROOT_CERT; +static const uint8_t dtls_key[] = DTLS_KEY; +static const uint8_t dtls_cert[] = DTLS_CERT; + +#define READ_TIMEOUT_MS 10000 /* 10 seconds */ +#define DEBUG_LEVEL 0 + +static void my_debug( void *ctx, int level, + const char *file, int line, + const char *str ) +{ + ((void) level); + + mbedtls_fprintf( (FILE *) ctx, "%s:%04d: %s", file, line, str ); + fflush( (FILE *) ctx ); +} + +int main( void ) +{ + int ret, len; + mbedtls_net_context listen_fd, client_fd; + unsigned char buf[1024]; + const char *pers = "dtls_server"; + unsigned char client_ip[16] = { 0 }; + size_t cliip_len; + mbedtls_ssl_cookie_ctx cookie_ctx; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; + mbedtls_timing_delay_context timer; +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_context cache; +#endif + + mbedtls_net_init( &listen_fd ); + mbedtls_net_init( &client_fd ); + mbedtls_ssl_init( &ssl ); + mbedtls_ssl_config_init( &conf ); + mbedtls_ssl_cookie_init( &cookie_ctx ); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_init( &cache ); +#endif + mbedtls_x509_crt_init( &srvcert ); + mbedtls_pk_init( &pkey ); + mbedtls_entropy_init( &entropy ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold( DEBUG_LEVEL ); +#endif + + /* + * 1. Load the certificates and private RSA key + */ + printf( "\n . Loading the server cert. and key..." ); + fflush( stdout ); + + /* + * This demonstration program uses embedded test certificates. + * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the + * server and CA certificates, as well as mbedtls_pk_parse_keyfile(). + */ + ret = mbedtls_x509_crt_parse( &srvcert, (const unsigned char *) dtls_cert, sizeof(dtls_cert)); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_x509_crt_parse( &srvcert, (const unsigned char *) dtls_root_cert, sizeof(dtls_root_cert)); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_pk_parse_key( &pkey, (const unsigned char *) dtls_key, sizeof(dtls_key), NULL, 0 ); + if( ret != 0 ) + { + printf( " failed\n ! mbedtls_pk_parse_key returned %d\n\n", ret ); + goto exit; + } + + printf( " ok\n" ); + + /* + * 2. Setup the "listening" UDP socket + */ + printf( " . Bind on udp/*/4433 ..." ); + fflush( stdout ); + + if( ( ret = mbedtls_net_bind( &listen_fd, BIND_IP, "4433", MBEDTLS_NET_PROTO_UDP ) ) != 0 ) + { + printf( " failed\n ! mbedtls_net_bind returned %d\n\n", ret ); + goto exit; + } + + printf( " ok\n" ); + + /* + * 3. Seed the RNG + */ + printf( " . Seeding the random number generator..." ); + fflush( stdout ); + + if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen( pers ) ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret ); + goto exit; + } + + printf( " ok\n" ); + + /* + * 4. Setup stuff + */ + printf( " . Setting up the DTLS data..." ); + fflush( stdout ); + + if( ( ret = mbedtls_ssl_config_defaults( &conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_DATAGRAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret ); + goto exit; + } + + mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg ); + mbedtls_ssl_conf_dbg( &conf, my_debug, stdout ); + mbedtls_ssl_conf_read_timeout( &conf, READ_TIMEOUT_MS ); + +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_conf_session_cache( &conf, &cache, + mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set ); +#endif + + mbedtls_ssl_conf_ca_chain( &conf, srvcert.next, NULL ); + if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret ); + goto exit; + } + + if( ( ret = mbedtls_ssl_cookie_setup( &cookie_ctx, + mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_cookie_setup returned %d\n\n", ret ); + goto exit; + } + + mbedtls_ssl_conf_dtls_cookies( &conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, + &cookie_ctx ); + + if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret ); + goto exit; + } + + mbedtls_ssl_set_timer_cb( &ssl, &timer, mbedtls_timing_set_delay, + mbedtls_timing_get_delay ); + + printf( " ok\n" ); + +reset: +#ifdef MBEDTLS_ERROR_C + if( ret != 0 ) + { + char error_buf[100]; + mbedtls_strerror( ret, error_buf, 100 ); + printf("Last error was: %d - %s\n\n", ret, error_buf ); + } +#endif + + mbedtls_net_free( &client_fd ); + + mbedtls_ssl_session_reset( &ssl ); + + /* + * 3. Wait until a client connects + */ + printf( " . Waiting for a remote connection ..." ); + fflush( stdout ); + + if( ( ret = mbedtls_net_accept( &listen_fd, &client_fd, + client_ip, sizeof( client_ip ), &cliip_len ) ) != 0 ) + { + printf( " failed\n ! mbedtls_net_accept returned %d\n\n", ret ); + goto exit; + } + + /* For HelloVerifyRequest cookies */ + if( ( ret = mbedtls_ssl_set_client_transport_id( &ssl, + client_ip, cliip_len ) ) != 0 ) + { + printf( " failed\n ! " + "mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n", (unsigned int) -ret ); + goto exit; + } + + mbedtls_ssl_set_bio( &ssl, &client_fd, + mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout ); + + printf( " ok\n" ); + + /* + * 5. Handshake + */ + printf( " . Performing the DTLS handshake..." ); + fflush( stdout ); + + do ret = mbedtls_ssl_handshake( &ssl ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ) + { + printf( " hello verification requested\n" ); + ret = 0; + goto reset; + } + else if( ret != 0 ) + { + printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", (unsigned int) -ret ); + goto reset; + } + + printf( " ok\n" ); + +read_echo: + /* + * 6. Read the echo Request + */ + printf( " < Read from client:" ); + fflush( stdout ); + + len = sizeof( buf ) - 1; + memset( buf, 0, sizeof( buf ) ); + + do ret = mbedtls_ssl_read( &ssl, buf, len ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_TIMEOUT: + printf( " timeout\n\n" ); + goto reset; + + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + printf( " connection was closed gracefully\n" ); + ret = 0; + goto close_notify; + + default: + printf( " mbedtls_ssl_read returned -0x%x\n\n", (unsigned int) -ret ); + goto reset; + } + } + + len = ret; + printf( " %d bytes read\n\n%s\n\n", len, buf ); + + /* + * 7. Echo back + */ + printf( " > Write to client:" ); + fflush( stdout ); + + do ret = mbedtls_ssl_write( &ssl, buf, len ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret < 0 ) + { + printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); + goto exit; + } + + len = ret; + printf( " %d bytes written\n\n%s\n\n", len, buf ); + goto read_echo; + + // Wait for the client to close the connection + while(1) { + do ret = mbedtls_ssl_read( &ssl, buf, len ); + while( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_TIMEOUT: + printf( " timeout\n\n" ); + goto reset; + + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + printf( " connection was closed gracefully\n" ); + ret = 0; + goto close_notify; + break; + + default: + printf( " mbedtls_ssl_read returned -0x%x\n\n", (unsigned int) -ret ); + goto reset; + } + } + } + + /* + * 8. Done, cleanly close the connection + */ +close_notify: + printf( " . Closing the connection..." ); + + /* No error checking, the connection might be closed already */ + do ret = mbedtls_ssl_close_notify( &ssl ); + while( ret == MBEDTLS_ERR_SSL_WANT_WRITE ); + ret = 0; + + printf( " done\n" ); + + goto reset; + + /* + * Final clean-ups and exit + */ +exit: + +#ifdef MBEDTLS_ERROR_C + if( ret != 0 ) + { + char error_buf[100]; + mbedtls_strerror( ret, error_buf, 100 ); + printf( "Last error was: %d - %s\n\n", ret, error_buf ); + } +#endif + + mbedtls_net_free( &client_fd ); + mbedtls_net_free( &listen_fd ); + + mbedtls_x509_crt_free( &srvcert ); + mbedtls_pk_free( &pkey ); + mbedtls_ssl_free( &ssl ); + mbedtls_ssl_config_free( &conf ); + mbedtls_ssl_cookie_free( &cookie_ctx ); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_free( &cache ); +#endif + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + +#if defined(_WIN32) + printf( " Press Enter to exit this program.\n" ); + fflush( stdout ); getchar(); +#endif + + /* Shell can not handle large exit numbers -> 1 for errors */ + if( ret < 0 ) + ret = 1; + + mbedtls_exit( ret ); +} +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_PROTO_DTLS && + MBEDTLS_SSL_COOKIE_C && MBEDTLS_NET_C && MBEDTLS_ENTROPY_C && + MBEDTLS_CTR_DRBG_C && MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_RSA_C + && MBEDTLS_CERTS_C && MBEDTLS_PEM_PARSE_C && MBEDTLS_TIMING_C */ diff --git a/pico_w/wifi/dtls/host/server.sh b/pico_w/wifi/dtls/host/server.sh new file mode 100755 index 000000000..853fe55f1 --- /dev/null +++ b/pico_w/wifi/dtls/host/server.sh @@ -0,0 +1,14 @@ +#!/usr/bin/bash + +SERVER_ADDR=${DTLS_SERVER:-$1} +if [ -z "$SERVER_ADDR" ]; then + echo Pass dtls server address as a parameter or set DTLS_SERVER + exit 1 +fi +CERT_FOLDER=../certs/$SERVER_ADDR +if [ ! -e $CERT_FOLDER/server.crt ]; then + echo Cannot find server certificate + exit 2 +fi +echo Waiting for client to connect. Manually echo text received +openssl s_server -dtls -cert $CERT_FOLDER/server.crt -key $CERT_FOLDER/server.key -verifyCAfile $CERT_FOLDER/ca.crt -timeout diff --git a/pico_w/wifi/dtls/lwipopts.h b/pico_w/wifi/dtls/lwipopts.h new file mode 100644 index 000000000..4a5a2ef50 --- /dev/null +++ b/pico_w/wifi/dtls/lwipopts.h @@ -0,0 +1,12 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +#define MEM_SIZE 8000 + +#include "lwipopts_examples_common.h" + +#define LWIP_DEBUG 1 +#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_ON + +#endif + diff --git a/pico_w/wifi/dtls/mbedtls_config.h b/pico_w/wifi/dtls/mbedtls_config.h new file mode 100644 index 000000000..1ec01f6ab --- /dev/null +++ b/pico_w/wifi/dtls/mbedtls_config.h @@ -0,0 +1,11 @@ +#ifndef MBEDTLS_CONFIG_TLS_CLIENT_H +#define MBEDTLS_CONFIG_TLS_CLIENT_H + +#include "mbedtls_config_examples_common.h" + +// Needed for dtls +#define MBEDTLS_SSL_PROTO_DTLS +#define MBEDTLS_SSL_COOKIE_C +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + +#endif \ No newline at end of file