Skip to content

Commit 9bb7e37

Browse files
authored
[nexus] add SRPC-TC-7 for SRP client key persistence (openthread#12788)
This commit adds the Nexus test case 1_3_SRPC_TC_7 which verifies that a Thread device re-registers its service with the same KEY record after a reboot, as per the Thread 1.3 test specification. The implementation includes: - tests/nexus/test_1_3_SRPC_TC_7.cpp: Executes the test sequence by forming a Thread network with a Border Router (BR_1), a Router, and a DUT (TD_1). It registers a service on the DUT, simulates a reboot using Node::Reset(), and re-registers the same service. - tests/nexus/verify_1_3_SRPC_TC_7.py: Performs automated verification of the captured traffic. It ensures that the SRP Update sent after reboot contains a KEY record identical to the one sent before reboot. It includes a monkey-patch to access the dns.key.public_key field in the packet verifier. - Integrated the new test into tests/nexus/CMakeLists.txt and tests/nexus/run_nexus_tests.sh. The test validates that the SRP client correctly persists its key material across reboots, which is essential for maintaining service registration continuity.
1 parent 1b20885 commit 9bb7e37

4 files changed

Lines changed: 402 additions & 0 deletions

File tree

tests/nexus/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ ot_nexus_test(1_3_SRP_TC_15 "cert;nexus")
278278
ot_nexus_test(1_3_SRPC_TC_1 "cert;nexus")
279279
ot_nexus_test(1_3_SRPC_TC_4 "cert;nexus")
280280
ot_nexus_test(1_3_SRPC_TC_5 "cert;nexus")
281+
ot_nexus_test(1_3_SRPC_TC_7 "cert;nexus")
281282

282283
# Misc tests
283284
ot_nexus_test(border_admitter "core;nexus")

tests/nexus/run_nexus_tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ DEFAULT_TESTS=(
213213
"1_3_SRPC_TC_1"
214214
"1_3_SRPC_TC_4"
215215
"1_3_SRPC_TC_5"
216+
"1_3_SRPC_TC_7"
216217
)
217218

218219
# Use provided arguments or the default test list

tests/nexus/test_1_3_SRPC_TC_7.cpp

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/*
2+
* Copyright (c) 2026, The OpenThread Authors.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. Neither the name of the copyright holder nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
#include <stdio.h>
30+
31+
#include "platform/nexus_core.hpp"
32+
#include "platform/nexus_node.hpp"
33+
34+
namespace ot {
35+
namespace Nexus {
36+
37+
/**
38+
* Time to advance for a node to form a network and become leader, in milliseconds.
39+
*/
40+
static constexpr uint32_t kFormNetworkTime = 13 * 1000;
41+
42+
/**
43+
* Time to advance for a node to join a network, in milliseconds.
44+
*/
45+
static constexpr uint32_t kJoinNetworkTime = 10 * 1000;
46+
47+
/**
48+
* Time to advance for the network to stabilize, in milliseconds.
49+
*/
50+
static constexpr uint32_t kStabilizationTime = 10 * 1000;
51+
52+
/**
53+
* Time to wait for SRP registration to complete.
54+
*/
55+
static constexpr uint32_t kSrpRegistrationTime = 5 * 1000;
56+
57+
/**
58+
* Infrastructure interface index.
59+
*/
60+
static constexpr uint32_t kInfraIfIndex = 1;
61+
62+
/**
63+
* SRP Lease time in seconds.
64+
*/
65+
static constexpr uint32_t kSrpLease1h = 3600;
66+
67+
/**
68+
* SRP Key Lease time in seconds.
69+
*/
70+
static constexpr uint32_t kSrpKeyLease1d = 86400;
71+
72+
/**
73+
* SRP service port.
74+
*/
75+
static constexpr uint16_t kSrpServicePort = 33333;
76+
77+
/**
78+
* SRP service and host names.
79+
*/
80+
static const char kSrpServiceType[] = "_thread-test._udp";
81+
static const char kSrpInstanceName[] = "service-test-1";
82+
static const char kSrpHostName[] = "host-test-1";
83+
84+
void Test_1_3_SRPC_TC_7(const char *aJsonFileName)
85+
{
86+
/**
87+
* 3.7. [1.3] [CERT] [COMPONENT] Thread Device Reboots - Re-registers service with same KEY
88+
*
89+
* 3.7.1. Purpose
90+
* To verify that the Thread Component DUT:
91+
* - Is able to re-register a service again after reboot.
92+
* - Uses the same key for signing the SRP Update, as indicated in the KEY record.
93+
*
94+
* 3.7.2. Topology
95+
* - BR 1 Border Router Reference Device and Leader
96+
* - Router 1-Thread Router reference device
97+
* - TD_1 (DUT)-Any Thread Device (FTD or MTD): Thread Component DUT
98+
* - Eth 1-IPv6 host reference device on the AIL
99+
*
100+
* Spec Reference | V1.1 Section | V1.3.0 Section
101+
* -----------------|--------------|---------------
102+
* SRP Client | N/A | 2.3.2
103+
*/
104+
105+
Core nexus;
106+
107+
Node &br1 = nexus.CreateNode();
108+
Node &router1 = nexus.CreateNode();
109+
Node &td1 = nexus.CreateNode();
110+
Node &eth1 = nexus.CreateNode();
111+
112+
br1.SetName("BR_1");
113+
router1.SetName("Router_1");
114+
td1.SetName("TD_1");
115+
eth1.SetName("Eth_1");
116+
117+
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
118+
119+
Log("Step 1: Enable & form topology");
120+
121+
/**
122+
* Step 1
123+
* - Device: Eth 1, BR 1, Router 1, TD 1
124+
* - Description (SRPC-3.7): Enable & form topology
125+
* - Pass Criteria:
126+
* - Single Thread Network is formed
127+
*/
128+
129+
br1.Form();
130+
nexus.AdvanceTime(kFormNetworkTime);
131+
132+
router1.Join(br1);
133+
nexus.AdvanceTime(kJoinNetworkTime);
134+
135+
td1.Join(br1, Node::kAsFed);
136+
nexus.AdvanceTime(kJoinNetworkTime);
137+
138+
SuccessOrQuit(eth1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
139+
140+
br1.Get<BorderRouter::InfraIf>().Init(kInfraIfIndex, true);
141+
br1.Get<BorderRouter::RoutingManager>().Init();
142+
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().SetEnabled(true));
143+
144+
SuccessOrQuit(br1.Get<Srp::Server>().SetAddressMode(Srp::Server::kAddressModeUnicast));
145+
br1.Get<Srp::Server>().SetEnabled(true);
146+
147+
nexus.AdvanceTime(kStabilizationTime);
148+
149+
nexus.AddTestVar("BR_1_MLEID_ADDR", br1.Get<Mle::Mle>().GetMeshLocalEid().ToString().AsCString());
150+
nexus.AddTestVar("TD_1_MLEID_ADDR", td1.Get<Mle::Mle>().GetMeshLocalEid().ToString().AsCString());
151+
nexus.AddTestVar("TD_1_OMR_ADDR", td1.FindGlobalAddress().ToString().AsCString());
152+
153+
String<6> portString;
154+
portString.Append("%u", br1.Get<Srp::Server>().GetPort());
155+
nexus.AddTestVar("BR_1_SRP_PORT", portString.AsCString());
156+
157+
Log("Step 2: Harness instructs the DUT to register the service");
158+
159+
/**
160+
* Step 2
161+
* - Device: TD 1 (DUT)
162+
* - Description (SRPC-3.7): Harness instructs the DUT to register the service: $ORIGIN default.service.arpa.
163+
* service-test-1._thread-test._udp ( SRV 33333 host-test-1 ) host-test-1 AAAA <OMR address of TD_1> with the
164+
* following options: Update Lease Option Lease: 60 minutes Key Lease: 1 day
165+
* - Pass Criteria:
166+
* - The DUT MUST send an SRP Update to BR_1
167+
* - BR_1 MUST respond Rcode=0 (NoError).
168+
*/
169+
170+
td1.Get<Srp::Client>().EnableAutoStartMode(nullptr, nullptr);
171+
SuccessOrQuit(td1.Get<Srp::Client>().SetHostName(kSrpHostName));
172+
SuccessOrQuit(td1.Get<Srp::Client>().EnableAutoHostAddress());
173+
td1.Get<Srp::Client>().SetLeaseInterval(kSrpLease1h);
174+
td1.Get<Srp::Client>().SetKeyLeaseInterval(kSrpKeyLease1d);
175+
176+
{
177+
Srp::Client::Service service;
178+
ClearAllBytes(service);
179+
service.mName = kSrpServiceType;
180+
service.mInstanceName = kSrpInstanceName;
181+
service.mPort = kSrpServicePort;
182+
SuccessOrQuit(td1.Get<Srp::Client>().AddService(service));
183+
}
184+
185+
nexus.AdvanceTime(kSrpRegistrationTime);
186+
187+
VerifyOrQuit(td1.Get<Srp::Client>().GetHostInfo().GetState() == Srp::Client::kRegistered);
188+
189+
Log("Step 3: Harness instructs device to reboot.");
190+
191+
/**
192+
* Step 3
193+
* - Device: TD 1 (DUT)
194+
* - Description (SRPC-3.7): Harness instructs device to reboot.
195+
* - Pass Criteria:
196+
* - N/A
197+
*/
198+
199+
td1.Reset();
200+
nexus.AdvanceTime(kStabilizationTime);
201+
202+
Log("Step 4: (Repeat step 2)");
203+
204+
/**
205+
* Step 4
206+
* - Device: TD 1 (DUT)
207+
* - Description (SRPC-3.7): (Repeat step 2)
208+
* - Pass Criteria:
209+
* - The DUT MUST send an SRP Update to BR_1
210+
* - The KEY record in the SRP Update MUST have an equal value to the KEY record that was sent in step 2
211+
* - BR_1 MUST respond Rcode=0 (NoError).
212+
*/
213+
214+
td1.Join(br1, Node::kAsFed);
215+
nexus.AdvanceTime(kJoinNetworkTime);
216+
217+
td1.Get<Srp::Client>().EnableAutoStartMode(nullptr, nullptr);
218+
SuccessOrQuit(td1.Get<Srp::Client>().SetHostName(kSrpHostName));
219+
SuccessOrQuit(td1.Get<Srp::Client>().EnableAutoHostAddress());
220+
td1.Get<Srp::Client>().SetLeaseInterval(kSrpLease1h);
221+
td1.Get<Srp::Client>().SetKeyLeaseInterval(kSrpKeyLease1d);
222+
223+
{
224+
Srp::Client::Service service;
225+
ClearAllBytes(service);
226+
service.mName = kSrpServiceType;
227+
service.mInstanceName = kSrpInstanceName;
228+
service.mPort = kSrpServicePort;
229+
SuccessOrQuit(td1.Get<Srp::Client>().AddService(service));
230+
}
231+
232+
nexus.AdvanceTime(kSrpRegistrationTime);
233+
234+
VerifyOrQuit(td1.Get<Srp::Client>().GetHostInfo().GetState() == Srp::Client::kRegistered);
235+
236+
nexus.SaveTestInfo(aJsonFileName);
237+
}
238+
239+
} // namespace Nexus
240+
} // namespace ot
241+
242+
int main(int argc, char *argv[])
243+
{
244+
ot::Nexus::Test_1_3_SRPC_TC_7((argc > 2) ? argv[2] : "test_1_3_SRPC_TC_7.json");
245+
printf("All tests passed\n");
246+
return 0;
247+
}

0 commit comments

Comments
 (0)