1 /*
2 * Copyright (c) 2025, 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 <stdarg.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "platform/nexus_core.hpp"
34 #include "platform/nexus_node.hpp"
35
36 namespace ot {
37 namespace Nexus {
38
TestBorderAgent(void)39 void TestBorderAgent(void)
40 {
41 Core nexus;
42 Node &node0 = nexus.CreateNode();
43 Node &node1 = nexus.CreateNode();
44 Ip6::SockAddr sockAddr;
45 Pskc pskc;
46 Coap::Message *message;
47
48 Log("------------------------------------------------------------------------------------------------------");
49 Log("TestBorderAgent");
50
51 nexus.AdvanceTime(0);
52
53 // Form the topology:
54 // - node0 leader acting as Border Agent,
55 // - node1 staying disconnected (acting as candidate)
56
57 node0.Form();
58 nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
59 VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
60
61 SuccessOrQuit(node1.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
62 node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
63 node1.Get<ThreadNetif>().Up();
64
65 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
66 Log("Check Border Agent initial state");
67
68 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
69
70 SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(node0.Get<MeshCoP::BorderAgent>().GetUdpPort()));
71
72 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73 Log("Establish a DTLS connection to Border Agent");
74
75 sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
76 sockAddr.SetPort(node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
77
78 node0.Get<KeyManager>().GetPskc(pskc);
79 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
80
81 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
82 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
83
84 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
85
86 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
87
88 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
89
90 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91 Log("Disconnect from candidate side");
92
93 node1.Get<Tmf::SecureAgent>().Close();
94
95 nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
96
97 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
98 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
99
100 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101 Log("Establish a secure connection again");
102
103 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
104 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
105
106 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
107
108 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
109
110 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
111
112 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113 Log("Send `Commissioner Petition` TMF command to become full commissioner");
114
115 message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
116 VerifyOrQuit(message != nullptr);
117 SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
118 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
119
120 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
121
122 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateAccepted);
123
124 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
125 Log("Send `Commissioner Keep Alive` and check timeout behavior");
126
127 nexus.AdvanceTime(30 * Time::kOneSecondInMsec);
128
129 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateAccepted);
130 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
131
132 message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerKeepAlive);
133 VerifyOrQuit(message != nullptr);
134 SuccessOrQuit(Tlv::Append<MeshCoP::StateTlv>(*message, MeshCoP::StateTlv::kAccept));
135 SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
136 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
137
138 Log(" Wait for 49 seconds (TIMEOUT_LEAD_PET is 50 seconds) and check BA state");
139
140 nexus.AdvanceTime(49 * Time::kOneSecondInMsec);
141
142 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateAccepted);
143 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
144
145 Log(" Wait for additional 5 seconds (ensuring TIMEOUT_LEAD_PET and session disconnect guard time expires)");
146
147 nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
148
149 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
150 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
151
152 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
153 Log("Establish a secure session again and petition to become commissioner");
154
155 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
156 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
157
158 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
159 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
160
161 message = node1.Get<Tmf::SecureAgent>().NewPriorityConfirmablePostMessage(kUriCommissionerPetition);
162 VerifyOrQuit(message != nullptr);
163 SuccessOrQuit(Tlv::Append<MeshCoP::CommissionerIdTlv>(*message, "node1"));
164 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().SendMessage(*message));
165
166 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
167
168 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateAccepted);
169 }
170
171 static bool sEphemeralKeyCallbackCalled = false;
172
HandleEphemeralKeyChange(void * aContext)173 void HandleEphemeralKeyChange(void *aContext)
174 {
175 Node *node;
176
177 VerifyOrQuit(aContext != nullptr);
178 node = reinterpret_cast<Node *>(aContext);
179
180 Log(" EphemeralKeyCallback() active:%u connected:%u", node->Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive(),
181 node->Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
182 sEphemeralKeyCallbackCalled = true;
183 }
184
TestBorderAgentEphemeralKey(void)185 void TestBorderAgentEphemeralKey(void)
186 {
187 static const char kEphemeralKey[] = "nexus1234";
188 static constexpr uint16_t kEphemeralKeySize = sizeof(kEphemeralKey) - 1;
189 static constexpr uint16_t kUdpPort = 49155;
190
191 Core nexus;
192 Node &node0 = nexus.CreateNode();
193 Node &node1 = nexus.CreateNode();
194 Ip6::SockAddr sockAddr;
195 Coap::Message *message;
196
197 Log("------------------------------------------------------------------------------------------------------");
198 Log("TestBorderAgentEphemeralKey");
199
200 nexus.AdvanceTime(0);
201
202 // Form the topology:
203 // - node0 leader acting as Border Agent,
204 // - node1 staying disconnected (acting as candidate)
205
206 node0.Form();
207 nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
208 VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
209
210 SuccessOrQuit(node1.Get<Mac::Mac>().SetPanChannel(node0.Get<Mac::Mac>().GetPanChannel()));
211 node1.Get<Mac::Mac>().SetPanId(node0.Get<Mac::Mac>().GetPanId());
212 node1.Get<ThreadNetif>().Up();
213
214 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215 Log("Check Border Agent ephemeral key initial state");
216
217 sEphemeralKeyCallbackCalled = false;
218 VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
219 VerifyOrQuit(!sEphemeralKeyCallbackCalled);
220
221 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
222 Log("Set and start ephemeral key on Border Agent");
223
224 node0.Get<MeshCoP::BorderAgent>().SetEphemeralKeyCallback(HandleEphemeralKeyChange, &node0);
225
226 SuccessOrQuit(node0.Get<MeshCoP::BorderAgent>().SetEphemeralKey(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
227
228 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
229 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
230 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetUdpPort() == kUdpPort);
231
232 SuccessOrQuit(node0.Get<Ip6::Filter>().AddUnsecurePort(kUdpPort));
233
234 nexus.AdvanceTime(0);
235 VerifyOrQuit(sEphemeralKeyCallbackCalled);
236
237 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
238 Log("Establish a secure connection with BA using the ephemeral key");
239
240 sEphemeralKeyCallbackCalled = false;
241 sockAddr.SetAddress(node0.Get<Mle::Mle>().GetLinkLocalAddress());
242 sockAddr.SetPort(kUdpPort);
243
244 SuccessOrQuit(
245 node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize));
246
247 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
248 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
249
250 nexus.AdvanceTime(1 * Time::kOneSecondInMsec);
251
252 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
253 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
254 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
255 VerifyOrQuit(sEphemeralKeyCallbackCalled);
256
257 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
258 Log("Disconnect from candidate side, check that ephemeral key use is stopped");
259
260 sEphemeralKeyCallbackCalled = false;
261
262 node1.Get<Tmf::SecureAgent>().Close();
263
264 nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
265
266 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
267 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
268
269 VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
270 VerifyOrQuit(sEphemeralKeyCallbackCalled);
271
272 nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
273
274 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275 Log("Start using ephemeral key again with short timeout (20 seconds) and establish a connection");
276
277 SuccessOrQuit(
278 node0.Get<MeshCoP::BorderAgent>().SetEphemeralKey(kEphemeralKey, 20 * Time::kOneSecondInMsec, kUdpPort));
279
280 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
281 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
282 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetUdpPort() == kUdpPort);
283
284 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Open());
285 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
286
287 nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
288
289 VerifyOrQuit(node1.Get<Tmf::SecureAgent>().IsConnected());
290 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateConnected);
291 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
292
293 Log(" Check the ephemeral key timeout behavior");
294
295 sEphemeralKeyCallbackCalled = false;
296 nexus.AdvanceTime(25 * Time::kOneSecondInMsec);
297
298 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
299 VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
300 VerifyOrQuit(sEphemeralKeyCallbackCalled);
301
302 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
303 Log("Start using ephemeral key without any candidate connecting, check timeout");
304
305 SuccessOrQuit(node0.Get<MeshCoP::BorderAgent>().SetEphemeralKey(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
306
307 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
308 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
309 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetUdpPort() == kUdpPort);
310
311 Log(" Wait more than 120 seconds (default ephemeral key timeout)");
312 sEphemeralKeyCallbackCalled = false;
313 nexus.AdvanceTime(122 * Time::kOneSecondInMsec);
314
315 VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
316 VerifyOrQuit(sEphemeralKeyCallbackCalled);
317
318 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
319 Log("Check that ephemeral key use is stopped after max failed connection attempts");
320
321 SuccessOrQuit(node0.Get<MeshCoP::BorderAgent>().SetEphemeralKey(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
322
323 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() == MeshCoP::BorderAgent::kStateStarted);
324 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
325 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetUdpPort() == kUdpPort);
326
327 SuccessOrQuit(
328 node1.Get<Tmf::SecureAgent>().SetPsk(reinterpret_cast<const uint8_t *>(kEphemeralKey), kEphemeralKeySize - 2));
329
330 for (uint8_t numAttempts = 1; numAttempts < 10; numAttempts++)
331 {
332 Log(" Attempt %u to connect with the wrong key", numAttempts);
333
334 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
335
336 nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
337
338 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
339 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().GetState() != MeshCoP::BorderAgent::kStateConnected);
340 VerifyOrQuit(node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
341 }
342
343 Log(" Attempt 10 (final attempt) to connect with the wrong key, check that ephemeral key use is stopped");
344
345 sEphemeralKeyCallbackCalled = false;
346 SuccessOrQuit(node1.Get<Tmf::SecureAgent>().Connect(sockAddr));
347 nexus.AdvanceTime(3 * Time::kOneSecondInMsec);
348
349 VerifyOrQuit(!node1.Get<Tmf::SecureAgent>().IsConnected());
350 VerifyOrQuit(!node0.Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive());
351 VerifyOrQuit(sEphemeralKeyCallbackCalled);
352 }
353
354 } // namespace Nexus
355 } // namespace ot
356
main(void)357 int main(void)
358 {
359 ot::Nexus::TestBorderAgent();
360 ot::Nexus::TestBorderAgentEphemeralKey();
361 printf("All tests passed\n");
362 return 0;
363 }
364