mirror of
https://github.com/espressif/openthread.git
synced 2026-09-27 03:17:39 +00:00
In nexus tests, DNS browser and resolver objects must be initialized using ClearAllBytes before use to ensure predictable behavior. This commit adds missing ClearAllBytes calls for: - Dns::Multicast::Core::Browser - Dns::Multicast::Core::TxtResolver - Dns::Multicast::Core::SrvResolver - Dns::Multicast::Core::AddressResolver In test_1_3_SRP_TC_4.cpp, ClearAllBytes is now called before browser reuse in Step 19. Redundant includes of common/clearable.hpp were removed as it is available transitively. A blank line was added after Browser declarations for consistency.
922 lines
36 KiB
C++
922 lines
36 KiB
C++
/*
|
||
* Copyright (c) 2026, 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 <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "platform/nexus_core.hpp"
|
||
#include "platform/nexus_node.hpp"
|
||
#include "thread/uri_paths.hpp"
|
||
#include "utils/verhoeff_checksum.hpp"
|
||
|
||
namespace ot {
|
||
namespace Nexus {
|
||
|
||
/**
|
||
* Time to advance for a node to form a network and become leader, in milliseconds.
|
||
*/
|
||
static constexpr uint32_t kFormNetworkTime = 50 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Time to advance for the DUT to connect to the network, in milliseconds.
|
||
*/
|
||
static constexpr uint32_t kAttachTime = 10 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Time to advance for mDNS discovery, in milliseconds.
|
||
*/
|
||
static constexpr uint32_t kMdnsDiscoveryTime = 5 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Time to advance for DTLS connection, in milliseconds.
|
||
*/
|
||
static constexpr uint32_t kConnectTimeout = 3 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Time to advance for TMF message exchange, in milliseconds.
|
||
*/
|
||
static constexpr uint32_t kTmfExchangeTime = 1 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Max ePSKc connection attempts.
|
||
*/
|
||
static constexpr uint8_t kMaxEpskcConnAttempts = 10;
|
||
|
||
/**
|
||
* The filename for the DTLS key log.
|
||
*/
|
||
static const char kKeyLogFilename[] = "/tmp/test_1_4_CS_TC_3.keys";
|
||
|
||
/**
|
||
* The master secret length in bytes.
|
||
*/
|
||
static constexpr uint8_t kMasterSecretLength = 48;
|
||
|
||
/**
|
||
* The random buffer length in bytes.
|
||
*/
|
||
static constexpr uint8_t kRandomBufferLength = MeshCoP::SecureTransport::kSecureTransportRandomBufferSize;
|
||
|
||
/**
|
||
* Infrastructure interface index.
|
||
*/
|
||
static constexpr uint32_t kInfraIfIndex = 1;
|
||
|
||
/**
|
||
* The UDP port for ePSKc DTLS session.
|
||
*/
|
||
static constexpr uint16_t kEpskcPort = 1234;
|
||
|
||
/**
|
||
* Time to wait for DTLS retransmissions to finish (in milliseconds).
|
||
*/
|
||
static constexpr uint32_t kDtlsRetransmissionWaitTime = 60 * Time::kOneSecondInMsec;
|
||
|
||
/**
|
||
* Time to wait for DUT state to be cleaned up after DTLS session (in milliseconds).
|
||
*/
|
||
static constexpr uint32_t kDutStateCleanTime = 100 * Time::kOneSecondInMsec;
|
||
|
||
using EphemeralKeyManager = MeshCoP::BorderAgent::EphemeralKeyManager;
|
||
using Manager = MeshCoP::BorderAgent::Manager;
|
||
|
||
static void HandleMbedtlsExportKeys(void *aContext,
|
||
mbedtls_ssl_key_export_type aType,
|
||
const unsigned char *aMasterSecret,
|
||
size_t aMasterSecretLen,
|
||
const unsigned char aClientRandom[kRandomBufferLength],
|
||
const unsigned char aServerRandom[kRandomBufferLength],
|
||
mbedtls_tls_prf_types aTlsPrfType)
|
||
{
|
||
FILE *file = fopen(kKeyLogFilename, "a");
|
||
|
||
OT_UNUSED_VARIABLE(aContext);
|
||
OT_UNUSED_VARIABLE(aMasterSecretLen);
|
||
OT_UNUSED_VARIABLE(aServerRandom);
|
||
OT_UNUSED_VARIABLE(aTlsPrfType);
|
||
|
||
if (file != nullptr)
|
||
{
|
||
if (aType == MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET)
|
||
{
|
||
fprintf(file, "CLIENT_RANDOM ");
|
||
for (int i = 0; i < kRandomBufferLength; i++)
|
||
{
|
||
fprintf(file, "%02x", aClientRandom[i]);
|
||
}
|
||
fprintf(file, " ");
|
||
for (int i = 0; i < kMasterSecretLength; i++)
|
||
{
|
||
fprintf(file, "%02x", aMasterSecret[i]);
|
||
}
|
||
fprintf(file, "\n");
|
||
}
|
||
fclose(file);
|
||
}
|
||
}
|
||
|
||
typedef Dns::Name::Buffer DnsName;
|
||
|
||
struct BrowseOutcome
|
||
{
|
||
DnsName mServiceInstance;
|
||
DnsName mHostName;
|
||
uint16_t mPort;
|
||
Ip6::Address mAddress;
|
||
};
|
||
|
||
static Array<BrowseOutcome, 5> sBrowseOutcomes;
|
||
|
||
static void HandleBrowseCallback(otInstance *aInstance, const Dns::Multicast::Core::BrowseResult *aResult)
|
||
{
|
||
BrowseOutcome *outcome;
|
||
|
||
OT_UNUSED_VARIABLE(aInstance);
|
||
|
||
outcome = sBrowseOutcomes.PushBack();
|
||
VerifyOrQuit(outcome != nullptr);
|
||
|
||
SuccessOrQuit(StringCopy(outcome->mServiceInstance, aResult->mServiceInstance));
|
||
}
|
||
|
||
static void HandleSrvCallback(otInstance *aInstance, const Dns::Multicast::Core::SrvResult *aResult)
|
||
{
|
||
OT_UNUSED_VARIABLE(aInstance);
|
||
|
||
for (BrowseOutcome &outcome : sBrowseOutcomes)
|
||
{
|
||
if (StringMatch(outcome.mServiceInstance, aResult->mServiceInstance))
|
||
{
|
||
outcome.mPort = aResult->mPort;
|
||
SuccessOrQuit(StringCopy(outcome.mHostName, aResult->mHostName));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void HandleAddressCallback(otInstance *aInstance, const Dns::Multicast::Core::AddressResult *aResult)
|
||
{
|
||
OT_UNUSED_VARIABLE(aInstance);
|
||
|
||
for (BrowseOutcome &outcome : sBrowseOutcomes)
|
||
{
|
||
if (StringMatch(outcome.mHostName, aResult->mHostName))
|
||
{
|
||
if (aResult->mAddressesLength > 0)
|
||
{
|
||
outcome.mAddress = AsCoreType(&aResult->mAddresses[0].mAddress);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void DiscoverMeshcopEService(Node &aCommNode, Core &aNexus, Ip6::Address &aAddress, uint16_t &aPort)
|
||
{
|
||
sBrowseOutcomes.Clear();
|
||
{
|
||
Dns::Multicast::Core::Browser browser;
|
||
|
||
ClearAllBytes(browser);
|
||
browser.mServiceType = "_meshcop-e._udp";
|
||
browser.mInfraIfIndex = kInfraIfIndex;
|
||
browser.mCallback = HandleBrowseCallback;
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StartBrowser(browser));
|
||
aNexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StopBrowser(browser));
|
||
}
|
||
|
||
VerifyOrQuit(sBrowseOutcomes.GetLength() == 1);
|
||
|
||
{
|
||
Dns::Multicast::Core::SrvResolver srvResolver;
|
||
ClearAllBytes(srvResolver);
|
||
srvResolver.mServiceInstance = sBrowseOutcomes[0].mServiceInstance;
|
||
srvResolver.mServiceType = "_meshcop-e._udp";
|
||
srvResolver.mInfraIfIndex = kInfraIfIndex;
|
||
srvResolver.mCallback = HandleSrvCallback;
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StartSrvResolver(srvResolver));
|
||
aNexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StopSrvResolver(srvResolver));
|
||
}
|
||
|
||
{
|
||
Dns::Multicast::Core::AddressResolver addrResolver;
|
||
ClearAllBytes(addrResolver);
|
||
addrResolver.mHostName = sBrowseOutcomes[0].mHostName;
|
||
addrResolver.mInfraIfIndex = kInfraIfIndex;
|
||
addrResolver.mCallback = HandleAddressCallback;
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StartIp6AddressResolver(addrResolver));
|
||
aNexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(aCommNode.Get<Dns::Multicast::Core>().StopIp6AddressResolver(addrResolver));
|
||
}
|
||
|
||
aPort = sBrowseOutcomes[0].mPort;
|
||
aAddress = sBrowseOutcomes[0].mAddress;
|
||
}
|
||
|
||
void Test1_4_CS_TC_3(void)
|
||
{
|
||
/**
|
||
* 12.3. [1.4] [CERT] [COMPONENT] BR component creates numeric passcode and Candidate connects
|
||
*
|
||
* 12.3.1. Purpose
|
||
* This test is for automatically testing BR component DUTs. Instead of a test operator typing the passcode, the
|
||
* DUT generates a passcode and shares it via the Thread Harness API to the Harness so that it can execute the
|
||
* automated test. No UI, GUI or display is needed on the BR DUT in this case.
|
||
* In detail, the purpose is to verify that the Border Router DUT component:
|
||
* - correctly implements the Thread Credential Sharing feature by supporting the Thread Administration Sharing
|
||
* procedure
|
||
* - is able to generate a numeric Thread Administration One-Time Passcode consisting of 9 digits
|
||
* - allows an external Candidate to connect using the ePSKc that is based on the passcode
|
||
* - is able to facilitate the Candidate’s operations
|
||
* - enables access to a Candidate using the ePSKc, only once.
|
||
*
|
||
* 12.3.2. Topology
|
||
* - BR_1 – Border Router Thread Component DUT
|
||
* - Router_1 – Thread Router reference device and Leader
|
||
* - Comm_1 – IPv6 host reference device on the AIL that implements a Commissioner Candidate role.
|
||
*
|
||
* Spec Reference | V1.4 Section
|
||
* --------------------------|-----------------
|
||
* Thread Credential Sharing | 8.4.1.2.3, 8.4.9
|
||
*/
|
||
|
||
Core nexus;
|
||
|
||
Node &router1 = nexus.CreateNode();
|
||
Node &br1 = nexus.CreateNode();
|
||
Node &comm1 = nexus.CreateNode();
|
||
|
||
router1.SetName("Router_1");
|
||
br1.SetName("BR_1");
|
||
comm1.SetName("Comm_1");
|
||
|
||
router1.Get<Manager>().SetEnabled(false);
|
||
comm1.Get<Manager>().SetEnabled(false);
|
||
|
||
nexus.AdvanceTime(0);
|
||
|
||
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||
|
||
// Remove any existing key log file.
|
||
remove(kKeyLogFilename);
|
||
|
||
#if OPENTHREAD_CONFIG_MBEDTLS_PROVIDES_SSL_KEY_EXPORT
|
||
static_cast<MeshCoP::SecureTransport &>(comm1.Get<Tmf::SecureAgent>())
|
||
.SetKeylogCallback(HandleMbedtlsExportKeys, &comm1.Get<Tmf::SecureAgent>());
|
||
#endif
|
||
|
||
SuccessOrQuit(br1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||
|
||
comm1.Get<ThreadNetif>().Up();
|
||
|
||
// Wait for the interfaces to be ready and addresses to be configured.
|
||
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 1: Enable and form network.");
|
||
|
||
/**
|
||
* Step 1
|
||
* - Device: Router_1, BR_1 (DUT)
|
||
* - Description (CS-12.3): Enable and form network. The DUT is configured with the Network Key of the test
|
||
* Thread Network, so that it can connect to it.
|
||
* - Pass Criteria: The DUT MUST connect to Router_1’s network.
|
||
*/
|
||
|
||
router1.Form();
|
||
nexus.AdvanceTime(kFormNetworkTime);
|
||
|
||
br1.Join(router1);
|
||
nexus.AdvanceTime(kAttachTime);
|
||
VerifyOrQuit(br1.Get<Mle::Mle>().IsAttached());
|
||
|
||
SuccessOrQuit(comm1.Get<Mac::Mac>().SetPanChannel(router1.Get<Mac::Mac>().GetPanChannel()));
|
||
comm1.Get<Mac::Mac>().SetPanId(router1.Get<Mac::Mac>().GetPanId());
|
||
comm1.Get<ThreadNetif>().Up();
|
||
|
||
br1.Get<Manager>().SetEnabled(true);
|
||
nexus.AdvanceTime(Time::kOneSecondInMsec);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 2: Comm_1 discovers BR DUT service.");
|
||
|
||
/**
|
||
* Step 2
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to do mDNS Qtype=PTR discovery query for DNS-SD service type
|
||
* “_meshcop._udp.local”, to find the BR DUT service. ... Harness then verifies data in the SRV record and TXT
|
||
* record as detailed to the right.
|
||
* - Pass Criteria:
|
||
* - The DUT MUST respond with an mDNS message that contains one PTR record of type “_meshcop._udp.local” in the
|
||
* Answer section.
|
||
* - Comm_1 MUST be able to obtain the TXT record as detailed on the left.
|
||
* - In the TXT record:
|
||
* - ePSKc Support flag (Bit 11) in State Bitmap (sb) MUST be ‘1’ (ePSKc supported)
|
||
* - ‘vn’ field MUST be present and contain >= 1 characters.
|
||
* - ‘mn’ field MUST be present and contain >= 1 characters.
|
||
* - Comm_1 MUST be able to obtain the SRV record as detailed on the left. Harness logs and stores the port
|
||
* number in the SRV record for later use.
|
||
*/
|
||
|
||
// Border Agent starts automatically when attached.
|
||
VerifyOrQuit(br1.Get<Manager>().IsEnabled());
|
||
|
||
{
|
||
Dns::Multicast::Core::Browser browser;
|
||
|
||
ClearAllBytes(browser);
|
||
browser.mServiceType = "_meshcop._udp";
|
||
browser.mInfraIfIndex = kInfraIfIndex;
|
||
browser.mCallback = HandleBrowseCallback;
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StartBrowser(browser));
|
||
nexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StopBrowser(browser));
|
||
}
|
||
|
||
VerifyOrQuit(sBrowseOutcomes.GetLength() == 1);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 3: BR_1 (DUT) produces passcode.");
|
||
|
||
/**
|
||
* Step 3
|
||
* - Device: BR_1 (DUT)
|
||
* - Description (CS-12.3): Harness instructs the DUT, via Test Harness API, to produce the Thread Administration
|
||
* One-Time Passcode.
|
||
* - Pass Criteria: Passcode is successfully retrieved.
|
||
*/
|
||
|
||
EphemeralKeyManager::Tap tap1;
|
||
SuccessOrQuit(tap1.GenerateRandom());
|
||
Log("Passcode 1: %s", tap1.mTap);
|
||
nexus.AddTestVar("Passcode1", tap1.mTap);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 4: Harness validates the passcode.");
|
||
|
||
/**
|
||
* Step 4
|
||
* - Device: Harness
|
||
* - Description (CS-12.3): Harness validates the passcode. Harness calculates the ePSKc as follows: each
|
||
* character from the passcode string is taken as an ePSKc octet.
|
||
* - Pass Criteria:
|
||
* - Harness validates the following, using its passcode validation function:
|
||
* - The passcode MUST contain only digits 0-9.
|
||
* - The passcode MUST be 9 digits long.
|
||
* - The last digit of the passcode MUST be equal to the check digit calculated by the Verhoeff algorithm on
|
||
* the first 8 digits.
|
||
*/
|
||
|
||
SuccessOrQuit(tap1.Validate());
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 5: Comm_1 discovers meshcop-e service.");
|
||
|
||
/**
|
||
* Step 5
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to discover a DNS-SD service type via mDNS, Qtype=PTR, name
|
||
* “_meshcop-e._udp.local”. ... Harness stores the discovered IPv6 address and port for later use; and logs it.
|
||
* - Pass Criteria:
|
||
* - The DUT MUST respond (mDNS) with a DNS-SD message that contains one PTR record of type
|
||
* “_meshcop-e._udp.local” in the Answer section.
|
||
* - Comm_1 MUST be able to obtain an IPv6 address (from AAAA record) and port (from SRV record) of the
|
||
* “meshcop-e” service as detailed on the left.
|
||
*/
|
||
|
||
SuccessOrQuit(br1.Get<EphemeralKeyManager>().Start(tap1.mTap, 0, kEpskcPort));
|
||
|
||
uint16_t epskcPort;
|
||
Ip6::Address br1Addr;
|
||
DiscoverMeshcopEService(comm1, nexus, br1Addr, epskcPort);
|
||
Log("Discovered ePSKc address: %s, port: %u", br1Addr.ToString().AsCString(), epskcPort);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 6: Comm_1 connects with incorrect ePSKc.");
|
||
|
||
/**
|
||
* Step 6
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect to the DUT on the address and port found in step 5.
|
||
* For the DTLS PSK, it uses as ePSKc an incorrect ePSKc which is calculated as follows: 1. Seven octets
|
||
* (b[0]…b[6]) copied from the correct ePSKc of step 4. 2. One octet (b[7]) as a random digit that differs from
|
||
* the correct ePSKc of step 4. 3. One octet (b[8]) calculated as the correct Verhoeff check digit over the
|
||
* previous 8 numeric digits. ... Harness then waits for 3 seconds.
|
||
* - Pass Criteria:
|
||
* - The DTLS handshake/connection to the DUT MUST NOT succeed: the DUT MUST reject attempt to establish a DTLS
|
||
* session.
|
||
*/
|
||
|
||
{
|
||
char incorrectTap[EphemeralKeyManager::Tap::kLength + 1];
|
||
char checksum;
|
||
uint8_t i = 7;
|
||
|
||
memcpy(incorrectTap, tap1.mTap, EphemeralKeyManager::Tap::kLength + 1);
|
||
incorrectTap[i]++;
|
||
if (incorrectTap[i] > '9')
|
||
{
|
||
incorrectTap[i] = '0';
|
||
}
|
||
incorrectTap[EphemeralKeyManager::Tap::kLength - 1] = '\0';
|
||
SuccessOrQuit(Utils::VerhoeffChecksum::Calculate(incorrectTap, checksum));
|
||
incorrectTap[EphemeralKeyManager::Tap::kLength - 1] = checksum;
|
||
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(incorrectTap),
|
||
EphemeralKeyManager::Tap::kLength));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(!comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
|
||
// Wait longer to ensure all retransmissions are finished.
|
||
nexus.AdvanceTime(kDtlsRetransmissionWaitTime);
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 7: Comm_1 connects with correct ePSKc.");
|
||
|
||
/**
|
||
* Step 7
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect again to the DUT on the address and port found in
|
||
* step 5. For the DTLS PSK, it now uses the correct ePSKc as calculated in step 4.
|
||
* - Pass Criteria: The DTLS handshake/connection to the DUT MUST succeed.
|
||
*/
|
||
|
||
{
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(tap1.mTap),
|
||
EphemeralKeyManager::Tap::kLength));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 8: Comm_1 sends MGMT_ACTIVE_GET.req.");
|
||
|
||
/**
|
||
* Step 8
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to send TMF message MGMT_ACTIVE_GET.req to the DUT over the
|
||
* secure DTLS session. The Get TLV is not included in the request.
|
||
* - Pass Criteria:
|
||
* - Comm_1 MUST receive the response TMF message MGMT_ACTIVE_GET.rsp over the secure DTLS session.
|
||
* - Response payload MUST contain Network Key TLV, ...
|
||
*/
|
||
|
||
{
|
||
Coap::Message *message;
|
||
message = comm1.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriActiveGet);
|
||
VerifyOrQuit(message != nullptr);
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
||
}
|
||
nexus.AdvanceTime(kTmfExchangeTime);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 9: Comm_1 sends MGMT_PENDING_GET.req.");
|
||
|
||
/**
|
||
* Step 9
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to send MGMT_PENDING_GET.req to the DUT over the secure DTLS
|
||
* session. The Get TLV is not included in the request.
|
||
* - Pass Criteria:
|
||
* - Comm_1 MUST receive the TMF response message MGMT_PENDING_GET.rsp.
|
||
* - Response CoAP payload MUST be empty.
|
||
*/
|
||
|
||
{
|
||
Coap::Message *message;
|
||
message = comm1.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriPendingGet);
|
||
VerifyOrQuit(message != nullptr);
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
||
}
|
||
nexus.AdvanceTime(kTmfExchangeTime);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 10: Comm_1 closes the DTLS connection.");
|
||
|
||
/**
|
||
* Step 10
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to close the Commissioner session, i.e. close the DTLS
|
||
* connection. Then, wait for 3 seconds.
|
||
* - Pass Criteria: N/A
|
||
*/
|
||
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 11: Comm_1 connects again (should fail).");
|
||
|
||
/**
|
||
* Step 11
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect again to the DUT on the address and port found in
|
||
* step 5. For the DTLS PSK, it uses the correct ePSKc as determined in step 4. ...
|
||
* - Pass Criteria:
|
||
* - The DTLS handshake/connection to the DUT MUST NOT succeed: the DUT MUST reject the DTLS connection attempt.
|
||
*/
|
||
|
||
{
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(!comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
|
||
// Wait much longer to ensure all retransmissions are finished and the DUT state is clean.
|
||
// MbedTLS retransmissions can last for a while.
|
||
nexus.AdvanceTime(kDutStateCleanTime);
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 12: Wait 3s and clear mDNS cache.");
|
||
|
||
/**
|
||
* Step 12
|
||
* - Device: Harness
|
||
* - Description (CS-12.3): Harness waits for 3 seconds. Harness clears its mDNS discovery cache.
|
||
* - Pass Criteria: N/A
|
||
*/
|
||
|
||
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
||
// Nexus mDNS cache is handled by AdvanceTime usually or explicitly.
|
||
// In this test, we just clear our browse outcomes.
|
||
sBrowseOutcomes.Clear();
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 13: Comm_1 discovers meshcop-e service (should fail).");
|
||
|
||
/**
|
||
* Step 13
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to discover a service type via mDNS, Qtype=PTR, name
|
||
* “_meshcop-e._udp.local”.
|
||
* - Pass Criteria: The DUT MUST NOT respond with an mDNS message that contains a PTR record of type
|
||
* “_meshcop-e._udp.local” in the Answer section.
|
||
*/
|
||
|
||
{
|
||
Dns::Multicast::Core::Browser browser;
|
||
|
||
ClearAllBytes(browser);
|
||
browser.mServiceType = "_meshcop-e._udp";
|
||
browser.mInfraIfIndex = kInfraIfIndex;
|
||
browser.mCallback = HandleBrowseCallback;
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StartBrowser(browser));
|
||
nexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StopBrowser(browser));
|
||
}
|
||
VerifyOrQuit(sBrowseOutcomes.GetLength() == 0);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 14: BR_1 (DUT) produces new passcode.");
|
||
|
||
/**
|
||
* Step 14
|
||
* - Device: BR_1 (DUT)
|
||
* - Description (CS-12.3): Harness instructs the DUT, via Test Harness API, to produce the Thread Administration
|
||
* One-Time Passcode.
|
||
* - Pass Criteria: Passcode is successfully retrieved.
|
||
*/
|
||
|
||
EphemeralKeyManager::Tap tap2;
|
||
SuccessOrQuit(tap2.GenerateRandom());
|
||
Log("Passcode 2: %s", tap2.mTap);
|
||
nexus.AddTestVar("Passcode2", tap2.mTap);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 15: Harness validates the new passcode.");
|
||
|
||
/**
|
||
* Step 15
|
||
* - Device: Harness
|
||
* - Description (CS-12.3): Harness validates the passcode. From the passcode, the Harness calculates the ePSKc
|
||
* using the procedure of step 4.
|
||
* - Pass Criteria:
|
||
* - Harness validates the following, using its passcode validation function:
|
||
* - The passcode MUST differ from the passcode previously obtained in step 4
|
||
* - The passcode MUST contain only digits 0-9
|
||
* - The passcode MUST be 9 digits long
|
||
* - The last digit of the passcode MUST be equal to the check digit calculated by the Verhoeff algorithm on
|
||
* the first 8 digits.
|
||
*/
|
||
|
||
SuccessOrQuit(tap2.Validate());
|
||
VerifyOrQuit(!StringMatch(tap2.mTap, tap1.mTap));
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 16: Comm_1 discovers meshcop-e service.");
|
||
|
||
/**
|
||
* Step 16
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to discover a service type via mDNS, Qtype=PTR, name
|
||
* “_meshcop-e._udp.local”. ... Harness stores the discovered IPv6 address and port for later use; and logs these.
|
||
* - Pass Criteria:
|
||
* - The DUT MUST respond with an mDNS message that contains one PTR record of type “_meshcop-e._udp.local” in
|
||
* the Answer section.
|
||
* - Comm_1 MUST be able to obtain an IPv6 address (from AAAA record) and port (from SRV record) of the
|
||
* “meshcop-e” service as detailed on the left.
|
||
*/
|
||
|
||
SuccessOrQuit(br1.Get<EphemeralKeyManager>().Start(tap2.mTap, 0, kEpskcPort + 1));
|
||
|
||
DiscoverMeshcopEService(comm1, nexus, br1Addr, epskcPort);
|
||
Log("Discovered ePSKc address: %s, port: %u", br1Addr.ToString().AsCString(), epskcPort);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 17: Comm_1 connects with correct ePSKc.");
|
||
|
||
/**
|
||
* Step 17
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect to the DUT on the address and port found in step
|
||
* 16. For the DTLS PSK, it uses the correct ePSKc as calculated in step 15.
|
||
* - Pass Criteria: The DTLS handshake/connection to the DUT MUST succeed.
|
||
*/
|
||
|
||
{
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(tap2.mTap),
|
||
EphemeralKeyManager::Tap::kLength));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 18: Comm_1 sends MGMT_ACTIVE_GET.req.");
|
||
|
||
/**
|
||
* Step 18
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs the device to send TMF message MGMT_ACTIVE_GET.req to the DUT over
|
||
* the secure DTLS session. The Get TLV is not included in the request.
|
||
* - Pass Criteria:
|
||
* - Comm_1 MUST receive the response TMF message MGMT_ACTIVE_GET.rsp over the secure DTLS session.
|
||
* - Response payload MUST contain Network Key TLV, ...
|
||
*/
|
||
|
||
{
|
||
Coap::Message *message;
|
||
message = comm1.Get<Tmf::SecureAgent>().AllocateAndInitPriorityConfirmablePostMessage(kUriActiveGet);
|
||
VerifyOrQuit(message != nullptr);
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SendMessage(*message));
|
||
}
|
||
nexus.AdvanceTime(kTmfExchangeTime);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 19: Comm_1 closes the DTLS connection.");
|
||
|
||
/**
|
||
* Step 19
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to close the DTLS connection.
|
||
* - Pass Criteria: N/A
|
||
*/
|
||
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 20: BR_1 (DUT) produces new passcode.");
|
||
|
||
/**
|
||
* Step 20
|
||
* - Device: BR_1 (DUT)
|
||
* - Description (CS-12.3): Harness instructs the DUT, via Test Harness API, to produce the Thread Administration
|
||
* One-Time Passcode.
|
||
* - Pass Criteria: Passcode is successfully retrieved.
|
||
*/
|
||
|
||
EphemeralKeyManager::Tap tap3;
|
||
SuccessOrQuit(tap3.GenerateRandom());
|
||
Log("Passcode 3: %s", tap3.mTap);
|
||
nexus.AddTestVar("Passcode3", tap3.mTap);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 21: Harness validates the new passcode.");
|
||
|
||
/**
|
||
* Step 21
|
||
* - Device: Harness
|
||
* - Description (CS-12.3): Harness validates the passcode. From the passcode, the Harness calculates the ePSKc
|
||
* again using the procedure of step 4.
|
||
* - Pass Criteria:
|
||
* - Harness validates the following, equal to step 4, using its passcode validation function:
|
||
* - The passcode MUST contain only digits 0-9.
|
||
* - The passcode MUST be 9 digits long.
|
||
* - The last digit of the passcode MUST be equal to the check digit calculated by the Verhoeff algorithm on
|
||
* the first 8 digits.
|
||
* - Harness also validates that the passcode MUST be different from the passcodes obtained before in steps 4
|
||
* and 15.
|
||
*/
|
||
|
||
SuccessOrQuit(tap3.Validate());
|
||
VerifyOrQuit(!StringMatch(tap3.mTap, tap1.mTap));
|
||
VerifyOrQuit(!StringMatch(tap3.mTap, tap2.mTap));
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 22: Comm_1 discovers meshcop-e service.");
|
||
|
||
/**
|
||
* Step 22
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to discover a service type via mDNS, Qtype=PTR, name
|
||
* “_meshcop-e._udp.local”. ... Harness stores the discovered IPv6 address and port for later use; and logs these.
|
||
* - Pass Criteria:
|
||
* - DUT MUST respond with an mDNS message that contains one PTR record of type “_meshcop-e._udp.local” in the
|
||
* Answer section.
|
||
* - Comm_1 MUST be able to obtain an IPv6 address (from AAAA record) and port (from SRV record) of the
|
||
* “meshcop-e” service as detailed on the left.
|
||
*/
|
||
|
||
SuccessOrQuit(br1.Get<EphemeralKeyManager>().Start(tap3.mTap, 0, kEpskcPort + 2));
|
||
|
||
DiscoverMeshcopEService(comm1, nexus, br1Addr, epskcPort);
|
||
Log("Discovered ePSKc address: %s, port: %u", br1Addr.ToString().AsCString(), epskcPort);
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 23: Comm_1 connects with incorrect ePSKc 10 times.");
|
||
|
||
/**
|
||
* Step 23
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect to the DUT on the address and port found in step
|
||
* 22. For the DTLS PSK, it uses an incorrect ePSKc as calculated using the procedure of step 6 applied to the
|
||
* passcode found in step 21. This connection attempt is done in total MAX_EPSKC_CONN_ATTEMPTS (10) times. Each
|
||
* time, the pass criteria are checked. Between attempts, the Harness pauses for 2 seconds.
|
||
* - Pass Criteria:
|
||
* - The DTLS handshake/connection to DUT MUST NOT succeed.
|
||
* - In all cases, the DUT MUST NOT send ICMPv6 Port Unreachable error - this way of failing is not allowed here.
|
||
* The DUT MUST respond with DTLS messages.
|
||
*/
|
||
|
||
{
|
||
char incorrectTap[EphemeralKeyManager::Tap::kLength + 1];
|
||
char checksum;
|
||
uint8_t i = 7;
|
||
|
||
memcpy(incorrectTap, tap3.mTap, EphemeralKeyManager::Tap::kLength + 1);
|
||
incorrectTap[i]++;
|
||
if (incorrectTap[i] > '9')
|
||
{
|
||
incorrectTap[i] = '0';
|
||
}
|
||
incorrectTap[EphemeralKeyManager::Tap::kLength - 1] = '\0';
|
||
SuccessOrQuit(Utils::VerhoeffChecksum::Calculate(incorrectTap, checksum));
|
||
incorrectTap[EphemeralKeyManager::Tap::kLength - 1] = checksum;
|
||
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(incorrectTap),
|
||
EphemeralKeyManager::Tap::kLength));
|
||
|
||
for (uint8_t attempt = 1; attempt <= kMaxEpskcConnAttempts; attempt++)
|
||
{
|
||
Log("Attempt %u with incorrect ePSKc.", attempt);
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(!comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
||
}
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 24: Comm_1 connects with correct ePSKc (should fail).");
|
||
|
||
/**
|
||
* Step 24
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to connect again to the DUT on the address and port found in
|
||
* step 22. For the DTLS PSK, it uses the correct ePSKc as calculated in step 21. But the connection still fails
|
||
* due to the maximum number of attempts passed.
|
||
* - Pass Criteria:
|
||
* - The DTLS handshake/connection to DUT MUST NOT succeed.
|
||
* - Note: the reason that the correct ePSKc does not succeed here is that the maximum number of failed
|
||
* connection attempts has been reached. This is being tested here.
|
||
*/
|
||
|
||
{
|
||
Ip6::SockAddr br1SockAddr;
|
||
br1SockAddr.SetAddress(br1Addr);
|
||
br1SockAddr.SetPort(epskcPort);
|
||
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(tap3.mTap),
|
||
EphemeralKeyManager::Tap::kLength));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Open(Ip6::kNetifBackbone));
|
||
SuccessOrQuit(comm1.Get<Tmf::SecureAgent>().Connect(br1SockAddr));
|
||
|
||
nexus.AdvanceTime(kConnectTimeout);
|
||
VerifyOrQuit(!comm1.Get<Tmf::SecureAgent>().IsConnected());
|
||
comm1.Get<Tmf::SecureAgent>().Close();
|
||
}
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 25: Comm_1 clears its mDNS discovery cache.");
|
||
|
||
/**
|
||
* Step 25
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs device to clear its mDNS discovery cache.
|
||
* - Pass Criteria: N/A
|
||
*/
|
||
|
||
sBrowseOutcomes.Clear();
|
||
|
||
Log("---------------------------------------------------------------------------------------");
|
||
Log("Step 26: Comm_1 discovers meshcop-e service (should fail).");
|
||
|
||
/**
|
||
* Step 26
|
||
* - Device: Comm_1
|
||
* - Description (CS-12.3): Harness instructs the device to discover a service type via mDNS, Qtype=PTR, name
|
||
* “_meshcop-e._udp.local”.
|
||
* - Pass Criteria: The DUT MUST NOT respond with an mDNS message that contains a PTR record of type
|
||
* “_meshcop-e._udp.local” in the Answer section.
|
||
*/
|
||
|
||
{
|
||
Dns::Multicast::Core::Browser browser;
|
||
|
||
ClearAllBytes(browser);
|
||
browser.mServiceType = "_meshcop-e._udp";
|
||
browser.mInfraIfIndex = kInfraIfIndex;
|
||
browser.mCallback = HandleBrowseCallback;
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StartBrowser(browser));
|
||
nexus.AdvanceTime(kMdnsDiscoveryTime);
|
||
SuccessOrQuit(comm1.Get<Dns::Multicast::Core>().StopBrowser(browser));
|
||
}
|
||
VerifyOrQuit(sBrowseOutcomes.GetLength() == 0);
|
||
|
||
nexus.SaveTestInfo("test_1_4_CS_TC_3.json");
|
||
}
|
||
|
||
} // namespace Nexus
|
||
} // namespace ot
|
||
|
||
int main(void)
|
||
{
|
||
ot::Nexus::Test1_4_CS_TC_3();
|
||
printf("All tests passed\n");
|
||
return 0;
|
||
}
|