From 517340d79ab164fdc40e5115f36644b92f6de1ee Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Sat, 13 Apr 2019 12:15:28 +0100 Subject: [PATCH] Add DTLS support to libcoap using TinyDTLS As TinyDTLS is already a submodule to libcoap, adding in DTLS support is straightforward, but only supports PSK at this point in time. components/coap/CMakeLists.txt: components/coap/component.mk: Add in the new files that have to be built Replace libcoap/src/coap_notls.c with libcoap/src/coap_tinydtls.c components/coap/libcoap: Update the version to include the correct version of tinydtls submodule components/coap/port/include/coap/dtls_config.h: New port file for DTLS components/coap/port/include/coap_config_posix.h: components/coap/port/rijndael.c Include building with TinyDTLS examples/protocols/coap_client/README.md: examples/protocols/coap_client/main/Kconfig.projbuild: examples/protocols/coap_client/main/coap_client_example_main.c: Update CoAP client to support DTLS examples/protocols/coap_server/README.md: examples/protocols/coap_server/main/Kconfig.projbuild: examples/protocols/coap_server/main/coap_server_example_main.c: Update CoAP server to support DTLS Change "no data" to "Hello World!" to prevent confusion See Issue #1379 --- components/coap/CMakeLists.txt | 19 ++- components/coap/component.mk | 8 +- components/coap/port/coap_io.c | 50 +++++- .../coap/port/include/coap/dtls_config.h | 156 ++++++++++++++++++ .../coap/port/include/coap_config_posix.h | 7 + components/coap/port/rijndael.c | 82 +++++++++ examples/protocols/coap_client/README.md | 22 ++- .../coap_client/main/Kconfig.projbuild | 19 ++- .../main/coap_client_example_main.c | 56 ++++++- examples/protocols/coap_server/README.md | 25 ++- .../coap_server/main/Kconfig.projbuild | 10 +- .../main/coap_server_example_main.c | 60 +++++-- 12 files changed, 468 insertions(+), 46 deletions(-) create mode 100644 components/coap/port/include/coap/dtls_config.h create mode 100644 components/coap/port/rijndael.c diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 1ec70b4990fc..2f8a8fc930f5 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_ADD_INCLUDEDIRS port/include port/include/coap libcoap/include libcoap/include/coap2) +set(COMPONENT_ADD_INCLUDEDIRS port/include port/include/coap libcoap/include libcoap/include/coap2 libcoap/ext/tinydtls libcoap/ext/tinydtls/aes) set(COMPONENT_SRCS "libcoap/src/address.c" "libcoap/src/async.c" @@ -17,8 +17,21 @@ set(COMPONENT_SRCS "libcoap/src/address.c" "libcoap/src/str.c" "libcoap/src/subscribe.c" "libcoap/src/uri.c" - "libcoap/src/coap_notls.c" - "port/coap_io.c") + "libcoap/src/coap_tinydtls.c" + "port/coap_io.c" + "libcoap/ext/tinydtls/ccm.c" + "libcoap/ext/tinydtls/crypto.c" + "libcoap/ext/tinydtls/dtls.c" + "libcoap/ext/tinydtls/dtls_debug.c" + "libcoap/ext/tinydtls/dtls_prng.c" + "libcoap/ext/tinydtls/dtls_time.c" + "libcoap/ext/tinydtls/hmac.c" + "libcoap/ext/tinydtls/netq.c" + "libcoap/ext/tinydtls/peer.c" + "libcoap/ext/tinydtls/session.c" + "port/rijndael.c" + "libcoap/ext/tinydtls/ecc/ecc.c" + "libcoap/ext/tinydtls/sha2/sha2.c") set(COMPONENT_REQUIRES lwip) diff --git a/components/coap/component.mk b/components/coap/component.mk index 2eb07afd2c95..60a6d1540469 100644 --- a/components/coap/component.mk +++ b/components/coap/component.mk @@ -2,11 +2,13 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2 +COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2 libcoap/ext/tinydtls libcoap/ext/tinydtls/aes -COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o libcoap/src/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o libcoap/src/coap_notls.o port/coap_io.o +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o libcoap/src/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o libcoap/src/coap_tinydtls.o port/coap_io.o \ +libcoap/ext/tinydtls/ccm.o libcoap/ext/tinydtls/crypto.o libcoap/ext/tinydtls/dtls.o libcoap/ext/tinydtls/dtls_debug.o libcoap/ext/tinydtls/dtls_prng.o libcoap/ext/tinydtls/dtls_time.o libcoap/ext/tinydtls/hmac.o libcoap/ext/tinydtls/netq.o libcoap/ext/tinydtls/peer.o libcoap/ext/tinydtls/session.o \ +port/rijndael.o libcoap/ext/tinydtls/ecc/ecc.o libcoap/ext/tinydtls/sha2/sha2.o -COMPONENT_SRCDIRS := libcoap/src libcoap port +COMPONENT_SRCDIRS := libcoap/ext/tinydtls/aes libcoap/ext/tinydtls/ecc libcoap/ext/tinydtls/sha2 libcoap/ext/tinydtls libcoap/src libcoap port COMPONENT_SUBMODULES += libcoap diff --git a/components/coap/port/coap_io.c b/components/coap/port/coap_io.c index 58391add6bb9..87bdd38b1aba 100644 --- a/components/coap/port/coap_io.c +++ b/components/coap/port/coap_io.c @@ -53,6 +53,7 @@ #include "pdu.h" #include "utlist.h" #include "resource.h" +#include "coap_mutex.h" #if !defined(WITH_CONTIKI) /* define generic PKTINFO for IPv4 */ @@ -971,6 +972,7 @@ coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { #ifndef COAP_BAD_RECVMSG /* a buffer large enough to hold all packet info types, ipv6 is the largest */ char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct cmsghdr *cmsg; struct msghdr mhdr; struct iovec iov[1]; @@ -987,6 +989,12 @@ coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { mhdr.msg_control = buf; mhdr.msg_controllen = sizeof(buf); + /* set a big first length incase recvmsg() does not implement updating + msg_control as well as preset the first cmsg with bad data */ + cmsg = (struct cmsghdr *)buf; + cmsg->cmsg_len = CMSG_LEN(sizeof(buf)); + cmsg->cmsg_level = -1; + cmsg->cmsg_type = -1; #if defined(_WIN32) if (!lpWSARecvMsg) { @@ -1023,6 +1031,7 @@ coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { } else { #ifndef COAP_BAD_RECVMSG struct cmsghdr *cmsg; + int dst_found = 0; packet->src.size = mhdr.msg_namelen; packet->length = (size_t)len; @@ -1040,6 +1049,7 @@ coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { u.c = CMSG_DATA(cmsg); packet->ifindex = (int)(u.p->ipi6_ifindex); memcpy(&packet->dst.addr.sin6.sin6_addr, &u.p->ipi6_addr, sizeof(struct in6_addr)); + dst_found = 1; break; } @@ -1060,15 +1070,34 @@ coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { } else { memcpy(&packet->dst.addr.sin.sin_addr, &u.p->ipi_addr, sizeof(struct in_addr)); } + dst_found = 1; break; } #elif defined(IP_RECVDSTADDR) if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { - packet->ifindex = 0; + packet->ifindex = sock->fd; memcpy(&packet->dst.addr.sin.sin_addr, CMSG_DATA(cmsg), sizeof(struct in_addr)); + dst_found = 1; break; } #endif /* IP_PKTINFO */ + if (!dst_found) { + /* cmsg_level / cmsg_type combination we do not understand + (ignore preset case for bad recvmsg() not updating cmsg) */ + if (cmsg->cmsg_level != -1 && cmsg->cmsg_type != -1) { + coap_log(LOG_DEBUG, + "cmsg_level = %d and cmsg_type = %d not supported - fix\n", + cmsg->cmsg_level, cmsg->cmsg_type); + } + } + } + if (!dst_found) { + /* Not expected, but cmsg_level and cmsg_type don't match above and + may need a new case */ + packet->ifindex = sock->fd; + if (getsockname(sock->fd, &packet->dst.addr.sa, &packet->dst.size) < 0) { + coap_log(LOG_DEBUG, "Cannot determine local port\n"); + } } #else /* COAP_BAD_RECVMSG */ packet->length = (size_t)len; @@ -1304,14 +1333,24 @@ coap_write(coap_context_t *ctx, int coap_run_once(coap_context_t *ctx, unsigned timeout_ms) { +#if COAP_CONSTRAINED_STACK + static coap_mutex_t static_mutex = COAP_MUTEX_INITIALIZER; + static fd_set readfds, writefds, exceptfds; + static coap_socket_t *sockets[64]; +#else /* ! COAP_CONSTRAINED_STACK */ fd_set readfds, writefds, exceptfds; + coap_socket_t *sockets[64]; +#endif /* ! COAP_CONSTRAINED_STACK */ coap_fd_t nfds = 0; struct timeval tv; coap_tick_t before, now; int result; - coap_socket_t *sockets[64]; unsigned int num_sockets = 0, i, timeout; +#if COAP_CONSTRAINED_STACK + coap_mutex_lock(&static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + coap_ticks(&before); timeout = coap_write(ctx, sockets, (unsigned int)(sizeof(sockets) / sizeof(sockets[0])), &num_sockets, before); @@ -1350,6 +1389,9 @@ coap_run_once(coap_context_t *ctx, unsigned timeout_ms) { if (errno != EINTR) { #endif coap_log(LOG_DEBUG, "%s", coap_socket_strerror()); +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ return -1; } } @@ -1370,6 +1412,10 @@ coap_run_once(coap_context_t *ctx, unsigned timeout_ms) { coap_ticks(&now); coap_read(ctx, now); +#if COAP_CONSTRAINED_STACK + coap_mutex_unlock(&static_mutex); +#endif /* COAP_CONSTRAINED_STACK */ + return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); } diff --git a/components/coap/port/include/coap/dtls_config.h b/components/coap/port/include/coap/dtls_config.h new file mode 100644 index 000000000000..df72fe1c1e6d --- /dev/null +++ b/components/coap/port/include/coap/dtls_config.h @@ -0,0 +1,156 @@ +/* dtls_config.h. Generated from dtls_config.h.in by configure. */ +/* dtls_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to 1 if building with ECC support. */ +#define DTLS_ECC 1 + +/* Define to 1 if building with PSK support */ +#define DTLS_PSK 1 + +/* Define to 1 if you have the header file. */ +#ifndef HAVE_ARPA_INET_H +#define HAVE_ARPA_INET_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef HAVE_ASSERT_H +#define HAVE_ASSERT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fls' function. */ +/* #undef HAVE_FLS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if struct sockaddr_in6 has a member sin6_len. */ +/* #undef HAVE_SOCKADDR_IN6_SIN6_LEN */ + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#ifndef HAVE_SYS_SOCKET_H +#define HAVE_SYS_SOCKET_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#ifndef HAVE_TIME_H +#define HAVE_TIME_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "tinydtls" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "tinydtls 0.8.6" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "tinydtls" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.8.6" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +#define WITH_SHA256 +#define SHA2_USE_INTTYPES_H +#define DTLSv12 +#define DTLS_CHECK_CONTENTTYPE +#define DTLS_CONSTRAINED_STACK 1 +#define ESPIDF_VERSION +#define DTLS_EXT_RIJNDAEL + +#define rijndaelEncrypt rijndaelEncrypt_dtls +#define rijndaelKeySetupEnc rijndaelKeySetupEnc_dtls diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h index d675b7a58399..a686cfba33e8 100644 --- a/components/coap/port/include/coap_config_posix.h +++ b/components/coap/port/include/coap_config_posix.h @@ -21,6 +21,7 @@ #ifdef WITH_POSIX #include +#include #define HAVE_SYS_SOCKET_H #define HAVE_MALLOC @@ -34,6 +35,12 @@ #define PACKAGE_VERSION "?" #define COAP_BAD_RECVMSG +#define HAVE_LIBTINYDTLS +#define COAP_CONSTRAINED_STACK 1 +#define ESPIDF_VERSION +#define DTLS_EXT_RIJNDAEL + +#define gai_strerror(x) "gai_strerror() not supported" #endif /* WITH_POSIX */ #endif /* COAP_CONFIG_POSIX_H_ */ diff --git a/components/coap/port/rijndael.c b/components/coap/port/rijndael.c new file mode 100644 index 000000000000..1296bc62506d --- /dev/null +++ b/components/coap/port/rijndael.c @@ -0,0 +1,82 @@ +/* $OpenBSD: rijndael.c,v 1.19 2008/06/09 07:49:45 djm Exp $ */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* #include */ +/* #include */ + +#include "rijndael.h" + +/* setup key context for encryption only */ +int +rijndael_set_key_enc_only(rijndael_ctx *ctx, const u_char *key, int bits) +{ + int rounds; + + rounds = rijndaelKeySetupEnc(ctx->ek, key, bits); + if (rounds == 0) + return -1; + + ctx->Nr = rounds; +#ifdef WITH_AES_DECRYPT + ctx->enc_only = 1; +#endif + + return 0; +} + +#ifdef WITH_AES_DECRYPT +/* setup key context for both encryption and decryption */ +int +rijndael_set_key(rijndael_ctx *ctx, const u_char *key, int bits) +{ + int rounds; + + rounds = rijndaelKeySetupEnc(ctx->ek, key, bits); + if (rounds == 0) + return -1; + if (rijndaelKeySetupDec(ctx->dk, key, bits) != rounds) + return -1; + + ctx->Nr = rounds; + ctx->enc_only = 0; + + return 0; +} + +void +rijndael_decrypt(rijndael_ctx *ctx, const u_char *src, u_char *dst) +{ + rijndaelDecrypt(ctx->dk, ctx->Nr, src, dst); +} +#endif + +void +rijndael_encrypt(rijndael_ctx *ctx, const u_char *src, u_char *dst) +{ + rijndaelEncrypt(ctx->ek, ctx->Nr, src, dst); +} diff --git a/examples/protocols/coap_client/README.md b/examples/protocols/coap_client/README.md index d058e3c3114e..f7f318cd9663 100644 --- a/examples/protocols/coap_client/README.md +++ b/examples/protocols/coap_client/README.md @@ -2,14 +2,22 @@ # CoAP client example (See the README.md file in the upper level 'examples' directory for more information about examples.) -this CoAP client example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example. +This CoAP client example is very simplified adaptation of one of the +[libcoap](https://github.com/obgm/libcoap) examples. -CoAP client example would connect your ESP32 device to any CoAP server, fetch data from CoAP server and upstream data to CoAP server. +CoAP client example will connect your ESP32 device to a CoAP server, send off a GET request and +fetch the response data from CoAP server. The client can be extended to PUT / POST / DELETE requests, +as well as supporting the Observer extensions [RFC7641](https://tools.ietf.org/html/rfc7641). -The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things. -The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation. +If the URI is prefixed with coaps:// instead of coap://, then the CoAP client will attempt to use +the DTLS protocol using the defined Preshared Keys which the CoAP server needs to know about. -please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. +The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with +constrained nodes and constrained networks in the Internet of Things. +The protocol is designed for machine-to-machine (M2M) applications such as smart energy and +building automation. + +Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. ## How to use example @@ -23,6 +31,8 @@ make menuconfig * Set Target Uri under Example Configuration * Set WiFi SSID under Example Configuration * Set WiFi Password under Example Configuration +* Set Preshared Key to use in connection to the server +* Set PSK Client identity (username) ### Build and Flash @@ -77,4 +87,4 @@ with `coap://` or `coap+tcp://` for a coap server that supports TCP (not all do including coap+tcp://californium.eclipse.org). * libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` -to `#define COAP_LOGGING_LEVEL 9` +to `#define COAP_LOGGING_LEVEL 7` diff --git a/examples/protocols/coap_client/main/Kconfig.projbuild b/examples/protocols/coap_client/main/Kconfig.projbuild index 05a0d714f59d..72198a105c76 100644 --- a/examples/protocols/coap_client/main/Kconfig.projbuild +++ b/examples/protocols/coap_client/main/Kconfig.projbuild @@ -4,7 +4,7 @@ menu "Example Configuration" string "Target Uri" default "coap://californium.eclipse.org" help - Target uri for the example to use. + Target uri for the example to use. Use coaps:// prefix for encrypted traffic using the PSK. config WIFI_SSID string "WiFi SSID" @@ -18,4 +18,19 @@ menu "Example Configuration" help WiFi password (WPA or WPA2) for the example to use. -endmenu \ No newline at end of file + config COAP_PSK_KEY + string "Preshared Key (PSK) to used in the connection to the CoAP server" + default "secret-key" + help + The Preshared Key to use to encrypt the communicatons. The same key must be + used at both ends of the CoAP connection, and the CoaP client must request + an URI prefixed with coaps:// instead of coap:// for DTLS to be used. + + config COAP_PSK_IDENTITY + string "PSK Client identity (username)" + default "coap-client" + help + The identity (or username) to use to identify to the CoAP server which + PSK key to use. + +endmenu diff --git a/examples/protocols/coap_client/main/coap_client_example_main.c b/examples/protocols/coap_client/main/coap_client_example_main.c index 2c1b1294a934..350da932201e 100644 --- a/examples/protocols/coap_client/main/coap_client_example_main.c +++ b/examples/protocols/coap_client/main/coap_client_example_main.c @@ -7,6 +7,13 @@ CONDITIONS OF ANY KIND, either express or implied. */ +/* + * WARNING + * libcoap is not multi-thread safe, so only this thread must make any coap_*() + * calls. Any external (to this thread) data transmitted in/out via libcoap + * therefore has to be passed in/out by xQueue*() via this thread. + */ + #include #include #include @@ -34,7 +41,20 @@ #define COAP_DEFAULT_TIME_SEC 5 -/* Set this to 9 to get verbose logging from within libcoap */ +/* The examples use simple Pre-Shared-Key configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_PSK_KEY "some-agreed-preshared-key" + + Note: PSK will only be used if the URI is prefixed with coaps:// + instead of coap:// and the PSK must be one that the server supports + (potentially associated with the IDENTITY) +*/ +#define EXAMPLE_COAP_PSK_KEY CONFIG_COAP_PSK_KEY +#define EXAMPLE_COAP_PSK_IDENTITY CONFIG_COAP_PSK_IDENTITY + +/* Set this to 7 to get verbose logging from within libcoap */ #define COAP_LOGGING_LEVEL 0 /* The examples use uri "coap://californium.eclipse.org" that @@ -149,6 +169,8 @@ static void coap_example_task(void *p) char* phostname = NULL; coap_set_log_level(COAP_LOGGING_LEVEL); + coap_dtls_set_log_level(COAP_LOGGING_LEVEL); + while (1) { #define BUFSIZE 40 unsigned char _buf[BUFSIZE]; @@ -174,7 +196,7 @@ static void coap_example_task(void *p) if ((uri.scheme==COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) || (uri.scheme==COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported())) { - ESP_LOGE(TAG, "CoAP server uri scheme error"); + ESP_LOGE(TAG, "CoAP server uri scheme is not supported"); break; } @@ -248,10 +270,25 @@ static void coap_example_task(void *p) dst_addr.addr.sin.sin_port = htons(uri.port); dst_addr.addr.sin.sin_addr.s_addr = ip4_addr->addr; - session = coap_new_client_session(ctx, &src_addr, &dst_addr, - uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : - uri.scheme==COAP_URI_SCHEME_COAPS_TCP ? COAP_PROTO_TLS : - uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_UDP); + /* + * Note that if the URI starts with just coap:// (not coaps://) the + * session will still be plain text. + * + * coaps+tcp:// is NOT supported by the underlying TinyDTLS library, + * so COAP_URI_SCHEME_COAPS_TCP will have failed in a test above, + * but the code is left in for completeness. + */ + if (uri.scheme==COAP_URI_SCHEME_COAPS || uri.scheme==COAP_URI_SCHEME_COAPS_TCP) { + session = coap_new_client_session_psk(ctx, &src_addr, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS, + EXAMPLE_COAP_PSK_IDENTITY, + (const uint8_t*)EXAMPLE_COAP_PSK_KEY, + sizeof(EXAMPLE_COAP_PSK_KEY)-1); + } else { + session = coap_new_client_session(ctx, &src_addr, &dst_addr, + uri.scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP : + COAP_PROTO_UDP); + } if (!session) { ESP_LOGE(TAG, "coap_new_client_session() failed"); goto clean_up; @@ -293,7 +330,10 @@ static void coap_example_task(void *p) if (session) coap_session_release(session); if (ctx) coap_free_context(ctx); coap_cleanup(); - /* Only send the request off once */ + /* + * change the following line to something like sleep(2) + * if you want the request to continually be sent + */ break; } @@ -344,5 +384,5 @@ void app_main(void) { ESP_ERROR_CHECK( nvs_flash_init() ); wifi_conn_init(); - xTaskCreate(coap_example_task, "coap", 1024 * 5, NULL, 5, NULL); + xTaskCreate(coap_example_task, "coap", 1024 * 4, NULL, 5, NULL); } diff --git a/examples/protocols/coap_server/README.md b/examples/protocols/coap_server/README.md index 44366936d06e..9664a536b6a8 100644 --- a/examples/protocols/coap_server/README.md +++ b/examples/protocols/coap_server/README.md @@ -2,14 +2,22 @@ # CoAP server example (See the README.md file in the upper level 'examples' directory for more information about examples.) -This CoAP server example is adaptation of one of the [libcoap](https://github.com/obgm/libcoap) example. +This CoAP server example is very simplified adaptation of one of the +[libcoap](https://github.com/obgm/libcoap) examples. -CoAP server example would startup a daemon task, receive data from CoAP client and transmit data to CoAP client. +CoAP server example will startup a daemon task, receive requests / data from CoAP client and transmit +data to CoAP client. -The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things. -The protocol is designed for machine-to-machine (M2M) applications such as smart energy and building automation. +If the incoming request requests the use of DTLS (connecting to port 5684), then the CoAP server will +try to establish a DTLS session using the previously defined Preshared Key, which must be the same +as the one that the CoAP client is using. -please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. +The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with +constrained nodes and constrained networks in the Internet of Things. +The protocol is designed for machine-to-machine (M2M) applications such as smart energy and +building automation. + +Please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) for more details. ## How to use example @@ -22,6 +30,7 @@ make menuconfig * Set default serial port under Serial Flasher config * Set WiFi SSID under Example Configuration * Set WiFi Password under Example Configuration +* Set Preshared Key to use in the connection from the client ### Build and Flash @@ -54,8 +63,8 @@ I (2622) CoAP_server: Connected to AP ... ``` -if a CoAP client query `/Espressif` resource, CoAP server would return `"no data"` -until a CoAP client does a PUT with some data. +If a CoAP client queries the `/Espressif` resource, CoAP server will return `"no data"` +until a CoAP client does a PUT with different data. ## libcoap Documentation This can be found at https://libcoap.net/doc/reference/4.2.0/ @@ -65,4 +74,4 @@ This can be found at https://libcoap.net/doc/reference/4.2.0/ fetches `/.well-known/core` * libcoap logging can be increased by changing `#define COAP_LOGGING_LEVEL 0` -to `#define COAP_LOGGING_LEVEL 9` +to `#define COAP_LOGGING_LEVEL 7` diff --git a/examples/protocols/coap_server/main/Kconfig.projbuild b/examples/protocols/coap_server/main/Kconfig.projbuild index 2772f30da8ee..c53471fd97f0 100644 --- a/examples/protocols/coap_server/main/Kconfig.projbuild +++ b/examples/protocols/coap_server/main/Kconfig.projbuild @@ -12,4 +12,12 @@ menu "Example Configuration" help WiFi password (WPA or WPA2) for the example to use. -endmenu \ No newline at end of file + config COAP_PSK_KEY + string "Preshared Key (PSK) to used in the connection from the CoAP client" + default "secret-key" + help + The Preshared Key to use to encrypt the communicatons. The same key must be + used at both ends of the CoAP connection, and the CoaP client must request + an URI prefixed with coaps:// instead of coap:// for DTLS to be used. + +endmenu diff --git a/examples/protocols/coap_server/main/coap_server_example_main.c b/examples/protocols/coap_server/main/coap_server_example_main.c index f41ca9dfddc2..5709105a182d 100644 --- a/examples/protocols/coap_server/main/coap_server_example_main.c +++ b/examples/protocols/coap_server/main/coap_server_example_main.c @@ -7,6 +7,13 @@ CONDITIONS OF ANY KIND, either express or implied. */ +/* + * WARNING + * libcoap is not multi-thread safe, so only this thread must make any coap_*() + * calls. Any external (to this thread) data transmitted in/out via libcoap + * therefore has to be passed in/out by xQueue*() via this thread. + */ + #include #include @@ -31,7 +38,19 @@ #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD -/* Set this to 9 to get verbose logging from within libcoap */ +/* The examples use simple Pre-Shared-Key configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_PSK_KEY "some-agreed-preshared-key" + + Note: PSK will only be used if the incoming request was set up with a URI + prefixes with coaps:// instead of coap:// and the CoAP client is using the + same PSK. +*/ +#define EXAMPLE_COAP_PSK_KEY CONFIG_COAP_PSK_KEY + +/* Set this to 7 to get verbose logging from within libcoap */ #define COAP_LOGGING_LEVEL 0 static EventGroupHandle_t wifi_event_group; @@ -45,6 +64,8 @@ const static char *TAG = "CoAP_server"; static char espressif_data[100]; static int espressif_data_len = 0; +#define INITIAL_DATA "Hello World!" + /* * The resource handler */ @@ -74,7 +95,7 @@ hnd_espressif_put(coap_context_t *ctx, coap_resource_notify_observers(resource, NULL); - if (strcmp (espressif_data, "no data") == 0) { + if (strcmp (espressif_data, INITIAL_DATA) == 0) { response->code = COAP_RESPONSE_CODE(201); } else { @@ -85,7 +106,7 @@ hnd_espressif_put(coap_context_t *ctx, (void)coap_get_data(request, &size, &data); if (size == 0) { /* re-init */ - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); } else { espressif_data_len = size > sizeof (espressif_data) ? sizeof (espressif_data) : size; @@ -103,7 +124,7 @@ hnd_espressif_delete(coap_context_t *ctx, coap_pdu_t *response) { coap_resource_notify_observers(resource, NULL); - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); response->code = COAP_RESPONSE_CODE(202); } @@ -111,15 +132,16 @@ hnd_espressif_delete(coap_context_t *ctx, static void coap_example_thread(void *p) { coap_context_t *ctx = NULL; - coap_address_t serv_addr; + coap_address_t serv_addr; coap_resource_t *resource = NULL; - snprintf(espressif_data, sizeof(espressif_data), "no data"); + snprintf(espressif_data, sizeof(espressif_data), INITIAL_DATA); espressif_data_len = strlen(espressif_data); coap_set_log_level(COAP_LOGGING_LEVEL); + coap_dtls_set_log_level(COAP_LOGGING_LEVEL); + while (1) { - coap_endpoint_t *ep_udp = NULL; - coap_endpoint_t *ep_tcp = NULL; + coap_endpoint_t *ep = NULL; unsigned wait_ms; /* Wait for the callback to set the CONNECTED_BIT in the @@ -139,14 +161,26 @@ static void coap_example_thread(void *p) if (!ctx) { continue; } - ep_udp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); - if (!ep_udp) { + /* Need PSK setup before we set up endpoints */ + coap_context_set_psk(ctx, "CoAP", + (const uint8_t*)EXAMPLE_COAP_PSK_KEY, + sizeof(EXAMPLE_COAP_PSK_KEY)-1); + + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP); + if (!ep) { goto clean_up; } - ep_tcp = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); - if (!ep_tcp) { + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_TCP); + if (!ep) { goto clean_up; } + if (coap_dtls_is_supported()) { + serv_addr.addr.sin.sin_port = htons(COAPS_DEFAULT_PORT); + ep = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_DTLS); + if (!ep) { + goto clean_up; + } + } resource = coap_resource_init(coap_make_str_const("Espressif"), 0); if (!resource) { goto clean_up; @@ -226,5 +260,5 @@ void app_main(void) ESP_ERROR_CHECK( nvs_flash_init() ); wifi_conn_init(); - xTaskCreate(coap_example_thread, "coap", 1024 * 5, NULL, 5, NULL); + xTaskCreate(coap_example_thread, "coap", 1024 * 4, NULL, 5, NULL); }