diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/CMakeLists.txt b/samples/subsys/mgmt/mcumgr/smp_svr/CMakeLists.txt index 1e85a89ddc646..36f23e3286c9b 100644 --- a/samples/subsys/mgmt/mcumgr/smp_svr/CMakeLists.txt +++ b/samples/subsys/mgmt/mcumgr/smp_svr/CMakeLists.txt @@ -14,3 +14,22 @@ project(smp_svr) target_sources(app PRIVATE src/main.c) target_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_BT app PRIVATE src/bluetooth.c) + +if(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) + # Use dummy certificate files + set(cert_dir certificates) + set(cert_files echo-apps-cert.der;echo-apps-key.der) + set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated) + + message(WARNING "Using dummy certificate files, these are provided for demonstration only") + + foreach(inc_file ${cert_files}) + generate_inc_file_for_target( + app + ${cert_dir}/${inc_file} + ${gen_dir}/${inc_file}.inc + ) + endforeach() + + target_sources(app PRIVATE src/udp_dtls.c) +endif() diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-cert.der b/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-cert.der new file mode 100644 index 0000000000000..bfcb335e31c8c Binary files /dev/null and b/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-cert.der differ diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-key.der b/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-key.der new file mode 100644 index 0000000000000..5a4d67372ea41 Binary files /dev/null and b/samples/subsys/mgmt/mcumgr/smp_svr/certificates/echo-apps-key.der differ diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml b/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml index bd05351e9118e..6be485e0e5cea 100644 --- a/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml +++ b/samples/subsys/mgmt/mcumgr/smp_svr/sample.yaml @@ -37,6 +37,16 @@ tests: - frdm_k64f integration_platforms: - frdm_k64f + sample.mcumgr.smp_svr.udp_dtls: + extra_args: + - EXTRA_CONF_FILE="udp_dtls.conf" + - CONFIG_IMG_MANAGER=n + - SB_CONFIG_BOOTLOADER_NONE=y + platform_allow: + - native_sim + integration_platforms: + - native_sim + build_only: true sample.mcumgr.smp_svr.udp.802154.subg: extra_args: EXTRA_CONF_FILE="udp.conf;802154-subg.conf" platform_allow: beagleconnect_freedom diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/src/common.h b/samples/subsys/mgmt/mcumgr/smp_svr/src/common.h index 3e7398df0b8ee..b8a51c5abb833 100644 --- a/samples/subsys/mgmt/mcumgr/smp_svr/src/common.h +++ b/samples/subsys/mgmt/mcumgr/smp_svr/src/common.h @@ -5,3 +5,4 @@ */ void start_smp_bluetooth_adverts(void); +int setup_udp_dtls(void); diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/src/main.c b/samples/subsys/mgmt/mcumgr/smp_svr/src/main.c index a66afb702cd49..2f33b436fe0c0 100644 --- a/samples/subsys/mgmt/mcumgr/smp_svr/src/main.c +++ b/samples/subsys/mgmt/mcumgr/smp_svr/src/main.c @@ -17,6 +17,9 @@ #ifdef CONFIG_MCUMGR_GRP_STAT #include #endif +#ifdef CONFIG_MCUMGR_TRANSPORT_UDP_DTLS +#include +#endif #define LOG_LEVEL LOG_LEVEL_DBG #include @@ -67,6 +70,20 @@ int main(void) } #endif +#ifdef CONFIG_MCUMGR_TRANSPORT_UDP_DTLS + rc = setup_udp_dtls(); + + if (rc == 0) { + rc = smp_udp_open(); + + if (rc != 0) { + LOG_ERR("UDP transport open failed: %d", rc); + } + } else { + LOG_ERR("TLS init failed, cannot start UDP transport"); + } +#endif + #ifdef CONFIG_MCUMGR_TRANSPORT_BT start_smp_bluetooth_adverts(); #endif diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/src/udp_dtls.c b/samples/subsys/mgmt/mcumgr/smp_svr/src/udp_dtls.c new file mode 100644 index 0000000000000..d6d5c445b20db --- /dev/null +++ b/samples/subsys/mgmt/mcumgr/smp_svr/src/udp_dtls.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +LOG_MODULE_DECLARE(smp_sample); + +static const unsigned char server_certificate[] = { +#include "echo-apps-cert.der.inc" +}; + +/* This is the private key in pkcs#8 format. */ +static const unsigned char private_key[] = { +#include "echo-apps-key.der.inc" +}; + +int setup_udp_dtls(void) +{ + int rc; + + rc = tls_credential_add(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG, + TLS_CREDENTIAL_PUBLIC_CERTIFICATE, server_certificate, + sizeof(server_certificate)); + + if (rc < 0) { + LOG_ERR("Failed to register public certificate: %d", rc); + return rc; + } + + rc = tls_credential_add(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, private_key, sizeof(private_key)); + + if (rc < 0) { + LOG_ERR("Failed to register private key: %d", rc); + } + + return rc; +} diff --git a/samples/subsys/mgmt/mcumgr/smp_svr/udp_dtls.conf b/samples/subsys/mgmt/mcumgr/smp_svr/udp_dtls.conf new file mode 100644 index 0000000000000..3a4b35788fd8a --- /dev/null +++ b/samples/subsys/mgmt/mcumgr/smp_svr/udp_dtls.conf @@ -0,0 +1,38 @@ +# Enable the UDP DTLS MCUmgr transport. +CONFIG_MCUMGR_TRANSPORT_UDP=y +CONFIG_MCUMGR_TRANSPORT_UDP_DTLS=y +CONFIG_MCUMGR_TRANSPORT_UDP_IPV4=y +CONFIG_MCUMGR_TRANSPORT_UDP_IPV6=y + +# Network settings +CONFIG_NETWORKING=y +CONFIG_NET_UDP=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y +CONFIG_NET_SOCKETS_ENABLE_DTLS=y +CONFIG_NET_SOCKETS_DTLS_TIMEOUT=30000 +CONFIG_NET_SOCKETS_DTLS_MAX_FRAGMENT_LENGTH=2048 +CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE=0 +CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=4 +CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=4 +CONFIG_NET_SOCKETS_TLS_MAX_CIPHERSUITES=4 +CONFIG_NET_SOCKETS_TLS_MAX_CLIENT_SESSION_COUNT=1 +CONFIG_NET_CONNECTION_MANAGER=y +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.1.1" +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" + +# mbedtls settings +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_PKCS1_V21=y +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y +CONFIG_MBEDTLS_MD=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 diff --git a/subsys/mgmt/mcumgr/transport/Kconfig.udp b/subsys/mgmt/mcumgr/transport/Kconfig.udp index 0560ddb6dc16a..4109795b6c4d1 100644 --- a/subsys/mgmt/mcumgr/transport/Kconfig.udp +++ b/subsys/mgmt/mcumgr/transport/Kconfig.udp @@ -62,9 +62,31 @@ config MCUMGR_TRANSPORT_UDP_MTU MCUMGR_TRANSPORT_UDP_MTU <= MCUMGR_TRANSPORT_NETBUF_SIZE + SMP msg overhead - address size where address size is determined by IPv4/IPv6 selection. +config MCUMGR_TRANSPORT_UDP_DTLS + bool "DTLS" + select MBEDTLS + select MBEDTLS_ENABLE_HEAP + select NET_SOCKETS_SOCKOPT_TLS + select NET_SOCKETS_ENABLE_DTLS + select TLS_CREDENTIALS + help + Enable DTLS for UDP transport, this means normal non-authenticated connections will not + be supported and the transport will not be started automatically at boot-up, the + application will need to add the certificates to the system and set them up before + opening the transport using the `smp_udp_open` function. + +config MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG + int "TLS credential tag" + default 1 + depends on MCUMGR_TRANSPORT_UDP_DTLS + help + The TLS tag which the application must add the certificates to before starting the UDP + transport. + config MCUMGR_TRANSPORT_UDP_AUTOMATIC_INIT bool "UDP SMP autostart" default y + depends on !MCUMGR_TRANSPORT_UDP_DTLS help Enable starting the UDP SMP transport at boot time without needing any code in the application to do this, otherwise will need the user diff --git a/subsys/mgmt/mcumgr/transport/src/smp_udp.c b/subsys/mgmt/mcumgr/transport/src/smp_udp.c index 2dc703dceadb7..822f71fd5243c 100644 --- a/subsys/mgmt/mcumgr/transport/src/smp_udp.c +++ b/subsys/mgmt/mcumgr/transport/src/smp_udp.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2019-2020, Prevas A/S - * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2022-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,10 @@ #include #include +#if defined(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) +#include +#endif + #include #define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL @@ -167,6 +171,10 @@ static int create_socket(enum proto_type proto, int *sock) struct sockaddr *addr = (struct sockaddr *)&addr_storage; socklen_t addr_len = 0; +#if defined(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) + int socket_role = TLS_DTLS_ROLE_SERVER; +#endif + if (IS_ENABLED(CONFIG_MCUMGR_TRANSPORT_UDP_IPV4) && proto == PROTOCOL_IPV4) { struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; @@ -187,7 +195,11 @@ static int create_socket(enum proto_type proto, int *sock) addr6->sin6_addr = in6addr_any; } +#if defined(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) + tmp_sock = zsock_socket(addr->sa_family, SOCK_DGRAM, IPPROTO_DTLS_1_2); +#else tmp_sock = zsock_socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); +#endif err = errno; if (tmp_sock < 0) { @@ -197,6 +209,29 @@ static int create_socket(enum proto_type proto, int *sock) return -err; } +#if defined(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) + sec_tag_t sec_tag_list[] = { + CONFIG_MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG, + }; + + err = zsock_setsockopt(tmp_sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list, + sizeof(sec_tag_list)); + + if (err < 0) { + LOG_ERR("Failed to set UDP secure option: %d", errno); + return err; + } + + /* Set role to DTLS server */ + err = zsock_setsockopt(tmp_sock, SOL_TLS, TLS_DTLS_ROLE, &socket_role, + sizeof(socket_role)); + + if (err < 0) { + LOG_ERR("Failed to set DTLS role secure option: %d", errno); + return err; + } +#endif + if (zsock_bind(tmp_sock, addr, addr_len) < 0) { err = errno; LOG_ERR("Could not bind to receive socket (%s), err: %i", @@ -305,6 +340,28 @@ int smp_udp_open(void) { bool started = false; +#if defined(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS) + int rc; + size_t len = 0; + + rc = tls_credential_get(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG, + TLS_CREDENTIAL_PUBLIC_CERTIFICATE, NULL, &len); + + if (rc == -ENOENT) { + LOG_ERR("Missing DTLS public certificate credential"); + return rc; + } + + len = 0; + rc = tls_credential_get(CONFIG_MCUMGR_TRANSPORT_UDP_DTLS_TLS_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, NULL, &len); + + if (rc == -ENOENT) { + LOG_ERR("Missing DTLS private key credential"); + return rc; + } +#endif + #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4 if (k_thread_join(&smp_udp_configs.ipv4.thread, K_NO_WAIT) == 0 || threads_created == false) {