From 3c9084bfed32c9e45a7afbee61ad3be8c5a21ab2 Mon Sep 17 00:00:00 2001 From: tlhc Date: Tue, 16 Jun 2026 12:37:28 +0800 Subject: [PATCH] feat: add openHiTLS backend support - Add openHiTLS DTLS/TLS backend - Integrate openHiTLS crypto into libcoap (D)TLS and OSCORE paths. - Extend TLS tests and add interop validation script. --- .github/workflows/main.yml | 107 +- CMakeLists.txt | 43 +- LICENSE | 7 + Makefile.am | 2 + README.md | 2 + cmake/FindopenHiTLS.cmake | 102 + cmake_coap_defines.h.in | 6 + configure.ac | 142 +- examples/Makefile.am | 1 + examples/tls_backend_testcases.sh | 1573 +++++++++++++++ include/coap3/coap_dtls.h | 1 + m4/ac_check_cryptolibs.m4 | 49 +- m4/ax_pkg_check_openhitls.m4 | 140 ++ man/coap-client.txt.in | 6 +- man/coap-rd.txt.in | 6 +- man/coap-server.txt.in | 6 +- man/coap_address.txt.in | 5 +- man/coap_async.txt.in | 5 +- man/coap_attribute.txt.in | 5 +- man/coap_block.txt.in | 5 +- man/coap_cache.txt.in | 5 +- man/coap_call_home.txt.in | 5 +- man/coap_context.txt.in | 5 +- man/coap_deprecated.txt.in | 5 +- man/coap_encryption.txt.in | 15 +- man/coap_endpoint_client.txt.in | 5 +- man/coap_endpoint_server.txt.in | 5 +- man/coap_handler.txt.in | 5 +- man/coap_init.txt.in | 5 +- man/coap_io.txt.in | 5 +- man/coap_keepalive.txt.in | 5 +- man/coap_locking.txt.in | 5 +- man/coap_logging.txt.in | 5 +- man/coap_observe.txt.in | 5 +- man/coap_oscore.txt.in | 5 +- man/coap_pdu_access.txt.in | 5 +- man/coap_pdu_options.txt.in | 5 +- man/coap_pdu_setup.txt.in | 5 +- man/coap_pdu_transmit.txt.in | 5 +- man/coap_persist.txt.in | 5 +- man/coap_proxy.txt.in | 5 +- man/coap_recovery.txt.in | 5 +- man/coap_resource.txt.in | 5 +- man/coap_session.txt.in | 6 +- man/coap_string.txt.in | 5 +- man/coap_supported.txt.in | 5 +- man/coap_threads.txt.in | 5 +- man/coap_tls_library.txt.in | 9 +- man/coap_uri.txt.in | 5 +- src/coap_debug.c | 10 + src/coap_notls.c | 6 +- src/coap_openhitls.c | 3059 +++++++++++++++++++++++++++++ src/coap_oscore.c | 15 +- src/coap_sha1.c | 4 +- tests/test_tls.c | 1077 +++++++++- 55 files changed, 6428 insertions(+), 116 deletions(-) create mode 100644 cmake/FindopenHiTLS.cmake create mode 100755 examples/tls_backend_testcases.sh create mode 100644 m4/ax_pkg_check_openhitls.m4 create mode 100644 src/coap_openhitls.c diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 42a69523f1..b9e85a99bc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,9 @@ env: PLATFORM: posix TESTS: yes FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + OPENHITLS_REPO: https://gitcode.com/openHiTLS/openhitls.git + # Pinned until the next openHiTLS release + OPENHITLS_REF: 0a7d5a3748f94c594832a16a7a970cb19d8b4b12 jobs: pre-commit: @@ -57,13 +60,37 @@ jobs: strategy: matrix: CC: ["gcc", "clang"] - TLS: ["no", "openssl", "gnutls", "mbedtls", "wolfssl"] + TLS: ["no", "openssl", "gnutls", "mbedtls", "wolfssl", "openhitls"] steps: - uses: actions/checkout@v6 - name: setup run: | sudo apt-get update && sudo apt-get install -y libcunit1-dev libmbedtls-dev libgnutls28-dev libwolfssl-dev libtool libtool-bin exuberant-ctags valgrind ./autogen.sh + - name: cache openHiTLS + if: matrix.TLS == 'openhitls' + id: cache_openhitls + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/openhitls-prefix + key: openhitls-${{ runner.os }}-${{ runner.arch }}-${{ env.OPENHITLS_REF }}-full + - name: build openHiTLS + if: matrix.TLS == 'openhitls' && steps.cache_openhitls.outputs.cache-hit != 'true' + run: | + rm -rf "$RUNNER_TEMP/openhitls-src" "$RUNNER_TEMP/openhitls-prefix" + git init "$RUNNER_TEMP/openhitls-src" + cd "$RUNNER_TEMP/openhitls-src" + git remote add origin "$OPENHITLS_REPO" + git fetch --depth 1 origin "$OPENHITLS_REF" + git checkout FETCH_HEAD + cmake -S . -B build -DHITLS_BUILD_PROFILE=full -DCMAKE_BUILD_TYPE=Release + cmake --build build --parallel "$(nproc)" + cmake --install build --prefix "$RUNNER_TEMP/openhitls-prefix" + - name: install openHiTLS + if: matrix.TLS == 'openhitls' + run: | + sudo cp -a "$RUNNER_TEMP/openhitls-prefix/." /usr/local/ + sudo ldconfig - name: configure no-TLS if: matrix.TLS == 'no' run: | @@ -90,7 +117,7 @@ jobs: strategy: matrix: CC: ["gcc", "clang"] - TLS: ["mbedtls"] + TLS: ["mbedtls", "openhitls"] OSCORE_ONLY: ["yes", "no"] steps: - uses: actions/checkout@v6 @@ -98,6 +125,30 @@ jobs: run: | sudo apt-get update && sudo apt-get install -y libcunit1-dev libmbedtls-dev libgnutls28-dev libwolfssl-dev libtool libtool-bin exuberant-ctags valgrind ./autogen.sh + - name: cache openHiTLS + if: matrix.TLS == 'openhitls' + id: cache_openhitls + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/openhitls-prefix + key: openhitls-${{ runner.os }}-${{ runner.arch }}-${{ env.OPENHITLS_REF }}-full + - name: build openHiTLS + if: matrix.TLS == 'openhitls' && steps.cache_openhitls.outputs.cache-hit != 'true' + run: | + rm -rf "$RUNNER_TEMP/openhitls-src" "$RUNNER_TEMP/openhitls-prefix" + git init "$RUNNER_TEMP/openhitls-src" + cd "$RUNNER_TEMP/openhitls-src" + git remote add origin "$OPENHITLS_REPO" + git fetch --depth 1 origin "$OPENHITLS_REF" + git checkout FETCH_HEAD + cmake -S . -B build -DHITLS_BUILD_PROFILE=full -DCMAKE_BUILD_TYPE=Release + cmake --build build --parallel "$(nproc)" + cmake --install build --prefix "$RUNNER_TEMP/openhitls-prefix" + - name: install openHiTLS + if: matrix.TLS == 'openhitls' + run: | + sudo cp -a "$RUNNER_TEMP/openhitls-prefix/." /usr/local/ + sudo ldconfig - name: configure OSCORE Only if: matrix.OSCORE_ONLY == 'yes' run: | @@ -142,7 +193,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - TLS: ["no", "openssl", "gnutls", "mbedtls", "wolfssl", "tinydtls"] + TLS: ["no", "openssl", "gnutls", "mbedtls", "wolfssl", "tinydtls", "openhitls"] steps: - uses: actions/checkout@v6 with: @@ -151,6 +202,30 @@ jobs: run: | sudo apt-get update && sudo apt-get install -y libcunit1-dev libmbedtls-dev libgnutls28-dev libwolfssl-dev valgrind cmake -E make_directory $GITHUB_WORKSPACE/build-${{matrix.TLS}}-cmake + - name: cache openHiTLS + if: matrix.TLS == 'openhitls' + id: cache_openhitls + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/openhitls-prefix + key: openhitls-${{ runner.os }}-${{ runner.arch }}-${{ env.OPENHITLS_REF }}-full + - name: build openHiTLS + if: matrix.TLS == 'openhitls' && steps.cache_openhitls.outputs.cache-hit != 'true' + run: | + rm -rf "$RUNNER_TEMP/openhitls-src" "$RUNNER_TEMP/openhitls-prefix" + git init "$RUNNER_TEMP/openhitls-src" + cd "$RUNNER_TEMP/openhitls-src" + git remote add origin "$OPENHITLS_REPO" + git fetch --depth 1 origin "$OPENHITLS_REF" + git checkout FETCH_HEAD + cmake -S . -B build -DHITLS_BUILD_PROFILE=full -DCMAKE_BUILD_TYPE=Release + cmake --build build --parallel "$(nproc)" + cmake --install build --prefix "$RUNNER_TEMP/openhitls-prefix" + - name: install openHiTLS + if: matrix.TLS == 'openhitls' + run: | + sudo cp -a "$RUNNER_TEMP/openhitls-prefix/." /usr/local/ + sudo ldconfig - name: configure no-TLS if: matrix.TLS == 'no' run: | @@ -174,7 +249,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - TLS: ["mbedtls"] + TLS: ["mbedtls", "openhitls"] OSCORE_ONLY: ["yes", "no"] steps: - uses: actions/checkout@v6 @@ -184,6 +259,30 @@ jobs: run: | sudo apt-get update && sudo apt-get install -y libcunit1-dev libmbedtls-dev libgnutls28-dev libwolfssl-dev valgrind cmake -E make_directory $GITHUB_WORKSPACE/build-${{matrix.TLS}}-${{matrix.OSCORE_ONLY}}-cmake + - name: cache openHiTLS + if: matrix.TLS == 'openhitls' + id: cache_openhitls + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/openhitls-prefix + key: openhitls-${{ runner.os }}-${{ runner.arch }}-${{ env.OPENHITLS_REF }}-full + - name: build openHiTLS + if: matrix.TLS == 'openhitls' && steps.cache_openhitls.outputs.cache-hit != 'true' + run: | + rm -rf "$RUNNER_TEMP/openhitls-src" "$RUNNER_TEMP/openhitls-prefix" + git init "$RUNNER_TEMP/openhitls-src" + cd "$RUNNER_TEMP/openhitls-src" + git remote add origin "$OPENHITLS_REPO" + git fetch --depth 1 origin "$OPENHITLS_REF" + git checkout FETCH_HEAD + cmake -S . -B build -DHITLS_BUILD_PROFILE=full -DCMAKE_BUILD_TYPE=Release + cmake --build build --parallel "$(nproc)" + cmake --install build --prefix "$RUNNER_TEMP/openhitls-prefix" + - name: install openHiTLS + if: matrix.TLS == 'openhitls' + run: | + sudo cp -a "$RUNNER_TEMP/openhitls-prefix/." /usr/local/ + sudo ldconfig - name: configure OSCORE Only if: matrix.OSCORE_ONLY == 'yes' run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 64df4431d2..f019189094 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,11 +119,11 @@ set(DTLS_BACKEND STRING "\ Name of the dtls backend, only relevant if `ENABLE_DTLS` is ON which is default. \ -Possible values: default, gnutls, openssl, wolfssl, mbedtls and tinydtls. \ +Possible values: default, gnutls, openssl, wolfssl, mbedtls, tinydtls and openhitls. \ If specified then this library will be searched and if found also used. \ If not found then the cmake configuration will stop with an error. \ If not specified, then cmake will try to use the first one found in the following order: \ -gnutls, openssl, wolfssl, mbedtls, tinydtls \ +gnutls, openssl, wolfssl, mbedtls, tinydtls, openhitls \ ") set_property( CACHE DTLS_BACKEND @@ -134,14 +134,15 @@ set_property( openssl wolfssl mbedtls - tinydtls) + tinydtls + openhitls) set(OSCORE_BACKEND "none" CACHE STRING "\ Name of the oscore backend, only relevant if `ENABLE_DTLS` is OFF which is not the \ -default. Possible values: none and mbedtls. \ +default. Possible values: none, mbedtls and openhitls. \ If specified then this library will be searched and if found also used. \ If not found then the cmake configuration will stop with an error. \ ") @@ -149,7 +150,8 @@ set_property( CACHE OSCORE_BACKEND PROPERTY STRINGS none - mbedtls) + mbedtls + openhitls) option( USE_VENDORED_TINYDTLS "compile with the tinydtls project in the submodule if on, otherwise try to find the compiled lib with find_package" @@ -528,6 +530,8 @@ set(WITH_TINYDTLS OFF) set(WITH_MBEDTLS OFF) set(WITH_MBEDTLS_OSCORE OFF) set(WITH_WOLFSSL OFF) +set(WITH_OPENHITLS OFF) +set(WITH_OPENHITLS_OSCORE OFF) if(ENABLE_DTLS) message(STATUS "compiling with DTLS support") @@ -616,6 +620,17 @@ if(ENABLE_DTLS) endif() endif() + if((DTLS_BACKEND STREQUAL "openhitls") OR ((DTLS_BACKEND STREQUAL "default") AND (NOT DTLS_FOUND))) + # libopenhitls + find_package(openHiTLS ${DTLS_REQUIRED}) + if(openHiTLS_FOUND) + set(WITH_OPENHITLS_OSCORE ON) + message(STATUS "compiling with openHiTLS support") + set(COAP_WITH_LIBOPENHITLS 1) + set(DTLS_FOUND ON) + endif() + endif() + if(((DTLS_BACKEND STREQUAL "default") OR (DTLS_BACKEND STREQUAL "ignore")) AND (NOT DTLS_FOUND)) # no cryto lib found message( @@ -639,6 +654,17 @@ elseif(NOT ((OSCORE_BACKEND STREQUAL "none") OR (OSCORE_BACKEND STREQUAL ""))) endif() endif() + if((OSCORE_BACKEND STREQUAL "openhitls") AND (NOT DTLS_FOUND)) + # libopenhitls + find_package(openHiTLS REQUIRED) + if(openHiTLS_FOUND) + set(WITH_OPENHITLS_OSCORE ON) + message(STATUS "compiling with openHiTLS OSCORE support") + set(COAP_WITH_LIBOPENHITLS_OSCORE 1) + set(DTLS_FOUND ON) + endif() + endif() + if((ZEPHYR_BASE) AND (OSCORE_BACKEND STREQUAL "zephyr") AND (NOT DTLS_FOUND)) set(WITH_MBEDTLS_OSCORE ON) message(STATUS "compiling with mbedtls OSCORE support") @@ -721,11 +747,14 @@ message(STATUS "WITH_OPENSSL:....................${WITH_OPENSSL}") message(STATUS "WITH_WOLFSSL:....................${WITH_WOLFSSL}") message(STATUS "WITH_MBEDTLS:....................${WITH_MBEDTLS}") message(STATUS "WITH_MBEDTLS_OSCORE:.............${WITH_MBEDTLS_OSCORE}") +message(STATUS "WITH_OPENHITLS:..................${WITH_OPENHITLS}") +message(STATUS "WITH_OPENHITLS_OSCORE:...........${WITH_OPENHITLS_OSCORE}") message(STATUS "HAVE_LIBTINYDTLS:................${TinyDTLS_VERSION}") message(STATUS "HAVE_LIBGNUTLS:..................${GNUTLS_VERSION}") message(STATUS "HAVE_LIBOPENSSL:.................${OPENSSL_VERSION}") message(STATUS "HAVE_LIBWOLFSSL:.................${wolfssl_VERSION}") message(STATUS "HAVE_LIBMBEDTLS:.................${MbedTLS_VERSION}") +message(STATUS "HAVE_LIBOPENHITLS:...............${openHiTLS_VERSION}") message(STATUS "WITH_EPOLL:......................${WITH_EPOLL}") message(STATUS "WITH_OBSERVE_PERSIST:............${WITH_OBSERVE_PERSIST}") message(STATUS "BUILD_SHARED_LIBS:...............${BUILD_SHARED_LIBS}") @@ -809,6 +838,8 @@ target_sources( $<$:${CMAKE_CURRENT_LIST_DIR}/src/coap_gnutls.c> $<$:${CMAKE_CURRENT_LIST_DIR}/src/coap_mbedtls.c> $<$:${CMAKE_CURRENT_LIST_DIR}/src/coap_mbedtls.c> + $<$:${CMAKE_CURRENT_LIST_DIR}/src/coap_openhitls.c> + $<$:${CMAKE_CURRENT_LIST_DIR}/src/coap_openhitls.c> # needed for OSCORE if enabled $<$:${CMAKE_CURRENT_LIST_DIR}/src/oscore/oscore.c> $<$:${CMAKE_CURRENT_LIST_DIR}/src/oscore/oscore_cbor.c> @@ -856,6 +887,8 @@ target_link_libraries( $<$:MbedTLS::mbedtls> $<$:MbedTLS::mbedtls> $<$:wolfssl::wolfssl> + $<$:openHiTLS::openhitls> + $<$:openHiTLS::openhitls> $<$:ws2_32> $<$:iphlpapi> $<$:pthread>) diff --git a/LICENSE b/LICENSE index 05a0302e76..bfbc5030b2 100644 --- a/LICENSE +++ b/LICENSE @@ -122,6 +122,13 @@ When compiled with wolfSSL support, this software includes components that are licensed under the terms of the GPLv2 license (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html). +======================================================================== +openHiTLS + +When compiled with openHiTLS support, this software includes components +that are licensed under the terms of the Mulan PSL v2 license +(http://license.coscl.org.cn/MulanPSL2). + ======================================================================== SHA1 diff --git a/Makefile.am b/Makefile.am index 6b7e4c974f..9f3419e1f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ EXTRA_DIST = \ cmake/FindCUnit.cmake \ cmake/FindMbedTLS.cmake \ cmake/FindTinyDTLS.cmake \ + cmake/FindopenHiTLS.cmake \ cmake/Findwolfssl.cmake \ coap_config.h.contiki \ coap_config.h.riot \ @@ -215,6 +216,7 @@ libcoap_@LIBCOAP_NAME_SUFFIX@_la_SOURCES = \ src/coap_net.c \ src/coap_netif.c \ src/coap_notls.c \ + src/coap_openhitls.c \ src/coap_openssl.c \ src/coap_option.c \ src/coap_oscore.c \ diff --git a/README.md b/README.md index 729851be2d..9c0771750c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ There is (D)TLS support for the following libraries * [TinyDTLS](https://github.com/eclipse/tinydtls) [PSK and RPK] [DTLS Only] +* [openHiTLS](https://gitcode.com/openHiTLS/openhitls) (Minimum version 0.4.0) [PKI and PSK] + The examples directory contain a CoAP client, CoAP Resource Directory server and a CoAP server to demonstrate the use of this library. diff --git a/cmake/FindopenHiTLS.cmake b/cmake/FindopenHiTLS.cmake new file mode 100644 index 0000000000..1cfb4c62db --- /dev/null +++ b/cmake/FindopenHiTLS.cmake @@ -0,0 +1,102 @@ +# FindopenHiTLS +# ------------- +# +# Find the openHiTLS crypto and (D)TLS libraries. +# +# Imported Targets +# ^^^^^^^^^^^^^^^^ +# +# ``openHiTLS::openhitls`` +# The openHiTLS libraries, if found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# ``openHiTLS_FOUND``, ``openHiTLS_VERSION``, ``OPENHITLS_INCLUDE_DIRS``, +# ``OPENHITLS_LIBRARIES``. +# +# Hints +# ^^^^^ +# +# ``OPENHITLS_ROOT_DIR`` openHiTLS installation prefix. + +set(OPENHITLS_ROOT_DIR "" CACHE PATH "openHiTLS installation prefix") + +find_path( + OPENHITLS_INCLUDE_DIR + NAMES hitls/crypto/crypt_eal_init.h + HINTS ${OPENHITLS_ROOT_DIR} + PATH_SUFFIXES include) +find_library( + OPENHITLS_CRYPTO_LIBRARY + NAMES hitls_crypto + HINTS ${OPENHITLS_ROOT_DIR} + PATH_SUFFIXES lib lib64) +find_library( + OPENHITLS_BSL_LIBRARY + NAMES hitls_bsl + HINTS ${OPENHITLS_ROOT_DIR} + PATH_SUFFIXES lib lib64) +find_library( + OPENHITLS_TLS_LIBRARY + NAMES hitls_tls + HINTS ${OPENHITLS_ROOT_DIR} + PATH_SUFFIXES lib lib64) +find_library( + OPENHITLS_PKI_LIBRARY + NAMES hitls_pki + HINTS ${OPENHITLS_ROOT_DIR} + PATH_SUFFIXES lib lib64) + +set(_OPENHITLS_REQUIRED_VARS + OPENHITLS_INCLUDE_DIR + OPENHITLS_TLS_LIBRARY + OPENHITLS_PKI_LIBRARY + OPENHITLS_CRYPTO_LIBRARY + OPENHITLS_BSL_LIBRARY) +set(OPENHITLS_INCLUDE_DIRS + "${OPENHITLS_INCLUDE_DIR}" + # TBD: openhitls internal header include sub module use bare path + # (e.g. hitls/tls/hitls.h has #include "bsl_obj.h"), which only resolves when + # each module directory is on the include path. + # once openHiTLS adjust internal header include, below lines should be removed. + "${OPENHITLS_INCLUDE_DIR}/hitls/tls" + "${OPENHITLS_INCLUDE_DIR}/hitls/pki" + "${OPENHITLS_INCLUDE_DIR}/hitls/bsl" + "${OPENHITLS_INCLUDE_DIR}/hitls/crypto") +if(OPENHITLS_INCLUDE_DIR AND EXISTS "${OPENHITLS_INCLUDE_DIR}/hitls/bsl/bsl_version.h") + file(STRINGS "${OPENHITLS_INCLUDE_DIR}/hitls/bsl/bsl_version.h" openHiTLS_VERSION_STR + REGEX "#[\t ]*define[\t ]+OPENHITLS_VERSION_S[\t ]+\"[^\"]+\"") + string(REGEX REPLACE "^.*OPENHITLS_VERSION_S[\t ]+\"openHiTLS[\t ]+([0-9]+\\.[0-9]+\\.[0-9]+).*$" + "\\1" openHiTLS_VERSION_STR "${openHiTLS_VERSION_STR}") + set(openHiTLS_VERSION "${openHiTLS_VERSION_STR}") + unset(openHiTLS_VERSION_STR) +endif() +set(OPENHITLS_LIBRARIES + "${OPENHITLS_TLS_LIBRARY}" + "${OPENHITLS_PKI_LIBRARY}" + "${OPENHITLS_CRYPTO_LIBRARY}" + "${OPENHITLS_BSL_LIBRARY}" + "${CMAKE_DL_LIBS}") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + openHiTLS + FAIL_MESSAGE "Could NOT find openHiTLS, set OPENHITLS_ROOT_DIR to the openHiTLS installation prefix" + REQUIRED_VARS ${_OPENHITLS_REQUIRED_VARS} + VERSION_VAR openHiTLS_VERSION) + +if(openHiTLS_FOUND AND NOT TARGET openHiTLS::openhitls) + add_library(openHiTLS::openhitls INTERFACE IMPORTED) + set_target_properties( + openHiTLS::openhitls + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OPENHITLS_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${OPENHITLS_LIBRARIES}") +endif() + +mark_as_advanced( + OPENHITLS_INCLUDE_DIR + OPENHITLS_CRYPTO_LIBRARY + OPENHITLS_BSL_LIBRARY + OPENHITLS_TLS_LIBRARY + OPENHITLS_PKI_LIBRARY) diff --git a/cmake_coap_defines.h.in b/cmake_coap_defines.h.in index d3cf5b33cd..cc4a1c5803 100644 --- a/cmake_coap_defines.h.in +++ b/cmake_coap_defines.h.in @@ -80,6 +80,12 @@ /* Define to 1 if the system has openssl */ #cmakedefine COAP_WITH_LIBOPENSSL @COAP_WITH_LIBOPENSSL@ +/* Define to 1 if the system has openHiTLS */ +#cmakedefine COAP_WITH_LIBOPENHITLS @COAP_WITH_LIBOPENHITLS@ + +/* Define to 1 if the system has openHiTLS and OSCORE only */ +#cmakedefine COAP_WITH_LIBOPENHITLS_OSCORE @COAP_WITH_LIBOPENHITLS_OSCORE@ + /* Define to 1 if the system has libtinydtls */ #cmakedefine COAP_WITH_LIBTINYDTLS @COAP_WITH_LIBTINYDTLS@ diff --git a/configure.ac b/configure.ac index 67dcb3a0ce..7ce4fda740 100644 --- a/configure.ac +++ b/configure.ac @@ -400,18 +400,22 @@ AM_CONDITIONAL(BUILD_MANPAGES, [test "x$build_manpages" = "xyes"]) # __dtls__ # The Datagram Transport Layer Security (DTLS) feature needs cryptography # functions. -# We currently support the GnuTLS, OpenSSL, WolfSSL, MbedTLS and TinyDLTS libraries. +# We currently support the GnuTLS, OpenSSL, WolfSSL, MbedTLS, TinyDTLS +# and openHiTLS libraries. # The user can preselect the cryptography library that should be used by adding '--with-gnutls', -# '--with-openssl', --with-wolfssl, --with-mbedtls or --with-tinydtls. +# '--with-openssl', --with-wolfssl, --with-mbedtls, --with-tinydtls or +# --with-openhitls. # # If the user isn't using a selection we first search for GnuTLS and fallback -# to OpenSSL, WolfSSL, mbedtls or TinyDTLS if a TLS library couldn't be found. +# to OpenSSL, WolfSSL, mbedtls, TinyDTLS or openHiTLS if a TLS library couldn't +# be found. gnutls_version_required=3.3.0 openssl_version_required=1.1.0 mbedtls_version_required=2.7.10 wolfssl_version_required=5.2.0 tinydtls_version_required=0.8.6 +openhitls_version_required=0.4.0 AC_ARG_ENABLE([dtls], [AS_HELP_STRING([--enable-dtls], @@ -455,22 +459,52 @@ AC_ARG_WITH([tinydtls], [with_tinydtls="$withval"], [with_tinydtls="no"]) +AC_ARG_WITH([openhitls], + [AS_HELP_STRING([--with-openhitls], + [Use openHiTLS for DTLS/TLS functions])], + [with_openhitls="$withval"], + [with_openhitls="no"]) + +AC_ARG_WITH([openhitls-oscore], + [AS_HELP_STRING([--with-openhitls-oscore], + [Use openHiTLS for OSCORE functions])], + [with_openhitls_oscore="$withval"], + [with_openhitls_oscore="no"]) + AC_ARG_WITH([submodule-tinydtls], [AS_HELP_STRING([--with-submodule-tinydtls], [Use the TinyDTLS provided in the git submodule over the system-provided version [default=fallback to submodule if --with-tinydtls is explicitly set and no system-provided version was found])])], [with_submodule_tinydtls="$withval"], [with_submodule_tinydtls="explicit_fallback"]) -if test "x$with_gnutls" = "xyes" -o "x$with_openssl" = "xyes" -o "x$with_wolfssl" = "xyes" -o "x$with_mbedtls" = "xyes" -o "x$with_tinydtls" = "xyes"; then +case "$with_openhitls" in +yes|no) ;; +*) AC_MSG_ERROR([==> --with-openhitls only accepts yes or no. Use OpenHiTLS_CFLAGS/OpenHiTLS_LIBS for a non-default installation prefix.]) ;; +esac + +case "$with_openhitls_oscore" in +yes|no) ;; +*) AC_MSG_ERROR([==> --with-openhitls-oscore only accepts yes or no. Use OpenHiTLS_CFLAGS/OpenHiTLS_LIBS for a non-default installation prefix.]) ;; +esac + +if test "x$build_dtls" = "xyes" -a "x$with_openhitls_oscore" = "xyes"; then + if test "x$with_openhitls" = "xno"; then + with_openhitls="yes" + with_openhitls_oscore="no" + fi +fi + +if test "x$with_gnutls" = "xyes" -o "x$with_openssl" = "xyes" -o "x$with_wolfssl" = "xyes" -o "x$with_mbedtls" = "xyes" -o "x$with_tinydtls" = "xyes" -o "x$with_openhitls" = "xyes"; then if test "x$build_dtls" = "xno"; then - # Give an advice that '--with_gnutls', '--with_openssl', '--with_wolfssl', '--with-mbedtls' or '--with-tinydtls' was used but + # Give an advice that '--with-gnutls', '--with-openssl', '--with-wolfssl', '--with-mbedtls', '--with-tinydtls' or '--with-openhitls' was used but # DTLS support isn't configured. - AC_MSG_WARN([==> Using the configure options '--with-gnutls', '--with-openssl', '--with_wolfssl', '--with-mbedtls' or '--with-tinydtls' without '--enable-dtls' is useless and will be ignored.]) + AC_MSG_WARN([==> Using the configure options '--with-gnutls', '--with-openssl', '--with-wolfssl', '--with-mbedtls', '--with-tinydtls' or '--with-openhitls' without '--enable-dtls' is useless and will be ignored.]) with_gnutls="no" with_openssl="no" with_wolfssl="no" with_mbedtls="no" with_tinydtls="no" + with_openhitls="no" with_submodule_tinydtls="no" fi fi @@ -503,8 +537,14 @@ if test "x$build_dtls" = "xyes"; then if test "x$with_tinydtls" = "xyes"; then TLSCOUNT=`expr $TLSCOUNT + 1` fi + if test "x$with_openhitls" = "xyes"; then + TLSCOUNT=`expr $TLSCOUNT + 1` + fi + if test "x$with_openhitls_oscore" = "xyes"; then + TLSCOUNT=`expr $TLSCOUNT + 1` + fi if test "$TLSCOUNT" -gt 1; then - AC_MSG_ERROR([==> You can't use more than 1 of the options '--with-gnutls', '--with-openssl', '--with-mbedtls', '--with-mbedtls-odcore' or '--with-tinydtls' at the same time while '--enable-dtls' is selected! + AC_MSG_ERROR([==> You can't use more than 1 of the options '--with-gnutls', '--with-openssl', '--with-mbedtls', '--with-mbedtls-oscore', '--with-tinydtls', '--with-openhitls' or '--with-openhitls-oscore' at the same time while '--enable-dtls' is selected! ==> Please note, the option '--enable-dtls' is turned on by default if not explicitly disabled!]) fi @@ -533,6 +573,11 @@ if test "x$build_dtls" = "xyes"; then [have_mbedtls="yes"], [have_mbedtls="no"]) + # openHiTLS + AX_PKG_CHECK_OPENHITLS([dtls], + [have_openhitls="yes"], + [have_openhitls="no"]) + if test "${TinyDTLS_CFLAGS+set}" = "set"; then tinydtls_cflags_overridden="yes" fi @@ -545,6 +590,20 @@ if test "x$build_dtls" = "xyes"; then [have_tinydtls="yes"], [have_tinydtls="no"]) + if test "x$with_openhitls" = "xyes"; then + if test "x$have_openhitls" != "xyes"; then + AC_MSG_ERROR([==> You want to build libcoap with DTLS support by the openHiTLS library but the openHiTLS libraries could not be found! + Build and install openHiTLS with HITLS_BUILD_PROFILE=full, + or select a different TLS library or disable the DTLS support using '--disable-dtls'.]) + fi + AC_MSG_NOTICE([The use of openHiTLS was explicitly requested with configure option '--with-openhitls'!]) + have_gnutls="no" + have_openssl="no" + have_wolfssl="no" + have_mbedtls="no" + have_tinydtls="no" + fi + # TBD ? # The user wants to use explicit GnuTLS if '--with-gnutls' was set. @@ -731,11 +790,21 @@ if test "x$build_dtls" = "xyes"; then have_openssl="no" # don't confuse AC_MSG_RESULT at the end of the script have_wolfssl="no" # don't confuse AC_MSG_RESULT at the end of the script + # ... and if not found check openHiTLS is suitable. + elif test "x$have_openhitls" = "xyes"; then + AC_MSG_NOTICE([Using auto selected library openHiTLS for DTLS support!]) + with_openhitls_auto="yes" + have_gnutls="no" + have_wolfssl="no" + have_openssl="no" + have_mbedtls="no" + have_tinydtls="no" + # Note that the TinyDTLS submodule is used only when explicitly requested. # Giving out an error message if we haven't found at least one crypto library. else - AC_MSG_ERROR([==> Option '--enable-dtls' is set but none of the needed cryptography libraries GnuTLS, OpenSSL, wolfSSL, Mbed TLS or TinyDTLS could be found! - Install at least one of the package(s) that contains the development files for GnuTLS (>= $gnutls_version_required), OpenSSL(>= $openssl_version_required), wolfSSL(>= $wolfssl_version_required), Mbed TLS(>= $mbedtls_version_required), or TinyDTLS(>= $tinydtls_version_required) + AC_MSG_ERROR([==> Option '--enable-dtls' is set but none of the needed cryptography libraries GnuTLS, OpenSSL, wolfSSL, Mbed TLS, TinyDTLS or openHiTLS could be found! + Install at least one of the package(s) that contains the development files for GnuTLS (>= $gnutls_version_required), OpenSSL(>= $openssl_version_required), wolfSSL(>= $wolfssl_version_required), Mbed TLS(>= $mbedtls_version_required), TinyDTLS(>= $tinydtls_version_required), or openHiTLS(>= $openhitls_version_required), or disable the DTLS support using '--disable-dtls'.]) fi fi @@ -774,6 +843,11 @@ if test "x$build_dtls" = "xyes"; then DTLS_LIBS="$TinyDTLS_LIBS" AC_DEFINE(COAP_WITH_LIBTINYDTLS, [1], [Define to 1 if the system has libtinydtls.]) fi + if test "x$with_openhitls" = "xyes" -o "x$with_openhitls_auto" = "xyes"; then + DTLS_CFLAGS="$OpenHiTLS_CFLAGS" + DTLS_LIBS="$OpenHiTLS_LIBS" + AC_DEFINE(COAP_WITH_LIBOPENHITLS, [1], [Define to 1 if the system has openHiTLS.]) + fi AC_SUBST(DTLS_CFLAGS) AC_SUBST(DTLS_LIBS) else @@ -782,8 +856,11 @@ else if test "x$with_mbedtls_oscore" = "xyes"; then TLSCOUNT=`expr $TLSCOUNT + 1` fi + if test "x$with_openhitls_oscore" = "xyes"; then + TLSCOUNT=`expr $TLSCOUNT + 1` + fi if test "$TLSCOUNT" -gt 1; then - AC_MSG_ERROR([==> You can't use more than 1 of the options '--with-mbedtls-oscore' at the same time while '--disable-dtls' is selected! + AC_MSG_ERROR([==> You can't use more than 1 of the options '--with-mbedtls-oscore' or '--with-openhitls-oscore' at the same time while '--disable-dtls' is selected! ==> Please note, the option '--enable-dtls' is turned on by default if not explicitly disabled!]) fi @@ -799,6 +876,22 @@ else AC_DEFINE(COAP_WITH_LIBMBEDTLS_OSCORE, [1], [Define to 1 if the system has libmbedtls2.7.10.]) fi + if test "x$with_openhitls_oscore" = "xyes"; then + AX_PKG_CHECK_OPENHITLS([oscore], + [have_openhitls_oscore="yes"], + [have_openhitls_oscore="no"]) + if test "x$have_openhitls_oscore" != "xyes"; then + AC_MSG_ERROR([==> You want to build libcoap with OSCORE support by the openHiTLS library but the openHiTLS libraries could not be found! + Build and install openHiTLS with HITLS_BUILD_PROFILE=full, + or disable the OSCORE support using '--disable-oscore'.]) + fi + AC_MSG_NOTICE([The use of openHiTLS was explicitly requested with configure option '--with-openhitls-oscore'!]) + + DTLS_CFLAGS="$OpenHiTLS_CFLAGS" + DTLS_LIBS="$OpenHiTLS_LIBS" + AC_DEFINE(COAP_WITH_LIBOPENHITLS_OSCORE, [1], [Define to 1 if the system has openHiTLS OSCORE crypto support.]) + fi + fi # Define the Library name extension for the TLS the library was linked against @@ -812,8 +905,12 @@ elif test "x$with_mbedtls" = "xyes" -o "x$with_mbedtls_auto" = "xyes"; then LIBCOAP_DTLS_LIB_EXTENSION_NAME=-mbedtls elif test "x$with_mbedtls_oscore" = "xyes"; then LIBCOAP_DTLS_LIB_EXTENSION_NAME=-mbedtls-oscore +elif test "x$with_openhitls_oscore" = "xyes"; then + LIBCOAP_DTLS_LIB_EXTENSION_NAME=-openhitls-oscore elif test "x$with_tinydtls" = "xyes"; then LIBCOAP_DTLS_LIB_EXTENSION_NAME=-tinydtls +elif test "x$with_openhitls" = "xyes" -o "x$with_openhitls_auto" = "xyes"; then + LIBCOAP_DTLS_LIB_EXTENSION_NAME=-openhitls else LIBCOAP_DTLS_LIB_EXTENSION_NAME=-notls AC_DEFINE(HAVE_NOTLS, [1], [Define to 1 if libcoap has no tls library support.]) @@ -841,7 +938,10 @@ if test "x$build_oscore" = "xyes"; then fi else if test "x$with_mbedtls_oscore" = "xyes"; then - AC_MSG_ERROR([==> --with-mbedtls-oscore and --dsable-oscore incompatible]) + AC_MSG_ERROR([==> --with-mbedtls-oscore and --disable-oscore are incompatible]) + fi + if test "x$with_openhitls_oscore" = "xyes"; then + AC_MSG_ERROR([==> --with-openhitls-oscore and --disable-oscore are incompatible]) fi fi @@ -1505,6 +1605,26 @@ if test "x$with_tinydtls" = "xyes"; then AC_MSG_RESULT([ TinyDTLS_CFLAGS : "$DTLS_CFLAGS"]) AC_MSG_RESULT([ TinyDTLS_LIBS : "$DTLS_LIBS"]) fi +if test "x$with_openhitls" = "xyes" -o "x$with_openhitls_auto" = "xyes"; then + AC_MSG_RESULT([ build DTLS support : "yes"]) + if test "x$openhitls_has_pkgconfig" = "xyes"; then + AC_MSG_RESULT([ --> openHiTLS around : "yes" (found openHiTLS $openhitls_version using pkg-config)]) + else + AC_MSG_RESULT([ --> openHiTLS around : "yes" (found openHiTLS $openhitls_version without pkg-config)]) + fi + AC_MSG_RESULT([ openHiTLS_CFLAGS : "$OpenHiTLS_CFLAGS"]) + AC_MSG_RESULT([ openHiTLS_LIBS : "$OpenHiTLS_LIBS"]) +fi +if test "x$with_openhitls_oscore" = "xyes"; then + AC_MSG_RESULT([ build OSCORE support : "yes"]) + if test "x$openhitls_has_pkgconfig" = "xyes"; then + AC_MSG_RESULT([ --> openHiTLS around : "yes" (found openHiTLS $openhitls_version using pkg-config)]) + else + AC_MSG_RESULT([ --> openHiTLS around : "yes" (found openHiTLS $openhitls_version without pkg-config)]) + fi + AC_MSG_RESULT([ openHiTLS_CFLAGS : "$OpenHiTLS_CFLAGS"]) + AC_MSG_RESULT([ openHiTLS_LIBS : "$OpenHiTLS_LIBS"]) +fi if test "x$build_dtls" != "xyes"; then AC_MSG_RESULT([ build DTLS support : "no"]) fi diff --git a/examples/Makefile.am b/examples/Makefile.am index e977f180f3..1849daa9f8 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -20,6 +20,7 @@ EXTRA_DIST = \ interop/e_client.conf \ interop/f_client.conf \ interop/g_client.conf \ + tls_backend_testcases.sh \ oscore_testcases.sh # just do nothing if 'BUILD_EXAMPLES' isn't defined diff --git a/examples/tls_backend_testcases.sh b/examples/tls_backend_testcases.sh new file mode 100755 index 0000000000..e80bae94f3 --- /dev/null +++ b/examples/tls_backend_testcases.sh @@ -0,0 +1,1573 @@ +#!/bin/bash + +# +# Run local TLS backend smoke tests against coap-server and coap-client. +# +# The current scope covers common TLS backend: +# - PSK handshake success +# - IPv6 PSK handshake success +# - PSK client identity-hint callback +# - PSK server identity callback +# - wrong PSK negative path +# - client first-flight loss +# - server HelloVerifyRequest loss +# - server ServerHello flight loss +# - client flight loss timeout +# - server flight loss timeout +# - TLS-over-TCP PSK handshake success +# - TLS-over-TCP peer close handling +# - TLS-over-TCP PKI handshake success +# - PKI handshake success +# - PKI intermediate CA chain loading +# - PKI certificate chain depth limit success +# - PKI certificate chain depth exceeded negative path +# - PKI server PEM buffer loading +# - PKI mutual authentication success +# - PKI root CA file mutual authentication +# - PKI root CA directory mutual authentication +# - PKI invalid root CA file negative path +# - PKI invalid root CA directory negative path +# - concurrent PSK and PKI server configuration +# - PKI missing client certificate negative path +# - PKI SNI certificate selection +# - wrong PKI CA negative path +# + +INDIR=$(dirname "$0") + +CLIENT=$INDIR/coap-client +SERVER=$INDIR/coap-server +TARGET_IP=127.0.0.1 +SNI_HOST=localhost +BASE_PORT=5689 +PSK_KEY=secret +BAD_PSK_KEY=wrong +SNI_PSK_KEY=snikey +PSK_IDENTITY=user +PSK_HINT=hint +SNI_PSK_HINT=snihint +CLIENT_TIMEOUT=12 +TLS_LIBRARY_DIR= +PARTIAL_LOGS=no +FULL_LOGS=no +KEEP_LOGS=no +DTLS_LOGS= +INVOCATION_OPTS=no + +NO_PASS=0 +NO_FAIL=0 +NO_SKIP=0 +SERVER_PID= +LOGDIR= +SANITIZER_PATTERN="ERROR: AddressSanitizer|ERROR: LeakSanitizer|SUMMARY: AddressSanitizer|SUMMARY: LeakSanitizer|LeakSanitizer: detected memory leaks|runtime error:" + +usage () { + echo "Usage: $(basename "$0") [-c client] [-s server] [-h host] [-n sni-host] [-p base-port]" + echo " [-L tls-library-dir] [-k psk-key] [-u identity]" + echo " [-H hint] [-B seconds] [-D] [-P] [-F] [-K] [-I]" + echo " where -D - Include DTLS logging" + echo " -F - Full logs, when fail reporting" + echo " -K - Keep logs after a run" + echo " -P - partial logs, when fail reporting (recommended)" + echo " -I - Add invocation options to logs" +} + +while getopts "c:s:h:n:p:L:k:u:H:IB:DPFK" OPTION; do + case $OPTION in + c) + CLIENT="$OPTARG" + ;; + s) + SERVER="$OPTARG" + ;; + h) + TARGET_IP="$OPTARG" + ;; + n) + SNI_HOST="$OPTARG" + ;; + p) + BASE_PORT="$OPTARG" + ;; + L) + TLS_LIBRARY_DIR="$OPTARG" + ;; + k) + PSK_KEY="$OPTARG" + ;; + u) + PSK_IDENTITY="$OPTARG" + ;; + H) + PSK_HINT="$OPTARG" + ;; + D) + DTLS_LOGS=-V7 + ;; + B) + CLIENT_TIMEOUT="$OPTARG" + ;; + P) + PARTIAL_LOGS=yes + ;; + F) + FULL_LOGS=yes + ;; + K) + KEEP_LOGS=yes + ;; + I) + INVOCATION_OPTS=yes + ;; + *) + usage + exit 1 + ;; + esac +done + +if [ ! -x "$CLIENT" ]; then + echo "Client executable not found or not executable: $CLIENT" + exit 1 +fi + +if [ ! -x "$SERVER" ]; then + echo "Server executable not found or not executable: $SERVER" + exit 1 +fi + +case "$BASE_PORT" in + *[!0-9]*|"") + echo "Invalid base port: $BASE_PORT" + exit 1 + ;; +esac + +format_uri_host () { + case "$1" in + *:*) printf '[%s]' "$1" ;; + *) printf '%s' "$1" ;; + esac +} + +SECURE_PORT=$((BASE_PORT + 1)) +TARGET_URI_HOST=$(format_uri_host "$TARGET_IP") +SNI_URI_HOST=$(format_uri_host "$SNI_HOST") +TARGET_URI=coaps://$TARGET_URI_HOST:$SECURE_PORT/.well-known/core +SNI_URI=coaps://$SNI_URI_HOST:$SECURE_PORT/.well-known/core +TLS_URI=coaps+tcp://$TARGET_URI_HOST:$SECURE_PORT/.well-known/core +PKI_URI=coaps://$SNI_URI_HOST:$SECURE_PORT/.well-known/core +TLS_PKI_URI=coaps+tcp://$SNI_URI_HOST:$SECURE_PORT/.well-known/core + +if command -v mktemp >/dev/null 2>&1; then + LOGDIR=$(mktemp -d "${TMPDIR:-/tmp}"/libcoap-tls-backend.XXXXXX) +else + LOGDIR=${TMPDIR:-/tmp}/libcoap-tls-backend.$$ + mkdir -p "$LOGDIR" +fi + +ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=1:halt_on_error=1:exitcode=66} +LSAN_OPTIONS=${LSAN_OPTIONS:-exitcode=66} +export ASAN_OPTIONS LSAN_OPTIONS + +run_with_tls_env () { + if [ "$INVOCATION_OPTS" = "yes" ] ; then + echo $@ + fi + if [ -n "$TLS_LIBRARY_DIR" ]; then + LD_LIBRARY_PATH="$TLS_LIBRARY_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ + DYLD_LIBRARY_PATH="$TLS_LIBRARY_DIR${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" \ + "$@" + else + "$@" + fi +} + +start_with_tls_env () { + if [ "$INVOCATION_OPTS" = "yes" ] ; then + echo $@ + fi + if [ -n "$TLS_LIBRARY_DIR" ]; then + export LD_LIBRARY_PATH="$TLS_LIBRARY_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + export DYLD_LIBRARY_PATH="$TLS_LIBRARY_DIR${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" + fi + exec "$@" +} + +cleanup () { + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" >/dev/null 2>&1 + wait "$SERVER_PID" >/dev/null 2>&1 + SERVER_PID= + fi + + if [ "$KEEP_LOGS" = yes ]; then + echo "Logs kept in $LOGDIR" + else + rm -rf "$LOGDIR" + fi +} + +trap cleanup EXIT INT TERM + +show_case_logs () { + case_name=$1 + client_log=$LOGDIR/$case_name.client + server_log=$LOGDIR/$case_name.server + + if [ "$FULL_LOGS" = yes ]; then + echo "--- $case_name client log ---" + cat "$client_log" + echo "--- $case_name server log ---" + cat "$server_log" + elif [ "$PARTIAL_LOGS" = yes ]; then + echo "--- $case_name client log ---" + [ -f "$client_log" ] && + grep -E "COAP_EVENT|2\\.05|No response|cannot send|DTLS retransmit|Packet [0-9]+ dropped|CN '|Identity Hint|error|alert" "$client_log" || true + echo "--- $case_name server log ---" + [ -f "$server_log" ] && + grep -E "COAP_EVENT|handler|Packet [0-9]+ dropped|DTLS retransmit|SNI '|Identity '|Switching to using|error|alert" "$server_log" || true + fi +} + +pass_case () { + echo "Pass" + NO_PASS=$((NO_PASS + 1)) +} + +skip_case () { + echo "Skip ($1)" + NO_SKIP=$((NO_SKIP + 1)) +} + +fail_case () { + case_name=$1 + reason=$2 + + echo "Fail" + echo " $reason" + show_case_logs "$case_name" + NO_FAIL=$((NO_FAIL + 1)) +} + +start_server () { + case_name=$1 + loss=$2 + sni_file=$3 + proto=$4 + id_file=$5 + server_log=$LOGDIR/$case_name.server + server_args="-A $TARGET_IP -p $BASE_PORT -k $PSK_KEY -h $PSK_HINT -v 8 $DTLS_LOGS" + + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" >/dev/null 2>&1 + wait "$SERVER_PID" >/dev/null 2>&1 + SERVER_PID= + fi + + if [ -n "$loss" ]; then + server_args="$server_args -l $loss" + fi + if [ -n "$sni_file" ]; then + server_args="$server_args -s $sni_file" + fi + if [ -n "$id_file" ]; then + server_args="$server_args -i $id_file" + fi + if [ -n "$proto" ]; then + server_args="$server_args -U $proto" + fi + + start_with_tls_env "$SERVER" $server_args > "$server_log" 2>&1 & + SERVER_PID=$! + + for _i in 1 2 3 4 5 6 7 8 9 10; do + if ! kill -0 "$SERVER_PID" >/dev/null 2>&1; then + return 1 + fi + if [ -f "$server_log" ] && + grep -Eq "created (DTLS|TLS)[[:space:]]+endpoint" "$server_log"; then + return 0 + fi + sleep 1 + done + + return 1 +} + +run_client () { + case_name=$1 + key=$2 + loss=$3 + timeout_secs=$4 + uri=${5:-$TARGET_URI} + hint_file=$6 + client_log=$LOGDIR/$case_name.client + client_args="-m get -k $key -u $PSK_IDENTITY -v 8 $DTLS_LOGS -B $timeout_secs" + + if [ -n "$loss" ]; then + client_args="$client_args -l $loss" + fi + if [ -n "$hint_file" ]; then + client_args="$client_args -h $hint_file" + fi + + run_with_tls_env "$CLIENT" $client_args "$uri" > "$client_log" 2>&1 + return 0 +} + +generate_pki_files () { + case_name=$1 + pki_dir=$LOGDIR/pki + ca_conf=$pki_dir/ca.cnf + server_conf=$pki_dir/server.cnf + alt_server_conf=$pki_dir/alt_server.cnf + sni_server_conf=$pki_dir/sni_server.cnf + client_conf=$pki_dir/client.cnf + inter_conf=$pki_dir/inter.cnf + deep_inter_conf=$pki_dir/deep_inter.cnf + + mkdir -p "$pki_dir" + if [ -f "$pki_dir/server.pem" ] && + [ -f "$pki_dir/self_server.pem" ] && + [ -f "$pki_dir/alt_server.pem" ] && + [ -f "$pki_dir/sni_combined.pem" ] && + [ -f "$pki_dir/chain_server.pem" ] && + [ -f "$pki_dir/chain_depth_ok_server.pem" ] && + [ -f "$pki_dir/chain_depth_ok_server.key" ] && + [ -f "$pki_dir/chain_depth_bad_server.pem" ] && + [ -f "$pki_dir/chain_depth_bad_server.key" ] && + [ -f "$pki_dir/client.pem" ]; then + return 0 + fi + + cat > "$ca_conf" < "$server_conf" < "$alt_server_conf" < "$sni_server_conf" < "$client_conf" < "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -x509 -newkey rsa:2048 -nodes -days 1 -sha256 \ + -keyout "$pki_dir/bad_ca.key" -out "$pki_dir/bad_ca.pem" \ + -config "$ca_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/server.key" -out "$pki_dir/server.csr" \ + -config "$server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/server.csr" \ + -CA "$pki_dir/ca.pem" -CAkey "$pki_dir/ca.key" -CAcreateserial \ + -out "$pki_dir/server.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -x509 -newkey rsa:2048 -nodes -days 1 -sha256 \ + -keyout "$pki_dir/self_server.key" -out "$pki_dir/self_server.pem" \ + -config "$server_conf" -extensions v3_req \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/alt_server.key" -out "$pki_dir/alt_server.csr" \ + -config "$alt_server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/alt_server.csr" \ + -CA "$pki_dir/ca.pem" -CAkey "$pki_dir/ca.key" -CAcreateserial \ + -out "$pki_dir/alt_server.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$alt_server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/sni_server.key" -out "$pki_dir/sni_server.csr" \ + -config "$sni_server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/sni_server.csr" \ + -CA "$pki_dir/ca.pem" -CAkey "$pki_dir/ca.key" -CAcreateserial \ + -out "$pki_dir/sni_server.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$sni_server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/client.key" -out "$pki_dir/client.csr" \ + -config "$client_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/client.csr" \ + -CA "$pki_dir/ca.pem" -CAkey "$pki_dir/ca.key" -CAcreateserial \ + -out "$pki_dir/client.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$client_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + cat "$pki_dir/sni_server.pem" "$pki_dir/sni_server.key" \ + > "$pki_dir/sni_combined.pem" || return 1 + + cat > "$inter_conf" <> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/inter.csr" \ + -CA "$pki_dir/ca.pem" -CAkey "$pki_dir/ca.key" -CAcreateserial \ + -out "$pki_dir/inter.pem" -days 1 -sha256 \ + -extensions v3_intermediate -extfile "$inter_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/chain_server.key" -out "$pki_dir/chain_server.csr" \ + -config "$server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/chain_server.csr" \ + -CA "$pki_dir/inter.pem" -CAkey "$pki_dir/inter.key" -CAcreateserial \ + -out "$pki_dir/chain_leaf.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + cat "$pki_dir/chain_leaf.pem" "$pki_dir/inter.pem" \ + > "$pki_dir/chain_server.pem" || return 1 + + cat > "$deep_inter_conf" <> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/$depth_name.csr" \ + -CA "$depth_issuer_cert" -CAkey "$depth_issuer_key" -CAcreateserial \ + -out "$pki_dir/$depth_name.pem" -days 1 -sha256 \ + -extensions v3_intermediate -extfile "$deep_inter_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + depth_issuer_cert=$pki_dir/$depth_name.pem + depth_issuer_key=$pki_dir/$depth_name.key + done + + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/chain_depth_ok_server.key" \ + -out "$pki_dir/chain_depth_ok_server.csr" \ + -config "$server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/chain_depth_ok_server.csr" \ + -CA "$pki_dir/chain_depth_i3.pem" \ + -CAkey "$pki_dir/chain_depth_i3.key" -CAcreateserial \ + -out "$pki_dir/chain_depth_ok_leaf.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + cat "$pki_dir/chain_depth_ok_leaf.pem" \ + "$pki_dir/chain_depth_i3.pem" \ + "$pki_dir/chain_depth_i2.pem" \ + "$pki_dir/chain_depth_i1.pem" \ + > "$pki_dir/chain_depth_ok_server.pem" || return 1 + + openssl req -new -newkey rsa:2048 -nodes -sha256 \ + -keyout "$pki_dir/chain_depth_bad_server.key" \ + -out "$pki_dir/chain_depth_bad_server.csr" \ + -config "$server_conf" >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + openssl x509 -req -in "$pki_dir/chain_depth_bad_server.csr" \ + -CA "$pki_dir/chain_depth_i4.pem" \ + -CAkey "$pki_dir/chain_depth_i4.key" -CAcreateserial \ + -out "$pki_dir/chain_depth_bad_leaf.pem" -days 1 -sha256 \ + -extensions v3_req -extfile "$server_conf" \ + >> "$LOGDIR/$case_name.openssl" 2>&1 || return 1 + cat "$pki_dir/chain_depth_bad_leaf.pem" \ + "$pki_dir/chain_depth_i4.pem" \ + "$pki_dir/chain_depth_i3.pem" \ + "$pki_dir/chain_depth_i2.pem" \ + "$pki_dir/chain_depth_i1.pem" \ + > "$pki_dir/chain_depth_bad_server.pem" || return 1 + return 0 +} + +prepare_ca_hash_dir () { + case_name=$1 + pki_dir=$LOGDIR/pki + ca_dir=$pki_dir/ca_dir + ca_hash= + + mkdir -p "$ca_dir" || return 1 + ca_hash=$(openssl x509 -in "$pki_dir/ca.pem" -subject_hash -noout \ + 2>> "$LOGDIR/$case_name.openssl") || return 1 + if [ -z "$ca_hash" ]; then + return 1 + fi + cp "$pki_dir/ca.pem" "$ca_dir/$ca_hash.0" +} + +start_pki_server () { + case_name=$1 + cert_file=${2:-} + key_file=${3:-} + sni_file=${4:-} + verify_client=${5:-no} + root_ca_file=${6:-} + pem_buf=${7:-no} + with_psk=${8:-no} + proto=${9:-} + server_log=$LOGDIR/$case_name.server + pki_dir=$LOGDIR/pki + + if [ -z "$cert_file" ]; then + cert_file=$pki_dir/server.pem + fi + if [ -z "$key_file" ]; then + key_file=$pki_dir/server.key + fi + + server_args="-A $TARGET_IP -p $BASE_PORT -v 8 $DTLS_LOGS -c $cert_file -j $key_file" + if [ -n "$root_ca_file" ]; then + server_args="$server_args -R $root_ca_file" + else + server_args="$server_args -C $pki_dir/ca.pem" + fi + if [ "$pem_buf" = yes ]; then + server_args="$server_args -m" + fi + if [ "$with_psk" = yes ]; then + server_args="$server_args -k $PSK_KEY -h $PSK_HINT" + fi + if [ -n "$proto" ]; then + server_args="$server_args -U $proto" + fi + + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" >/dev/null 2>&1 + wait "$SERVER_PID" >/dev/null 2>&1 + SERVER_PID= + fi + + if [ -n "$sni_file" ]; then + server_args="$server_args -S $sni_file" + fi + if [ "$verify_client" != yes ]; then + server_args="$server_args -n" + fi + + start_with_tls_env "$SERVER" $server_args > "$server_log" 2>&1 & + SERVER_PID=$! + + for _i in 1 2 3 4 5 6 7 8 9 10; do + if ! kill -0 "$SERVER_PID" >/dev/null 2>&1; then + return 1 + fi + if [ -f "$server_log" ] && + grep -Eq "created (DTLS|TLS)[[:space:]]+endpoint" "$server_log"; then + return 0 + fi + sleep 1 + done + + return 1 +} + +run_pki_client () { + case_name=$1 + ca_file=$2 + timeout_secs=$3 + client_cert=${4:-no} + root_ca_file=${5:-} + uri=${6:-$PKI_URI} + client_log=$LOGDIR/$case_name.client + pki_dir=$LOGDIR/pki + client_args="-m get -v 8 $DTLS_LOGS -B $timeout_secs" + + if [ -n "$root_ca_file" ]; then + client_args="$client_args -R $root_ca_file" + else + client_args="$client_args -C $ca_file" + fi + + if [ "$client_cert" = yes ]; then + client_args="$client_args -c $pki_dir/client.pem -j $pki_dir/client.key" + fi + + run_with_tls_env "$CLIENT" $client_args "$uri" > "$client_log" 2>&1 + return 0 +} + +assert_contains () { + file=$1 + pattern=$2 + + [ -f "$file" ] || return 1 + grep -qE "$pattern" "$file" +} + +assert_not_contains () { + file=$1 + pattern=$2 + + [ -f "$file" ] || return 1 + ! grep -qE "$pattern" "$file" +} + +assert_either_contains () { + first_file=$1 + second_file=$2 + pattern=$3 + + assert_contains "$first_file" "$pattern" || + assert_contains "$second_file" "$pattern" +} + +have_ipv6_loopback () { + if command -v ip >/dev/null 2>&1; then + ip -6 addr show dev lo 2>/dev/null | grep -q '::1/128' && return 0 + fi + if command -v ifconfig >/dev/null 2>&1; then + ifconfig lo0 2>/dev/null | grep -q 'inet6 ::1' && return 0 + ifconfig lo 2>/dev/null | grep -q 'inet6 ::1' && return 0 + fi + [ -r /proc/net/if_inet6 ] && + grep -qi '^00000000000000000000000000000001' /proc/net/if_inet6 +} + +check_sanitizer_logs () { + if grep -R -E "$SANITIZER_PATTERN" "$LOGDIR" >/dev/null 2>&1; then + echo "Sanitizer issue detected" + grep -R -E "$SANITIZER_PATTERN" "$LOGDIR" 2>/dev/null | head -40 + return 1 + fi + return 0 +} + +run_psk_success () { + case_name=psk_success + echo -n "PSK success - " + + if ! start_server "$case_name" ""; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "DTLS: netif: recv[[:space:]]+[0-9]+ bytes" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "DTLS PSK GET did not complete" + fi +} + +run_psk_ipv6_success () { + case_name=psk_ipv6_success + saved_target_ip=$TARGET_IP + ipv6_uri=coaps://[::1]:$SECURE_PORT/.well-known/core + + echo -n "IPv6 PSK success - " + + if ! have_ipv6_loopback; then + skip_case "::1 is not configured" + return + fi + + TARGET_IP=::1 + if ! start_server "$case_name" ""; then + TARGET_IP=$saved_target_ip + skip_case "::1 DTLS bind is not available" + return + fi + TARGET_IP=$saved_target_ip + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" "$ipv6_uri" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "IPv6 DTLS PSK GET did not complete" + fi +} + +run_psk_identity_hint_callback () { + case_name=psk_identity_hint_callback + echo -n "PSK client identity-hint callback - " + hint_file=$LOGDIR/$case_name.hints + + printf '%s,%s,%s\n' "$PSK_HINT" "$PSK_IDENTITY" "$PSK_KEY" > "$hint_file" + + if ! start_server "$case_name" ""; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" "$TARGET_URI" \ + "$hint_file" + + if assert_contains "$LOGDIR/$case_name.client" "does not support Identity Hint selection"; then + skip_case "Identity Hint not supported" + elif assert_contains "$LOGDIR/$case_name.client" "Identity Hint '$PSK_HINT' provided" && + assert_contains "$LOGDIR/$case_name.client" "Switching to using '$PSK_IDENTITY' identity \\+ '$PSK_KEY' key" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "client identity-hint callback did not select PSK" + fi +} + +run_psk_identity_callback () { + case_name=psk_identity_callback + echo -n "PSK server identity callback - " + id_file=$LOGDIR/$case_name.ids + + printf ',%s,%s\n' "$PSK_IDENTITY" "$PSK_KEY" > "$id_file" + + if ! start_server "$case_name" "" "" "" "$id_file"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.server" "Identity '$PSK_IDENTITY' requested, current hint ''" && + assert_contains "$LOGDIR/$case_name.server" "Switching to using '$PSK_KEY' key" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "server identity callback did not select PSK" + fi +} + +run_wrong_psk () { + case_name=wrong_psk + echo -n "Wrong PSK negative - " + + if ! start_server "$case_name" ""; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$BAD_PSK_KEY" "" 3 + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu"; then + pass_case + else + fail_case "$case_name" "wrong PSK reached an unexpected connected/application path" + fi +} + +run_client_first_flight_loss () { + case_name=client_first_flight_loss + echo -n "Client first-flight loss - " + + if ! start_server "$case_name" ""; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "1" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.client" "Following packet no 1 dropped" && + assert_contains "$LOGDIR/$case_name.client" "DTLS retransmit timeout" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05"; then + pass_case + else + fail_case "$case_name" "client first-flight loss did not recover" + fi +} + +run_server_hello_verify_loss () { + case_name=server_hello_verify_loss + echo -n "Server HelloVerifyRequest loss - " + + if ! start_server "$case_name" "1"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.server" "Following packet no 1 dropped" && + assert_contains "$LOGDIR/$case_name.client" "DTLS: netif: recv[[:space:]]+[0-9]+ bytes" && + assert_either_contains "$LOGDIR/$case_name.client" \ + "$LOGDIR/$case_name.server" "DTLS retransmit timeout" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05"; then + pass_case + else + fail_case "$case_name" "server HelloVerifyRequest loss did not recover" + fi +} + +run_server_flight_loss () { + case_name=server_flight_loss + echo -n "Server handshake flight loss - " + + if ! start_server "$case_name" "2"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.server" "Following packet no 2 dropped" && + assert_either_contains "$LOGDIR/$case_name.client" \ + "$LOGDIR/$case_name.server" "DTLS retransmit timeout" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05"; then + pass_case + else + fail_case "$case_name" "server handshake flight loss did not recover" + fi +} + +run_client_flight_loss_timeout () { + case_name=client_flight_loss_timeout + echo -n "Client flight loss timeout - " + + if ! start_server "$case_name" ""; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "100%" 3 + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|DTLS retransmit timeout"; then + pass_case + else + fail_case "$case_name" "client flight loss reached an unexpected connected/application path" + fi +} + +run_server_flight_loss_timeout () { + case_name=server_flight_loss_timeout + echo -n "Server flight loss timeout - " + + if ! start_server "$case_name" "100%"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" 3 + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|DTLS retransmit timeout"; then + pass_case + else + fail_case "$case_name" "server flight loss reached an unexpected connected/application path" + fi +} + +run_psk_sni () { + case_name=psk_sni + echo -n "PSK SNI key selection - " + sni_file=$LOGDIR/$case_name.sni + + printf '%s,%s,%s\n' "$SNI_HOST" "$SNI_PSK_HINT" "$SNI_PSK_KEY" > "$sni_file" + + if ! start_server "$case_name" "" "$sni_file"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$SNI_PSK_KEY" "" "$CLIENT_TIMEOUT" "$SNI_URI" + + if assert_contains "$LOGDIR/$case_name.server" "SNI '$SNI_HOST' requested" && + assert_contains "$LOGDIR/$case_name.server" "Switching to using '$SNI_PSK_HINT' hint" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05"; then + pass_case + else + fail_case "$case_name" "SNI PSK selection did not complete" + fi +} + +run_tls_psk_success () { + case_name=tls_psk_success + echo -n "TLS-over-TCP PSK success - " + + if ! start_server "$case_name" "" "" "coaps+tcp"; then + fail_case "$case_name" "server did not start" + return + fi + + run_client "$case_name" "$PSK_KEY" "" "$CLIENT_TIMEOUT" "$TLS_URI" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "TLS-over-TCP PSK GET did not complete" + fi +} + +run_tls_psk_peer_close () { + case_name=tls_psk_peer_close + client_log=$LOGDIR/$case_name.client + closed_server=0 + + echo -n "TLS-over-TCP peer close - " + + if ! start_server "$case_name" "" "" "coaps+tcp"; then + fail_case "$case_name" "server did not start" + return + fi + + run_with_tls_env "$CLIENT" -m get -G 3 -k "$PSK_KEY" -u "$PSK_IDENTITY" \ + -v 8 $DTLS_LOGS -B 6 "$TLS_URI" > "$client_log" 2>&1 & + client_pid=$! + + for _i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do + if assert_contains "$client_log" "2\\.05"; then + kill "$SERVER_PID" >/dev/null 2>&1 + wait "$SERVER_PID" >/dev/null 2>&1 + SERVER_PID= + closed_server=1 + break + fi + if ! kill -0 "$client_pid" >/dev/null 2>&1; then + break + fi + sleep 0.1 + done + + wait "$client_pid" >/dev/null 2>&1 + + if [ "$closed_server" -eq 1 ] && + assert_contains "$client_log" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$client_log" "2\\.05" && + assert_contains "$client_log" "COAP_EVENT_DTLS_(CLOSED|ERROR)" && + assert_contains "$client_log" "cannot send CoAP pdu" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "TLS-over-TCP peer close did not report a (D)TLS close/error event" + fi +} + +run_tls_pki_success () { + case_name=tls_pki_success + echo -n "TLS-over-TCP PKI success - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" no "" no no "coaps+tcp"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/ca.pem" "$CLIENT_TIMEOUT" no "" \ + "$TLS_PKI_URI" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "TLS-over-TCP PKI GET did not complete" + fi +} + +run_pki_success () { + case_name=pki_success + echo -n "PKI success - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/ca.pem" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "PKI GET did not complete" + fi +} + +# Server sends leaf+intermediate while the client trusts only the root CA. +run_pki_intermediate_chain () { + case_name=pki_intermediate_chain + echo -n "PKI intermediate CA chain (leaf+intermediate sent, client trusts root only) - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "$pki_dir/chain_server.pem" \ + "$pki_dir/chain_server.key" "" no "$pki_dir/ca.pem"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" no "$pki_dir/ca.pem" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "PKI intermediate-chain GET did not complete (intermediate CA not sent?)" + fi +} + +# Boundary case: cert_chain_verify_depth=2 allows three intermediate CAs. +run_pki_chain_depth_limit () { + case_name=pki_chain_depth_limit + echo -n "PKI certificate chain depth limit (leaf+3 intermediates+root) - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "$pki_dir/chain_depth_ok_server.pem" \ + "$pki_dir/chain_depth_ok_server.key" "" no "$pki_dir/ca.pem"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" no "$pki_dir/ca.pem" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "PKI chain at configured depth limit did not complete" + fi +} + +# Negative boundary: four intermediate CAs exceed the default chain depth. +run_pki_chain_depth_too_long () { + case_name=pki_chain_depth_too_long + echo -n "PKI certificate chain depth exceeded (leaf+4 intermediates+root) - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "$pki_dir/chain_depth_bad_server.pem" \ + "$pki_dir/chain_depth_bad_server.key" "" no "$pki_dir/ca.pem"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" no "$pki_dir/ca.pem" + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_either_contains "$LOGDIR/$case_name.client" "$LOGDIR/$case_name.server" \ + "certificate chain too long|constraint limits|unknown_ca|0x400000f|alert|No response received"; then + pass_case + else + fail_case "$case_name" "PKI chain beyond configured depth limit was accepted" + fi +} + +run_pki_self_signed_leaf () { + case_name=pki_self_signed_leaf + echo -n "PKI self-signed leaf allowed - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "$pki_dir/self_server.pem" \ + "$pki_dir/self_server.key"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" no "$pki_dir/bad_ca.pem" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "self-signed leaf PKI GET did not complete" + fi +} + +run_pki_self_signed_common_ca () { + case_name=pki_self_signed_common_ca + echo -n "PKI self-signed with common CA required negative - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "$pki_dir/self_server.pem" \ + "$pki_dir/self_server.key"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$pki_dir/bad_ca.pem" "$CLIENT_TIMEOUT" + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR"; then + pass_case + else + fail_case "$case_name" "self-signed leaf accepted while common CA was required" + fi +} + +run_pki_non_self_signed_override () { + case_name=pki_non_self_signed_override + echo -n "PKI non-self-signed with override open negative - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" no "$pki_dir/bad_ca.pem" + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR"; then + pass_case + else + fail_case "$case_name" "non-self-signed cert accepted via self-signed override" + fi +} + +run_pki_server_pem_buf () { + case_name=pki_server_pem_buf + echo -n "PKI server PEM buffer loading - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" no "" yes; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/ca.pem" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$case_name" "PKI PEM buffer server did not complete" + fi +} + +run_pki_mutual_auth () { + case_name=pki_mutual_auth + echo -n "PKI mutual authentication - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" yes; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/ca.pem" "$CLIENT_TIMEOUT" yes + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "CN 'libcoap-test-client' presented by client"; then + pass_case + else + fail_case "$case_name" "PKI mutual authentication did not complete" + fi +} + +run_psk_pki_dual_mode () { + case_name=psk_pki_dual_mode + dual_case=$case_name + echo -n "Concurrent PSK and PKI server configuration - " + + if ! generate_pki_files "$dual_case"; then + fail_case "$dual_case" "certificate generation failed" + return + fi + if ! start_pki_server "$dual_case" "" "" "" no "" no yes; then + fail_case "$dual_case" "server did not start" + return + fi + + run_client "${dual_case}_psk" "$PSK_KEY" "" "$CLIENT_TIMEOUT" + run_pki_client "${dual_case}_pki" "$LOGDIR/pki/ca.pem" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/${dual_case}_psk.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/${dual_case}_psk.client" "2\\.05" && + assert_contains "$LOGDIR/${dual_case}_pki.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/${dual_case}_pki.client" "2\\.05" && + assert_contains "$LOGDIR/${dual_case}_pki.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$dual_case.server" "call handler for pseudo resource '.well-known/core'"; then + pass_case + else + fail_case "$dual_case" "concurrent PSK and PKI server did not complete both handshakes" + fi +} + +run_pki_root_ca_file () { + case_name=pki_root_ca_file + echo -n "PKI root CA file mutual authentication - " + pki_dir=$LOGDIR/pki + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" yes "$pki_dir/ca.pem"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" yes "$pki_dir/ca.pem" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "CN 'libcoap-test-client' presented by client"; then + pass_case + else + fail_case "$case_name" "PKI root CA file mutual authentication did not complete" + fi +} + +run_pki_root_ca_dir () { + case_name=pki_root_ca_dir + echo -n "PKI root CA directory mutual authentication - " + pki_dir=$LOGDIR/pki + ca_dir=$pki_dir/ca_dir + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! prepare_ca_hash_dir "$case_name"; then + fail_case "$case_name" "CA hash directory preparation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" yes "$ca_dir"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" "$CLIENT_TIMEOUT" yes "$ca_dir" + + if assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server" && + assert_contains "$LOGDIR/$case_name.server" "CN 'libcoap-test-client' presented by client"; then + pass_case + else + fail_case "$case_name" "PKI root CA directory mutual authentication did not complete" + fi +} + +run_pki_root_ca_file_invalid () { + case_name=pki_root_ca_file_invalid + echo -n "PKI invalid root CA file negative - " + pki_dir=$LOGDIR/pki + invalid_ca_file=$pki_dir/invalid_ca.pem + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + printf '%s\n' "invalid root ca" > "$invalid_ca_file" + if ! start_pki_server "$case_name"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" 3 no "$invalid_ca_file" + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR|Unable to set up PKI keys"; then + pass_case + else + fail_case "$case_name" "invalid root CA file reached an unexpected connected/application path" + fi +} + +run_pki_root_ca_dir_invalid () { + case_name=pki_root_ca_dir_invalid + echo -n "PKI invalid root CA directory negative - " + pki_dir=$LOGDIR/pki + invalid_ca_dir=$pki_dir/invalid_ca_dir + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + mkdir -p "$invalid_ca_dir" || { + fail_case "$case_name" "invalid CA directory preparation failed" + return + } + printf '%s\n' "invalid root ca" > "$invalid_ca_dir/not-a-hash.0" + if ! start_pki_server "$case_name"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "" 3 no "$invalid_ca_dir" + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR|Unable to set up PKI keys"; then + pass_case + else + fail_case "$case_name" "invalid root CA directory reached an unexpected connected/application path" + fi +} + +run_pki_missing_client_cert () { + case_name=pki_missing_client_cert + echo -n "PKI missing client certificate negative - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name" "" "" "" yes; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/ca.pem" 3 + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR"; then + pass_case + else + fail_case "$case_name" "missing client certificate reached an unexpected connected/application path" + fi +} + +run_pki_sni () { + case_name=pki_sni + echo -n "PKI SNI certificate selection - " + pki_dir=$LOGDIR/pki + sni_file=$LOGDIR/$case_name.sni + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + + printf '%s,%s,%s\n' "$SNI_HOST" "$pki_dir/sni_combined.pem" \ + "$pki_dir/ca.pem" > "$sni_file" + + if ! start_pki_server "$case_name" "$pki_dir/alt_server.pem" \ + "$pki_dir/alt_server.key" "$sni_file"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$pki_dir/ca.pem" "$CLIENT_TIMEOUT" + + if assert_contains "$LOGDIR/$case_name.server" "SNI '$SNI_HOST' requested" && + assert_contains "$LOGDIR/$case_name.server" "Switching to using cert '.*sni_combined.pem' \\+ ca '.*ca.pem'" && + assert_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_contains "$LOGDIR/$case_name.client" "2\\.05" && + assert_contains "$LOGDIR/$case_name.client" "CN '$SNI_HOST' presented by server"; then + pass_case + else + fail_case "$case_name" "PKI SNI certificate selection did not complete" + fi +} + +run_wrong_pki_ca () { + case_name=wrong_pki_ca + echo -n "Wrong PKI CA negative - " + + if ! generate_pki_files "$case_name"; then + fail_case "$case_name" "certificate generation failed" + return + fi + if ! start_pki_server "$case_name"; then + fail_case "$case_name" "server did not start" + return + fi + + run_pki_client "$case_name" "$LOGDIR/pki/bad_ca.pem" 3 + + if assert_not_contains "$LOGDIR/$case_name.client" "COAP_EVENT_DTLS_CONNECTED" && + assert_not_contains "$LOGDIR/$case_name.server" "call handler for pseudo resource '.well-known/core'" && + assert_contains "$LOGDIR/$case_name.client" "No response received within the timeout|cannot send CoAP pdu|COAP_EVENT_DTLS_ERROR"; then + pass_case + else + fail_case "$case_name" "wrong CA reached an unexpected connected/application path" + fi +} + +run_psk_success +run_psk_ipv6_success +run_psk_identity_hint_callback +run_psk_identity_callback +run_wrong_psk +run_client_first_flight_loss +run_server_hello_verify_loss +run_server_flight_loss +run_client_flight_loss_timeout +run_server_flight_loss_timeout +run_psk_sni +run_tls_psk_success +run_tls_psk_peer_close +run_tls_pki_success +run_pki_success +run_pki_intermediate_chain +run_pki_chain_depth_limit +run_pki_chain_depth_too_long +run_pki_self_signed_leaf +run_pki_self_signed_common_ca +run_pki_non_self_signed_override +run_pki_server_pem_buf +run_pki_mutual_auth +run_pki_root_ca_file +run_pki_root_ca_dir +run_pki_root_ca_file_invalid +run_pki_root_ca_dir_invalid +run_psk_pki_dual_mode +run_pki_missing_client_cert +run_pki_sni +run_wrong_pki_ca + +echo +echo "DTLS/TLS backend tests: $NO_PASS passed, $NO_FAIL failed, $NO_SKIP skipped" + +if ! check_sanitizer_logs; then + NO_FAIL=$((NO_FAIL + 1)) +fi + +if [ "$NO_FAIL" -ne 0 ]; then + exit 1 +fi + +exit 0 diff --git a/include/coap3/coap_dtls.h b/include/coap3/coap_dtls.h index a8838c5259..1e62678f2a 100644 --- a/include/coap3/coap_dtls.h +++ b/include/coap3/coap_dtls.h @@ -78,6 +78,7 @@ typedef enum coap_tls_library_t { COAP_TLS_LIBRARY_GNUTLS, /**< Using GnuTLS library */ COAP_TLS_LIBRARY_MBEDTLS, /**< Using Mbed TLS library */ COAP_TLS_LIBRARY_WOLFSSL, /**< Using wolfSSL library */ + COAP_TLS_LIBRARY_OPENHITLS, /**< Using openHiTLS library */ } coap_tls_library_t; /** diff --git a/m4/ac_check_cryptolibs.m4 b/m4/ac_check_cryptolibs.m4 index 1e27f24881..1362ad8cf3 100644 --- a/m4/ac_check_cryptolibs.m4 +++ b/m4/ac_check_cryptolibs.m4 @@ -1,7 +1,7 @@ # # SYNOPSIS # -# AX_CHECK_{GNUTLS|OPENSSL|MBEDTLS|WOLFSSL|TINYDTLS}_VERSION +# AX_CHECK_{GNUTLS|OPENSSL|MBEDTLS|WOLFSSL|TINYDTLS|OPENHITLS}_VERSION # # DESCRIPTION # @@ -96,3 +96,50 @@ AC_DEFUN([AX_CHECK_TINYDTLS_VERSION], AC_MSG_ERROR([==> TinyDTLS $tinydtls_version too old. TinyDTLS >= $tinydtls_version_required required for suitable DTLS support build.]) fi ]) dnl AX_CHECK_TINYDTLS_VERSION + +AC_DEFUN([AX_CHECK_OPENHITLS_VERSION], + [openhitls_version_check_result="no" + AC_MSG_CHECKING([for openHiTLS headers]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#ifndef OPENHITLS_VERSION_I +#error OPENHITLS_VERSION_I missing +#endif +]], [[]])], + [AC_MSG_RESULT([yes]) + openhitls_version_check_result="yes"], + [AC_MSG_RESULT([no])]) + if test "x$openhitls_version_check_result" = "xyes"; then + AC_COMPUTE_INT([openhitls_version_i], + [OPENHITLS_VERSION_I], + [[#include ]], + [openhitls_version_i=0]) + if test "x$openhitls_version_i" = "x0"; then + openhitls_version="unknown" + else + openhitls_version_major=`expr $openhitls_version_i / 268435456` + openhitls_version_minor=`expr \( $openhitls_version_i % 268435456 \) / 1048576` + openhitls_version_patch=`expr \( $openhitls_version_i % 1048576 \) / 65536` + openhitls_version="$openhitls_version_major.$openhitls_version_minor.$openhitls_version_patch" + fi + AC_MSG_CHECKING([for compatible openHiTLS version (>= $openhitls_version_required)]) + openhitls_version_required_major=`printf '%s\n' "$openhitls_version_required" | sed -n 's/^\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)$/\1/p'` + openhitls_version_required_minor=`printf '%s\n' "$openhitls_version_required" | sed -n 's/^\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)$/\2/p'` + openhitls_version_required_patch=`printf '%s\n' "$openhitls_version_required" | sed -n 's/^\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)\.\([[0-9]][[0-9]]*\)$/\3/p'` + if test "x$openhitls_version_required_major" = "x" || + test "x$openhitls_version_required_minor" = "x" || + test "x$openhitls_version_required_patch" = "x"; then + AC_MSG_ERROR([==> Invalid openHiTLS required version '$openhitls_version_required'.]) + fi + openhitls_version_required_i=`expr \( $openhitls_version_required_major \* 268435456 \) + \( $openhitls_version_required_minor \* 1048576 \) + \( $openhitls_version_required_patch \* 65536 \)` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#if OPENHITLS_VERSION_I < $openhitls_version_required_i +#error openHiTLS version too old +#endif +]], [[]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + openhitls_version_check_result="no"]) + fi +]) dnl AX_CHECK_OPENHITLS_VERSION diff --git a/m4/ax_pkg_check_openhitls.m4 b/m4/ax_pkg_check_openhitls.m4 new file mode 100644 index 0000000000..d6edd4596e --- /dev/null +++ b/m4/ax_pkg_check_openhitls.m4 @@ -0,0 +1,140 @@ +# +# SYNOPSIS +# +# AX_PKG_CHECK_OPENHITLS(MODE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This m4 file contains helper functions for checking the headers and +# libraries for openHiTLS which does not always have a pkg-config file. +# +# LICENSE +# +# Copyright (c) 2026 openHiTLS Project +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +AC_DEFUN([AX_PKG_CHECK_OPENHITLS], [ +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +AC_REQUIRE([AC_PROG_CC]) +AC_ARG_VAR([OpenHiTLS_CFLAGS], [C compiler flags for openHiTLS, overriding pkg-config]) +AC_ARG_VAR([OpenHiTLS_LIBS], [linker flags for openHiTLS, overriding pkg-config]) +AC_LANG_PUSH([C]) + +if test "x$1" != "xdtls" -a "x$1" != "xoscore"; then + AC_MSG_ERROR([==> Invalid openHiTLS check mode '$1'.]) +fi + +# pkg-config module name and manual fallback flags per mode. openHiTLS does not +# yet ship a .pc file, so the pkg-config probe below normally fails and we fall +# back to the default flags. +# pkg-config metadata. Update openhitls_pc_module when that name is known. +if test "x$1" = "xdtls"; then + openhitls_pc_module="hitls" + if test -d /usr/local/include/hitls ; then + openhitls_default_cflags="-I/usr/local/include -I/usr/local/include/hitls/tls -I/usr/local/include/hitls/bsl -I/usr/local/include/hitls/crypto -I/usr/local/include/hitls/pki" + else + openhitls_default_cflags="-I/usr/include/hitls/tls -I/usr/include/hitls/bsl -I/usr/include/hitls/crypto -I/usr/include/hitls/pki" + fi + openhitls_default_libs="-lhitls_tls -lhitls_crypto -lhitls_pki -lhitls_bsl -ldl" +else + openhitls_pc_module="hitls_crypto" + if test -d /usr/local/include/hitls ; then + openhitls_default_cflags="-I/usr/local/include -I/usr/local/include/hitls/bsl -I/usr/local/include/hitls/crypto" + else + openhitls_default_cflags="-I/usr/include/hitls/bsl -I/usr/include/hitls/crypto" + fi + openhitls_default_libs="-lhitls_crypto -lhitls_bsl -ldl" +fi + +if test "x${OpenHiTLS_CFLAGS+set}" = "xset"; then + openhitls_cflags_overridden="yes" +else + openhitls_cflags_overridden="no" +fi +if test "x${OpenHiTLS_LIBS+set}" = "xset"; then + openhitls_libs_overridden="yes" +else + openhitls_libs_overridden="no" +fi + +# If OpenHiTLS_CFLAGS and OpenHiTLS_LIBS are overridden, pkg-config would always +# assume the library was found, so skip it and use the supplied flags directly. +openhitls_has_pkgconfig="no" +if test "x$openhitls_libs_overridden" = "xyes" -a "x$openhitls_cflags_overridden" = "xyes"; then + openhitls_has_pkgconfig="no" +else + # When statically linking against libcoap, all transitive dependencies need + # to be specified as linker flags as well. Use pkg-config --static for that. + if test "x$enable_static" = "xyes"; then + KEEP_PKG_CONFIG=$PKG_CONFIG + PKG_CONFIG="$PKG_CONFIG --static" + PKG_CHECK_MODULES([OpenHiTLS], + [$openhitls_pc_module], + [openhitls_has_pkgconfig="yes"], + [openhitls_has_pkgconfig="no"]) + PKG_CONFIG=$KEEP_PKG_CONFIG + else + PKG_CHECK_MODULES([OpenHiTLS], + [$openhitls_pc_module], + [openhitls_has_pkgconfig="yes"], + [openhitls_has_pkgconfig="no"]) + fi +fi + +# Fall back to the default flags when pkg-config did not provide them. +openhitls_lib_dir="" +if test "x$OpenHiTLS_CFLAGS" = "x"; then + OpenHiTLS_CFLAGS="$openhitls_default_cflags" + openhitls_lib_dir="-L/usr/local/lib -L/usr/local/lib64" +fi + +if test "x$OpenHiTLS_LIBS" = "x"; then + OpenHiTLS_LIBS="$openhitls_lib_dir $openhitls_default_libs" +fi +openhitls_version="openHiTLS" + +coap_OpenHiTLS_save_CFLAGS="$CFLAGS" +coap_OpenHiTLS_save_LIBS="$LIBS" +CFLAGS="$CFLAGS $OpenHiTLS_CFLAGS" +LIBS="$OpenHiTLS_LIBS $LIBS" +AX_CHECK_OPENHITLS_VERSION +if test "x$openhitls_version_check_result" != "xyes"; then + openhitls_check_result="no" +elif test "x$1" = "xdtls"; then + AC_MSG_CHECKING([for openHiTLS DTLS/TLS backend APIs]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +]], [[ + (void)CRYPT_EAL_Init(CRYPT_EAL_INIT_ALL); + (void)HITLS_New(NULL); + return 0; +]])], + [openhitls_check_result="yes"], + [openhitls_check_result="no"]) + AC_MSG_RESULT([$openhitls_check_result]) +else + AC_MSG_CHECKING([for openHiTLS OSCORE backend APIs]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +]], [[ + (void)CRYPT_EAL_Init(CRYPT_EAL_INIT_ALL); + return 0; +]])], + [openhitls_check_result="yes"], + [openhitls_check_result="no"]) + AC_MSG_RESULT([$openhitls_check_result]) +fi +CFLAGS="$coap_OpenHiTLS_save_CFLAGS" +LIBS="$coap_OpenHiTLS_save_LIBS" +if test "x$openhitls_check_result" = "xyes"; then + $2 +else + $3 +fi +AC_LANG_POP +]) dnl AX_PKG_CHECK_OPENHITLS diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 803a258ce1..0036e718cf 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -14,6 +14,7 @@ coap-client, coap-client-gnutls, coap-client-mbedtls, coap-client-openssl, +coap-client-openhitls, coap-client-notls - CoAP Client based on libcoap @@ -35,8 +36,9 @@ SYNOPSIS For *coap-client* versions that use libcoap compiled for different (D)TLS libraries, *coap-client-notls*, *coap-client-gnutls*, -*coap-client-openssl*, *coap-client-mbedtls* or *coap-client-tinydtls* may be -available. Otherwise, *coap-client* uses the default libcoap (D)TLS support. +*coap-client-openssl*, *coap-client-mbedtls*, *coap-client-tinydtls* or +*coap-client-openhitls* may be available. Otherwise, *coap-client* uses the +default libcoap (D)TLS support. DESCRIPTION ----------- diff --git a/man/coap-rd.txt.in b/man/coap-rd.txt.in index 16e86893f9..d39d1dfae6 100644 --- a/man/coap-rd.txt.in +++ b/man/coap-rd.txt.in @@ -14,6 +14,7 @@ coap-rd, coap-rd-gnutls , coap-rd-mbedtls, coap-rd-openssl, +coap-rd-openhitls, coap-rd-notls - CoAP Resource Directory based on libcoap @@ -26,8 +27,9 @@ SYNOPSIS For *coap-rd* versions that use libcoap compiled for different (D)TLS libraries, *coap-rd-notls*, *coap-rd-gnutls*, -*coap-rd-openssl*, *coap-rd-mbedtls* or *coap-rd-tinydtls* may be -available. Otherwise, *coap-rd* uses the default libcoap (D)TLS support. +*coap-rd-openssl*, *coap-rd-mbedtls*, *coap-rd-tinydtls* or +*coap-rd-openhitls* may be available. Otherwise, *coap-rd* uses the default +libcoap (D)TLS support. DESCRIPTION ----------- diff --git a/man/coap-server.txt.in b/man/coap-server.txt.in index c64a64ef40..bb4fdd1a20 100644 --- a/man/coap-server.txt.in +++ b/man/coap-server.txt.in @@ -14,6 +14,7 @@ coap-server, coap-server-gnutls, coap-server-mbedtls, coap-server-openssl, +coap-server-openhitls, coap-server-notls - CoAP Server based on libcoap @@ -39,8 +40,9 @@ SYNOPSIS For *coap-server* versions that use libcoap compiled for different (D)TLS libraries, *coap-server-notls*, *coap-server-gnutls*, -*coap-server-openssl*, *coap-server-mbedtls* or *coap-server-tinydtls* may be -available. Otherwise, *coap-server* uses the default libcoap (D)TLS support. +*coap-server-openssl*, *coap-server-mbedtls*, *coap-server-tinydtls* or +*coap-server-openhitls* may be available. Otherwise, *coap-server* uses the +default libcoap (D)TLS support. DESCRIPTION ----------- diff --git a/man/coap_address.txt.in b/man/coap_address.txt.in index 191e8a32be..c37dd0bf3f 100644 --- a/man/coap_address.txt.in +++ b/man/coap_address.txt.in @@ -73,8 +73,9 @@ const uint8_t *_host_, size_t _host_len_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_async.txt.in b/man/coap_async.txt.in index 77e7253b96..a047321131 100644 --- a/man/coap_async.txt.in +++ b/man/coap_async.txt.in @@ -44,8 +44,9 @@ coap_cache_app_data_free_callback_t _app_cb_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_attribute.txt.in b/man/coap_attribute.txt.in index a34367301e..deecca810a 100644 --- a/man/coap_attribute.txt.in +++ b/man/coap_attribute.txt.in @@ -31,8 +31,9 @@ coap_str_const_t *_name_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_block.txt.in b/man/coap_block.txt.in index ae88185cb1..a69c383d02 100644 --- a/man/coap_block.txt.in +++ b/man/coap_block.txt.in @@ -65,8 +65,9 @@ size_t _length_, const uint8_t *_data_, size_t _offset_, size_t _total_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_cache.txt.in b/man/coap_cache.txt.in index 27be86ace6..dd59860644 100644 --- a/man/coap_cache.txt.in +++ b/man/coap_cache.txt.in @@ -64,8 +64,9 @@ void *_app_data_, coap_cache_app_data_free_callback_t _app_cb_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_call_home.txt.in b/man/coap_call_home.txt.in index a86724ee06..4274570f24 100644 --- a/man/coap_call_home.txt.in +++ b/man/coap_call_home.txt.in @@ -29,8 +29,9 @@ SYNOPSIS For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_context.txt.in b/man/coap_context.txt.in index 98e45a360d..5a69b25e08 100644 --- a/man/coap_context.txt.in +++ b/man/coap_context.txt.in @@ -89,8 +89,9 @@ uint64_t _rate_limit_ppm_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_deprecated.txt.in b/man/coap_deprecated.txt.in index 756d326fdd..4ec4f66d2f 100644 --- a/man/coap_deprecated.txt.in +++ b/man/coap_deprecated.txt.in @@ -128,8 +128,9 @@ void *_data_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_encryption.txt.in b/man/coap_encryption.txt.in index 9b45952743..efc49e824a 100644 --- a/man/coap_encryption.txt.in +++ b/man/coap_encryption.txt.in @@ -36,8 +36,9 @@ const char *_ca_file_, const char *_ca_dir_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION @@ -49,7 +50,8 @@ specific underlying TLS implementation type (e.g. https://www.openssl.org[OpenSS https://www.gnutls.org[GnuTLS], https://www.trustedfirmware.org/projects/mbed-tls/[Mbed TLS], https://wolfssl.com[wolfSSL], -https://github.com/eclipse/tinydtls[TinyDTLS] or noTLS). +https://github.com/eclipse/tinydtls[TinyDTLS], +https://gitcode.com/openHiTLS/openhitls[openHiTLS] or noTLS). When the libcoap library is linked into an application, it is possible that the application needs to dynamically determine whether DTLS or TLS is supported, what type of TLS implementation libcoap was compiled with, as well @@ -67,6 +69,9 @@ version is 1.1.0. *NOTE:* If wolfSSL is being used, then the minimum wolfSSL library version is 5.2.0. +*NOTE:* If openHiTLS is being used, then the minimum openHiTLS library version is +0.4.0. + *NOTE:* If GnuTLS is going to interoperate with TinyDTLS, then a minimum revision of GnuTLS 3.5.5 which supports CCM algorithms is required by TinyDTLS as TinyDTLS currently only supports CCM. @@ -79,8 +84,8 @@ as TinyDTLS currently only supports CCM. *NOTE:* For Raw Public Key support, GnuTLS library version must be 3.6.6 or later. For Raw Public Key support, wolfSSL library version must be 5.6.4 or later. TinyDTLS only supports TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, curve -secp256r1 and hash SHA-256. There currently is no OpenSSL or Mbed TLS RPK support -(respective library limitations). +secp256r1 and hash SHA-256. There currently is no OpenSSL, Mbed TLS +or openHiTLS RPK support (respective library limitations). Network traffic can be un-encrypted or encrypted with libcoap if there is an underlying TLS library. diff --git a/man/coap_endpoint_client.txt.in b/man/coap_endpoint_client.txt.in index de2d1499f2..949fe7d050 100644 --- a/man/coap_endpoint_client.txt.in +++ b/man/coap_endpoint_client.txt.in @@ -47,8 +47,9 @@ coap_app_data_free_callback_t _callback_, coap_str_const_t *_ws_host_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_endpoint_server.txt.in b/man/coap_endpoint_server.txt.in index a7cb16045a..f9356d9b71 100644 --- a/man/coap_endpoint_server.txt.in +++ b/man/coap_endpoint_server.txt.in @@ -46,8 +46,9 @@ const char *_groupname_, const char *_ifname_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_handler.txt.in b/man/coap_handler.txt.in index 3d1b82385d..2f47fa919c 100644 --- a/man/coap_handler.txt.in +++ b/man/coap_handler.txt.in @@ -49,8 +49,9 @@ uint32_t _dynamic_max_)*; For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_init.txt.in b/man/coap_init.txt.in index 915696798c..64190c3c62 100644 --- a/man/coap_init.txt.in +++ b/man/coap_init.txt.in @@ -26,8 +26,9 @@ SYNOPSIS For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_io.txt.in b/man/coap_io.txt.in index 73334025d0..dd6817d004 100644 --- a/man/coap_io.txt.in +++ b/man/coap_io.txt.in @@ -68,8 +68,9 @@ unsigned int *rem_timeout_ms)*; For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_keepalive.txt.in b/man/coap_keepalive.txt.in index 7fd56dac39..982ede9126 100644 --- a/man/coap_keepalive.txt.in +++ b/man/coap_keepalive.txt.in @@ -24,8 +24,9 @@ unsigned int _seconds_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_locking.txt.in b/man/coap_locking.txt.in index a19678f1b0..983503fa48 100644 --- a/man/coap_locking.txt.in +++ b/man/coap_locking.txt.in @@ -61,8 +61,9 @@ coap_func_t _callback_function__, coap_code_t _failed_statement_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_logging.txt.in b/man/coap_logging.txt.in index 933eca7399..5398236b8d 100644 --- a/man/coap_logging.txt.in +++ b/man/coap_logging.txt.in @@ -103,8 +103,9 @@ char *_buffer_, size_t _length_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_observe.txt.in b/man/coap_observe.txt.in index d554f5f3f2..4bfc019d8d 100644 --- a/man/coap_observe.txt.in +++ b/man/coap_observe.txt.in @@ -35,8 +35,9 @@ coap_pdu_type_t _message_type_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_oscore.txt.in b/man/coap_oscore.txt.in index e983028c2c..e274394130 100644 --- a/man/coap_oscore.txt.in +++ b/man/coap_oscore.txt.in @@ -70,8 +70,9 @@ coap_oscore_conf_t *_oscore_conf_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_pdu_access.txt.in b/man/coap_pdu_access.txt.in index 2e897a572d..22e3ea62d2 100644 --- a/man/coap_pdu_access.txt.in +++ b/man/coap_pdu_access.txt.in @@ -77,8 +77,9 @@ coap_opt_iterator_t *_oi_, const coap_opt_filter_t *_filter_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_pdu_options.txt.in b/man/coap_pdu_options.txt.in index a0d67b09c7..f01cda0875 100644 --- a/man/coap_pdu_options.txt.in +++ b/man/coap_pdu_options.txt.in @@ -70,8 +70,9 @@ uint8_t *_buffer_, size_t *_buflen_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_pdu_setup.txt.in b/man/coap_pdu_setup.txt.in index 93f03011fd..4b51eeb309 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -60,8 +60,9 @@ const uint8_t *_data_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_pdu_transmit.txt.in b/man/coap_pdu_transmit.txt.in index d46119b0cf..1d8624a51b 100644 --- a/man/coap_pdu_transmit.txt.in +++ b/man/coap_pdu_transmit.txt.in @@ -33,8 +33,9 @@ coap_pdu_t **_response_pdu_, uint32_t _timeout_ms_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_persist.txt.in b/man/coap_persist.txt.in index 6af1348d86..222591a826 100644 --- a/man/coap_persist.txt.in +++ b/man/coap_persist.txt.in @@ -46,8 +46,9 @@ uint32_t _start_observe_no_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_proxy.txt.in b/man/coap_proxy.txt.in index 86da41ffb2..792cb865da 100644 --- a/man/coap_proxy.txt.in +++ b/man/coap_proxy.txt.in @@ -47,8 +47,9 @@ coap_proxy_response_handler_t _handler_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_recovery.txt.in b/man/coap_recovery.txt.in index f2c828a744..2e1b7380fd 100644 --- a/man/coap_recovery.txt.in +++ b/man/coap_recovery.txt.in @@ -100,8 +100,9 @@ uint32_t _value_)*; For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_resource.txt.in b/man/coap_resource.txt.in index 48d858c4ce..512b5d5b68 100644 --- a/man/coap_resource.txt.in +++ b/man/coap_resource.txt.in @@ -78,8 +78,9 @@ const coap_string_t *query_filter);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_session.txt.in b/man/coap_session.txt.in index 6de3816503..bf42db37c5 100644 --- a/man/coap_session.txt.in +++ b/man/coap_session.txt.in @@ -88,8 +88,9 @@ const coap_session_t *_session_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION @@ -236,6 +237,7 @@ GnuTLS: gnutls_session_t (implicit *) Mbed TLS: mbedtls_ssl_context* wolfSSL: WOLFSSL* TinyDTLS: struct dtls_context* +openHiTLS: HITLS_Ctx* ---- The *coap_session_get_tls*() function is used to get the pointer to the TLS diff --git a/man/coap_string.txt.in b/man/coap_string.txt.in index 71f9c50b07..f82958ac11 100644 --- a/man/coap_string.txt.in +++ b/man/coap_string.txt.in @@ -56,8 +56,9 @@ SYNOPSIS For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_supported.txt.in b/man/coap_supported.txt.in index a1b4420b34..c923b64352 100644 --- a/man/coap_supported.txt.in +++ b/man/coap_supported.txt.in @@ -83,8 +83,9 @@ SYNOPSIS For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_threads.txt.in b/man/coap_threads.txt.in index 37e5a76cdb..65dbac409c 100644 --- a/man/coap_threads.txt.in +++ b/man/coap_threads.txt.in @@ -35,8 +35,9 @@ uint32_t _thread_count_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/man/coap_tls_library.txt.in b/man/coap_tls_library.txt.in index eeedc527e5..d4e2ae8f3e 100644 --- a/man/coap_tls_library.txt.in +++ b/man/coap_tls_library.txt.in @@ -38,8 +38,9 @@ SYNOPSIS For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION @@ -49,7 +50,8 @@ specific TLS implementation type (e.g. https://www.openssl.org[OpenSSL], https://www.gnutls.org[GnuTLS], https://www.trustedfirmware.org/projects/mbed-tls/[Mbed TLS], https://wolfssl.com[wolfSSL], -https://github.com/eclipse/tinydtls[TinyDTLS] or noTLS). +https://github.com/eclipse/tinydtls[TinyDTLS], +https://gitcode.com/openHiTLS/openhitls[openHiTLS] or noTLS). When the libcoap library is linked into an application, it is possible that the application needs to dynamically determine whether DTLS or TLS is supported, what type of TLS implementation libcoap was compiled with, as well @@ -92,6 +94,7 @@ typedef enum coap_tls_library_t { COAP_TLS_LIBRARY_GNUTLS, /* Using GnuTLS library */ COAP_TLS_LIBRARY_MBEDTLS, /* Using Mbed TLS library */ COAP_TLS_LIBRARY_WOLFSSL, /* Using wolfSSL library */ + COAP_TLS_LIBRARY_OPENHITLS, /* Using openHiTLS library */ } coap_tls_library_t; typedef struct coap_tls_version_t { diff --git a/man/coap_uri.txt.in b/man/coap_uri.txt.in index cfc14d829f..a91500142f 100644 --- a/man/coap_uri.txt.in +++ b/man/coap_uri.txt.in @@ -58,8 +58,9 @@ int _create_port_host_opt_, uint8_t *_buf_, size_t _buflen_);* For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, -*-lcoap-@LIBCOAP_API_VERSION@-wolfssl* -or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with +*-lcoap-@LIBCOAP_API_VERSION@-wolfssl*, +*-lcoap-@LIBCOAP_API_VERSION@-tinydtls* +or *-lcoap-@LIBCOAP_API_VERSION@-openhitls*. Otherwise, link with *-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support. DESCRIPTION diff --git a/src/coap_debug.c b/src/coap_debug.c index cc2c348ad9..03b49e2ed0 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -1294,6 +1294,16 @@ coap_string_tls_version(char *buffer, size_t bufsize) { (unsigned long)((tls_version->built_version >> 12) & 0xfff), (unsigned long)((tls_version->built_version >> 0) & 0xfff)); break; + case COAP_TLS_LIBRARY_OPENHITLS: + snprintf(buffer, bufsize, "TLS Library: openHiTLS - runtime %lu.%lu.%lu, " + "libcoap built for %lu.%lu.%lu", + (unsigned long)((tls_version->version >> 28) & 0x0f), + (unsigned long)((tls_version->version >> 20) & 0xff), + (unsigned long)((tls_version->version >> 16) & 0x0f), + (unsigned long)((tls_version->built_version >> 28) & 0x0f), + (unsigned long)((tls_version->built_version >> 20) & 0xff), + (unsigned long)((tls_version->built_version >> 16) & 0x0f)); + break; default: snprintf(buffer, bufsize, "Library type %d unknown", tls_version->type); break; diff --git a/src/coap_notls.c b/src/coap_notls.c index 43a367f192..c35881e717 100644 --- a/src/coap_notls.c +++ b/src/coap_notls.c @@ -30,7 +30,7 @@ coap_tls_engine_remove(void) { } #endif /* ! COAP_WITH_LIBOPENSSL */ -#if ! COAP_WITH_LIBTINYDTLS && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBWOLFSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS +#if ! COAP_WITH_LIBTINYDTLS && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBWOLFSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS && ! COAP_WITH_LIBOPENHITLS int coap_dtls_is_supported(void) { @@ -96,7 +96,7 @@ coap_dtls_set_cid_tuple_change(coap_context_t *c_context, uint8_t every) { } #endif /* COAP_CLIENT_SUPPORT */ -#if ! COAP_WITH_LIBMBEDTLS_OSCORE +#if ! COAP_WITH_LIBMBEDTLS_OSCORE && ! COAP_WITH_LIBOPENHITLS_OSCORE coap_tls_version_t * coap_get_tls_library_version(void) { static coap_tls_version_t version; @@ -250,7 +250,7 @@ coap_digest_final(coap_digest_ctx_t *digest_ctx, } #endif /* COAP_SERVER_SUPPORT */ -#endif /* ! COAP_WITH_LIBMBEDTLS_OSCORE */ +#endif /* ! COAP_WITH_LIBMBEDTLS_OSCORE && ! COAP_WITH_LIBOPENHITLS_OSCORE */ /* (D)TLS specific functions */ diff --git a/src/coap_openhitls.c b/src/coap_openhitls.c new file mode 100644 index 0000000000..df4466f201 --- /dev/null +++ b/src/coap_openhitls.c @@ -0,0 +1,3059 @@ +/* + * coap_openhitls.c -- openHiTLS Datagram Transport Layer Support for libcoap + * + * Copyright (C) 2026 openHiTLS Project. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +/** + * @file coap_openhitls.c + * @brief openHiTLS specific interface functions. + */ + +#include "coap3/coap_libcoap_build.h" + +#if COAP_WITH_LIBOPENHITLS || COAP_WITH_LIBOPENHITLS_OSCORE + +#include +#include +#include +#include +#include +#include + +#ifdef COAP_EPOLL_SUPPORT +#include +#endif /* COAP_EPOLL_SUPPORT */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if COAP_WITH_LIBOPENHITLS +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif /* defined(__GNUC__) */ +#include +#include +#include +#include +#include +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif /* defined(__GNUC__) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* COAP_WITH_LIBOPENHITLS */ + +#if COAP_WITH_LIBOPENHITLS +#define IS_PSK 0x01 +#define IS_PKI 0x02 +#define COAP_HITLS_DTLS_OVERHEAD 37 +#define COAP_HITLS_IPV4_UDP_OVERHEAD 28 +#define COAP_HITLS_IPV6_UDP_OVERHEAD 48 +#define COAP_HITLS_COOKIE_SECRET_LEN 32 +#define COAP_HITLS_COOKIE_LEN 32 +/* +1 converts libcoap verify_depth to the intermediate-CA limit. + * +2 adds the leaf certificate and trust anchor for openHiTLS maxDepth. + */ +#define COAP_HITLS_VERIFY_DEPTH_TO_MAX_CHAIN_DEPTH(depth) \ + ((depth) + 1 + 2) + +typedef struct coap_hitls_context_t { + coap_context_t *coap_context; + coap_dtls_pki_t setup_data; + char *root_ca_file; + char *root_ca_dir; + int psk_pki_enabled; + int trust_store_defined; + int cookie_secret_set; + uint8_t cookie_secret[COAP_HITLS_COOKIE_SECRET_LEN]; +} coap_hitls_context_t; + +typedef struct coap_hitls_env_t { + HITLS_Ctx *ctx; + BSL_UIO *uio; + BSL_UIO_Method *method; + const uint8_t *pdu; + size_t pdu_len; + coap_session_t *session; + int established; + coap_dtls_role_t role; + coap_proto_t proto; + int hello_verify_sent; + int mtu_exceeded; + int had_fatal; + coap_tick_t last_timeout; +} coap_hitls_env_t; + +static coap_log_t dtls_log_level = COAP_LOG_EMERG; +#endif /* COAP_WITH_LIBOPENHITLS */ +static int coap_hitls_started = 0; +#if COAP_WITH_LIBOPENHITLS +static const uint8_t coap_hitls_alpn[] = { 4, 'c', 'o', 'a', 'p' }; +static const uint16_t coap_hitls_psk_cipher_suites[] = { + HITLS_PSK_WITH_AES_128_GCM_SHA256, + HITLS_PSK_WITH_AES_256_GCM_SHA384, + HITLS_PSK_WITH_AES_256_CCM, + HITLS_PSK_WITH_CHACHA20_POLY1305_SHA256 +}; +static const uint16_t coap_hitls_pki_cipher_suites[] = { + HITLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + HITLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + HITLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + HITLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + HITLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + HITLS_DHE_RSA_WITH_AES_256_GCM_SHA384 +}; +static const uint16_t coap_hitls_psk_pki_cipher_suites[] = { + HITLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + HITLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + HITLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + HITLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + HITLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + HITLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + HITLS_PSK_WITH_AES_128_GCM_SHA256, + HITLS_PSK_WITH_AES_256_GCM_SHA384, + HITLS_PSK_WITH_AES_256_CCM, + HITLS_PSK_WITH_CHACHA20_POLY1305_SHA256 +}; + +static void +coap_hitls_get_cipher_suites(int enabled, const uint16_t **cipher_suites, + uint32_t *cipher_suites_count) { + if ((enabled & IS_PSK) && (enabled & IS_PKI)) { + *cipher_suites = coap_hitls_psk_pki_cipher_suites; + *cipher_suites_count = + (uint32_t)(sizeof(coap_hitls_psk_pki_cipher_suites) / + sizeof(coap_hitls_psk_pki_cipher_suites[0])); + } else if (enabled & IS_PKI) { + *cipher_suites = coap_hitls_pki_cipher_suites; + *cipher_suites_count = + (uint32_t)(sizeof(coap_hitls_pki_cipher_suites) / + sizeof(coap_hitls_pki_cipher_suites[0])); + } else { + *cipher_suites = coap_hitls_psk_cipher_suites; + *cipher_suites_count = + (uint32_t)(sizeof(coap_hitls_psk_cipher_suites) / + sizeof(coap_hitls_psk_cipher_suites[0])); + } +} + +static void +coap_hitls_mark_fatal(coap_session_t *session) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + + if (env) + env->had_fatal = 1; +} + +static void +coap_hitls_log_err_stack(const coap_session_t *session) { + int32_t e; + + while ((e = BSL_ERR_GetError()) != BSL_SUCCESS) + coap_dtls_log(COAP_LOG_WARN, "* %s: openHiTLS error 0x%08x\n", + coap_session_str(session), (unsigned int)e); +} + +static void +coap_hitls_log_fatal_err_stack(coap_session_t *session) { + coap_hitls_mark_fatal(session); + coap_hitls_log_err_stack(session); +} + +/* TLS alert levels and descriptions as defined by RFC 5246 / RFC 8446. */ +enum { + ALERT_LEVEL_WARNING = 1, + ALERT_LEVEL_FATAL = 2, + ALERT_CLOSE_NOTIFY = 0, + ALERT_UNEXPECTED_MESSAGE = 10, + ALERT_BAD_RECORD_MAC = 20, + ALERT_RECORD_OVERFLOW = 22, + ALERT_HANDSHAKE_FAILURE = 40, + ALERT_BAD_CERTIFICATE = 42, + ALERT_UNSUPPORTED_CERTIFICATE = 43, + ALERT_CERTIFICATE_REVOKED = 44, + ALERT_CERTIFICATE_EXPIRED = 45, + ALERT_ILLEGAL_PARAMETER = 47, + ALERT_UNKNOWN_CA = 48, + ALERT_DECODE_ERROR = 50, + ALERT_DECRYPT_ERROR = 51, + ALERT_PROTOCOL_VERSION = 70, + ALERT_INSUFFICIENT_SECURITY = 71, + ALERT_INTERNAL_ERROR = 80, + ALERT_INAPPROPRIATE_FALLBACK = 86, + ALERT_NO_RENEGOTIATION = 100, + ALERT_MISSING_EXTENSION = 109, + ALERT_UNSUPPORTED_EXTENSION = 110, + ALERT_UNRECOGNIZED_NAME = 112, + ALERT_CERTIFICATE_REQUIRED = 116, + ALERT_NO_APPLICATION_PROTOCOL = 120 +}; + +static const char * +coap_hitls_alert_desc(int desc) { + switch (desc) { + case ALERT_CLOSE_NOTIFY: + return "close_notify"; + case ALERT_UNEXPECTED_MESSAGE: + return "unexpected_message"; + case ALERT_BAD_RECORD_MAC: + return "bad_record_mac"; + case ALERT_RECORD_OVERFLOW: + return "record_overflow"; + case ALERT_HANDSHAKE_FAILURE: + return "handshake_failure"; + case ALERT_BAD_CERTIFICATE: + return "bad_certificate"; + case ALERT_UNSUPPORTED_CERTIFICATE: + return "unsupported_certificate"; + case ALERT_CERTIFICATE_REVOKED: + return "certificate_revoked"; + case ALERT_CERTIFICATE_EXPIRED: + return "certificate_expired"; + case ALERT_ILLEGAL_PARAMETER: + return "illegal_parameter"; + case ALERT_UNKNOWN_CA: + return "unknown_ca"; + case ALERT_DECODE_ERROR: + return "decode_error"; + case ALERT_DECRYPT_ERROR: + return "decrypt_error"; + case ALERT_PROTOCOL_VERSION: + return "protocol_version"; + case ALERT_INSUFFICIENT_SECURITY: + return "insufficient_security"; + case ALERT_INTERNAL_ERROR: + return "internal_error"; + case ALERT_INAPPROPRIATE_FALLBACK: + return "inappropriate_fallback"; + case ALERT_NO_RENEGOTIATION: + return "no_renegotiation"; + case ALERT_MISSING_EXTENSION: + return "missing_extension"; + case ALERT_UNSUPPORTED_EXTENSION: + return "unsupported_extension"; + case ALERT_UNRECOGNIZED_NAME: + return "unrecognized_name"; + case ALERT_CERTIFICATE_REQUIRED: + return "certificate_required"; + case ALERT_NO_APPLICATION_PROTOCOL: + return "no_application_protocol"; + default: + return "unknown"; + } +} + +/* The info callback packs the alert as (level << 8) | description. */ +static void +coap_hitls_info_cb(const HITLS_Ctx *ctx, int32_t event_type, int32_t value) { + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + int alert_level = (value >> 8) & 0xff; + int alert_desc = value & 0xff; + const char *dir; + + if ((event_type & INDICATE_EVENT_ALERT) == 0 || !session) + return; + dir = (event_type & INDICATE_EVENT_READ) ? "received" : "sent"; + if (alert_level == ALERT_LEVEL_FATAL && alert_desc != ALERT_CLOSE_NOTIFY) + coap_log_warn("* %s: openHiTLS %s fatal alert: %s (%d)\n", + coap_session_str(session), dir, + coap_hitls_alert_desc(alert_desc), alert_desc); + else + coap_log_info("* %s: openHiTLS %s alert: %s (%d)\n", + coap_session_str(session), dir, + coap_hitls_alert_desc(alert_desc), alert_desc); +} + +static const char * +coap_hitls_verify_err_str(int32_t err) { + switch (err) { + case HITLS_X509_ERR_TIME_EXPIRED: + case HITLS_X509_ERR_VFY_NOTAFTER_EXPIRED: + return "certificate expired"; + case HITLS_X509_ERR_TIME_FUTURE: + case HITLS_X509_ERR_VFY_NOTBEFORE_IN_FUTURE: + return "certificate not yet valid"; + case HITLS_X509_ERR_ISSUE_CERT_NOT_FOUND: + return "issuer certificate not found"; + case HITLS_X509_ERR_ROOT_CERT_NOT_FOUND: + return "self-signed or root CA not found"; + case HITLS_X509_ERR_VFY_CRL_NOT_FOUND: + return "CRL not found"; + case HITLS_X509_ERR_VFY_THISUPDATE_IN_FUTURE: + case HITLS_X509_ERR_VFY_NEXTUPDATE_EXPIRED: + return "CRL expired or not yet valid"; + case HITLS_X509_ERR_VFY_CHECK_SECBITS: + return "certificate security strength too low"; + case HITLS_X509_ERR_VFY_HOSTNAME_FAIL: + return "hostname mismatch"; + case HITLS_X509_ERR_VFY_GET_NOTBEFORE_FAIL: + return "cannot read certificate validity start"; + case HITLS_X509_ERR_VFY_GET_NOTAFTER_FAIL: + return "cannot read certificate validity end"; + case BSL_SAL_TIME_SYS_ERROR: + return "system time unavailable"; + default: + return "certificate verification failed"; + } +} +#endif /* COAP_WITH_LIBOPENHITLS */ + +static int +coap_hitls_startup(void) { + if (!coap_hitls_started) { + int32_t ret = CRYPT_EAL_Init(CRYPT_EAL_INIT_ALL); + + if (ret != CRYPT_SUCCESS) { + coap_log_err("CRYPT_EAL_Init() returned 0x%x\n", (unsigned int)ret); + return 0; + } +#if COAP_WITH_LIBOPENHITLS + ret = HITLS_CertMethodInit(); + if (ret != HITLS_SUCCESS) { + coap_log_err("HITLS_CertMethodInit() returned 0x%x\n", + (unsigned int)ret); + CRYPT_EAL_Cleanup(CRYPT_EAL_INIT_ALL); + return 0; + } + HITLS_CryptMethodInit(); +#endif /* COAP_WITH_LIBOPENHITLS */ + coap_hitls_started = 1; + } + return coap_hitls_started; +} + +#if COAP_WITH_LIBOPENHITLS +static char * +coap_hitls_strdup(const char *s) { + size_t len; + char *copy; + + if (!s) + return NULL; + len = strlen(s); + copy = (char *)coap_malloc_type(COAP_STRING, len + 1); + if (!copy) + return NULL; + memcpy(copy, s, len + 1); + return copy; +} + +static uint8_t * +coap_hitls_read_file(const char *file, uint32_t *buf_len) { + FILE *fp; + long file_len; + uint8_t *buf = NULL; + size_t read_len; + + if (!file || !buf_len) + return NULL; + + fp = fopen(file, "rb"); + if (!fp) + return NULL; + + if (fseek(fp, 0, SEEK_END) != 0) + goto fail; + file_len = ftell(fp); + if (file_len <= 0 || (uintmax_t)file_len > UINT32_MAX) + goto fail; + if (fseek(fp, 0, SEEK_SET) != 0) + goto fail; + + buf = (uint8_t *)coap_malloc_type(COAP_STRING, (size_t)file_len); + if (!buf) + goto fail; + read_len = fread(buf, 1, (size_t)file_len, fp); + if (read_len != (size_t)file_len) { + coap_free_type(COAP_STRING, buf); + buf = NULL; + goto fail; + } + fclose(fp); + *buf_len = (uint32_t)read_len; + return buf; + +fail: + fclose(fp); + return NULL; +} + +static size_t +coap_hitls_strnlen(const uint8_t *s, size_t max_len) { + size_t len = 0; + + if (!s) + return 0; + while (len < max_len && s[len]) + len++; + return len; +} + +static uint32_t +coap_hitls_copy_bin(uint8_t *dst, uint32_t dst_len, + const coap_bin_const_t *src, int add_nul) { + size_t extra = add_nul ? 1 : 0; + + if (!dst || !src || !src->s || + extra > (size_t)dst_len || src->length > (size_t)dst_len - extra) + return 0; + memcpy(dst, src->s, src->length); + if (add_nul) + dst[src->length] = '\000'; + return (uint32_t)src->length; +} + +#if COAP_SERVER_SUPPORT +static int +coap_hitls_cookie_mac(coap_session_t *session, uint8_t *cookie, + uint32_t *cookie_len) { + static const uint8_t cookie_label[] = "libcoap openhitls dtls cookie"; + coap_hitls_context_t *context; + CRYPT_EAL_MacCtx *ctx = NULL; + uint32_t out_len; + int ret = 0; + + if (!session || !session->context || !cookie || !cookie_len || + *cookie_len < COAP_HITLS_COOKIE_LEN || + session->addr_info.local.size == 0 || + session->addr_info.remote.size == 0) + return 0; + + context = (coap_hitls_context_t *)session->context->dtls_context; + if (!context || !context->cookie_secret_set) + return 0; + + ctx = CRYPT_EAL_MacNewCtx(CRYPT_MAC_HMAC_SHA256); + if (!ctx) + return 0; + + out_len = *cookie_len; + if (CRYPT_EAL_MacInit(ctx, context->cookie_secret, + COAP_HITLS_COOKIE_SECRET_LEN) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacUpdate(ctx, cookie_label, + (uint32_t)sizeof(cookie_label) - 1) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacUpdate(ctx, + (const uint8_t *)&session->addr_info.local.addr, + (uint32_t)session->addr_info.local.size) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacUpdate(ctx, + (const uint8_t *)&session->addr_info.remote.addr, + (uint32_t)session->addr_info.remote.size) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacFinal(ctx, cookie, &out_len) != CRYPT_SUCCESS || + out_len != COAP_HITLS_COOKIE_LEN) + goto finish; + + *cookie_len = out_len; + ret = 1; + +finish: + CRYPT_EAL_MacFreeCtx(ctx); + return ret; +} + +static int +coap_hitls_cookie_equal(const uint8_t *a, const uint8_t *b, uint32_t len) { + uint8_t diff = 0; + uint32_t i; + + for (i = 0; i < len; i++) + diff |= (uint8_t)(a[i] ^ b[i]); + return diff == 0; +} + +static int32_t +coap_hitls_cookie_gen_cb(HITLS_Ctx *ctx, uint8_t *cookie, + uint32_t *cookie_len) { + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + + return coap_hitls_cookie_mac(session, cookie, cookie_len) ? + HITLS_COOKIE_GENERATE_SUCCESS : HITLS_COOKIE_GENERATE_ERROR; +} + +static int32_t +coap_hitls_cookie_verify_cb(HITLS_Ctx *ctx, const uint8_t *cookie, + uint32_t cookie_len) { + uint8_t expected[COAP_HITLS_COOKIE_LEN]; + uint32_t expected_len = sizeof(expected); + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + + if (!cookie || cookie_len != COAP_HITLS_COOKIE_LEN || + !coap_hitls_cookie_mac(session, expected, &expected_len) || + expected_len != cookie_len || + !coap_hitls_cookie_equal(cookie, expected, cookie_len)) + return HITLS_COOKIE_VERIFY_ERROR; + return HITLS_COOKIE_VERIFY_SUCCESS; +} + +static uint32_t +coap_hitls_u24(const uint8_t *p) { + return ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | p[2]; +} + +static int +coap_hitls_client_hello_cookie_valid(coap_session_t *session, + const uint8_t *data, size_t data_len) { + uint8_t expected[COAP_HITLS_COOKIE_LEN]; + uint32_t expected_len = sizeof(expected); + size_t body_offset = 13 + 12; + size_t body_end; + size_t offset; + uint32_t record_len; + uint32_t hs_len; + uint32_t frag_offset; + uint32_t frag_len; + uint8_t session_id_len; + uint8_t cookie_len; + + if (!data || data_len < body_offset || data[0] != 22 || data[13] != 1) + return -1; + + record_len = ((uint32_t)data[11] << 8) | data[12]; + hs_len = coap_hitls_u24(&data[14]); + frag_offset = coap_hitls_u24(&data[19]); + frag_len = coap_hitls_u24(&data[22]); + if (record_len > data_len - 13 || hs_len > record_len - 12 || + frag_offset != 0 || frag_len != hs_len) + return -1; + + body_end = body_offset + hs_len; + if (body_end > data_len || body_end < body_offset + 35) + return -1; + + offset = body_offset + 34; + session_id_len = data[offset++]; + if (offset + session_id_len + 1 > body_end) + return -1; + offset += session_id_len; + + cookie_len = data[offset++]; + if (cookie_len == 0) + return 0; + if (offset + cookie_len > body_end || + cookie_len != COAP_HITLS_COOKIE_LEN || + !coap_hitls_cookie_mac(session, expected, &expected_len) || + expected_len != cookie_len) + return -1; + + return coap_hitls_cookie_equal(&data[offset], expected, cookie_len) ? 1 : -1; +} +#endif /* COAP_SERVER_SUPPORT */ + +static int +coap_hitls_pki_len(const uint8_t *buf, size_t len, HITLS_ParseFormat format, + uint32_t *out_len) { + if (!buf) + return 0; + if (format == TLS_PARSE_FORMAT_PEM && len && buf[len - 1] == '\000') + len--; + if (len > UINT32_MAX) + return 0; + *out_len = (uint32_t)len; + return 1; +} + +static int +coap_hitls_key_define_supported(coap_pki_define_t define) { + switch (define) { + case COAP_PKI_KEY_DEF_PEM: + case COAP_PKI_KEY_DEF_PEM_BUF: + case COAP_PKI_KEY_DEF_DER: + case COAP_PKI_KEY_DEF_DER_BUF: + return 1; + case COAP_PKI_KEY_DEF_RPK_BUF: + case COAP_PKI_KEY_DEF_PKCS11: + case COAP_PKI_KEY_DEF_PKCS11_RPK: + case COAP_PKI_KEY_DEF_ENGINE: + default: + return 0; + } +} + +static int +coap_hitls_check_define_key(coap_dtls_key_t *key, coap_define_issue_key_t type, + coap_pki_define_t define, + const coap_dtls_role_t role) { + if (coap_hitls_key_define_supported(define)) + return 1; + return coap_dtls_define_issue(type, COAP_DEFINE_FAIL_NOT_SUPPORTED, key, + role, 0); +} + +static int +coap_hitls_check_pki_key_supported(const coap_dtls_pki_t *setup_data, + const coap_dtls_role_t role) { + coap_dtls_key_t key; + + if (setup_data->is_rpk_not_cert) { + coap_log_warn("openHiTLS backend has no RPK support\n"); + return 0; + } + if (setup_data->pki_key.key_type == COAP_PKI_KEY_PKCS11) { + coap_log_warn("openHiTLS backend has no PKCS11 support\n"); + return 0; + } + + coap_dtls_map_key_type_to_define(setup_data, &key); + if (key.key_type != COAP_PKI_KEY_DEFINE) + return 0; + + return coap_hitls_check_define_key(&key, COAP_DEFINE_KEY_CA, + key.key.define.ca_def, role) && + coap_hitls_check_define_key(&key, COAP_DEFINE_KEY_PUBLIC, + key.key.define.public_cert_def, role) && + coap_hitls_check_define_key(&key, COAP_DEFINE_KEY_PRIVATE, + key.key.define.private_key_def, role); +} + +static int +coap_hitls_verify_error_allowed(const coap_dtls_pki_t *setup_data, + int32_t err_code) { + if (err_code == HITLS_PKI_SUCCESS) + return 1; + if (!setup_data || !setup_data->verify_peer_cert) + return 1; + + switch (err_code) { + case HITLS_X509_ERR_TIME_EXPIRED: + case HITLS_X509_ERR_TIME_FUTURE: + case HITLS_X509_ERR_VFY_NOTBEFORE_IN_FUTURE: + case HITLS_X509_ERR_VFY_NOTAFTER_EXPIRED: + return setup_data->allow_expired_certs; + case HITLS_X509_ERR_VFY_CRL_NOT_FOUND: + return !setup_data->check_cert_revocation || setup_data->allow_no_crl; + case HITLS_X509_ERR_VFY_THISUPDATE_IN_FUTURE: + case HITLS_X509_ERR_VFY_NEXTUPDATE_EXPIRED: + return !setup_data->check_cert_revocation || + setup_data->allow_expired_crl; + default: + return 0; + } +} + +static int +coap_hitls_get_verify_session(HITLS_CERT_StoreCtx *store_ctx, + coap_session_t **session) { + HITLS_Ctx *ctx = NULL; + + if (!store_ctx || !session) + return 0; + *session = NULL; + if (HITLS_X509_StoreCtxCtrl((HITLS_X509_StoreCtx *)store_ctx, + HITLS_X509_STORECTX_GET_USR_DATA, + &ctx, sizeof(ctx)) != HITLS_PKI_SUCCESS || + !ctx) + return 0; + *session = (coap_session_t *)HITLS_GetUserData(ctx); + return *session != NULL; +} + +static int +coap_hitls_get_verify_cert(HITLS_CERT_StoreCtx *store_ctx, + HITLS_X509_Cert **cert, int32_t *depth) { + if (!store_ctx || !cert || !depth) + return 0; + + *cert = NULL; + *depth = 0; + (void)HITLS_X509_StoreCtxCtrl((HITLS_X509_StoreCtx *)store_ctx, + HITLS_X509_STORECTX_GET_CUR_DEPTH, + depth, sizeof(*depth)); + return HITLS_X509_StoreCtxCtrl((HITLS_X509_StoreCtx *)store_ctx, + HITLS_X509_STORECTX_GET_CUR_CERT, + cert, sizeof(*cert)) == HITLS_PKI_SUCCESS && + *cert; +} + +static int +coap_hitls_cert_is_self_signed(HITLS_X509_Cert *cert) { + bool self_signed = false; + + return cert && + HITLS_X509_CertCtrl(cert, HITLS_X509_IS_SELF_SIGNED, + &self_signed, + sizeof(self_signed)) == HITLS_PKI_SUCCESS && + self_signed; +} + +/* + * The self-signed override bypasses the library chain verification, which is + * where the validity period is normally checked. Apply the same check here, + * using the library time helpers, so an expired or not-yet-valid self-signed + * leaf is not accepted. Returns HITLS_PKI_SUCCESS or a specific failure code. + */ +static int32_t +coap_hitls_cert_time_valid(HITLS_X509_Cert *cert) { + BSL_TIME not_before; + BSL_TIME not_after; + int64_t start; + int64_t end; + int64_t now = BSL_SAL_CurrentSysTimeGet(); + + if (now <= 0) + return BSL_SAL_TIME_SYS_ERROR; + if (HITLS_X509_CertCtrl(cert, HITLS_X509_GET_BEFORE_TIME, ¬_before, + sizeof(not_before)) != HITLS_PKI_SUCCESS || + BSL_SAL_DateToUtcTimeConvert(¬_before, &start) != BSL_SUCCESS) + return HITLS_X509_ERR_VFY_GET_NOTBEFORE_FAIL; + if (start > now) + return HITLS_X509_ERR_VFY_NOTBEFORE_IN_FUTURE; + if (HITLS_X509_CertCtrl(cert, HITLS_X509_GET_AFTER_TIME, ¬_after, + sizeof(not_after)) != HITLS_PKI_SUCCESS || + BSL_SAL_DateToUtcTimeConvert(¬_after, &end) != BSL_SUCCESS) + return HITLS_X509_ERR_VFY_GET_NOTAFTER_FAIL; + if (end < now) + return HITLS_X509_ERR_VFY_NOTAFTER_EXPIRED; + return HITLS_PKI_SUCCESS; +} + +static int +coap_hitls_get_peer_leaf_cert(HITLS_CERT_StoreCtx *store_ctx, + HITLS_X509_Cert **cert, + int32_t *chain_count) { + HITLS_X509_List *peer_chain = NULL; + + if (!store_ctx || !cert || !chain_count) + return 0; + + *cert = NULL; + *chain_count = 0; + if (HITLS_X509_StoreCtxCtrl((HITLS_X509_StoreCtx *)store_ctx, + HITLS_X509_STORECTX_GET_PEER_CERT_CHAIN, + &peer_chain, + sizeof(peer_chain)) != HITLS_PKI_SUCCESS || + !peer_chain) + return 0; + + *chain_count = BSL_LIST_COUNT(peer_chain); + *cert = (HITLS_X509_Cert *)BSL_LIST_FIRST_ELMT(peer_chain); + return *cert != NULL; +} + +static int +coap_hitls_self_signed_leaf_allowed(const coap_dtls_pki_t *setup_data, + HITLS_CERT_StoreCtx *store_ctx, + HITLS_X509_Cert **leaf_cert) { + HITLS_X509_Cert *cert = NULL; + int32_t chain_count = 0; + + if (leaf_cert) + *leaf_cert = NULL; + if (!setup_data || !setup_data->allow_self_signed || + setup_data->check_common_ca) + return 0; + if (!coap_hitls_get_peer_leaf_cert(store_ctx, &cert, &chain_count) || + chain_count != 1 || !coap_hitls_cert_is_self_signed(cert)) + return 0; + if (leaf_cert) + *leaf_cert = cert; + return 1; +} + +static int +coap_hitls_verify_cb_self_signed_allowed(const coap_dtls_pki_t *setup_data, + HITLS_CERT_StoreCtx *store_ctx) { + HITLS_X509_Cert *cert = NULL; + int32_t depth = 0; + + return setup_data && setup_data->allow_self_signed && + !setup_data->check_common_ca && + coap_hitls_get_verify_cert(store_ctx, &cert, &depth) && + depth == 0 && coap_hitls_cert_is_self_signed(cert); +} + +static int +coap_hitls_validate_cn_cert(coap_session_t *session, + const coap_dtls_pki_t *setup_data, + HITLS_X509_Cert *cert, + unsigned depth, + int validated) { + uint8_t *der = NULL; + uint32_t der_len = 0; + BSL_Buffer cn; + int ret; + + if (!setup_data->validate_cn_call_back) + return 1; + memset(&cn, 0, sizeof(cn)); + (void)HITLS_X509_CertCtrl(cert, HITLS_X509_GET_ENCODELEN, + &der_len, sizeof(der_len)); + if (der_len) + (void)HITLS_X509_CertCtrl(cert, HITLS_X509_GET_ENCODE, + &der, sizeof(der)); + (void)HITLS_X509_CertCtrl(cert, HITLS_X509_GET_SUBJECT_CN_STR, + &cn, sizeof(cn)); + + coap_lock_callback_ret(ret, + setup_data->validate_cn_call_back( + cn.data ? (const char *)cn.data : "", + der, der_len, session, depth, validated, + setup_data->cn_call_back_arg)); + if (cn.data) + BSL_SAL_Free(cn.data); + return ret; +} + +static int +coap_hitls_verify_cb(int32_t err_code, HITLS_CERT_StoreCtx *store_ctx) { + coap_session_t *session = NULL; + coap_hitls_context_t *context; + coap_dtls_pki_t *setup_data; + HITLS_X509_Cert *cert; + int32_t depth; + int allowed; + + if (!coap_hitls_get_verify_session(store_ctx, &session) || + !session->context || !session->context->dtls_context) + return err_code; + + context = (coap_hitls_context_t *)session->context->dtls_context; + setup_data = &context->setup_data; + if (err_code == HITLS_X509_ERR_ISSUE_CERT_NOT_FOUND || + err_code == HITLS_X509_ERR_ROOT_CERT_NOT_FOUND) { + allowed = coap_hitls_verify_cb_self_signed_allowed(setup_data, + store_ctx); + } else { + allowed = coap_hitls_verify_error_allowed(setup_data, err_code); + } + if (!allowed) { + coap_log_warn("* %s: certificate verification failed: %s (0x%x)\n", + coap_session_str(session), + coap_hitls_verify_err_str(err_code), (unsigned int)err_code); + return err_code; + } + + if (setup_data->validate_cn_call_back && + coap_hitls_get_verify_cert(store_ctx, &cert, &depth)) { + if (!coap_hitls_validate_cn_cert(session, setup_data, cert, + depth < 0 ? 0 : (unsigned)depth, + err_code == HITLS_PKI_SUCCESS)) { + coap_log_warn("* %s: certificate verification failed: %s\n", + coap_session_str(session), + coap_hitls_verify_err_str(HITLS_X509_ERR_VFY_HOSTNAME_FAIL)); + return HITLS_X509_ERR_VFY_HOSTNAME_FAIL; + } + } + + return HITLS_PKI_SUCCESS; +} + +static int32_t +coap_hitls_app_verify_cb(HITLS_CERT_StoreCtx *store_ctx, + void *arg COAP_UNUSED) { + coap_session_t *session = NULL; + coap_hitls_context_t *context; + coap_dtls_pki_t *setup_data; + HITLS_X509_List *peer_chain = NULL; + HITLS_X509_Cert *leaf_cert = NULL; + int32_t ret; + + if (!coap_hitls_get_verify_session(store_ctx, &session) || + !session->context || !session->context->dtls_context) + return HITLS_X509_ERR_INVALID_PARAM; + + if (HITLS_X509_StoreCtxCtrl((HITLS_X509_StoreCtx *)store_ctx, + HITLS_X509_STORECTX_GET_PEER_CERT_CHAIN, + &peer_chain, + sizeof(peer_chain)) != HITLS_PKI_SUCCESS || + !peer_chain) + return HITLS_X509_ERR_INVALID_PARAM; + + ret = HITLS_X509_CertVerify((HITLS_X509_StoreCtx *)store_ctx, peer_chain); + if (ret == HITLS_PKI_SUCCESS) + return HITLS_APP_VERIFY_CALLBACK_SUCCESS; + + context = (coap_hitls_context_t *)session->context->dtls_context; + setup_data = &context->setup_data; + if ((ret == HITLS_X509_ERR_ISSUE_CERT_NOT_FOUND || + ret == HITLS_X509_ERR_ROOT_CERT_NOT_FOUND) && + coap_hitls_self_signed_leaf_allowed(setup_data, store_ctx, + &leaf_cert)) { + if (!setup_data->allow_expired_certs) { + int32_t time_ret = coap_hitls_cert_time_valid(leaf_cert); + + if (time_ret != HITLS_PKI_SUCCESS) { + coap_log_warn("* %s: certificate verification failed: %s\n", + coap_session_str(session), + coap_hitls_verify_err_str(time_ret)); + return time_ret; + } + } + coap_log_info(" %s: %s: overridden: 'self-signed' depth=0\n", + coap_session_str(session), + coap_hitls_verify_err_str(ret)); + if (!coap_hitls_validate_cn_cert(session, setup_data, leaf_cert, 0, 0)) + return HITLS_X509_ERR_VFY_HOSTNAME_FAIL; + return HITLS_APP_VERIFY_CALLBACK_SUCCESS; + } + + return ret; +} + +static int +coap_hitls_is_retry(int32_t ret) { + switch (ret) { + case HITLS_WANT_CONNECT: + case HITLS_WANT_ACCEPT: + case HITLS_WANT_READ: + case HITLS_WANT_WRITE: + case HITLS_WANT_BACKUP: + case HITLS_WANT_CLIENT_HELLO_CB: + case HITLS_WANT_X509_LOOKUP: + case HITLS_REC_NORMAL_IO_BUSY: + case HITLS_REC_NORMAL_RECV_BUF_EMPTY: + return 1; + default: + return 0; + } +} + +static int +coap_hitls_is_closed(int32_t ret) { + return ret == HITLS_CM_LINK_CLOSED; +} + +static uint8_t +coap_hitls_udp_overhead(const coap_hitls_env_t *env) { +#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI) && !defined(RIOT_VERSION) + if (env && env->session) { + switch (env->session->addr_info.remote.addr.sa.sa_family) { +#ifdef AF_INET6 + case AF_INET6: + return COAP_HITLS_IPV6_UDP_OVERHEAD; +#endif /* AF_INET6 */ +#ifdef AF_INET + case AF_INET: +#endif /* AF_INET */ + default: + break; + } + } +#else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ + (void)env; +#endif /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ + return COAP_HITLS_IPV4_UDP_OVERHEAD; +} + +static void +coap_hitls_set_connected(coap_session_t *session, coap_hitls_env_t *env) { + if (env->established) + return; + + env->established = 1; + if (session->state == COAP_SESSION_STATE_HANDSHAKE) { + coap_handle_event_lkd(session->context, COAP_EVENT_DTLS_CONNECTED, + session); + session->sock.lfunc[COAP_LAYER_TLS].l_establish(session); + } +} + +static int +coap_hitls_check_handshake_done(coap_session_t *session, + coap_hitls_env_t *env) { + uint8_t done = 0; + + if (HITLS_IsHandShakeDone(env->ctx, &done) == HITLS_SUCCESS && done) { + coap_hitls_set_connected(session, env); + return 1; + } + return 0; +} + +static int +coap_hitls_handshake(coap_session_t *session, coap_hitls_env_t *env) { + int32_t ret; + + BSL_ERR_ClearError(); + if (env->role == COAP_DTLS_ROLE_CLIENT) + ret = HITLS_Connect(env->ctx); + else + ret = HITLS_Accept(env->ctx); + + if (ret == HITLS_SUCCESS) + return coap_hitls_check_handshake_done(session, env); + if (coap_hitls_check_handshake_done(session, env)) + return 1; + if (coap_hitls_is_retry(ret)) + return 0; + + coap_log_warn("coap_hitls_handshake: returned 0x%x\n", (unsigned int)ret); + coap_hitls_log_fatal_err_stack(session); + session->dtls_event = COAP_EVENT_DTLS_ERROR; + return -1; +} + +static int32_t +coap_hitls_uio_write(BSL_UIO *uio, const void *buf, uint32_t len, + uint32_t *write_len) { + coap_hitls_env_t *env = (coap_hitls_env_t *)BSL_UIO_GetUserData(uio); + ssize_t ret; + + if (write_len) + *write_len = 0; + if (!env || !env->session || !buf || !write_len) + return BSL_NULL_INPUT; + if (!coap_netif_available(env->session) +#if COAP_SERVER_SUPPORT + && env->session->endpoint == NULL +#endif /* COAP_SERVER_SUPPORT */ + ) { + errno = ECONNRESET; + return BSL_UIO_IO_EXCEPTION; + } + + (void)BSL_UIO_ClearFlags(uio, BSL_UIO_FLAGS_RWS | BSL_UIO_FLAGS_SHOULD_RETRY); + ret = env->session->sock.lfunc[COAP_LAYER_TLS].l_write(env->session, + (const uint8_t *)buf, len); + if (ret < 0) { +#ifdef EMSGSIZE + if (errno == EMSGSIZE) { + env->mtu_exceeded = 1; + (void)BSL_UIO_SetFlags(uio, BSL_UIO_FLAGS_WRITE | BSL_UIO_FLAGS_SHOULD_RETRY); + return BSL_SUCCESS; + } +#endif /* EMSGSIZE */ + if (errno == ENOTCONN || errno == ECONNREFUSED) + env->session->dtls_event = COAP_EVENT_DTLS_ERROR; + return BSL_UIO_IO_EXCEPTION; + } + if (ret == 0) { + (void)BSL_UIO_SetFlags(uio, BSL_UIO_FLAGS_WRITE | BSL_UIO_FLAGS_SHOULD_RETRY); + return BSL_SUCCESS; + } + *write_len = (uint32_t)ret; + return BSL_SUCCESS; +} + +static int32_t +coap_hitls_uio_read(BSL_UIO *uio, void *buf, uint32_t len, + uint32_t *read_len) { + coap_hitls_env_t *env = (coap_hitls_env_t *)BSL_UIO_GetUserData(uio); + size_t copy_len; + + if (read_len) + *read_len = 0; + if (!env || !buf || !read_len) + return BSL_NULL_INPUT; + if (env->proto == COAP_PROTO_TLS) { + ssize_t ret; + + (void)BSL_UIO_ClearFlags(uio, BSL_UIO_FLAGS_RWS | BSL_UIO_FLAGS_SHOULD_RETRY); + ret = env->session->sock.lfunc[COAP_LAYER_TLS].l_read(env->session, + (uint8_t *)buf, len); + if (ret < 0) + return errno == ECONNRESET ? BSL_UIO_IO_EOF : BSL_UIO_IO_EXCEPTION; + if (ret == 0) { + (void)BSL_UIO_SetFlags(uio, BSL_UIO_FLAGS_READ | BSL_UIO_FLAGS_SHOULD_RETRY); + return BSL_SUCCESS; + } + *read_len = (uint32_t)ret; + return BSL_SUCCESS; + } + if (!env->pdu || env->pdu_len == 0) { + (void)BSL_UIO_SetFlags(uio, BSL_UIO_FLAGS_READ | BSL_UIO_FLAGS_SHOULD_RETRY); + return BSL_SUCCESS; + } + + copy_len = env->pdu_len < len ? env->pdu_len : len; + memcpy(buf, env->pdu, copy_len); + env->pdu += copy_len; + env->pdu_len -= copy_len; + *read_len = (uint32_t)copy_len; + return BSL_SUCCESS; +} + +static int32_t +coap_hitls_uio_ctrl(BSL_UIO *uio, int32_t cmd, int32_t larg, void *parg) { + coap_hitls_env_t *env = (coap_hitls_env_t *)BSL_UIO_GetUserData(uio); + + if (!env) + return BSL_NULL_INPUT; + + switch (cmd) { + case BSL_UIO_GET_FD: + if (parg) { +#if defined(_WIN32) + *(int32_t *)parg = -1; +#elif COAP_SERVER_SUPPORT + *(int32_t *)parg = (int32_t)(COAP_PROTO_NOT_RELIABLE(env->session->proto) ? + env->session->type != COAP_SESSION_TYPE_CLIENT ? + env->session->endpoint->sock.fd : + env->session->sock.fd : + env->session->sock.fd); +#else /* ! _WIN32 && ! COAP_SERVER_SUPPORT */ + *(int32_t *)parg = (int32_t)env->session->sock.fd; +#endif /* ! _WIN32 */ + } + return BSL_SUCCESS; + case BSL_UIO_SET_FD: + case BSL_UIO_SET_PEER_IP_ADDR: + case BSL_UIO_UDP_SET_CONNECTED: + case BSL_UIO_FLUSH: + case BSL_UIO_RESET: + return BSL_SUCCESS; + case BSL_UIO_GET_PEER_IP_ADDR: + if (!parg || !env->session) + return BSL_NULL_INPUT; + if (larg == (int32_t)sizeof(BSL_UIO_CtrlGetPeerIpAddrParam)) { + BSL_UIO_CtrlGetPeerIpAddrParam *param = + (BSL_UIO_CtrlGetPeerIpAddrParam *)parg; + uint32_t addr_len = (uint32_t)env->session->addr_info.remote.size; + + if (!param->addr || param->size < addr_len) + return BSL_INVALID_ARG; + memcpy(param->addr, &env->session->addr_info.remote.addr.sa, addr_len); + param->size = addr_len; + return BSL_SUCCESS; + } + if (env->session->addr_info.remote.size > (socklen_t)larg) + return BSL_INVALID_ARG; + memcpy(parg, &env->session->addr_info.remote.addr.sa, + env->session->addr_info.remote.size); + return BSL_SUCCESS; + case BSL_UIO_PENDING: + case BSL_UIO_WPENDING: + if (parg) + *(uint64_t *)parg = cmd == BSL_UIO_PENDING ? env->pdu_len : 0; + return BSL_SUCCESS; + case BSL_UIO_UDP_GET_MTU_OVERHEAD: + if (!parg) + return BSL_NULL_INPUT; + if (larg != (int32_t)sizeof(uint8_t)) + return BSL_INVALID_ARG; + *(uint8_t *)parg = coap_hitls_udp_overhead(env); + return BSL_SUCCESS; + case BSL_UIO_UDP_QUERY_MTU: + if (!parg) + return BSL_NULL_INPUT; + if (larg != (int32_t)sizeof(uint32_t)) + return BSL_INVALID_ARG; + *(uint32_t *)parg = env->session->mtu > UINT32_MAX ? + UINT32_MAX : (uint32_t)env->session->mtu; + return BSL_SUCCESS; + case BSL_UIO_UDP_MTU_EXCEEDED: + if (!parg) + return BSL_NULL_INPUT; + if (larg != (int32_t)sizeof(bool)) + return BSL_INVALID_ARG; + *(bool *)parg = env->mtu_exceeded != 0; + env->mtu_exceeded = 0; + return BSL_SUCCESS; + default: + return BSL_SUCCESS; + } +} + +static int +coap_hitls_setup_uio(coap_hitls_env_t *env) { + BSL_UIO *uio; + BSL_UIO_TransportType type = + env->proto == COAP_PROTO_DTLS ? BSL_UIO_UDP : BSL_UIO_TCP; + + env->method = BSL_UIO_NewMethod(); + if (!env->method) + return 0; +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif /* defined(__GNUC__) */ + if (BSL_UIO_SetMethodType(env->method, type) != BSL_SUCCESS || + BSL_UIO_SetMethod(env->method, BSL_UIO_WRITE_CB, + (void *)coap_hitls_uio_write) != BSL_SUCCESS || + BSL_UIO_SetMethod(env->method, BSL_UIO_READ_CB, + (void *)coap_hitls_uio_read) != BSL_SUCCESS || + BSL_UIO_SetMethod(env->method, BSL_UIO_CTRL_CB, + (void *)coap_hitls_uio_ctrl) != BSL_SUCCESS) { + BSL_UIO_FreeMethod(env->method); + env->method = NULL; + return 0; + } +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif /* defined(__GNUC__) */ + + uio = BSL_UIO_New(env->method); + if (!uio) + return 0; + if (BSL_UIO_SetUserData(uio, env) != BSL_SUCCESS) { + BSL_UIO_Free(uio); + return 0; + } + BSL_UIO_SetInit(uio, true); + if (HITLS_SetUio(env->ctx, uio) != HITLS_SUCCESS) { + BSL_UIO_SetUserData(uio, NULL); + BSL_UIO_Free(uio); + return 0; + } + env->uio = HITLS_GetUio(env->ctx); + BSL_UIO_Free(uio); + return 1; +} + +static void +coap_hitls_update_mtu(coap_hitls_env_t *env) { + uint16_t mtu; + int32_t ret; + + if (!env || !env->ctx || !env->session || env->proto != COAP_PROTO_DTLS) + return; + + mtu = env->session->mtu > UINT16_MAX ? UINT16_MAX : + (uint16_t)env->session->mtu; + ret = HITLS_SetMtu(env->ctx, mtu); + if (ret != HITLS_SUCCESS) { + coap_log_warn("HITLS_SetMtu(%u) returned 0x%x\n", mtu, + (unsigned int)ret); + } +} + +#if COAP_CLIENT_SUPPORT +static uint32_t +coap_hitls_psk_client_cb(HITLS_Ctx *ctx, const uint8_t *hint, + uint8_t *identity, uint32_t max_identity_len, + uint8_t *psk, uint32_t max_psk_len) { + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + coap_dtls_cpsk_t *setup_data; + const coap_dtls_cpsk_info_t *cpsk_info; + const coap_bin_const_t *psk_identity; + const coap_bin_const_t *psk_key; + + if (!session || !session->context) + return 0; + + setup_data = &session->cpsk_setup_data; + if (setup_data->validate_ih_call_back) { + coap_bin_const_t temp; + coap_str_const_t lhint; + + temp.s = hint ? hint : (const uint8_t *)""; + temp.length = coap_hitls_strnlen(temp.s, COAP_DTLS_MAX_PSK_IDENTITY); + coap_session_refresh_psk_hint(session, &temp); + + lhint.s = temp.s; + lhint.length = temp.length; + coap_lock_callback_ret(cpsk_info, + setup_data->validate_ih_call_back(&lhint, + session, + setup_data->ih_call_back_arg)); + if (!cpsk_info) + return 0; + coap_session_refresh_psk_identity(session, &cpsk_info->identity); + coap_session_refresh_psk_key(session, &cpsk_info->key); + psk_identity = &cpsk_info->identity; + psk_key = &cpsk_info->key; + } else { + psk_identity = coap_get_session_client_psk_identity(session); + psk_key = coap_get_session_client_psk_key(session); + } + + if (coap_hitls_copy_bin(identity, max_identity_len, psk_identity, 1) == 0 || + coap_hitls_copy_bin(psk, max_psk_len, psk_key, 0) == 0) + return 0; + return (uint32_t)psk_key->length; +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT +static HITLS_Config *coap_hitls_new_server_sni_config(coap_session_t *session, coap_proto_t proto, + const coap_dtls_key_t *new_key); + +static int32_t +coap_hitls_sni_cb(HITLS_Ctx *ctx, int *alert COAP_UNUSED, + void *arg COAP_UNUSED) { + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + coap_hitls_context_t *context = + session && session->context ? + (coap_hitls_context_t *)session->context->dtls_context : NULL; + coap_dtls_pki_t *pki_setup_data = context ? &context->setup_data : NULL; + coap_dtls_spsk_t *psk_setup_data; + const char *sni; + int accepted = 0; + + if (!session || !session->context) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + sni = HITLS_GetServerName(ctx, HITLS_SNI_HOSTNAME_TYPE); + if (!sni) + sni = ""; + + if (pki_setup_data && pki_setup_data->validate_sni_call_back) { + coap_dtls_key_t *new_key; + HITLS_Config *new_config; + + coap_lock_callback_ret(new_key, + pki_setup_data->validate_sni_call_back( + sni, pki_setup_data->sni_call_back_arg)); + if (!new_key) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + new_config = coap_hitls_new_server_sni_config(session, session->proto, + new_key); + if (!new_config) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + if (!HITLS_SetNewConfig(ctx, new_config)) { + HITLS_CFG_FreeConfig(new_config); + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + } + HITLS_CFG_FreeConfig(new_config); + accepted = 1; + } + + psk_setup_data = &session->context->spsk_setup_data; + if (psk_setup_data->validate_sni_call_back) { + const coap_dtls_spsk_info_t *new_entry; + + coap_lock_callback_ret(new_entry, + psk_setup_data->validate_sni_call_back( + sni, session, psk_setup_data->sni_call_back_arg)); + if (!new_entry) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + if (!coap_session_refresh_psk_hint(session, &new_entry->hint) || + !coap_session_refresh_psk_key(session, &new_entry->key)) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + if (new_entry->hint.s && new_entry->hint.length > 0 && + new_entry->hint.length <= UINT32_MAX && + HITLS_SetPskIdentityHint(ctx, new_entry->hint.s, + (uint32_t)new_entry->hint.length) != HITLS_SUCCESS) + return HITLS_ACCEPT_SNI_ERR_ALERT_FATAL; + accepted = 1; + } + + return accepted ? HITLS_ACCEPT_SNI_ERR_OK : HITLS_ACCEPT_SNI_ERR_NOACK; +} + +static uint32_t +coap_hitls_psk_server_cb(HITLS_Ctx *ctx, const uint8_t *identity, + uint8_t *psk, uint32_t max_psk_len) { + coap_session_t *session = (coap_session_t *)HITLS_GetUserData(ctx); + coap_dtls_spsk_t *setup_data; + coap_bin_const_t lidentity; + const coap_bin_const_t *psk_key; + + if (!session || !session->context) + return 0; + + setup_data = &session->context->spsk_setup_data; + lidentity.s = identity ? identity : (const uint8_t *)""; + lidentity.length = coap_hitls_strnlen(lidentity.s, + COAP_DTLS_MAX_PSK_IDENTITY); + coap_session_refresh_psk_identity(session, &lidentity); + + if (setup_data->validate_id_call_back) { + coap_lock_callback_ret(psk_key, + setup_data->validate_id_call_back(&lidentity, + session, + setup_data->id_call_back_arg)); + coap_session_refresh_psk_key(session, psk_key); + } else { + psk_key = coap_get_session_server_psk_key(session); + } + + if (coap_hitls_copy_bin(psk, max_psk_len, psk_key, 0) == 0) + return 0; + return (uint32_t)psk_key->length; +} +#endif /* COAP_SERVER_SUPPORT */ + +static int32_t +coap_hitls_alpn_select_cb(HITLS_Ctx *ctx COAP_UNUSED, + uint8_t **selected_proto, + uint8_t *selected_proto_len, + uint8_t *client_alpn_list, + uint32_t client_alpn_list_size, + void *user_data COAP_UNUSED) { + if (HITLS_SelectAlpnProtocol(selected_proto, selected_proto_len, + coap_hitls_alpn, sizeof(coap_hitls_alpn), + client_alpn_list, + client_alpn_list_size) != HITLS_SUCCESS || + !selected_proto || !*selected_proto || + !selected_proto_len || *selected_proto_len != 4) + return HITLS_ALPN_ERR_ALERT_FATAL; + return HITLS_ALPN_ERR_OK; +} + +static int +coap_hitls_load_ca(HITLS_Config *config, coap_dtls_key_t *key, + coap_dtls_role_t role) { + coap_pki_key_define_t *define = &key->key.define; + uint32_t len; + uint8_t *buf; + int32_t ret; + + switch (define->ca_def) { + case COAP_PKI_KEY_DEF_PEM: + if (!define->ca.s_byte || !define->ca.s_byte[0]) + return 1; + if (HITLS_CFG_LoadVerifyFile(config, define->ca.s_byte) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_PEM_BUF: + if (!define->ca.u_byte || !define->ca_len) + return 1; + if (!coap_hitls_pki_len(define->ca.u_byte, define->ca_len, + TLS_PARSE_FORMAT_PEM, &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + if (HITLS_CFG_LoadVerifyBuffer(config, define->ca.u_byte, len, + TLS_PARSE_FORMAT_PEM) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER_BUF: + if (!define->ca.u_byte || !define->ca_len) + return 1; + if (!coap_hitls_pki_len(define->ca.u_byte, define->ca_len, + TLS_PARSE_FORMAT_ASN1, &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + if (HITLS_CFG_LoadVerifyBuffer(config, define->ca.u_byte, len, + TLS_PARSE_FORMAT_ASN1) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER: + if (!define->ca.s_byte || !define->ca.s_byte[0]) + return 1; + buf = coap_hitls_read_file(define->ca.s_byte, &len); + if (!buf) + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, 0); + ret = HITLS_CFG_LoadVerifyBuffer(config, buf, len, TLS_PARSE_FORMAT_ASN1); + coap_free_type(COAP_STRING, buf); + if (ret == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_BAD, key, role, ret); + case COAP_PKI_KEY_DEF_RPK_BUF: + case COAP_PKI_KEY_DEF_PKCS11: + case COAP_PKI_KEY_DEF_PKCS11_RPK: + case COAP_PKI_KEY_DEF_ENGINE: + if (define->ca.u_byte && define->ca.u_byte[0]) + return coap_dtls_define_issue(COAP_DEFINE_KEY_CA, + COAP_DEFINE_FAIL_NOT_SUPPORTED, key, + role, 0); + return 1; + default: + return 1; + } +} + +static int +coap_hitls_load_public_cert(HITLS_Config *config, coap_dtls_key_t *key, + coap_dtls_role_t role) { + coap_pki_key_define_t *define = &key->key.define; + uint32_t len; + + switch (define->public_cert_def) { + case COAP_PKI_KEY_DEF_PEM: + if (!define->public_cert.s_byte || !define->public_cert.s_byte[0]) + return 1; + /* A PEM buffer may hold leaf + intermediate CAs; load the whole chain. */ + if (HITLS_CFG_UseCertificateChainFile(config, + define->public_cert.s_byte) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER: + if (!define->public_cert.s_byte || !define->public_cert.s_byte[0]) + return 1; + if (HITLS_CFG_LoadCertFile(config, define->public_cert.s_byte, + TLS_PARSE_FORMAT_ASN1) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_PEM_BUF: + if (!define->public_cert.u_byte || !define->public_cert_len) + return 1; + if (!coap_hitls_pki_len(define->public_cert.u_byte, + define->public_cert_len, TLS_PARSE_FORMAT_PEM, + &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + /* A PEM buffer may hold leaf + intermediate CAs; load the whole chain. */ + if (HITLS_CFG_UseCertificateChainBuffer(config, define->public_cert.u_byte, + len, TLS_PARSE_FORMAT_PEM) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER_BUF: + if (!define->public_cert.u_byte || !define->public_cert_len) + return 1; + if (!coap_hitls_pki_len(define->public_cert.u_byte, + define->public_cert_len, TLS_PARSE_FORMAT_ASN1, + &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + if (HITLS_CFG_LoadCertBuffer(config, define->public_cert.u_byte, len, + TLS_PARSE_FORMAT_ASN1) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_RPK_BUF: + case COAP_PKI_KEY_DEF_PKCS11: + case COAP_PKI_KEY_DEF_PKCS11_RPK: + case COAP_PKI_KEY_DEF_ENGINE: + if (define->public_cert.u_byte && define->public_cert.u_byte[0]) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PUBLIC, + COAP_DEFINE_FAIL_NOT_SUPPORTED, key, + role, 0); + return 1; + default: + return 1; + } +} + +static int +coap_hitls_load_private_key(HITLS_Config *config, coap_dtls_key_t *key, + coap_dtls_role_t role) { + coap_pki_key_define_t *define = &key->key.define; + uint32_t len; + + switch (define->private_key_def) { + case COAP_PKI_KEY_DEF_PEM: + if (!define->private_key.s_byte || !define->private_key.s_byte[0]) + return 1; + if (HITLS_CFG_LoadKeyFile(config, define->private_key.s_byte, + TLS_PARSE_FORMAT_PEM) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER: + if (!define->private_key.s_byte || !define->private_key.s_byte[0]) + return 1; + if (HITLS_CFG_LoadKeyFile(config, define->private_key.s_byte, + TLS_PARSE_FORMAT_ASN1) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_PEM_BUF: + if (!define->private_key.u_byte || !define->private_key_len) + return 1; + if (!coap_hitls_pki_len(define->private_key.u_byte, + define->private_key_len, TLS_PARSE_FORMAT_PEM, + &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + if (HITLS_CFG_LoadKeyBuffer(config, define->private_key.u_byte, len, + TLS_PARSE_FORMAT_PEM) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_DER_BUF: + if (!define->private_key.u_byte || !define->private_key_len) + return 1; + if (!coap_hitls_pki_len(define->private_key.u_byte, + define->private_key_len, TLS_PARSE_FORMAT_ASN1, + &len)) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + if (HITLS_CFG_LoadKeyBuffer(config, define->private_key.u_byte, len, + TLS_PARSE_FORMAT_ASN1) == HITLS_SUCCESS) + return 1; + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_BAD, key, role, 0); + case COAP_PKI_KEY_DEF_RPK_BUF: + case COAP_PKI_KEY_DEF_PKCS11: + case COAP_PKI_KEY_DEF_PKCS11_RPK: + case COAP_PKI_KEY_DEF_ENGINE: + if (define->private_key.u_byte && define->private_key.u_byte[0]) + return coap_dtls_define_issue(COAP_DEFINE_KEY_PRIVATE, + COAP_DEFINE_FAIL_NOT_SUPPORTED, key, + role, 0); + return 1; + default: + return 1; + } +} + +static int +coap_hitls_load_root_cas(HITLS_Config *config, coap_hitls_context_t *context) { + if (context->root_ca_file && + HITLS_CFG_LoadVerifyFile(config, context->root_ca_file) != HITLS_SUCCESS) { + coap_log_warn("Unable to install root CA file '%s'\n", + context->root_ca_file); + return 0; + } + if (context->root_ca_dir && + HITLS_CFG_LoadVerifyDir(config, context->root_ca_dir) != HITLS_SUCCESS) { + coap_log_warn("Unable to install root CA directory '%s'\n", + context->root_ca_dir); + return 0; + } + if (context->trust_store_defined && + HITLS_CFG_LoadDefaultCAPath(config) != HITLS_SUCCESS) { + coap_log_warn("Unable to load trusted root CAs\n"); + return 0; + } + return 1; +} + +static int +coap_hitls_configure_pki(HITLS_Config *config, coap_session_t *session, + coap_dtls_role_t role, + const coap_dtls_pki_t *pki_setup_data) { + coap_hitls_context_t *context = + session && session->context ? + (coap_hitls_context_t *)session->context->dtls_context : NULL; + coap_dtls_pki_t setup_data_copy; + coap_dtls_pki_t *setup_data = context ? &context->setup_data : NULL; + coap_dtls_key_t key; + + if (pki_setup_data) { + setup_data_copy = *pki_setup_data; + setup_data = &setup_data_copy; + } + if (!context || !setup_data) + return 0; + if (!coap_hitls_check_pki_key_supported(setup_data, role)) + return 0; + + coap_dtls_map_key_type_to_define(setup_data, &key); + if (key.key_type != COAP_PKI_KEY_DEFINE) + return 0; + + if (!coap_hitls_load_root_cas(config, context) || + !coap_hitls_load_ca(config, &key, role) || + !coap_hitls_load_public_cert(config, &key, role) || + !coap_hitls_load_private_key(config, &key, role)) + return 0; + + if (setup_data->cert_chain_validation && + HITLS_CFG_SetVerifyDepth(config, + COAP_HITLS_VERIFY_DEPTH_TO_MAX_CHAIN_DEPTH( + setup_data->cert_chain_verify_depth)) != + HITLS_SUCCESS) + return 0; + if (setup_data->check_cert_revocation && + HITLS_CFG_SetVerifyFlags(config, + HITLS_X509_VFY_FLAG_CRL_ALL) != HITLS_SUCCESS) + return 0; + if (HITLS_CFG_SetVerifyCb(config, coap_hitls_verify_cb) != HITLS_SUCCESS) + return 0; + if (HITLS_CFG_SetCertVerifyCb(config, coap_hitls_app_verify_cb, + NULL) != HITLS_SUCCESS) + return 0; + + if (role == COAP_DTLS_ROLE_CLIENT) { + if (setup_data->client_sni && setup_data->client_sni[0] && + HITLS_CFG_SetServerName(config, + (uint8_t *)setup_data->client_sni, + (uint32_t)strlen(setup_data->client_sni)) != HITLS_SUCCESS) + return 0; + } else { + if (HITLS_CFG_SetClientVerifySupport(config, + setup_data->verify_peer_cert ? true : false) != HITLS_SUCCESS) + return 0; + if (setup_data->verify_peer_cert && + HITLS_CFG_SetNoClientCertSupport(config, false) != HITLS_SUCCESS) + return 0; + } + + if (HITLS_CFG_SetVerifyNoneSupport(config, + setup_data->verify_peer_cert ? false : true) != HITLS_SUCCESS) + return 0; + return 1; +} + +#if COAP_SERVER_SUPPORT +static HITLS_Config * +coap_hitls_new_server_sni_config(coap_session_t *session, coap_proto_t proto, + const coap_dtls_key_t *new_key) { + coap_hitls_context_t *context = + session && session->context ? + (coap_hitls_context_t *)session->context->dtls_context : NULL; + const uint16_t *cipher_suites; + uint32_t cipher_suites_count; + int enabled = context && context->psk_pki_enabled ? + context->psk_pki_enabled : IS_PKI; + coap_dtls_pki_t pki_setup_data; + HITLS_Config *config; + + if (!context || !new_key) + return NULL; + + config = proto == COAP_PROTO_DTLS ? HITLS_CFG_NewDTLS12Config() : + HITLS_CFG_NewTLS12Config(); + if (!config) + return NULL; + + coap_hitls_get_cipher_suites(enabled, &cipher_suites, &cipher_suites_count); + if (HITLS_CFG_SetCipherSuites(config, cipher_suites, + cipher_suites_count) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (proto == COAP_PROTO_DTLS && + (HITLS_CFG_SetFlightTransmitSwitch(config, true) != HITLS_SUCCESS || + HITLS_CFG_SetDtlsCookieExchangeSupport(config, true) != HITLS_SUCCESS || + HITLS_CFG_SetCookieGenCb(config, + coap_hitls_cookie_gen_cb) != HITLS_SUCCESS || + HITLS_CFG_SetCookieVerifyCb(config, + coap_hitls_cookie_verify_cb) != HITLS_SUCCESS)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (proto == COAP_PROTO_TLS && + HITLS_CFG_SetAlpnProtosSelectCb(config, coap_hitls_alpn_select_cb, + NULL) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (enabled & IS_PSK) { + const coap_bin_const_t *hint = coap_get_session_server_psk_hint(session); + + if (hint && hint->s && hint->length <= UINT32_MAX && + HITLS_CFG_SetPskIdentityHint(config, hint->s, + (uint32_t)hint->length) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (HITLS_CFG_SetPskServerCallback(config, + coap_hitls_psk_server_cb) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + } + + pki_setup_data = context->setup_data; + pki_setup_data.pki_key = *new_key; + if (!coap_hitls_configure_pki(config, session, COAP_DTLS_ROLE_SERVER, + &pki_setup_data)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + + (void)HITLS_CFG_SetInfoCb(config, coap_hitls_info_cb); + return config; +} +#endif /* COAP_SERVER_SUPPORT */ + +static HITLS_Config * +coap_hitls_new_config(coap_session_t *session, coap_dtls_role_t role, + coap_proto_t proto) { + coap_hitls_context_t *hitls_context = + session && session->context ? + (coap_hitls_context_t *)session->context->dtls_context : NULL; + const uint16_t *cipher_suites; + uint32_t cipher_suites_count; + int enabled = hitls_context && hitls_context->psk_pki_enabled ? + hitls_context->psk_pki_enabled : IS_PSK; + HITLS_Config *config = proto == COAP_PROTO_DTLS ? + HITLS_CFG_NewDTLS12Config() : + HITLS_CFG_NewTLS12Config(); + + if (!config) + return NULL; + + coap_hitls_get_cipher_suites(enabled, &cipher_suites, &cipher_suites_count); + + if (HITLS_CFG_SetCipherSuites(config, cipher_suites, + cipher_suites_count) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (proto == COAP_PROTO_DTLS && + (HITLS_CFG_SetFlightTransmitSwitch(config, true) != HITLS_SUCCESS || + HITLS_CFG_SetDtlsCookieExchangeSupport(config, + role == COAP_DTLS_ROLE_SERVER) != HITLS_SUCCESS)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } +#if COAP_SERVER_SUPPORT + if (proto == COAP_PROTO_DTLS && role == COAP_DTLS_ROLE_SERVER && + (HITLS_CFG_SetCookieGenCb(config, + coap_hitls_cookie_gen_cb) != HITLS_SUCCESS || + HITLS_CFG_SetCookieVerifyCb(config, + coap_hitls_cookie_verify_cb) != HITLS_SUCCESS)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } +#endif /* COAP_SERVER_SUPPORT */ + if (proto == COAP_PROTO_TLS && + ((role == COAP_DTLS_ROLE_CLIENT && + HITLS_CFG_SetAlpnProtos(config, coap_hitls_alpn, + sizeof(coap_hitls_alpn)) != HITLS_SUCCESS) || + (role == COAP_DTLS_ROLE_SERVER && + HITLS_CFG_SetAlpnProtosSelectCb(config, + coap_hitls_alpn_select_cb, + NULL) != HITLS_SUCCESS))) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + + if (role == COAP_DTLS_ROLE_CLIENT && (enabled & IS_PSK)) { +#if COAP_CLIENT_SUPPORT + coap_dtls_cpsk_t *setup_data = &session->cpsk_setup_data; + + if (setup_data->client_sni && setup_data->client_sni[0] && + HITLS_CFG_SetServerName(config, + (uint8_t *)setup_data->client_sni, + (uint32_t)strlen(setup_data->client_sni)) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (HITLS_CFG_SetPskClientCallback(config, + coap_hitls_psk_client_cb) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } +#else /* ! COAP_CLIENT_SUPPORT */ + (void)session; + HITLS_CFG_FreeConfig(config); + return NULL; +#endif /* ! COAP_CLIENT_SUPPORT */ + } else if (role == COAP_DTLS_ROLE_SERVER && (enabled & IS_PSK)) { +#if COAP_SERVER_SUPPORT + const coap_bin_const_t *hint = coap_get_session_server_psk_hint(session); + + if (hint && hint->s && hint->length <= UINT32_MAX && + HITLS_CFG_SetPskIdentityHint(config, hint->s, + (uint32_t)hint->length) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + if (HITLS_CFG_SetPskServerCallback(config, + coap_hitls_psk_server_cb) != HITLS_SUCCESS) { + HITLS_CFG_FreeConfig(config); + return NULL; + } +#else /* ! COAP_SERVER_SUPPORT */ + (void)session; + HITLS_CFG_FreeConfig(config); + return NULL; +#endif /* ! COAP_SERVER_SUPPORT */ + } + +#if COAP_SERVER_SUPPORT + if (role == COAP_DTLS_ROLE_SERVER) { + int needs_sni = 0; + + if ((enabled & IS_PSK) && session && session->context && + session->context->spsk_setup_data.validate_sni_call_back) + needs_sni = 1; + if ((enabled & IS_PKI) && hitls_context && + hitls_context->setup_data.validate_sni_call_back) + needs_sni = 1; + if (needs_sni && + (HITLS_CFG_SetServerNameCb(config, + coap_hitls_sni_cb) != HITLS_SUCCESS || + HITLS_CFG_SetServerNameArg(config, NULL) != HITLS_SUCCESS)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + } +#endif /* COAP_SERVER_SUPPORT */ + + if ((enabled & IS_PKI) && + !coap_hitls_configure_pki(config, session, role, NULL)) { + HITLS_CFG_FreeConfig(config); + return NULL; + } + + (void)HITLS_CFG_SetInfoCb(config, coap_hitls_info_cb); + return config; +} + +static void +coap_hitls_free_env(coap_hitls_env_t *env) { + if (!env) + return; + if (env->ctx) { + if (env->established && !env->had_fatal) + (void)HITLS_Close(env->ctx); + if (env->uio) + BSL_UIO_SetUserData(env->uio, NULL); + HITLS_Free(env->ctx); + } else if (env->uio) { + BSL_UIO_SetUserData(env->uio, NULL); + } + if (env->method) + BSL_UIO_FreeMethod(env->method); + coap_free_type(COAP_STRING, env); +} + +static coap_hitls_env_t * +coap_hitls_live_env(coap_session_t *session, coap_hitls_env_t *env) { + return session && session->tls == (void *)env ? env : NULL; +} + +static void +coap_hitls_clear_pdu(coap_hitls_env_t *env) { + if (env) { + env->pdu = NULL; + env->pdu_len = 0; + } +} + +static coap_hitls_env_t * +coap_hitls_new_env(coap_session_t *session, coap_dtls_role_t role, + coap_proto_t proto) { + coap_hitls_env_t *env = + (coap_hitls_env_t *)coap_malloc_type(COAP_STRING, sizeof(*env)); + coap_hitls_context_t *hitls_context = + session && session->context ? + (coap_hitls_context_t *)session->context->dtls_context : NULL; + HITLS_Config *config; + + if (!env) + return NULL; + memset(env, 0, sizeof(*env)); + env->session = session; + env->role = role; + env->proto = proto; + + config = coap_hitls_new_config(session, role, proto); + if (!config) { + coap_free_type(COAP_STRING, env); + return NULL; + } + + env->ctx = HITLS_New(config); + HITLS_CFG_FreeConfig(config); + if (!env->ctx) { + coap_free_type(COAP_STRING, env); + return NULL; + } + if (HITLS_SetUserData(env->ctx, session) != HITLS_SUCCESS || + !coap_hitls_setup_uio(env)) { + coap_hitls_free_env(env); + return NULL; + } + coap_hitls_update_mtu(env); + if (hitls_context && (hitls_context->psk_pki_enabled & IS_PKI) && + hitls_context->setup_data.additional_tls_setup_call_back && + !hitls_context->setup_data.additional_tls_setup_call_back(env->ctx, + &hitls_context->setup_data)) { + coap_hitls_free_env(env); + return NULL; + } + + return env; +} + +int +coap_dtls_is_supported(void) { + return 1; +} + +int +coap_tls_is_supported(void) { +#if !COAP_DISABLE_TCP + return 1; +#else /* COAP_DISABLE_TCP */ + return 0; +#endif /* COAP_DISABLE_TCP */ +} + +int +coap_dtls_psk_is_supported(void) { + return 1; +} + +int +coap_dtls_pki_is_supported(void) { + return 1; +} + +int +coap_dtls_pkcs11_is_supported(void) { + return 0; +} + +int +coap_dtls_rpk_is_supported(void) { + return 0; +} + +int +coap_dtls_cid_is_supported(void) { + return 0; +} + +#if COAP_CLIENT_SUPPORT +int +coap_dtls_set_cid_tuple_change(coap_context_t *c_context COAP_UNUSED, + uint8_t every COAP_UNUSED) { + return 0; +} +#endif /* COAP_CLIENT_SUPPORT */ + +void +coap_dtls_set_log_level(coap_log_t level) { + dtls_log_level = level; +} + +coap_log_t +coap_dtls_get_log_level(void) { + return dtls_log_level; +} +#endif /* COAP_WITH_LIBOPENHITLS */ + +coap_tls_version_t * +coap_get_tls_library_version(void) { + static coap_tls_version_t version; + + version.version = HITLS_VersionNum(); + version.built_version = OPENHITLS_VERSION_I; + version.type = COAP_TLS_LIBRARY_OPENHITLS; + return &version; +} + +void +coap_dtls_startup(void) { + (void)coap_hitls_startup(); +} + +void +coap_dtls_shutdown(void) { + if (coap_hitls_started) { +#if COAP_WITH_LIBOPENHITLS + HITLS_CertMethodDeinit(); +#endif /* COAP_WITH_LIBOPENHITLS */ + CRYPT_EAL_Cleanup(CRYPT_EAL_INIT_ALL); + coap_hitls_started = 0; + } + coap_dtls_set_log_level(COAP_LOG_EMERG); +} + +void +coap_dtls_thread_shutdown(void) { + BSL_ERR_RemoveErrorStack(false); +} + +#if COAP_WITH_LIBOPENHITLS +void * +coap_dtls_get_tls(const coap_session_t *session, coap_tls_library_t *tls_lib) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + + if (tls_lib) + *tls_lib = COAP_TLS_LIBRARY_OPENHITLS; + return env ? env->ctx : NULL; +} + +void * +coap_dtls_new_context(coap_context_t *coap_context) { + coap_hitls_context_t *context = + (coap_hitls_context_t *)coap_malloc_type(COAP_STRING, sizeof(*context)); + + if (!context) + return NULL; + memset(context, 0, sizeof(*context)); + context->coap_context = coap_context; + if (!coap_prng_lkd(context->cookie_secret, + sizeof(context->cookie_secret))) { + coap_free_type(COAP_STRING, context); + return NULL; + } + context->cookie_secret_set = 1; + return context; +} + +void +coap_dtls_free_context(void *dtls_context) { + coap_hitls_context_t *context = (coap_hitls_context_t *)dtls_context; + + if (context) { + if (context->root_ca_file) + coap_free_type(COAP_STRING, context->root_ca_file); + if (context->root_ca_dir) + coap_free_type(COAP_STRING, context->root_ca_dir); + memset(context->cookie_secret, 0, sizeof(context->cookie_secret)); + context->cookie_secret_set = 0; + coap_free_type(COAP_STRING, context); + } +} + +#if COAP_SERVER_SUPPORT +int +coap_dtls_context_set_spsk(coap_context_t *coap_context, + coap_dtls_spsk_t *setup_data) { + coap_hitls_context_t *context; + + if (!coap_context || !setup_data) + return 0; + context = (coap_hitls_context_t *)coap_context->dtls_context; + if (!context) + return 0; + context->psk_pki_enabled |= IS_PSK; + return 1; +} +#endif /* COAP_SERVER_SUPPORT */ + +#if COAP_CLIENT_SUPPORT +int +coap_dtls_context_set_cpsk(coap_context_t *coap_context, + coap_dtls_cpsk_t *setup_data) { + coap_hitls_context_t *context; + + if (!coap_context || !setup_data) + return 0; + context = (coap_hitls_context_t *)coap_context->dtls_context; + if (!context) + return 0; + context->psk_pki_enabled |= IS_PSK; + return 1; +} +#endif /* COAP_CLIENT_SUPPORT */ + +int +coap_dtls_context_set_pki(coap_context_t *coap_context, + const coap_dtls_pki_t *setup_data, + const coap_dtls_role_t role) { + coap_hitls_context_t *context; + + if (!coap_context || !setup_data) + return 0; + context = (coap_hitls_context_t *)coap_context->dtls_context; + if (!context) + return 0; + if (!coap_hitls_check_pki_key_supported(setup_data, role)) + return 0; + context->setup_data = *setup_data; + if (!context->setup_data.verify_peer_cert) { + /* Needs to be clear so that no CA DNs are transmitted */ + context->setup_data.check_common_ca = 0; + /* Allow all of these but warn if issue */ + context->setup_data.allow_self_signed = 1; + context->setup_data.allow_expired_certs = 1; + context->setup_data.cert_chain_validation = 1; + context->setup_data.cert_chain_verify_depth = 10; + context->setup_data.check_cert_revocation = 1; + context->setup_data.allow_no_crl = 1; + context->setup_data.allow_expired_crl = 1; + context->setup_data.allow_bad_md_hash = 1; + context->setup_data.allow_short_rsa_length = 1; + } +#if COAP_CLIENT_SUPPORT + if (role == COAP_DTLS_ROLE_CLIENT) + context->psk_pki_enabled &= ~IS_PSK; +#else /* ! COAP_CLIENT_SUPPORT */ + (void)role; +#endif /* ! COAP_CLIENT_SUPPORT */ + context->psk_pki_enabled |= IS_PKI; + if (setup_data->use_cid) + coap_log_warn("openHiTLS backend has no Connection-ID support\n"); + return 1; +} + +int +coap_dtls_context_set_pki_root_cas(coap_context_t *coap_context, + const char *ca_file, + const char *ca_dir) { + coap_hitls_context_t *context; + char *new_ca_file = NULL; + char *new_ca_dir = NULL; + + if (!coap_context || (!ca_file && !ca_dir)) + return 0; + context = (coap_hitls_context_t *)coap_context->dtls_context; + if (!context) + return 0; + if (ca_file) { + new_ca_file = coap_hitls_strdup(ca_file); + if (!new_ca_file) + return 0; + } + if (ca_dir) { + new_ca_dir = coap_hitls_strdup(ca_dir); + if (!new_ca_dir) { + if (new_ca_file) + coap_free_type(COAP_STRING, new_ca_file); + return 0; + } + } + if (context->root_ca_file) + coap_free_type(COAP_STRING, context->root_ca_file); + if (context->root_ca_dir) + coap_free_type(COAP_STRING, context->root_ca_dir); + context->root_ca_file = new_ca_file; + context->root_ca_dir = new_ca_dir; + return 1; +} + +int +coap_dtls_context_load_pki_trust_store(coap_context_t *coap_context) { + coap_hitls_context_t *context = + coap_context ? (coap_hitls_context_t *)coap_context->dtls_context : NULL; + + if (!context) + return 0; + context->trust_store_defined = 1; + return 1; +} + +int +coap_dtls_context_check_keys_enabled(coap_context_t *coap_context) { + coap_hitls_context_t *context = + coap_context ? (coap_hitls_context_t *)coap_context->dtls_context : NULL; + + return context && context->psk_pki_enabled; +} +#endif /* COAP_WITH_LIBOPENHITLS */ + +#if COAP_SERVER_SUPPORT +coap_digest_ctx_t * +coap_digest_setup(void) { + CRYPT_EAL_MdCtx *digest_ctx; + + if (!coap_hitls_startup()) + return NULL; + digest_ctx = CRYPT_EAL_MdNewCtx(CRYPT_MD_SHA256); + if (!digest_ctx) + return NULL; + if (CRYPT_EAL_MdInit(digest_ctx) != CRYPT_SUCCESS) { + CRYPT_EAL_MdFreeCtx(digest_ctx); + return NULL; + } + return digest_ctx; +} + +void +coap_digest_free(coap_digest_ctx_t *digest_ctx) { + if (digest_ctx) + CRYPT_EAL_MdFreeCtx((CRYPT_EAL_MdCtx *)digest_ctx); +} + +int +coap_digest_update(coap_digest_ctx_t *digest_ctx, + const uint8_t *data, + size_t data_len) { + CRYPT_EAL_MdCtx *ctx = (CRYPT_EAL_MdCtx *)digest_ctx; + + if (!ctx || (!data && data_len)) + return 0; + while (data_len) { + uint32_t chunk = data_len > UINT32_MAX ? UINT32_MAX : (uint32_t)data_len; + + if (CRYPT_EAL_MdUpdate(ctx, data, chunk) != CRYPT_SUCCESS) + return 0; + data += chunk; + data_len -= chunk; + } + return 1; +} + +int +coap_digest_final(coap_digest_ctx_t *digest_ctx, + coap_digest_t *digest_buffer) { + CRYPT_EAL_MdCtx *ctx = (CRYPT_EAL_MdCtx *)digest_ctx; + uint32_t len = sizeof(*digest_buffer); + int ret; + + if (!ctx || !digest_buffer) + return 0; + ret = CRYPT_EAL_MdFinal(ctx, (uint8_t *)digest_buffer, &len) == CRYPT_SUCCESS && + len == sizeof(*digest_buffer); + coap_digest_free(digest_ctx); + return ret; +} +#endif /* COAP_SERVER_SUPPORT */ + +#if COAP_WS_SUPPORT +static const struct { + cose_alg_t alg; + CRYPT_MD_AlgId md; + uint32_t length; +} coap_hitls_hashs[] = { + {COSE_ALGORITHM_SHA_1, CRYPT_MD_SHA1, 20}, + {COSE_ALGORITHM_SHA_256_64, CRYPT_MD_SHA256, 8}, + {COSE_ALGORITHM_SHA_256_256, CRYPT_MD_SHA256, 32}, + {COSE_ALGORITHM_SHA_512, CRYPT_MD_SHA512, 64}, +}; + +int +coap_crypto_hash(cose_alg_t alg, + const coap_bin_const_t *data, + coap_bin_const_t **hash) { + coap_binary_t *digest; + uint32_t len; + size_t i; + CRYPT_MD_AlgId md = CRYPT_MD_SHA1; + uint32_t out_len = 0; + + if (!data || !hash || data->length > UINT32_MAX || !coap_hitls_startup()) + return 0; + for (i = 0; i < sizeof(coap_hitls_hashs) / sizeof(coap_hitls_hashs[0]); i++) { + if (coap_hitls_hashs[i].alg == alg) { + md = coap_hitls_hashs[i].md; + out_len = coap_hitls_hashs[i].length; + break; + } + } + if (out_len == 0) { + coap_log_debug("coap_crypto_hash: algorithm %d not supported\n", alg); + return 0; + } + + len = CRYPT_EAL_MdGetDigestSize(md); + digest = coap_new_binary(len); + if (!digest) + return 0; + + if (CRYPT_EAL_Md(md, data->s, (uint32_t)data->length, + digest->s, &len) != CRYPT_SUCCESS || + len != digest->length) { + coap_delete_binary(digest); + return 0; + } + if (out_len < digest->length) + digest->length = out_len; + + *hash = (coap_bin_const_t *)digest; + return 1; +} +#endif /* COAP_WS_SUPPORT */ + +#if COAP_OSCORE_SUPPORT + +int +coap_oscore_is_supported(void) { + return 1; +} + +static int +coap_hitls_get_cipher_alg(cose_alg_t alg, CRYPT_CIPHER_AlgId *cipher_alg, + size_t *key_len) { + switch ((int)alg) { + case COSE_ALGORITHM_AES_CCM_16_64_128: + if (cipher_alg) + *cipher_alg = CRYPT_CIPHER_AES128_CCM; + if (key_len) + *key_len = 16; + return 1; + case COSE_ALGORITHM_AES_CCM_16_64_256: + if (cipher_alg) + *cipher_alg = CRYPT_CIPHER_AES256_CCM; + if (key_len) + *key_len = 32; + return 1; + default: + coap_log_debug("coap_hitls_get_cipher_alg: COSE cipher %d not supported\n", + alg); + return 0; + } +} + +static int +coap_hitls_get_hmac_alg(cose_hmac_alg_t hmac_alg, CRYPT_MAC_AlgId *mac_alg, + size_t *mac_len) { + switch ((int)hmac_alg) { + case COSE_HMAC_ALG_HMAC256_256: + if (mac_alg) + *mac_alg = CRYPT_MAC_HMAC_SHA256; + if (mac_len) + *mac_len = COSE_ALGORITHM_HMAC256_256_HASH_LEN; + return 1; + case COSE_HMAC_ALG_HMAC384_384: + if (mac_alg) + *mac_alg = CRYPT_MAC_HMAC_SHA384; + if (mac_len) + *mac_len = COSE_ALGORITHM_HMAC384_384_HASH_LEN; + return 1; + case COSE_HMAC_ALG_HMAC512_512: + if (mac_alg) + *mac_alg = CRYPT_MAC_HMAC_SHA512; + if (mac_len) + *mac_len = COSE_ALGORITHM_HMAC512_512_HASH_LEN; + return 1; + default: + coap_log_debug("coap_hitls_get_hmac_alg: COSE HMAC %d not supported\n", + hmac_alg); + return 0; + } +} + +int +coap_crypto_check_cipher_alg(cose_alg_t alg) { + return coap_hitls_get_cipher_alg(alg, NULL, NULL); +} + +int +coap_crypto_check_hkdf_alg(cose_hkdf_alg_t hkdf_alg) { + cose_hmac_alg_t hmac_alg; + + if (!cose_get_hmac_alg_for_hkdf(hkdf_alg, &hmac_alg)) + return 0; + return coap_hitls_get_hmac_alg(hmac_alg, NULL, NULL); +} + +static int +coap_hitls_check_ccm_params(const coap_crypto_aes_ccm_t *ccm, size_t key_len) { + if (!ccm || !ccm->key.s || !ccm->nonce || ccm->key.length != key_len) + return 0; + if (ccm->l == 0 || ccm->l > 8) + return 0; + if (ccm->tag_len == 0 || ccm->tag_len > UINT32_MAX) + return 0; + return 1; +} + +static int +coap_hitls_aead_set_common(CRYPT_EAL_CipherCtx *ctx, + const coap_crypto_aes_ccm_t *ccm, + const coap_bin_const_t *aad, uint64_t msg_len) { + uint32_t tag_len = (uint32_t)ccm->tag_len; + uint32_t aad_len = 0; + + if (CRYPT_EAL_CipherCtrl(ctx, CRYPT_CTRL_SET_TAGLEN, + &tag_len, sizeof(tag_len)) != CRYPT_SUCCESS) + return 0; + if (CRYPT_EAL_CipherCtrl(ctx, CRYPT_CTRL_SET_MSGLEN, + &msg_len, sizeof(msg_len)) != CRYPT_SUCCESS) + return 0; + if (aad && aad->length) { + if (aad->length > UINT32_MAX) + return 0; + aad_len = (uint32_t)aad->length; + } + return CRYPT_EAL_CipherCtrl(ctx, CRYPT_CTRL_SET_AAD, + aad_len ? (void *)(uintptr_t)aad->s : NULL, + aad_len) == CRYPT_SUCCESS; +} + +int +coap_crypto_aead_encrypt(const coap_crypto_param_t *params, + coap_bin_const_t *data, + coap_bin_const_t *aad, + uint8_t *result, + size_t *max_result_len) { + CRYPT_CIPHER_AlgId cipher_alg; + CRYPT_EAL_CipherCtx *ctx = NULL; + const coap_crypto_aes_ccm_t *ccm; + size_t key_len; + uint32_t out_len; + uint32_t tag_len; + uint64_t msg_len; + int ret = 0; + + if (!params || !data || !result || !max_result_len) + return 0; + if (!coap_hitls_get_cipher_alg(params->alg, &cipher_alg, &key_len)) + return 0; + + ccm = ¶ms->params.aes; + if (!coap_hitls_check_ccm_params(ccm, key_len) || + data->length > UINT32_MAX || + ccm->tag_len > *max_result_len || + data->length > *max_result_len - ccm->tag_len || + !coap_hitls_startup()) + return 0; + + ctx = CRYPT_EAL_CipherNewCtx(cipher_alg); + if (!ctx) + return 0; + + out_len = (uint32_t)data->length; + tag_len = (uint32_t)ccm->tag_len; + msg_len = data->length; + if (CRYPT_EAL_CipherInit(ctx, ccm->key.s, (uint32_t)ccm->key.length, + ccm->nonce, (uint32_t)(15 - ccm->l), + true) != CRYPT_SUCCESS) + goto finish; + if (!coap_hitls_aead_set_common(ctx, ccm, aad, msg_len)) + goto finish; + if (CRYPT_EAL_CipherUpdate(ctx, data->s, (uint32_t)data->length, + result, &out_len) != CRYPT_SUCCESS) + goto finish; + if (out_len != data->length) + goto finish; + if (CRYPT_EAL_CipherCtrl(ctx, CRYPT_CTRL_GET_TAG, + result + out_len, tag_len) != CRYPT_SUCCESS) + goto finish; + + *max_result_len = (size_t)out_len + ccm->tag_len; + ret = 1; + +finish: + CRYPT_EAL_CipherFreeCtx(ctx); + return ret; +} + +int +coap_crypto_aead_decrypt(const coap_crypto_param_t *params, + coap_bin_const_t *data, + coap_bin_const_t *aad, + uint8_t *result, + size_t *max_result_len) { + CRYPT_CIPHER_AlgId cipher_alg; + CRYPT_EAL_CipherCtx *ctx = NULL; + const coap_crypto_aes_ccm_t *ccm; + const uint8_t *tag; + size_t key_len; + size_t cipher_len; + uint32_t out_len; + uint32_t final_len; + uint32_t tag_len; + uint64_t msg_len; + int ret = 0; + + if (!params || !data || !result || !max_result_len) + return 0; + if (!coap_hitls_get_cipher_alg(params->alg, &cipher_alg, &key_len)) + return 0; + + ccm = ¶ms->params.aes; + if (!coap_hitls_check_ccm_params(ccm, key_len) || + data->length < ccm->tag_len || + data->length > UINT32_MAX || + !coap_hitls_startup()) + return 0; + + cipher_len = data->length - ccm->tag_len; + if (*max_result_len < cipher_len) + return 0; + + ctx = CRYPT_EAL_CipherNewCtx(cipher_alg); + if (!ctx) + return 0; + + tag = data->s + cipher_len; + out_len = (uint32_t)cipher_len; + final_len = 0; + tag_len = (uint32_t)ccm->tag_len; + msg_len = cipher_len; + if (CRYPT_EAL_CipherInit(ctx, ccm->key.s, (uint32_t)ccm->key.length, + ccm->nonce, (uint32_t)(15 - ccm->l), + false) != CRYPT_SUCCESS) + goto finish; + if (!coap_hitls_aead_set_common(ctx, ccm, aad, msg_len)) + goto finish; + if (CRYPT_EAL_CipherCtrl(ctx, CRYPT_CTRL_SET_TAG, + (void *)(uintptr_t)tag, tag_len) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_CipherUpdate(ctx, data->s, (uint32_t)cipher_len, + result, &out_len) != CRYPT_SUCCESS) + goto finish; + if (out_len != cipher_len) + goto finish; + if (CRYPT_EAL_CipherFinal(ctx, result, &final_len) != CRYPT_SUCCESS) + goto finish; + if (final_len != 0) + goto finish; + + *max_result_len = out_len; + ret = 1; + +finish: + CRYPT_EAL_CipherFreeCtx(ctx); + return ret; +} + +int +coap_crypto_hmac(cose_hmac_alg_t hmac_alg, + coap_bin_const_t *key, + coap_bin_const_t *data, + coap_bin_const_t **hmac) { + CRYPT_MAC_AlgId mac_alg; + CRYPT_EAL_MacCtx *ctx = NULL; + coap_binary_t *dummy = NULL; + size_t mac_len; + uint32_t out_len; + int ret = 0; + + if (!key || !data || !hmac || + key->length > UINT32_MAX || data->length > UINT32_MAX || + !coap_hitls_get_hmac_alg(hmac_alg, &mac_alg, &mac_len) || + !coap_hitls_startup()) + return 0; + + dummy = coap_new_binary(mac_len); + if (!dummy) + return 0; + + ctx = CRYPT_EAL_MacNewCtx(mac_alg); + if (!ctx) + goto finish; + + out_len = (uint32_t)dummy->length; + if (CRYPT_EAL_MacInit(ctx, key->s, (uint32_t)key->length) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacUpdate(ctx, data->s, (uint32_t)data->length) != CRYPT_SUCCESS) + goto finish; + if (CRYPT_EAL_MacFinal(ctx, dummy->s, &out_len) != CRYPT_SUCCESS || + out_len != dummy->length) + goto finish; + + *hmac = (coap_bin_const_t *)dummy; + dummy = NULL; + ret = 1; + +finish: + CRYPT_EAL_MacFreeCtx(ctx); + coap_delete_binary(dummy); + return ret; +} + +#endif /* COAP_OSCORE_SUPPORT */ + +#if COAP_WITH_LIBOPENHITLS +#if COAP_CLIENT_SUPPORT +void * +coap_dtls_new_client_session(coap_session_t *session) { + coap_hitls_env_t *env = + coap_hitls_new_env(session, COAP_DTLS_ROLE_CLIENT, COAP_PROTO_DTLS); + + session->tls = env; + if (env && coap_hitls_handshake(session, env) < 0) { + coap_hitls_free_env(env); + session->tls = NULL; + return NULL; + } + return env; +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT +void * +coap_dtls_new_server_session(coap_session_t *session) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + + if (!env) + return NULL; + if (coap_hitls_handshake(session, env) < 0) { + coap_hitls_free_env(env); + session->tls = NULL; + return NULL; + } + return env; +} +#endif /* COAP_SERVER_SUPPORT */ + +void +coap_dtls_free_session(coap_session_t *session) { + if (session && session->context && session->tls) { + coap_hitls_free_env((coap_hitls_env_t *)session->tls); + session->tls = NULL; + coap_handle_event_lkd(session->context, COAP_EVENT_DTLS_CLOSED, session); + } +} + +void +coap_dtls_session_update_mtu(coap_session_t *session) { + if (session) + coap_hitls_update_mtu((coap_hitls_env_t *)session->tls); +} + +ssize_t +coap_dtls_send(coap_session_t *session, const uint8_t *data, size_t data_len) { + coap_hitls_env_t *env = (coap_hitls_env_t *)session->tls; + uint32_t written = 0; + int32_t ret; + + if (!env || data_len > UINT32_MAX) + return -1; + + session->dtls_event = -1; + coap_log_debug("* %s: dtls: sent %4d bytes\n", + coap_session_str(session), (int)data_len); + if (!env->established) { + ret = coap_hitls_handshake(session, env); + if (ret == 1) + return coap_dtls_send(session, data, data_len); + return ret == 0 ? 0 : -1; + } + + BSL_ERR_ClearError(); + ret = HITLS_Write(env->ctx, data, (uint32_t)data_len, &written); + if (ret == HITLS_SUCCESS) { + if (written == 0) + return 0; + return (ssize_t)written; + } + if (coap_hitls_is_retry(ret)) + return 0; + + session->dtls_event = coap_hitls_is_closed(ret) ? + COAP_EVENT_DTLS_CLOSED : COAP_EVENT_DTLS_ERROR; + if (session->dtls_event == COAP_EVENT_DTLS_ERROR) { + coap_log_warn("coap_dtls_send: returned 0x%x\n", (unsigned int)ret); + coap_hitls_log_fatal_err_stack(session); + } + coap_handle_event_lkd(session->context, session->dtls_event, session); + return -1; +} + +int +coap_dtls_is_context_timeout(void) { + return 0; +} + +coap_tick_t +coap_dtls_get_context_timeout(void *dtls_context COAP_UNUSED) { + return 0; +} + +coap_tick_t +coap_dtls_get_timeout(coap_session_t *session, coap_tick_t now) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + uint64_t timeout_us = 0; + int32_t ret; + + if (!env) + return 0; + ret = HITLS_DtlsGetTimeout(env->ctx, &timeout_us); + if (ret == HITLS_MSG_HANDLE_ERR_WITHOUT_TIMEOUT_ACTION) + return 0; + if (ret != HITLS_SUCCESS) { + coap_log_warn("HITLS_DtlsGetTimeout() returned 0x%x\n", + (unsigned int)ret); + return 0; + } + if (timeout_us == 0) { + /* + * Timer already due: pace it so one I/O cycle does not fire DTLS timeouts + * back-to-back and exhaust dtls_timeout_count prematurely. + */ + if (env->last_timeout + COAP_DTLS_RETRANSMIT_COAP_TICKS > now) + return env->last_timeout + COAP_DTLS_RETRANSMIT_COAP_TICKS; + env->last_timeout = now; + return now; + } + return now + (coap_tick_t)((timeout_us * COAP_TICKS_PER_SECOND + 999999) / + 1000000); +} + +int +coap_dtls_handle_timeout(coap_session_t *session) { + coap_hitls_env_t *env = (coap_hitls_env_t *)session->tls; + int32_t ret; + + if (!env) + return 1; + if (++session->dtls_timeout_count > session->max_retransmit) { + coap_session_disconnected_lkd(session, COAP_NACK_TLS_FAILED); + return 1; + } + + BSL_ERR_ClearError(); + ret = HITLS_DtlsProcessTimeout(env->ctx); + if (ret == HITLS_SUCCESS || + ret == HITLS_MSG_HANDLE_DTLS_RETRANSMIT_NOT_TIMEOUT || + coap_hitls_is_retry(ret)) + return 0; + + coap_log_warn("HITLS_DtlsProcessTimeout() returned 0x%x\n", + (unsigned int)ret); + coap_hitls_log_fatal_err_stack(session); + coap_session_disconnected_lkd(session, COAP_NACK_TLS_FAILED); + return 1; +} + +int +coap_dtls_receive(coap_session_t *session, const uint8_t *data, + size_t data_len) { + coap_hitls_env_t *env = (coap_hitls_env_t *)session->tls; + int ret = -1; + + if (!env) + return -1; + if (env->pdu_len) + coap_log_err("** %s: Previous data not read %" PRIuS " bytes\n", + coap_session_str(session), env->pdu_len); + + session->dtls_event = -1; + env->pdu = data; + env->pdu_len = data_len; + + if (env->established) { +#if COAP_CONSTRAINED_STACK + static uint8_t pdu[COAP_RXBUFFER_SIZE]; +#else /* ! COAP_CONSTRAINED_STACK */ + uint8_t pdu[COAP_RXBUFFER_SIZE]; +#endif /* ! COAP_CONSTRAINED_STACK */ + uint32_t read_len = 0; + int32_t hret; + + BSL_ERR_ClearError(); + hret = HITLS_Read(env->ctx, pdu, sizeof(pdu), &read_len); + + if (hret == HITLS_SUCCESS && read_len > 0) { + coap_log_debug("* %s: dtls: recv %4d bytes\n", + coap_session_str(session), (int)read_len); + ret = coap_handle_dgram(session->context, session, pdu, read_len); + goto finish; + } + if (hret == HITLS_SUCCESS || coap_hitls_is_retry(hret)) { + ret = -1; + goto finish; + } + session->dtls_event = coap_hitls_is_closed(hret) ? + COAP_EVENT_DTLS_CLOSED : COAP_EVENT_DTLS_ERROR; + if (session->dtls_event == COAP_EVENT_DTLS_ERROR) { + coap_log_warn("coap_dtls_receive: returned 0x%x (length %" PRIdS ")\n", + (unsigned int)hret, data_len); + coap_hitls_log_fatal_err_stack(session); + } + } else { + /* + * On completion coap_hitls_set_connected() drives l_establish(); on error + * session->dtls_event is set and handled below, so the handshake return + * value is not needed here. + */ + (void)coap_hitls_handshake(session, env); + ret = -1; + } + + if (session->dtls_event >= 0) { + coap_handle_event_lkd(session->context, session->dtls_event, session); + if (session->dtls_event == COAP_EVENT_DTLS_ERROR || + session->dtls_event == COAP_EVENT_DTLS_CLOSED) { + coap_session_disconnected_lkd(session, COAP_NACK_TLS_FAILED); + ret = -1; + } + } + +finish: + coap_hitls_clear_pdu(coap_hitls_live_env(session, env)); + return ret; +} + +#if COAP_SERVER_SUPPORT +int +coap_dtls_hello(coap_session_t *session, const uint8_t *data, + size_t data_len) { + coap_hitls_env_t *env = (coap_hitls_env_t *)session->tls; + int32_t ret; + int cookie_valid = 0; + + if (!env) { + env = coap_hitls_new_env(session, COAP_DTLS_ROLE_SERVER, COAP_PROTO_DTLS); + if (!env) + return -1; + session->tls = env; + } + + env->pdu = data; + env->pdu_len = data_len; + + if (!env->hello_verify_sent) { + BSL_ERR_ClearError(); + ret = HITLS_Listen(env->ctx, &session->addr_info.remote.addr.sa); + if (env->pdu_len) { + coap_log_debug("coap_dtls_hello: ret 0x%x: remaining data %" PRIuS "\n", + (unsigned int)ret, env->pdu_len); + } + coap_hitls_clear_pdu(env); + + if (ret == HITLS_SUCCESS) + return 1; + if (coap_hitls_is_retry(ret)) { + /* HITLS_Listen emits HelloVerifyRequest; Accept flushes it. */ + ret = coap_hitls_handshake(session, env); + if (ret < 0) + return -1; + env = coap_hitls_live_env(session, env); + if (!env) + return -1; + env->hello_verify_sent = 1; + return 0; + } + + coap_log_warn("coap_dtls_hello: returned 0x%x\n", (unsigned int)ret); + coap_hitls_log_fatal_err_stack(session); + session->dtls_event = COAP_EVENT_DTLS_ERROR; + return -1; + } + + cookie_valid = coap_hitls_client_hello_cookie_valid(session, data, data_len); + ret = coap_hitls_handshake(session, env); + env = coap_hitls_live_env(session, env); + coap_hitls_clear_pdu(env); + + if (ret < 0 || !env) + return -1; + return cookie_valid == 1 ? 1 : 0; +} +#endif /* COAP_SERVER_SUPPORT */ + +unsigned int +coap_dtls_get_overhead(coap_session_t *session COAP_UNUSED) { + return COAP_HITLS_DTLS_OVERHEAD; +} + +#if !COAP_DISABLE_TCP +#if COAP_CLIENT_SUPPORT +void * +coap_tls_new_client_session(coap_session_t *session) { + coap_hitls_env_t *env = + coap_hitls_new_env(session, COAP_DTLS_ROLE_CLIENT, COAP_PROTO_TLS); + + session->tls = env; + if (env && coap_hitls_handshake(session, env) < 0) { + coap_hitls_free_env(env); + session->tls = NULL; + return NULL; + } + return env; +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT +void * +coap_tls_new_server_session(coap_session_t *session) { + coap_hitls_env_t *env = + coap_hitls_new_env(session, COAP_DTLS_ROLE_SERVER, COAP_PROTO_TLS); + + session->tls = env; + if (env && coap_hitls_handshake(session, env) < 0) { + coap_hitls_free_env(env); + session->tls = NULL; + return NULL; + } + return env; +} +#endif /* COAP_SERVER_SUPPORT */ + +void +coap_tls_free_session(coap_session_t *session) { + coap_dtls_free_session(session); +} + +static void +coap_hitls_tcp_want(coap_session_t *session, HITLS_Ctx *ctx) { + uint8_t rwstate = HITLS_NOTHING; + + (void)HITLS_GetRwstate(ctx, &rwstate); + if (rwstate == HITLS_WRITING) { + session->sock.flags |= COAP_SOCKET_WANT_WRITE; +#ifdef COAP_EPOLL_SUPPORT + coap_epoll_ctl_mod(&session->sock, + EPOLLOUT | + ((session->sock.flags & COAP_SOCKET_WANT_READ) ? + EPOLLIN : 0), + __func__); +#endif /* COAP_EPOLL_SUPPORT */ + } else { + session->sock.flags |= COAP_SOCKET_WANT_READ; + } +} + +ssize_t +coap_tls_write(coap_session_t *session, + const uint8_t *data, + size_t data_len) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + uint32_t written = 0; + int32_t ret; + + if (!env || data_len > UINT32_MAX) { + errno = ENXIO; + return -1; + } + + session->dtls_event = -1; + if (!env->established) { + ret = coap_hitls_handshake(session, env); + if (ret < 0) + return -1; + if (ret == 0) + coap_hitls_tcp_want(session, env->ctx); + return 0; + } + + BSL_ERR_ClearError(); + ret = HITLS_Write(env->ctx, data, (uint32_t)data_len, &written); + if (ret == HITLS_SUCCESS) { + if (written > 0) { + if (written == data_len) + coap_log_debug("* %s: tls: sent %4d bytes\n", + coap_session_str(session), (int)written); + else + coap_log_debug("* %s: tls: sent %4d of %4" PRIdS " bytes\n", + coap_session_str(session), (int)written, data_len); + } + return (ssize_t)written; + } + if (coap_hitls_is_retry(ret)) { + coap_hitls_tcp_want(session, env->ctx); + return 0; + } + + session->dtls_event = coap_hitls_is_closed(ret) ? + COAP_EVENT_DTLS_CLOSED : COAP_EVENT_DTLS_ERROR; + if (session->dtls_event == COAP_EVENT_DTLS_ERROR) { + coap_log_warn("coap_tls_write: returned 0x%x\n", (unsigned int)ret); + coap_hitls_log_fatal_err_stack(session); + } + coap_handle_event_lkd(session->context, session->dtls_event, session); + return -1; +} + +ssize_t +coap_tls_read(coap_session_t *session, + uint8_t *data, + size_t data_len) { + coap_hitls_env_t *env = session ? (coap_hitls_env_t *)session->tls : NULL; + uint32_t read_len = 0; + int32_t ret; + + if (!env || data_len > UINT32_MAX) { + errno = ENXIO; + return -1; + } + + session->dtls_event = -1; + if (!env->established) { + ret = coap_hitls_handshake(session, env); + if (ret < 0) + return -1; + if (ret == 0) + coap_hitls_tcp_want(session, env->ctx); + return 0; + } + + BSL_ERR_ClearError(); + ret = HITLS_Read(env->ctx, data, (uint32_t)data_len, &read_len); + if (ret == HITLS_SUCCESS) { + if (read_len > 0) + coap_log_debug("* %s: tls: recv %4d bytes\n", + coap_session_str(session), (int)read_len); + return (ssize_t)read_len; + } + if (coap_hitls_is_retry(ret)) { + coap_hitls_tcp_want(session, env->ctx); + errno = EAGAIN; + return 0; + } + + session->dtls_event = coap_hitls_is_closed(ret) ? + COAP_EVENT_DTLS_CLOSED : COAP_EVENT_DTLS_ERROR; + if (session->dtls_event == COAP_EVENT_DTLS_ERROR) { + coap_log_warn("coap_tls_read: returned 0x%x (length %" PRIdS ")\n", + (unsigned int)ret, data_len); + coap_hitls_log_fatal_err_stack(session); + } + coap_handle_event_lkd(session->context, session->dtls_event, session); + if (session->dtls_event == COAP_EVENT_DTLS_ERROR || + session->dtls_event == COAP_EVENT_DTLS_CLOSED) + coap_session_disconnected_lkd(session, COAP_NACK_TLS_FAILED); + return -1; +} +#endif /* !COAP_DISABLE_TCP */ +#endif /* COAP_WITH_LIBOPENHITLS */ + +#endif /* COAP_WITH_LIBOPENHITLS || COAP_WITH_LIBOPENHITLS_OSCORE */ diff --git a/src/coap_oscore.c b/src/coap_oscore.c index 551e9eb932..fffba162b4 100644 --- a/src/coap_oscore.c +++ b/src/coap_oscore.c @@ -3042,21 +3042,12 @@ coap_oscore_register_external_handlers(coap_context_t *con } COAP_API int -coap_oscore_recipient_set_last_seq(coap_oscore_conf_t *oscore_conf, - const coap_bin_const_t *recipient_id, - uint64_t last_seq) { - (void)oscore_conf; - (void)recipient_id; - (void)last_seq; - return 0; -} - -COAP_API int -coap_oscore_recipient_set_seq_window(coap_oscore_conf_t *oscore_conf, +coap_oscore_recipient_set_latest_seq(coap_oscore_conf_t *oscore_conf, const coap_bin_const_t *recipient_id, - uint64_t seq_window) { + uint64_t last_seq, uint64_t seq_window) { (void)oscore_conf; (void)recipient_id; + (void)last_seq; (void)seq_window; return 0; } diff --git a/src/coap_sha1.c b/src/coap_sha1.c index 0fdb184f5e..e62603c304 100644 --- a/src/coap_sha1.c +++ b/src/coap_sha1.c @@ -70,7 +70,7 @@ #include "coap3/coap_libcoap_build.h" -#if COAP_WS_SUPPORT && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS && ! COAP_WITH_LIBMBEDTLS_OSCORE && ! COAP_WITH_LIBWOLFSSL +#if COAP_WS_SUPPORT && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS && ! COAP_WITH_LIBMBEDTLS_OSCORE && ! COAP_WITH_LIBOPENHITLS && ! COAP_WITH_LIBOPENHITLS_OSCORE && ! COAP_WITH_LIBWOLFSSL /* * Define the SHA1 circular left shift macro */ @@ -396,4 +396,4 @@ SHA1PadMessage(SHA1Context *context) { SHA1ProcessMessageBlock(context); } -#endif /* COAP_WS_SUPPORT && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS && ! COAP_WITH_LIBMBEDTLS_OSCORE */ +#endif /* COAP_WS_SUPPORT && ! COAP_WITH_LIBOPENSSL && ! COAP_WITH_LIBGNUTLS && ! COAP_WITH_LIBMBEDTLS && ! COAP_WITH_LIBMBEDTLS_OSCORE && ! COAP_WITH_LIBOPENHITLS && ! COAP_WITH_LIBOPENHITLS_OSCORE && ! COAP_WITH_LIBWOLFSSL */ diff --git a/tests/test_tls.c b/tests/test_tls.c index 2a9df21344..f0acedabcb 100644 --- a/tests/test_tls.c +++ b/tests/test_tls.c @@ -34,6 +34,14 @@ #include #endif /* COAP_WITH_LIBOPENSSL */ +#ifdef COAP_WITH_LIBOPENHITLS +#define HAVE_DTLS 1 +#endif /* COAP_WITH_LIBOPENHITLS */ + +#if defined(COAP_WITH_LIBOPENHITLS) || COAP_WITH_LIBOPENHITLS_OSCORE +#include +#endif /* COAP_WITH_LIBOPENHITLS || COAP_WITH_LIBOPENHITLS_OSCORE */ + #ifdef COAP_WITH_LIBWOLFSSL #define HAVE_DTLS 1 #include @@ -45,7 +53,7 @@ #include #endif /* COAP_WITH_LIBGNUTLS */ -#if COAP_WITH_LIBMBEDTLS +#ifdef COAP_WITH_LIBMBEDTLS #define HAVE_DTLS 1 #include #endif /* COAP_WITH_LIBMBEDTLS */ @@ -56,7 +64,7 @@ #define ReturnIf_CU_ASSERT_PTR_NOT_NULL(value) \ CU_ASSERT_PTR_NOT_NULL(value); \ - if ((void*)value == NULL) return; + if ((const void*)value == NULL) return; static void t_tls1(void) { @@ -104,6 +112,9 @@ t_tls2(void) { #elif COAP_WITH_LIBMBEDTLS || COAP_WITH_LIBMBEDTLS_OSCORE version.version = MBEDTLS_VERSION_NUMBER; version.type = COAP_TLS_LIBRARY_MBEDTLS; +#elif defined(COAP_WITH_LIBOPENHITLS) || COAP_WITH_LIBOPENHITLS_OSCORE + version.version = HITLS_VersionNum(); + version.type = COAP_TLS_LIBRARY_OPENHITLS; #else /* no DTLS */ version.version = 0; version.type = COAP_TLS_LIBRARY_NOTLS; @@ -114,6 +125,1038 @@ t_tls2(void) { CU_ASSERT(version.type == v->type); } +static void +t_tls3(void) { + char buffer[256]; + int have_dtls = coap_dtls_is_supported(); + int have_tls = coap_tls_is_supported(); + + memset(buffer, 0, sizeof(buffer)); + CU_ASSERT_PTR_EQUAL(coap_string_tls_support(buffer, sizeof(buffer)), buffer); + if (!have_dtls && !have_tls) { + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "No DTLS or TLS support")); + } else { + const char *transport = have_dtls ? + have_tls ? "DTLS and TLS support" : + "DTLS and no TLS support" : + "No DTLS and TLS support"; + + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, transport)); + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "PSK")); + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "PKI")); + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "PKCS11")); + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "RPK")); + CU_ASSERT_PTR_NOT_NULL(strstr(buffer, "CID")); + } +} + +#if COAP_SERVER_SUPPORT +static void +t_tls4(void) { + static const uint8_t key[] = "secret"; + coap_context_t *ctx; + coap_dtls_spsk_t setup_data; + + if (!coap_dtls_psk_is_supported()) + return; + + memset(&setup_data, 0, sizeof(setup_data)); + setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION; + setup_data.psk_info.hint.s = (const uint8_t *)"hint"; + setup_data.psk_info.hint.length = strlen("hint"); + setup_data.psk_info.key.s = key; + setup_data.psk_info.key.length = sizeof(key) - 1; + + ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(ctx); + + CU_ASSERT(coap_context_set_psk2(ctx, NULL) == 0); + CU_ASSERT(coap_context_set_psk(ctx, "hint", key, sizeof(key) - 1) == 1); + CU_ASSERT(ctx->spsk_setup_data.psk_info.hint.length == strlen("hint")); + CU_ASSERT(memcmp(ctx->spsk_setup_data.psk_info.hint.s, + "hint", strlen("hint")) == 0); + CU_ASSERT(ctx->spsk_setup_data.psk_info.key.length == sizeof(key) - 1); + CU_ASSERT(memcmp(ctx->spsk_setup_data.psk_info.key.s, + key, sizeof(key) - 1) == 0); + CU_ASSERT(coap_dtls_context_check_keys_enabled(ctx) != 0); + + coap_free_context(ctx); +} + +static void +t_tls5(void) { + static const struct { + const char *name; + uint8_t verify_peer_cert; + uint8_t check_common_ca; + uint8_t allow_self_signed; + uint8_t allow_expired_certs; + uint8_t cert_chain_validation; + uint8_t cert_chain_verify_depth; + uint8_t check_cert_revocation; + uint8_t allow_no_crl; + uint8_t allow_expired_crl; + uint8_t allow_bad_md_hash; + uint8_t allow_short_rsa_length; + } pki_matrix[] = { + /* Server PKI setup with peer verification disabled. */ + { "no-peer-verification", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* Full peer verification with CRL checking enabled. */ + { "strict-peer-verification", 1, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0 }, + /* Peer verification requiring a common trusted CA. */ + { "common-ca", 1, 1, 0, 0, 1, 3, 0, 0, 0, 0, 0 }, + /* Peer verification accepting self-signed certificates. */ + { "allow-self-signed", 1, 0, 1, 0, 1, 2, 0, 0, 0, 0, 0 }, + /* Peer verification accepting expired certificates. */ + { "allow-expired-certs", 1, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0 }, + /* CRL checking with missing CRLs allowed. */ + { "allow-no-crl", 1, 0, 0, 0, 1, 2, 1, 1, 0, 0, 0 }, + /* CRL checking with expired CRLs allowed. */ + { "allow-expired-crl", 1, 0, 0, 0, 1, 2, 1, 0, 1, 0, 0 }, + /* Peer verification accepting weak certificate hashes. */ + { "allow-bad-md-hash", 1, 0, 0, 0, 1, 2, 0, 0, 0, 1, 0 }, + /* Peer verification accepting short RSA keys. */ + { "allow-short-rsa", 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1 }, + /* Peer verification with chain-depth enforcement disabled. */ + { "disable-chain-depth", 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + coap_context_t *ctx; + coap_dtls_pki_t setup_data; + size_t i; + + if (!coap_dtls_pki_is_supported()) + return; + + memset(&setup_data, 0, sizeof(setup_data)); + setup_data.version = COAP_DTLS_PKI_SETUP_VERSION; + setup_data.verify_peer_cert = 0; + setup_data.pki_key.key_type = COAP_PKI_KEY_PEM; + + ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(ctx); + + CU_ASSERT(coap_context_set_pki(ctx, NULL) == 0); + setup_data.version = 0; + CU_ASSERT(coap_context_set_pki(ctx, &setup_data) == 0); + CU_ASSERT(coap_context_set_pki_root_cas(ctx, NULL, NULL) == 0); + + setup_data.version = COAP_DTLS_PKI_SETUP_VERSION; + CU_ASSERT(coap_context_set_pki(ctx, &setup_data) == 1); + + coap_free_context(ctx); + + for (i = 0; i < sizeof(pki_matrix) / sizeof(pki_matrix[0]); i++) { + int ret; + + memset(&setup_data, 0, sizeof(setup_data)); + setup_data.version = COAP_DTLS_PKI_SETUP_VERSION; + setup_data.verify_peer_cert = pki_matrix[i].verify_peer_cert; + setup_data.check_common_ca = pki_matrix[i].check_common_ca; + setup_data.allow_self_signed = pki_matrix[i].allow_self_signed; + setup_data.allow_expired_certs = pki_matrix[i].allow_expired_certs; + setup_data.cert_chain_validation = pki_matrix[i].cert_chain_validation; + setup_data.cert_chain_verify_depth = pki_matrix[i].cert_chain_verify_depth; + setup_data.check_cert_revocation = pki_matrix[i].check_cert_revocation; + setup_data.allow_no_crl = pki_matrix[i].allow_no_crl; + setup_data.allow_expired_crl = pki_matrix[i].allow_expired_crl; + setup_data.allow_bad_md_hash = pki_matrix[i].allow_bad_md_hash; + setup_data.allow_short_rsa_length = pki_matrix[i].allow_short_rsa_length; + setup_data.pki_key.key_type = COAP_PKI_KEY_PEM; + + ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(ctx); + ret = coap_context_set_pki(ctx, &setup_data); + if (!ret) + fprintf(stderr, "W: DTLS PKI matrix case '%s' failed\n", + pki_matrix[i].name); + CU_ASSERT(ret == 1); + coap_free_context(ctx); + } +} +#endif /* COAP_SERVER_SUPPORT */ + +static void +t_tls6(void) { + coap_context_t *ctx; + int support = coap_dtls_cid_is_supported(); + + ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(ctx); + + CU_ASSERT(coap_context_set_cid_tuple_change(ctx, 0) == support); + CU_ASSERT(coap_context_set_cid_tuple_change(ctx, 1) == support); + + coap_free_context(ctx); +} + +static void +t_tls7(void) { + CU_ASSERT(coap_tls_engine_configure(NULL) == 0); + CU_ASSERT(coap_tls_engine_remove() == 0); + coap_dtls_set_log_level(COAP_LOG_DEBUG); + CU_ASSERT(coap_dtls_get_log_level() == COAP_LOG_DEBUG); + coap_dtls_set_log_level(COAP_LOG_WARN); + CU_ASSERT(coap_dtls_get_log_level() == COAP_LOG_WARN); + coap_dtls_shutdown(); + CU_ASSERT(coap_dtls_get_log_level() == COAP_LOG_EMERG); + coap_dtls_startup(); +} + +static void +t_tls8(void) { + coap_tls_library_t tls_lib = COAP_TLS_LIBRARY_NOTLS; + coap_tls_library_t expected_dtls_lib = + coap_dtls_is_supported() ? coap_get_tls_library_version()->type + : COAP_TLS_LIBRARY_NOTLS; + + CU_ASSERT_PTR_NULL(coap_session_get_tls(NULL, &tls_lib)); + CU_ASSERT(tls_lib == COAP_TLS_LIBRARY_NOTLS); + CU_ASSERT_PTR_NULL(coap_dtls_get_tls(NULL, &tls_lib)); + CU_ASSERT(tls_lib == expected_dtls_lib); +} + +#if COAP_SERVER_SUPPORT +static void +t_tls9(void) { + static const uint8_t data[] = "digest input"; + coap_digest_ctx_t *digest_ctx; + coap_digest_t digest; + + digest_ctx = coap_digest_setup(); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(digest_ctx); + CU_ASSERT(coap_digest_update(digest_ctx, data, sizeof(data) - 1) == 1); + CU_ASSERT(coap_digest_final(digest_ctx, &digest) == 1); +} +#endif /* COAP_SERVER_SUPPORT */ + +#if COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT +static void COAP_UNUSED +t_tls_init_address(coap_address_t *addr, uint16_t port) { + coap_address_init(addr); + addr->addr.sin.sin_family = AF_INET; + addr->addr.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr->size = sizeof(addr->addr.sin); + coap_address_set_port(addr, port); +} +#endif /* COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT */ + +#if COAP_CLIENT_SUPPORT +static ssize_t +t_tls_test_write_cb(coap_session_t *session COAP_UNUSED, + const uint8_t *data COAP_UNUSED, + size_t datalen) { + return (ssize_t)datalen; +} + +static coap_session_t * +t_tls_new_test_session(coap_context_t *ctx, coap_proto_t proto) { + coap_session_t *session; + + session = coap_malloc_type(COAP_SESSION, sizeof(*session)); + if (!session) + return NULL; + memset(session, 0, sizeof(*session)); + session->proto = proto; + session->type = COAP_SESSION_TYPE_CLIENT; + session->context = ctx; + session->mtu = COAP_DEFAULT_MTU; + session->sock.flags = COAP_SOCKET_NOT_EMPTY | COAP_SOCKET_CONNECTED; + session->sock.lfunc[COAP_LAYER_TLS].l_write = t_tls_test_write_cb; + return session; +} + +static void +t_tls_free_test_session(coap_session_t *session) { + if (!session) + return; + if (session->tls) + coap_dtls_free_session(session); + coap_free_type(COAP_SESSION, session); +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT +static unsigned int tls_hello_write_count; +static size_t tls_hello_write_len; +static int tls_hello_write_overflow; +static uint8_t tls_hello_write_data[512]; + +static void +t_tls_hello_write_reset(void) { + tls_hello_write_count = 0; + tls_hello_write_len = 0; + tls_hello_write_overflow = 0; + memset(tls_hello_write_data, 0, sizeof(tls_hello_write_data)); +} + +static ssize_t +t_tls_hello_write_cb(coap_session_t *session COAP_UNUSED, + const uint8_t *data, + size_t datalen) { + if (tls_hello_write_count == 0) { + tls_hello_write_len = datalen; + if (datalen <= sizeof(tls_hello_write_data)) + memcpy(tls_hello_write_data, data, datalen); + else + tls_hello_write_overflow = 1; + } + tls_hello_write_count++; + return (ssize_t)datalen; +} + +static uint32_t +t_tls_u24(const uint8_t *p) { + return ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | p[2]; +} + +static void +t_tls_set_u16(uint8_t *p, uint32_t v) { + p[0] = (uint8_t)(v >> 8); + p[1] = (uint8_t)v; +} + +static void +t_tls_set_u24(uint8_t *p, uint32_t v) { + p[0] = (uint8_t)(v >> 16); + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)v; +} + +static int +t_tls_extract_hvr_cookie(const uint8_t *data, size_t data_len, + uint8_t *cookie, size_t cookie_size, + size_t *cookie_len) { + size_t body_offset = 13 + 12; + size_t body_end; + size_t offset; + uint32_t record_len; + uint32_t hs_len; + uint8_t len; + + if (!data || !cookie || !cookie_len || + data_len < body_offset || data[0] != 22 || data[13] != 3) + return 0; + + record_len = ((uint32_t)data[11] << 8) | data[12]; + hs_len = t_tls_u24(&data[14]); + if (record_len > data_len - 13 || hs_len > record_len - 12) + return 0; + + body_end = body_offset + hs_len; + if (body_end > data_len || body_end < body_offset + 3) + return 0; + + offset = body_offset + 2; + len = data[offset++]; + if (len == 0 || offset + len > body_end || len > cookie_size) + return 0; + + memcpy(cookie, &data[offset], len); + *cookie_len = len; + return 1; +} + +static int +t_tls_build_cookie_client_hello(const uint8_t *client_hello, + size_t client_hello_len, + const uint8_t *cookie, + size_t cookie_len, + uint8_t *out, + size_t *out_len) { + size_t body_offset = 13 + 12; + size_t body_end; + size_t offset; + size_t cookie_offset; + size_t new_len; + uint32_t record_len; + uint32_t hs_len; + uint8_t session_id_len; + uint8_t old_cookie_len; + + if (!client_hello || !cookie || !out || !out_len || + cookie_len > 255 || client_hello_len < body_offset) + return 0; + + record_len = ((uint32_t)client_hello[11] << 8) | client_hello[12]; + hs_len = t_tls_u24(&client_hello[14]); + if (record_len > client_hello_len - 13 || hs_len > record_len - 12) + return 0; + + body_end = body_offset + hs_len; + if (body_end > client_hello_len || body_end < body_offset + 35) + return 0; + + offset = body_offset + 34; + session_id_len = client_hello[offset++]; + if (offset + session_id_len + 1 > body_end) + return 0; + offset += session_id_len; + old_cookie_len = client_hello[offset]; + cookie_offset = offset + 1; + if (cookie_offset + old_cookie_len > client_hello_len) + return 0; + + new_len = client_hello_len - old_cookie_len + cookie_len; + if (new_len > *out_len) + return 0; + + memcpy(out, client_hello, cookie_offset); + out[offset] = (uint8_t)cookie_len; + memcpy(&out[cookie_offset], cookie, cookie_len); + memcpy(&out[cookie_offset + cookie_len], + &client_hello[cookie_offset + old_cookie_len], + client_hello_len - cookie_offset - old_cookie_len); + + t_tls_set_u16(&out[11], + record_len - old_cookie_len + (uint32_t)cookie_len); + t_tls_set_u24(&out[14], + hs_len - old_cookie_len + (uint32_t)cookie_len); + t_tls_set_u16(&out[17], 1); + t_tls_set_u24(&out[22], + hs_len - old_cookie_len + (uint32_t)cookie_len); + out[10] = 1; + *out_len = new_len; + return 1; +} + +static void +t_tls10(void) { + static const uint8_t client_hello[] = { + 0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0xfe, 0xfd, 0x73, 0xf8, 0x35, 0x82, 0x43, + 0x39, 0xd4, 0x08, 0x0b, 0x8d, 0x23, 0x15, 0x8e, + 0xb6, 0xe5, 0x12, 0xc7, 0x6c, 0x3d, 0x2d, 0xcf, + 0x8e, 0x8a, 0xed, 0xf8, 0xc7, 0x4c, 0xd4, 0x5b, + 0xe6, 0xf5, 0xb4, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0xa8, 0x00, 0xa9, 0xc0, 0xa5, 0xcc, 0xab, 0x00, + 0xff, 0x01, 0x00, 0x00, 0x38, 0x00, 0x0d, 0x00, + 0x28, 0x00, 0x26, 0x04, 0x03, 0x05, 0x03, 0x06, + 0x03, 0x08, 0x07, 0x08, 0x09, 0x08, 0x0a, 0x08, + 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, + 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03, 0x03, + 0x01, 0x03, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, + 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x00 + }; + static const uint8_t psk[] = "secretPSK"; + uint8_t cookie[64]; + uint8_t bad_cookie[64]; + size_t cookie_len = 0; + uint8_t bad_client_hello[256]; + uint8_t second_client_hello[256]; + size_t bad_client_hello_len = sizeof(bad_client_hello); + size_t second_client_hello_len = sizeof(second_client_hello); + coap_context_t *ctx; + coap_dtls_spsk_t setup_data; + coap_endpoint_t endpoint; + coap_session_t *session; + coap_tls_version_t *version; + int ret; + + if (!coap_dtls_is_supported() || !coap_dtls_psk_is_supported()) + return; + version = coap_get_tls_library_version(); + if (version && version->type == COAP_TLS_LIBRARY_TINYDTLS) + return; + + ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(ctx); + + memset(&setup_data, 0, sizeof(setup_data)); + setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION; + setup_data.psk_info.key.s = psk; + setup_data.psk_info.key.length = sizeof(psk) - 1; + CU_ASSERT(coap_context_set_psk2(ctx, &setup_data) == 1); + + memset(&endpoint, 0, sizeof(endpoint)); + endpoint.context = ctx; + endpoint.proto = COAP_PROTO_DTLS; + endpoint.default_mtu = COAP_DEFAULT_MTU; + t_tls_init_address(&endpoint.bind_addr, 5684); + + session = coap_malloc_type(COAP_SESSION, sizeof(*session)); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(session); + memset(session, 0, sizeof(*session)); + session->proto = COAP_PROTO_DTLS; + session->type = COAP_SESSION_TYPE_HELLO; + session->state = COAP_SESSION_STATE_NONE; + session->mtu = COAP_DEFAULT_MTU; + session->context = ctx; + session->endpoint = &endpoint; + session->sock.lfunc[COAP_LAYER_TLS].l_write = t_tls_hello_write_cb; + t_tls_init_address(&session->addr_info.local, 5684); + t_tls_init_address(&session->addr_info.remote, 56840); + + t_tls_hello_write_reset(); + ret = coap_dtls_hello(session, client_hello, sizeof(client_hello)); + CU_ASSERT(ret == 0 || ret == 1); + if (ret == 1) + goto finish; + CU_ASSERT(ret == 0); + CU_ASSERT(tls_hello_write_count == 1); + CU_ASSERT(tls_hello_write_len > 0); + CU_ASSERT(tls_hello_write_overflow == 0); + if (tls_hello_write_overflow) + goto finish; + CU_ASSERT(t_tls_extract_hvr_cookie(tls_hello_write_data, + tls_hello_write_len, + cookie, sizeof(cookie), + &cookie_len) == 1); + CU_ASSERT(cookie_len > 0); + CU_ASSERT(cookie_len <= sizeof(bad_cookie)); + if (cookie_len == 0 || cookie_len > sizeof(bad_cookie)) + goto finish; + CU_ASSERT(t_tls_build_cookie_client_hello(client_hello, + sizeof(client_hello), + cookie, cookie_len, + second_client_hello, + &second_client_hello_len) == 1); + memcpy(bad_cookie, cookie, cookie_len); + bad_cookie[0] ^= 0x01; + CU_ASSERT(t_tls_build_cookie_client_hello(client_hello, + sizeof(client_hello), + bad_cookie, cookie_len, + bad_client_hello, + &bad_client_hello_len) == 1); + + t_tls_hello_write_reset(); + ret = coap_dtls_hello(session, bad_client_hello, bad_client_hello_len); + CU_ASSERT(ret == 0); + CU_ASSERT(tls_hello_write_count == 1); + CU_ASSERT(tls_hello_write_len > 0); + CU_ASSERT(t_tls_extract_hvr_cookie(tls_hello_write_data, + tls_hello_write_len, + cookie, sizeof(cookie), + &cookie_len) == 1); + + t_tls_hello_write_reset(); + ret = coap_dtls_hello(session, second_client_hello, + second_client_hello_len); + CU_ASSERT(ret == 1); + +finish: + if (session->tls) + coap_dtls_free_session(session); + coap_delete_bin_const(session->client_cid); + coap_free_type(COAP_SESSION, session); + coap_free_context(ctx); +} +#endif /* COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT */ + +#if COAP_CLIENT_SUPPORT +static coap_context_t * +t_tls_new_pki_context(const coap_dtls_pki_t *setup_data) { + coap_context_t *ctx = coap_new_context(NULL); + + CU_ASSERT_PTR_NOT_NULL_FATAL(ctx); +#if COAP_SERVER_SUPPORT + CU_ASSERT(coap_context_set_pki(ctx, setup_data) == 1); +#else /* ! COAP_SERVER_SUPPORT */ + (void)setup_data; +#endif /* ! COAP_SERVER_SUPPORT */ + return ctx; +} + +static void +t_tls_expect_pki_client_result(const coap_dtls_pki_t *setup_data, + int expect_tls) { + coap_context_t *ctx = t_tls_new_pki_context(setup_data); + coap_session_t *session = t_tls_new_test_session(ctx, COAP_PROTO_DTLS); + void *tls; + + if (!session) { + CU_ASSERT_PTR_NOT_NULL(session); + coap_free_context(ctx); + return; + } + tls = coap_dtls_new_client_session(session); + if (tls && !session->tls) + session->tls = tls; + if (expect_tls > 0) { + CU_ASSERT_PTR_NOT_NULL(tls); + } else if (expect_tls == 0) { + CU_ASSERT_PTR_NULL(tls); + } + t_tls_free_test_session(session); + coap_free_context(ctx); +} + +static void +t_tls11(void) { + coap_dtls_pki_t setup_data; + + if (!coap_dtls_pki_is_supported()) + return; + + memset(&setup_data, 0, sizeof(setup_data)); + setup_data.version = COAP_DTLS_PKI_SETUP_VERSION; + setup_data.verify_peer_cert = 0; + setup_data.pki_key.key_type = COAP_PKI_KEY_DEFINE; + setup_data.pki_key.key.define.ca_def = COAP_PKI_KEY_DEF_PEM_BUF; + setup_data.pki_key.key.define.public_cert_def = COAP_PKI_KEY_DEF_PEM_BUF; + setup_data.pki_key.key.define.private_key_def = COAP_PKI_KEY_DEF_PEM_BUF; + t_tls_expect_pki_client_result(&setup_data, 1); + + setup_data.pki_key.key.define.ca_def = COAP_PKI_KEY_DEF_DER_BUF; + setup_data.pki_key.key.define.public_cert_def = COAP_PKI_KEY_DEF_DER_BUF; + setup_data.pki_key.key.define.private_key_def = COAP_PKI_KEY_DEF_DER_BUF; + t_tls_expect_pki_client_result(&setup_data, 1); +} +#endif /* COAP_CLIENT_SUPPORT */ + +#if COAP_OSCORE_SUPPORT +static void +t_tls12(void) { + if (!coap_oscore_is_supported()) + return; + + CU_ASSERT(coap_crypto_check_cipher_alg(COSE_ALGORITHM_AES_CCM_16_64_128) != 0); + CU_ASSERT(coap_crypto_check_cipher_alg(COSE_ALGORITHM_AES_CCM_64_64_128) == 0); + CU_ASSERT(coap_crypto_check_cipher_alg((cose_alg_t)0) == 0); + CU_ASSERT(coap_crypto_check_hkdf_alg(COSE_HKDF_ALG_HKDF_SHA_256) != 0); + CU_ASSERT(coap_crypto_check_hkdf_alg((cose_hkdf_alg_t)0) == 0); +} + +static void +t_tls13(void) { + static const uint8_t key128[COSE_ALGORITHM_AES_CCM_16_64_128_KEY_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const uint8_t nonce[COSE_ALGORITHM_AES_CCM_16_64_128_NONCE_LEN] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c + }; + static const uint8_t plaintext[] = "tls-crypto"; + static const uint8_t aad_data[] = "aad"; + coap_crypto_param_t params; + coap_bin_const_t data = { sizeof(plaintext) - 1, plaintext }; + coap_bin_const_t aad = { sizeof(aad_data) - 1, aad_data }; + coap_bin_const_t key = { sizeof(key128), key128 }; + coap_bin_const_t hmac_data = { sizeof(plaintext) - 1, plaintext }; + coap_bin_const_t *hmac = NULL; + uint8_t encrypted[64]; + uint8_t decrypted[64]; + size_t encrypted_len = sizeof(encrypted); + size_t decrypted_len = sizeof(decrypted); + + if (!coap_oscore_is_supported() || + !coap_crypto_check_cipher_alg(COSE_ALGORITHM_AES_CCM_16_64_128)) + return; + + memset(¶ms, 0, sizeof(params)); + params.alg = COSE_ALGORITHM_AES_CCM_16_64_128; + params.params.aes.key.s = key128; + params.params.aes.key.length = sizeof(key128); + params.params.aes.nonce = nonce; + params.params.aes.tag_len = COSE_ALGORITHM_AES_CCM_16_64_128_TAG_LEN; + params.params.aes.l = 2; + + CU_ASSERT(coap_crypto_aead_encrypt(¶ms, &data, &aad, encrypted, + &encrypted_len) == 1); + CU_ASSERT(encrypted_len == data.length + params.params.aes.tag_len); + + data.s = encrypted; + data.length = encrypted_len; + CU_ASSERT(coap_crypto_aead_decrypt(¶ms, &data, &aad, decrypted, + &decrypted_len) == 1); + CU_ASSERT(decrypted_len == sizeof(plaintext) - 1); + CU_ASSERT(memcmp(decrypted, plaintext, decrypted_len) == 0); + + encrypted[encrypted_len - 1] ^= 0x01; + decrypted_len = sizeof(decrypted); + CU_ASSERT(coap_crypto_aead_decrypt(¶ms, &data, &aad, decrypted, + &decrypted_len) == 0); + + CU_ASSERT(coap_crypto_hmac(COSE_HMAC_ALG_HMAC256_256, &key, &hmac_data, + &hmac) == 1); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hmac); + CU_ASSERT(hmac->length == COSE_ALGORITHM_HMAC256_256_HASH_LEN); + coap_delete_binary((coap_binary_t *)hmac); + + /* HMAC-384 and HMAC-512 are optional; verify the digest where supported. */ + hmac = NULL; + if (coap_crypto_hmac(COSE_HMAC_ALG_HMAC384_384, &key, &hmac_data, &hmac) == 1) { + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hmac); + CU_ASSERT(hmac->length == COSE_ALGORITHM_HMAC384_384_HASH_LEN); + coap_delete_binary((coap_binary_t *)hmac); + } + hmac = NULL; + if (coap_crypto_hmac(COSE_HMAC_ALG_HMAC512_512, &key, &hmac_data, &hmac) == 1) { + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hmac); + CU_ASSERT(hmac->length == COSE_ALGORITHM_HMAC512_512_HASH_LEN); + coap_delete_binary((coap_binary_t *)hmac); + } + /* An unknown COSE HMAC algorithm must be rejected. */ + hmac = NULL; + CU_ASSERT(coap_crypto_hmac((cose_hmac_alg_t)0, &key, &hmac_data, &hmac) == 0); +} +#endif /* COAP_OSCORE_SUPPORT */ + +#if COAP_WS_SUPPORT +static void +t_tls14(void) { + static const uint8_t hash_data[] = "abc"; + static const uint8_t sha1_digest[] = { + 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, + 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, + 0x9c, 0xd0, 0xd8, 0x9d + }; + coap_bin_const_t data = { sizeof(hash_data) - 1, hash_data }; + coap_bin_const_t *hash = NULL; + + CU_ASSERT(coap_crypto_hash(COSE_ALGORITHM_SHA_1, &data, &hash) == 1); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hash); + CU_ASSERT(hash->length == sizeof(sha1_digest)); + CU_ASSERT(memcmp(hash->s, sha1_digest, sizeof(sha1_digest)) == 0); + coap_delete_binary((coap_binary_t *)hash); + +#if COAP_WITH_LIBOPENSSL || COAP_WITH_LIBGNUTLS || \ + COAP_WITH_LIBMBEDTLS || COAP_WITH_LIBWOLFSSL || \ + COAP_WITH_LIBOPENHITLS + { + static const uint8_t sha256_digest[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + static const uint8_t sha512_digest[] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, + 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, + 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, + 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, + 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, + 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f + }; + + hash = NULL; + if (coap_crypto_hash(COSE_ALGORITHM_SHA_256_256, &data, &hash) == 1) { + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hash); + CU_ASSERT(hash->length == sizeof(sha256_digest)); + CU_ASSERT(memcmp(hash->s, sha256_digest, sizeof(sha256_digest)) == 0); + coap_delete_binary((coap_binary_t *)hash); + } + + /* SHA-256/64 keeps only the leading 8 bytes of the SHA-256 digest. */ + hash = NULL; + if (coap_crypto_hash(COSE_ALGORITHM_SHA_256_64, &data, &hash) == 1) { + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hash); + CU_ASSERT(hash->length == 8); + CU_ASSERT(memcmp(hash->s, sha256_digest, 8) == 0); + coap_delete_binary((coap_binary_t *)hash); + } + + hash = NULL; + if (coap_crypto_hash(COSE_ALGORITHM_SHA_512, &data, &hash) == 1) { + ReturnIf_CU_ASSERT_PTR_NOT_NULL(hash); + CU_ASSERT(hash->length == sizeof(sha512_digest)); + CU_ASSERT(memcmp(hash->s, sha512_digest, sizeof(sha512_digest)) == 0); + coap_delete_binary((coap_binary_t *)hash); + } + } +#endif /* real-crypto backends */ +} +#endif /* COAP_WS_SUPPORT */ + +#if COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && COAP_IPV4_SUPPORT +typedef struct { + coap_context_t *server_ctx; + coap_context_t *client_ctx; + coap_session_t *server_session; + int client_dtls_connected; + int client_session_connected; + int client_dtls_closed_or_error; + int server_session_connected; + unsigned int lower_write_calls; +} t_tls_write_error_state_t; + +static t_tls_write_error_state_t tls_write_error_state; + +static int +t_tls_write_error_event_handler(coap_session_t *session, + const coap_event_t event) { + coap_context_t *ctx = coap_session_get_context(session); + + if (ctx == tls_write_error_state.client_ctx) { + if (event == COAP_EVENT_DTLS_CONNECTED) + tls_write_error_state.client_dtls_connected = 1; + else if (event == COAP_EVENT_SESSION_CONNECTED) + tls_write_error_state.client_session_connected = 1; + else if (event == COAP_EVENT_DTLS_CLOSED || + event == COAP_EVENT_DTLS_ERROR) + tls_write_error_state.client_dtls_closed_or_error = 1; + } else if (ctx == tls_write_error_state.server_ctx) { + if (event == COAP_EVENT_SERVER_SESSION_CONNECTED) { + tls_write_error_state.server_session = session; + tls_write_error_state.server_session_connected = 1; + } + } + return 0; +} + +static void +t_tls_io_process_pair(coap_context_t *server_ctx, coap_context_t *client_ctx, + unsigned int rounds, uint32_t timeout_ms) { + unsigned int i; + + for (i = 0; i < rounds; i++) { + (void)coap_io_process(client_ctx, timeout_ms); + (void)coap_io_process(server_ctx, timeout_ms); + } +} + +#if !COAP_DISABLE_TCP +static int +t_tls_reset_server_socket(coap_session_t *session) { +#ifdef SO_LINGER + struct linger linger_opt; + + if (!session || session->sock.fd == COAP_INVALID_SOCKET) + return 0; + + memset(&linger_opt, 0, sizeof(linger_opt)); + linger_opt.l_onoff = 1; + linger_opt.l_linger = 0; + if (setsockopt(session->sock.fd, SOL_SOCKET, SO_LINGER, + (const void *)&linger_opt, + sizeof(linger_opt)) == COAP_SOCKET_ERROR) + return 0; + coap_socket_strm_close(&session->sock); + return 1; +#else /* ! SO_LINGER */ + (void)session; + return 0; +#endif /* ! SO_LINGER */ +} + +static void +t_tls15(void) { + static const uint8_t psk[] = "secretPSK"; + static const uint8_t identity[] = "client"; + coap_context_t *server_ctx = NULL; + coap_context_t *client_ctx = NULL; + coap_endpoint_t *endpoint = NULL; + coap_session_t *client_session = NULL; + coap_address_t listen_addr; + coap_address_t server_addr; + coap_dtls_spsk_t spsk; + coap_dtls_cpsk_t cpsk; + coap_mid_t mid; + unsigned int i; + + if (!coap_tls_is_supported() || !coap_dtls_psk_is_supported()) + return; + + memset(&tls_write_error_state, 0, sizeof(tls_write_error_state)); + server_ctx = coap_new_context(NULL); + ReturnIf_CU_ASSERT_PTR_NOT_NULL(server_ctx); + client_ctx = coap_new_context(NULL); + if (!client_ctx) { + CU_ASSERT_PTR_NOT_NULL(client_ctx); + goto finish; + } + + coap_register_event_handler(server_ctx, t_tls_write_error_event_handler); + coap_register_event_handler(client_ctx, t_tls_write_error_event_handler); + tls_write_error_state.server_ctx = server_ctx; + tls_write_error_state.client_ctx = client_ctx; + + memset(&spsk, 0, sizeof(spsk)); + spsk.version = COAP_DTLS_SPSK_SETUP_VERSION; + spsk.psk_info.key.s = psk; + spsk.psk_info.key.length = sizeof(psk) - 1; + CU_ASSERT(coap_context_set_psk2(server_ctx, &spsk) == 1); + + t_tls_init_address(&listen_addr, 0); + endpoint = coap_new_endpoint(server_ctx, &listen_addr, COAP_PROTO_TLS); + if (!endpoint) { + CU_ASSERT_PTR_NOT_NULL(endpoint); + goto finish; + } + coap_address_copy(&server_addr, &endpoint->bind_addr); + + memset(&cpsk, 0, sizeof(cpsk)); + cpsk.version = COAP_DTLS_CPSK_SETUP_VERSION; + cpsk.psk_info.identity.s = identity; + cpsk.psk_info.identity.length = sizeof(identity) - 1; + cpsk.psk_info.key.s = psk; + cpsk.psk_info.key.length = sizeof(psk) - 1; + + client_session = coap_new_client_session_psk3(client_ctx, NULL, + &server_addr, COAP_PROTO_TLS, + &cpsk, NULL, NULL, NULL); + if (!client_session) { + CU_ASSERT_PTR_NOT_NULL(client_session); + goto finish; + } + + for (i = 0; i < 200; i++) { + t_tls_io_process_pair(server_ctx, client_ctx, 1, 10); + if (tls_write_error_state.client_dtls_connected && + tls_write_error_state.client_session_connected && + tls_write_error_state.server_session_connected) + break; + } + + CU_ASSERT(tls_write_error_state.client_dtls_connected == 1); + CU_ASSERT(tls_write_error_state.client_session_connected == 1); + CU_ASSERT(tls_write_error_state.server_session_connected == 1); + CU_ASSERT_PTR_NOT_NULL(tls_write_error_state.server_session); + if (!tls_write_error_state.client_dtls_connected || + !tls_write_error_state.client_session_connected || + !tls_write_error_state.server_session_connected || + !tls_write_error_state.server_session) + goto finish; + + CU_ASSERT(t_tls_reset_server_socket(tls_write_error_state.server_session) == 1); + mid = coap_session_send_ping(client_session); + CU_ASSERT(mid == COAP_INVALID_MID); + CU_ASSERT(tls_write_error_state.client_dtls_closed_or_error == 1); + +finish: + if (client_session) + coap_session_release(client_session); + if (server_ctx) + coap_free_context(server_ctx); + if (client_ctx) + coap_free_context(client_ctx); +} +#endif /* !COAP_DISABLE_TCP */ + +static ssize_t +t_tls_lower_write_error_cb(coap_session_t *session COAP_UNUSED, + const uint8_t *data COAP_UNUSED, + size_t datalen COAP_UNUSED) { + tls_write_error_state.lower_write_calls++; + errno = ECONNREFUSED; + return -1; +} + +static coap_session_t * +t_tls_new_dtls_psk_pair(coap_context_t **server_ctx, + coap_context_t **client_ctx) { + static const uint8_t psk[] = "secretPSK"; + static const uint8_t identity[] = "client"; + coap_endpoint_t *endpoint; + coap_session_t *client_session; + coap_address_t listen_addr; + coap_address_t server_addr; + coap_dtls_spsk_t spsk; + coap_dtls_cpsk_t cpsk; + unsigned int i; + + *server_ctx = coap_new_context(NULL); + if (!*server_ctx) { + CU_ASSERT_PTR_NOT_NULL(*server_ctx); + return NULL; + } + *client_ctx = coap_new_context(NULL); + if (!*client_ctx) { + CU_ASSERT_PTR_NOT_NULL(*client_ctx); + goto fail; + } + + coap_register_event_handler(*server_ctx, t_tls_write_error_event_handler); + coap_register_event_handler(*client_ctx, t_tls_write_error_event_handler); + tls_write_error_state.server_ctx = *server_ctx; + tls_write_error_state.client_ctx = *client_ctx; + + memset(&spsk, 0, sizeof(spsk)); + spsk.version = COAP_DTLS_SPSK_SETUP_VERSION; + spsk.psk_info.key.s = psk; + spsk.psk_info.key.length = sizeof(psk) - 1; + CU_ASSERT(coap_context_set_psk2(*server_ctx, &spsk) == 1); + + t_tls_init_address(&listen_addr, 0); + endpoint = coap_new_endpoint(*server_ctx, &listen_addr, COAP_PROTO_DTLS); + if (!endpoint) { + CU_ASSERT_PTR_NOT_NULL(endpoint); + goto fail; + } + coap_address_copy(&server_addr, &endpoint->bind_addr); + + memset(&cpsk, 0, sizeof(cpsk)); + cpsk.version = COAP_DTLS_CPSK_SETUP_VERSION; + cpsk.psk_info.identity.s = identity; + cpsk.psk_info.identity.length = sizeof(identity) - 1; + cpsk.psk_info.key.s = psk; + cpsk.psk_info.key.length = sizeof(psk) - 1; + + client_session = coap_new_client_session_psk3(*client_ctx, NULL, + &server_addr, COAP_PROTO_DTLS, + &cpsk, NULL, NULL, NULL); + if (!client_session) { + CU_ASSERT_PTR_NOT_NULL(client_session); + goto fail; + } + + for (i = 0; i < 300; i++) { + t_tls_io_process_pair(*server_ctx, *client_ctx, 1, 10); + if (client_session->state == COAP_SESSION_STATE_ESTABLISHED) + break; + } + + CU_ASSERT(client_session->state == COAP_SESSION_STATE_ESTABLISHED); + if (client_session->state != COAP_SESSION_STATE_ESTABLISHED) { + coap_session_release(client_session); + goto fail; + } + + return client_session; + +fail: + if (*server_ctx) { + coap_free_context(*server_ctx); + *server_ctx = NULL; + } + if (*client_ctx) { + coap_free_context(*client_ctx); + *client_ctx = NULL; + } + return NULL; +} + +static void +t_tls16(void) { + coap_context_t *server_ctx = NULL; + coap_context_t *client_ctx = NULL; + coap_session_t *client_session; + coap_tls_version_t *version; + coap_mid_t mid; + + if (!coap_dtls_is_supported() || !coap_dtls_psk_is_supported()) + return; + version = coap_get_tls_library_version(); + if (version && version->type == COAP_TLS_LIBRARY_TINYDTLS) + return; + + memset(&tls_write_error_state, 0, sizeof(tls_write_error_state)); + client_session = t_tls_new_dtls_psk_pair(&server_ctx, &client_ctx); + if (!client_session) + goto finish; + tls_write_error_state.lower_write_calls = 0; + client_session->sock.lfunc[COAP_LAYER_TLS].l_write = + t_tls_lower_write_error_cb; + mid = coap_session_send_ping(client_session); + CU_ASSERT(mid == COAP_INVALID_MID); + CU_ASSERT(tls_write_error_state.lower_write_calls > 0); + CU_ASSERT(tls_write_error_state.client_dtls_closed_or_error == 1); + coap_session_release(client_session); + client_session = NULL; + +finish: + if (server_ctx) + coap_free_context(server_ctx); + if (client_ctx) + coap_free_context(client_ctx); +} +#endif /* COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && COAP_IPV4_SUPPORT */ + static int t_tls_tests_create(void) { coap_startup(); @@ -140,6 +1183,36 @@ t_init_tls_tests(void) { TLS_TEST(suite, t_tls1); TLS_TEST(suite, t_tls2); + TLS_TEST(suite, t_tls3); +#if COAP_SERVER_SUPPORT + TLS_TEST(suite, t_tls4); + TLS_TEST(suite, t_tls5); +#endif /* COAP_SERVER_SUPPORT */ + TLS_TEST(suite, t_tls6); + TLS_TEST(suite, t_tls7); + TLS_TEST(suite, t_tls8); +#if COAP_SERVER_SUPPORT + TLS_TEST(suite, t_tls9); +#endif /* COAP_SERVER_SUPPORT */ +#if COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT + TLS_TEST(suite, t_tls10); +#endif /* COAP_SERVER_SUPPORT && COAP_IPV4_SUPPORT */ +#if COAP_CLIENT_SUPPORT + TLS_TEST(suite, t_tls11); +#endif /* COAP_CLIENT_SUPPORT */ +#if COAP_OSCORE_SUPPORT + TLS_TEST(suite, t_tls12); + TLS_TEST(suite, t_tls13); +#endif /* COAP_OSCORE_SUPPORT */ +#if COAP_WS_SUPPORT + TLS_TEST(suite, t_tls14); +#endif /* COAP_WS_SUPPORT */ +#if COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && !COAP_DISABLE_TCP && COAP_IPV4_SUPPORT + TLS_TEST(suite, t_tls15); +#endif /* COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && !COAP_DISABLE_TCP && COAP_IPV4_SUPPORT */ +#if COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && COAP_IPV4_SUPPORT + TLS_TEST(suite, t_tls16); +#endif /* COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT && COAP_IPV4_SUPPORT */ return suite; }