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