diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index f260f039557a..597665a5f51a 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -45,6 +45,18 @@ permissions: jobs: + ble_sim: + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Compile + run: | + ./script/cmake-build simulation -DOT_BBTC=ON + expects-linux: runs-on: ubuntu-20.04 env: diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index be85e29d39da..a3308b93517d 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -86,6 +86,7 @@ ot_option(OT_ASSERT OPENTHREAD_CONFIG_ASSERT_ENABLE "assert function OT_ASSERT() ot_option(OT_BACKBONE_ROUTER OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE "backbone router functionality") ot_option(OT_BACKBONE_ROUTER_DUA_NDPROXYING OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE "BBR DUA ND Proxy") ot_option(OT_BACKBONE_ROUTER_MULTICAST_ROUTING OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE "BBR MR") +ot_option(OT_BBTC OPENTHREAD_CONFIG_BLE_SECURE_ENABLE "Ble based thread commissioning") ot_option(OT_BORDER_AGENT OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE "border agent") ot_option(OT_BORDER_AGENT_ID OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE "create and save border agent ID") ot_option(OT_BORDER_ROUTER OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE "border router") diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni index 9bbb543d2575..dacc983ac9b4 100644 --- a/etc/gn/openthread.gni +++ b/etc/gn/openthread.gni @@ -81,6 +81,9 @@ if (openthread_enable_core_config_args) { # Enable backbone router functionality openthread_config_backbone_router_enable = false + # Enable BLE based commissioning functionality + openthread_config_ble_secure_enable = false + # Enable border agent support openthread_config_border_agent_enable = false diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 36ff95726db4..32a8ba25376f 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -30,6 +30,7 @@ ANYCAST_LOCATOR ?= 0 BACKBONE_ROUTER ?= 0 +BBTC ?= 0 BIG_ENDIAN ?= 0 BORDER_AGENT ?= 0 BORDER_ROUTER ?= 0 @@ -104,6 +105,10 @@ ifeq ($(BACKBONE_ROUTER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1 endif +ifeq ($(BBTC),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_BLE_SECURE_ENABLE=1 +endif + ifeq ($(BIG_ENDIAN),1) COMMONCFLAGS += -DBYTE_ORDER_BIG_ENDIAN=1 endif diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt index 883ee641ce22..3dd0d10bdca5 100644 --- a/examples/platforms/simulation/CMakeLists.txt +++ b/examples/platforms/simulation/CMakeLists.txt @@ -59,6 +59,7 @@ set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) add_library(openthread-simulation alarm.c + ble.c crypto.c diag.c dso_transport.c diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c new file mode 100644 index 000000000000..5a6c372c12a4 --- /dev/null +++ b/examples/platforms/simulation/ble.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval, uint8_t aType) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + OT_UNUSED_VARIABLE(aType); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} diff --git a/include/Makefile.am b/include/Makefile.am index 6f18341b543e..23ee8cc7df3d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -37,6 +37,7 @@ EXTRA_DIST = \ openthread_headers = \ openthread/backbone_router.h \ openthread/backbone_router_ftd.h \ + openthread/ble_secure.h \ openthread/border_agent.h \ openthread/border_router.h \ openthread/border_routing.h \ @@ -101,6 +102,7 @@ dist_openthread_HEADERS = $(openthread_headers) ot_platform_headers = \ openthread/platform/alarm-micro.h \ openthread/platform/alarm-milli.h \ + openthread/platform/ble.h \ openthread/platform/border_routing.h \ openthread/platform/crypto.h \ openthread/platform/debug_uart.h \ diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 5bc1c09677be..b9f9097a724f 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -41,6 +41,7 @@ source_set("openthread") { public = [ "backbone_router.h", "backbone_router_ftd.h", + "ble_secure.h", "border_agent.h", "border_router.h", "border_routing.h", @@ -84,6 +85,7 @@ source_set("openthread") { "ping_sender.h", "platform/alarm-micro.h", "platform/alarm-milli.h", + "platform/ble.h", "platform/border_routing.h", "platform/crypto.h", "platform/debug_uart.h", diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h new file mode 100644 index 000000000000..7355af857485 --- /dev/null +++ b/include/openthread/ble_secure.h @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines the top-level functions for the OpenThread BLE Secure implementation. + * + * @note + * The functions in this module require the build-time feature `OPENTHREAD_CONFIG_BLE_SECURE_ENABLE=1`. + * + * @note + * To enable cipher suite DTLS_PSK_WITH_AES_128_CCM_8, MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * must be enabled in mbedtls-config.h + * To enable cipher suite DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + * MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED must be enabled in mbedtls-config.h. + */ + +#ifndef OPENTHREAD_BLE_SECURE_H_ +#define OPENTHREAD_BLE_SECURE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-ble-secure + * + * @brief + * This module includes functions that control BLE Secure (TLS over BLE) communication. + * + * The functions in this module are available when BLE Secure API feature + * (`OPENTHREAD_CONFIG_BLE_SECURE_ENABLE`) is enabled. + * + * @{ + * + */ + +#define OT_TCAT_ELEVATION_PSK_LENGTH \ + 32 ///< Maximum string length of a secure BLE elevation PSK (does not include null char). + +/** + * This enumeration represents TCAT TLV types. + * + */ +typedef enum otTcatTlvType +{ + OT_TCAT_TLV_COMMAND = 0, ///< TCAT Command TLV + OT_TCAT_TLV_RESPONSE = 1, ///< TCAT Response TLV + OT_TCAT_TLV_ACTIVE_DATASET = 16, ///< TCAT Active Dataset TLV + OT_TCAT_TLV_APPLICATION = 18, ///< TCAT Application TLV + +} otTcatTlvType; + +/** + * This enumeration represents TCAT Command types. + * + */ +typedef enum otTcatCommandType +{ + OT_TCAT_COMMAND_TERMINATE = 0, ///< Terminate connection + OT_TCAT_COMMAND_THREAD_START = 1, ///< Start Thread Interface + OT_TCAT_COMMAND_THREAD_STOP = 2, ///< Stop Thread Interface + +} otTcatCommandType; + +/** + * This enumeration represents TCAT Command types. + * + */ +typedef enum otTcatResponseType +{ + OT_TCAT_RESPONSE_SUCCESS = 0, ///< Success + OT_TCAT_RESPONSE_INVALID_STATE = 1, ///< Invalid State + OT_TCAT_RESPONSE_PARSE_ERROR = 2, ///< Invalid State + +} otTcatResponseType; + +/** + * This structure represents a TCAT vendor information. + * + */ +typedef struct otTcatVendorInfo +{ + const char *mProvisioningUrl; ///< Provisioning URL path string + const char *mVendorName; ///< Vendor name string + const char *mVendorModel; ///< Vendor model string + const char *mVendorSwVersion; ///< Venor softwae version string + const char *mVendorData; ///< Vendor stpecific data string +} otTcatVendorInfo; + +/** + * This structure represents a TCAT elevation PSKd. + * + */ +typedef struct otTcatElevationPsk +{ + char m8[OT_TCAT_ELEVATION_PSK_LENGTH + 1]; ///< Char string array (must be null terminated - +1 is for null char). +} otTcatElevationPsk; + +/** + * This function pointer is called to notify the completion of a join operation. + * + * @param[in] aError OT_ERROR_NONE if the join process succeeded. + * OT_ERROR_SECURITY if the join process failed due to security credentials. + * + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleTcatJoin)(otError aError, void *aContext); + +/** + * This function pointer is called when the secure BLE connection state changes. + * + * @param[in] aConnected TRUE, if a secure connection was established, FALSE otherwise. + * @param[in] aBleConnectionOpen TRUE if a BLE connection was established to carry a TLS data stream, FALSE + * otherwise. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleBleSecureConnect)(bool aConnected, bool aBleConnectionOpen, void *aContext); + +/** + * This function pointer is called when data was received over the TLS connection. + * When TLV mode is activate, the function will be called once a complete TLV was received and the + * message offset points to the TLV value. + * + * + * @param[in] aMessage A pointer to the message. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleBleSecureReceive)(otMessage *aMessage, void *aContext); + +/** + * This function starts the BLE Secure service. + * When TLV mode is activate, the function will be called once a complete TLV was received and the + * message offset points to the TLV value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aConnectHandler A pointer to a function that will be called when the connection + * state changes. + * @param[in] aReceiveHandler A pointer to a function that will be called once data has been received + * over the TLS connection. + * @param[in] aTlvMode A boolean value indicating if line mode shall be activated. + * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. + * + * @retval OT_ERROR_NONE Successfully started the BLE Secure server. + * @retval OT_ERROR_ALREADY The service was stated already. + * + */ +otError otBleSecureStart(otInstance *aInstance, + otHandleBleSecureConnect aConnectHandler, + otHandleBleSecureReceive aReceiveHandler, + bool aTlvMode, + void *aContext); + +/** + * Enables the TCAT protocol over BLE Secure. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aElevationPsk A pointer to the PSK for elevating the commissioner rights (may be NULL). + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call, may be + * NULL). + * @param[in] aHandler A pointer to a function that is called when the join operation completes. + * + * @retval OT_ERROR_NONE Successfully started the BLE Secure Joiner role. + * @retval OT_ERROR_INVALID_ARGS @p aElevationPsk or @p aVendorInfo is invalid. + * @retval OT_ERROR_INVALID_STATE The BLE function has not been started or line mode is not selected. + * + */ +otError otBleSecureTcatStart(otInstance *aInstance, + const char *aElevationPsk, + otTcatVendorInfo *aVendorInfo, + otHandleTcatJoin aHandler); + +/** + * This function stops the BLE Secure server. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otBleSecureStop(otInstance *aInstance); + +/** + * This method sets the Pre-Shared Key (PSK) and cipher suite + * TLS_PSK_WITH_AES_128_CCM_8. + * + * @note This function requires the build-time feature `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK length. + * @param[in] aPskIdentity The Identity Name for the PSK. + * @param[in] aPskIdLength The PSK Identity Length. + * + */ +void otBleSecureSetPsk(otInstance *aInstance, + const uint8_t *aPsk, + uint16_t aPskLength, + const uint8_t *aPskIdentity, + uint16_t aPskIdLength); + +/** + * This method returns the peer x509 certificate base64 encoded. + * + * @note This function requires the build-time features `MBEDTLS_BASE64_C` and + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aPeerCert A pointer to the base64 encoded certificate buffer. + * @param[out] aCertLength The length of the base64 encoded peer certificate. + * @param[in] aCertBufferSize The buffer size of aPeerCert. + * + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NONE Successfully get the peer certificate. + * @retval OT_ERROR_NO_BUFS Can't allocate memory for certificate. + * + */ +otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, + unsigned char *aPeerCert, + size_t *aCertLength, + size_t aCertBufferSize); + +/** + * This method returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ANS1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @note This function requires the build-time feature + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * @param[out] aAns1Type A pointer to the ANS1 type of the attribute written to the buffer. + * + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * + */ +otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance, + const char *aOid, + size_t aOidLength, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize, + int *aAns1Type); + +/** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @note This function requires the build-time feature + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_NOT_FOUND The requested attribute was not found. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NOT_IMPLEMENTED The value of aThreadOidDescriptor is >127. + * @retval OT_ERROR_PARSE The certificate extensions could not be parsed. + * + */ +otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); + +/** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_NOT_FOUND The requested attribute was not found. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NOT_IMPLEMENTED The value of aThreadOidDescriptor is >127. + * @retval OT_ERROR_PARSE The certificate extensions could not be parsed. + * + */ +otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); + +/** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the CA x509 certificate chain, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_NOT_FOUND The requested attribute was not found. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NOT_IMPLEMENTED The value of aThreadOidDescriptor is >127. + * @retval OT_ERROR_PARSE The certificate extensions could not be parsed. + * + */ +otError otBleSecureGetThreadAttributeFromCaCertificateChain(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); + +/** + * This method sets the authentication mode for the BLE secure connection. + * + * Disable or enable the verification of peer certificate. + * Must be called before start. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVerifyPeerCertificate true, to verify the peer certificate. + * + */ +void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate); + +/** + * This method sets the local device's X509 certificate with corresponding private key for + * TLS session with TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8. + * + * @note This function requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aX509Cert A pointer to the PEM formatted X509 certificate. + * @param[in] aX509Length The length of certificate. + * @param[in] aPrivateKey A pointer to the PEM formatted private key. + * @param[in] aPrivateKeyLength The length of the private key. + * + */ +void otBleSecureSetCertificate(otInstance *aInstance, + const uint8_t *aX509Cert, + uint32_t aX509Length, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength); + +/** + * This method sets the trusted top level CAs. It is needed for validating the + * certificate of the peer. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @note This function requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aX509CaCertificateChain A pointer to the PEM formatted X509 CA chain. + * @param[in] aX509CaCertChainLength The length of chain. + * + */ +void otBleSecureSetCaCertificateChain(otInstance *aInstance, + const uint8_t *aX509CaCertificateChain, + uint32_t aX509CaCertChainLength); + +/** + * This method initializes TLS session with a peer using an already open BLE connection. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_ERROR_NONE Successfully started TLS connection. + * + */ +otError otBleSecureConnect(otInstance *aInstance); + +/** + * This method stops the TLS connection. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otBleSecureDisconnect(otInstance *aInstance); + +/** + * This method indicates whether or not the TLS session is connected. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE The TLS session is connected. + * @retval FALSE The TLS session is not connected. + * + */ +bool otBleSecureIsConnected(otInstance *aInstance); + +/** + * This method indicates whether or not the TLS session is active. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE If TLS session is active. + * @retval FALSE If TLS session is not active. + * + */ +bool otBleSecureIsConnectionActive(otInstance *aInstance); + +/** + * This method indicates whether or not the commissioner rights have been elevated using elevation PSK. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE If commissioner rights are elevated. + * @retval FALSE If commissioner rights are not elevated. + * + */ +bool otBleSecureIsTcatElevated(otInstance *aInstance); + +/** + * This method indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ +bool otBleSecureIsTcatEnabled(otInstance *aInstance); + +/** + * This method sends a secure BLE message. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the message to send. + * + * If the return value is OT_ERROR_NONE, OpenThread takes ownership of @p aMessage, and the caller should no longer + * reference @p aMessage. If the return value is not OT_ERROR_NONE, the caller retains ownership of @p aMessage, + * including freeing @p aMessage if the message buffer is no longer needed. + * + * @retval OT_ERROR_NONE Successfully sent message. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage); + +/** + * This method sends a secure BLE data packet. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aBuf A pointer to the data to send. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval OT_ERROR_NONE Successfully sent data. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); + +/** + * This method sends a secure BLE data packet containing a TCAT application TLV. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aBuf A pointer to the data to send. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval OT_ERROR_NONE Successfully sent data. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); + +/** + * This method flushes the send buffer. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_ERROR_NONE Successfully flushed output buffer. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureFlush(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENTHREAD_BLE_SECURE_H_ */ diff --git a/include/openthread/instance.h b/include/openthread/instance.h index b835b4b328b0..61f4bd7ca5b5 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (335) +#define OPENTHREAD_API_VERSION (336) /** * @addtogroup api-instance diff --git a/include/openthread/platform/ble.h b/include/openthread/platform/ble.h new file mode 100644 index 000000000000..4d1920ff1a06 --- /dev/null +++ b/include/openthread/platform/ble.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines a OpenThread BLE GATT peripheral interface driver. + * + */ + +#ifndef OT_PLATFORM_BLE_H_ +#define OT_PLATFORM_BLE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** + * @addtogroup plat-ble + * + * @brief + * This module includes the platform abstraction for BLE Host communication. + * The platform needs to implement Bluetooth LE 4.2 or higher. + * + * @{ + * + */ + +enum +{ + /** + * Time slot duration on PHY layer in microseconds (0.625ms). + */ + OT_BLE_TIMESLOT_UNIT = 625, + + /** + * Minimum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (20ms). + */ + OT_BLE_ADV_INTERVAL_MIN = 0x0020, + + /** + * Maximum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (10.24s). + */ + OT_BLE_ADV_INTERVAL_MAX = 0x4000, + + /** + * Unit used to calculate interval duration (0.625ms). + */ + OT_BLE_ADV_INTERVAL_UNIT = OT_BLE_TIMESLOT_UNIT, +}; + +/** + * This structure represents an BLE packet. + * + */ +typedef struct otBleRadioPacket +{ + uint8_t *mValue; ///< The value of an attribute + uint16_t mLength; ///< Length of the @p mValue. + int8_t mPower; ///< Transmit/receive power in dBm. +} otBleRadioPacket; + +/******************************************************************************* + * @section Bluetooth Low Energy management. + ******************************************************************************/ + +/** + * Enable the Bluetooth Low Energy radio. + * + * @note BLE Device should use the highest ATT_MTU supported that does not + * exceed OT_BLE_ATT_MTU_MAX octets. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval ::OT_ERROR_NONE Successfully enabled. + * @retval ::OT_ERROR_FAILED The BLE radio could not be enabled. + */ +otError otPlatBleEnable(otInstance *aInstance); + +/** + * Disable the Bluetooth Low Energy radio. + * + * When disabled, the BLE stack will flush event queues and not generate new + * events. The BLE peripheral is turned off or put into a low power sleep + * state. Any dynamic memory used by the stack should be released, + * but static memory may remain reserved. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval ::OT_ERROR_NONE Successfully transitioned to disabled. + * @retval ::OT_ERROR_FAILED The BLE radio could not be disabled. + */ +otError otPlatBleDisable(otInstance *aInstance); + +/**************************************************************************** + * @section Bluetooth Low Energy GAP. + ***************************************************************************/ + +/** + * Starts BLE Advertising procedure. + * + * The BLE device shall use undirected advertising with no filter applied. + * A single BLE Advertising packet must be sent on all advertising + * channels (37, 38 and 39). + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aInterval The interval between subsequent advertising packets + * in OT_BLE_ADV_INTERVAL_UNIT units. + * Shall be within OT_BLE_ADV_INTERVAL_MIN and + * OT_BLE_ADV_INTERVAL_MAX range. + * @param[in] aType The advertisement properties as a bitmask: + * whether it is connectable | scannable. + * + * @retval ::OT_ERROR_NONE Advertising procedure has been started. + * @retval ::OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * @retval ::OT_ERROR_INVALID_ARGS Invalid interval value has been supplied. + */ +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval, uint8_t aType); + +/** + * Stops BLE Advertising procedure. + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval ::OT_ERROR_NONE Advertising procedure has been stopped. + * @retval ::OT_ERROR_INVALID_STATE BLE Device is in invalid state. + */ +otError otPlatBleGapAdvStop(otInstance *aInstance); + +/** + * The BLE driver calls this method to notify OpenThread that a BLE Device has + * been connected. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aConnectionId The identifier of the open connection. + * + */ +extern void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId); + +/** + * The BLE driver calls this method to notify OpenThread that the BLE Device + * has been disconnected. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aConnectionId The identifier of the closed connection. + * + */ +extern void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId); + +/** + * Disconnects BLE connection. + * + * The BLE device shall indicate the OT_BLE_HCI_REMOTE_USER_TERMINATED HCI code reason. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval ::OT_ERROR_NONE Disconnection procedure has been started. + * @retval ::OT_ERROR_INVALID_STATE BLE Device is in invalid state. + */ +otError otPlatBleGapDisconnect(otInstance *aInstance); + +/******************************************************************************* + * @section Bluetooth Low Energy GATT Common. + *******************************************************************************/ + +/** + * Reads currently use value of ATT_MTU. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aMtu A pointer contains current ATT_MTU value. + * + * @retval ::OT_ERROR_NONE ATT_MTU value has been placed in @p aMtu. + * @retval ::OT_ERROR_FAILED BLE Device cannot determine its ATT_MTU. + */ +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu); + +/******************************************************************************* + * @section Bluetooth Low Energy GATT Server. + ******************************************************************************/ + +/** + * Sends ATT Handle Value Indication. + * + * @note This function shall be used only for GATT Server. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aHandle The handle of the attribute to be indicated. + * @param[in] aPacket A pointer to the packet contains value to be indicated. + * + * @retval ::OT_ERROR_NONE ATT Handle Value Indication has been sent. + * @retval ::OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * @retval ::OT_ERROR_INVALID_ARGS Invalid handle value, data or data length has been supplied. + * @retval ::OT_ERROR_NO_BUFS No available internal buffer found. + */ +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket); + +/** + * The BLE driver calls this method to notify OpenThread that an ATT Write Request + * packet has been received. + * + * @note This function shall be used only for GATT Server. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aHandle The handle of the attribute to be written. + * @param[in] aPacket A pointer to the packet contains value to be written to the attribute. + * + */ +extern void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket); + +/** + * The BLE driver calls this method to notify OpenThread that an ATT Read Request + * packet has been received. + * + * @note This function shall be used only for GATT Server. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aHandle The handle of the attribute to be read. + * @param[out] aPacket A pointer to the packet to be filled with pointers to attribute data to be read. + * + */ +extern void otPlatBleGattServerOnReadRequest(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // OT_PLATFORM_BLE_H_ diff --git a/script/check-arm-build b/script/check-arm-build index 398dff766827..c440958295e0 100755 --- a/script/check-arm-build +++ b/script/check-arm-build @@ -91,7 +91,7 @@ build_nrf52840() main() { - export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" + export CPPFLAGS="${CPPFLAGS-} -DNDEBUG" if [[ $# == 0 ]]; then build_nrf52840 diff --git a/script/test b/script/test index 8a2372598812..17cf0478e5da 100755 --- a/script/test +++ b/script/test @@ -656,7 +656,7 @@ main() { envsetup "$@" - if [[ -z ${1:-} ]]; then + if [[ -z ${1-} ]]; then print_usage 1 fi diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 8c9aaa4e2f89..9c15dd741af4 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -74,6 +74,10 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" ] } + if (OPENTHREAD_CONFIG_BLE_SECURE_ENABLE) { + defines += [ "OPENTHREAD_CONFIG_BLE_SECURE_ENABLE=1" ] + } + if (openthread_config_border_agent_enable) { defines += [ "OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1" ] } @@ -301,6 +305,7 @@ config("core_config") { openthread_core_files = [ "api/backbone_router_api.cpp", "api/backbone_router_ftd_api.cpp", + "api/ble_secure_api.cpp", "api/border_agent_api.cpp", "api/border_router_api.cpp", "api/border_routing_api.cpp", @@ -530,6 +535,8 @@ openthread_core_files = [ "meshcop/network_name.hpp", "meshcop/panid_query_client.cpp", "meshcop/panid_query_client.hpp", + "meshcop/tcat_agent.cpp", + "meshcop/tcat_agent.hpp", "meshcop/timestamp.cpp", "meshcop/timestamp.hpp", "net/checksum.cpp", @@ -585,6 +592,8 @@ openthread_core_files = [ "net/tcp6_ext.hpp", "net/udp6.cpp", "net/udp6.hpp", + "radio/ble_secure.cpp", + "radio/ble_secure.hpp", "radio/max_power_table.hpp", "radio/radio.cpp", "radio/radio.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index acd33ffe6662..99499ad372bd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMMON_INCLUDES set(COMMON_SOURCES api/backbone_router_api.cpp api/backbone_router_ftd_api.cpp + api/ble_secure_api.cpp api/border_agent_api.cpp api/border_router_api.cpp api/border_routing_api.cpp @@ -158,6 +159,7 @@ set(COMMON_SOURCES meshcop/meshcop_tlvs.cpp meshcop/network_name.cpp meshcop/panid_query_client.cpp + meshcop/tcat_agent.cpp meshcop/timestamp.cpp net/checksum.cpp net/dhcp6_client.cpp @@ -185,6 +187,7 @@ set(COMMON_SOURCES net/tcp6.cpp net/tcp6_ext.cpp net/udp6.cpp + radio/ble_secure.cpp radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index feeb5221acd2..656f9f54aaef 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -123,6 +123,7 @@ libopenthread_mtd_a_CPPFLAGS = \ SOURCES_COMMON = \ api/backbone_router_api.cpp \ api/backbone_router_ftd_api.cpp \ + api/ble_secure_api.cpp \ api/border_agent_api.cpp \ api/border_router_api.cpp \ api/border_routing_api.cpp \ @@ -248,6 +249,7 @@ SOURCES_COMMON = \ meshcop/meshcop_tlvs.cpp \ meshcop/network_name.cpp \ meshcop/panid_query_client.cpp \ + meshcop/tcat_agent.cpp \ meshcop/timestamp.cpp \ net/checksum.cpp \ net/dhcp6_client.cpp \ @@ -275,6 +277,7 @@ SOURCES_COMMON = \ net/tcp6.cpp \ net/tcp6_ext.cpp \ net/udp6.cpp \ + radio/ble_secure.cpp \ radio/radio.cpp \ radio/radio_callbacks.cpp \ radio/radio_platform.cpp \ @@ -571,6 +574,7 @@ HEADERS_COMMON = \ meshcop/meshcop_tlvs.hpp \ meshcop/network_name.hpp \ meshcop/panid_query_client.hpp \ + meshcop/tcat_agent.hpp \ meshcop/timestamp.hpp \ net/checksum.hpp \ net/dhcp6.hpp \ @@ -600,6 +604,7 @@ HEADERS_COMMON = \ net/tcp6_ext.hpp \ net/udp6.hpp \ openthread-core-config.h \ + radio/ble_secure.hpp \ radio/max_power_table.hpp \ radio/radio.hpp \ radio/trel_interface.hpp \ diff --git a/src/core/api/ble_secure_api.cpp b/src/core/api/ble_secure_api.cpp new file mode 100644 index 000000000000..d210a6cf63ae --- /dev/null +++ b/src/core/api/ble_secure_api.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the OpenThread BLE Secure API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#include + +#include +#include "common/as_core_type.hpp" +#include "common/locator_getters.hpp" +#include "meshcop/tcat_agent.hpp" +#include "radio/ble_secure.hpp" + +#include + +using namespace ot; + +otError otBleSecureStart(otInstance *aInstance, + otHandleBleSecureConnect aConnectHandler, + otHandleBleSecureReceive aReceiveHandler, + bool aTlvMode, + void *aContext) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().Start(aConnectHandler, aReceiveHandler, aTlvMode, aContext); +} + +otError otBleSecureTcatStart(otInstance *aInstance, + const char *aElevationPsk, + otTcatVendorInfo *aVendorInfo, + otHandleTcatJoin aHandler) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().TcatStart( + aElevationPsk, static_cast(aVendorInfo), aHandler); +} + +void otBleSecureStop(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationBleSecure().Stop(); } + +#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +void otBleSecureSetPsk(otInstance *aInstance, + const uint8_t *aPsk, + uint16_t aPskLength, + const uint8_t *aPskIdentity, + uint16_t aPskIdLength) +{ + OT_ASSERT(aPsk != nullptr && aPskLength != 0 && aPskIdentity != nullptr && aPskIdLength != 0); + + AsCoreType(aInstance).GetApplicationBleSecure().SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); +} +#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, + unsigned char *aPeerCert, + size_t *aCertLength, + size_t aCertBufferSize) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().GetPeerCertificateBase64(aPeerCert, aCertLength, + aCertBufferSize); +} +#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance, + const char *aOid, + size_t aOidLength, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize, + int *aAns1Type) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().GetPeerSubjectAttributeByOid( + aOid, aOidLength, aAttributeBuffer, aAttributeLength, aAttributeBufferSize, aAns1Type); +} + +otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().GetThreadAttributeFromPeerCertificate( + aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, aAttributeBufferSize); +} +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().GetThreadAttributeFromOwnCertificate( + aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, aAttributeBufferSize); +} + +otError otBleSecureGetThreadAttributeFromCaCertificateChain(otInstance *aInstance, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().GetThreadAttributeFromCaCertificateChain( + aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, aAttributeBufferSize); +} + +void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate) +{ + AsCoreType(aInstance).GetApplicationBleSecure().SetSslAuthMode(aVerifyPeerCertificate); +} + +#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +void otBleSecureSetCertificate(otInstance *aInstance, + const uint8_t *aX509Cert, + uint32_t aX509Length, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength) +{ + OT_ASSERT(aX509Cert != nullptr && aX509Length != 0 && aPrivateKey != nullptr && aPrivateKeyLength != 0); + + AsCoreType(aInstance).GetApplicationBleSecure().SetCertificate(aX509Cert, aX509Length, aPrivateKey, + aPrivateKeyLength); +} + +void otBleSecureSetCaCertificateChain(otInstance *aInstance, + const uint8_t *aX509CaCertificateChain, + uint32_t aX509CaCertChainLength) +{ + OT_ASSERT(aX509CaCertificateChain != nullptr && aX509CaCertChainLength != 0); + + AsCoreType(aInstance).GetApplicationBleSecure().SetCaCertificateChain(aX509CaCertificateChain, + aX509CaCertChainLength); +} +#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +otError otBleSecureConnect(otInstance *aInstance) { return AsCoreType(aInstance).GetApplicationBleSecure().Connect(); } + +void otBleSecureDisconnect(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationBleSecure().Disconnect(); } + +bool otBleSecureIsConnected(otInstance *aInstance) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().IsConnected(); +} + +bool otBleSecureIsConnectionActive(otInstance *aInstance) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().IsConnectionActive(); +} + +bool otBleSecureIsTcatElevated(otInstance *aInstance) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().IsTcatElevated(); +} + +bool otBleSecureIsTcatEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().IsTcatEnabled(); +} + +otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().SendMessage(static_cast(aMessage)); +} + +otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().Send(aBuf, aLength); +} + +otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) +{ + return AsCoreType(aInstance).GetApplicationBleSecure().SendApplicationTlv(aBuf, aLength); +} + +otError otBleSecureFlush(otInstance *aInstance) { return AsCoreType(aInstance).GetApplicationBleSecure().Flush(); } + +void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aHandle); // Only a single handle is expected for RX + + if (aPacket == NULL) + return; + + AsCoreType(aInstance).GetApplicationBleSecure().HandleBleReceive(aPacket->mValue, aPacket->mLength); +} + +void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId) +{ + AsCoreType(aInstance).GetApplicationBleSecure().HandleBleConnected(aConnectionId); +} + +void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId) +{ + AsCoreType(aInstance).GetApplicationBleSecure().HandleBleDisconnected(aConnectionId); +} + +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 10d545d669ac..ff01a7e584fd 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -201,6 +201,9 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE , mApplicationCoapSecure(*this, /* aLayerTwoSecurity */ true) #endif +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + , mApplicationBleSecure(*this) +#endif #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE , mPingSender(*this) #endif diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 0a262bab0337..561df3572455 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -97,6 +97,7 @@ #include "net/sntp_client.hpp" #include "net/srp_client.hpp" #include "net/srp_server.hpp" +#include "radio/ble_secure.hpp" #include "thread/address_resolver.hpp" #include "thread/announce_begin_server.hpp" #include "thread/announce_sender.hpp" @@ -326,6 +327,16 @@ class Instance : public otInstance, private NonCopyable Coap::CoapSecure &GetApplicationCoapSecure(void) { return mApplicationCoapSecure; } #endif +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + /** + * This method returns a reference to application BLE Secure object. + * + * @returns A reference to the application BLE Secure object. + * + */ + Ble::BleSecure &GetApplicationBleSecure(void) { return mApplicationBleSecure; } +#endif + #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** * Enables/disables the "DNS name compressions" mode. @@ -590,6 +601,10 @@ class Instance : public otInstance, private NonCopyable Coap::CoapSecure mApplicationCoapSecure; #endif +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + Ble::BleSecure mApplicationBleSecure; +#endif + #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE Utils::PingSender mPingSender; #endif diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index a40cb0503593..e97f2fb55ff4 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -282,7 +282,8 @@ class Message : public otMessage, public Buffer, public GetProvider kTypeSupervision = 2, ///< A child supervision frame. kTypeMacEmptyData = 3, ///< An empty MAC data frame. kTypeIp4 = 4, ///< A full uncompressed IPv4 packet, for NAT64. - kTypeOther = 5, ///< Other (data) message. + kTypeBle = 5, ///< A BLE payload message. + kTypeOther = 6, ///< Other (data) message. }; /** diff --git a/src/core/config/dtls.h b/src/core/config/dtls.h index 09e833788bcc..43691306c7c6 100644 --- a/src/core/config/dtls.h +++ b/src/core/config/dtls.h @@ -59,7 +59,7 @@ #ifndef OPENTHREAD_CONFIG_DTLS_ENABLE #define OPENTHREAD_CONFIG_DTLS_ENABLE \ (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \ - OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE) + OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE || OPENTHREAD_CONFIG_BLE_SECURE_ENABLE) #endif #endif // CONFIG_DTLS_H_ diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/dtls.cpp index 28337a62e697..2a46f66e7e56 100644 --- a/src/core/meshcop/dtls.cpp +++ b/src/core/meshcop/dtls.cpp @@ -73,7 +73,7 @@ const int Dtls::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE}; #endif #endif -Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) +Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport) : InstanceLocator(aInstance) , mState(kStateClosed) , mPskLength(0) @@ -82,12 +82,15 @@ Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) , mTimerIntermediate(0) , mTimerSet(false) , mLayerTwoSecurity(aLayerTwoSecurity) + , mDatagramTransport(aDatagramTransport) , mReceiveMessage(nullptr) + , mContext(nullptr) , mSocket(aInstance) + , mTransportContext(nullptr) , mMessageSubType(Message::kSubTypeNone) , mMessageDefaultSubType(Message::kSubTypeNone) { -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED mPreSharedKey = nullptr; mPreSharedKeyIdentity = nullptr; @@ -121,9 +124,12 @@ Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) void Dtls::FreeMbedtls(void) { #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - mbedtls_ssl_cookie_free(&mCookieCtx); + if (mDatagramTransport) + { + mbedtls_ssl_cookie_free(&mCookieCtx); + } #endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED mbedtls_x509_crt_free(&mCaChain); mbedtls_x509_crt_free(&mOwnCert); @@ -144,7 +150,8 @@ Error Dtls::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHand mConnectedCallback.Set(aConnectedHandler, aContext); mReceiveCallback.Set(aReceiveHandler, aContext); - mState = kStateOpen; + mContext = aContext; + mState = kStateOpen; exit: return error; @@ -178,11 +185,17 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI ExitNow(); case Dtls::kStateOpen: + IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort()))); + mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort()); mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface()); - mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); + if (Get().HasUnicastAddress(aMessageInfo.GetSockAddr())) + { + mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); + } + mMessageInfo.SetSockPort(aMessageInfo.GetSockPort()); SuccessOrExit(Setup(false)); @@ -232,6 +245,7 @@ Error Dtls::Bind(TransportCallback aCallback, void *aContext) VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready); mTransportCallback.Set(aCallback, aContext); + mTransportContext = aContext; exit: return error; @@ -248,7 +262,7 @@ Error Dtls::Setup(bool aClient) mbedtls_ssl_init(&mSsl); mbedtls_ssl_config_init(&mConf); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED mbedtls_x509_crt_init(&mCaChain); mbedtls_x509_crt_init(&mOwnCert); @@ -256,15 +270,20 @@ Error Dtls::Setup(bool aClient) #endif #endif #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - mbedtls_ssl_cookie_init(&mCookieCtx); + if (mDatagramTransport) + { + mbedtls_ssl_cookie_init(&mCookieCtx); + } #endif - rval = mbedtls_ssl_config_defaults(&mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); + rval = mbedtls_ssl_config_defaults( + &mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, + mDatagramTransport ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); VerifyOrExit(rval == 0); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE - if (mVerifyPeerCertificate && mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) +#if OPENTHREAD_CONFIG_TLS_API_ENABLE + if (mVerifyPeerCertificate && (mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 || + mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)) { mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_REQUIRED); } @@ -313,7 +332,7 @@ Error Dtls::Setup(bool aClient) mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, this); #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - if (!aClient) + if (!aClient && mDatagramTransport) { rval = mbedtls_ssl_cookie_setup(&mCookieCtx, Crypto::MbedTls::CryptoSecurePrng, nullptr); VerifyOrExit(rval == 0); @@ -326,16 +345,20 @@ Error Dtls::Setup(bool aClient) VerifyOrExit(rval == 0); mbedtls_ssl_set_bio(&mSsl, this, &Dtls::HandleMbedtlsTransmit, HandleMbedtlsReceive, nullptr); - mbedtls_ssl_set_timer_cb(&mSsl, this, &Dtls::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer); + + if (mDatagramTransport) + { + mbedtls_ssl_set_timer_cb(&mSsl, this, &Dtls::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer); + } if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) { rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - rval = SetApplicationCoapSecureKeys(); + rval = SetApplicationSecureKeys(); } #endif VerifyOrExit(rval == 0); @@ -348,10 +371,10 @@ Error Dtls::Setup(bool aClient) { LogInfo("DTLS started"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogInfo("Application Coap Secure DTLS started"); + LogInfo("Application Secure (D)TLS started"); } #endif @@ -369,14 +392,16 @@ Error Dtls::Setup(bool aClient) return Crypto::MbedTls::MapError(rval); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -int Dtls::SetApplicationCoapSecureKeys(void) +#if OPENTHREAD_CONFIG_TLS_API_ENABLE +int Dtls::SetApplicationSecureKeys(void) { int rval = 0; switch (mCipherSuites[0]) { case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED if (mCaChainSrc != nullptr) { @@ -426,14 +451,15 @@ int Dtls::SetApplicationCoapSecureKeys(void) return rval; } -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE void Dtls::Close(void) { Disconnect(); - mState = kStateClosed; - mTimerSet = false; + mState = kStateClosed; + mTransportContext = nullptr; + mTimerSet = false; mTransportCallback.Clear(); IgnoreError(mSocket.Close()); @@ -472,7 +498,7 @@ Error Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength) return error; } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED void Dtls::SetCertificate(const uint8_t *aX509Certificate, @@ -491,7 +517,15 @@ void Dtls::SetCertificate(const uint8_t *aX509Certificate, mPrivateKeySrc = aPrivateKey; mPrivateKeyLength = aPrivateKeyLength; - mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; + if (mDatagramTransport) + { + mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; + } + else + { + mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + } + mCipherSuites[1] = 0; } @@ -550,7 +584,163 @@ Error Dtls::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLeng } #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +Error Dtls::GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize, + int *aAns1Type) +{ + Error error = kErrorNone; + const mbedtls_asn1_named_data *data; + size_t length; + mbedtls_x509_crt *peerCert = const_cast(mbedtls_ssl_get_peer_cert(&mSsl)); + + if (aAttributeLength != nullptr) + { + *aAttributeLength = 0; + } + + VerifyOrExit(aAttributeBuffer != nullptr, error = kErrorNoBufs); + VerifyOrExit(peerCert != nullptr, error = kErrorInvalidState); + data = mbedtls_asn1_find_named_data(&peerCert->subject, aOid, aOidLength); + VerifyOrExit(data != nullptr, error = kErrorNotFound); + length = data->val.len; + VerifyOrExit(length <= aAttributeBufferSize, error = kErrorNoBufs); + + if (aAttributeLength != nullptr) + { + *aAttributeLength = length; + } + + if (aAns1Type != nullptr) + { + *aAns1Type = data->val.tag; + } + + memcpy(aAttributeBuffer, data->val.p, length); + +exit: + return error; +} + +Error Dtls::GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&mSsl); + + return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); +} + +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +Error Dtls::GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + const mbedtls_x509_crt *cert = &mOwnCert; + + return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); +} + +Error Dtls::GetThreadAttributeFromCaCertificateChain(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + const mbedtls_x509_crt *cert = &mCaChain; + + return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); +} + +Error Dtls::GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) +{ + Error error = kErrorNotFound; + char oid[9] = {0x2B, 0x06, 0x01, 0x04, 0x01, static_cast(0x82), static_cast(0xDF), + 0x2A, 0x00}; // 1.3.6.1.4.1.44970.0 + mbedtls_x509_buf v3_ext; + unsigned char *p, *end, *end_ext_data; + size_t len; + mbedtls_x509_buf extn_oid; + int ret, is_critical; + + if (aAttributeLength != nullptr) + { + *aAttributeLength = 0; + } + + VerifyOrExit(aCert != nullptr, error = kErrorInvalidState); + v3_ext = aCert->v3_ext; + p = v3_ext.p; + VerifyOrExit(p != nullptr, error = kErrorInvalidState); + end = p + v3_ext.len; + VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0, + error = kErrorParse); + VerifyOrExit(end == p + len, error = kErrorParse); + + VerifyOrExit(aThreadOidDescriptor < 128, error = kErrorNotImplemented); + oid[sizeof(oid) - 1] = static_cast(aThreadOidDescriptor); + + while (p < end) + { + is_critical = 0; + VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0, + error = kErrorParse); + end_ext_data = p + len; + + // Get extension ID + VerifyOrExit(mbedtls_asn1_get_tag(&p, end_ext_data, &extn_oid.len, MBEDTLS_ASN1_OID) == 0, error = kErrorParse); + extn_oid.tag = MBEDTLS_ASN1_OID; + extn_oid.p = p; + p += extn_oid.len; + + // Get optional critical + ret = mbedtls_asn1_get_bool(&p, end_ext_data, &is_critical); + VerifyOrExit(ret == 0 || ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = kErrorParse); + + // Data should be octet string type + VerifyOrExit(mbedtls_asn1_get_tag(&p, end_ext_data, &len, MBEDTLS_ASN1_OCTET_STRING) == 0, error = kErrorParse); + VerifyOrExit(end_ext_data == p + len, error = kErrorParse); + + if (is_critical) + continue; + if (extn_oid.len != sizeof(oid)) + continue; + + if (memcmp(extn_oid.p, oid, sizeof(oid)) == 0) + { + if (aAttributeLength != nullptr) + { + *aAttributeLength = len; + } + + if (aAttributeBuffer != nullptr) + { + VerifyOrExit(len <= aAttributeBufferSize, error = kErrorNoBufs); + memcpy(aAttributeBuffer, p, len); + } + + error = kErrorNone; + break; + } + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_SSL_SRV_C Error Dtls::SetClientId(const uint8_t *aClientId, uint8_t aLength) @@ -606,10 +796,10 @@ int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength) { LogDebg("HandleMbedtlsTransmit"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsTransmit"); + LogDebg("Application CoapSecure or Ble HandleMbedtlsTransmit"); } #endif @@ -650,10 +840,10 @@ int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength) { LogDebg("HandleMbedtlsReceive"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsReceive"); + LogDebg("Application CoapSecure or Ble HandleMbedtlsReceive"); } #endif @@ -682,10 +872,10 @@ int Dtls::HandleMbedtlsGetTimer(void) { LogDebg("HandleMbedtlsGetTimer"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsGetTimer"); + LogDebg("Application CoapSecure or Ble HandleMbedtlsGetTimer"); } #endif @@ -720,10 +910,10 @@ void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish) { LogDebg("SetTimer"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure SetTimer"); + LogDebg("Application CoapSecure or Ble SetTimer"); } #endif diff --git a/src/core/meshcop/dtls.hpp b/src/core/meshcop/dtls.hpp index 1ff30e41d179..726517a8c951 100644 --- a/src/core/meshcop/dtls.hpp +++ b/src/core/meshcop/dtls.hpp @@ -36,12 +36,22 @@ #include "openthread-core-config.h" +#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BLE_SECURE_ENABLE +#define OPENTHREAD_CONFIG_TLS_API_ENABLE 1 +#endif + #include #include #include #include -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE +#ifndef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#error OPENTHREAD_CONFIG_BLE_SECURE_ENABLE requires MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#endif +#endif + +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #include #include @@ -77,7 +87,7 @@ class Dtls : public InstanceLocator * @param[in] aLayerTwoSecurity Specifies whether to use layer two security or not. * */ - explicit Dtls(Instance &aInstance, bool aLayerTwoSecurity); + explicit Dtls(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport = true); /** * Pointer is called when a connection is established or torn down. @@ -210,7 +220,7 @@ class Dtls : public InstanceLocator */ Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED /** * Sets the Pre-Shared Key (PSK) for DTLS sessions- @@ -278,6 +288,106 @@ class Dtls : public InstanceLocator Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize); #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /** + * Returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ANS1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * @param[out] aAns1Type A pointer to the ANS1 type of the attribute written to the buffer. + * + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * + */ + Error GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize, + int *aAns1Type); + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the CA x509 certificate chain, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromCaCertificateChain(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); + /** * Set the authentication mode for a dtls connection. * @@ -288,7 +398,7 @@ class Dtls : public InstanceLocator * */ void SetSslAuthMode(bool aVerifyPeerCertificate) { mVerifyPeerCertificate = aVerifyPeerCertificate; } -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_SSL_SRV_C /** @@ -355,7 +465,7 @@ class Dtls : public InstanceLocator static constexpr uint32_t kGuardTimeNewConnectionMilli = 2000; -#if !OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if !OPENTHREAD_CONFIG_TLS_API_ENABLE static constexpr uint16_t kApplicationDataMaxLength = 1152; #else static constexpr uint16_t kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH; @@ -367,14 +477,20 @@ class Dtls : public InstanceLocator void FreeMbedtls(void); Error Setup(bool aClient); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE /** * Set keys and/or certificates for dtls session dependent of used cipher suite. * * @retval mbedtls error, 0 if successfully. * */ - int SetApplicationCoapSecureKeys(void); + int SetApplicationSecureKeys(void); + + Error GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert, + int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize); #endif static void HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr); @@ -457,7 +573,7 @@ class Dtls : public InstanceLocator #endif #endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED const uint8_t *mCaChainSrc; uint32_t mCaChainLength; @@ -492,16 +608,19 @@ class Dtls : public InstanceLocator bool mTimerSet : 1; bool mLayerTwoSecurity : 1; + bool mDatagramTransport : 1; Message *mReceiveMessage; Callback mConnectedCallback; Callback mReceiveCallback; + void *mContext; Ip6::MessageInfo mMessageInfo; Ip6::Udp::Socket mSocket; Callback mTransportCallback; + void *mTransportContext; Message::SubType mMessageSubType; Message::SubType mMessageDefaultSubType; diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp new file mode 100644 index 000000000000..2a4e3dafa876 --- /dev/null +++ b/src/core/meshcop/tcat_agent.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the TCAT Agent service. + */ + +#include "tcat_agent.hpp" + +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#include + +#include "common/array.hpp" +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/encoding.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" +#include "common/string.hpp" +#include "meshcop/meshcop.hpp" +#include "radio/radio.hpp" +#include "thread/thread_netif.hpp" +#include "thread/uri_paths.hpp" +#include "utils/otns.hpp" + +namespace ot { +namespace MeshCoP { + +RegisterLogModule("TcatAgent"); + +bool TcatAgent::VendorInfo::IsValid(void) const +{ + if (mProvisioningUrl != nullptr && !IsValidUtf8String(mProvisioningUrl)) + return false; + + return true; +} + +TcatAgent::TcatAgent(Instance &aInstance) + : InstanceLocator(aInstance) + , mVendorInfo(nullptr) + , mJoinCallback(nullptr) + , mEnabled(false) + , mElevated(false) +{ + mElevationPsk.m8[0] = 0; +} + +Error TcatAgent::Start(const char *aElevationPsk, TcatAgent::VendorInfo *aVendorInfo, JoinCallback aHandler) +{ + Error error = kErrorNone; + + LogInfo("TCAT agent starting"); + + VerifyOrExit(aElevationPsk != nullptr, error = kErrorInvalidArgs); + VerifyOrExit(strlen(aElevationPsk) <= OT_TCAT_ELEVATION_PSK_LENGTH, error = kErrorInvalidArgs); + + if (aVendorInfo != nullptr) + { + VerifyOrExit(aVendorInfo->IsValid(), error = kErrorInvalidArgs); + } + + strcpy(mElevationPsk.m8, aElevationPsk); + mVendorInfo = aVendorInfo; + mJoinCallback = aHandler; + mEnabled = true; + mElevated = false; + +exit: + LogError("start TCAT agent", error); + return error; +} + +void TcatAgent::Stop(void) +{ + mEnabled = false; + mElevated = false; + mJoinCallback = nullptr; + LogInfo("TCAT agent stopped"); +} + +Error TcatAgent::HandleSingleTlv(ot::Message &aIncommingMessage, + ot::Message &aOutgoingMessage, + MeshCoP::Dtls &aTlsContext) +{ + Error error = kErrorNone; + ot::Tlv tlv; + uint16_t offset = aIncommingMessage.GetOffset(); + CommandType command; + + SuccessOrExit(error = aIncommingMessage.Read(offset, tlv)); + VerifyOrExit(tlv.GetType() != kApplication, error = kErrorNotTmf); + VerifyOrExit(tlv.IsExtended() == false, error = kErrorParse); + offset += 2; + + switch (tlv.GetType()) + { + case TlvType::kCommand: + SuccessOrExit(error = aIncommingMessage.Read(offset, &command, sizeof(command))); + error = HandleCommand(command, aOutgoingMessage, aTlsContext); + break; + + case TlvType::kActiveDataset: + error = HandleActiveDataset(aIncommingMessage, offset, tlv.GetLength(), aTlsContext); + break; + + default: + error = kErrorParse; + } + +exit: + return error; +} + +Error TcatAgent::HandleCommand(CommandType aCommand, ot::Message &aOutgoingMessage, MeshCoP::Dtls &aTlsContext) +{ + OT_UNUSED_VARIABLE(aTlsContext); + + Error error = kErrorNone; + ot::Tlv tlv; + ResponseType response = ResponseType::kSuccess; + + tlv.SetType(TlvType::kResponse); + tlv.SetLength(sizeof(response)); + + switch (aCommand) + { + case CommandType::kTerminate: + Stop(); + break; + + case CommandType::kThreadStart: + +#if OPENTHREAD_CONFIG_LINK_RAW_ENABLE + if (Get().IsEnabled()) + response = ResponseType::kInvalidState; +#endif + if (response == ResponseType::kSuccess) + { + Get().Up(); + if (Get().Start() != kErrorNone) + response = ResponseType::kInvalidState; + } + + break; + + case CommandType::kThreadStop: + Get().Stop(); + break; + + default: + response = ResponseType::kParseError; + } + + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + SuccessOrExit(error = aOutgoingMessage.Append(response)); + +exit: + return error; +} + +Error TcatAgent::HandleActiveDataset(ot::Message &aIncommingMessage, + uint16_t aOffset, + uint16_t aLength, + MeshCoP::Dtls &aTlsContext) +{ + OT_UNUSED_VARIABLE(aTlsContext); + + Dataset dataset; + otOperationalDatasetTlvs datasetTlvs; + Error error = kErrorNone; + + SuccessOrExit(error = dataset.ReadFromMessage(aIncommingMessage, aOffset, aLength)); + dataset.ConvertTo(datasetTlvs); + Get().Save(datasetTlvs); + +exit: + return error; +} + +} // namespace MeshCoP +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp new file mode 100644 index 000000000000..b415c5619d36 --- /dev/null +++ b/src/core/meshcop/tcat_agent.hpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the TCAT Agent service. + */ + +#ifndef TCAT_AGENT_HPP_ +#define TCAT_AGENT_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#include + +#include "common/as_core_type.hpp" +#include "common/locator.hpp" +#include "common/log.hpp" +#include "common/message.hpp" +#include "common/non_copyable.hpp" +#include "mac/mac_types.hpp" +#include "meshcop/dtls.hpp" +#include "meshcop/meshcop.hpp" +#include "meshcop/meshcop_tlvs.hpp" + +namespace ot { + +namespace MeshCoP { + +class TcatAgent : public InstanceLocator, private NonCopyable +{ +public: + /** + * This function pointer is called to notify the completion of a join operation. + * + * Please see otHandleTcatJoin for details. + * + */ + typedef otHandleTcatJoin JoinCallback; + + /** + * This structure represents a TCAT elevation PSK. + * + * Please see otTcatElevationPsk for details. + * + */ + typedef otTcatElevationPsk ElevationPsk; + + /** + * This structure represents the TCAT vendor information. + * + */ + class VendorInfo : public otTcatVendorInfo + { + public: + /** + * This method validates whether the TCAT vendor information is valid. + * + * @returns Whether the parameters are valid. + * + */ + bool IsValid(void) const; + }; + + /** + * TCAT TLV Types. + * + */ + enum TlvType : uint8_t + { + kCommand = OT_TCAT_TLV_COMMAND, ///< Command TLV + kResponse = OT_TCAT_TLV_RESPONSE, ///< Response TLV + kActiveDataset = OT_TCAT_TLV_ACTIVE_DATASET, ///< Active Dataset TLV + kApplication = OT_TCAT_TLV_APPLICATION, ///< Application TLV + }; + + /** + * TCAT Command Types. + * + */ + enum CommandType : uint8_t + { + kTerminate = OT_TCAT_COMMAND_TERMINATE, ///< Terminate connection + kThreadStart = OT_TCAT_COMMAND_THREAD_START, ///< Start Thread Interface + kThreadStop = OT_TCAT_COMMAND_THREAD_STOP, ///< Stop Thread Interface + }; + + /** + * TCAT Response Types. + * + */ + enum ResponseType : uint8_t + { + kSuccess = OT_TCAT_RESPONSE_SUCCESS, ///< Success + kInvalidState = OT_TCAT_RESPONSE_INVALID_STATE, ///< Invalid State + kParseError = OT_TCAT_RESPONSE_PARSE_ERROR, ///< Invalid State + }; + + /** + * This constructor initializes the Joiner object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit TcatAgent(Instance &aInstance); + + /** + * Enables the TCAT protocol. + * + * @retval kErrorNone Successfully started the TCAT agent. + * @retval kErrorInvalidArgs The aElevationPsk or the aVendorInfo is invalid. + * + */ + Error Start(const char *aElevationPsk, VendorInfo *aVendorInfo, JoinCallback aHandler); + + /** + * This method stops the TCAT protocol. + * + */ + void Stop(void); + + /** + * This method indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ + bool IsEnabled(void) const { return mEnabled; } + + /** + * This method indicates whether or not the TCAT session has elevated rights. + * + * @retval TRUE The TCAT session has elevated rights. + * @retval FALSE The TCAT session does not have elevated rights. + * + */ + bool IsElevated(void) const { return mElevated; } + + /** + * This method processes an incoming TCAT TLV. + * + * @retval kErrorNone Successfully processed. + * @retval kErrorInvalidArgs The invalid argument value. + * @retval kErrorParse The incoming meassge could not be parsed. + * @retval kErrorNotTmf The incoming message was an application TLV. + * + */ + Error HandleSingleTlv(ot::Message &aIncommingMessage, ot::Message &aOutgoingMessage, MeshCoP::Dtls &aTlsContext); + +private: + Error HandleCommand(CommandType aCommand, ot::Message &aOutgoingMessage, MeshCoP::Dtls &aTlsContext); + Error HandleActiveDataset(ot::Message &aIncommingMessage, + uint16_t aOffset, + uint16_t aLength, + MeshCoP::Dtls &aTlsContext); + + static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + + ElevationPsk mElevationPsk; + VendorInfo *mVendorInfo; + JoinCallback mJoinCallback; + bool mEnabled; + bool mElevated; +}; + +} // namespace MeshCoP +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#endif // TCAT_AGENT_HPP_ diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp new file mode 100644 index 000000000000..d130f2014c0f --- /dev/null +++ b/src/core/radio/ble_secure.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2016, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ble_secure.hpp" + +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#include +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" +#include "common/tlvs.hpp" +#include "meshcop/dtls.hpp" + +/** + * @file + * This file implements the secure Ble agent. + */ + +namespace ot { +namespace Ble { + +RegisterLogModule("BleSecure"); + +BleSecure::BleSecure(Instance &aInstance) + : InstanceLocator(aInstance) + , mTls(aInstance, false, false) + , mTcatAgent(aInstance) + , mConnectCallback(nullptr) + , mReceiveCallback(nullptr) + , mContext(nullptr) + , mTlvMode(0) + , mReceivedMessage(nullptr) + , mSentMessage(nullptr) + , mTransmitTask(aInstance, BleSecure::HandleTransmit, this) + , mBleConnectionOpen(false) + , mMtuSize(kInitialMtuSize) +{ +} + +Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext) +{ + Error error = kErrorNone; + + mConnectCallback = aConnectHandler; + mReceiveCallback = aReceiveHandler; + mTlvMode = aTlvMode; + mContext = aContext; + + mMtuSize = kInitialMtuSize; + + SuccessOrExit(error = otPlatBleEnable(&GetInstance())); + SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), 0, 0)); + SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this)); + SuccessOrExit(error = mTls.Bind(HandleTransport, this)); + +exit: + return error; +} + +Error BleSecure::TcatStart(const char *aElevationPsk, + MeshCoP::TcatAgent::VendorInfo *aVendorInfo, + MeshCoP::TcatAgent::JoinCallback aHandler) +{ + return mTcatAgent.Start(aElevationPsk, aVendorInfo, aHandler); +} + +void BleSecure::Stop(void) +{ + otPlatBleGapAdvStop(&GetInstance()); + otPlatBleDisable(&GetInstance()); + mBleConnectionOpen = false; + mMtuSize = kInitialMtuSize; + + if (mTcatAgent.IsEnabled()) + mTcatAgent.Stop(); + mTls.Close(); + + mTransmitQueue.DequeueAndFreeAll(); + + mConnectCallback = nullptr; + mReceiveCallback = nullptr; + mContext = nullptr; + + FreeMessage(mReceivedMessage); + mReceivedMessage = nullptr; + FreeMessage(mSentMessage); + mSentMessage = nullptr; +} + +Error BleSecure::Connect() +{ + Ip6::SockAddr sockaddr; + + return mTls.Connect(sockaddr); +} + +void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd) +{ + static_assert(static_cast(MeshCoP::JoinerPskd::kMaxLength) <= + static_cast(MeshCoP::Dtls::kPskMaxLength), + "The maximum length of TLS PSK is smaller than joiner PSKd"); + + SuccessOrAssert(mTls.SetPsk(reinterpret_cast(aPskd.GetAsCString()), aPskd.GetLength())); +} + +Error BleSecure::SendMessage(ot::Message *aMessage) +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + VerifyOrExit(mSentMessage != nullptr, error = kErrorNoBufs); + VerifyOrExit(aMessage != nullptr, error = kErrorInvalidArgs); + SuccessOrExit(error = mSentMessage->AppendBytesFromMessage(*aMessage, 0, aMessage->GetLength())); + + if (mSentMessage->GetLength() > kFlushThreshold) + { + error = Flush(); + } + + if (error == kErrorNone) + FreeMessage(aMessage); + +exit: + return error; +} + +Error BleSecure::Send(uint8_t *aBuf, uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + VerifyOrExit(mSentMessage != nullptr, error = kErrorNoBufs); + SuccessOrExit(error = mSentMessage->AppendBytes(aBuf, aLength)); + + if (mSentMessage->GetLength() > kFlushThreshold) + { + error = Flush(); + } + +exit: + return error; +} + +Error BleSecure::SendApplicationTlv(uint8_t *aBuf, uint16_t aLength) +{ + if (aLength > 254) + { + ot::ExtendedTlv tlv; + + tlv.SetType(ot::MeshCoP::TcatAgent::TlvType::kApplication); + tlv.SetLength(aLength); + Send(reinterpret_cast(&tlv), sizeof(tlv)); + } + else + { + ot::Tlv tlv; + + tlv.SetType(ot::MeshCoP::TcatAgent::TlvType::kApplication); + tlv.SetLength(aLength); + Send(reinterpret_cast(&tlv), sizeof(tlv)); + } + + return Send(aBuf, aLength); +} + +Error BleSecure::Flush() +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + VerifyOrExit(mSentMessage != nullptr, error = kErrorNoBufs); + VerifyOrExit(mSentMessage->GetLength() != 0, error = kErrorNone); + + mTransmitQueue.Enqueue(*mSentMessage); + mTransmitTask.Post(); + + mSentMessage = Get().Allocate(Message::kTypeBle); + VerifyOrExit(mSentMessage != nullptr, error = kErrorNoBufs); + +exit: + return error; +} + +Error BleSecure::HandleBleReceive(uint8_t *aBuf, uint16_t aLength) +{ + ot::Message *message = nullptr; + Ip6::MessageInfo theMessageInfo; + + VerifyOrExit((message = Get().Allocate(Message::kTypeBle, 0)) != nullptr); + SuccessOrExit(message->AppendBytes(aBuf, aLength)); + + // Cannot call Receive(..) directly because Setup(..) and mState are private + mTls.HandleUdpReceive(*message, theMessageInfo); + // mTls.Receive(*message); + FreeMessage(message); + + return kErrorNone; + +exit: + FreeMessage(message); + return kErrorNoBufs; +} + +Error BleSecure::HandleBleConnected(uint16_t aConnectionId) +{ + OT_UNUSED_VARIABLE(aConnectionId); + + mBleConnectionOpen = true; + + otPlatBleGattMtuGet(&GetInstance(), &mMtuSize); + + if (mConnectCallback != nullptr) + { + mConnectCallback(IsConnected(), mBleConnectionOpen, mContext); + } + + return kErrorNone; +} + +Error BleSecure::HandleBleDisconnected(uint16_t aConnectionId) +{ + OT_UNUSED_VARIABLE(aConnectionId); + + mBleConnectionOpen = false; + mMtuSize = kInitialMtuSize; + + if (!IsConnected() && mConnectCallback != nullptr) + { + mConnectCallback(false, mBleConnectionOpen, mContext); + } + + Disconnect(); // Stop TLS connection attempt from client if still running + + return kErrorNone; +} + +void BleSecure::HandleTlsConnected(void *aContext, bool aConnected) +{ + return static_cast(aContext)->HandleTlsConnected(aConnected); +} + +void BleSecure::HandleTlsConnected(bool aConnected) +{ + if (aConnected) + { + if (mReceivedMessage == nullptr) + { + mReceivedMessage = Get().Allocate(Message::kTypeBle); + } + + if (mSentMessage == nullptr) + { + mSentMessage = Get().Allocate(Message::kTypeBle); + } + } + else + { + FreeMessage(mReceivedMessage); + mReceivedMessage = nullptr; + FreeMessage(mSentMessage); + mSentMessage = nullptr; + } + + if (mConnectCallback != nullptr) + { + mConnectCallback(aConnected, mBleConnectionOpen, mContext); + } +} + +void BleSecure::HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength) +{ + return static_cast(aContext)->HandleTlsReceive(aBuf, aLength); +} + +void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength) +{ + VerifyOrExit(mReceivedMessage != nullptr); + + if (mTlvMode) + { + ot::Tlv tlv; + uint32_t requiredBytes = sizeof(Tlv); + uint32_t offset; + + while (aLength > 0) + { + if (mReceivedMessage->GetLength() < requiredBytes) + { + uint32_t missingBytes = requiredBytes - mReceivedMessage->GetLength(); + + if (missingBytes > aLength) + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength)); + break; + } + else + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, missingBytes)); + aLength -= missingBytes; + aBuf += missingBytes; + } + } + + mReceivedMessage->Read(0, tlv); + + if (tlv.IsExtended()) + { + ot::ExtendedTlv extTlv; + requiredBytes = sizeof(extTlv); + + if (mReceivedMessage->GetLength() < requiredBytes) + continue; + + mReceivedMessage->Read(0, extTlv); + requiredBytes = extTlv.GetSize(); + offset = sizeof(extTlv); + } + else + { + requiredBytes = tlv.GetSize(); + offset = sizeof(tlv); + } + + if (mReceivedMessage->GetLength() < requiredBytes) + continue; + + // TLV fully loaded + + if (mTcatAgent.IsEnabled() && mSentMessage != nullptr) + { + if (mSentMessage != nullptr) + { + Error error = mTcatAgent.HandleSingleTlv(*mReceivedMessage, *mSentMessage, mTls); + Flush(); + + if (error == kErrorNotTmf && mReceiveCallback != nullptr) + { + mReceivedMessage->SetOffset(offset); + mReceiveCallback(mReceivedMessage, mContext); + } + } + } + else if (mReceiveCallback != nullptr) + { + mReceivedMessage->SetOffset(offset); + mReceiveCallback(mReceivedMessage, mContext); + } + + SuccessOrExit(mReceivedMessage->SetLength(0)); // also sets the offset to 0 + requiredBytes = sizeof(Tlv); + } + } + else + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength)); + + if (mReceiveCallback != nullptr) + { + mReceiveCallback(mReceivedMessage, mContext); + } + + mReceivedMessage->SetLength(0); + } + +exit:; +} + +void BleSecure::HandleTransmit(Tasklet &aTasklet) +{ + static_cast(static_cast(aTasklet).GetContext())->HandleTransmit(); +} + +void BleSecure::HandleTransmit(void) +{ + Error error = kErrorNone; + ot::Message *message = mTransmitQueue.GetHead(); + + VerifyOrExit(message != nullptr); + mTransmitQueue.Dequeue(*message); + + if (mTransmitQueue.GetHead() != nullptr) + { + mTransmitTask.Post(); + } + + SuccessOrExit(error = mTls.Send(*message, message->GetLength())); + +exit: + if (error != kErrorNone) + { + LogNote("Transmit: %s", ErrorToString(error)); + message->Free(); + } + else + { + LogDebg("Transmit: %s", ErrorToString(error)); + } +} + +Error BleSecure::HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + return static_cast(aContext)->HandleTransport(aMessage); +} + +Error BleSecure::HandleTransport(ot::Message &aMessage) +{ + otBleRadioPacket packet; + uint16_t len = aMessage.GetLength(); + uint16_t offset = 0; + otError error = OT_ERROR_NONE; + + while (len > 0) + { + if (len <= mMtuSize - kGattOverhead) + packet.mLength = len; + else + packet.mLength = mMtuSize - kGattOverhead; + + if (packet.mLength > kPacketBufferSize) + packet.mLength = kPacketBufferSize; + + aMessage.Read(offset, mPacketBuffer, packet.mLength); + packet.mValue = mPacketBuffer; + packet.mPower = 0; + + error = otPlatBleGattServerIndicate(&GetInstance(), kTxBleHandle, &packet); + if (error != OT_ERROR_NONE) + break; + + len -= packet.mLength; + offset += packet.mLength; + } + + // if(error != OT_ERROR_NONE) --> close connection? Not free message? + + aMessage.Free(); + return kErrorNone; +} + +} // namespace Ble +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp new file mode 100644 index 000000000000..643a4e562f1b --- /dev/null +++ b/src/core/radio/ble_secure.hpp @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BLE_SECURE_HPP_ +#define BLE_SECURE_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#include + +#include "meshcop/dtls.hpp" +#include "meshcop/meshcop.hpp" +#include "meshcop/tcat_agent.hpp" + +//#include + +/** + * @file + * This file includes definitions for the secure BLE agent. + */ + +namespace ot { + +namespace Ble { + +class BleSecure : public InstanceLocator, private NonCopyable +{ +public: + /** + * This function pointer is called when the secure BLE connection state changes. + * + * Please see otHandleBleSecureConnect for details. + * + */ + typedef otHandleBleSecureConnect ConnectCallback; + + /** + * This function pointer is called when data was received over the TLS connection. + * If line mode is activated the function is called only after EOL has been received. + * + * Please see otHandleBleSecureReceive for details. + * + */ + typedef otHandleBleSecureReceive ReceiveCallback; + + /** + * This constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit BleSecure(Instance &aInstance); + + /** + * This method starts the secure BLE agent. + * + * @retval kErrorNone Successfully started the BLE agent. + * @retval kErrorAlready Already started. + * + */ + Error Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext); + + /** + * Enables the TCAT protocol over BLE Secure. + * + * @retval kErrorNone Successfully started the BLE Secure Joiner role. + * @retval kErrorInvalidArgs The aElevationPsk or the aVendorInfo is invalid. + * @retval kErrorInvaidState The BLE function has not been started or line mode is not selected. + * + */ + Error TcatStart(const char *aElevationPsk, + MeshCoP::TcatAgent::VendorInfo *aVendorInfo, + MeshCoP::TcatAgent::JoinCallback aHandler); + + /** + * This method stops the secure BLE agent. + * + */ + void Stop(void); + + /** + * This method initializes TLS session with a peer using an already open BLE connection. + * + * @retval kErrorNone Successfully started TLS connection. + * + */ + Error Connect(); + + /** + * This method indicates whether or not the TLS session is active. + * + * @retval TRUE If TLS session is active. + * @retval FALSE If TLS session is not active. + * + */ + bool IsConnectionActive(void) const { return mTls.IsConnectionActive(); } + + /** + * This method indicates whether or not the TLS session is connected. + * + * @retval TRUE The TLS session is connected. + * @retval FALSE The TLS session is not connected. + * + */ + bool IsConnected(void) const { return mTls.IsConnected(); } + + /** + * This method indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ + bool IsTcatEnabled(void) const { return mTcatAgent.IsEnabled(); } + + /** + * This method indicates whether or not the BBTC connection has elevated TCAT rights. + * + * @retval TRUE The BBTC connection has elevated rights. + * @retval FALSE The BBTC connection does not have elevated rights. + * + */ + bool IsTcatElevated(void) const { return mTcatAgent.IsElevated(); } + + /** + * This method stops the TLS connection. + * + */ + void Disconnect(void) { mTls.Disconnect(); } + + /** + * This method sets the PSK. + * + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK length. + * + * @retval kErrorNone Successfully set the PSK. + * @retval kErrorInvalidArgs The PSK is invalid. + * + */ + Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { return mTls.SetPsk(aPsk, aPskLength); } + + /** + * This method sets the PSK. + * + * @param[in] aPskd A Joiner PSKd. + * + */ + void SetPsk(const MeshCoP::JoinerPskd &aPskd); + +#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + /** + * This method sets the Pre-Shared Key (PSK) for TLS sessions identified by a PSK. + * + * TLS mode "TLS with AES 128 CCM 8" for secure BLE. + * + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK char length. + * @param[in] aPskIdentity The Identity Name for the PSK. + * @param[in] aPskIdLength The PSK Identity Length. + * + */ + void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength) + { + mTls.SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); + } +#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + /** + * This method sets a X509 certificate with corresponding private key for TLS session. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[in] aX509Cert A pointer to the PEM formatted X509 PEM certificate. + * @param[in] aX509Length The length of certificate. + * @param[in] aPrivateKey A pointer to the PEM formatted private key. + * @param[in] aPrivateKeyLength The length of the private key. + * + */ + void SetCertificate(const uint8_t *aX509Cert, + uint32_t aX509Length, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength) + { + mTls.SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength); + } + + /** + * This method sets the trusted top level CAs. It is needed for validate the certificate of the peer. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[in] aX509CaCertificateChain A pointer to the PEM formatted X509 CA chain. + * @param[in] aX509CaCertChainLength The length of chain. + * + */ + void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) + { + mTls.SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength); + } +#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /** + * This method returns the peer x509 certificate base64 encoded. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[out] aPeerCert A pointer to the base64 encoded certificate buffer. + * @param[out] aCertLength The length of the base64 encoded peer certificate. + * @param[in] aCertBufferSize The buffer size of aPeerCert. + * + * @retval kErrorNone Successfully get the peer certificate. + * @retval kErrorNoBufs Can't allocate memory for certificate. + * + */ + Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize) + { + return mTls.GetPeerCertificateBase64(aPeerCert, aCertLength, aCertBufferSize); + } +#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /** + * This method returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ANS1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * @param[out] aAns1Type A pointer to the ANS1 type of the attribute written to the buffer. + * + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * + */ + Error GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize, + int *aAns1Type) + { + return mTls.GetPeerSubjectAttributeByOid(aOid, aOidLength, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize, aAns1Type); + } + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) + { + return mTls.GetThreadAttributeFromPeerCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); + } +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) + { + return mTls.GetThreadAttributeFromOwnCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); + } + + /** + * This method returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the CA x509 certificate chain, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * This method requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[out] aAttributeLength A pointer to the length of the attribute written to the buffer. + * @param[in] aAttributeBufferSize The buffer size of aAttributeBuffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromCaCertificateChain(int aThreadOidDescriptor, + unsigned char *aAttributeBuffer, + size_t *aAttributeLength, + size_t aAttributeBufferSize) + { + return mTls.GetThreadAttributeFromCaCertificateChain(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength, + aAttributeBufferSize); + } + + /** + * This method sets the authentication mode for the BLE secure connection. It disables or enables the verification + * of peer certificate. + * + * @param[in] aVerifyPeerCertificate true, if the peer certificate should be verified + * + */ + void SetSslAuthMode(bool aVerifyPeerCertificate) { mTls.SetSslAuthMode(aVerifyPeerCertificate); } + + /** + * This method sends a secure BLE message. + * + * @param[in] aMessage A pointer to the message to send. + * + * If the return value is kErrorNone, OpenThread takes ownership of @p aMessage, and the caller should no longer + * reference @p aMessage. If the return value is not kErrorNone, the caller retains ownership of @p aMessage, + * including freeing @p aMessage if the message buffer is no longer needed. + * + * @retval kErrorNone Successfully sent message. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error SendMessage(Message *aMessage); + + /** + * This method sends a secure BLE data packet. + * + * @param[in] aBuf A pointer to the data to send. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval kErrorNone Successfully sent data. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error Send(uint8_t *aBuf, uint16_t aLength); + + /** + * This method sends a secure BLE data packet containing a TCAT application TLV. + * + * @param[in] aBuf A pointer to the data to send. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval kErrorNone Successfully sent data. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error SendApplicationTlv(uint8_t *aBuf, uint16_t aLength); + + /** + * This method sends all remaining bytes in the send buffer. + * + * @retval kErrorNone Successfully enqueued data into the output interface. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error Flush(); + + /** + * This method is used to pass data received over a BLE link to the secure BLE server. + * + * @param[in] aBuf A pointer to the data received. + * @param[in] aLength A number indicating the length of the data buffer. + * + */ + Error HandleBleReceive(uint8_t *aBuf, uint16_t aLength); + + /** + * This method is used to notify the secure BLE server that a BLE Device has been connected. + * + * @param[in] aConnectionId The identifier of the open connection. + * + */ + Error HandleBleConnected(uint16_t aConnectionId); + + /** + * This method is used to notify the secure BLE server that the BLE Device has been disconnected. + * + * @param[in] aConnectionId The identifier of the open connection. + * + */ + Error HandleBleDisconnected(uint16_t aConnectionId); + +private: + static constexpr uint8_t kInitialMtuSize = 23; // ATT_MTU + static constexpr uint8_t kGattOverhead = 3; // BLE GATT payload fits MTU size - 3 bytes + static constexpr uint8_t kPacketBufferSize = 64; // must be >= kInitialMtuSize - kGattOverhead + static constexpr uint16_t kTxBleHandle = 0; // Characteristics Handle for TX (not used) + static constexpr uint16_t kFlushThreshold = + 256; // mSentMessage length exceeding this threshold triggers flushing the buffer + + static void HandleTlsConnected(void *aContext, bool aConnected); + void HandleTlsConnected(bool aConnected); + + static void HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength); + void HandleTlsReceive(uint8_t *aBuf, uint16_t aLength); + + static void HandleTransmit(Tasklet &aTasklet); + void HandleTransmit(void); + + static Error HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Error HandleTransport(ot::Message &aMessage); + + MeshCoP::Dtls mTls; + MeshCoP::TcatAgent mTcatAgent; + ConnectCallback mConnectCallback; + ReceiveCallback mReceiveCallback; + void *mContext; + bool mTlvMode; + ot::Message *mReceivedMessage; + ot::Message *mSentMessage; + ot::MessageQueue mTransmitQueue; + TaskletContext mTransmitTask; + uint8_t mPacketBuffer[kPacketBufferSize]; + bool mBleConnectionOpen; + uint16_t mMtuSize; +}; + +} // namespace Ble +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + +#endif // BLE_SECURE_HPP_ diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 02a30dc08219..e555f175ab8d 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -103,6 +103,7 @@ endif() add_library(openthread-posix alarm.cpp + ble.cpp backbone.cpp backtrace.cpp config_file.cpp diff --git a/src/posix/platform/ble.cpp b/src/posix/platform/ble.cpp new file mode 100644 index 000000000000..5a6c372c12a4 --- /dev/null +++ b/src/posix/platform/ble.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval, uint8_t aType) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + OT_UNUSED_VARIABLE(aType); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 5487643140e5..753f2d3e182f 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -35,6 +35,9 @@ #include #include +#ifdef OPENTHREAD_CONFIG_BLE_SECURE_ENABLE +#include +#endif enum { @@ -623,4 +626,53 @@ void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery } #endif +#ifdef OPENTHREAD_CONFIG_BLE_SECURE_ENABLE +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval, uint8_t aType) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + OT_UNUSED_VARIABLE(aType); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} +#endif // OPENTHREAD_CONFIG_BLE_SECURE_ENABLE + } // extern "C"