1 /*
2 * Copyright 2025 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <platform-zephyr.h>
8 #include <openthread/border_agent.h>
9 #include <openthread/cli.h>
10 #include <openthread/dns.h>
11 #include <openthread/thread.h>
12 #include <openthread/verhoeff_checksum.h>
13 #include <openthread/platform/entropy.h>
14 #include "common/code_utils.hpp"
15 #include "openthread_border_router.h"
16 #include <zephyr/sys/byteorder.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <inttypes.h>
20 #include <zephyr/kernel.h>
21 #include <zephyr/shell/shell.h>
22
23 static struct otInstance *ot_instance_ptr;
24 static bool border_agent_is_init;
25
26 #if defined(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE)
27 /* Byte values, 9 bytes for the key, one for null terminator. */
28 static uint8_t ephemeral_key_string[10];
29
30 static uint32_t ephemeral_key_timeout;
31 static bool epskc_active;
32
33 static otError generate_ephemeral_key(void);
34 static void handle_border_agent_ephemeral_key_callback(void *context);
35 static otError border_agent_enable_epskc_service(uint32_t timeout);
36
37 /* shell related functions */
38 static int border_agent_eph_key_shell_cmd(const struct shell *sh, size_t argc, char **argv);
39 static int border_agent_eph_key_run(size_t argc, char **argv);
40
41 #endif /* CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE */
42
43 static void append_vendor_txt_data(uint8_t *txt_data, uint16_t *txt_data_len);
44
border_agent_init(otInstance * instance)45 otError border_agent_init(otInstance *instance)
46 {
47 otError error = OT_ERROR_NONE;
48
49 ot_instance_ptr = instance;
50
51 if (!border_agent_is_init) {
52 uint8_t txt_buffer[128] = {0};
53 uint16_t txt_buffer_len = 0;
54
55 VerifyOrExit(otBorderAgentSetMeshCoPServiceBaseName(instance,
56 otbr_base_service_instance_name)
57 == OT_ERROR_NONE, error = OT_ERROR_FAILED);
58 append_vendor_txt_data(txt_buffer, &txt_buffer_len);
59 otBorderAgentSetVendorTxtData(instance, txt_buffer, txt_buffer_len);
60 #if defined(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE)
61 otBorderAgentEphemeralKeySetEnabled(instance, true);
62 otBorderAgentEphemeralKeySetCallback(instance,
63 handle_border_agent_ephemeral_key_callback,
64 instance);
65 #endif /* CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE */
66
67 border_agent_is_init = true;
68 }
69 exit:
70 return error;
71 }
72
border_agent_deinit(void)73 void border_agent_deinit(void)
74 {
75 #if defined(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE)
76 otBorderAgentEphemeralKeySetEnabled(ot_instance_ptr, false);
77 epskc_active = false;
78 #endif /* CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE */
79 border_agent_is_init = false;
80 }
81
append_vendor_txt_data(uint8_t * txt_data,uint16_t * txt_data_len)82 static void append_vendor_txt_data(uint8_t *txt_data, uint16_t *txt_data_len)
83 {
84 *txt_data_len = 0;
85 size_t i = 0;
86 otDnsTxtEntry txt_entries[] = {
87 {.mKey = "vn", .mValue = (uint8_t *)otbr_vendor_name,
88 .mValueLength = strlen(otbr_vendor_name)},
89 {.mKey = "mn", .mValue = (uint8_t *)otbr_model_name,
90 .mValueLength = strlen(otbr_model_name)}};
91
92 for (i = 0; i < ARRAY_SIZE(txt_entries); ++i) {
93 const otDnsTxtEntry *entry = &txt_entries[i];
94 uint8_t key_len = (uint8_t)strlen(entry->mKey);
95 uint8_t total_len = key_len + 1 + entry->mValueLength; /* +1 for '=' */
96
97 txt_data[(*txt_data_len)++] = total_len;
98 memcpy(txt_data + *txt_data_len, entry->mKey, key_len);
99 *txt_data_len += key_len;
100
101 txt_data[(*txt_data_len)++] = '=';
102 memcpy(txt_data + *txt_data_len, entry->mValue, entry->mValueLength);
103 *txt_data_len += entry->mValueLength;
104 }
105
106 }
107
108 #if defined(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE)
109
110 /** Defining functions below as weak allows applications to implement their specific behaviour,
111 * i.e., custom messages/used print function.
112 */
print_ephemeral_key(const char * ephemeral_key,uint32_t timeout)113 __weak void print_ephemeral_key(const char *ephemeral_key, uint32_t timeout)
114 {
115 otCliOutputFormat("\r\n Use this passcode to enable an additional device to administer "
116 "and manage your Thread network, including adding new devices to it. "
117 "\r\nThis passcode is not required for an app to communicate with "
118 "existing devices on your Thread network.");
119 otCliOutputFormat("\r\n\nePSKc : %s", ephemeral_key);
120 otCliOutputFormat("\r\n\nValid for %" PRIu32 " seconds.\r\n", timeout);
121 }
122
print_ephemeral_key_expired_message(void)123 __weak void print_ephemeral_key_expired_message(void)
124 {
125 otCliOutputFormat("\r\nEphemeral Key disabled.\r\n");
126 }
127
generate_ephemeral_key(void)128 static otError generate_ephemeral_key(void)
129 {
130 otError error = OT_ERROR_NONE;
131 uint8_t i = 0;
132 uint32_t random;
133 char verhoeff_checksum;
134
135 memset(ephemeral_key_string, 0, sizeof(ephemeral_key_string));
136
137 VerifyOrExit(otPlatEntropyGet((uint8_t *)&random, sizeof(random)) == OT_ERROR_NONE,
138 error = OT_ERROR_FAILED);
139 random %= 100000000;
140 i += snprintf((char *)ephemeral_key_string, sizeof(ephemeral_key_string),
141 "%08u", random);
142 VerifyOrExit(otVerhoeffChecksumCalculate((const char *)ephemeral_key_string,
143 &verhoeff_checksum) == OT_ERROR_NONE,
144 error = OT_ERROR_FAILED);
145 snprintf((char *)&ephemeral_key_string[i], sizeof(ephemeral_key_string) - i,
146 "%c", verhoeff_checksum);
147 exit:
148 return error;
149 }
150
handle_border_agent_ephemeral_key_callback(void * context)151 static void handle_border_agent_ephemeral_key_callback(void *context)
152 {
153 char formatted_epskc[12] = {0};
154 otBorderAgentEphemeralKeyState eph_key_state;
155
156 eph_key_state = otBorderAgentEphemeralKeyGetState((otInstance *)context);
157
158 switch (eph_key_state) {
159 case OT_BORDER_AGENT_STATE_STOPPED:
160 if (epskc_active) {
161 epskc_active = false;
162 print_ephemeral_key_expired_message();
163 }
164 break;
165
166 case OT_BORDER_AGENT_STATE_STARTED:
167 snprintf(formatted_epskc, sizeof(formatted_epskc), "%.3s %.3s %.3s",
168 ephemeral_key_string, ephemeral_key_string + 3,
169 ephemeral_key_string + 6);
170 print_ephemeral_key(formatted_epskc, (uint32_t)(ephemeral_key_timeout / 1000UL));
171 epskc_active = true;
172 break;
173
174 case OT_BORDER_AGENT_STATE_CONNECTED:
175 /* connected to */
176 case OT_BORDER_AGENT_STATE_ACCEPTED:
177 default:
178 break;
179 }
180 }
border_agent_enable_epskc_service(uint32_t timeout)181 static otError border_agent_enable_epskc_service(uint32_t timeout)
182 {
183 otError error = OT_ERROR_NONE;
184
185 VerifyOrExit(border_agent_is_init, error = OT_ERROR_INVALID_STATE);
186
187 ephemeral_key_timeout = (timeout &&
188 timeout >= OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT &&
189 timeout <= OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT) ?
190 timeout : OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT;
191
192 VerifyOrExit((generate_ephemeral_key() == OT_ERROR_NONE), error = OT_ERROR_FAILED);
193 error = otBorderAgentEphemeralKeyStart(ot_instance_ptr,
194 (const char *)ephemeral_key_string,
195 ephemeral_key_timeout, 0);
196
197 exit:
198 return error;
199 }
200
border_agent_eph_key_run(size_t argc,char ** argv)201 static int border_agent_eph_key_run(size_t argc, char **argv)
202 {
203 if (strcmp(argv[1], "enable") == 0) {
204 uint32_t timeout = (argc >= 1) ? strtoul(argv[2], NULL, 10) : 0;
205
206 if (border_agent_enable_epskc_service(timeout) != OT_ERROR_NONE) {
207 otCliOutputFormat("Invalid state\r\n");
208 }
209 return 0;
210 }
211
212 if (strcmp(argv[1], "help") == 0) {
213 otCliOutputFormat("ephkey enable [timeout-in-msec]\r\n");
214 }
215
216 return 0;
217 }
218
border_agent_eph_key_shell_cmd(const struct shell * sh,size_t argc,char ** argv)219 static int border_agent_eph_key_shell_cmd(const struct shell *sh, size_t argc, char **argv)
220 {
221 if (argc < 2) {
222 shell_help(sh);
223 return -ENOEXEC;
224 }
225
226 border_agent_eph_key_run(argc, argv);
227
228 return 0;
229 }
230 #endif /* CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE */
231
232 #if defined(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE)
233 SHELL_CMD_ARG_REGISTER(ephkey, NULL, "Ephemeral key support", border_agent_eph_key_shell_cmd, 2,
234 CONFIG_SHELL_ARGC_MAX);
235 #endif /* CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE */
236