Skip to content

Commit

Permalink
Add DTLS support to libcoap using TinyDTLS
Browse files Browse the repository at this point in the history
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

components/coap/Makefile.projbuild:

Update the compiler options with -DESPIDF_VERSION

components/coap/libcoap:

Update the version to include the correct version of tinydtls submodule

components/coap/port/dtls_prng.c:
components/coap/port/include/coap/dtls_config.h:

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

See Issue espressif#1379
  • Loading branch information
mrdeep1 committed Mar 26, 2019
1 parent e2ed49c commit 7eb4825
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 35 deletions.
21 changes: 17 additions & 4 deletions components/coap/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
"port/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"
"libcoap/ext/tinydtls/aes/rijndael.c"
"libcoap/ext/tinydtls/ecc/ecc.c"
"libcoap/ext/tinydtls/sha2/sha2.c")

set(COMPONENT_REQUIRES lwip)

Expand All @@ -27,5 +40,5 @@ register_component()
# Needed for coap headers in public builds, also.
#
# TODO: find a way to move this to a port header
target_compile_definitions(${COMPONENT_TARGET} PUBLIC WITH_POSIX)
target_compile_definitions(${COMPONENT_TARGET} PUBLIC WITH_POSIX ESPIDF_VERSION)

2 changes: 1 addition & 1 deletion components/coap/Makefile.projbuild
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CPPFLAGS += -DWITH_POSIX
CPPFLAGS += -DWITH_POSIX -DESPIDF_VERSION
8 changes: 5 additions & 3 deletions components/coap/component.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 port/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 \
libcoap/ext/tinydtls/aes/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

42 changes: 42 additions & 0 deletions components/coap/port/dtls_prng.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
*
* Copyright (c) 2011-2019 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
* Achim Kraus - session recovery
* Sachin Agrawal - rehandshake support
* Jon Shallow - platform dependent prng support
*
*******************************************************************************/

#include "tinydtls.h"
#include "dtls_prng.h"
#include <esp_system.h>

#include <stdlib.h>

/**
* Fills \p buf with \p len random bytes. This is the default
* implementation for prng(). You might want to change prng() to use
* a better PRNG on your specific platform.
*/
int
dtls_prng(unsigned char *buf, size_t len) {
esp_fill_random(buf, len);
return 1;
}

void
dtls_prng_init(unsigned seed) {
return;
}

153 changes: 153 additions & 0 deletions components/coap/port/include/coap/dtls_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* 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 <arpa/inet.h> header file. */
#ifndef HAVE_ARPA_INET_H
#define HAVE_ARPA_INET_H 1
#endif

/* Define to 1 if you have the <assert.h> header file. */
#ifndef HAVE_ASSERT_H
#define HAVE_ASSERT_H 1
#endif

/* Define to 1 if you have the <fcntl.h> 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 <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1

/* Define to 1 if you have the <memory.h> 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 <netdb.h> header file. */
#define HAVE_NETDB_H 1

/* Define to 1 if you have the <netinet/in.h> 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 <stddef.h> header file. */
#define HAVE_STDDEF_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> 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 <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1

/* Define to 1 if you have the <sys/socket.h> header file. */
#ifndef HAVE_SYS_SOCKET_H
#define HAVE_SYS_SOCKET_H 1
#endif

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <time.h> header file. */
#ifndef HAVE_TIME_H
#define HAVE_TIME_H 1
#endif

/* Define to 1 if you have the <unistd.h> 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 <sys/types.h> does not define. */
/* #undef size_t */

#define WITH_SHA256
#define SHA2_USE_INTTYPES_H
#define DTLSv12
#define DTLS_CHECK_CONTENTTYPE

#define rijndaelEncrypt rijndaelEncrypt_dtls
#define rijndaelKeySetupEnc rijndaelKeySetupEnc_dtls
1 change: 1 addition & 0 deletions components/coap/port/include/coap_config_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#define PACKAGE_VERSION "?"

#define COAP_BAD_RECVMSG
#define HAVE_LIBTINYDTLS

#endif /* WITH_POSIX */
#endif /* COAP_CONFIG_POSIX_H_ */
22 changes: 16 additions & 6 deletions examples/protocols/coap_client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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`
19 changes: 17 additions & 2 deletions examples/protocols/coap_client/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -18,4 +18,19 @@ menu "Example Configuration"
help
WiFi password (WPA or WPA2) for the example to use.

endmenu
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
Loading

13 comments on commit 7eb4825

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this error after changing the uri to coaps:// ----> "CoAP_client: CoAP server uri scheme is not supported" when trying to run coap_client.
Any clue what is the problem?

@mrdeep1
Copy link
Owner Author

@mrdeep1 mrdeep1 commented on 7eb4825 Jun 10, 2019 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pegahnikbakht I have now found how to directly get to the update.

It looks like you are working on an older commit - there certainly was a forced update to the libcoap-tinydtls branch (as in espressif#3345) which is working code.

There are a fair number of changes between 7eb4825 and 90f4379 (which is espressif#3345). I suggest you retry things using the latest code.

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrdeep1
I download your latest commit with: git clone -b libcoap-tinydtls --recursive https://github.com/mrdeep1/esp-idf.git

and I'm testing on ESP32 board.
I set they key as 1234 and I've setup a server with libcoap.

any idea?

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pegahnikbakht The keys need to be set to the same value in both the coap_client and the external coap-server. In addition, the Identity (or user name) needs to be set in the coap_client. Both the key and identity are set under Example Configuration when using "make menuconfig".

The coap_client Target URI needs to be set to the IP address of the appliance running the CoAP Server, and the path should just be a / or perhaps /.well-known/core .

The coap-server needs to be built with DTLS support (one of TinyDTLS or OpenSSL) and then run using the "-k common-key" option. Adding in -v9 option will output more debugging information.

coaps:// communication takes place over port 5684/udp (5683 is for coap:// which is unencrypted). You need to make sure that networking otherwise is correctly set up.

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ofcourse I setup keys on both side and I setup identity also using make menuconfig.
Both client and server has local IP address.

Still I get this error "CoAP_client: CoAP server uri scheme is not supported"

I enabled support for DTLS on server and normally it works but with the esp32 it doesn't work.

have you tested on esp32?

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pegahnikbakht I have not run it on native hardware, but have run it using qemu_esp32 and it works as expected. My tests however are done using IPv4 - are you using IPv6?

The message "CoAP server uri scheme is not supported" comes from the code fragment from .../esp-idf/examples/protocols/coap_client/main/coap_client_example_main.c

        if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) {
            ESP_LOGE(TAG, "CoAP server uri error");
            break;
        }

        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 is not supported");
            break;
        }

So the URI is parsed and uri.scheme is set based on coap:// or coaps:// etc. Assuming it is coaps://, then the function coap_dtls_is_supported() needs to return something other than 0 to not report the error message.

coaps+tcp:// will definitely fail the test.

coap_dtls_is_supported() returns 1 if built with TINYDTLS. I assume that there were no code building warnings etc.

What exactly is the complete URI you are trying to use?

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm testing on real hardware :(
This is the url I'm using : "coaps://192.168.10.146/"

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we are early into the startup, we should not be doing any fancy stuff that the real h/w does for us.

In .../esp-idf, does "git log" say that 90f4379 is the current version?

In ...//esp-idf/components/coap/libcoap, does "git log" say that e0c6df7703cb474f5daa9ecd23ab5a269dab4118is the current version?

In ...//esp-idf/components/coap/libcoap/ext/tinydtls, does "git log" say that 7f8c86e501e690301630029fa9bae22424adf618 is the current version?

I am using make as opposed to cmake for my build environment (using Centos 7).

In /esp-idf/components/coap/port/include, coap_config.h is included by the libcoap librart src files, and coap_config.h should be including coap_config_posix.h which defines HAVE_LIBTINYDTLS. This then causes the TinyDTLS modules to be built and added into the libraries. Then coap_dtls_is_supported() should be returning a 1.

So, it looks like you may have a build issue where something is not getting built correctly. I suggest that you add in some debug statements into coap_client_example_main.c to print out the values of uri.scheme and the return value from the function coap_dtls_is_supported().

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worked finally 👍

The problem was /esp-idf/components/coap/libcoap commit.

Thanks so much :)

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pegahnikbakht Hmm - surprised that git clone -b libcoap-tinydtls --recursive https://github.com/mrdeep1/esp-idf.git did not recursively pull in the correct version of libcoap.

@pegahnikbakht
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrdeep1 One more question, do you know how I can force client to use TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 cipher suite in tinydtls instead of TLS_PSK_WITH_AES_128_CCM_8?

Seems like the default is TLS_PSK_WITH_AES_128_CCM_8 .

@mrdeep1
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pegahnikbakht Simple answer is that you cannot do this with the current code. TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 is not a PSK algorithm, although TinyDTLS does support this, but there is no libcoap support.

PR obgm/libcoap#128 has been raised for this, but the code in that PR does not compile. RPK support has been talked about, but no work other than obgm/libcoap#128 has been done for this.

Please sign in to comment.