mirror of
https://github.com/espressif/openthread.git
synced 2026-08-14 14:47:46 +00:00
[nexus] implement 1.4 Thread Administration Sharing test (1_4_CS_TC_3) (#12832)
This commit implements the Thread 1.4 Credential Sharing (CS) TC-3
Nexus test, focusing on Thread Administration Sharing using ePSKc.
The test case covers mDNS discovery, ePSKc generation/validation,
and DTLS secure transport for TMF message exchange.
Detailed changes:
- Implement full 26-step Nexus test in tests/nexus/test_1_4_CS_TC_3.cpp
covering the following procedures:
- mDNS discovery of meshcop and meshcop-e service instances.
- Validation of State Bitmap (sb) and other TXT record fields.
- Generation and Verhoeff-based validation of One-Time Passcodes.
- DTLS handshakes using correct and incorrect ePSKc values.
- MGMT_ACTIVE_GET and MGMT_PENDING_GET request/response exchanges
over the secure DTLS session.
- Testing of ephemeral key expiration and max connection attempts.
- Add Python-based packet verification script in
tests/nexus/verify_1_4_CS_TC_3.py using pktverify to ensure
protocol compliance.
- Register the 1_4_CS_TC_3 test in tests/nexus/CMakeLists.txt.
- Add 1_4_CS_TC_3 to the default test list in run_nexus_tests.sh.
This commit is contained in:
@@ -293,6 +293,7 @@ ot_nexus_test(1_4_TREL_TC_4 "cert;nexus")
|
||||
ot_nexus_test(1_4_TREL_TC_5 "cert;nexus")
|
||||
ot_nexus_test(1_4_TREL_TC_6 "cert;nexus")
|
||||
ot_nexus_test(1_4_DNS_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_4_CS_TC_3 "cert;nexus")
|
||||
|
||||
# Misc tests
|
||||
ot_nexus_test(border_admitter "core;nexus")
|
||||
|
||||
@@ -227,6 +227,7 @@ DEFAULT_TESTS=(
|
||||
"1_4_TREL_TC_5"
|
||||
"1_4_TREL_TC_6"
|
||||
"1_4_DNS_TC_1"
|
||||
"1_4_CS_TC_3"
|
||||
)
|
||||
|
||||
# Use provided arguments or the default test list
|
||||
|
||||
@@ -0,0 +1,917 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
|
||||
# Add the current directory to sys.path to find verify_utils
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(CUR_DIR)
|
||||
|
||||
import verify_utils
|
||||
from pktverify import consts
|
||||
from pktverify.null_field import nullField
|
||||
|
||||
# Constants
|
||||
EPSKC_PORT = 1234
|
||||
ALERT_DESC_CLOSE_NOTIFY = 0
|
||||
ALERT_LEVEL_FATAL = 2
|
||||
ALERT_DESC_DECRYPT_ERROR = 51
|
||||
|
||||
MESHCOP_SERVICE = '_meshcop._udp.local'
|
||||
MESHCOP_E_SERVICE = '_meshcop-e._udp.local'
|
||||
|
||||
|
||||
def verify(pv):
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
COMM_1_LL = 'fe80::ff:fe00:2'
|
||||
BR_1_LL = 'fe80::ff:fe00:1'
|
||||
BR_1_INFRA = 'fe80::ff:fe00:1'
|
||||
|
||||
def get_txt_entries(p):
|
||||
entries = {}
|
||||
txt_list = []
|
||||
|
||||
# Try to get all values if it's a list
|
||||
layer = None
|
||||
if hasattr(p, 'mdns') and p.mdns:
|
||||
layer = p.mdns
|
||||
elif hasattr(p, 'dns') and p.dns:
|
||||
layer = p.dns
|
||||
|
||||
if layer:
|
||||
txt_list = layer.txt
|
||||
if not isinstance(txt_list, list):
|
||||
txt_list = [txt_list]
|
||||
|
||||
for txt in txt_list:
|
||||
if isinstance(txt, nullField.__class__):
|
||||
continue
|
||||
# txt is now a Bytes object (hex representation)
|
||||
# Find the '=' character (0x3d)
|
||||
try:
|
||||
eq_idx = txt.index(0x3d)
|
||||
key = bytes(txt[:eq_idx]).decode()
|
||||
value = txt[eq_idx + 1:]
|
||||
entries[key] = value
|
||||
except ValueError:
|
||||
entries[bytes(txt).decode()] = None
|
||||
|
||||
return entries
|
||||
|
||||
# Step 1: Enable and form network.
|
||||
print("Step 1: Enable and form network.")
|
||||
|
||||
# Step 2: Comm_1 discovers BR DUT service.
|
||||
print("Step 2: Comm_1 discovers BR DUT service.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
mdns_resp = pkts.filter_ipv6_src(BR_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_SERVICE in p.mdns.resp.name).\
|
||||
must_next()
|
||||
|
||||
# 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.
|
||||
txts = get_txt_entries(mdns_resp)
|
||||
assert 'sb' in txts, "sb field missing in TXT record"
|
||||
sb = struct.unpack('>I', txts['sb'])[0]
|
||||
assert (sb >> 11) & 1 == 1, f"ePSKc support flag (bit 11) in sb is not 1 (sb={sb:08x})"
|
||||
assert 'vn' in txts and len(txts['vn']) >= 1, "vn field missing or empty in TXT record"
|
||||
assert 'mn' in txts and len(txts['mn']) >= 1, "mn field missing or empty in TXT record"
|
||||
|
||||
# Step 3 & 4: Passcode generation and validation.
|
||||
print("Step 3 & 4: Passcode generation and validation.")
|
||||
# (Validated by C++ test)
|
||||
|
||||
# Step 5: Comm_1 discovers meshcop-e service.
|
||||
print("Step 5: Comm_1 discovers meshcop-e service.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
mdns_resp = pkts.filter_ipv6_src(BR_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.resp.name).\
|
||||
must_next()
|
||||
|
||||
port_step5 = mdns_resp.mdns.srv.port[0]
|
||||
print(f"Step 5 Discovered port: {port_step5}")
|
||||
|
||||
# Step 6: Comm_1 connects with incorrect ePSKc.
|
||||
print("Step 6: Comm_1 connects with incorrect ePSKc.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
filter(lambda p: p.dtls.handshake.cookie is not nullField).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_ALERT in p.dtls.record.content_type).\
|
||||
must_next()
|
||||
|
||||
# Step 7: Comm_1 connects with correct ePSKc.
|
||||
print("Step 7: Comm_1 connects with correct ePSKc.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
filter(lambda p: p.dtls.handshake.cookie is not nullField).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_FINISHED in p.dtls.handshake.type).\
|
||||
must_next()
|
||||
|
||||
# Step 8: Comm_1 sends MGMT_ACTIVE_GET.req.
|
||||
print("Step 8: Comm_1 sends MGMT_ACTIVE_GET.req.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: p.coap.opt.uri_path == ['c', 'ag']).\
|
||||
must_next()
|
||||
|
||||
active_get_rsp = pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step5).\
|
||||
filter(lambda p: p.coap.type == 2).\
|
||||
must_next()
|
||||
|
||||
# Response payload MUST contain Network Key, Ext PAN ID, Network Name, PAN ID, PSKc, Security Policy,
|
||||
# Active Timestamp, Mesh-Local Prefix, Channel, Channel Mask.
|
||||
# Note: pktverify uses 'master_key' for Network Key TLV.
|
||||
active_get_rsp.must_verify(lambda p: all(
|
||||
p.coap.tlv.has(field) for field in [
|
||||
'network_key',
|
||||
'ext_pan_id',
|
||||
'network_name',
|
||||
'pan_id',
|
||||
'pskc',
|
||||
'security_policy',
|
||||
'active_timestamp',
|
||||
'mesh_local_prefix',
|
||||
'channel',
|
||||
'channel_mask',
|
||||
]))
|
||||
|
||||
# Step 9: Comm_1 sends MGMT_PENDING_GET.req.
|
||||
print("Step 9: Comm_1 sends MGMT_PENDING_GET.req.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: p.coap.opt.uri_path == ['c', 'pg']).\
|
||||
must_next()
|
||||
|
||||
pending_get_rsp = pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step5).\
|
||||
filter(lambda p: p.coap.type == 2).\
|
||||
must_next()
|
||||
|
||||
# Response CoAP payload MUST be empty.
|
||||
assert pending_get_rsp.coap.payload is nullField, "MGMT_PENDING_GET response payload is not empty"
|
||||
|
||||
# Step 10: Comm_1 closes the DTLS connection.
|
||||
print("Step 10: Comm_1 closes the DTLS connection.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_ALERT in p.dtls.record.content_type).\
|
||||
must_next()
|
||||
|
||||
# Step 11: Comm_1 connects again (should fail).
|
||||
print("Step 11: Comm_1 connects again (should fail).")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
must_next()
|
||||
|
||||
# The DUT may either send an ALERT or simply not respond (since ephemeral key is stopped).
|
||||
# We check if there's an ALERT, but don't fail if there isn't one as long as connection doesn't succeed.
|
||||
pkts.copy().filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step5).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_FINISHED in p.dtls.handshake.type).\
|
||||
must_not_next()
|
||||
|
||||
# Step 13: Comm_1 discovers meshcop-e service (should fail).
|
||||
print("Step 13: Comm_1 discovers meshcop-e service (should fail).")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
# Step 16: Comm_1 discovers meshcop-e service.
|
||||
print("Step 16: Comm_1 discovers meshcop-e service.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
mdns_resp = pkts.filter_ipv6_src(BR_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.resp.name).\
|
||||
must_next()
|
||||
|
||||
port_step16 = mdns_resp.mdns.srv.port[0]
|
||||
print(f"Step 16 Discovered port: {port_step16}")
|
||||
|
||||
# Step 17: Comm_1 connects with correct ePSKc.
|
||||
print("Step 17: Comm_1 connects with correct ePSKc.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step16).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
filter(lambda p: p.dtls.handshake.cookie is not nullField).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step16).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_FINISHED in p.dtls.handshake.type).\
|
||||
must_next()
|
||||
|
||||
# Step 18: Comm_1 sends MGMT_ACTIVE_GET.req.
|
||||
print("Step 18: Comm_1 sends MGMT_ACTIVE_GET.req.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step16).\
|
||||
filter(lambda p: p.coap.opt.uri_path == ['c', 'ag']).\
|
||||
must_next()
|
||||
|
||||
active_get_rsp = pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step16).\
|
||||
filter(lambda p: p.coap.type == 2).\
|
||||
must_next()
|
||||
|
||||
active_get_rsp.must_verify(lambda p: all(
|
||||
p.coap.tlv.has(field) for field in [
|
||||
'network_key',
|
||||
'ext_pan_id',
|
||||
'network_name',
|
||||
'pan_id',
|
||||
'pskc',
|
||||
'security_policy',
|
||||
'active_timestamp',
|
||||
'mesh_local_prefix',
|
||||
'channel',
|
||||
'channel_mask',
|
||||
]))
|
||||
|
||||
# Step 19: Comm_1 closes the DTLS connection.
|
||||
print("Step 19: Comm_1 closes the DTLS connection.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step16).\
|
||||
filter(lambda p: consts.CONTENT_ALERT in p.dtls.record.content_type).\
|
||||
must_next()
|
||||
|
||||
# Step 22: Comm_1 discovers meshcop-e service.
|
||||
print("Step 22: Comm_1 discovers meshcop-e service.")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
mdns_resp = pkts.filter_ipv6_src(BR_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.resp.name).\
|
||||
must_next()
|
||||
|
||||
port_step22 = mdns_resp.mdns.srv.port[0]
|
||||
print(f"Step 22 Discovered port: {port_step22}")
|
||||
|
||||
# Step 23: Comm_1 connects with incorrect ePSKc 10 times.
|
||||
print("Step 23: Comm_1 connects with incorrect ePSKc 10 times.")
|
||||
for i in range(10):
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step22).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
filter(lambda p: p.dtls.handshake.cookie is not nullField).\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step22).\
|
||||
filter(lambda p: consts.CONTENT_ALERT in p.dtls.record.content_type).\
|
||||
must_next()
|
||||
|
||||
# Step 24: Comm_1 connects with correct ePSKc (should fail).
|
||||
print("Step 24: Comm_1 connects with correct ePSKc (should fail).")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst(BR_1_INFRA).\
|
||||
filter(lambda p: p.udp.dstport == port_step22).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_CLIENT_HELLO in p.dtls.handshake.type).\
|
||||
must_next()
|
||||
|
||||
# Similar to Step 11, the DUT may either send an ALERT or not respond.
|
||||
pkts.copy().filter_ipv6_src(BR_1_INFRA).\
|
||||
filter_ipv6_dst(COMM_1_LL).\
|
||||
filter(lambda p: p.udp.srcport == port_step22).\
|
||||
filter(lambda p: consts.CONTENT_HANDSHAKE in p.dtls.record.content_type).\
|
||||
filter(lambda p: consts.HANDSHAKE_FINISHED in p.dtls.handshake.type).\
|
||||
must_not_next()
|
||||
|
||||
# Step 26: Comm_1 discovers meshcop-e service (should fail).
|
||||
print("Step 26: Comm_1 discovers meshcop-e service (should fail).")
|
||||
pkts.filter_ipv6_src(COMM_1_LL).\
|
||||
filter_ipv6_dst('ff02::fb').\
|
||||
filter(lambda p: p.udp.dstport == 5353).\
|
||||
filter(lambda p: MESHCOP_E_SERVICE in p.mdns.qry.name).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Add keylog file to Wireshark preferences if it exists.
|
||||
keylog_file = '/tmp/test_1_4_CS_TC_3.keys'
|
||||
if os.path.exists(keylog_file):
|
||||
consts.WIRESHARK_OVERRIDE_PREFS['tls.keylog_file'] = keylog_file
|
||||
for port in [1234, 1235, 1236]:
|
||||
consts.WIRESHARK_DECODE_AS_ENTRIES[f'udp.port=={port}'] = 'dtls'
|
||||
consts.WIRESHARK_DECODE_AS_ENTRIES[f'dtls.port=={port}'] = 'coap'
|
||||
verify_utils.run_main(verify)
|
||||
Reference in New Issue
Block a user