From 90f4379c7696516499384c2759b3e4a2f512f420 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: components/coap/port/include/coap/hmac.h components/coap/port/rijndael.c New port files for DTLS components/coap/port/include/coap_config_posix.h: 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 | 18 +- components/coap/component.mk | 8 +- components/coap/libcoap | 2 +- components/coap/port/coap_io.c | 50 ++++- .../coap/port/include/coap/dtls_config.h | 165 +++++++++++++++++ components/coap/port/include/coap/hmac.h | 173 ++++++++++++++++++ .../coap/port/include/coap_config_posix.h | 13 ++ components/coap/port/rijndael.c | 82 +++++++++ examples/protocols/coap_client/README.md | 30 ++- .../coap_client/main/Kconfig.projbuild | 17 +- .../main/coap_client_example_main.c | 100 +++++++--- examples/protocols/coap_server/README.md | 26 ++- .../main/coap_server_example_main.c | 60 ++++-- 13 files changed, 675 insertions(+), 69 deletions(-) create mode 100644 components/coap/port/include/coap/dtls_config.h create mode 100644 components/coap/port/include/coap/hmac.h create mode 100644 components/coap/port/rijndael.c diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 1ec70b4990fc..3c55ecb607e5 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,20 @@ 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") set(COMPONENT_REQUIRES lwip) diff --git a/components/coap/component.mk b/components/coap/component.mk index 2eb07afd2c95..c4f405ecf3ac 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 -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/libcoap b/components/coap/libcoap index cfec0d072c5b..e0c6df7703cb 160000 --- a/components/coap/libcoap +++ b/components/coap/libcoap @@ -1 +1 @@ -Subproject commit cfec0d072c5b99ed3e54828ca50ea2f6b91e1f50 +Subproject commit e0c6df7703cb474f5daa9ecd23ab5a269dab4118 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..c0d2c9a12022 --- /dev/null +++ b/components/coap/port/include/coap/dtls_config.h @@ -0,0 +1,165 @@ +/* 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. */ +#ifndef HAVE_NETDB_H +#define HAVE_NETDB_H 1 +#endif + +/* 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 */ + +/* Always enabled in ESP-IDF */ +#ifndef WITH_POSIX +#define WITH_POSIX +#endif + +#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 + +#include "hmac.h" + +#define rijndaelEncrypt rijndaelEncrypt_dtls +#define rijndaelKeySetupEnc rijndaelKeySetupEnc_dtls diff --git a/components/coap/port/include/coap/hmac.h b/components/coap/port/include/coap/hmac.h new file mode 100644 index 000000000000..ed78dbdf1c1e --- /dev/null +++ b/components/coap/port/include/coap/hmac.h @@ -0,0 +1,173 @@ +/******************************************************************************* + * + * Copyright (c) 2011, 2012, 2013, 2014, 2015 Olaf Bergmann (TZI) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Olaf Bergmann - initial API and implementation + * Hauke Mehrtens - memory optimization, ECC integration + * + *******************************************************************************/ + +#ifndef _DTLS_HMAC_H_ +#define _DTLS_HMAC_H_ + +#include + +#include "tinydtls.h" +#include "global.h" + +#ifdef WITH_SHA256 +#ifdef RIOT_VERSION +#include "hashes/sha256.h" + +typedef sha256_context_t dtls_hash_ctx; +typedef sha256_context_t dtls_sha256_ctx; +#define DTLS_HASH_CTX_SIZE sizeof(sha256_context_t) +#define DTLS_SHA256_DIGEST_LENGTH (SHA256_DIGEST_LENGTH) + +#define dtls_sha256_init(Ctx) sha256_init((Ctx)) +#define dtls_sha256_update(Ctx,Input,Len) sha256_update((Ctx), (Input), (Len)) +#define dtls_sha256_final(Buf,Ctx) sha256_final((Ctx), (Buf)) + +#elif defined ESPIDF_VERSION +#include "sodium/crypto_hash_sha256.h" + +typedef crypto_hash_sha256_state dtls_hash_ctx; +typedef crypto_hash_sha256_state dtls_sha256_ctx; +#define DTLS_HASH_CTX_SIZE sizeof(crypto_hash_sha256_state) +#define DTLS_SHA256_DIGEST_LENGTH (32) + +#define dtls_sha256_init(Ctx) crypto_hash_sha256_init((Ctx)) +#define dtls_sha256_update(Ctx,Input,Len) crypto_hash_sha256_update((Ctx), (Input), (Len)) +#define dtls_sha256_final(Buf,Ctx) crypto_hash_sha256_final((Ctx), (Buf)) + +#else /* RIOT_VERSION */ + +/** Aaron D. Gifford's implementation of SHA256 + * see http://www.aarongifford.com/ */ +#include "sha2/sha2.h" + +typedef dtls_sha256_ctx dtls_hash_ctx; +#define DTLS_HASH_CTX_SIZE sizeof(dtls_sha256_ctx) + +#endif /* RIOT_VERSION */ + + +typedef dtls_hash_ctx *dtls_hash_t; + + +static inline void +dtls_hash_init(dtls_hash_t ctx) { + dtls_sha256_init((dtls_sha256_ctx *)ctx); +} + +static inline void +dtls_hash_update(dtls_hash_t ctx, const unsigned char *input, size_t len) { + dtls_sha256_update((dtls_sha256_ctx *)ctx, input, len); +} + +static inline size_t +dtls_hash_finalize(unsigned char *buf, dtls_hash_t ctx) { + dtls_sha256_final(buf, (dtls_sha256_ctx *)ctx); + return DTLS_SHA256_DIGEST_LENGTH; +} +#endif /* WITH_SHA256 */ + +void dtls_hmac_storage_init(void); + +/** + * \defgroup HMAC Keyed-Hash Message Authentication Code (HMAC) + * NIST Standard FIPS 198 describes the Keyed-Hash Message Authentication + * Code (HMAC) which is used as hash function for the DTLS PRF. + * @{ + */ + +#define DTLS_HMAC_BLOCKSIZE 64 /**< size of hmac blocks */ +#define DTLS_HMAC_DIGEST_SIZE 32 /**< digest size (for SHA-256) */ +#define DTLS_HMAC_MAX 64 /**< max number of bytes in digest */ + +/** + * List of known hash functions for use in dtls_hmac_init(). The + * identifiers are the same as the HashAlgorithm defined in + * Section 7.4.1.4.1 of RFC 5246. + */ +typedef enum { + HASH_NONE=0, HASH_MD5=1, HASH_SHA1=2, HASH_SHA224=3, + HASH_SHA256=4, HASH_SHA384=5, HASH_SHA512=6 +} dtls_hashfunc_t; + +/** + * Context for HMAC generation. This object is initialized with + * dtls_hmac_init() and must be passed to dtls_hmac_update() and + * dtls_hmac_finalize(). Once, finalized, the component \c H is + * invalid and must be initialized again with dtls_hmac_init() before + * the structure can be used again. + */ +typedef struct { + unsigned char pad[DTLS_HMAC_BLOCKSIZE]; /**< ipad and opad storage */ + dtls_hash_ctx data; /**< context for hash function */ +} dtls_hmac_context_t; + +/** + * Initializes an existing HMAC context. + * + * @param ctx The HMAC context to initialize. + * @param key The secret key. + * @param klen The length of @p key. + */ +void dtls_hmac_init(dtls_hmac_context_t *ctx, const unsigned char *key, size_t klen); + +/** + * Allocates a new HMAC context \p ctx with the given secret key. + * This function returns \c 1 if \c ctx has been set correctly, or \c + * 0 or \c -1 otherwise. Note that this function allocates new storage + * that must be released by dtls_hmac_free(). + * + * \param key The secret key. + * \param klen The length of \p key. + * \return A new dtls_hmac_context_t object or @c NULL on error + */ +dtls_hmac_context_t *dtls_hmac_new(const unsigned char *key, size_t klen); + +/** + * Releases the storage for @p ctx that has been allocated by + * dtls_hmac_new(). + * + * @param ctx The dtls_hmac_context_t to free. + */ +void dtls_hmac_free(dtls_hmac_context_t *ctx); + +/** + * Updates the HMAC context with data from \p input. + * + * \param ctx The HMAC context. + * \param input The input data. + * \param ilen Size of \p input. + */ +void dtls_hmac_update(dtls_hmac_context_t *ctx, + const unsigned char *input, size_t ilen); + +/** + * Completes the HMAC generation and writes the result to the given + * output parameter \c result. The buffer must be large enough to hold + * the message digest created by the actual hash function. If in + * doubt, use \c DTLS_HMAC_MAX. The function returns the number of + * bytes written to \c result. + * + * \param ctx The HMAC context. + * \param result Output parameter where the MAC is written to. + * \return Length of the MAC written to \p result. + */ +int dtls_hmac_finalize(dtls_hmac_context_t *ctx, unsigned char *result); + +/**@}*/ + +#endif /* _DTLS_HMAC_H_ */ diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h index d675b7a58399..982c5f8444b2 100644 --- a/components/coap/port/include/coap_config_posix.h +++ b/components/coap/port/include/coap_config_posix.h @@ -21,11 +21,13 @@ #ifdef WITH_POSIX #include +#include #define HAVE_SYS_SOCKET_H #define HAVE_MALLOC #define HAVE_ARPA_INET_H #define HAVE_TIME_H +#define HAVE_NETDB_H #define IP_PKTINFO IP_MULTICAST_IF #define IPV6_PKTINFO IPV6_V6ONLY @@ -34,6 +36,17 @@ #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" + +int dtls_prng(unsigned char *buf, size_t len); +void dtls_prng_init(unsigned seed); +#define prng(Buf,Length) dtls_prng((Buf), (Length)) +#define prng_init(Value) dtls_prng_init((uint16_t)(Value)) #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..ea2c29e6e649 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 @@ -19,10 +27,11 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) make menuconfig ``` -* Set serial port under Serial Flasher config -* Set Target Uri under Example Configuration * Set WiFi SSID under Example Configuration * Set WiFi Password under Example Configuration +* Set CoAP Target Uri under Example Configuration +* Set CoAP Preshared Key to use in connection to the server +* Set CoAP PSK Client identity (username) ### Build and Flash @@ -72,9 +81,10 @@ published under EPL+EDL: http://www.eclipse.org/californium/ This can be found at https://libcoap.net/doc/reference/4.2.0/ ## Troubleshooting -* Please make sure Target Url includes valid `host`, optional `port`, optional `path`, and begins -with `coap://` or `coap+tcp://` for a coap server that supports TCP +* Please make sure Target Url includes valid `host`, optional `port`, +optional `path`, and begins with `coap://`, `coaps://` 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 929b8cf4fa9f..296a3975165d 100644 --- a/examples/protocols/coap_client/main/Kconfig.projbuild +++ b/examples/protocols/coap_client/main/Kconfig.projbuild @@ -4,6 +4,21 @@ 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 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 38761267f64f..f891a7c1aa88 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 @@ -27,7 +34,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 @@ -127,14 +147,15 @@ static void message_handler(coap_context_t *ctx, coap_session_t *session, static void coap_example_task(void *p) { struct hostent *hp; - struct ip4_addr *ip4_addr; - coap_address_t dst_addr, src_addr; + coap_address_t dst_addr; static coap_uri_t uri; const char* server_uri = COAP_DEFAULT_DEMO_URI; 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]; @@ -153,7 +174,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; } @@ -172,19 +193,31 @@ static void coap_example_task(void *p) ESP_LOGE(TAG, "DNS lookup failed"); vTaskDelay(1000 / portTICK_PERIOD_MS); free(phostname); - continue; + goto clean_up; + } + { + char tmpbuf[INET6_ADDRSTRLEN]; + coap_address_init(&dst_addr); + switch (hp->h_addrtype) { + case AF_INET: + dst_addr.addr.sin.sin_family = AF_INET; + dst_addr.addr.sin.sin_port = htons(uri.port); + memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr)); + inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf)); + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf); + break; + case AF_INET6: + dst_addr.addr.sin6.sin6_family = AF_INET6; + dst_addr.addr.sin6.sin6_port = htons(uri.port); + memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr)); + inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf)); + ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf); + break; + default: + ESP_LOGE(TAG, "DNS lookup response failed"); + goto clean_up; + } } - - /* Code to print the resolved IP. - - Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ - ip4_addr = (struct ip4_addr *)hp->h_addr; - ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*ip4_addr)); - - coap_address_init(&src_addr); - src_addr.addr.sin.sin_family = AF_INET; - src_addr.addr.sin.sin_port = htons(0); - src_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; if (uri.path.length) { buflen = BUFSIZE; @@ -222,15 +255,25 @@ static void coap_example_task(void *p) goto clean_up; } - coap_address_init(&dst_addr); - dst_addr.addr.sin.sin_family = AF_INET; - 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, NULL, &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, NULL, &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; @@ -272,7 +315,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; } @@ -291,5 +337,5 @@ void app_main(void) */ ESP_ERROR_CHECK(example_connect()); - xTaskCreate(coap_example_task, "coap", 5 * 1024, NULL, 5, NULL); + xTaskCreate(coap_example_task, "coap", 4 * 1024, NULL, 5, NULL); } diff --git a/examples/protocols/coap_server/README.md b/examples/protocols/coap_server/README.md index 44366936d06e..b470e506a414 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 @@ -19,9 +27,9 @@ please refer to [RFC7252](https://www.rfc-editor.org/rfc/pdfrfc/rfc7252.txt.pdf) make menuconfig ``` -* Set default serial port under Serial Flasher config * Set WiFi SSID under Example Configuration * Set WiFi Password under Example Configuration +* Set CoAP Preshared Key to use in the connection from the client ### Build and Flash @@ -54,8 +62,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 +73,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/coap_server_example_main.c b/examples/protocols/coap_server/main/coap_server_example_main.c index eb5e131baaf9..33da3969d06a 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 @@ -24,12 +31,26 @@ #include "coap.h" -/* 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 char espressif_data[100]; static int espressif_data_len = 0; +#define INITIAL_DATA "Hello World!" + /* * The resource handler */ @@ -59,7 +80,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 { @@ -70,7 +91,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; @@ -88,7 +109,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); } @@ -96,15 +117,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; /* Prepare the CoAP server socket */ @@ -117,14 +139,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; @@ -171,5 +205,5 @@ void app_main(void) */ ESP_ERROR_CHECK(example_connect()); - xTaskCreate(coap_example_thread, "coap", 1024 * 5, NULL, 5, NULL); + xTaskCreate(coap_example_thread, "coap", 1024 * 4, NULL, 5, NULL); }