Files
openthread/tests/unit/test_tcat.cpp
T
Esko Dijk 926dc828d2 [tcat][ble] improve internal state management of ble simulation platform (#12913)
This adds sIsEnabled for more realistic BLE simulation platform behavior. Also
improves internal disconnection state management, per review comments.

Adds asynchronous calling of otPlatBleGapOnDisconnected() to avoid doing the
'disconnected' callback twice, per review comments. A unit test is extended to
verify that the callback happens once.

For test_platform.cpp 2 instances of '#ifdef' are fixed to '#if'.
2026-06-08 14:47:21 -07:00

895 lines
46 KiB
C++

/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "openthread-core-config.h"
#include "test_platform.h"
#include "test_util.h"
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
#include <openthread/ble_secure.h>
#include "cli/cli_dataset.hpp"
#include "radio/ble_secure.hpp"
#define OT_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"
#define OT_TCAT_PRIV_KEY \
"-----BEGIN EC PRIVATE KEY-----\n" \
"MHcCAQEEIIqKM1QTlNaquV74W6Viz/ggXoLqlPOP6LagSyaFO3oUoAoGCCqGSM49\n" \
"AwEHoUQDQgAE11h/4vKZXVXv+1GDZo066spItloTdpCi0bux0jvpQSHLdQBIc+40\n" \
"zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdg==\n" \
"-----END EC PRIVATE KEY-----\n"
#define OT_TCAT_TRUSTED_ROOT_CERTIFICATE \
"-----BEGIN CERTIFICATE-----\n" \
"MIICOzCCAeGgAwIBAgIJAKOc2hehOGoBMAoGCCqGSM49BAMCMHExJjAkBgNVBAMM\n" \
"HVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJlYWQg\n" \
"R3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQswCQYD\n" \
"VQQGEwJVUzAeFw0yNDA1MDMyMDAyMThaFw00NDA0MjgyMDAyMThaMHExJjAkBgNV\n" \
"BAMMHVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJl\n" \
"YWQgR3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQsw\n" \
"CQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGy850VBIPTkN3oL\n" \
"x++zIUsZk2k26w4fuieFz9oNvjdb5W14+Yf3mvGWsl4NHyLxqhmamVAR4h7zWRlZ\n" \
"0XyMVpKjYjBgMB4GA1UdEQQXMBWBE3RvbUB0aHJlYWRncm91cC5vcmcwDgYDVR0P\n" \
"AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF+rGyloiKHUtDGo\n" \
"hmHn52ZZ7fgZMAoGCCqGSM49BAMCA0gAMEUCIQCTq1qjPZs9fAJB6ppTXs588Pnu\n" \
"eVFOwC8bd//D99KiHAIgU84kwFHIyDvFqu6y+u1hFqBGsiuTmKwZ2PHhVe/xK1k=\n" \
"-----END CERTIFICATE-----\n"
#define COMM_NETWORK_NAME "OpenThread-c64e"
#define COMM_XPAN_ID {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe}
#define COMM_XPAN_ID_ALT {0xef, 0x13, 0x98, 0xc2, 0xfd, 0x50, 0x4b, 0x67}
namespace ot {
namespace MeshCoP {
static constexpr char kPskdVendor[] = "J01NM3";
static constexpr char kUrl[] = "dummy_url";
static constexpr char kDomainName[] = "DefaultDomain";
static constexpr char kNetworkName[] = COMM_NETWORK_NAME;
static constexpr char kWrongName[] = "WrongName";
static const uint8_t kExtPanId[8] = COMM_XPAN_ID;
static const uint8_t kExtPanIdAlt[8] = COMM_XPAN_ID_ALT;
static constexpr uint16_t kConnectionId = 0;
static constexpr int kCertificateThreadVersion = 2;
static constexpr int kCertificateAuthorizationField = 3;
static constexpr otTcatVendorInfo vendorInfo = {.mProvisioningUrl = kUrl, .mPskdString = kPskdVendor};
// TCAT command class bits for expressing any combination of classes in tests
static constexpr uint16_t kClassNone = 0;
static constexpr uint16_t kClassGeneral = 1 << TcatAgent::kGeneral;
static constexpr uint16_t kClassCommissioning = 1 << TcatAgent::kCommissioning;
static constexpr uint16_t kClassExtraction = 1 << TcatAgent::kExtraction;
static constexpr uint16_t kClassDecommissioning = 1 << TcatAgent::kDecommissioning;
static constexpr uint16_t kClassApplication = 1 << TcatAgent::kApplication;
// TCAT authorization fields
static const uint8_t kDeviceCert1AuthField[5] = {0x20, 0x01, 0x01, 0x01, 0x01};
static const uint8_t kDeviceCert2AuthField[5] = {0x20, 0x02, 0x03, 0x04, 0x24};
static const uint8_t kCommCert1AuthField[5] = {0x21, 0x01, 0x01, 0x01, 0x01};
static const uint8_t kCommCert2AuthField[5] = {0x21, 0x1F, 0x3F, 0x3F, 0x3F};
static const uint8_t kCommCert4AuthField[5] = {0x21, 0x21, 0x05, 0x09, 0x11};
static const uint8_t kCommCert5AuthField[5] = {0x21, 0x03, 0x02, 0x83, 0x41};
static const otOperationalDataset kFullDataset = {
.mActiveTimestamp =
{
.mSeconds = 1,
.mTicks = 0,
.mAuthoritative = false,
},
.mNetworkKey =
{
.m8 = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
},
.mNetworkName = {COMM_NETWORK_NAME},
.mExtendedPanId =
{
.m8 = COMM_XPAN_ID,
},
.mMeshLocalPrefix =
{
.m8 = {0xfd, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
},
.mPanId = 0x1234,
.mChannel = 11,
.mPskc =
{
.m8 = {0xc2, 0x3a, 0x76, 0xe9, 0x8f, 0x1a, 0x64, 0x83, 0x63, 0x9b, 0x1a, 0xc1, 0x27, 0x1e, 0x2e, 0x27},
},
.mSecurityPolicy =
{
.mRotationTime = 672,
.mObtainNetworkKeyEnabled = true,
.mNativeCommissioningEnabled = true,
.mRoutersEnabled = true,
.mExternalCommissioningEnabled = true,
},
.mChannelMask = 0x07fff800,
.mComponents =
{
.mIsActiveTimestampPresent = true,
.mIsNetworkKeyPresent = true,
.mIsNetworkNamePresent = true,
.mIsExtendedPanIdPresent = true,
.mIsMeshLocalPrefixPresent = true,
.mIsPanIdPresent = true,
.mIsChannelPresent = true,
.mIsPskcPresent = true,
.mIsSecurityPolicyPresent = true,
.mIsChannelMaskPresent = true,
},
};
static const otOperationalDataset kPartialDataset = {
.mNetworkKey =
{
.m8 = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
},
.mComponents =
{
.mIsActiveTimestampPresent = false,
.mIsNetworkKeyPresent = true,
.mIsNetworkNamePresent = false,
.mIsExtendedPanIdPresent = false,
.mIsMeshLocalPrefixPresent = false,
.mIsPanIdPresent = false,
.mIsChannelPresent = false,
.mIsPskcPresent = false,
.mIsSecurityPolicyPresent = false,
.mIsChannelMaskPresent = false,
},
};
static Dataset::Info sFullDataset, sPartialDataset;
static NetworkName sCommNetworkName, sCommDomainName;
static ExtendedPanId sCommExtPanId;
static TcatAgent::CertificateAuthorizationField sCommAuth, sDeviceAuth;
// Helper class to test BLE connection state.
class TestBleSecure
{
public:
TestBleSecure(void)
: mIsConnected(false)
, mIsBleConnectionOpen(false)
, mConnectCallbackCount(0)
{
}
void HandleBleSecureConnect(bool aConnected, bool aBleConnectionOpen)
{
mIsConnected = aConnected;
mIsBleConnectionOpen = aBleConnectionOpen;
mConnectCallbackCount++;
}
bool IsConnected(void) const { return mIsConnected; }
bool IsBleConnectionOpen(void) const { return mIsBleConnectionOpen; }
uint32_t GetConnectCallbackCount(void) const { return mConnectCallbackCount; }
void ResetConnectCallbackCount(void) { mConnectCallbackCount = 0; }
private:
bool mIsConnected;
bool mIsBleConnectionOpen;
uint32_t mConnectCallbackCount;
};
static void HandleBleSecureConnect(otInstance *aInstance, bool aConnected, bool aBleConnectionOpen, void *aContext)
{
OT_UNUSED_VARIABLE(aInstance);
static_cast<TestBleSecure *>(aContext)->HandleBleSecureConnect(aConnected, aBleConnectionOpen);
}
// test helper to validate that only classes set '1' in aCommandClassesBitmap are authorized and others not.
static bool CommandClassesAuthorized(const TcatAgent *aAgent, const uint16_t aCommandClassesBitmap)
{
bool validationResult = true;
static_assert(TcatAgent::kInvalid < 16, "kInvalid must be less than 16 to fit in uint16_t");
for (uint16_t i = TcatAgent::kGeneral; i <= TcatAgent::kInvalid; i++)
{
const bool isAuthorizedByAgent = aAgent->IsCommandClassAuthorized(static_cast<TcatAgent::CommandClass>(i));
const bool isAuthorizationExpected = (aCommandClassesBitmap & (1 << i)) != 0;
if (isAuthorizedByAgent != isAuthorizationExpected)
{
printf("Expected command class %d authorization '%d', but TCAT Agent reports '%d'\n", i,
isAuthorizationExpected, isAuthorizedByAgent);
validationResult = false;
}
}
return validationResult;
}
// test helper to validate if Set Active Dataset commands are authorized or not, given the dataset to write.
static bool SetActiveDatasetAuthorized(const TcatAgent *aAgent, const Dataset::Info &aDatasetInfo)
{
Dataset dataset;
// Convert high-level Dataset::Info into TLVs representation required by IsSetActiveDatasetAuthorized()
VerifyOrQuit(dataset.WriteTlvsFrom(aDatasetInfo) == kErrorNone);
return aAgent->IsSetActiveDatasetAuthorized(&dataset);
}
static Instance *TestInitInstanceTcat(void)
{
Instance *instance = testInitInstance();
otBleSecureSetCertificate(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_X509_CERT), sizeof(OT_TCAT_X509_CERT),
reinterpret_cast<const uint8_t *>(OT_TCAT_PRIV_KEY), sizeof(OT_TCAT_PRIV_KEY));
otBleSecureSetCaCertificateChain(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_TRUSTED_ROOT_CERTIFICATE),
sizeof(OT_TCAT_TRUSTED_ROOT_CERTIFICATE));
otBleSecureSetSslAuthMode(instance, true);
SuccessOrQuit(otBleSecureSetTcatVendorInfo(instance, &vendorInfo));
// reset default data items used across tests
sFullDataset = AsCoreType(&kFullDataset);
sPartialDataset = AsCoreType(&kPartialDataset);
IgnoreError(sCommNetworkName.Set(kNetworkName));
IgnoreError(sCommDomainName.Set(kDomainName));
memcpy(&sCommExtPanId, &kExtPanId, sizeof(sCommExtPanId));
memcpy(&sCommAuth, &kCommCert1AuthField, sizeof(sCommAuth));
memcpy(&sDeviceAuth, &kDeviceCert1AuthField, sizeof(sDeviceAuth));
sPlatBleLastAdvSetDataLen = 0;
memset(sPlatBleLastAdvSetData, 0, OT_TCAT_ADVERTISEMENT_MAX_LEN);
return instance;
}
void TestTcatConnectionAndCertAttributes(void)
{
uint8_t attributeBuffer[8];
size_t attributeLen;
TestBleSecure ble;
Instance *instance = TestInitInstanceTcat();
// Validate BLE secure and Tcat start APIs
VerifyOrQuit(otBleSecureTcatStart(instance, nullptr) == kErrorInvalidState);
SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble));
VerifyOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, nullptr) == kErrorAlready);
SuccessOrQuit(otBleSecureTcatStart(instance, nullptr));
// Validate connection callbacks when platform informs that peer has connected/disconnected
VerifyOrQuit(!otBleSecureIsConnected(instance));
otPlatBleGapOnConnected(instance, kConnectionId);
VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
otPlatBleGapOnDisconnected(instance, kConnectionId);
VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
// Verify that Thread-attribute parsing isn't available yet when not connected as client or server.
attributeLen = sizeof(attributeBuffer);
VerifyOrQuit(otBleSecureGetThreadAttributeFromPeerCertificate(instance, kCertificateAuthorizationField,
&attributeBuffer[0],
&attributeLen) == kErrorInvalidState);
attributeLen = sizeof(attributeBuffer);
VerifyOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(
instance, kCertificateThreadVersion, &attributeBuffer[0], &attributeLen) == kErrorInvalidState);
// Validate connection callbacks when calling `otBleSecureDisconnect()`
otPlatBleGapOnConnected(instance, kConnectionId);
VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
ble.ResetConnectCallbackCount();
otBleSecureDisconnect(instance);
VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
// Regression test: a locally-initiated disconnect must invoke the connect callback exactly once.
VerifyOrQuit(ble.GetConnectCallbackCount() == 1);
// Validate TLS connection can be started (as client) only when peer is BLE-connected
otPlatBleGapOnConnected(instance, kConnectionId);
SuccessOrQuit(otBleSecureConnect(instance));
VerifyOrQuit(otBleSecureIsConnectionActive(instance));
// Once in TLS client connecting state, the below cert eval functions are available.
// Test that the Thread-specific attributes from own certificate can be decoded properly.
attributeLen = 1;
SuccessOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(instance, kCertificateThreadVersion,
&attributeBuffer[0], &attributeLen));
VerifyOrQuit(attributeLen == 1 && attributeBuffer[0] >= kThreadVersion1p4);
static_assert(5 == sizeof(kDeviceCert1AuthField), "expectedTcatAuthField size incorrect for test");
attributeLen = 5;
SuccessOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(instance, kCertificateAuthorizationField,
&attributeBuffer[0], &attributeLen));
VerifyOrQuit(attributeLen == 5 && memcmp(&kDeviceCert1AuthField, &attributeBuffer, attributeLen) == 0);
// Validate TLS client connection can be started only when peer is BLE-connected
otBleSecureDisconnect(instance);
VerifyOrQuit(otBleSecureConnect(instance) == kErrorInvalidState);
// Validate Tcat agent state changes after stopping BLE secure
VerifyOrQuit(otBleSecureIsTcatAgentStarted(instance));
otBleSecureStop(instance);
VerifyOrQuit(!otBleSecureIsTcatAgentStarted(instance));
testFreeInstance(instance);
}
void TestTcatAdvertisementUpdates(void)
{
TestBleSecure ble;
Instance *instance = TestInitInstanceTcat();
uint8_t advDataSnapshot[OT_TCAT_ADVERTISEMENT_MAX_LEN];
uint16_t advDataSnapshotLen;
VerifyOrQuit(sPlatBleLastAdvSetDataLen == 0, "Adv data should be unset before BleSecure start");
SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble));
SuccessOrQuit(otBleSecureTcatStart(instance, nullptr));
VerifyOrQuit(sPlatBleLastAdvSetDataLen > 0, "Adv data should be set after BleSecure start");
advDataSnapshotLen = sPlatBleLastAdvSetDataLen;
memcpy(advDataSnapshot, sPlatBleLastAdvSetData, advDataSnapshotLen);
otPlatBleGapOnConnected(instance, kConnectionId);
SuccessOrQuit(otBleSecureConnect(instance));
// BLE connect and initiating TLS does not change the adv data.
VerifyOrQuit(sPlatBleLastAdvSetDataLen == advDataSnapshotLen &&
memcmp(sPlatBleLastAdvSetData, advDataSnapshot, advDataSnapshotLen) == 0,
"Adv data changed unexpectedly after BLE connect");
advDataSnapshotLen = sPlatBleLastAdvSetDataLen;
memcpy(advDataSnapshot, sPlatBleLastAdvSetData, advDataSnapshotLen);
// Commissioner sets dataset, then disconnects
instance->Get<ActiveDatasetManager>().SaveLocal(sPartialDataset);
otBleSecureDisconnect(instance);
// Adv is changed due to the partial dataset now being advertised in the S flag.
VerifyOrQuit(sPlatBleLastAdvSetDataLen == advDataSnapshotLen, "Adv data length changed unexpectedly");
VerifyOrQuit(memcmp(sPlatBleLastAdvSetData, advDataSnapshot, advDataSnapshotLen) != 0,
"Adv data did not change after disconnect, which it should due to S flag");
otBleSecureStop(instance);
testFreeInstance(instance);
}
class UnitTester
{
private:
// Mock action: TCAT Commissioner connects with authorization aCommAuth while device has aDeviceAuth.
static void MockCommissionerConnected(TcatAgent *aAgent,
const TcatAgent::CertificateAuthorizationField aCommAuth,
const TcatAgent::CertificateAuthorizationField aDeviceAuth,
bool aIsCommissionedAtStart)
{
// This mock function mimics the steps in TcatAgent::Connected() without requiring the actual TLS
// session object.
aAgent->ClearCommissionerState();
aAgent->mCommissionerAuthorizationField = aCommAuth;
aAgent->mDeviceAuthorizationField = aDeviceAuth;
aAgent->mIsCommissioned = aIsCommissionedAtStart;
aAgent->mNextState =
(aAgent->mState == TcatAgent::kStateActiveTemporary) ? TcatAgent::kStateStandby : TcatAgent::kStateActive;
aAgent->mState = TcatAgent::kStateConnected;
aAgent->NotifyStateChange();
}
// Mock condition: commissioner has or has not the given Extended Pan ID in its certificate.
static void MockExtPanId(TcatAgent *aAgent, bool aCommHasExtPanId, const ExtendedPanId *aExtPanId)
{
aAgent->mCommissionerHasExtendedPanId = aCommHasExtPanId;
aAgent->mCommissionerExtendedPanId = *aExtPanId;
}
// Mock condition: commissioner has or has not the given Network Name in its certificate.
static void MockNetworkName(TcatAgent *aAgent, bool aCommHasNetworkName, const NetworkName *aNetworkName)
{
aAgent->mCommissionerHasNetworkName = aCommHasNetworkName;
aAgent->mCommissionerNetworkName = *aNetworkName;
}
// Mock condition: commissioner has or has not the given Domain Name in its certificate.
static void MockDomainName(TcatAgent *aAgent, bool aCommHasDomainName, const NetworkName *aDomainName)
{
aAgent->mCommissionerHasDomainName = aCommHasDomainName;
aAgent->mCommissionerDomainName = *aDomainName;
}
public:
static void TestTcatCommissioner1Auth(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsCommissioned());
// validate no Commissioner authorizations if not connected
VerifyOrQuit(CommandClassesAuthorized(agent, kClassNone));
// Mock TCAT Commissioner 1 connects to the agent - verify it has access to all classes
// ====================================================================================
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
// Write a partial Active Dataset and verify that Commissioner can still overwrite this with another dataset
// if needed.
instance->Get<ActiveDatasetManager>().SaveLocal(sPartialDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(instance->Get<ActiveDatasetManager>().IsPartiallyComplete());
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// Write a full Active Dataset and verify that Commissioner can still overwrite this.
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsPartiallyComplete());
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// And back to partial dataset.
instance->Get<ActiveDatasetManager>().SaveLocal(sPartialDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(instance->Get<ActiveDatasetManager>().IsPartiallyComplete());
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// provide PSKc proof-of-possession - verify access is same as before
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(instance->Get<ActiveDatasetManager>().IsPartiallyComplete());
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner2Auth(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// Mock TCAT Commissioner 2 connects to the agent - verify it only has access to class General by default.
// CommCert2 contains Network Name and Extended PAN ID in this initial test, but not the (also-required)
// Thread Domain Name.
// =======================================================================================================
memcpy(&sCommAuth, &kCommCert2AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
MockNetworkName(agent, true, &sCommNetworkName);
MockExtPanId(agent, true, &sCommExtPanId);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
// Verify that Set Active Dataset can't be used yet, despite a matching XPAN ID and Network Name for the
// dataset that the Commissioner wants to write.
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// provide PSKd proof-of-possession - this is required for all 4 command classes, but not sufficient yet.
// So verify there's no change.
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// Commissioner cert now has a matching Domain Name - does not unlock any new classes, because
// Network Name and XPAN ID can't match, due to Device being uncommissioned. Writing Active Dataset works now.
MockDomainName(agent, true, &sCommDomainName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// Writing a partial dataset does not work: misses the required Network Name and XPAN ID
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Commissioner now has no XPAN ID anymore in cert - verify this prevents Set Active Dataset.
// It's a misconfig in the Commissioner's cert.
MockExtPanId(agent, false, &sCommExtPanId);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Commissioner now has correct XPAN ID in cert, but not matching the dataset it wants to write.
MockExtPanId(agent, true, &sCommExtPanId);
sFullDataset.mExtendedPanId.m8[2]++; // modify bits in dataset to be written.
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Commissioner now attempts to write a dataset with XPAN ID matching to that in cert.
sFullDataset.mExtendedPanId = sCommExtPanId;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: Active Dataset is now configured (device is commissioned), but XPAN ID in Dataset
// doesn't match the XPAN ID in the Commissioner's cert; and PSKc proof is not given yet,
// so most classes remain unavailable.
sFullDataset.mExtendedPanId.m8[2]++; // modify bits in dataset to be written.
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(instance->Get<ActiveDatasetManager>().IsCommissioned());
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// XPAN ID now matches again; class Commissioning authorization (0x1F) is restored. It doesn't require
// PSKc proof.
sFullDataset.mExtendedPanId = sCommExtPanId;
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
// Active Dataset can be overwritten because Device was uncommissioned at session start.
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Now PSKc proof is given, unlocking more command classes
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Commissioner connects again - this time, the Device is already commissioned at the start of the session.
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
MockNetworkName(agent, true, &sCommNetworkName);
MockExtPanId(agent, true, &sCommExtPanId);
MockDomainName(agent, true, &sCommDomainName);
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// PSKd proof does not authorize Set Active Dataset - because device is already commissioned.
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner4Auth(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// Mock TCAT Commissioner 4 connects to the Device - verify it only has access to class General by default.
// The Device is commissioned already at start of the TCAT Link.
// =======================================================================================================
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
memcpy(&sCommAuth, &kCommCert4AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// PSKc proof - satisfies 0x21. Set Active Dataset is not allowed: Device already commissioned.
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// Matching network name now in Commissioner cert - satisfies 0x05
MockNetworkName(agent, true, &sCommNetworkName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: matching XPAN ID present in Comm cert - satisfies 0x09
MockExtPanId(agent, true, &sCommExtPanId);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: Thread Domain name in cert matches - Application class added.
MockDomainName(agent, true, &sCommDomainName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: PSKc proof not given - Commissioning class is revoked
agent->mPskcVerified = false;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassExtraction | kClassDecommissioning |
kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: network name present, but mismatch - Extraction revoked
NetworkName wrongNetworkName;
SuccessOrQuit(wrongNetworkName.Set("WrongName"));
MockNetworkName(agent, true, &wrongNetworkName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassDecommissioning | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: XPAN ID present, but mismatch - Decommissioning revoked
sFullDataset.mExtendedPanId.m8[4]++; // change bits to force a mismatch
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
MockExtPanId(agent, true, &sCommExtPanId);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: XPAN ID not present in dataset - same as before
sFullDataset.mExtendedPanId = sCommExtPanId; // restore changes bits of above
sFullDataset.mComponents.mIsExtendedPanIdPresent = false;
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: Network Name not present in dataset - same as before
sFullDataset.mComponents.mIsNetworkNamePresent = false;
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// New situation: Device is decommissioned (by some other Commissioner). Then, this Commissioner
// connects again and does PSKc proof. Set Active Dataset access should now be allowed.
instance->Get<ActiveDatasetManager>().Clear();
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner5Auth(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// Mock TCAT Commissioner 5 connects to the agent - it requires checks that are unknown to the Device
// ==================================================================================================
memcpy(&sCommAuth, &kCommCert5AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// PSKd proof is given: it won't enable Extraction, because Extraction access flag bit 0 = 0.
// Also it won't enable Decommissioning, because this class has an unknown flag bit 7 set i.e. the Commissioner
// is configured to require a method that the TCAT Device doesn't know about. Application class is also not
// enabled, since it requires a check with unknown flag bit 6. Device will enable Commissioning class.
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Connect again and test the situation that Device was uncommissioned at connection start.
// Commissioning is now enabled with PSKd proof.
instance->Get<ActiveDatasetManager>().Clear();
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner1AuthWithDeviceRequirements(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// test different auth info: for TCAT Device 2 which has specific authorization requirements per class.
memcpy(&sDeviceAuth, &kDeviceCert2AuthField, sizeof(sDeviceAuth));
// Mock TCAT Commissioner 1 connects to the agent - verify
// ====================================================================================
memcpy(&sCommAuth, &kCommCert1AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassExtraction));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// PSKd proof
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sPartialDataset));
// New situation: Device is commissioned and Commissioner has matching Network Name; and connects.
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
MockNetworkName(agent, true, &sCommNetworkName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassExtraction | kClassDecommissioning |
kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// PSKc proof
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassExtraction | kClassDecommissioning |
kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// If Network Name does not match
NetworkName wrongNetworkName;
SuccessOrQuit(wrongNetworkName.Set(kWrongName));
MockNetworkName(agent, true, &wrongNetworkName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassExtraction | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
agent->mPskdVerified = true;
agent->mPskcVerified = false;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner2AuthWithDeviceRequirements(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// test different auth info: for TCAT Device 2 which has specific authorization requirements per class.
memcpy(&sDeviceAuth, &kDeviceCert2AuthField, sizeof(sDeviceAuth));
// Mock TCAT Commissioner 2 connects to the agent
// ==============================================
memcpy(&sCommAuth, &kCommCert2AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, false);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// PSKd proof
agent->mPskdVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Network Name match
MockNetworkName(agent, true, &sCommNetworkName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// XPAN ID match
MockExtPanId(agent, true, &sCommExtPanId);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Domain Name match - but Commissioning in general is still not authorized, due to missing Active Dataset.
// Hence the Network Name and XPAN ID checks cannot succeed in general. They will succeed now for the
// specific 'Set Active Dataset' command.
MockDomainName(agent, true, &sCommDomainName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
// the partial dataset cannot be written, because it lacks the required Network Name and XPAN ID combo.
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// PSKc proof
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
// Try write a full dataset with differing XPAN ID - this fails
sFullDataset.mExtendedPanId.m8[2]++;
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// Test an equivalent case to above where the device does have a full dataset stored already, and the
// Commissioner connects. Now it has full access to all classes due to matching Network Name / XPAN ID combo.
sFullDataset = AsCoreType(&kFullDataset);
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
agent->mPskdVerified = true;
agent->mPskcVerified = true;
MockNetworkName(agent, true, &sCommNetworkName);
MockExtPanId(agent, true, &sCommExtPanId);
MockDomainName(agent, true, &sCommDomainName);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassDecommissioning |
kClassExtraction | kClassApplication));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sPartialDataset));
testFreeInstance(instance);
}
static void TestTcatCommissioner4AuthWithExistingPartialDataset(void)
{
Instance *instance = TestInitInstanceTcat();
TcatAgent *agent = &instance->Get<TcatAgent>();
// TCAT device was commissioned earlier on with a partial dataset.
instance->Get<ActiveDatasetManager>().SaveLocal(sPartialDataset);
// Mock TCAT Commissioner 4 connects to the Device.
// static const uint8_t kCommCert4AuthField[5] = {0x21, 0x21, 0x05, 0x09, 0x11};
memcpy(&sCommAuth, &kCommCert4AuthField, sizeof(sCommAuth));
MockCommissionerConnected(agent, sCommAuth, sDeviceAuth, true);
MockNetworkName(agent, true, &sCommNetworkName);
MockExtPanId(agent, true, &sCommExtPanId);
// It wants access to Extraction class (0x05) based on matching Network Name, but it's denied.
// Decommissioning (0x09) based on matching XPAN ID is also denied.
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// PSKc proof - satisfies 0x21. Set Active Dataset is not allowed: Device already commissioned.
agent->mPskcVerified = true;
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
// As a sanity check, redo the test assuming that a (matching) full dataset was initially in the Device.
// Now, Extraction and Commissioning classes do work because of matching elements in the Commcert.
instance->Get<ActiveDatasetManager>().SaveLocal(sFullDataset);
VerifyOrQuit(CommandClassesAuthorized(agent, kClassGeneral | kClassCommissioning | kClassExtraction |
kClassDecommissioning));
VerifyOrQuit(!SetActiveDatasetAuthorized(agent, sFullDataset));
testFreeInstance(instance);
}
}; // class UnitTester
} // namespace MeshCoP
} // namespace ot
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
int main(void)
{
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
ot::MeshCoP::TestTcatConnectionAndCertAttributes();
ot::MeshCoP::UnitTester::TestTcatCommissioner1Auth();
ot::MeshCoP::UnitTester::TestTcatCommissioner2Auth();
ot::MeshCoP::UnitTester::TestTcatCommissioner4Auth();
ot::MeshCoP::UnitTester::TestTcatCommissioner5Auth();
ot::MeshCoP::UnitTester::TestTcatCommissioner1AuthWithDeviceRequirements();
ot::MeshCoP::UnitTester::TestTcatCommissioner2AuthWithDeviceRequirements();
ot::MeshCoP::UnitTester::TestTcatCommissioner4AuthWithExistingPartialDataset();
ot::MeshCoP::TestTcatAdvertisementUpdates();
printf("All tests passed\n");
#else
printf("TCAT feature is not enabled\n");
#endif
return 0;
}