diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp index ab29bd0fdeef..3493d410bf2b 100644 --- a/src/cli/cli_tcat.cpp +++ b/src/cli/cli_tcat.cpp @@ -93,6 +93,19 @@ otTcatDeviceId sVendorDeviceIds[OT_TCAT_DEVICE_ID_MAX]; const char kPskdVendor[] = "JJJJJJ"; const char kUrl[] = "dummy_url"; +static bool IsDeviceIdSet() +{ + for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + { + if (vendorDeviceId.mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + { + return true; + } + } + + return false; +} + static void HandleBleSecureReceive(otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, @@ -210,6 +223,11 @@ template <> otError Tcat::Process(Arg aArgs[]) mVendorInfo.mPskdString = kPskdVendor; mVendorInfo.mProvisioningUrl = kUrl; + if (IsDeviceIdSet()) + { + mVendorInfo.mDeviceIds = sVendorDeviceIds; + } + 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)); diff --git a/src/core/common/code_utils.hpp b/src/core/common/code_utils.hpp index 647042a9c89c..432d07d9fb5e 100644 --- a/src/core/common/code_utils.hpp +++ b/src/core/common/code_utils.hpp @@ -172,4 +172,22 @@ */ static inline void IgnoreError(otError aError) { OT_UNUSED_VARIABLE(aError); } +/** + * Executes the `statement` and performs action on success. + * + * Checks for the specified status, which is expected to commonly be successful, and execute action if + * status was successful. + * + * @param[in] aStatus A scalar status to be evaluated against zero (0). + * + */ +#define OnSuccess(...) \ + do \ + { \ + if ((OT_FIRST_ARG(__VA_ARGS__)) == 0) \ + { \ + OT_SECOND_ARG(__VA_ARGS__); \ + } \ + } while (false) + #endif // CODE_UTILS_HPP_ diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 49470828dc07..b0d939fb4d0f 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -414,17 +414,24 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg error = HandleDecomission(); break; case kTlvPing: - error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length); - if (error == kErrorNone) - { - response = true; - } + OnSuccess(error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length), response = true); + break; + case kTlvGetNetworkName: + OnSuccess(error = HandleGetNetworkName(aOutgoingMessage), response = true); + break; + case kTlvGetDeviceId: + OnSuccess(error = HandleGetDeviceId(aOutgoingMessage), response = true); + break; + case kTlvGetExtendedPanID: + OnSuccess(error = HandleGetExtPanId(aOutgoingMessage), response = true); + break; + case kTlvGetProvisioningURL: + OnSuccess(error = HandleGetProvisioningUrl(aOutgoingMessage), response = true); break; default: error = kErrorInvalidCommand; } } - if (!response) { StatusCode statusCode; @@ -540,6 +547,95 @@ Error TcatAgent::HandlePing(const Message &aIncomingMessage, return error; } +Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage) +{ + Error error = kErrorNone; + const char *name = otThreadGetNetworkName(&GetInstance()); + size_t length = strlen(name); + ot::Tlv tlv; + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + VerifyOrExit(length > 0, error = kErrorInvalidState); + + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(static_cast(length)); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + SuccessOrExit(error = aOutgoingMessage.AppendBytes(name, length)); + +exit: + return error; +} + +Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage) +{ + uint8_t deviceId[8]; + uint8_t length = 0; + ot::Tlv tlv; + Error error = kErrorNone; + + if (mVendorInfo->mDeviceIds != nullptr) + { + // If array is not null return first set device ID. + for (uint8_t i = 0; mVendorInfo->mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + { + length = mVendorInfo->mDeviceIds->mDeviceIdLen; + memcpy(deviceId, mVendorInfo->mDeviceIds[i].mDeviceId, length); + break; + } + } + + if (length == 0) + { + // If array is null or contains only empty entries return EUI64. + otPlatRadioGetIeeeEui64(&GetInstance(), deviceId); + length = 8; + } + + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(static_cast(length)); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + SuccessOrExit(error = aOutgoingMessage.AppendBytes(deviceId, length)); + +exit: + return error; +} + +Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage) +{ + Error error = kErrorNone; + const otExtendedPanId *name = + otThreadGetExtendedPanId(&GetInstance()); // is it possible to check if i't not set ask during review. + uint16_t length = OT_EXT_PAN_ID_SIZE; + ot::Tlv tlv; + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(static_cast(length)); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + SuccessOrExit(error = aOutgoingMessage.AppendBytes(name, length)); + +exit: + return error; +} + +Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage) +{ + Error error = kErrorNone; + size_t length = strlen(mVendorInfo->mProvisioningUrl); + ot::Tlv tlv; + + VerifyOrExit(length != 0, error = kErrorInvalidState); + + tlv.SetType(kTlvResponseWithPayload); + tlv.SetLength(static_cast(length)); + SuccessOrExit(error = aOutgoingMessage.Append(tlv)); + SuccessOrExit(error = aOutgoingMessage.AppendBytes(mVendorInfo->mProvisioningUrl, length)); + +exit: + return error; +} + Error TcatAgent::HandleStartThreadInterface(void) { Error error; diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 5d6d0dd9797e..16e041bc8104 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -168,6 +168,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable 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 @@ -354,6 +355,10 @@ class TcatAgent : public InstanceLocator, private NonCopyable Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength); Error HandleDecomission(void); Error HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, uint16_t aLength); + Error HandleGetNetworkName(Message &aOutgoingMessage); + Error HandleGetDeviceId(Message &aOutgoingMessage); + Error HandleGetExtPanId(Message &aOutgoingMessage); + Error HandleGetProvisioningUrl(Message &aOutgoingMessage); Error HandleStartThreadInterface(void); bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp index 9617cb39a3a6..53d344b89847 100755 --- a/tests/scripts/expect/cli-tcat.exp +++ b/tests/scripts/expect/cli-tcat.exp @@ -38,6 +38,11 @@ expect_line "Done" spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth" set py_client "$spawn_id" expect_line "Done" + +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x06" + send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" @@ -58,6 +63,26 @@ send "ping 512\n" expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" expect_line "\tLEN:\t512" +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t15" +expect_line "\tVALUE:\t0x4f70656e5468726561642d63363465" + +send "device_id\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0x18b4300000000001" + +send "ext_panid\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0xef1398c2fd504b67" + +send "provisioning_url\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t9" +expect_line "\tVALUE:\t0x64756d6d795f75726c" + send "exit\n" expect eof diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index bb0258ee27e8..b3b98407b117 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -105,6 +105,68 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) +class GetDeviceIdCommand(Command): + + def get_help_string(self) -> str: + return 'Get unique identifier for the TCAT device.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + data = (TLV(TcatTLVType.GET_DEVICE_ID.value, bytes()).to_bytes()) + response = await bless.send_with_resp(data) + if not response: + return + tlv_response = TLV.from_bytes(response) + return CommandResultTLV(tlv_response) + + +class GetExtPanIDCommand(Command): + + def get_help_string(self) -> str: + return 'Get a URL for an application suited to commission device.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + data = (TLV(TcatTLVType.GET_EXT_PAN_ID.value, bytes()).to_bytes()) + response = await bless.send_with_resp(data) + if not response: + return + tlv_response = TLV.from_bytes(response) + return CommandResultTLV(tlv_response) + + +class GetProvisioningUrlCommand(Command): + + def get_help_string(self) -> str: + return 'Get a URL for an application suited to commission device.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + data = (TLV(TcatTLVType.GET_PROVISIONING_URL.value, bytes()).to_bytes()) + response = await bless.send_with_resp(data) + if not response: + return + tlv_response = TLV.from_bytes(response) + print(f'Provisioning URL: "{str(tlv_response.value)}".') + return CommandResultTLV(tlv_response) + + +class GetNetworkNameCommand(Command): + + def get_help_string(self) -> str: + return 'Get the network name that is commissioned in the active dataset.' + + async def execute_default(self, args, context): + bless: BleStreamSecure = context['ble_sstream'] + data = (TLV(TcatTLVType.GET_NETWORK_NAME.value, bytes()).to_bytes()) + response = await bless.send_with_resp(data) + if not response: + return + tlv_response = TLV.from_bytes(response) + print(f'Network name: "{str(tlv_response.value)}".') + return CommandResultTLV(tlv_response) + + class PingCommand(Command): def get_help_string(self) -> str: diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 28c7fb9a1763..01e33bac31a5 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -29,7 +29,8 @@ import readline import shlex from ble.ble_stream_secure import BleStreamSecure -from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, PingCommand, +from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, GetDeviceIdCommand, + GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand, ThreadStateCommand, ScanCommand) from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset @@ -44,6 +45,10 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'hello': HelloCommand(), 'commission': CommissionCommand(), 'decommission': DecommissionCommand(), + 'device_id': GetDeviceIdCommand(), + 'ext_panid': GetExtPanIDCommand(), + 'provisioning_url': GetProvisioningUrlCommand(), + 'network_name': GetNetworkNameCommand(), 'ping': PingCommand(), 'dataset': DatasetCommand(), 'thread': ThreadStateCommand(), diff --git a/tools/tcat_ble_client/tlv/tcat_tlv.py b/tools/tcat_ble_client/tlv/tcat_tlv.py index a276cafb2007..7d26864ae787 100644 --- a/tools/tcat_ble_client/tlv/tcat_tlv.py +++ b/tools/tcat_ble_client/tlv/tcat_tlv.py @@ -31,8 +31,12 @@ class TcatTLVType(Enum): RESPONSE_W_STATUS = 0x01 RESPONSE_W_PAYLOAD = 0x02 + GET_NETWORK_NAME = 0x08 DISCONNECT = 0x09 PING = 0x0A + GET_DEVICE_ID = 0x0B + GET_EXT_PAN_ID = 0x0C + GET_PROVISIONING_URL = 0x0D ACTIVE_DATASET = 0x20 DECOMMISSION = 0x60 APPLICATION = 0x82