| /* |
| * 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; |
| } |