diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c index b757a5eba..2264ea366 100644 --- a/examples/platforms/simulation/ble.c +++ b/examples/platforms/simulation/ble.c @@ -244,6 +244,14 @@ otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementDat return OT_ERROR_NONE; } +otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + bool otPlatBleSupportsMultiRadio(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h index fb96d4a8c..fe2c056e2 100644 --- a/include/openthread/ble_secure.h +++ b/include/openthread/ble_secure.h @@ -85,6 +85,10 @@ typedef void (*otHandleBleSecureConnect)(otInstance *aInstance, /** * Pointer to call when data was received over a BLE Secure TLS connection. + * + * When TCAT has been started, the TCAT agent automatically responds with status OT_TCAT_STATUS_UNSUPPORTED + * if no response has been generated or no handler is defined. The application may generate a response to + * incoming TCAT application data or vendor-specific data by calling `otBleSecureSendApplicationTlv`. */ typedef otHandleTcatApplicationDataReceive otHandleBleSecureReceive; @@ -409,17 +413,30 @@ otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage); otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); /** - * Sends a secure BLE data packet containing a TCAT Send Application Data TLV. + * Sends a secure BLE data packet containing application data directed to the application layer @p aApplicationProtocol + * or a response to the latest received application data packet. * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. - * @param[in] aLength A number indicating the length of the data buffer. + * Only a single response can be sent while executing the `otHandleBleSecureReceive` handler. If no (further) response + * is expected `OT_ERROR_REJECTED` is returned. * - * @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. + * For responses with a payload @p aApplicationProtocol shall be set to `OT_TCAT_APPLICATION_PROTOCOL_PAYLOAD`. + * For responses with a status @p aApplicationProtocol shall be `OT_TCAT_APPLICATION_PROTOCOL_STATUS` and @ aBuf shall + * contain a single byte `otTcatStatusCode` value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aApplicationProtocol An application protocol the data is directed to. + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. + * @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. + * @retval OT_ERROR_REJECTED Application protocol is response with data or status but no response is pending. */ -otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); +otError otBleSecureSendApplicationTlv(otInstance *aInstance, + otTcatApplicationProtocol aApplicationProtocol, + uint8_t *aBuf, + uint16_t aLength); /** * Flushes the send buffer. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 0c7effe83..bc24a8b70 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (520) +#define OPENTHREAD_API_VERSION (521) /** * @addtogroup api-instance diff --git a/include/openthread/platform/ble.h b/include/openthread/platform/ble.h index c75705a75..85c89a90b 100644 --- a/include/openthread/platform/ble.h +++ b/include/openthread/platform/ble.h @@ -189,6 +189,22 @@ otError otPlatBleGetAdvertisementBuffer(otInstance *aInstance, uint8_t **aAdvert */ otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen); +/** + * Updates BLE Advertising data. + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aAdvertisementData The formatted TCAT advertisement frame. + * @param[in] aAdvertisementLen The TCAT advertisement frame length. + * + * @retval OT_ERROR_NONE Advertising procedure has been started. + * @retval OT_ERROR_FAILED Update of data failed. + * @retval OT_ERROR_INVALID_ARGS Invalid value has been supplied. + * + */ +otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen); + /** * Starts BLE Advertising procedure. * diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h index 80256d7b8..99c5afd46 100644 --- a/include/openthread/tcat.h +++ b/include/openthread/tcat.h @@ -44,6 +44,7 @@ #ifndef OPENTHREAD_TCAT_H_ #define OPENTHREAD_TCAT_H_ +#include #include #include @@ -66,8 +67,9 @@ extern "C" { * @{ */ -#define OT_TCAT_MAX_SERVICE_NAME_LENGTH \ +#define OT_TCAT_SERVICE_NAME_MAX_LENGTH \ 15 ///< Maximum string length of a UDP or TCP service name (does not include null char). +#define OT_TCAT_APPLICATION_LAYER_MAX_COUNT 4 ///< Maximum number of application layer service names supported #define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement. #define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code. @@ -85,9 +87,10 @@ typedef enum otTcatStatusCode OT_TCAT_STATUS_VALUE_ERROR = 3, ///< The value of the transmitted TLV has an error OT_TCAT_STATUS_GENERAL_ERROR = 4, ///< An error not matching any other category occurred OT_TCAT_STATUS_BUSY = 5, ///< Command cannot be executed because the resource is busy - OT_TCAT_STATUS_UNDEFINED = 6, ///< The requested value, data or service is not defined (currently) or not present - OT_TCAT_STATUS_HASH_ERROR = 7, ///< The hash value presented by the commissioner was incorrect - OT_TCAT_STATUS_UNAUTHORIZED = 16, ///< Sender does not have sufficient authorization for the given command + OT_TCAT_STATUS_UNDEFINED = 6, ///< The requested value, data or service is not defined (currently) or not present + OT_TCAT_STATUS_HASH_ERROR = 7, ///< The hash value presented by the commissioner was incorrect + OT_TCAT_STATUS_INVALID_STATE = 8, ///< The TCAT device is not in a correct state for the given command + OT_TCAT_STATUS_UNAUTHORIZED = 16, ///< Sender does not have sufficient authorization for the given command } otTcatStatusCode; @@ -96,9 +99,16 @@ typedef enum otTcatStatusCode */ typedef enum otTcatApplicationProtocol { - OT_TCAT_APPLICATION_PROTOCOL_NONE = 0, ///< Message which has been sent without activating the TCAT agent - OT_TCAT_APPLICATION_PROTOCOL_STATUS = 1, ///< Message directed to a UDP service - OT_TCAT_APPLICATION_PROTOCOL_TCP = 2, ///< Message directed to a TCP service + OT_TCAT_APPLICATION_PROTOCOL_NONE = 0, ///< Message which has been sent without activating the TCAT agent + OT_TCAT_APPLICATION_PROTOCOL_STATUS = 0x01, /** Message directed to any application protocol indicating a + response with status value (one byte otTcatStatusCode) */ + OT_TCAT_APPLICATION_PROTOCOL_RESPONSE = + 0x02, ///< Message directed to any application protocol indicating a response with payload + OT_TCAT_APPLICATION_PROTOCOL_1 = 0x81, ///< Message directed to application protocol 1 + OT_TCAT_APPLICATION_PROTOCOL_2 = 0x82, ///< Message directed to application protocol 2 + OT_TCAT_APPLICATION_PROTOCOL_3 = 0x83, ///< Message directed to application protocol 3 + OT_TCAT_APPLICATION_PROTOCOL_4 = 0x84, ///< Message directed to application protocol 4 + OT_TCAT_APPLICATION_PROTOCOL_VENDOR = 0x9F, ///< Message directed to a vendor specific application protocol } otTcatApplicationProtocol; @@ -151,35 +161,44 @@ typedef struct otTcatGeneralDeviceId */ 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; ///< Vendor software version string - const char *mVendorData; ///< Vendor specific data string - const char *mPskdString; ///< Vendor managed pre-shared key for device - const char *mInstallCode; ///< Vendor managed install code string - const otTcatAdvertisedDeviceId *mAdvertisedDeviceIds; /** Vendor managed advertised device ID array. - Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ - const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array. - (if NULL: device ID is set to EUI-64 in binary format)*/ + const char *mProvisioningUrl; ///< Provisioning URL path string + const char *mVendorName; ///< Vendor name string + const char *mVendorModel; ///< Vendor model string + const char *mVendorSwVersion; ///< Vendor software version string + const char *mVendorData; ///< Vendor specific data string + const char *mPskdString; ///< Vendor managed pre-shared key for device + const char *mInstallCode; ///< Vendor managed install code string + const otTcatAdvertisedDeviceId + *mAdvertisedDeviceIds; /** Vendor managed advertised device ID array. + Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ + const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array. + (if NULL: device ID is set to EUI-64 in binary format) */ + const char *mApplicationServiceName[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]; /** Array with application service names + as C string with maximum length + OT_TCAT_SERVICE_NAME_MAX_LENGTH or + NULL if not supported */ + bool mApplicationServiceIsTcp[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]; /** Array with boolean values indicating + if the service is of TCP type (otherwise + UDP) */ } otTcatVendorInfo; /** - * Pointer to call when application data was received over a TCAT TLS connection. + * Pointer to call when application data or vendor-specific data was received over a TCAT TLS connection. + * The application may generate a response to an incoming TCAT application data packet. The TCAT agent + * automatically responds with status OT_TCAT_STATUS_UNSUPPORTED if no response has been generated or + * no handler is defined. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aMessage A pointer to the message. * @param[in] aOffset The offset where the application data begins. - * @param[in] aTcatApplicationProtocol The protocol type of the message received. - * @param[in] aServiceName The name of the service the message is direced to. + * @param[in] aTcatApplicationProtocol The application protocol the message is targeted to. * @param[in] aContext A pointer to arbitrary context information. */ typedef void (*otHandleTcatApplicationDataReceive)(otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, otTcatApplicationProtocol aTcatApplicationProtocol, - const char *aServiceName, void *aContext); /** diff --git a/src/cli/README_TCAT.md b/src/cli/README_TCAT.md index b96ebd481..dafa74135 100644 --- a/src/cli/README_TCAT.md +++ b/src/cli/README_TCAT.md @@ -63,6 +63,25 @@ tcat advid clear Done ``` +### certid + +Displays the ID of currently selected TCAT device certificate. A TCAT device supports multiple identities for testing purposes. + +```bash +tcat certid +0 +Done +``` + +### certid \ + +Selects the ID of the TCAT device certificate. A TCAT device supports multiple identities for testing purposes. + +```bash +tcat certid 1 +Done +``` + ### devid Displays currently set TCAT device id. diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp index 5a0bfe496..aa3003275 100644 --- a/src/cli/cli_tcat.cpp +++ b/src/cli/cli_tcat.cpp @@ -32,7 +32,9 @@ #include "cli/cli_tcat.hpp" #include "common/code_utils.hpp" +#include "common/debug.hpp" #include "common/error.hpp" +#include "common/string.hpp" #include @@ -43,31 +45,60 @@ #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE +#define CERT_SET_COUNT 2 +#define CERT_MAX_SIZE 1024 +#define KEY_MAX_SIZE 512 + // DeviceCert1 default identity for TCAT certification testing. +// DeviceCert2 extra example. // WARNING: storage of private keys in code or program memory MUST NOT be used in production. // The below code is for testing purposes only. For production, secure key storage must be // used to store private keys. -#define OT_CLI_TCAT_X509_CERT \ - "-----BEGIN CERTIFICATE-----\n" \ - "MIIB6TCCAZCgAwIBAgICNekwCgYIKoZIzj0EAwIwcTEmMCQGA1UEAwwdVGhyZWFk\n" \ - "IENlcnRpZmljYXRpb24gRGV2aWNlQ0ExGTAXBgNVBAoMEFRocmVhZCBHcm91cCBJ\n" \ - "bmMxEjAQBgNVBAcMCVNhbiBSYW1vbjELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVT\n" \ - "MCAXDTI0MDUwNzA5Mzk0NVoYDzI5OTkxMjMxMDkzOTQ1WjA8MSEwHwYDVQQDDBhU\n" \ - "Q0FUIEV4YW1wbGUgRGV2aWNlQ2VydDExFzAVBgNVBAUTDjQ3MjMtOTgzMy0wMDAx\n" \ - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE11h/4vKZXVXv+1GDZo066spItloT\n" \ - "dpCi0bux0jvpQSHLdQBIc+40zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdqNLMEkw\n" \ - "HwYDVR0jBBgwFoAUX6sbKWiIodS0MaiGYefnZlnt+BkwEAYJKwYBBAGC3yoCBAMC\n" \ - "AQUwFAYJKwYBBAGC3yoDBAcEBSABAQEBMAoGCCqGSM49BAMCA0cAMEQCIHWu+Rd1\n" \ - "VRlzrD8KbuyJcJFTXh2sQ9UIrFIA7+4e/GVcAiAVBdGqTxbt3TGkBBllpafAUB2/\n" \ - "s0GJj7E33oblqy5eHQ==\n" \ - "-----END CERTIFICATE-----\n" +static const char *const OT_CLI_TCAT_X509_CERT[CERT_SET_COUNT] = {R"( +-----BEGIN CERTIFICATE----- +MIIB6TCCAZCgAwIBAgICNekwCgYIKoZIzj0EAwIwcTEmMCQGA1UEAwwdVGhyZWFk +IENlcnRpZmljYXRpb24gRGV2aWNlQ0ExGTAXBgNVBAoMEFRocmVhZCBHcm91cCBJ +bmMxEjAQBgNVBAcMCVNhbiBSYW1vbjELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVT +MCAXDTI0MDUwNzA5Mzk0NVoYDzI5OTkxMjMxMDkzOTQ1WjA8MSEwHwYDVQQDDBhU +Q0FUIEV4YW1wbGUgRGV2aWNlQ2VydDExFzAVBgNVBAUTDjQ3MjMtOTgzMy0wMDAx +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE11h/4vKZXVXv+1GDZo066spItloT +dpCi0bux0jvpQSHLdQBIc+40zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdqNLMEkw +HwYDVR0jBBgwFoAUX6sbKWiIodS0MaiGYefnZlnt+BkwEAYJKwYBBAGC3yoCBAMC +AQUwFAYJKwYBBAGC3yoDBAcEBSABAQEBMAoGCCqGSM49BAMCA0cAMEQCIHWu+Rd1 +VRlzrD8KbuyJcJFTXh2sQ9UIrFIA7+4e/GVcAiAVBdGqTxbt3TGkBBllpafAUB2/ +s0GJj7E33oblqy5eHQ== +-----END CERTIFICATE----- +)", + R"( +-----BEGIN CERTIFICATE----- +MIIB6TCCAZCgAwIBAgICNeowCgYIKoZIzj0EAwIwcTEmMCQGA1UEAwwdVGhyZWFk +IENlcnRpZmljYXRpb24gRGV2aWNlQ0ExGTAXBgNVBAoMEFRocmVhZCBHcm91cCBJ +bmMxEjAQBgNVBAcMCVNhbiBSYW1vbjELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVT +MCAXDTI0MDUwNzA5Mzk0NVoYDzI5OTkxMjMxMDkzOTQ1WjA8MSEwHwYDVQQDDBhU +Q0FUIEV4YW1wbGUgRGV2aWNlQ2VydDIxFzAVBgNVBAUTDjQ3MjMtOTgzMy0wMDAy +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE30GMkqSBj3049NtK6G/MRTqcDxpm +i1LxTpSxFIB7P9HVoVM7Cd9X6bBUp5FrSZI+KHtX2HKtXzmzsdJ3gxAmi6NLMEkw +HwYDVR0jBBgwFoAUX6sbKWiIodS0MaiGYefnZlnt+BkwEAYJKwYBBAGC3yoCBAMC +AQUwFAYJKwYBBAGC3yoDBAcEBSABAQEBMAoGCCqGSM49BAMCA0cAMEQCIAbZzVbC +toNYgSWSgxRGzLRo1YJANqRC7yRtJNKTdQ1ZAiAlgGxEW2lkxCAGPUK1m9Wbb4kl +7AhBhYlK6vZz/omTsQ== +-----END CERTIFICATE----- +)"}; -#define OT_CLI_TCAT_PRIV_KEY \ - "-----BEGIN EC PRIVATE KEY-----\n" \ - "MHcCAQEEIIqKM1QTlNaquV74W6Viz/ggXoLqlPOP6LagSyaFO3oUoAoGCCqGSM49\n" \ - "AwEHoUQDQgAE11h/4vKZXVXv+1GDZo066spItloTdpCi0bux0jvpQSHLdQBIc+40\n" \ - "zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdg==\n" \ - "-----END EC PRIVATE KEY-----\n" +static const char *const OT_CLI_TCAT_PRIV_KEY[CERT_SET_COUNT] = {R"( +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIqKM1QTlNaquV74W6Viz/ggXoLqlPOP6LagSyaFO3oUoAoGCCqGSM49 +AwEHoUQDQgAE11h/4vKZXVXv+1GDZo066spItloTdpCi0bux0jvpQSHLdQBIc+40 +zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdg== +-----END EC PRIVATE KEY----- +)", + R"( +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIP7Al8tJA3QgwD3yIuOSEmJkT3GlWmcHQ59JfhZOjSdUoAoGCCqGSM49 +AwEHoUQDQgAE30GMkqSBj3049NtK6G/MRTqcDxpmi1LxTpSxFIB7P9HVoVM7Cd9X +6bBUp5FrSZI+KHtX2HKtXzmzsdJ3gxAmiw== +-----END EC PRIVATE KEY----- +)"}; #define OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE \ "-----BEGIN CERTIFICATE-----\n" \ @@ -86,15 +117,16 @@ "-----END CERTIFICATE-----\n" namespace ot { - namespace Cli { otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX]; otTcatGeneralDeviceId sGeneralDeviceId; -const char kPskdVendor[] = "JJJJJJ"; -const char kInstallVendor[] = "InstallCode"; -const char kUrl[] = "dummy_url"; +const char kPskdVendor[] = "JJJJJJ"; +const char kInstallVendor[] = "InstallCode"; +const char kUrl[] = "dummy_url"; +const char kApplicationServiceName1[] = "echo"; +const char kApplicationServiceName2[] = "discard"; static bool IsDeviceIdSet(void) { @@ -114,28 +146,24 @@ static void HandleBleSecureReceive(otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, otTcatApplicationProtocol aTcatApplicationProtocol, - const char *aServiceName, void *aContext) { OT_UNUSED_VARIABLE(aContext); - OT_UNUSED_VARIABLE(aTcatApplicationProtocol); - OT_UNUSED_VARIABLE(aServiceName); - static constexpr int kTextMaxLen = 100; - static constexpr uint8_t kBufPrefixLen = 5; + static constexpr int kTextMaxLen = 100; - uint16_t nLen; - uint8_t buf[kTextMaxLen]; - - nLen = - otMessageRead(aMessage, static_cast(aOffset), buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1); - - memcpy(buf, "RECV:", kBufPrefixLen); - - buf[nLen + kBufPrefixLen] = 0; - - IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, buf, (uint16_t)strlen((char *)buf))); - IgnoreReturnValue(otBleSecureFlush(aInstance)); + if (aTcatApplicationProtocol == OT_TCAT_APPLICATION_PROTOCOL_1 || + aTcatApplicationProtocol == OT_TCAT_APPLICATION_PROTOCOL_VENDOR) + { + uint8_t buf[kTextMaxLen]; + uint16_t nLen = otMessageRead(aMessage, static_cast(aOffset), buf, sizeof(buf)); + IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, OT_TCAT_APPLICATION_PROTOCOL_RESPONSE, buf, nLen)); + } + else if (aTcatApplicationProtocol == OT_TCAT_APPLICATION_PROTOCOL_2) + { + uint8_t status = OT_TCAT_STATUS_SUCCESS; + IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, OT_TCAT_APPLICATION_PROTOCOL_STATUS, &status, 1)); + } } /** @@ -230,6 +258,38 @@ exit: return error; } +/** + * @cli tcat certid + * @code + * tcat devid certid 0 + * Done + * @endcode + * @cparam tcat certid [@ca{value}] + * * The `value` int value of the ID. + * @par + * Selects a predefined certificate. + */ +template <> otError Tcat::Process(Arg aArgs[]) +{ + Error error = kErrorNone; + uint8_t certCandidate = 0; + + if (aArgs[0].IsEmpty()) + { + OutputLine("%d", mSelectedCert); + ExitNow(); + } + + SuccessOrExit(error = aArgs[0].ParseAsUint8(certCandidate)); + + VerifyOrExit(certCandidate < CERT_SET_COUNT, error = kErrorInvalidArgs); + + mSelectedCert = certCandidate; + +exit: + return error; +} + /** * @cli tcat devid * @code @@ -292,9 +352,12 @@ template <> otError Tcat::Process(Arg aArgs[]) otError error = OT_ERROR_NONE; ClearAllBytes(mVendorInfo); - mVendorInfo.mPskdString = kPskdVendor; - mVendorInfo.mProvisioningUrl = kUrl; - mVendorInfo.mInstallCode = kInstallVendor; + mVendorInfo.mPskdString = kPskdVendor; + mVendorInfo.mProvisioningUrl = kUrl; + mVendorInfo.mInstallCode = kInstallVendor; + mVendorInfo.mApplicationServiceName[0] = kApplicationServiceName1; + mVendorInfo.mApplicationServiceName[1] = kApplicationServiceName2; + mVendorInfo.mApplicationServiceIsTcp[1] = true; if (IsDeviceIdSet()) { @@ -306,9 +369,10 @@ template <> otError Tcat::Process(Arg aArgs[]) mVendorInfo.mGeneralDeviceId = &sGeneralDeviceId; } - otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_X509_CERT), - sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast(OT_CLI_TCAT_PRIV_KEY), - sizeof(OT_CLI_TCAT_PRIV_KEY)); + otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_X509_CERT[mSelectedCert]), + StringLength(OT_CLI_TCAT_X509_CERT[mSelectedCert], CERT_MAX_SIZE) + 1, + reinterpret_cast(OT_CLI_TCAT_PRIV_KEY[mSelectedCert]), + StringLength(OT_CLI_TCAT_PRIV_KEY[mSelectedCert], KEY_MAX_SIZE) + 1); otBleSecureSetCaCertificateChain(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE), diff --git a/src/cli/cli_tcat.hpp b/src/cli/cli_tcat.hpp index 5a9bc56ae..5523a4257 100644 --- a/src/cli/cli_tcat.hpp +++ b/src/cli/cli_tcat.hpp @@ -55,6 +55,7 @@ public: */ Tcat(otInstance *aInstance, OutputImplementer &aOutputImplementer) : Utils(aInstance, aOutputImplementer) + , mSelectedCert(0) { } @@ -77,6 +78,7 @@ private: template otError Process(Arg aArgs[]); otTcatVendorInfo mVendorInfo; + uint8_t mSelectedCert; }; } // namespace Cli diff --git a/src/core/api/ble_secure_api.cpp b/src/core/api/ble_secure_api.cpp index cc737ffd5..f5d95e33c 100644 --- a/src/core/api/ble_secure_api.cpp +++ b/src/core/api/ble_secure_api.cpp @@ -195,9 +195,12 @@ otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) return AsCoreType(aInstance).Get().Send(aBuf, aLength); } -otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) +otError otBleSecureSendApplicationTlv(otInstance *aInstance, + otTcatApplicationProtocol aApplicationProtocol, + uint8_t *aBuf, + uint16_t aLength) { - return AsCoreType(aInstance).Get().SendApplicationTlv(aBuf, aLength); + return AsCoreType(aInstance).Get().SendApplicationTlv(MapEnum(aApplicationProtocol), aBuf, aLength); } otError otBleSecureFlush(otInstance *aInstance) { return AsCoreType(aInstance).Get().Flush(); } diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 805ce212d..f461e1cdd 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -33,6 +33,8 @@ #include "tcat_agent.hpp" #include "common/code_utils.hpp" +#include "common/error.hpp" +#include "crypto/storage.hpp" #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE @@ -56,7 +58,6 @@ bool TcatAgent::VendorInfo::IsValid(void) const TcatAgent::TcatAgent(Instance &aInstance) : InstanceLocator(aInstance) , mVendorInfo(nullptr) - , mCurrentApplicationProtocol(kApplicationProtocolNone) , mState(kStateDisabled) , mCommissionerHasNetworkName(false) , mCommissionerHasDomainName(false) @@ -65,9 +66,10 @@ TcatAgent::TcatAgent(Instance &aInstance) , mPskdVerified(false) , mPskcVerified(false) , mInstallCodeVerified(false) + , mIsCommissioned(false) + , mApplicationResponsePending(false) { mJoinerPskd.Clear(); - mCurrentServiceName[0] = 0; } Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext) @@ -79,9 +81,7 @@ Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallb mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext); mJoinCallback.Set(aHandler, aContext); mRandomChallenge = 0; - - mCurrentApplicationProtocol = kApplicationProtocolNone; - mState = kStateEnabled; + mState = kStateEnabled; exit: LogWarnOnError(error, "start TCAT agent"); @@ -90,14 +90,19 @@ exit: void TcatAgent::Stop(void) { - mCurrentApplicationProtocol = kApplicationProtocolNone; - mState = kStateDisabled; + mState = kStateDisabled; mAppDataReceiveCallback.Clear(); mJoinCallback.Clear(); - mRandomChallenge = 0; - mPskdVerified = false; - mPskcVerified = false; - mInstallCodeVerified = false; + mCommissionerHasNetworkName = false; + mCommissionerHasDomainName = false; + mCommissionerHasExtendedPanId = false; + mCommissionerNetworkName.m8[0] = '\0'; + mCommissionerDomainName.m8[0] = '\0'; + mRandomChallenge = 0; + mPskdVerified = false; + mPskcVerified = false; + mInstallCodeVerified = false; + mIsCommissioned = false; LogInfo("TCAT agent stopped"); } @@ -162,9 +167,8 @@ Error TcatAgent::Connected(MeshCoP::Tls::Extension &aTls) } } - mCurrentApplicationProtocol = kApplicationProtocolNone; - mCurrentServiceName[0] = 0; - mState = kStateConnected; + mState = kStateConnected; + mIsCommissioned = Get().IsCommissioned(); LogInfo("TCAT agent connected"); exit: @@ -173,8 +177,6 @@ exit: void TcatAgent::Disconnected(void) { - mCurrentApplicationProtocol = kApplicationProtocolNone; - if (mState != kStateDisabled) { mState = kStateEnabled; @@ -188,99 +190,113 @@ void TcatAgent::Disconnected(void) LogInfo("TCAT agent disconnected"); } +uint8_t TcatAgent::CheckAuthorizationRequirements(CommandClassFlags aFlagsRequired, Dataset::Info *aDatasetInfo) const +{ + uint8_t res = kAccessFlag; + + for (uint16_t flag = kPskdFlag; flag < kMaxFlag; flag <<= 1) + { + if (aFlagsRequired & flag) + { + switch (flag) + { + case kPskdFlag: + if (mPskdVerified) + { + res |= flag; + } + break; + + case kNetworkNameFlag: + if (aDatasetInfo != nullptr && mCommissionerHasNetworkName && + aDatasetInfo->IsPresent() && + (aDatasetInfo->Get() == mCommissionerNetworkName)) + { + res |= flag; + } + break; + + case kExtendedPanIdFlag: + if (aDatasetInfo != nullptr && mCommissionerHasExtendedPanId && + aDatasetInfo->IsPresent() && + (aDatasetInfo->Get() == mCommissionerExtendedPanId)) + { + res |= flag; + } + break; + + case kThreadDomainFlag: + + if (mCommissionerHasDomainName) + { +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4) + if (Get().GetDomainName() == mCommissionerDomainName) +#else + if (StringMatch(mCommissionerDomainName.GetAsCString(), NetworkName::kDomainNameInit)) +#endif + { + res |= flag; + } + } + break; + + case kPskcFlag: + if (mPskcVerified) + { + res |= flag; + } + break; + + default: + LogCrit("Error while processing access flags. Unexpected flag %d", flag); + OT_ASSERT(false); // Should not get here + } + } + } + + return res; +} + bool TcatAgent::CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, CommandClassFlags aDeviceCommandClassFlags, Dataset *aDataset) const { - bool authorized = false; - bool additionalDeviceRequirementMet = false; - bool domainNamesMatch = false; - bool networkNamesMatch = false; - bool extendedPanIdsMatch = false; + bool authorized = false; + uint8_t deviceRequirementMet; + uint8_t commissionerRequirementMet; + Dataset::Info datasetInfo; + Error datasetError = kErrorNone; VerifyOrExit(IsConnected()); - VerifyOrExit(aCommissionerCommandClassFlags & kAccessFlag); - if (aDeviceCommandClassFlags & kAccessFlag) + if (aDataset == nullptr) { - additionalDeviceRequirementMet = true; + datasetError = Get().Read(datasetInfo); + } + else + { + aDataset->ConvertTo(datasetInfo); } - if (!additionalDeviceRequirementMet && (aDeviceCommandClassFlags & kPskdFlag)) + if (datasetError == kErrorNone) { - additionalDeviceRequirementMet = mPskdVerified; + deviceRequirementMet = CheckAuthorizationRequirements(aDeviceCommandClassFlags, &datasetInfo); + commissionerRequirementMet = CheckAuthorizationRequirements(aCommissionerCommandClassFlags, &datasetInfo); + } + else + { + deviceRequirementMet = CheckAuthorizationRequirements(aDeviceCommandClassFlags, nullptr); + commissionerRequirementMet = CheckAuthorizationRequirements(aCommissionerCommandClassFlags, nullptr); } - if (!additionalDeviceRequirementMet && (aDeviceCommandClassFlags & kPskcFlag)) + if (aDataset != nullptr) // For set active operational dataset TLV the PSKc check is always successful { - additionalDeviceRequirementMet = mPskcVerified; + deviceRequirementMet |= kPskcFlag; + commissionerRequirementMet |= (aCommissionerCommandClassFlags & kPskcFlag); } - if (mCommissionerHasNetworkName || mCommissionerHasExtendedPanId) - { - Dataset::Info datasetInfo; - Error datasetError = kErrorNone; - - if (aDataset == nullptr) - { - datasetError = Get().Read(datasetInfo); - } - else - { - aDataset->ConvertTo(datasetInfo); - } - - if (datasetError == kErrorNone) - { - if (datasetInfo.IsPresent() && mCommissionerHasNetworkName && - (datasetInfo.Get() == mCommissionerNetworkName)) - { - networkNamesMatch = true; - } - - if (datasetInfo.IsPresent() && mCommissionerHasExtendedPanId && - (datasetInfo.Get() == mCommissionerExtendedPanId)) - { - extendedPanIdsMatch = true; - } - } - } - - if (!networkNamesMatch) - { - VerifyOrExit((aCommissionerCommandClassFlags & kNetworkNameFlag) == 0); - } - else if (aDeviceCommandClassFlags & kNetworkNameFlag) - { - additionalDeviceRequirementMet = true; - } - - if (!extendedPanIdsMatch) - { - VerifyOrExit((aCommissionerCommandClassFlags & kExtendedPanIdFlag) == 0); - } - else if (aDeviceCommandClassFlags & kExtendedPanIdFlag) - { - additionalDeviceRequirementMet = true; - } - -#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0); -#endif - - if (!domainNamesMatch) - { - VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0); - } - else if (aDeviceCommandClassFlags & kThreadDomainFlag) - { - additionalDeviceRequirementMet = true; - } - - if (additionalDeviceRequirementMet) - { - authorized = true; - } + authorized = (commissionerRequirementMet == aCommissionerCommandClassFlags) && + (deviceRequirementMet & aDeviceCommandClassFlags); exit: return authorized; @@ -306,7 +322,7 @@ bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const mDeviceAuthorizationField.mExtractionFlags, nullptr); break; - case kTlvDecommissioning: + case kDecommissioning: authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags, mDeviceAuthorizationField.mDecommissioningFlags, nullptr); break; @@ -324,46 +340,6 @@ bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const return authorized; } -TcatAgent::CommandClass TcatAgent::GetCommandClass(uint8_t aTlvType) const -{ - static constexpr int kGeneralTlvs = 0x1F; - static constexpr int kCommissioningTlvs = 0x3F; - static constexpr int kExtractionTlvs = 0x5F; - static constexpr int kTlvDecommissioningTlvs = 0x7F; - static constexpr int kApplicationTlvs = 0x9F; - - if (aTlvType <= kGeneralTlvs) - { - return kGeneral; - } - else if (aTlvType <= kCommissioningTlvs) - { - return kCommissioning; - } - else if (aTlvType <= kExtractionTlvs) - { - return kExtraction; - } - else if (aTlvType <= kTlvDecommissioningTlvs) - { - return kTlvDecommissioning; - } - else if (aTlvType <= kApplicationTlvs) - { - return kApplication; - } - else - { - return kInvalid; - } -} - -bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const -{ - CommandClass tlvCommandClass = GetCommandClass(aTlvType); - return IsCommandClassAuthorized(tlvCommandClass); -} - Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage) { Error error = kErrorParse; @@ -388,88 +364,98 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg offset += sizeof(ot::Tlv); } - if (!CanProcessTlv(tlv.GetType())) + switch (tlv.GetType()) { - error = kErrorRejected; + case kTlvDisconnect: + error = kErrorAbort; + response = true; // true - to avoid response-with-status being sent. + break; + + case kTlvSetActiveOperationalDataset: + error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length); + break; + + case kTlvGetActiveOperationalDataset: + error = HandleGetActiveOperationalDataset(aOutgoingMessage, response); + break; + + case kTlvGetDiagnosticTlvs: + error = HandleGetDiagnosticTlvs(aIncomingMessage, aOutgoingMessage, offset, length, response); + break; + + case kTlvStartThreadInterface: + error = HandleStartThreadInterface(); + break; + + case kTlvStopThreadInterface: + error = HandleStopThreadInterface(); + break; + + case kTlvGetApplicationLayers: + error = HandleGetApplicationLayers(aOutgoingMessage, response); + break; + + case kTlvSendApplicationData1: + case kTlvSendApplicationData2: + case kTlvSendApplicationData3: + case kTlvSendApplicationData4: + case kTlvSendVendorSpecificData: + error = HandleApplicationData(aIncomingMessage, offset, static_cast(tlv.GetType()), + response); + break; + + case kTlvDecommission: + error = HandleDecomission(); + break; + + case kTlvPing: + error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response); + break; + + case kTlvGetNetworkName: + error = HandleGetNetworkName(aOutgoingMessage, response); + break; + + case kTlvGetDeviceId: + error = HandleGetDeviceId(aOutgoingMessage, response); + break; + + case kTlvGetExtendedPanID: + error = HandleGetExtPanId(aOutgoingMessage, response); + break; + + case kTlvGetProvisioningURL: + error = HandleGetProvisioningUrl(aOutgoingMessage, response); + break; + + case kTlvPresentPskdHash: + error = HandlePresentPskdHash(aIncomingMessage, offset, length); + break; + + case kTlvPresentPskcHash: + error = HandlePresentPskcHash(aIncomingMessage, offset, length); + break; + + case kTlvPresentInstallCodeHash: + error = HandlePresentInstallCodeHash(aIncomingMessage, offset, length); + break; + + case kTlvRequestRandomNumChallenge: + error = HandleRequestRandomNumberChallenge(aOutgoingMessage, response); + break; + + case kTlvRequestPskdHash: + error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response); + break; + + case kTlvGetCommissionerCertificate: + error = HandleGetCommissionerCertificate(aOutgoingMessage, response); + break; + + default: + error = kErrorInvalidCommand; } - else - { - switch (tlv.GetType()) - { - case kTlvDisconnect: - error = kErrorAbort; - response = true; // true - to avoid response-with-status being sent. - break; - case kTlvSetActiveOperationalDataset: - error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length); - break; - - case kTlvGetActiveOperationalDataset: - error = HandleGetActiveOperationalDataset(aOutgoingMessage, response); - break; - - case kTlvGetDiagnosticTlvs: - error = HandleGetDiagnosticTlvs(aIncomingMessage, aOutgoingMessage, offset, length, response); - break; - - case kTlvStartThreadInterface: - error = HandleStartThreadInterface(); - break; - - case kTlvStopThreadInterface: - error = otThreadSetEnabled(&GetInstance(), false); - break; - - case kTlvSendApplicationData: - LogInfo("Application data len:%d, offset:%d", length, offset); - mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, offset, - MapEnum(mCurrentApplicationProtocol), mCurrentServiceName); - response = true; - error = kErrorNone; - break; - - case kTlvDecommission: - error = HandleDecomission(); - break; - - case kTlvPing: - error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response); - break; - case kTlvGetNetworkName: - error = HandleGetNetworkName(aOutgoingMessage, response); - break; - case kTlvGetDeviceId: - error = HandleGetDeviceId(aOutgoingMessage, response); - break; - case kTlvGetExtendedPanID: - error = HandleGetExtPanId(aOutgoingMessage, response); - break; - case kTlvGetProvisioningURL: - error = HandleGetProvisioningUrl(aOutgoingMessage, response); - break; - case kTlvPresentPskdHash: - error = HandlePresentPskdHash(aIncomingMessage, offset, length); - break; - case kTlvPresentPskcHash: - error = HandlePresentPskcHash(aIncomingMessage, offset, length); - break; - case kTlvPresentInstallCodeHash: - error = HandlePresentInstallCodeHash(aIncomingMessage, offset, length); - break; - case kTlvRequestRandomNumChallenge: - error = HandleRequestRandomNumberChallenge(aOutgoingMessage, response); - break; - case kTlvRequestPskdHash: - error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response); - break; - case kTlvGetCommissionerCertificate: - error = HandleGetCommissionerCertificate(aOutgoingMessage, response); - break; - default: - error = kErrorInvalidCommand; - } - } if (!response) { StatusCode statusCode; @@ -480,30 +466,40 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg statusCode = kStatusSuccess; break; - case kErrorInvalidState: - statusCode = kStatusUndefined; + case kErrorNotImplemented: + case kErrorInvalidCommand: + statusCode = kStatusUnsupported; break; case kErrorParse: statusCode = kStatusParseError; break; - case kErrorInvalidCommand: - statusCode = kStatusUnsupported; + case kErrorInvalidArgs: + statusCode = kStatusValueError; break; - case kErrorRejected: - statusCode = kStatusUnauthorized; + case kErrorBusy: + statusCode = kStatusBusy; break; - case kErrorNotImplemented: - statusCode = kStatusUnsupported; + case kErrorNotFound: + statusCode = kStatusUndefined; break; case kErrorSecurity: statusCode = kStatusHashError; break; + case kErrorInvalidState: + case kErrorAlready: + statusCode = kStatusInvalidState; + break; + + case kErrorRejected: + statusCode = kStatusUnauthorized; + break; + default: statusCode = kStatusGeneralError; break; @@ -524,9 +520,12 @@ Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessa uint8_t buf[kCommissionerCertMaxLength]; size_t bufLen = sizeof(buf); + VerifyOrExit(!mIsCommissioned, error = kErrorAlready); + offsetRange.Init(aOffset, aLength); SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange)); SuccessOrExit(error = dataset.ValidateTlvs()); + VerifyOrExit(dataset.ContainsTlv(Tlv::kNetworkKey), error = kErrorInvalidArgs); if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags, mDeviceAuthorizationField.mCommissioningFlags, &dataset)) @@ -550,13 +549,7 @@ Error TcatAgent::HandleGetActiveOperationalDataset(Message &aOutgoingMessage, bo Dataset dataset; Dataset::Tlvs datasetTlvs; - if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags, - mDeviceAuthorizationField.mCommissioningFlags, &dataset)) - { - error = kErrorRejected; - ExitNow(); - } - + VerifyOrExit(IsCommandClassAuthorized(kExtraction), error = kErrorRejected); SuccessOrExit(error = Get().Read(datasetTlvs)); SuccessOrExit( error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, datasetTlvs.mTlvs, datasetTlvs.mLength)); @@ -568,20 +561,12 @@ exit: Error TcatAgent::HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse) { - Error error = kErrorNone; - Dataset dataset; - uint8_t buf[kCommissionerCertMaxLength]; - uint16_t bufLen = sizeof(buf); + Error error = kErrorNone; + unsigned char buf[kCommissionerCertMaxLength]; + uint16_t bufLen = sizeof(buf); - if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags, - mDeviceAuthorizationField.mCommissioningFlags, &dataset)) - { - error = kErrorRejected; - ExitNow(); - } - - VerifyOrExit(kErrorNone == Get().ReadTcatCommissionerCertificate(buf, bufLen), - error = kErrorInvalidState); + VerifyOrExit(IsCommandClassAuthorized(kCommissioning), error = kErrorRejected); + VerifyOrExit(kErrorNone == Get().ReadTcatCommissionerCertificate(buf, bufLen), error = kErrorNotFound); SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, buf, bufLen)); aResponse = true; @@ -653,15 +638,8 @@ Error TcatAgent::HandleDecomission(void) Error error = kErrorNone; unsigned char buf[kCommissionerCertMaxLength]; size_t bufLen = sizeof(buf); - Dataset dataset; - - if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags, - mDeviceAuthorizationField.mDecommissioningFlags, &dataset)) - { - error = kErrorRejected; - ExitNow(); - } + VerifyOrExit(IsCommandClassAuthorized(kDecommissioning), error = kErrorRejected); SuccessOrExit(error = Get().GetPeerCertificateDer(buf, &bufLen, bufLen)); Get().SaveTcatCommissionerCertificate(buf, static_cast(bufLen)); @@ -719,9 +697,9 @@ Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &aResponse Error error = kErrorNone; MeshCoP::NameData nameData = Get().GetNetworkName().GetAsData(); - VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + VerifyOrExit(Get().IsCommissioned(), error = kErrorNotFound); #if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME - VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState); + VerifyOrExit(nameData.GetLength() > 0, error = kErrorNotFound); #endif SuccessOrExit( @@ -739,6 +717,8 @@ Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &aResponse) Mac::ExtAddress eui64; Error error = kErrorNone; + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); + if (mVendorInfo->mGeneralDeviceId != nullptr) { length = mVendorInfo->mGeneralDeviceId->mDeviceIdLen; @@ -765,7 +745,7 @@ Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &aResponse) { Error error; - VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + VerifyOrExit(Get().IsCommissioned(), error = kErrorNotFound); SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, &Get().GetExtPanId(), sizeof(ExtendedPanId))); @@ -780,10 +760,11 @@ Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &aResp Error error = kErrorNone; uint16_t length; + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState); length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength); - VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState); + VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorNotFound); SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length)); @@ -797,6 +778,7 @@ Error TcatAgent::HandlePresentPskdHash(const Message &aIncomingMessage, uint16_t { Error error = kErrorNone; + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); VerifyOrExit(mVendorInfo->mPskdString != nullptr, error = kErrorSecurity); SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mPskdString, @@ -828,6 +810,7 @@ Error TcatAgent::HandlePresentInstallCodeHash(const Message &aIncomingMessage, u { Error error = kErrorNone; + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); VerifyOrExit(mVendorInfo->mInstallCode != nullptr, error = kErrorSecurity); SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mInstallCode, @@ -861,6 +844,7 @@ Error TcatAgent::HandleRequestPskdHash(const Message &aIncomingMessage, uint64_t providedChallenge = 0; Crypto::HmacSha256::Hash hash; + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); VerifyOrExit(StringLength(mVendorInfo->mPskdString, kMaxPskdLength) != 0, error = kErrorFailed); VerifyOrExit(aLength == sizeof(providedChallenge), error = kErrorParse); @@ -903,12 +887,91 @@ void TcatAgent::CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBuf Crypto::Key cryptoKey; Crypto::HmacSha256 hmac; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Crypto::Storage::KeyRef keyRef; + SuccessOrExit(Crypto::Storage::ImportKey(keyRef, Crypto::Storage::kKeyTypeHmac, + Crypto::Storage::kKeyAlgorithmHmacSha256, Crypto::Storage::kUsageSignHash, + Crypto::Storage::kTypeVolatile, reinterpret_cast(aBuf), + aBufLen)); + cryptoKey.SetAsKeyRef(keyRef); +#else cryptoKey.Set(reinterpret_cast(aBuf), static_cast(aBufLen)); +#endif hmac.Start(cryptoKey); hmac.Update(aChallenge); hmac.Update(rawKey.p, static_cast(rawKey.len)); hmac.Finish(aHash); + +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Crypto::Storage::DestroyKey(keyRef); +exit: +#endif + return; +} + +Error TcatAgent::HandleGetApplicationLayers(Message &aOutgoingMessage, bool &aResponse) +{ + Error error = kErrorNone; + ot::Tlv tlv; + uint8_t replyLen = 0; + uint8_t count = 0; + + static_assert((kApplicationLayerMaxCount * (kServiceNameMaxLength + 2)) <= 250, + "Unsupported TCAT application layers configuration"); + + VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState); + VerifyOrExit(IsCommandClassAuthorized(kApplication), error = kErrorRejected); + + for (uint8_t i = 0; i < kApplicationLayerMaxCount && mVendorInfo->mApplicationServiceName[i] != nullptr; i++) + { + replyLen += sizeof(tlv); + replyLen += StringLength(mVendorInfo->mApplicationServiceName[i], kServiceNameMaxLength); + count++; + } + + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(replyLen); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + + for (uint8_t i = 0; i < count; i++) + { + uint16_t length = StringLength(mVendorInfo->mApplicationServiceName[i], kServiceNameMaxLength); + uint8_t type = mVendorInfo->mApplicationServiceIsTcp[i] ? kTlvServiceNameTcp : kTlvServiceNameUdp; + SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, type, mVendorInfo->mApplicationServiceName[i], length)); + } + + aResponse = true; + +exit: + return error; +} + +Error TcatAgent::HandleApplicationData(const Message &aIncomingMessage, + uint16_t aOffset, + TcatApplicationProtocol aApplicationProtocol, + bool &aResponse) +{ + Error error = kErrorNone; + + VerifyOrExit(IsCommandClassAuthorized(kApplication), error = kErrorRejected); + + mApplicationResponsePending = true; + mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, aOffset, + static_cast(aApplicationProtocol)); + + if (mApplicationResponsePending) + { + mApplicationResponsePending = false; + error = kErrorNotImplemented; // Application unsupported + } + else + { + aResponse = true; + } + +exit: + return error; } Error TcatAgent::HandleStartThreadInterface(void) @@ -916,6 +979,7 @@ Error TcatAgent::HandleStartThreadInterface(void) Error error; Dataset::Info datasetInfo; + VerifyOrExit(IsCommandClassAuthorized(kCommissioning), error = kErrorRejected); VerifyOrExit(Get().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState); VerifyOrExit(datasetInfo.IsPresent(), error = kErrorInvalidState); @@ -930,6 +994,18 @@ exit: return error; } +Error TcatAgent::HandleStopThreadInterface(void) +{ + Error error; + + VerifyOrExit(IsCommandClassAuthorized(kCommissioning), error = kErrorRejected); + + error = otThreadSetEnabled(&GetInstance(), false); + +exit: + return error; +} + void SeralizeTcatAdvertisementTlv(uint8_t *aBuffer, uint16_t &aOffset, TcatAdvertisementTlvType aType, @@ -996,9 +1072,11 @@ Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementDat reinterpret_cast(&caps)); } - tas.mRsv = 0; - tas.mMultiradioSupport = otPlatBleSupportsMultiRadio(&GetInstance()); - tas.mIsCommisionned = Get().IsCommissioned(); + tas.mRsv = 0; + tas.mMultiRadioSupport = otPlatBleSupportsMultiRadio(&GetInstance()); + tas.mStoresActiveOperationalDataset = + Get().IsPartiallyComplete() || Get().IsCommissioned(); + tas.mIsCommissioned = Get().IsCommissioned(); tas.mThreadNetworkActive = Get().IsAttached(); tas.mDeviceType = Get().GetDeviceMode().IsFullThreadDevice(); tas.mRxOnWhenIdle = Get().GetDeviceMode().IsRxOnWhenIdle(); diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 9225d5afe..027990f36 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -83,11 +83,11 @@ public: */ enum CommandClass { - kGeneral = OT_TCAT_COMMAND_CLASS_GENERAL, ///< TCAT commands related to general operations - kCommissioning = OT_TCAT_COMMAND_CLASS_COMMISSIONING, ///< TCAT commands related to commissioning - kExtraction = OT_TCAT_COMMAND_CLASS_EXTRACTION, ///< TCAT commands related to key extraction - kTlvDecommissioning = OT_TCAT_COMMAND_CLASS_DECOMMISSIONING, ///< TCAT commands related to de-commissioning - kApplication = OT_TCAT_COMMAND_CLASS_APPLICATION, ///< TCAT commands related to application layer + kGeneral = OT_TCAT_COMMAND_CLASS_GENERAL, ///< TCAT commands related to general operations + kCommissioning = OT_TCAT_COMMAND_CLASS_COMMISSIONING, ///< TCAT commands related to commissioning + kExtraction = OT_TCAT_COMMAND_CLASS_EXTRACTION, ///< TCAT commands related to key extraction + kDecommissioning = OT_TCAT_COMMAND_CLASS_DECOMMISSIONING, ///< TCAT commands related to decommissioning + kApplication = OT_TCAT_COMMAND_CLASS_APPLICATION, ///< TCAT commands related to application layer kInvalid ///< TCAT command belongs to reserved pool or is invalid }; @@ -109,8 +109,9 @@ public: kPskdFlag = 1 << 1, ///< Access requires proof-of-possession of the device's PSKd kNetworkNameFlag = 1 << 2, ///< Access requires matching network name kExtendedPanIdFlag = 1 << 3, ///< Access requires matching XPANID - kThreadDomainFlag = 1 << 4, ///< Access requires matching XPANID + kThreadDomainFlag = 1 << 4, ///< Access requires matching Thread Domain Name kPskcFlag = 1 << 5, ///< Access requires proof-of-possession of the device's PSKc + kMaxFlag = 1 << 6, ///< Maximum value of access flags }; /** @@ -151,47 +152,50 @@ public: enum CommandTlvType : uint8_t { // Command Class General - kTlvResponseWithStatus = 1, ///< TCAT response with status value TLV - kTlvResponseWithPayload = 2, ///< TCAT response with payload TLV - kTlvResponseEvent = 3, ///< TCAT response event TLV (reserved) - kTlvGetNetworkName = 8, ///< TCAT network name query TLV - kTlvDisconnect = 9, ///< TCAT disconnect request TLV - kTlvPing = 10, ///< TCAT ping request TLV - kTlvGetDeviceId = 11, ///< TCAT device ID query TLV - kTlvGetExtendedPanID = 12, ///< TCAT extended PAN ID query TLV - kTlvGetProvisioningURL = 13, ///< TCAT provisioning URL query TLV - kTlvPresentPskdHash = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash - kTlvPresentPskcHash = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash - kTlvPresentInstallCodeHash = 18, ///< TCAT commissioner rights elevation request TLV using install code - kTlvRequestRandomNumChallenge = 19, ///< TCAT random number challenge query TLV - kTlvRequestPskdHash = 20, ///< TCAT PSKd hash request TLV + kTlvResponseWithStatus = 0x01, ///< TCAT response with status value TLV + kTlvResponseWithPayload = 0x02, ///< TCAT response with payload TLV + kTlvResponseEvent = 0x03, ///< TCAT response event TLV (reserved) + kTlvGetNetworkName = 0x08, ///< TCAT network name query TLV + kTlvDisconnect = 0x09, ///< TCAT disconnect request TLV + kTlvPing = 0x0A, ///< TCAT ping request TLV + kTlvGetDeviceId = 0x0B, ///< TCAT device ID query TLV + kTlvGetExtendedPanID = 0x0C, ///< TCAT extended PAN ID query TLV + kTlvGetProvisioningURL = 0x0D, ///< TCAT provisioning URL query TLV + kTlvPresentPskdHash = 0x10, ///< TCAT commissioner rights elevation request TLV using PSKd hash + kTlvPresentPskcHash = 0x11, ///< TCAT commissioner rights elevation request TLV using PSKc hash + kTlvPresentInstallCodeHash = 0x12, ///< TCAT commissioner rights elevation request TLV using install code + kTlvRequestRandomNumChallenge = 0x13, ///< TCAT random number challenge query TLV + kTlvRequestPskdHash = 0x14, ///< TCAT PSKd hash request TLV // Command Class Commissioning - kTlvSetActiveOperationalDataset = 32, ///< TCAT active operational dataset TLV - kTlvSetActiveOperationalDatasetAlternative = 33, ///< TCAT active operational dataset alternative #1 TLV - kTlvGetProvisioningTlvs = 36, ///< TCAT provisioning TLVs query TLV - kTlvGetCommissionerCertificate = 37, ///< TCAT commissioner certificate query TLV - kTlvGetDiagnosticTlvs = 38, ///< TCAT diagnostics TLVs query TLV - kTlvStartThreadInterface = 39, ///< TCAT start thread interface request TLV - kTlvStopThreadInterface = 40, ///< TCAT stop thread interface request TLV + kTlvSetActiveOperationalDataset = 0x20, ///< TCAT active operational dataset TLV + kTlvSetActiveOperationalDatasetAlternative = 0x21, ///< TCAT active operational dataset alternative #1 TLV + kTlvGetCommissionerCertificate = 0x25, ///< TCAT commissioner certificate query TLV + kTlvGetDiagnosticTlvs = 0x26, ///< TCAT diagnostics TLVs query TLV + kTlvStartThreadInterface = 0x27, ///< TCAT start thread interface request TLV + kTlvStopThreadInterface = 0x28, ///< TCAT stop thread interface request TLV // Command Class Extraction - kTlvGetActiveOperationalDataset = 64, ///< TCAT active oerational dataset query TLV - kTlvGetActiveOperationalDatasetAlternative = 65, ///< TCAT active oerational dataset alternative #1 query TLV + kTlvGetActiveOperationalDataset = 0x40, ///< TCAT active oerational dataset query TLV + kTlvGetActiveOperationalDatasetAlternative = 0x41, ///< TCAT active oerational dataset alternative #1 query TLV // Command Class Decommissioning - kTlvDecommission = 96, ///< TCAT decommission request TLV + kTlvDecommission = 0x60, ///< TCAT decommission request TLV // Command Class Application - kTlvSelectApplicationLayerUdp = 128, ///< TCAT select UDP protocol application layer request TLV - kTlvSelectApplicationLayerTcp = 129, ///< TCAT select TCP protocol application layer request TLV - kTlvSendApplicationData = 130, ///< TCAT send application data TLV - kTlvSendVendorSpecificData = 159, ///< TCAT send vendor specific command or data TLV + kTlvGetApplicationLayers = 0x80, ///< TCAT get application layers request TLV + kTlvSendApplicationData1 = 0x81, ///< TCAT send application data 1 TLV + kTlvSendApplicationData2 = 0x82, ///< TCAT send application data 2 TLV + kTlvSendApplicationData3 = 0x83, ///< TCAT send application data 3 TLV + kTlvSendApplicationData4 = 0x84, ///< TCAT send application data 4 TLV + kTlvServiceNameUdp = 0x89, ///< TCAT service name UDP sub-TLV (not used as a command) + kTlvServiceNameTcp = 0x8A, ///< TCAT service name TCP sub-TLV (not used as a command) + kTlvSendVendorSpecificData = 0x9F, ///< TCAT send vendor specific command or data TLV // Command Class CCM - kTlvSetLDevIdOperationalCert = 160, ///< TCAT LDevID operational certificate TLV - kTlvSetLDevIdPrivateKey = 161, ///< TCAT LDevID operational certificate pricate key TLV - kTlvSetDomainCaCert = 162, ///< TCAT domain CA certificate TLV + kTlvSetLDevIdOperationalCert = 0xA0, ///< TCAT LDevID operational certificate TLV + kTlvSetLDevIdPrivateKey = 0xA1, ///< TCAT LDevID operational certificate pricate key TLV + kTlvSetDomainCaCert = 0xA2, ///< TCAT domain CA certificate TLV }; /** @@ -208,6 +212,8 @@ public: kStatusUndefined = OT_TCAT_STATUS_UNDEFINED, ///< The requested value, data or service is not defined ///< (currently) or not present kStatusHashError = OT_TCAT_STATUS_HASH_ERROR, ///< The hash value presented by the commissioner was incorrect + kStatusInvalidState = + OT_TCAT_STATUS_INVALID_STATE, ///< The TCAT device is in invalid state to execute the command kStatusUnauthorized = OT_TCAT_STATUS_UNAUTHORIZED, ///< Sender does not have sufficient authorization for the given command }; @@ -219,8 +225,16 @@ public: { kApplicationProtocolNone = OT_TCAT_APPLICATION_PROTOCOL_NONE, ///< Message which has been sent without activating the TCAT agent - kApplicationProtocolUdp = OT_TCAT_APPLICATION_PROTOCOL_STATUS, ///< Message directed to a UDP service - kApplicationProtocolTcp = OT_TCAT_APPLICATION_PROTOCOL_TCP, ///< Message directed to a TCP service + kApplicationProtocolStatus = OT_TCAT_APPLICATION_PROTOCOL_STATUS, ///< Message directed to any application + ///< indicating a response with status value + kApplicationProtocolResponse = OT_TCAT_APPLICATION_PROTOCOL_RESPONSE, ///< Message directed to any application + ///< indicating a response with payload + kApplicationProtocol1 = OT_TCAT_APPLICATION_PROTOCOL_1, ///< Message directed to application 1 + kApplicationProtocol2 = OT_TCAT_APPLICATION_PROTOCOL_2, ///< Message directed to application 2 + kApplicationProtocol3 = OT_TCAT_APPLICATION_PROTOCOL_3, ///< Message directed to application 3 + kApplicationProtocol4 = OT_TCAT_APPLICATION_PROTOCOL_4, ///< Message directed to application 4 + kApplicationProtocolVendor = + OT_TCAT_APPLICATION_PROTOCOL_VENDOR, ///< Message directed to a vendor specific application }; /** @@ -332,6 +346,8 @@ public: * @retval FALSE The install code was not verified. */ bool GetInstallCodeVerifyStatus(void) const { return mInstallCodeVerified; } + bool GetApplicationResponsePending(void) { return mApplicationResponsePending; } + void NotifyApplicationResponseSent(void) { mApplicationResponsePending = false; } private: Error Connected(MeshCoP::Tls::Extension &aTls); @@ -365,7 +381,13 @@ private: uint16_t aLength, bool &aResponse); Error HandleStartThreadInterface(void); + Error HandleStopThreadInterface(void); Error HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse); + Error HandleGetApplicationLayers(Message &aOutgoingMessage, bool &aResponse); + Error HandleApplicationData(const Message &aIncomingMessage, + uint16_t aOffset, + TcatApplicationProtocol aApplicationProtocol, + bool &aResponse); Error VerifyHash(const Message &aIncomingMessage, uint16_t aOffset, @@ -374,12 +396,10 @@ private: size_t aBufLen); void CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen, Crypto::HmacSha256::Hash &aHash); - bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, - CommandClassFlags aDeviceCommandClassFlags, - Dataset *aDataset) const; - - bool CanProcessTlv(uint8_t aTlvType) const; - CommandClass GetCommandClass(uint8_t aTlvType) const; + bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, + CommandClassFlags aDeviceCommandClassFlags, + Dataset *aDataset) const; + uint8_t CheckAuthorizationRequirements(CommandClassFlags aFlagsChecked, Dataset::Info *aDatasetInfo) const; static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; static constexpr uint16_t kPingPayloadMaxLength = 512; @@ -389,6 +409,8 @@ private: static constexpr uint16_t kInstallCodeMaxSize = 255; static constexpr uint16_t kCommissionerCertMaxLength = 1024; static constexpr uint16_t kBufferReserve = 2048 / (kBufferSize - sizeof(otMessageBuffer)) + 1; + static constexpr uint8_t kServiceNameMaxLength = OT_TCAT_SERVICE_NAME_MAX_LENGTH; + static constexpr uint8_t kApplicationLayerMaxCount = OT_TCAT_APPLICATION_LAYER_MAX_COUNT; JoinerPskd mJoinerPskd; const VendorInfo *mVendorInfo; @@ -396,11 +418,9 @@ private: Callback mAppDataReceiveCallback; CertificateAuthorizationField mCommissionerAuthorizationField; CertificateAuthorizationField mDeviceAuthorizationField; - TcatApplicationProtocol mCurrentApplicationProtocol; NetworkName mCommissionerNetworkName; NetworkName mCommissionerDomainName; ExtendedPanId mCommissionerExtendedPanId; - char mCurrentServiceName[OT_TCAT_MAX_SERVICE_NAME_LENGTH + 1]; State mState; bool mCommissionerHasNetworkName : 1; bool mCommissionerHasDomainName : 1; @@ -409,6 +429,8 @@ private: bool mPskdVerified : 1; bool mPskcVerified : 1; bool mInstallCodeVerified : 1; + bool mIsCommissioned : 1; + bool mApplicationResponsePending : 1; friend class Ble::BleSecure; }; @@ -429,9 +451,9 @@ typedef UintTlvInfo Respons struct DeviceTypeAndStatus { uint8_t mRsv : 1; - bool mMultiradioSupport : 1; - bool mStoresActiveOpertonalDataset : 1; - bool mIsCommisionned : 1; + bool mMultiRadioSupport : 1; + bool mStoresActiveOperationalDataset : 1; + bool mIsCommissioned : 1; bool mThreadNetworkActive : 1; bool mIsBorderRouter : 1; bool mRxOnWhenIdle : 1; diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp index 392b4a2ea..94c03f06d 100644 --- a/src/core/radio/ble_secure.cpp +++ b/src/core/radio/ble_secure.cpp @@ -27,6 +27,7 @@ */ #include "ble_secure.hpp" +#include "common/error.hpp" #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE @@ -161,9 +162,27 @@ void BleSecure::Disconnect(void) IgnoreError(otPlatBleGapDisconnect(&GetInstance())); } + // Update advertisement + IgnoreError(NotifyAdvertisementChanged()); + mConnectCallback.InvokeIfSet(&GetInstance(), false, false); } +Error BleSecure::NotifyAdvertisementChanged(void) +{ + Error error = kErrorNone; + uint16_t advertisementLen = 0; + uint8_t *advertisementData = nullptr; + + VerifyOrExit(mBleState == kAdvertising); + SuccessOrExit(error = otPlatBleGetAdvertisementBuffer(&GetInstance(), &advertisementData)); + SuccessOrExit(error = mTcatAgent.GetAdvertisementData(advertisementLen, advertisementData)); + SuccessOrExit(error = otPlatBleGapAdvUpdateData(&GetInstance(), advertisementData, advertisementLen)); + +exit: + return error; +} + void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd) { static_assert(static_cast(MeshCoP::JoinerPskd::kMaxLength) <= @@ -207,14 +226,22 @@ exit: return error; } -Error BleSecure::SendApplicationTlv(uint8_t *aBuf, uint16_t aLength) +Error BleSecure::SendApplicationTlv(MeshCoP::TcatAgent::TcatApplicationProtocol aTcatApplicationProtocol, + uint8_t *aBuf, + uint16_t aLength) { Error error = kErrorNone; + + VerifyOrExit((aTcatApplicationProtocol != MeshCoP::TcatAgent::kApplicationProtocolStatus && + aTcatApplicationProtocol != MeshCoP::TcatAgent::kApplicationProtocolResponse) || + mTcatAgent.GetApplicationResponsePending(), + error = kErrorRejected); + if (aLength > Tlv::kBaseTlvMaxLength) { ot::ExtendedTlv tlv; - tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData); + tlv.SetType(static_cast(aTcatApplicationProtocol)); tlv.SetLength(aLength); SuccessOrExit(error = Send(reinterpret_cast(&tlv), sizeof(tlv))); } @@ -222,12 +249,19 @@ Error BleSecure::SendApplicationTlv(uint8_t *aBuf, uint16_t aLength) { ot::Tlv tlv; - tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData); + tlv.SetType(static_cast(aTcatApplicationProtocol)); tlv.SetLength((uint8_t)aLength); SuccessOrExit(error = Send(reinterpret_cast(&tlv), sizeof(tlv))); } - error = Send(aBuf, aLength); + SuccessOrExit(error = Send(aBuf, aLength)); + + if (aTcatApplicationProtocol == MeshCoP::TcatAgent::kApplicationProtocolStatus || + aTcatApplicationProtocol == MeshCoP::TcatAgent::kApplicationProtocolResponse) + { + mTcatAgent.NotifyApplicationResponseSent(); + } + exit: return error; } @@ -390,7 +424,7 @@ void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength) if (!mTlvMode) { SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength)); - mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, 0, OT_TCAT_APPLICATION_PROTOCOL_NONE, ""); + mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, 0, OT_TCAT_APPLICATION_PROTOCOL_NONE); IgnoreError(mReceivedMessage->SetLength(0)); } else @@ -476,7 +510,7 @@ void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength) { mReceivedMessage->SetOffset((uint16_t)offset); mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, (int32_t)offset, - OT_TCAT_APPLICATION_PROTOCOL_NONE, ""); + OT_TCAT_APPLICATION_PROTOCOL_NONE); } SuccessOrExit(mReceivedMessage->SetLength(0)); // also sets the offset to 0 diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index 978129f7f..6c46664c3 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -221,14 +221,19 @@ public: /** * Sends a secure BLE data packet containing a TCAT Send Application Data TLV. * - * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. - * @param[in] aLength A number indicating the length of the data buffer. + * @param[in] aApplicationProtocol An application protocol the data is directed to. + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT application TLV. + * @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. + * @retval kErrorNone Successfully sent data. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * @retval kErrorRejected Application protocol is response with data or status but no response is + * pending. */ - Error SendApplicationTlv(uint8_t *aBuf, uint16_t aLength); + Error SendApplicationTlv(MeshCoP::TcatAgent::TcatApplicationProtocol aApplicationProtocol, + uint8_t *aBuf, + uint16_t aLength); /** * Sends all remaining bytes in the send buffer. @@ -276,6 +281,14 @@ public: */ bool GetInstallCodeVerifyStatus(void) const { return mTcatAgent.GetInstallCodeVerifyStatus(); } + /** + * @brief Notifies the BLE layer that the BLE advertisement data should be updated. + * + * @retval kErrorNone Successfully updated. + * @return kErrorFailed Update failed. + */ + Error NotifyAdvertisementChanged(void); + private: enum BleState : uint8_t { diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 0cc98f991..8d769625a 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -35,6 +35,7 @@ #include "instance/instance.hpp" #include "openthread/platform/toolchain.h" +#include "radio/ble_secure.hpp" #include "utils/static_counter.hpp" namespace ot { @@ -353,6 +354,10 @@ void Mle::SetRole(DeviceRole aRole) break; } +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + IgnoreError(Get().NotifyAdvertisementChanged()); +#endif + // If the previous state is disabled, the parent can be in kStateRestored. if (!IsChild() && oldRole != kRoleDisabled) { diff --git a/src/posix/platform/ble.cpp b/src/posix/platform/ble.cpp index 23ebf0d12..a06d6a043 100644 --- a/src/posix/platform/ble.cpp +++ b/src/posix/platform/ble.cpp @@ -102,6 +102,14 @@ otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementDat return OT_ERROR_NONE; } +otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + bool otPlatBleSupportsMultiRadio(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); diff --git a/tests/gtest/fake_platform.cpp b/tests/gtest/fake_platform.cpp index cc568970b..efc9cebbb 100644 --- a/tests/gtest/fake_platform.cpp +++ b/tests/gtest/fake_platform.cpp @@ -536,6 +536,8 @@ bool otPlatBleSupportsMultiRadio(otInstance *) { return false; } otError otPlatBleGapAdvSetData(otInstance *, uint8_t *, uint16_t) { return OT_ERROR_NONE; } +otError otPlatBleGapAdvUpdateData(otInstance *, uint8_t *, uint16_t) { return OT_ERROR_NONE; } + OT_TOOL_WEAK otError otPlatRadioAddCalibratedPower(otInstance *, uint8_t, int16_t, const uint8_t *, uint16_t) { return OT_ERROR_NONE; diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index 8b9968d34..b16f86a52 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -266,7 +266,7 @@ proc fail {message} { error $message } -proc spawn_tcat_client_for_node {id} { +proc spawn_tcat_client_for_node {id {cert_path "tools/tcat_ble_client/auth"}} { global tcat_ids global spawn_id @@ -275,7 +275,7 @@ proc spawn_tcat_client_for_node {id} { send "tcat start\n" expect_line "Done" - spawn python "tools/tcat_ble_client/bbtc.py" --simulation $id --cert_path "tools/tcat_ble_client/auth" + spawn python "tools/tcat_ble_client/bbtc.py" --simulation $id --cert_path $cert_path expect_line "Done" set tcat_ids($id) $spawn_id diff --git a/tests/scripts/expect/cli-tcat-decommission.exp b/tests/scripts/expect/cli-tcat-decommission.exp index 302e8db08..cfd5e589c 100755 --- a/tests/scripts/expect/cli-tcat-decommission.exp +++ b/tests/scripts/expect/cli-tcat-decommission.exp @@ -31,7 +31,19 @@ source "tests/scripts/expect/_common.exp" spawn_node 1 "cli" -spawn_tcat_client_for_node 1 +spawn_tcat_client_for_node 1 tools/tcat_ble_client/auth-cert/CommCert2 + +send "commission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x10" + +send "random_challenge\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" + +send "present_hash pskd JJJJJJ\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x00" send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" @@ -41,6 +53,41 @@ send "thread start\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" +dispose_tcat_client 1 + +switch_node 1 +send "tcat stop\n" +expect_line "Done" + +spawn_tcat_client_for_node 1 + +send "commission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x08" + +send "get_dataset\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t106" + +send "decommission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" + +dispose_tcat_client 1 + +switch_node 1 +send "tcat stop\n" +expect_line "Done" + +spawn_tcat_client_for_node 1 + +send "commission\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x00" + +send "get_dataset\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t106" + send "decommission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" diff --git a/tests/scripts/expect/cli-tcat-hashes.exp b/tests/scripts/expect/cli-tcat-hashes.exp index 90540e44b..631235f37 100755 --- a/tests/scripts/expect/cli-tcat-hashes.exp +++ b/tests/scripts/expect/cli-tcat-hashes.exp @@ -31,11 +31,11 @@ source "tests/scripts/expect/_common.exp" spawn_node 1 "cli" -spawn_tcat_client_for_node 1 +spawn_tcat_client_for_node 1 tools/tcat_ble_client/auth-cert/CommCert2 send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" -expect_line "\tVALUE:\t0x00" +expect_line "\tVALUE:\t0x10" send "random_challenge\n" expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" @@ -46,13 +46,17 @@ expect_line "Requested hash is valid." expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" expect_line "\tLEN:\t32" +send "present_hash pskd AAAA\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x07" + send "present_hash pskd JJJJJJ\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" -send "present_hash pskd AAAA\n" +send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" -expect_line "\tVALUE:\t0x07" +expect_line "\tVALUE:\t0x00" send "present_hash install InstallCode\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp index 8296ee809..b483ebebc 100755 --- a/tests/scripts/expect/cli-tcat.exp +++ b/tests/scripts/expect/cli-tcat.exp @@ -39,7 +39,7 @@ expect_line "\tVALUE:\t0x06" send "get_dataset\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" -expect_line "\tVALUE:\t0x04" +expect_line "\tVALUE:\t0x06" send "get_comm_cert\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" @@ -94,6 +94,36 @@ expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" expect_line "\tLEN:\t9" expect_line "\tVALUE:\t0x64756d6d795f75726c" +send "get_apps\n" +expect_line "\tApplication 1 is UDP service: echo" +expect_line "\tApplication 2 is TCP service: discard" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t15" +expect_line "\tVALUE:\t0x89046563686f8a0764697363617264" + +send "appdata1 1122334455667788\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0x1122334455667788" + +send "appdata2 1122334455667788\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x00" + +send "appdata3 1122334455667788\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x01" + +send "appdata4 1122334455667788\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x01" + +send "vendor_data 74657874\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t4" +expect_line "\tVALUE:\t0x74657874" + + dispose_tcat_client 1 switch_node 1 diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 4fb5a1f38..6e313dae6 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -858,6 +858,14 @@ otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementDat return OT_ERROR_NONE; } +otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index 2305365d5..bf500d158 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -107,7 +107,102 @@ class HelloCommand(BleCommand): return 'Send round trip "Hello world!" message.' def prepare_data(self, args, context): - return TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + return TLV(TcatTLVType.VENDOR_APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + + +class GetApplicationLayersCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Getting application layers....' + + def get_help_string(self) -> str: + return 'Get supported application layer service names from device.' + + def prepare_data(self, args, context): + return TLV(TcatTLVType.GET_APPLICATION_LAYERS.value, bytes()).to_bytes() + + def process_response(self, tlv_response, context): + if tlv_response.type == TcatTLVType.RESPONSE_W_PAYLOAD.value: + payload = tlv_response.value + i = 0 + print('Service names:') + while payload: + tlv_application = TLV.from_bytes(payload) + payload = payload[2 + len(tlv_application.value):] + i += 1 + if (tlv_application.type == TcatTLVType.SERVICE_NAME_UDP.value): + print(f"\tApplication {i} is UDP service: {tlv_application.value.decode('ascii')}") + elif (tlv_application.type == TcatTLVType.SERVICE_NAME_TCP.value): + print(f"\tApplication {i} is TCP service: {tlv_application.value.decode('ascii')}") + else: + print('\tUnknown service type.') + else: + print('Dataset extraction error.') + + +class SendApplicationData1(BleCommand): + + def get_log_string(self) -> str: + return 'Sending data to application layer 1....' + + def get_help_string(self) -> str: + return 'Send hex encoded data to application layer 1.' + + def prepare_data(self, args, context): + payload = bytes.fromhex(args[0]) + return TLV(TcatTLVType.APPLICATION_DATA_1.value, payload).to_bytes() + + +class SendApplicationData2(BleCommand): + + def get_log_string(self) -> str: + return 'Sending data to application layer 2....' + + def get_help_string(self) -> str: + return 'Send hex encoded data to application layer 2.' + + def prepare_data(self, args, context): + payload = bytes.fromhex(args[0]) + return TLV(TcatTLVType.APPLICATION_DATA_2.value, payload).to_bytes() + + +class SendApplicationData3(BleCommand): + + def get_log_string(self) -> str: + return 'Sending data to application layer 3....' + + def get_help_string(self) -> str: + return 'Send hex encoded data to application layer 3.' + + def prepare_data(self, args, context): + payload = bytes.fromhex(args[0]) + return TLV(TcatTLVType.APPLICATION_DATA_3.value, payload).to_bytes() + + +class SendApplicationData4(BleCommand): + + def get_log_string(self) -> str: + return 'Sending data to application layer 4....' + + def get_help_string(self) -> str: + return 'Send hex encoded data to application layer 4.' + + def prepare_data(self, args, context): + payload = bytes.fromhex(args[0]) + return TLV(TcatTLVType.APPLICATION_DATA_4.value, payload).to_bytes() + + +class SendVendorData(BleCommand): + + def get_log_string(self) -> str: + return 'Sending data to vendor specific application layer....' + + def get_help_string(self) -> str: + return 'Send hex encoded data to vendor specific application layer.' + + def prepare_data(self, args, context): + payload = bytes.fromhex(args[0]) + return TLV(TcatTLVType.VENDOR_APPLICATION.value, payload).to_bytes() class CommissionCommand(BleCommand): diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 9d73411a5..9c6ad353a 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -33,7 +33,8 @@ from cli.base_commands import (DisconnectCommand, HelpCommand, HelloCommand, Com ExtractDatasetCommand, GetCommissionerCertificate, GetDeviceIdCommand, GetPskdHash, GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand, GetRandomNumberChallenge, ThreadStateCommand, ScanCommand, PresentHash, - DiagnosticTlvsCommand) + DiagnosticTlvsCommand, GetApplicationLayersCommand, SendVendorData, + SendApplicationData1, SendApplicationData2, SendApplicationData3, SendApplicationData4) from .tlv_commands import TlvCommand from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset @@ -49,6 +50,12 @@ class CLI: self._commands = { 'help': HelpCommand(), 'hello': HelloCommand(), + 'get_apps': GetApplicationLayersCommand(), + 'appdata1': SendApplicationData1(), + 'appdata2': SendApplicationData2(), + 'appdata3': SendApplicationData3(), + 'appdata4': SendApplicationData4(), + 'vendor_data': SendVendorData(), 'commission': CommissionCommand(), 'decommission': DecommissionCommand(), 'disconnect': DisconnectCommand(), diff --git a/tools/tcat_ble_client/cli/command.py b/tools/tcat_ble_client/cli/command.py index e6b819b10..da858f358 100644 --- a/tools/tcat_ble_client/cli/command.py +++ b/tools/tcat_ble_client/cli/command.py @@ -90,10 +90,7 @@ class CommandResultTLV(CommandResult): else: print(f'\tTYPE:\tunknown: {hex(tlv.type)} ({tlv.type})') print(f'\tLEN:\t{len(tlv.value)}') - if tlv_type == TcatTLVType.APPLICATION: - print(f'\tVALUE:\t{tlv.value.decode("ascii")}') - else: - print(f'\tVALUE:\t0x{tlv.value.hex()}') + print(f'\tVALUE:\t0x{tlv.value.hex()}') class CommandResultNone(CommandResult): diff --git a/tools/tcat_ble_client/dataset/dataset_entries.py b/tools/tcat_ble_client/dataset/dataset_entries.py index 0ffd6ea07..c1671f3db 100644 --- a/tools/tcat_ble_client/dataset/dataset_entries.py +++ b/tools/tcat_ble_client/dataset/dataset_entries.py @@ -22,6 +22,8 @@ from abc import ABC, abstractmethod from tlv.dataset_tlv import MeshcopTlvType from tlv.tlv import TLV +MASK_48_BITS = 0xFFFFFFFFFFFF + class DatasetEntry(ABC): @@ -66,13 +68,13 @@ class ActiveTimestamp(DatasetEntry): def set(self, args: List[str]): if len(args) == 0: raise ValueError('No argument for ActiveTimestamp') - self._seconds = int(args[0]) + self.seconds = int(args[0]) def set_from_tlv(self, tlv: TLV): (value,) = struct.unpack('>Q', tlv.value) self.ubit = value & 0x1 self.ticks = (value >> 1) & 0x7FFF - self.seconds = (value >> 16) & 0xFFFF + self.seconds = (value >> 16) & MASK_48_BITS def to_tlv(self): value = (self.seconds << 16) | (self.ticks << 1) | self.ubit @@ -92,7 +94,7 @@ class PendingTimestamp(DatasetEntry): def set(self, args: List[str]): if len(args) == 0: raise ValueError('No argument for PendingTimestamp') - self._seconds = int(args[0]) + self.seconds = int(args[0]) def set_from_tlv(self, tlv: TLV): (value,) = struct.unpack('>Q', tlv.value) @@ -476,6 +478,30 @@ class ChannelMaskEntry(DatasetEntry): return TLV.from_bytes(tlv) +class WakeupChannel(DatasetEntry): + + def __init__(self): + super().__init__(MeshcopTlvType.WAKEUP_CHANNEL) + self.length = 3 # spec defined + self.channel_page = 0 + self.channel = 0 + + def set(self, args: List[str]): + if len(args) == 0: + raise ValueError('No argument for WakeupChannel') + channel = int(args[0]) + self.channel = channel + + def set_from_tlv(self, tlv: TLV): + self.channel = int.from_bytes(tlv.value[1:3], byteorder='big') + self.channel_page = tlv.value[0] + + def to_tlv(self): + tlv = struct.pack('>BBB', self.type.value, self.length, self.channel_page) + tlv += struct.pack('>H', self.channel) + return TLV.from_bytes(tlv) + + ENTRY_CLASSES = { MeshcopTlvType.ACTIVETIMESTAMP: ActiveTimestamp, MeshcopTlvType.PENDINGTIMESTAMP: PendingTimestamp, @@ -488,7 +514,8 @@ ENTRY_CLASSES = { MeshcopTlvType.CHANNEL: Channel, MeshcopTlvType.PSKC: Pskc, MeshcopTlvType.SECURITYPOLICY: SecurityPolicy, - MeshcopTlvType.CHANNELMASK: ChannelMask + MeshcopTlvType.CHANNELMASK: ChannelMask, + MeshcopTlvType.WAKEUP_CHANNEL: WakeupChannel } diff --git a/tools/tcat_ble_client/poetry.lock b/tools/tcat_ble_client/poetry.lock index 72a06109f..330999350 100644 --- a/tools/tcat_ble_client/poetry.lock +++ b/tools/tcat_ble_client/poetry.lock @@ -1,14 +1,14 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "async-timeout" -version = "4.0.2" +version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [package.dependencies] @@ -67,54 +67,61 @@ files = [ [[package]] name = "dbus-fast" -version = "1.90.1" +version = "1.95.2" description = "A faster version of dbus-next" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "dbus_fast-1.90.1-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:657f9f292f770b50c755bc9cc3607ec0901a607d6d6e31c67aa953b73b31d66a"}, - {file = "dbus_fast-1.90.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:229cb2dc0942dfe36ce4f5a49cacb7f39ae05527c6ccec66b9a670ca7a02129a"}, - {file = "dbus_fast-1.90.1-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:4978d6dca49b778c426f279f014554e29ece6bfc7530fa8ab9d258f068e5954e"}, - {file = "dbus_fast-1.90.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2161851a1f90a1c2fe064d1870b04bdca0033b42fdc97cc7d5132637227ac915"}, - {file = "dbus_fast-1.90.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0012799b154fb6b066ff6948f5edd0c6bf8655fca6f3578fc78598334f9f978b"}, - {file = "dbus_fast-1.90.1-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:0b97748928e3e56bc98f292be894d1d3c2a4acc58555795a3aa3b74769b96542"}, - {file = "dbus_fast-1.90.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2baa2e1053ded0a5ccdefc651ee8fcd09d6f4f864b9f301363dbf0545f95a89"}, - {file = "dbus_fast-1.90.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0262ab1d3d07ac892645d4cec54daabd3ba4a096ca4c4b2e9f09abd1819ca663"}, - {file = "dbus_fast-1.90.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c39a352a893923d255031d15fa005ac5f5df2d1195729f206fc79a95b219daed"}, - {file = "dbus_fast-1.90.1-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:bd012a0ed7e6479bcc5b0efe91a45c3abb3e0e4e371a28c0f3c347cb9baffe8a"}, - {file = "dbus_fast-1.90.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3e895adfa89a6c08d23fd22707bd5ea8a301579f3a6ff8bd33df1e94ea16e8a"}, - {file = "dbus_fast-1.90.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a94806e9990b6a7fd4896ee2a979c20af4e5ba76bfddda55a7a79f4da268b51f"}, - {file = "dbus_fast-1.90.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae0b95a08db0a7e38452926c8d5964d41e5f20d8c89fd00b8913ec4f1908f7bd"}, - {file = "dbus_fast-1.90.1-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:3ce5153accbbb7fc2aeab055f46d5611c4c57978d43feba9257fba53c338ebbe"}, - {file = "dbus_fast-1.90.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3b05b68a7be76df3e4833c72d9fadf1b934359ed3fabd69ed205eaa18d132a6"}, - {file = "dbus_fast-1.90.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d407eb9a3581ee5cc047a664d3d15e9846e5a1c0b3565922c72ffe15fea590f2"}, - {file = "dbus_fast-1.90.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f06e4cbf560e682930f0d23ea93b1887c40db00b98ee4d6a207aa2a616851e"}, - {file = "dbus_fast-1.90.1-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:28a4ee863b4a42351afb38ce9432850922dc2f0d9d9863f98fcb47e23b710572"}, - {file = "dbus_fast-1.90.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd6090c794ca3404702b59cb4dc92f674e26a15bb79a9d5ae0236658c10cac5"}, - {file = "dbus_fast-1.90.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9e7be16e96bcad521f2c045561799fc486047b6e1ce071c35a5cea36a9ed19f4"}, - {file = "dbus_fast-1.90.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:01f582d9c3f24e1721f3dd9a62c7a558c7c8406752eb042738623b9d89f454e9"}, - {file = "dbus_fast-1.90.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:085fd80c7ea3e41a2ac32e419611036a042593778fecb4d92526a22fd2be2c0b"}, - {file = "dbus_fast-1.90.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0dbffd42875d31d0c7e2407eff685d5cb2dfa7c0448079c96ef2cec0afe8be"}, - {file = "dbus_fast-1.90.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:04eaaea336059909b5cb51c897fc343e038e80f698a049b1bf3002d162d4fec7"}, - {file = "dbus_fast-1.90.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc215dbccb798df07ee6104b984ae09c159ed3a633df915c3c6dd9df97af753"}, - {file = "dbus_fast-1.90.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:74f7afaf780fbbc7f39cc8167ec585f8e7ce140d214768f5c296c2b03d23e571"}, - {file = "dbus_fast-1.90.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f12c7bcec416e28ba1f741e43ad2eef7eb3c677838208cf6a801ced711b508c"}, - {file = "dbus_fast-1.90.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c0811b7bdfdce40072fd7c29f0c7e0145982b981bb068efd79a5e43ef14150e5"}, - {file = "dbus_fast-1.90.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1f00197e08c7c5837861624fd1afb8e1d8331d4e28e8d2ff01dc17ac2305ae2"}, - {file = "dbus_fast-1.90.1.tar.gz", hash = "sha256:eff98b45443681bd8876bbb1444b35112d62e8d12157f004d88ebe5f0481d5b7"}, + {file = "dbus_fast-1.95.2-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:69f19fb94ac714b917c51fcc329b51695f085f779841edd6e429170f1f073f47"}, + {file = "dbus_fast-1.95.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c71e84a7ce3b050745dfb2bcd32ade891ecf6f4b06d3baa4dfe30ad09720a0be"}, + {file = "dbus_fast-1.95.2-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:3ffcd1c999599da7806d7887d5ce1e9f0d814aa35af92daecab542c9b70794c1"}, + {file = "dbus_fast-1.95.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:85739b90e6f983494c3c7187d4daaa50f4f4369aa1e87e876066de97ca1bcd58"}, + {file = "dbus_fast-1.95.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e2c107caefa186919169e07a852edea3d931dfb6112584fa2c6e5653d91c0d2d"}, + {file = "dbus_fast-1.95.2-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:99902ca6c10368492fdf5a28321d86c7e92e50fd742f0163599602c442232d29"}, + {file = "dbus_fast-1.95.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3241252a744b0960e7eb9dcad2892af2bc695b4f47636bab2f17f6012d914ce"}, + {file = "dbus_fast-1.95.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f52ef5edef41a0ed29645d7c6e1ee89b5e8f5e5c1ba9901699dcec9cfaf8d961"}, + {file = "dbus_fast-1.95.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9706e10a6c24d4c03d28b1631e3882009964d5207a86cae2b42466635d7546be"}, + {file = "dbus_fast-1.95.2-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:3277944b418063ad051e8e49144151962691188b972f1fbca7af39fdef4f8a47"}, + {file = "dbus_fast-1.95.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab158bcf86c4b2e03c2f3453738a61de83c82cbed11f23331ccc9a9aba6d5b1"}, + {file = "dbus_fast-1.95.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:17d743d13dbde0691adb98f1a6c87f0cf5617a4c9169b4820972dc8869095c6b"}, + {file = "dbus_fast-1.95.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aefa695088405d313c703790ac503ad0b2ac1e4807393f1f02e410b6984aedd0"}, + {file = "dbus_fast-1.95.2-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:ffd3665a6fbe318aadac998ae117b19214e0782397c86ab47792a120979783a6"}, + {file = "dbus_fast-1.95.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43093d8e42342841c7ac69e236370c31949c35b659032888dae8b3af1fa35e9e"}, + {file = "dbus_fast-1.95.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f4b26849254fafb11656fc8c10f8bce67deab683694e0fc7b56edd891d4e118"}, + {file = "dbus_fast-1.95.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:331c4a26e010fd8ad3168b9680f39a7d0765507d3aadcbe5f250da0474877f14"}, + {file = "dbus_fast-1.95.2-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:7aee30619c79334980a25a1e94299a751b3f276644ec0a69c76bfe57be184e7a"}, + {file = "dbus_fast-1.95.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c5d344e3fe3c4b16593fad70448c66ca3130c8c692c74a921c15f18650581a7"}, + {file = "dbus_fast-1.95.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9180d4d39d8688b9d8d43f427f0f2883354fa9df18a1f46d14f1762b82249d1f"}, + {file = "dbus_fast-1.95.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e19173e65967581186666a47cd0af06111c7e957fb529198e532b35a47171460"}, + {file = "dbus_fast-1.95.2-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:fae11a8ed320013decd2bf6a982f010fff011bf935c4f32141e804ba7963719b"}, + {file = "dbus_fast-1.95.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f653085d9f96ed2e48434ba951cf4bf0229cc273a87b9799e7cf3a9a6612a56"}, + {file = "dbus_fast-1.95.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05d53e0db08de6b3ba43eb110e01b9e6264e7143849ea9772af45b15575c21b2"}, + {file = "dbus_fast-1.95.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e73b7dcbac5f418bd4b334ad112e40e8b3bc05c53a3fc451926450ce3bb805e"}, + {file = "dbus_fast-1.95.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:76fb9d11bd6ebe1f832350ec57ada352f096f24b5731e38bcbad86ab9b27190b"}, + {file = "dbus_fast-1.95.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26045c50bbd8c1674814759332333799b073216b2b2cac9e1cfebf757c7f926d"}, + {file = "dbus_fast-1.95.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:bd68b5e7d7c6cd10d804d072062100a4893ad0c19bdac03b53b1809c51a4e3c7"}, + {file = "dbus_fast-1.95.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea2692efe0d6d6d58fb427cd0ed53ab44ced0c2ada642b0c15fd139c60a706d6"}, + {file = "dbus_fast-1.95.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:33cee333a15241e516ee84aaabdd952dbb1a63f9e986028574451475e97a113f"}, + {file = "dbus_fast-1.95.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfd99cf624f769cf838e62d75ceb737b2866b1783a4f11a928cf38e4c906a5f"}, + {file = "dbus_fast-1.95.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b842e94140e39a196d4d83e02eddcbc461b906871ba02f1c5374055f2d628c47"}, + {file = "dbus_fast-1.95.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ca9968dd46b854e5c7b00be32cb273edaf17ddc9647811ff63ae3b9dca822d"}, + {file = "dbus_fast-1.95.2.tar.gz", hash = "sha256:3dd64c5cd362ceead6cc02603b6b4cbda58b2cbb6ec816a2f21b1901dfc3cb61"}, ] [[package]] name = "exceptiongroup" -version = "1.1.2" +version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, - {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] @@ -151,13 +158,13 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -254,13 +261,13 @@ pyobjc-core = ">=9.2" [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] diff --git a/tools/tcat_ble_client/tlv/dataset_tlv.py b/tools/tcat_ble_client/tlv/dataset_tlv.py index 4bd020c8d..ec12b9a0d 100644 --- a/tools/tcat_ble_client/tlv/dataset_tlv.py +++ b/tools/tcat_ble_client/tlv/dataset_tlv.py @@ -67,6 +67,8 @@ class MeshcopTlvType(Enum): PERIOD = 55 SCAN_DURATION = 56 ENERGY_LIST = 57 + THREAD_DOMAIN_NAME = 59 + WAKEUP_CHANNEL = 74 DISCOVERYREQUEST = 128 DISCOVERYRESPONSE = 129 JOINERADVERTISEMENT = 241 diff --git a/tools/tcat_ble_client/tlv/tcat_tlv.py b/tools/tcat_ble_client/tlv/tcat_tlv.py index e78e23c96..ce0604a1f 100644 --- a/tools/tcat_ble_client/tlv/tcat_tlv.py +++ b/tools/tcat_ble_client/tlv/tcat_tlv.py @@ -47,7 +47,14 @@ class TcatTLVType(Enum): GET_ACTIVE_DATASET = 0x40 GET_DIAGNOSTIC_TLVS = 0x26 DECOMMISSION = 0x60 - APPLICATION = 0x82 + GET_APPLICATION_LAYERS = 0x80 + APPLICATION_DATA_1 = 0x81 + APPLICATION_DATA_2 = 0x82 + APPLICATION_DATA_3 = 0x83 + APPLICATION_DATA_4 = 0x84 + SERVICE_NAME_UDP = 0x89 + SERVICE_NAME_TCP = 0x8A + VENDOR_APPLICATION = 0x9F THREAD_START = 0x27 THREAD_STOP = 0x28