/src/hostap/src/common/dpp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * DPP functionality shared between hostapd and wpa_supplicant |
3 | | * Copyright (c) 2017, Qualcomm Atheros, Inc. |
4 | | * Copyright (c) 2018-2020, The Linux Foundation |
5 | | * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. |
6 | | * |
7 | | * This software may be distributed under the terms of the BSD license. |
8 | | * See README for more details. |
9 | | */ |
10 | | |
11 | | #include "utils/includes.h" |
12 | | |
13 | | #include "utils/common.h" |
14 | | #include "utils/base64.h" |
15 | | #include "utils/json.h" |
16 | | #include "utils/ip_addr.h" |
17 | | #include "common/ieee802_11_common.h" |
18 | | #include "common/wpa_ctrl.h" |
19 | | #include "common/gas.h" |
20 | | #include "eap_common/eap_defs.h" |
21 | | #include "crypto/crypto.h" |
22 | | #include "crypto/random.h" |
23 | | #include "crypto/aes.h" |
24 | | #include "crypto/aes_siv.h" |
25 | | #include "drivers/driver.h" |
26 | | #include "dpp.h" |
27 | | #include "dpp_i.h" |
28 | | |
29 | | |
30 | | #ifdef CONFIG_TESTING_OPTIONS |
31 | | #ifdef CONFIG_DPP3 |
32 | | int dpp_version_override = 3; |
33 | | #elif defined(CONFIG_DPP2) |
34 | | int dpp_version_override = 2; |
35 | | #else |
36 | | int dpp_version_override = 1; |
37 | | #endif |
38 | | enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; |
39 | | #endif /* CONFIG_TESTING_OPTIONS */ |
40 | | |
41 | | |
42 | | void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) |
43 | 0 | { |
44 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); |
45 | 0 | } |
46 | | |
47 | | |
48 | | struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, |
49 | | size_t len) |
50 | 0 | { |
51 | 0 | struct wpabuf *msg; |
52 | |
|
53 | 0 | msg = wpabuf_alloc(8 + len); |
54 | 0 | if (!msg) |
55 | 0 | return NULL; |
56 | 0 | wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); |
57 | 0 | wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); |
58 | 0 | wpabuf_put_be24(msg, OUI_WFA); |
59 | 0 | wpabuf_put_u8(msg, DPP_OUI_TYPE); |
60 | 0 | wpabuf_put_u8(msg, 1); /* Crypto Suite */ |
61 | 0 | wpabuf_put_u8(msg, type); |
62 | 0 | return msg; |
63 | 0 | } |
64 | | |
65 | | |
66 | | const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) |
67 | 0 | { |
68 | 0 | u16 id, alen; |
69 | 0 | const u8 *pos = buf, *end = buf + len; |
70 | |
|
71 | 0 | while (end - pos >= 4) { |
72 | 0 | id = WPA_GET_LE16(pos); |
73 | 0 | pos += 2; |
74 | 0 | alen = WPA_GET_LE16(pos); |
75 | 0 | pos += 2; |
76 | 0 | if (alen > end - pos) |
77 | 0 | return NULL; |
78 | 0 | if (id == req_id) { |
79 | 0 | *ret_len = alen; |
80 | 0 | return pos; |
81 | 0 | } |
82 | 0 | pos += alen; |
83 | 0 | } |
84 | | |
85 | 0 | return NULL; |
86 | 0 | } |
87 | | |
88 | | |
89 | | static const u8 * dpp_get_attr_next(const u8 *prev, const u8 *buf, size_t len, |
90 | | u16 req_id, u16 *ret_len) |
91 | 0 | { |
92 | 0 | u16 id, alen; |
93 | 0 | const u8 *pos, *end = buf + len; |
94 | |
|
95 | 0 | if (!prev) |
96 | 0 | pos = buf; |
97 | 0 | else |
98 | 0 | pos = prev + WPA_GET_LE16(prev - 2); |
99 | 0 | while (end - pos >= 4) { |
100 | 0 | id = WPA_GET_LE16(pos); |
101 | 0 | pos += 2; |
102 | 0 | alen = WPA_GET_LE16(pos); |
103 | 0 | pos += 2; |
104 | 0 | if (alen > end - pos) |
105 | 0 | return NULL; |
106 | 0 | if (id == req_id) { |
107 | 0 | *ret_len = alen; |
108 | 0 | return pos; |
109 | 0 | } |
110 | 0 | pos += alen; |
111 | 0 | } |
112 | | |
113 | 0 | return NULL; |
114 | 0 | } |
115 | | |
116 | | |
117 | | int dpp_check_attrs(const u8 *buf, size_t len) |
118 | 0 | { |
119 | 0 | const u8 *pos, *end; |
120 | 0 | int wrapped_data = 0; |
121 | |
|
122 | 0 | pos = buf; |
123 | 0 | end = buf + len; |
124 | 0 | while (end - pos >= 4) { |
125 | 0 | u16 id, alen; |
126 | |
|
127 | 0 | id = WPA_GET_LE16(pos); |
128 | 0 | pos += 2; |
129 | 0 | alen = WPA_GET_LE16(pos); |
130 | 0 | pos += 2; |
131 | 0 | wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", |
132 | 0 | id, alen); |
133 | 0 | if (alen > end - pos) { |
134 | 0 | wpa_printf(MSG_DEBUG, |
135 | 0 | "DPP: Truncated message - not enough room for the attribute - dropped"); |
136 | 0 | return -1; |
137 | 0 | } |
138 | 0 | if (wrapped_data) { |
139 | 0 | wpa_printf(MSG_DEBUG, |
140 | 0 | "DPP: An unexpected attribute included after the Wrapped Data attribute"); |
141 | 0 | return -1; |
142 | 0 | } |
143 | 0 | if (id == DPP_ATTR_WRAPPED_DATA) |
144 | 0 | wrapped_data = 1; |
145 | 0 | pos += alen; |
146 | 0 | } |
147 | | |
148 | 0 | if (end != pos) { |
149 | 0 | wpa_printf(MSG_DEBUG, |
150 | 0 | "DPP: Unexpected octets (%d) after the last attribute", |
151 | 0 | (int) (end - pos)); |
152 | 0 | return -1; |
153 | 0 | } |
154 | | |
155 | 0 | return 0; |
156 | 0 | } |
157 | | |
158 | | |
159 | | void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) |
160 | 1.40k | { |
161 | 1.40k | if (!info) |
162 | 0 | return; |
163 | 1.40k | os_free(info->uri); |
164 | 1.40k | os_free(info->info); |
165 | 1.40k | os_free(info->chan); |
166 | 1.40k | os_free(info->host); |
167 | 1.40k | os_free(info->pk); |
168 | 1.40k | crypto_ec_key_deinit(info->pubkey); |
169 | 1.40k | str_clear_free(info->configurator_params); |
170 | 1.40k | os_free(info); |
171 | 1.40k | } |
172 | | |
173 | | |
174 | | const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) |
175 | 41 | { |
176 | 41 | switch (type) { |
177 | 41 | case DPP_BOOTSTRAP_QR_CODE: |
178 | 41 | return "QRCODE"; |
179 | 0 | case DPP_BOOTSTRAP_PKEX: |
180 | 0 | return "PKEX"; |
181 | 0 | case DPP_BOOTSTRAP_NFC_URI: |
182 | 0 | return "NFC-URI"; |
183 | 41 | } |
184 | 0 | return "??"; |
185 | 41 | } |
186 | | |
187 | | |
188 | | static int dpp_uri_valid_info(const char *info) |
189 | 47 | { |
190 | 1.04M | while (*info) { |
191 | 1.04M | unsigned char val = *info++; |
192 | | |
193 | 1.04M | if (val < 0x20 || val > 0x7e || val == 0x3b) |
194 | 13 | return 0; |
195 | 1.04M | } |
196 | | |
197 | 34 | return 1; |
198 | 47 | } |
199 | | |
200 | | |
201 | | static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) |
202 | 1.40k | { |
203 | 1.40k | bi->uri = os_strdup(uri); |
204 | 1.40k | return bi->uri ? 0 : -1; |
205 | 1.40k | } |
206 | | |
207 | | |
208 | | int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, |
209 | | const char *chan_list) |
210 | 1.40k | { |
211 | 1.40k | const char *pos = chan_list, *pos2; |
212 | 1.40k | int opclass = -1, channel, freq; |
213 | | |
214 | 11.8k | while (pos && *pos && *pos != ';') { |
215 | 11.2k | pos2 = pos; |
216 | 31.1k | while (*pos2 >= '0' && *pos2 <= '9') |
217 | 19.8k | pos2++; |
218 | 11.2k | if (*pos2 == '/') { |
219 | 1.05k | opclass = atoi(pos); |
220 | 1.05k | pos = pos2 + 1; |
221 | 1.05k | } |
222 | 11.2k | if (opclass <= 0) |
223 | 75 | goto fail; |
224 | 11.2k | channel = atoi(pos); |
225 | 11.2k | if (channel <= 0) |
226 | 74 | goto fail; |
227 | 29.7k | while (*pos >= '0' && *pos <= '9') |
228 | 18.6k | pos++; |
229 | 11.1k | freq = ieee80211_chan_to_freq(NULL, opclass, channel); |
230 | 11.1k | wpa_printf(MSG_DEBUG, |
231 | 11.1k | "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", |
232 | 11.1k | opclass, channel, freq); |
233 | 11.1k | bi->channels_listed = true; |
234 | 11.1k | if (freq < 0) { |
235 | 9.65k | wpa_printf(MSG_DEBUG, |
236 | 9.65k | "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", |
237 | 9.65k | opclass, channel); |
238 | 9.65k | } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { |
239 | 17 | wpa_printf(MSG_DEBUG, |
240 | 17 | "DPP: Too many channels in URI channel-list - ignore list"); |
241 | 17 | bi->num_freq = 0; |
242 | 17 | break; |
243 | 1.47k | } else { |
244 | 1.47k | bi->freq[bi->num_freq++] = freq; |
245 | 1.47k | } |
246 | | |
247 | 11.1k | if (*pos == ';' || *pos == '\0') |
248 | 709 | break; |
249 | 10.4k | if (*pos != ',') |
250 | 14 | goto fail; |
251 | 10.4k | pos++; |
252 | 10.4k | } |
253 | | |
254 | 1.24k | return 0; |
255 | 163 | fail: |
256 | 163 | wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); |
257 | 163 | return -1; |
258 | 1.40k | } |
259 | | |
260 | | |
261 | | int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) |
262 | 1.24k | { |
263 | 1.24k | if (!mac) |
264 | 1.09k | return 0; |
265 | | |
266 | 146 | if (hwaddr_aton2(mac, bi->mac_addr) < 0) { |
267 | 127 | wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); |
268 | 127 | return -1; |
269 | 127 | } |
270 | | |
271 | 19 | wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); |
272 | | |
273 | 19 | return 0; |
274 | 146 | } |
275 | | |
276 | | |
277 | | int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) |
278 | 1.11k | { |
279 | 1.11k | const char *end; |
280 | | |
281 | 1.11k | if (!info) |
282 | 1.06k | return 0; |
283 | | |
284 | 47 | end = os_strchr(info, ';'); |
285 | 47 | if (!end) |
286 | 0 | end = info + os_strlen(info); |
287 | 47 | bi->info = os_malloc(end - info + 1); |
288 | 47 | if (!bi->info) |
289 | 0 | return -1; |
290 | 47 | os_memcpy(bi->info, info, end - info); |
291 | 47 | bi->info[end - info] = '\0'; |
292 | 47 | wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); |
293 | 47 | if (!dpp_uri_valid_info(bi->info)) { |
294 | 13 | wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); |
295 | 13 | return -1; |
296 | 13 | } |
297 | | |
298 | 34 | return 0; |
299 | 47 | } |
300 | | |
301 | | |
302 | | int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version) |
303 | 1.10k | { |
304 | 1.10k | #ifdef CONFIG_DPP2 |
305 | 1.10k | if (!version || DPP_VERSION < 2) |
306 | 1.08k | return 0; |
307 | | |
308 | 19 | if (*version == '1') |
309 | 1 | bi->version = 1; |
310 | 18 | else if (*version == '2') |
311 | 1 | bi->version = 2; |
312 | 17 | else if (*version == '3') |
313 | 1 | bi->version = 3; |
314 | 16 | else |
315 | 16 | wpa_printf(MSG_DEBUG, "DPP: Unknown URI version"); |
316 | | |
317 | 19 | wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version); |
318 | 19 | #endif /* CONFIG_DPP2 */ |
319 | | |
320 | 19 | return 0; |
321 | 1.10k | } |
322 | | |
323 | | |
324 | | static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) |
325 | 1.03k | { |
326 | 1.03k | u8 *data; |
327 | 1.03k | size_t data_len; |
328 | 1.03k | int res; |
329 | 1.03k | const char *end; |
330 | | |
331 | 1.03k | end = os_strchr(info, ';'); |
332 | 1.03k | if (!end) |
333 | 0 | return -1; |
334 | | |
335 | 1.03k | data = base64_decode(info, end - info, &data_len); |
336 | 1.03k | if (!data) { |
337 | 881 | wpa_printf(MSG_DEBUG, |
338 | 881 | "DPP: Invalid base64 encoding on URI public-key"); |
339 | 881 | return -1; |
340 | 881 | } |
341 | 151 | wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", |
342 | 151 | data, data_len); |
343 | | |
344 | 151 | res = dpp_get_subject_public_key(bi, data, data_len); |
345 | 151 | os_free(data); |
346 | 151 | return res; |
347 | 1.03k | } |
348 | | |
349 | | |
350 | | static int dpp_parse_uri_supported_curves(struct dpp_bootstrap_info *bi, |
351 | | const char *txt) |
352 | 1.10k | { |
353 | 1.10k | int val; |
354 | | |
355 | 1.10k | if (!txt) |
356 | 1.03k | return 0; |
357 | | |
358 | 64 | val = hex2num(txt[0]); |
359 | 64 | if (val < 0) |
360 | 25 | return -1; |
361 | 39 | bi->supported_curves = val; |
362 | | |
363 | 39 | val = hex2num(txt[1]); |
364 | 39 | if (val > 0) |
365 | 21 | bi->supported_curves |= val << 4; |
366 | | |
367 | 39 | wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x", |
368 | 39 | bi->supported_curves); |
369 | | |
370 | 39 | return 0; |
371 | 64 | } |
372 | | |
373 | | |
374 | | static int dpp_parse_uri_host(struct dpp_bootstrap_info *bi, const char *txt) |
375 | 1.07k | { |
376 | 1.07k | const char *end; |
377 | 1.07k | char *port; |
378 | 1.07k | struct hostapd_ip_addr addr; |
379 | 1.07k | char buf[100], *pos; |
380 | | |
381 | 1.07k | if (!txt) |
382 | 1.00k | return 0; |
383 | | |
384 | 74 | end = os_strchr(txt, ';'); |
385 | 74 | if (!end) |
386 | 0 | end = txt + os_strlen(txt); |
387 | 74 | if (end - txt > (int) sizeof(buf) - 1) |
388 | 13 | return -1; |
389 | 61 | os_memcpy(buf, txt, end - txt); |
390 | 61 | buf[end - txt] = '\0'; |
391 | | |
392 | 61 | bi->port = DPP_TCP_PORT; |
393 | | |
394 | 61 | pos = buf; |
395 | 61 | if (*pos == '[') { |
396 | 16 | pos = &buf[1]; |
397 | 16 | port = os_strchr(pos, ']'); |
398 | 16 | if (!port) |
399 | 3 | return -1; |
400 | 13 | *port++ = '\0'; |
401 | 13 | if (*port == ':') |
402 | 1 | bi->port = atoi(port + 1); |
403 | 13 | } |
404 | | |
405 | 58 | if (hostapd_parse_ip_addr(pos, &addr) < 0) { |
406 | 40 | if (buf[0] != '[') { |
407 | 27 | port = os_strrchr(pos, ':'); |
408 | 27 | if (port) { |
409 | 15 | *port++ = '\0'; |
410 | 15 | bi->port = atoi(port); |
411 | 15 | } |
412 | 27 | } |
413 | 40 | if (hostapd_parse_ip_addr(pos, &addr) < 0) { |
414 | 30 | wpa_printf(MSG_INFO, |
415 | 30 | "DPP: Invalid IP address in URI host entry: %s", |
416 | 30 | pos); |
417 | 30 | return -1; |
418 | 30 | } |
419 | 40 | } |
420 | 28 | os_free(bi->host); |
421 | 28 | bi->host = os_memdup(&addr, sizeof(addr)); |
422 | 28 | if (!bi->host) |
423 | 0 | return -1; |
424 | | |
425 | 28 | wpa_printf(MSG_DEBUG, "DPP: host: %s port: %u", |
426 | 28 | hostapd_ip_txt(bi->host, buf, sizeof(buf)), bi->port); |
427 | | |
428 | 28 | return 0; |
429 | 28 | } |
430 | | |
431 | | |
432 | | static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) |
433 | 1.67k | { |
434 | 1.67k | const char *pos = uri; |
435 | 1.67k | const char *end; |
436 | 1.67k | const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; |
437 | 1.67k | const char *version = NULL, *supported_curves = NULL, *host = NULL; |
438 | 1.67k | struct dpp_bootstrap_info *bi; |
439 | | |
440 | 1.67k | wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); |
441 | | |
442 | 1.67k | if (os_strncmp(pos, "DPP:", 4) != 0) { |
443 | 37 | wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); |
444 | 37 | return NULL; |
445 | 37 | } |
446 | 1.63k | pos += 4; |
447 | | |
448 | 7.76k | for (;;) { |
449 | 7.76k | end = os_strchr(pos, ';'); |
450 | 7.76k | if (!end) |
451 | 1.63k | break; |
452 | | |
453 | 6.13k | if (end == pos) { |
454 | | /* Handle terminating ";;" and ignore unexpected ";" |
455 | | * for parsing robustness. */ |
456 | 211 | pos++; |
457 | 211 | continue; |
458 | 211 | } |
459 | | |
460 | 5.91k | if (pos[0] == 'C' && pos[1] == ':' && !chan_list) |
461 | 911 | chan_list = pos + 2; |
462 | 5.00k | else if (pos[0] == 'M' && pos[1] == ':' && !mac) |
463 | 155 | mac = pos + 2; |
464 | 4.85k | else if (pos[0] == 'I' && pos[1] == ':' && !info) |
465 | 56 | info = pos + 2; |
466 | 4.79k | else if (pos[0] == 'K' && pos[1] == ':' && !pk) |
467 | 1.40k | pk = pos + 2; |
468 | 3.39k | else if (pos[0] == 'V' && pos[1] == ':' && !version) |
469 | 28 | version = pos + 2; |
470 | 3.36k | else if (pos[0] == 'B' && pos[1] == ':' && !supported_curves) |
471 | 73 | supported_curves = pos + 2; |
472 | 3.29k | else if (pos[0] == 'H' && pos[1] == ':' && !host) |
473 | 83 | host = pos + 2; |
474 | 3.20k | else |
475 | 3.20k | wpa_hexdump_ascii(MSG_DEBUG, |
476 | 3.20k | "DPP: Ignore unrecognized URI parameter", |
477 | 3.20k | pos, end - pos); |
478 | 5.91k | pos = end + 1; |
479 | 5.91k | } |
480 | | |
481 | 1.63k | if (!pk) { |
482 | 233 | wpa_printf(MSG_INFO, "DPP: URI missing public-key"); |
483 | 233 | return NULL; |
484 | 233 | } |
485 | | |
486 | 1.40k | bi = os_zalloc(sizeof(*bi)); |
487 | 1.40k | if (!bi) |
488 | 0 | return NULL; |
489 | | |
490 | 1.40k | if (dpp_clone_uri(bi, uri) < 0 || |
491 | 1.40k | dpp_parse_uri_chan_list(bi, chan_list) < 0 || |
492 | 1.40k | dpp_parse_uri_mac(bi, mac) < 0 || |
493 | 1.40k | dpp_parse_uri_info(bi, info) < 0 || |
494 | 1.40k | dpp_parse_uri_version(bi, version) < 0 || |
495 | 1.40k | dpp_parse_uri_supported_curves(bi, supported_curves) < 0 || |
496 | 1.40k | dpp_parse_uri_host(bi, host) < 0 || |
497 | 1.40k | dpp_parse_uri_pk(bi, pk) < 0) { |
498 | 1.36k | dpp_bootstrap_info_free(bi); |
499 | 1.36k | bi = NULL; |
500 | 1.36k | } |
501 | | |
502 | 1.40k | return bi; |
503 | 1.40k | } |
504 | | |
505 | | |
506 | | void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status) |
507 | 0 | { |
508 | 0 | wpa_printf(MSG_DEBUG, "DPP: Status %d", status); |
509 | 0 | wpabuf_put_le16(msg, DPP_ATTR_STATUS); |
510 | 0 | wpabuf_put_le16(msg, 1); |
511 | 0 | wpabuf_put_u8(msg, status); |
512 | 0 | } |
513 | | |
514 | | |
515 | | void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash) |
516 | 0 | { |
517 | 0 | if (hash) { |
518 | 0 | wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); |
519 | 0 | wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); |
520 | 0 | wpabuf_put_le16(msg, SHA256_MAC_LEN); |
521 | 0 | wpabuf_put_data(msg, hash, SHA256_MAC_LEN); |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | |
526 | | static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, |
527 | | u16 num_modes, unsigned int freq) |
528 | 0 | { |
529 | 0 | u16 m; |
530 | 0 | int c, flag; |
531 | |
|
532 | 0 | if (!own_modes || !num_modes) |
533 | 0 | return 1; |
534 | | |
535 | 0 | for (m = 0; m < num_modes; m++) { |
536 | 0 | for (c = 0; c < own_modes[m].num_channels; c++) { |
537 | 0 | if ((unsigned int) own_modes[m].channels[c].freq != |
538 | 0 | freq) |
539 | 0 | continue; |
540 | 0 | flag = own_modes[m].channels[c].flag; |
541 | 0 | if (!(flag & (HOSTAPD_CHAN_DISABLED | |
542 | 0 | HOSTAPD_CHAN_NO_IR | |
543 | 0 | HOSTAPD_CHAN_RADAR))) |
544 | 0 | return 1; |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | 0 | wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); |
549 | 0 | return 0; |
550 | 0 | } |
551 | | |
552 | | |
553 | | static int freq_included(const unsigned int freqs[], unsigned int num, |
554 | | unsigned int freq) |
555 | 0 | { |
556 | 0 | while (num > 0) { |
557 | 0 | if (freqs[--num] == freq) |
558 | 0 | return 1; |
559 | 0 | } |
560 | 0 | return 0; |
561 | 0 | } |
562 | | |
563 | | |
564 | | static void freq_to_start(unsigned int freqs[], unsigned int num, |
565 | | unsigned int freq) |
566 | 0 | { |
567 | 0 | unsigned int i; |
568 | |
|
569 | 0 | for (i = 0; i < num; i++) { |
570 | 0 | if (freqs[i] == freq) |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | if (i == 0 || i >= num) |
574 | 0 | return; |
575 | 0 | os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); |
576 | 0 | freqs[0] = freq; |
577 | 0 | } |
578 | | |
579 | | |
580 | | static int dpp_channel_intersect(struct dpp_authentication *auth, |
581 | | struct hostapd_hw_modes *own_modes, |
582 | | u16 num_modes) |
583 | 0 | { |
584 | 0 | struct dpp_bootstrap_info *peer_bi = auth->peer_bi; |
585 | 0 | unsigned int i, freq; |
586 | |
|
587 | 0 | for (i = 0; i < peer_bi->num_freq; i++) { |
588 | 0 | freq = peer_bi->freq[i]; |
589 | 0 | if (freq_included(auth->freq, auth->num_freq, freq)) |
590 | 0 | continue; |
591 | 0 | if (dpp_channel_ok_init(own_modes, num_modes, freq)) |
592 | 0 | auth->freq[auth->num_freq++] = freq; |
593 | 0 | } |
594 | 0 | if (!auth->num_freq) { |
595 | 0 | wpa_printf(MSG_INFO, |
596 | 0 | "DPP: No available channels for initiating DPP Authentication"); |
597 | 0 | return -1; |
598 | 0 | } |
599 | 0 | auth->curr_freq = auth->freq[0]; |
600 | 0 | return 0; |
601 | 0 | } |
602 | | |
603 | | |
604 | | static int dpp_channel_local_list(struct dpp_authentication *auth, |
605 | | struct hostapd_hw_modes *own_modes, |
606 | | u16 num_modes) |
607 | 0 | { |
608 | 0 | u16 m; |
609 | 0 | int c, flag; |
610 | 0 | unsigned int freq; |
611 | |
|
612 | 0 | auth->num_freq = 0; |
613 | |
|
614 | 0 | if (!own_modes || !num_modes) { |
615 | 0 | auth->freq[0] = 2412; |
616 | 0 | auth->freq[1] = 2437; |
617 | 0 | auth->freq[2] = 2462; |
618 | 0 | auth->num_freq = 3; |
619 | 0 | return 0; |
620 | 0 | } |
621 | | |
622 | 0 | for (m = 0; m < num_modes; m++) { |
623 | 0 | for (c = 0; c < own_modes[m].num_channels; c++) { |
624 | 0 | freq = own_modes[m].channels[c].freq; |
625 | 0 | flag = own_modes[m].channels[c].flag; |
626 | 0 | if (flag & (HOSTAPD_CHAN_DISABLED | |
627 | 0 | HOSTAPD_CHAN_NO_IR | |
628 | 0 | HOSTAPD_CHAN_RADAR)) |
629 | 0 | continue; |
630 | 0 | if (freq_included(auth->freq, auth->num_freq, freq)) |
631 | 0 | continue; |
632 | 0 | auth->freq[auth->num_freq++] = freq; |
633 | 0 | if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { |
634 | 0 | m = num_modes; |
635 | 0 | break; |
636 | 0 | } |
637 | 0 | } |
638 | 0 | } |
639 | |
|
640 | 0 | return auth->num_freq == 0 ? -1 : 0; |
641 | 0 | } |
642 | | |
643 | | |
644 | | int dpp_prepare_channel_list(struct dpp_authentication *auth, |
645 | | unsigned int neg_freq, |
646 | | struct hostapd_hw_modes *own_modes, u16 num_modes) |
647 | 0 | { |
648 | 0 | int res; |
649 | 0 | char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; |
650 | 0 | unsigned int i; |
651 | |
|
652 | 0 | if (!own_modes) { |
653 | 0 | if (!neg_freq) |
654 | 0 | return -1; |
655 | 0 | auth->num_freq = 1; |
656 | 0 | auth->freq[0] = neg_freq; |
657 | 0 | auth->curr_freq = neg_freq; |
658 | 0 | return 0; |
659 | 0 | } |
660 | | |
661 | 0 | if (auth->peer_bi->num_freq > 0) |
662 | 0 | res = dpp_channel_intersect(auth, own_modes, num_modes); |
663 | 0 | else |
664 | 0 | res = dpp_channel_local_list(auth, own_modes, num_modes); |
665 | 0 | if (res < 0) |
666 | 0 | return res; |
667 | | |
668 | | /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most |
669 | | * likely channels first. */ |
670 | 0 | freq_to_start(auth->freq, auth->num_freq, 2462); |
671 | 0 | freq_to_start(auth->freq, auth->num_freq, 2412); |
672 | 0 | freq_to_start(auth->freq, auth->num_freq, 2437); |
673 | |
|
674 | 0 | auth->freq_idx = 0; |
675 | 0 | auth->curr_freq = auth->freq[0]; |
676 | |
|
677 | 0 | pos = freqs; |
678 | 0 | end = pos + sizeof(freqs); |
679 | 0 | for (i = 0; i < auth->num_freq; i++) { |
680 | 0 | res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); |
681 | 0 | if (os_snprintf_error(end - pos, res)) |
682 | 0 | break; |
683 | 0 | pos += res; |
684 | 0 | } |
685 | 0 | *pos = '\0'; |
686 | 0 | wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", |
687 | 0 | freqs); |
688 | |
|
689 | 0 | return 0; |
690 | 0 | } |
691 | | |
692 | | |
693 | | int dpp_gen_uri(struct dpp_bootstrap_info *bi) |
694 | 0 | { |
695 | 0 | char macstr[ETH_ALEN * 2 + 10]; |
696 | 0 | size_t len; |
697 | 0 | char supp_curves[10]; |
698 | 0 | char host[100]; |
699 | |
|
700 | 0 | len = 4; /* "DPP:" */ |
701 | 0 | if (bi->chan) |
702 | 0 | len += 3 + os_strlen(bi->chan); /* C:...; */ |
703 | 0 | if (is_zero_ether_addr(bi->mac_addr)) |
704 | 0 | macstr[0] = '\0'; |
705 | 0 | else |
706 | 0 | os_snprintf(macstr, sizeof(macstr), "M:" COMPACT_MACSTR ";", |
707 | 0 | MAC2STR(bi->mac_addr)); |
708 | 0 | len += os_strlen(macstr); /* M:...; */ |
709 | 0 | if (bi->info) |
710 | 0 | len += 3 + os_strlen(bi->info); /* I:...; */ |
711 | 0 | #ifdef CONFIG_DPP2 |
712 | 0 | len += 4; /* V:2; */ |
713 | 0 | #endif /* CONFIG_DPP2 */ |
714 | 0 | len += 4 + os_strlen(bi->pk); /* K:...;; */ |
715 | |
|
716 | 0 | if (bi->supported_curves) { |
717 | 0 | u8 val = bi->supported_curves; |
718 | |
|
719 | 0 | if (val & 0xf0) { |
720 | 0 | val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); |
721 | 0 | len += os_snprintf(supp_curves, sizeof(supp_curves), |
722 | 0 | "B:%02x;", val); |
723 | 0 | } else { |
724 | 0 | len += os_snprintf(supp_curves, sizeof(supp_curves), |
725 | 0 | "B:%x;", val); |
726 | 0 | } |
727 | 0 | } else { |
728 | 0 | supp_curves[0] = '\0'; |
729 | 0 | } |
730 | |
|
731 | 0 | host[0] = '\0'; |
732 | 0 | if (bi->host) { |
733 | 0 | char buf[100]; |
734 | 0 | const char *addr; |
735 | |
|
736 | 0 | addr = hostapd_ip_txt(bi->host, buf, sizeof(buf)); |
737 | 0 | if (!addr) |
738 | 0 | return -1; |
739 | 0 | if (bi->port == DPP_TCP_PORT) |
740 | 0 | len += os_snprintf(host, sizeof(host), "H:%s;", addr); |
741 | 0 | else if (bi->host->af == AF_INET) |
742 | 0 | len += os_snprintf(host, sizeof(host), "H:%s:%u;", |
743 | 0 | addr, bi->port); |
744 | 0 | else |
745 | 0 | len += os_snprintf(host, sizeof(host), "H:[%s]:%u;", |
746 | 0 | addr, bi->port); |
747 | 0 | } |
748 | | |
749 | 0 | os_free(bi->uri); |
750 | 0 | bi->uri = os_malloc(len + 1); |
751 | 0 | if (!bi->uri) |
752 | 0 | return -1; |
753 | 0 | os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%s%sK:%s;;", |
754 | 0 | bi->chan ? "C:" : "", bi->chan ? bi->chan : "", |
755 | 0 | bi->chan ? ";" : "", |
756 | 0 | macstr, |
757 | 0 | bi->info ? "I:" : "", bi->info ? bi->info : "", |
758 | 0 | bi->info ? ";" : "", |
759 | 0 | DPP_VERSION == 3 ? "V:3;" : |
760 | 0 | (DPP_VERSION == 2 ? "V:2;" : ""), |
761 | 0 | supp_curves, |
762 | 0 | host, |
763 | 0 | bi->pk); |
764 | 0 | return 0; |
765 | 0 | } |
766 | | |
767 | | |
768 | | struct dpp_authentication * |
769 | | dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx) |
770 | 0 | { |
771 | 0 | struct dpp_authentication *auth; |
772 | |
|
773 | 0 | auth = os_zalloc(sizeof(*auth)); |
774 | 0 | if (!auth) |
775 | 0 | return NULL; |
776 | 0 | auth->global = dpp; |
777 | 0 | auth->msg_ctx = msg_ctx; |
778 | 0 | auth->conf_resp_status = 255; |
779 | 0 | return auth; |
780 | 0 | } |
781 | | |
782 | | |
783 | | static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, |
784 | | const char *json) |
785 | 0 | { |
786 | 0 | size_t nonce_len; |
787 | 0 | size_t json_len, clear_len; |
788 | 0 | struct wpabuf *clear = NULL, *msg = NULL, *pe = NULL; |
789 | 0 | u8 *wrapped; |
790 | 0 | size_t attr_len; |
791 | | #ifdef CONFIG_DPP3 |
792 | | u8 auth_i[DPP_MAX_HASH_LEN]; |
793 | | #endif /* CONFIG_DPP3 */ |
794 | |
|
795 | 0 | wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); |
796 | |
|
797 | 0 | nonce_len = auth->curve->nonce_len; |
798 | 0 | if (random_get_bytes(auth->e_nonce, nonce_len)) { |
799 | 0 | wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); |
800 | 0 | goto fail; |
801 | 0 | } |
802 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); |
803 | 0 | json_len = os_strlen(json); |
804 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: configRequest JSON", json, json_len); |
805 | | |
806 | | /* { E-nonce, configAttrib }ke */ |
807 | 0 | clear_len = 4 + nonce_len + 4 + json_len; |
808 | | #ifdef CONFIG_DPP3 |
809 | | if (auth->waiting_new_key) { |
810 | | pe = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0); |
811 | | if (!pe) |
812 | | goto fail; |
813 | | clear_len += 4 + wpabuf_len(pe); |
814 | | |
815 | | if (dpp_derive_auth_i(auth, auth_i) < 0) |
816 | | goto fail; |
817 | | clear_len += 4 + auth->curve->hash_len; |
818 | | } |
819 | | #endif /* CONFIG_DPP3 */ |
820 | 0 | clear = wpabuf_alloc(clear_len); |
821 | 0 | attr_len = 4 + clear_len + AES_BLOCK_SIZE; |
822 | | #ifdef CONFIG_TESTING_OPTIONS |
823 | | if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) |
824 | | attr_len += 5; |
825 | | #endif /* CONFIG_TESTING_OPTIONS */ |
826 | 0 | msg = wpabuf_alloc(attr_len); |
827 | 0 | if (!clear || !msg) |
828 | 0 | goto fail; |
829 | | |
830 | | #ifdef CONFIG_TESTING_OPTIONS |
831 | | if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { |
832 | | wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); |
833 | | goto skip_e_nonce; |
834 | | } |
835 | | if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { |
836 | | wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); |
837 | | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
838 | | wpabuf_put_le16(clear, nonce_len - 1); |
839 | | wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); |
840 | | goto skip_e_nonce; |
841 | | } |
842 | | if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { |
843 | | wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); |
844 | | goto skip_wrapped_data; |
845 | | } |
846 | | #endif /* CONFIG_TESTING_OPTIONS */ |
847 | | |
848 | | /* E-nonce */ |
849 | 0 | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
850 | 0 | wpabuf_put_le16(clear, nonce_len); |
851 | 0 | wpabuf_put_data(clear, auth->e_nonce, nonce_len); |
852 | |
|
853 | | #ifdef CONFIG_TESTING_OPTIONS |
854 | | skip_e_nonce: |
855 | | if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { |
856 | | wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); |
857 | | goto skip_conf_attr_obj; |
858 | | } |
859 | | #endif /* CONFIG_TESTING_OPTIONS */ |
860 | |
|
861 | | #ifdef CONFIG_DPP3 |
862 | | if (pe) { |
863 | | wpa_printf(MSG_DEBUG, "DPP: Pe"); |
864 | | wpabuf_put_le16(clear, DPP_ATTR_I_PROTOCOL_KEY); |
865 | | wpabuf_put_le16(clear, wpabuf_len(pe)); |
866 | | wpabuf_put_buf(clear, pe); |
867 | | } |
868 | | if (auth->waiting_new_key) { |
869 | | wpa_printf(MSG_DEBUG, "DPP: Initiator Authentication Tag"); |
870 | | wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); |
871 | | wpabuf_put_le16(clear, auth->curve->hash_len); |
872 | | wpabuf_put_data(clear, auth_i, auth->curve->hash_len); |
873 | | } |
874 | | #endif /* CONFIG_DPP3 */ |
875 | | |
876 | | /* configAttrib */ |
877 | 0 | wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); |
878 | 0 | wpabuf_put_le16(clear, json_len); |
879 | 0 | wpabuf_put_data(clear, json, json_len); |
880 | |
|
881 | | #ifdef CONFIG_TESTING_OPTIONS |
882 | | skip_conf_attr_obj: |
883 | | #endif /* CONFIG_TESTING_OPTIONS */ |
884 | |
|
885 | 0 | wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); |
886 | 0 | wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
887 | 0 | wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
888 | | |
889 | | /* No AES-SIV AD */ |
890 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); |
891 | 0 | if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, |
892 | 0 | wpabuf_head(clear), wpabuf_len(clear), |
893 | 0 | 0, NULL, NULL, wrapped) < 0) |
894 | 0 | goto fail; |
895 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
896 | 0 | wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); |
897 | |
|
898 | | #ifdef CONFIG_TESTING_OPTIONS |
899 | | if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { |
900 | | wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); |
901 | | dpp_build_attr_status(msg, DPP_STATUS_OK); |
902 | | } |
903 | | skip_wrapped_data: |
904 | | #endif /* CONFIG_TESTING_OPTIONS */ |
905 | |
|
906 | 0 | wpa_hexdump_buf(MSG_DEBUG, |
907 | 0 | "DPP: Configuration Request frame attributes", msg); |
908 | 0 | out: |
909 | 0 | wpabuf_free(clear); |
910 | 0 | wpabuf_free(pe); |
911 | 0 | return msg; |
912 | | |
913 | 0 | fail: |
914 | 0 | wpabuf_free(msg); |
915 | 0 | msg = NULL; |
916 | 0 | goto out; |
917 | 0 | } |
918 | | |
919 | | |
920 | | void dpp_write_adv_proto(struct wpabuf *buf) |
921 | 0 | { |
922 | | /* Advertisement Protocol IE */ |
923 | 0 | wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); |
924 | 0 | wpabuf_put_u8(buf, 8); /* Length */ |
925 | 0 | wpabuf_put_u8(buf, 0x7f); |
926 | 0 | wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); |
927 | 0 | wpabuf_put_u8(buf, 5); |
928 | 0 | wpabuf_put_be24(buf, OUI_WFA); |
929 | 0 | wpabuf_put_u8(buf, DPP_OUI_TYPE); |
930 | 0 | wpabuf_put_u8(buf, 0x01); |
931 | 0 | } |
932 | | |
933 | | |
934 | | void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) |
935 | 0 | { |
936 | | /* GAS Query */ |
937 | 0 | wpabuf_put_le16(buf, wpabuf_len(query)); |
938 | 0 | wpabuf_put_buf(buf, query); |
939 | 0 | } |
940 | | |
941 | | |
942 | | struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, |
943 | | const char *json) |
944 | 0 | { |
945 | 0 | struct wpabuf *buf, *conf_req; |
946 | |
|
947 | 0 | conf_req = dpp_build_conf_req_attr(auth, json); |
948 | 0 | if (!conf_req) { |
949 | 0 | wpa_printf(MSG_DEBUG, |
950 | 0 | "DPP: No configuration request data available"); |
951 | 0 | return NULL; |
952 | 0 | } |
953 | | |
954 | 0 | buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); |
955 | 0 | if (!buf) { |
956 | 0 | wpabuf_free(conf_req); |
957 | 0 | return NULL; |
958 | 0 | } |
959 | | |
960 | 0 | dpp_write_adv_proto(buf); |
961 | 0 | dpp_write_gas_query(buf, conf_req); |
962 | 0 | wpabuf_free(conf_req); |
963 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); |
964 | |
|
965 | 0 | return buf; |
966 | 0 | } |
967 | | |
968 | | |
969 | | struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth, |
970 | | const char *name, |
971 | | enum dpp_netrole netrole, |
972 | | const char *mud_url, int *opclasses, |
973 | | const char *extra_name, |
974 | | const char *extra_value) |
975 | 0 | { |
976 | 0 | size_t len, name_len; |
977 | 0 | const char *tech = "infra"; |
978 | 0 | const char *dpp_name; |
979 | 0 | struct wpabuf *buf = NULL, *json = NULL; |
980 | 0 | char *csr = NULL; |
981 | |
|
982 | | #ifdef CONFIG_TESTING_OPTIONS |
983 | | if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) { |
984 | | static const char *bogus_tech = "knfra"; |
985 | | |
986 | | wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr"); |
987 | | tech = bogus_tech; |
988 | | } |
989 | | #endif /* CONFIG_TESTING_OPTIONS */ |
990 | |
|
991 | 0 | dpp_name = name ? name : "Test"; |
992 | 0 | name_len = os_strlen(dpp_name); |
993 | |
|
994 | 0 | len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4; |
995 | 0 | if (mud_url && mud_url[0]) |
996 | 0 | len += 10 + os_strlen(mud_url); |
997 | 0 | if (extra_name && extra_value && extra_name[0] && extra_value[0]) |
998 | 0 | len += 10 + os_strlen(extra_name) + os_strlen(extra_value); |
999 | 0 | #ifdef CONFIG_DPP2 |
1000 | 0 | if (auth->csr) { |
1001 | 0 | size_t csr_len; |
1002 | |
|
1003 | 0 | csr = base64_encode_no_lf(wpabuf_head(auth->csr), |
1004 | 0 | wpabuf_len(auth->csr), &csr_len); |
1005 | 0 | if (!csr) |
1006 | 0 | goto fail; |
1007 | 0 | len += 30 + csr_len; |
1008 | 0 | } |
1009 | 0 | #endif /* CONFIG_DPP2 */ |
1010 | 0 | json = wpabuf_alloc(len); |
1011 | 0 | if (!json) |
1012 | 0 | goto fail; |
1013 | | |
1014 | 0 | json_start_object(json, NULL); |
1015 | 0 | if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) |
1016 | 0 | goto fail; |
1017 | 0 | json_value_sep(json); |
1018 | 0 | json_add_string(json, "wi-fi_tech", tech); |
1019 | 0 | json_value_sep(json); |
1020 | 0 | json_add_string(json, "netRole", dpp_netrole_str(netrole)); |
1021 | 0 | if (mud_url && mud_url[0]) { |
1022 | 0 | json_value_sep(json); |
1023 | 0 | json_add_string(json, "mudurl", mud_url); |
1024 | 0 | } |
1025 | 0 | if (opclasses) { |
1026 | 0 | int i; |
1027 | |
|
1028 | 0 | json_value_sep(json); |
1029 | 0 | json_start_array(json, "bandSupport"); |
1030 | 0 | for (i = 0; opclasses[i]; i++) |
1031 | 0 | wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]); |
1032 | 0 | json_end_array(json); |
1033 | 0 | } |
1034 | 0 | if (csr) { |
1035 | 0 | json_value_sep(json); |
1036 | 0 | json_add_string(json, "pkcs10", csr); |
1037 | 0 | } |
1038 | 0 | if (extra_name && extra_value && extra_name[0] && extra_value[0]) { |
1039 | 0 | json_value_sep(json); |
1040 | 0 | wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value); |
1041 | 0 | } |
1042 | 0 | json_end_object(json); |
1043 | |
|
1044 | 0 | buf = dpp_build_conf_req(auth, wpabuf_head(json)); |
1045 | 0 | fail: |
1046 | 0 | wpabuf_free(json); |
1047 | 0 | os_free(csr); |
1048 | |
|
1049 | 0 | return buf; |
1050 | 0 | } |
1051 | | |
1052 | | |
1053 | | static int bin_str_eq(const char *val, size_t len, const char *cmp) |
1054 | 0 | { |
1055 | 0 | return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; |
1056 | 0 | } |
1057 | | |
1058 | | |
1059 | | struct dpp_configuration * dpp_configuration_alloc(const char *type) |
1060 | 0 | { |
1061 | 0 | struct dpp_configuration *conf; |
1062 | 0 | const char *end; |
1063 | 0 | size_t len; |
1064 | |
|
1065 | 0 | conf = os_zalloc(sizeof(*conf)); |
1066 | 0 | if (!conf) |
1067 | 0 | goto fail; |
1068 | | |
1069 | 0 | end = os_strchr(type, ' '); |
1070 | 0 | if (end) |
1071 | 0 | len = end - type; |
1072 | 0 | else |
1073 | 0 | len = os_strlen(type); |
1074 | |
|
1075 | 0 | if (bin_str_eq(type, len, "psk")) |
1076 | 0 | conf->akm = DPP_AKM_PSK; |
1077 | 0 | else if (bin_str_eq(type, len, "sae")) |
1078 | 0 | conf->akm = DPP_AKM_SAE; |
1079 | 0 | else if (bin_str_eq(type, len, "psk-sae") || |
1080 | 0 | bin_str_eq(type, len, "psk+sae")) |
1081 | 0 | conf->akm = DPP_AKM_PSK_SAE; |
1082 | 0 | else if (bin_str_eq(type, len, "sae-dpp") || |
1083 | 0 | bin_str_eq(type, len, "dpp+sae")) |
1084 | 0 | conf->akm = DPP_AKM_SAE_DPP; |
1085 | 0 | else if (bin_str_eq(type, len, "psk-sae-dpp") || |
1086 | 0 | bin_str_eq(type, len, "dpp+psk+sae")) |
1087 | 0 | conf->akm = DPP_AKM_PSK_SAE_DPP; |
1088 | 0 | else if (bin_str_eq(type, len, "dpp")) |
1089 | 0 | conf->akm = DPP_AKM_DPP; |
1090 | 0 | else if (bin_str_eq(type, len, "dot1x")) |
1091 | 0 | conf->akm = DPP_AKM_DOT1X; |
1092 | 0 | else |
1093 | 0 | goto fail; |
1094 | | |
1095 | 0 | return conf; |
1096 | 0 | fail: |
1097 | 0 | dpp_configuration_free(conf); |
1098 | 0 | return NULL; |
1099 | 0 | } |
1100 | | |
1101 | | |
1102 | | int dpp_akm_psk(enum dpp_akm akm) |
1103 | 0 | { |
1104 | 0 | return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || |
1105 | 0 | akm == DPP_AKM_PSK_SAE_DPP; |
1106 | 0 | } |
1107 | | |
1108 | | |
1109 | | int dpp_akm_sae(enum dpp_akm akm) |
1110 | 0 | { |
1111 | 0 | return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || |
1112 | 0 | akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; |
1113 | 0 | } |
1114 | | |
1115 | | |
1116 | | int dpp_akm_legacy(enum dpp_akm akm) |
1117 | 0 | { |
1118 | 0 | return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || |
1119 | 0 | akm == DPP_AKM_SAE; |
1120 | 0 | } |
1121 | | |
1122 | | |
1123 | | int dpp_akm_dpp(enum dpp_akm akm) |
1124 | 0 | { |
1125 | 0 | return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || |
1126 | 0 | akm == DPP_AKM_PSK_SAE_DPP; |
1127 | 0 | } |
1128 | | |
1129 | | |
1130 | | int dpp_akm_ver2(enum dpp_akm akm) |
1131 | 0 | { |
1132 | 0 | return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; |
1133 | 0 | } |
1134 | | |
1135 | | |
1136 | | int dpp_configuration_valid(const struct dpp_configuration *conf) |
1137 | 0 | { |
1138 | 0 | if (conf->ssid_len == 0) |
1139 | 0 | return 0; |
1140 | 0 | if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) |
1141 | 0 | return 0; |
1142 | 0 | if (dpp_akm_sae(conf->akm) && !conf->passphrase) |
1143 | 0 | return 0; |
1144 | 0 | return 1; |
1145 | 0 | } |
1146 | | |
1147 | | |
1148 | | void dpp_configuration_free(struct dpp_configuration *conf) |
1149 | 0 | { |
1150 | 0 | if (!conf) |
1151 | 0 | return; |
1152 | 0 | str_clear_free(conf->passphrase); |
1153 | 0 | os_free(conf->group_id); |
1154 | 0 | os_free(conf->csrattrs); |
1155 | 0 | os_free(conf->extra_name); |
1156 | 0 | os_free(conf->extra_value); |
1157 | 0 | bin_clear_free(conf, sizeof(*conf)); |
1158 | 0 | } |
1159 | | |
1160 | | |
1161 | | static int dpp_configuration_parse_helper(struct dpp_authentication *auth, |
1162 | | const char *cmd, int idx) |
1163 | 0 | { |
1164 | 0 | const char *pos, *end; |
1165 | 0 | struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; |
1166 | 0 | struct dpp_configuration *conf = NULL; |
1167 | 0 | size_t len; |
1168 | |
|
1169 | 0 | pos = os_strstr(cmd, " conf=sta-"); |
1170 | 0 | if (pos) { |
1171 | 0 | conf_sta = dpp_configuration_alloc(pos + 10); |
1172 | 0 | if (!conf_sta) |
1173 | 0 | goto fail; |
1174 | 0 | conf_sta->netrole = DPP_NETROLE_STA; |
1175 | 0 | conf = conf_sta; |
1176 | 0 | } |
1177 | | |
1178 | 0 | pos = os_strstr(cmd, " conf=ap-"); |
1179 | 0 | if (pos) { |
1180 | 0 | conf_ap = dpp_configuration_alloc(pos + 9); |
1181 | 0 | if (!conf_ap) |
1182 | 0 | goto fail; |
1183 | 0 | conf_ap->netrole = DPP_NETROLE_AP; |
1184 | 0 | conf = conf_ap; |
1185 | 0 | } |
1186 | | |
1187 | 0 | pos = os_strstr(cmd, " conf=configurator"); |
1188 | 0 | if (pos) |
1189 | 0 | auth->provision_configurator = 1; |
1190 | |
|
1191 | 0 | if (!conf) |
1192 | 0 | return 0; |
1193 | | |
1194 | 0 | pos = os_strstr(cmd, " ssid="); |
1195 | 0 | if (pos) { |
1196 | 0 | pos += 6; |
1197 | 0 | end = os_strchr(pos, ' '); |
1198 | 0 | conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); |
1199 | 0 | conf->ssid_len /= 2; |
1200 | 0 | if (conf->ssid_len > sizeof(conf->ssid) || |
1201 | 0 | hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) |
1202 | 0 | goto fail; |
1203 | 0 | } else { |
1204 | | #ifdef CONFIG_TESTING_OPTIONS |
1205 | | /* use a default SSID for legacy testing reasons */ |
1206 | | os_memcpy(conf->ssid, "test", 4); |
1207 | | conf->ssid_len = 4; |
1208 | | #else /* CONFIG_TESTING_OPTIONS */ |
1209 | 0 | goto fail; |
1210 | 0 | #endif /* CONFIG_TESTING_OPTIONS */ |
1211 | 0 | } |
1212 | | |
1213 | 0 | pos = os_strstr(cmd, " ssid_charset="); |
1214 | 0 | if (pos) { |
1215 | 0 | if (conf_ap) { |
1216 | 0 | wpa_printf(MSG_INFO, |
1217 | 0 | "DPP: ssid64 option (ssid_charset param) not allowed for AP enrollee"); |
1218 | 0 | goto fail; |
1219 | 0 | } |
1220 | 0 | conf->ssid_charset = atoi(pos + 14); |
1221 | 0 | } |
1222 | | |
1223 | 0 | pos = os_strstr(cmd, " pass="); |
1224 | 0 | if (pos) { |
1225 | 0 | size_t pass_len; |
1226 | |
|
1227 | 0 | pos += 6; |
1228 | 0 | end = os_strchr(pos, ' '); |
1229 | 0 | pass_len = end ? (size_t) (end - pos) : os_strlen(pos); |
1230 | 0 | pass_len /= 2; |
1231 | 0 | if (pass_len > 63 || pass_len < 8) |
1232 | 0 | goto fail; |
1233 | 0 | conf->passphrase = os_zalloc(pass_len + 1); |
1234 | 0 | if (!conf->passphrase || |
1235 | 0 | hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) |
1236 | 0 | goto fail; |
1237 | 0 | } |
1238 | | |
1239 | 0 | pos = os_strstr(cmd, " psk="); |
1240 | 0 | if (pos) { |
1241 | 0 | pos += 5; |
1242 | 0 | if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) |
1243 | 0 | goto fail; |
1244 | 0 | conf->psk_set = 1; |
1245 | 0 | } |
1246 | | |
1247 | 0 | pos = os_strstr(cmd, " group_id="); |
1248 | 0 | if (pos) { |
1249 | 0 | size_t group_id_len; |
1250 | |
|
1251 | 0 | pos += 10; |
1252 | 0 | end = os_strchr(pos, ' '); |
1253 | 0 | group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); |
1254 | 0 | conf->group_id = os_malloc(group_id_len + 1); |
1255 | 0 | if (!conf->group_id) |
1256 | 0 | goto fail; |
1257 | 0 | os_memcpy(conf->group_id, pos, group_id_len); |
1258 | 0 | conf->group_id[group_id_len] = '\0'; |
1259 | 0 | } |
1260 | | |
1261 | 0 | pos = os_strstr(cmd, " expiry="); |
1262 | 0 | if (pos) { |
1263 | 0 | long int val; |
1264 | |
|
1265 | 0 | pos += 8; |
1266 | 0 | val = strtol(pos, NULL, 0); |
1267 | 0 | if (val <= 0) |
1268 | 0 | goto fail; |
1269 | 0 | conf->netaccesskey_expiry = val; |
1270 | 0 | } |
1271 | | |
1272 | 0 | pos = os_strstr(cmd, " csrattrs="); |
1273 | 0 | if (pos) { |
1274 | 0 | pos += 10; |
1275 | 0 | end = os_strchr(pos, ' '); |
1276 | 0 | len = end ? (size_t) (end - pos) : os_strlen(pos); |
1277 | 0 | conf->csrattrs = os_zalloc(len + 1); |
1278 | 0 | if (!conf->csrattrs) |
1279 | 0 | goto fail; |
1280 | 0 | os_memcpy(conf->csrattrs, pos, len); |
1281 | 0 | } |
1282 | | |
1283 | 0 | pos = os_strstr(cmd, " conf_extra_name="); |
1284 | 0 | if (pos) { |
1285 | 0 | pos += 17; |
1286 | 0 | end = os_strchr(pos, ' '); |
1287 | 0 | len = end ? (size_t) (end - pos) : os_strlen(pos); |
1288 | 0 | conf->extra_name = os_zalloc(len + 1); |
1289 | 0 | if (!conf->extra_name) |
1290 | 0 | goto fail; |
1291 | 0 | os_memcpy(conf->extra_name, pos, len); |
1292 | 0 | } |
1293 | | |
1294 | 0 | pos = os_strstr(cmd, " conf_extra_value="); |
1295 | 0 | if (pos) { |
1296 | 0 | pos += 18; |
1297 | 0 | end = os_strchr(pos, ' '); |
1298 | 0 | len = end ? (size_t) (end - pos) : os_strlen(pos); |
1299 | 0 | len /= 2; |
1300 | 0 | conf->extra_value = os_zalloc(len + 1); |
1301 | 0 | if (!conf->extra_value || |
1302 | 0 | hexstr2bin(pos, (u8 *) conf->extra_value, len) < 0) |
1303 | 0 | goto fail; |
1304 | 0 | } |
1305 | | |
1306 | 0 | if (!dpp_configuration_valid(conf)) |
1307 | 0 | goto fail; |
1308 | | |
1309 | 0 | if (idx == 0) { |
1310 | 0 | auth->conf_sta = conf_sta; |
1311 | 0 | auth->conf_ap = conf_ap; |
1312 | 0 | } else if (idx == 1) { |
1313 | 0 | if (!auth->conf_sta) |
1314 | 0 | auth->conf_sta = conf_sta; |
1315 | 0 | else |
1316 | 0 | auth->conf2_sta = conf_sta; |
1317 | 0 | if (!auth->conf_ap) |
1318 | 0 | auth->conf_ap = conf_ap; |
1319 | 0 | else |
1320 | 0 | auth->conf2_ap = conf_ap; |
1321 | 0 | } else { |
1322 | 0 | goto fail; |
1323 | 0 | } |
1324 | 0 | return 0; |
1325 | | |
1326 | 0 | fail: |
1327 | 0 | dpp_configuration_free(conf_sta); |
1328 | 0 | dpp_configuration_free(conf_ap); |
1329 | 0 | return -1; |
1330 | 0 | } |
1331 | | |
1332 | | |
1333 | | static int dpp_configuration_parse(struct dpp_authentication *auth, |
1334 | | const char *cmd) |
1335 | 0 | { |
1336 | 0 | const char *pos; |
1337 | 0 | char *tmp; |
1338 | 0 | size_t len; |
1339 | 0 | int res; |
1340 | |
|
1341 | 0 | pos = os_strstr(cmd, " @CONF-OBJ-SEP@ "); |
1342 | 0 | if (!pos) |
1343 | 0 | return dpp_configuration_parse_helper(auth, cmd, 0); |
1344 | | |
1345 | 0 | len = pos - cmd; |
1346 | 0 | tmp = os_malloc(len + 1); |
1347 | 0 | if (!tmp) |
1348 | 0 | goto fail; |
1349 | 0 | os_memcpy(tmp, cmd, len); |
1350 | 0 | tmp[len] = '\0'; |
1351 | 0 | res = dpp_configuration_parse_helper(auth, tmp, 0); |
1352 | 0 | str_clear_free(tmp); |
1353 | 0 | if (res) |
1354 | 0 | goto fail; |
1355 | 0 | res = dpp_configuration_parse_helper(auth, cmd + len, 1); |
1356 | 0 | if (res) |
1357 | 0 | goto fail; |
1358 | 0 | return 0; |
1359 | 0 | fail: |
1360 | 0 | dpp_configuration_free(auth->conf_sta); |
1361 | 0 | dpp_configuration_free(auth->conf2_sta); |
1362 | 0 | dpp_configuration_free(auth->conf_ap); |
1363 | 0 | dpp_configuration_free(auth->conf2_ap); |
1364 | 0 | return -1; |
1365 | 0 | } |
1366 | | |
1367 | | |
1368 | | static struct dpp_configurator * |
1369 | | dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) |
1370 | 0 | { |
1371 | 0 | struct dpp_configurator *conf; |
1372 | |
|
1373 | 0 | if (!dpp) |
1374 | 0 | return NULL; |
1375 | | |
1376 | 0 | dl_list_for_each(conf, &dpp->configurator, |
1377 | 0 | struct dpp_configurator, list) { |
1378 | 0 | if (conf->id == id) |
1379 | 0 | return conf; |
1380 | 0 | } |
1381 | 0 | return NULL; |
1382 | 0 | } |
1383 | | |
1384 | | |
1385 | | int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd) |
1386 | 0 | { |
1387 | 0 | const char *pos; |
1388 | 0 | char *tmp = NULL; |
1389 | 0 | int ret = -1; |
1390 | |
|
1391 | 0 | if (!cmd || auth->configurator_set) |
1392 | 0 | return 0; |
1393 | 0 | auth->configurator_set = 1; |
1394 | |
|
1395 | 0 | if (cmd[0] != ' ') { |
1396 | 0 | size_t len; |
1397 | |
|
1398 | 0 | len = os_strlen(cmd); |
1399 | 0 | tmp = os_malloc(len + 2); |
1400 | 0 | if (!tmp) |
1401 | 0 | goto fail; |
1402 | 0 | tmp[0] = ' '; |
1403 | 0 | os_memcpy(tmp + 1, cmd, len + 1); |
1404 | 0 | cmd = tmp; |
1405 | 0 | } |
1406 | | |
1407 | 0 | wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); |
1408 | |
|
1409 | 0 | if (os_strstr(cmd, " conf=query")) { |
1410 | 0 | auth->configurator_set = 0; |
1411 | 0 | auth->use_config_query = true; |
1412 | 0 | ret = 0; |
1413 | 0 | goto fail; |
1414 | 0 | } |
1415 | | |
1416 | 0 | pos = os_strstr(cmd, " configurator="); |
1417 | 0 | if (!auth->conf && pos) { |
1418 | 0 | pos += 14; |
1419 | 0 | auth->conf = dpp_configurator_get_id(auth->global, atoi(pos)); |
1420 | 0 | if (!auth->conf) { |
1421 | 0 | wpa_printf(MSG_INFO, |
1422 | 0 | "DPP: Could not find the specified configurator"); |
1423 | 0 | goto fail; |
1424 | 0 | } |
1425 | 0 | } |
1426 | | |
1427 | 0 | pos = os_strstr(cmd, " conn_status="); |
1428 | 0 | if (pos) { |
1429 | 0 | pos += 13; |
1430 | 0 | auth->send_conn_status = atoi(pos); |
1431 | 0 | } |
1432 | |
|
1433 | 0 | pos = os_strstr(cmd, " akm_use_selector="); |
1434 | 0 | if (pos) { |
1435 | 0 | pos += 18; |
1436 | 0 | auth->akm_use_selector = atoi(pos); |
1437 | 0 | } |
1438 | |
|
1439 | 0 | if (dpp_configuration_parse(auth, cmd) < 0) { |
1440 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, |
1441 | 0 | "DPP: Failed to set configurator parameters"); |
1442 | 0 | goto fail; |
1443 | 0 | } |
1444 | 0 | ret = 0; |
1445 | 0 | fail: |
1446 | 0 | os_free(tmp); |
1447 | 0 | return ret; |
1448 | 0 | } |
1449 | | |
1450 | | |
1451 | | void dpp_auth_deinit(struct dpp_authentication *auth) |
1452 | 0 | { |
1453 | 0 | unsigned int i; |
1454 | |
|
1455 | 0 | if (!auth) |
1456 | 0 | return; |
1457 | 0 | dpp_configuration_free(auth->conf_ap); |
1458 | 0 | dpp_configuration_free(auth->conf2_ap); |
1459 | 0 | dpp_configuration_free(auth->conf_sta); |
1460 | 0 | dpp_configuration_free(auth->conf2_sta); |
1461 | 0 | crypto_ec_key_deinit(auth->own_protocol_key); |
1462 | 0 | crypto_ec_key_deinit(auth->peer_protocol_key); |
1463 | 0 | crypto_ec_key_deinit(auth->reconfig_old_protocol_key); |
1464 | 0 | wpabuf_free(auth->req_msg); |
1465 | 0 | wpabuf_free(auth->resp_msg); |
1466 | 0 | wpabuf_free(auth->conf_req); |
1467 | 0 | wpabuf_free(auth->reconfig_req_msg); |
1468 | 0 | wpabuf_free(auth->reconfig_resp_msg); |
1469 | 0 | for (i = 0; i < auth->num_conf_obj; i++) { |
1470 | 0 | struct dpp_config_obj *conf = &auth->conf_obj[i]; |
1471 | |
|
1472 | 0 | os_free(conf->connector); |
1473 | 0 | wpabuf_free(conf->c_sign_key); |
1474 | 0 | wpabuf_free(conf->certbag); |
1475 | 0 | wpabuf_free(conf->certs); |
1476 | 0 | wpabuf_free(conf->cacert); |
1477 | 0 | os_free(conf->server_name); |
1478 | 0 | wpabuf_free(conf->pp_key); |
1479 | 0 | } |
1480 | 0 | #ifdef CONFIG_DPP2 |
1481 | 0 | dpp_free_asymmetric_key(auth->conf_key_pkg); |
1482 | 0 | os_free(auth->csrattrs); |
1483 | 0 | wpabuf_free(auth->csr); |
1484 | 0 | wpabuf_free(auth->priv_key); |
1485 | 0 | wpabuf_free(auth->cacert); |
1486 | 0 | wpabuf_free(auth->certbag); |
1487 | 0 | os_free(auth->trusted_eap_server_name); |
1488 | 0 | wpabuf_free(auth->conf_resp_tcp); |
1489 | 0 | #endif /* CONFIG_DPP2 */ |
1490 | 0 | wpabuf_free(auth->net_access_key); |
1491 | 0 | dpp_bootstrap_info_free(auth->tmp_own_bi); |
1492 | 0 | if (auth->tmp_peer_bi) { |
1493 | 0 | dl_list_del(&auth->tmp_peer_bi->list); |
1494 | 0 | dpp_bootstrap_info_free(auth->tmp_peer_bi); |
1495 | 0 | } |
1496 | 0 | os_free(auth->e_name); |
1497 | 0 | os_free(auth->e_mud_url); |
1498 | 0 | os_free(auth->e_band_support); |
1499 | | #ifdef CONFIG_TESTING_OPTIONS |
1500 | | os_free(auth->config_obj_override); |
1501 | | os_free(auth->discovery_override); |
1502 | | os_free(auth->groups_override); |
1503 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1504 | 0 | bin_clear_free(auth, sizeof(*auth)); |
1505 | 0 | } |
1506 | | |
1507 | | |
1508 | | static struct wpabuf * |
1509 | | dpp_build_conf_start(struct dpp_authentication *auth, |
1510 | | struct dpp_configuration *conf, size_t tailroom) |
1511 | 0 | { |
1512 | 0 | struct wpabuf *buf; |
1513 | |
|
1514 | | #ifdef CONFIG_TESTING_OPTIONS |
1515 | | if (auth->discovery_override) |
1516 | | tailroom += os_strlen(auth->discovery_override); |
1517 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1518 | |
|
1519 | 0 | buf = wpabuf_alloc(200 + tailroom); |
1520 | 0 | if (!buf) |
1521 | 0 | return NULL; |
1522 | 0 | json_start_object(buf, NULL); |
1523 | 0 | json_add_string(buf, "wi-fi_tech", "infra"); |
1524 | 0 | json_value_sep(buf); |
1525 | | #ifdef CONFIG_TESTING_OPTIONS |
1526 | | if (auth->discovery_override) { |
1527 | | wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", |
1528 | | auth->discovery_override); |
1529 | | wpabuf_put_str(buf, "\"discovery\":"); |
1530 | | wpabuf_put_str(buf, auth->discovery_override); |
1531 | | json_value_sep(buf); |
1532 | | return buf; |
1533 | | } |
1534 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1535 | 0 | json_start_object(buf, "discovery"); |
1536 | 0 | if (((!conf->ssid_charset || auth->peer_version < 2) && |
1537 | 0 | json_add_string_escape(buf, "ssid", conf->ssid, |
1538 | 0 | conf->ssid_len) < 0) || |
1539 | 0 | ((conf->ssid_charset && auth->peer_version >= 2) && |
1540 | 0 | json_add_base64url(buf, "ssid64", conf->ssid, |
1541 | 0 | conf->ssid_len) < 0)) { |
1542 | 0 | wpabuf_free(buf); |
1543 | 0 | return NULL; |
1544 | 0 | } |
1545 | 0 | if (conf->ssid_charset > 0) { |
1546 | 0 | json_value_sep(buf); |
1547 | 0 | json_add_int(buf, "ssid_charset", conf->ssid_charset); |
1548 | 0 | } |
1549 | 0 | json_end_object(buf); |
1550 | 0 | json_value_sep(buf); |
1551 | |
|
1552 | 0 | return buf; |
1553 | 0 | } |
1554 | | |
1555 | | |
1556 | | int dpp_build_jwk(struct wpabuf *buf, const char *name, |
1557 | | struct crypto_ec_key *key, const char *kid, |
1558 | | const struct dpp_curve_params *curve) |
1559 | 0 | { |
1560 | 0 | struct wpabuf *pub; |
1561 | 0 | const u8 *pos; |
1562 | 0 | int ret = -1; |
1563 | |
|
1564 | 0 | pub = crypto_ec_key_get_pubkey_point(key, 0); |
1565 | 0 | if (!pub) |
1566 | 0 | goto fail; |
1567 | | |
1568 | 0 | json_start_object(buf, name); |
1569 | 0 | json_add_string(buf, "kty", "EC"); |
1570 | 0 | json_value_sep(buf); |
1571 | 0 | json_add_string(buf, "crv", curve->jwk_crv); |
1572 | 0 | json_value_sep(buf); |
1573 | 0 | pos = wpabuf_head(pub); |
1574 | 0 | if (json_add_base64url(buf, "x", pos, curve->prime_len) < 0) |
1575 | 0 | goto fail; |
1576 | 0 | json_value_sep(buf); |
1577 | 0 | pos += curve->prime_len; |
1578 | 0 | if (json_add_base64url(buf, "y", pos, curve->prime_len) < 0) |
1579 | 0 | goto fail; |
1580 | 0 | if (kid) { |
1581 | 0 | json_value_sep(buf); |
1582 | 0 | json_add_string(buf, "kid", kid); |
1583 | 0 | } |
1584 | 0 | json_end_object(buf); |
1585 | 0 | ret = 0; |
1586 | 0 | fail: |
1587 | 0 | wpabuf_free(pub); |
1588 | 0 | return ret; |
1589 | 0 | } |
1590 | | |
1591 | | |
1592 | | static void dpp_build_legacy_cred_params(struct wpabuf *buf, |
1593 | | struct dpp_configuration *conf) |
1594 | 0 | { |
1595 | 0 | if (conf->passphrase && os_strlen(conf->passphrase) < 64) { |
1596 | 0 | json_add_string_escape(buf, "pass", conf->passphrase, |
1597 | 0 | os_strlen(conf->passphrase)); |
1598 | 0 | } else if (conf->psk_set) { |
1599 | 0 | char psk[2 * sizeof(conf->psk) + 1]; |
1600 | |
|
1601 | 0 | wpa_snprintf_hex(psk, sizeof(psk), |
1602 | 0 | conf->psk, sizeof(conf->psk)); |
1603 | 0 | json_add_string(buf, "psk_hex", psk); |
1604 | 0 | forced_memzero(psk, sizeof(psk)); |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | | |
1609 | | const char * dpp_netrole_str(enum dpp_netrole netrole) |
1610 | 0 | { |
1611 | 0 | switch (netrole) { |
1612 | 0 | case DPP_NETROLE_STA: |
1613 | 0 | return "sta"; |
1614 | 0 | case DPP_NETROLE_AP: |
1615 | 0 | return "ap"; |
1616 | 0 | case DPP_NETROLE_CONFIGURATOR: |
1617 | 0 | return "configurator"; |
1618 | 0 | default: |
1619 | 0 | return "??"; |
1620 | 0 | } |
1621 | 0 | } |
1622 | | |
1623 | | |
1624 | | static bool dpp_supports_curve(const char *curve, struct dpp_bootstrap_info *bi) |
1625 | 0 | { |
1626 | 0 | enum dpp_bootstrap_supported_curves idx; |
1627 | |
|
1628 | 0 | if (!bi || !bi->supported_curves) |
1629 | 0 | return true; /* no support indication available */ |
1630 | | |
1631 | 0 | if (os_strcmp(curve, "prime256v1") == 0) |
1632 | 0 | idx = DPP_BOOTSTRAP_CURVE_P_256; |
1633 | 0 | else if (os_strcmp(curve, "secp384r1") == 0) |
1634 | 0 | idx = DPP_BOOTSTRAP_CURVE_P_384; |
1635 | 0 | else if (os_strcmp(curve, "secp521r1") == 0) |
1636 | 0 | idx = DPP_BOOTSTRAP_CURVE_P_521; |
1637 | 0 | else if (os_strcmp(curve, "brainpoolP256r1") == 0) |
1638 | 0 | idx = DPP_BOOTSTRAP_CURVE_BP_256; |
1639 | 0 | else if (os_strcmp(curve, "brainpoolP384r1") == 0) |
1640 | 0 | idx = DPP_BOOTSTRAP_CURVE_BP_384; |
1641 | 0 | else if (os_strcmp(curve, "brainpoolP512r1") == 0) |
1642 | 0 | idx = DPP_BOOTSTRAP_CURVE_BP_512; |
1643 | 0 | else |
1644 | 0 | return true; |
1645 | | |
1646 | 0 | return bi->supported_curves & BIT(idx); |
1647 | 0 | } |
1648 | | |
1649 | | |
1650 | | static struct wpabuf * |
1651 | | dpp_build_conf_obj_dpp(struct dpp_authentication *auth, |
1652 | | struct dpp_configuration *conf) |
1653 | 0 | { |
1654 | 0 | struct wpabuf *buf = NULL; |
1655 | 0 | char *signed_conn = NULL; |
1656 | 0 | size_t tailroom; |
1657 | 0 | const struct dpp_curve_params *curve; /* C-sign-key curve */ |
1658 | 0 | const struct dpp_curve_params *nak_curve; /* netAccessKey curve */ |
1659 | 0 | struct wpabuf *dppcon = NULL; |
1660 | 0 | size_t extra_len = 1000; |
1661 | 0 | int incl_legacy; |
1662 | 0 | enum dpp_akm akm; |
1663 | 0 | const char *akm_str; |
1664 | |
|
1665 | 0 | if (!auth->conf) { |
1666 | 0 | wpa_printf(MSG_INFO, |
1667 | 0 | "DPP: No configurator specified - cannot generate DPP config object"); |
1668 | 0 | goto fail; |
1669 | 0 | } |
1670 | 0 | curve = auth->conf->curve; |
1671 | 0 | if (dpp_akm_dpp(conf->akm) && |
1672 | 0 | !dpp_supports_curve(curve->name, auth->peer_bi)) { |
1673 | 0 | wpa_printf(MSG_DEBUG, |
1674 | 0 | "DPP: Enrollee does not support C-sign-key curve (%s) - cannot generate config object", |
1675 | 0 | curve->name); |
1676 | 0 | goto fail; |
1677 | 0 | } |
1678 | 0 | if (auth->new_curve && auth->new_key_received) |
1679 | 0 | nak_curve = auth->new_curve; |
1680 | 0 | else |
1681 | 0 | nak_curve = auth->curve; |
1682 | 0 | if (!dpp_supports_curve(nak_curve->name, auth->peer_bi)) { |
1683 | 0 | wpa_printf(MSG_DEBUG, |
1684 | 0 | "DPP: Enrollee does not support netAccessKey curve (%s) - cannot generate config object", |
1685 | 0 | nak_curve->name); |
1686 | 0 | goto fail; |
1687 | 0 | } |
1688 | | |
1689 | 0 | akm = conf->akm; |
1690 | 0 | if (dpp_akm_ver2(akm) && auth->peer_version < 2) { |
1691 | 0 | wpa_printf(MSG_DEBUG, |
1692 | 0 | "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); |
1693 | 0 | akm = DPP_AKM_DPP; |
1694 | 0 | } |
1695 | |
|
1696 | | #ifdef CONFIG_TESTING_OPTIONS |
1697 | | if (auth->groups_override) |
1698 | | extra_len += os_strlen(auth->groups_override); |
1699 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1700 | |
|
1701 | 0 | if (conf->group_id) |
1702 | 0 | extra_len += os_strlen(conf->group_id); |
1703 | | |
1704 | | /* Connector (JSON dppCon object) */ |
1705 | 0 | dppcon = wpabuf_alloc(extra_len + 2 * nak_curve->prime_len * 4 / 3); |
1706 | 0 | if (!dppcon) |
1707 | 0 | goto fail; |
1708 | | #ifdef CONFIG_TESTING_OPTIONS |
1709 | | if (auth->groups_override) { |
1710 | | wpabuf_put_u8(dppcon, '{'); |
1711 | | if (auth->groups_override) { |
1712 | | wpa_printf(MSG_DEBUG, |
1713 | | "DPP: TESTING - groups override: '%s'", |
1714 | | auth->groups_override); |
1715 | | wpabuf_put_str(dppcon, "\"groups\":"); |
1716 | | wpabuf_put_str(dppcon, auth->groups_override); |
1717 | | json_value_sep(dppcon); |
1718 | | } |
1719 | | goto skip_groups; |
1720 | | } |
1721 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1722 | 0 | json_start_object(dppcon, NULL); |
1723 | 0 | json_start_array(dppcon, "groups"); |
1724 | 0 | json_start_object(dppcon, NULL); |
1725 | 0 | json_add_string(dppcon, "groupId", |
1726 | 0 | conf->group_id ? conf->group_id : "*"); |
1727 | 0 | json_value_sep(dppcon); |
1728 | 0 | json_add_string(dppcon, "netRole", dpp_netrole_str(conf->netrole)); |
1729 | 0 | json_end_object(dppcon); |
1730 | 0 | json_end_array(dppcon); |
1731 | 0 | json_value_sep(dppcon); |
1732 | | #ifdef CONFIG_TESTING_OPTIONS |
1733 | | skip_groups: |
1734 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1735 | 0 | if (!auth->peer_protocol_key) { |
1736 | 0 | wpa_printf(MSG_DEBUG, |
1737 | 0 | "DPP: No peer protocol key available to build netAccessKey JWK"); |
1738 | 0 | goto fail; |
1739 | 0 | } |
1740 | | #ifdef CONFIG_DPP3 |
1741 | | if (auth->conf->net_access_key_curve && |
1742 | | auth->curve != auth->conf->net_access_key_curve && |
1743 | | !auth->new_key_received) { |
1744 | | if (!dpp_supports_curve(auth->conf->net_access_key_curve->name, |
1745 | | auth->peer_bi)) { |
1746 | | wpa_printf(MSG_DEBUG, |
1747 | | "DPP: Enrollee does not support the required netAccessKey curve (%s) - cannot generate config object", |
1748 | | auth->conf->net_access_key_curve->name); |
1749 | | goto fail; |
1750 | | } |
1751 | | wpa_printf(MSG_DEBUG, |
1752 | | "DPP: Peer protocol key curve (%s) does not match the required netAccessKey curve (%s) - %s", |
1753 | | auth->curve->name, |
1754 | | auth->conf->net_access_key_curve->name, |
1755 | | auth->waiting_new_key ? |
1756 | | "the required key not received" : |
1757 | | "request a new key"); |
1758 | | if (auth->waiting_new_key) |
1759 | | auth->waiting_new_key = false; /* failed */ |
1760 | | else |
1761 | | auth->waiting_new_key = true; |
1762 | | goto fail; |
1763 | | } |
1764 | | #endif /* CONFIG_DPP3 */ |
1765 | 0 | if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, |
1766 | 0 | nak_curve) < 0) { |
1767 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); |
1768 | 0 | goto fail; |
1769 | 0 | } |
1770 | 0 | if (conf->netaccesskey_expiry) { |
1771 | 0 | struct os_tm tm; |
1772 | 0 | char expiry[30]; |
1773 | |
|
1774 | 0 | if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { |
1775 | 0 | wpa_printf(MSG_DEBUG, |
1776 | 0 | "DPP: Failed to generate expiry string"); |
1777 | 0 | goto fail; |
1778 | 0 | } |
1779 | 0 | os_snprintf(expiry, sizeof(expiry), |
1780 | 0 | "%04u-%02u-%02uT%02u:%02u:%02uZ", |
1781 | 0 | tm.year, tm.month, tm.day, |
1782 | 0 | tm.hour, tm.min, tm.sec); |
1783 | 0 | json_value_sep(dppcon); |
1784 | 0 | json_add_string(dppcon, "expiry", expiry); |
1785 | 0 | } |
1786 | | #ifdef CONFIG_DPP3 |
1787 | | json_value_sep(dppcon); |
1788 | | json_add_int(dppcon, "version", auth->peer_version); |
1789 | | #endif /* CONFIG_DPP3 */ |
1790 | 0 | json_end_object(dppcon); |
1791 | 0 | wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", |
1792 | 0 | (const char *) wpabuf_head(dppcon)); |
1793 | |
|
1794 | 0 | signed_conn = dpp_sign_connector(auth->conf, dppcon); |
1795 | 0 | if (!signed_conn) |
1796 | 0 | goto fail; |
1797 | | |
1798 | 0 | incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); |
1799 | 0 | tailroom = 1000; |
1800 | 0 | tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); |
1801 | 0 | tailroom += os_strlen(signed_conn); |
1802 | 0 | if (incl_legacy) |
1803 | 0 | tailroom += 1000; |
1804 | 0 | if (akm == DPP_AKM_DOT1X) { |
1805 | 0 | if (auth->certbag) |
1806 | 0 | tailroom += 2 * wpabuf_len(auth->certbag); |
1807 | 0 | if (auth->cacert) |
1808 | 0 | tailroom += 2 * wpabuf_len(auth->cacert); |
1809 | 0 | if (auth->trusted_eap_server_name) |
1810 | 0 | tailroom += os_strlen(auth->trusted_eap_server_name); |
1811 | 0 | tailroom += 1000; |
1812 | 0 | } |
1813 | 0 | if (conf->extra_name && conf->extra_value) |
1814 | 0 | tailroom += 10 + os_strlen(conf->extra_name) + |
1815 | 0 | os_strlen(conf->extra_value); |
1816 | 0 | buf = dpp_build_conf_start(auth, conf, tailroom); |
1817 | 0 | if (!buf) |
1818 | 0 | goto fail; |
1819 | | |
1820 | 0 | if (auth->akm_use_selector && dpp_akm_ver2(akm)) |
1821 | 0 | akm_str = dpp_akm_selector_str(akm); |
1822 | 0 | else |
1823 | 0 | akm_str = dpp_akm_str(akm); |
1824 | 0 | json_start_object(buf, "cred"); |
1825 | 0 | json_add_string(buf, "akm", akm_str); |
1826 | 0 | json_value_sep(buf); |
1827 | 0 | if (incl_legacy) { |
1828 | 0 | dpp_build_legacy_cred_params(buf, conf); |
1829 | 0 | json_value_sep(buf); |
1830 | 0 | } |
1831 | 0 | if (akm == DPP_AKM_DOT1X) { |
1832 | 0 | json_start_object(buf, "entCreds"); |
1833 | 0 | if (!auth->certbag) |
1834 | 0 | goto fail; |
1835 | 0 | json_add_base64(buf, "certBag", wpabuf_head(auth->certbag), |
1836 | 0 | wpabuf_len(auth->certbag)); |
1837 | 0 | if (auth->cacert) { |
1838 | 0 | json_value_sep(buf); |
1839 | 0 | json_add_base64(buf, "caCert", |
1840 | 0 | wpabuf_head(auth->cacert), |
1841 | 0 | wpabuf_len(auth->cacert)); |
1842 | 0 | } |
1843 | 0 | if (auth->trusted_eap_server_name) { |
1844 | 0 | json_value_sep(buf); |
1845 | 0 | json_add_string(buf, "trustedEapServerName", |
1846 | 0 | auth->trusted_eap_server_name); |
1847 | 0 | } |
1848 | 0 | json_value_sep(buf); |
1849 | 0 | json_start_array(buf, "eapMethods"); |
1850 | 0 | wpabuf_printf(buf, "%d", EAP_TYPE_TLS); |
1851 | 0 | json_end_array(buf); |
1852 | 0 | json_end_object(buf); |
1853 | 0 | json_value_sep(buf); |
1854 | 0 | } |
1855 | 0 | wpabuf_put_str(buf, "\"signedConnector\":\""); |
1856 | 0 | wpabuf_put_str(buf, signed_conn); |
1857 | 0 | wpabuf_put_str(buf, "\""); |
1858 | 0 | json_value_sep(buf); |
1859 | 0 | if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, |
1860 | 0 | curve) < 0) { |
1861 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); |
1862 | 0 | goto fail; |
1863 | 0 | } |
1864 | 0 | #ifdef CONFIG_DPP2 |
1865 | 0 | if (auth->peer_version >= 2 && auth->conf->pp_key) { |
1866 | 0 | json_value_sep(buf); |
1867 | 0 | if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL, |
1868 | 0 | curve) < 0) { |
1869 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK"); |
1870 | 0 | goto fail; |
1871 | 0 | } |
1872 | 0 | } |
1873 | 0 | #endif /* CONFIG_DPP2 */ |
1874 | | |
1875 | 0 | json_end_object(buf); |
1876 | 0 | if (conf->extra_name && conf->extra_value) { |
1877 | 0 | json_value_sep(buf); |
1878 | 0 | wpabuf_printf(buf, "\"%s\":%s", conf->extra_name, |
1879 | 0 | conf->extra_value); |
1880 | 0 | } |
1881 | 0 | json_end_object(buf); |
1882 | |
|
1883 | 0 | wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", |
1884 | 0 | wpabuf_head(buf), wpabuf_len(buf)); |
1885 | |
|
1886 | | #ifdef CONFIG_DPP3 |
1887 | | if (!auth->conf->net_access_key_curve) { |
1888 | | /* All netAccessKey values used in the network will have to be |
1889 | | * from the same curve for network introduction to work, so |
1890 | | * hardcode the first used netAccessKey curve for consecutive |
1891 | | * operations if there was no explicit configuration of which |
1892 | | * curve to use. */ |
1893 | | wpa_printf(MSG_DEBUG, |
1894 | | "DPP: Update Configurator to require netAccessKey curve %s based on first provisioning", |
1895 | | nak_curve->name); |
1896 | | auth->conf->net_access_key_curve = nak_curve; |
1897 | | } |
1898 | | #endif /* CONFIG_DPP3 */ |
1899 | |
|
1900 | 0 | out: |
1901 | 0 | os_free(signed_conn); |
1902 | 0 | wpabuf_free(dppcon); |
1903 | 0 | return buf; |
1904 | 0 | fail: |
1905 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); |
1906 | 0 | wpabuf_free(buf); |
1907 | 0 | buf = NULL; |
1908 | 0 | goto out; |
1909 | 0 | } |
1910 | | |
1911 | | |
1912 | | static struct wpabuf * |
1913 | | dpp_build_conf_obj_legacy(struct dpp_authentication *auth, |
1914 | | struct dpp_configuration *conf) |
1915 | 0 | { |
1916 | 0 | struct wpabuf *buf; |
1917 | 0 | const char *akm_str; |
1918 | 0 | size_t len = 1000; |
1919 | |
|
1920 | 0 | if (conf->extra_name && conf->extra_value) |
1921 | 0 | len += 10 + os_strlen(conf->extra_name) + |
1922 | 0 | os_strlen(conf->extra_value); |
1923 | 0 | buf = dpp_build_conf_start(auth, conf, len); |
1924 | 0 | if (!buf) |
1925 | 0 | return NULL; |
1926 | | |
1927 | 0 | if (auth->akm_use_selector && dpp_akm_ver2(conf->akm)) |
1928 | 0 | akm_str = dpp_akm_selector_str(conf->akm); |
1929 | 0 | else |
1930 | 0 | akm_str = dpp_akm_str(conf->akm); |
1931 | 0 | json_start_object(buf, "cred"); |
1932 | 0 | json_add_string(buf, "akm", akm_str); |
1933 | 0 | json_value_sep(buf); |
1934 | 0 | dpp_build_legacy_cred_params(buf, conf); |
1935 | 0 | json_end_object(buf); |
1936 | 0 | if (conf->extra_name && conf->extra_value) { |
1937 | 0 | json_value_sep(buf); |
1938 | 0 | wpabuf_printf(buf, "\"%s\":%s", conf->extra_name, |
1939 | 0 | conf->extra_value); |
1940 | 0 | } |
1941 | 0 | json_end_object(buf); |
1942 | |
|
1943 | 0 | wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", |
1944 | 0 | wpabuf_head(buf), wpabuf_len(buf)); |
1945 | |
|
1946 | 0 | return buf; |
1947 | 0 | } |
1948 | | |
1949 | | |
1950 | | static int dpp_get_peer_bi_id(struct dpp_authentication *auth) |
1951 | 0 | { |
1952 | 0 | struct dpp_bootstrap_info *bi; |
1953 | |
|
1954 | 0 | if (auth->peer_bi) |
1955 | 0 | return auth->peer_bi->id; |
1956 | 0 | if (auth->tmp_peer_bi) |
1957 | 0 | return auth->tmp_peer_bi->id; |
1958 | | |
1959 | 0 | bi = os_zalloc(sizeof(*bi)); |
1960 | 0 | if (!bi) |
1961 | 0 | return -1; |
1962 | 0 | bi->id = dpp_next_id(auth->global); |
1963 | 0 | dl_list_add(&auth->global->bootstrap, &bi->list); |
1964 | 0 | auth->tmp_peer_bi = bi; |
1965 | 0 | return bi->id; |
1966 | 0 | } |
1967 | | |
1968 | | |
1969 | | static struct wpabuf * |
1970 | | dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole, |
1971 | | int idx, bool cert_req) |
1972 | 0 | { |
1973 | 0 | struct dpp_configuration *conf = NULL; |
1974 | |
|
1975 | | #ifdef CONFIG_TESTING_OPTIONS |
1976 | | if (auth->config_obj_override) { |
1977 | | if (idx != 0) |
1978 | | return NULL; |
1979 | | wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); |
1980 | | return wpabuf_alloc_copy(auth->config_obj_override, |
1981 | | os_strlen(auth->config_obj_override)); |
1982 | | } |
1983 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1984 | |
|
1985 | 0 | if (idx == 0) { |
1986 | 0 | if (netrole == DPP_NETROLE_STA) |
1987 | 0 | conf = auth->conf_sta; |
1988 | 0 | else if (netrole == DPP_NETROLE_AP) |
1989 | 0 | conf = auth->conf_ap; |
1990 | 0 | } else if (idx == 1) { |
1991 | 0 | if (netrole == DPP_NETROLE_STA) |
1992 | 0 | conf = auth->conf2_sta; |
1993 | 0 | else if (netrole == DPP_NETROLE_AP) |
1994 | 0 | conf = auth->conf2_ap; |
1995 | 0 | } |
1996 | 0 | if (!conf) { |
1997 | 0 | if (idx == 0) { |
1998 | 0 | if (auth->use_config_query) { |
1999 | 0 | wpa_printf(MSG_DEBUG, |
2000 | 0 | "DPP: No configuration available for Enrollee(%s) - waiting for configuration", |
2001 | 0 | dpp_netrole_str(netrole)); |
2002 | 0 | auth->waiting_config = true; |
2003 | 0 | dpp_get_peer_bi_id(auth); |
2004 | 0 | return NULL; |
2005 | 0 | } |
2006 | 0 | wpa_printf(MSG_DEBUG, |
2007 | 0 | "DPP: No configuration available for Enrollee(%s) - reject configuration request", |
2008 | 0 | dpp_netrole_str(netrole)); |
2009 | 0 | } |
2010 | 0 | return NULL; |
2011 | 0 | } |
2012 | | |
2013 | 0 | if (conf->akm == DPP_AKM_DOT1X) { |
2014 | 0 | if (!auth->conf) { |
2015 | 0 | wpa_printf(MSG_DEBUG, |
2016 | 0 | "DPP: No Configurator data available"); |
2017 | 0 | return NULL; |
2018 | 0 | } |
2019 | 0 | if (!cert_req && !auth->certbag) { |
2020 | 0 | wpa_printf(MSG_DEBUG, |
2021 | 0 | "DPP: No certificate data available for dot1x configuration"); |
2022 | 0 | return NULL; |
2023 | 0 | } |
2024 | 0 | return dpp_build_conf_obj_dpp(auth, conf); |
2025 | 0 | } |
2026 | 0 | if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf)) |
2027 | 0 | return dpp_build_conf_obj_dpp(auth, conf); |
2028 | 0 | return dpp_build_conf_obj_legacy(auth, conf); |
2029 | 0 | } |
2030 | | |
2031 | | |
2032 | | struct wpabuf * |
2033 | | dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, |
2034 | | u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req) |
2035 | 0 | { |
2036 | 0 | struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL, *pc = NULL; |
2037 | 0 | size_t clear_len, attr_len; |
2038 | 0 | struct wpabuf *clear = NULL, *msg = NULL; |
2039 | 0 | u8 *wrapped; |
2040 | 0 | const u8 *addr[1]; |
2041 | 0 | size_t len[1]; |
2042 | 0 | enum dpp_status_error status; |
2043 | |
|
2044 | 0 | if (auth->force_conf_resp_status != DPP_STATUS_OK) { |
2045 | 0 | status = auth->force_conf_resp_status; |
2046 | 0 | goto forced_status; |
2047 | 0 | } |
2048 | | |
2049 | 0 | if (netrole == DPP_NETROLE_CONFIGURATOR) { |
2050 | 0 | #ifdef CONFIG_DPP2 |
2051 | 0 | env_data = dpp_build_enveloped_data(auth); |
2052 | 0 | #endif /* CONFIG_DPP2 */ |
2053 | 0 | } else { |
2054 | 0 | conf = dpp_build_conf_obj(auth, netrole, 0, cert_req); |
2055 | 0 | if (conf) { |
2056 | 0 | wpa_hexdump_ascii(MSG_DEBUG, |
2057 | 0 | "DPP: configurationObject JSON", |
2058 | 0 | wpabuf_head(conf), wpabuf_len(conf)); |
2059 | 0 | conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req); |
2060 | 0 | } |
2061 | 0 | } |
2062 | |
|
2063 | 0 | if (!conf && auth->waiting_config) |
2064 | 0 | return NULL; |
2065 | 0 | if (conf || env_data) |
2066 | 0 | status = DPP_STATUS_OK; |
2067 | 0 | else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta && |
2068 | 0 | auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr) |
2069 | 0 | status = DPP_STATUS_CSR_NEEDED; |
2070 | | #ifdef CONFIG_DPP3 |
2071 | | else if (auth->waiting_new_key) |
2072 | | status = DPP_STATUS_NEW_KEY_NEEDED; |
2073 | | #endif /* CONFIG_DPP3 */ |
2074 | 0 | else |
2075 | 0 | status = DPP_STATUS_CONFIGURE_FAILURE; |
2076 | 0 | forced_status: |
2077 | 0 | auth->conf_resp_status = status; |
2078 | | |
2079 | | /* { E-nonce, configurationObject[, sendConnStatus]}ke */ |
2080 | 0 | clear_len = 4 + e_nonce_len; |
2081 | 0 | if (conf) |
2082 | 0 | clear_len += 4 + wpabuf_len(conf); |
2083 | 0 | if (conf2) |
2084 | 0 | clear_len += 4 + wpabuf_len(conf2); |
2085 | 0 | if (env_data) |
2086 | 0 | clear_len += 4 + wpabuf_len(env_data); |
2087 | 0 | if (auth->peer_version >= 2 && auth->send_conn_status && |
2088 | 0 | netrole == DPP_NETROLE_STA) |
2089 | 0 | clear_len += 4; |
2090 | 0 | if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && |
2091 | 0 | auth->conf_sta->csrattrs) |
2092 | 0 | clear_len += 4 + os_strlen(auth->conf_sta->csrattrs); |
2093 | | #ifdef CONFIG_DPP3 |
2094 | | if (status == DPP_STATUS_NEW_KEY_NEEDED) { |
2095 | | struct crypto_ec_key *new_pc; |
2096 | | |
2097 | | clear_len += 6; /* Finite Cyclic Group attribute */ |
2098 | | |
2099 | | wpa_printf(MSG_DEBUG, |
2100 | | "DPP: Generate a new own protocol key for the curve %s", |
2101 | | auth->conf->net_access_key_curve->name); |
2102 | | new_pc = dpp_gen_keypair(auth->conf->net_access_key_curve); |
2103 | | if (!new_pc) { |
2104 | | wpa_printf(MSG_DEBUG, "DPP: Failed to generate new Pc"); |
2105 | | return NULL; |
2106 | | } |
2107 | | pc = crypto_ec_key_get_pubkey_point(new_pc, 0); |
2108 | | if (!pc) { |
2109 | | crypto_ec_key_deinit(new_pc); |
2110 | | return NULL; |
2111 | | } |
2112 | | crypto_ec_key_deinit(auth->own_protocol_key); |
2113 | | auth->own_protocol_key = new_pc; |
2114 | | auth->new_curve = auth->conf->net_access_key_curve; |
2115 | | clear_len += 4 + wpabuf_len(pc); |
2116 | | } |
2117 | | #endif /* CONFIG_DPP3 */ |
2118 | 0 | clear = wpabuf_alloc(clear_len); |
2119 | 0 | attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; |
2120 | | #ifdef CONFIG_TESTING_OPTIONS |
2121 | | if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) |
2122 | | attr_len += 5; |
2123 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2124 | 0 | msg = wpabuf_alloc(attr_len); |
2125 | 0 | if (!clear || !msg) |
2126 | 0 | goto fail; |
2127 | | |
2128 | | #ifdef CONFIG_TESTING_OPTIONS |
2129 | | if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { |
2130 | | wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); |
2131 | | goto skip_e_nonce; |
2132 | | } |
2133 | | if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { |
2134 | | wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); |
2135 | | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
2136 | | wpabuf_put_le16(clear, e_nonce_len); |
2137 | | wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); |
2138 | | wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); |
2139 | | goto skip_e_nonce; |
2140 | | } |
2141 | | if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { |
2142 | | wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); |
2143 | | goto skip_wrapped_data; |
2144 | | } |
2145 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2146 | | |
2147 | | /* E-nonce */ |
2148 | 0 | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
2149 | 0 | wpabuf_put_le16(clear, e_nonce_len); |
2150 | 0 | wpabuf_put_data(clear, e_nonce, e_nonce_len); |
2151 | |
|
2152 | | #ifdef CONFIG_TESTING_OPTIONS |
2153 | | skip_e_nonce: |
2154 | | if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { |
2155 | | wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); |
2156 | | goto skip_config_obj; |
2157 | | } |
2158 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2159 | |
|
2160 | 0 | if (conf) { |
2161 | 0 | wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); |
2162 | 0 | wpabuf_put_le16(clear, wpabuf_len(conf)); |
2163 | 0 | wpabuf_put_buf(clear, conf); |
2164 | 0 | } |
2165 | 0 | if (auth->peer_version >= 2 && conf2) { |
2166 | 0 | wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); |
2167 | 0 | wpabuf_put_le16(clear, wpabuf_len(conf2)); |
2168 | 0 | wpabuf_put_buf(clear, conf2); |
2169 | 0 | } else if (conf2) { |
2170 | 0 | wpa_printf(MSG_DEBUG, |
2171 | 0 | "DPP: Second Config Object available, but peer does not support more than one"); |
2172 | 0 | } |
2173 | 0 | if (env_data) { |
2174 | 0 | wpabuf_put_le16(clear, DPP_ATTR_ENVELOPED_DATA); |
2175 | 0 | wpabuf_put_le16(clear, wpabuf_len(env_data)); |
2176 | 0 | wpabuf_put_buf(clear, env_data); |
2177 | 0 | } |
2178 | |
|
2179 | 0 | if (auth->peer_version >= 2 && auth->send_conn_status && |
2180 | 0 | netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) { |
2181 | 0 | wpa_printf(MSG_DEBUG, "DPP: sendConnStatus"); |
2182 | 0 | wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS); |
2183 | 0 | wpabuf_put_le16(clear, 0); |
2184 | 0 | } |
2185 | |
|
2186 | 0 | if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && |
2187 | 0 | auth->conf_sta->csrattrs) { |
2188 | 0 | auth->waiting_csr = true; |
2189 | 0 | wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request"); |
2190 | 0 | wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ); |
2191 | 0 | wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs)); |
2192 | 0 | wpabuf_put_str(clear, auth->conf_sta->csrattrs); |
2193 | 0 | } |
2194 | |
|
2195 | | #ifdef CONFIG_DPP3 |
2196 | | if (status == DPP_STATUS_NEW_KEY_NEEDED && auth->conf && |
2197 | | auth->conf->net_access_key_curve) { |
2198 | | u16 ike_group = auth->conf->net_access_key_curve->ike_group; |
2199 | | |
2200 | | /* Finite Cyclic Group attribute */ |
2201 | | wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u", |
2202 | | ike_group); |
2203 | | wpabuf_put_le16(clear, DPP_ATTR_FINITE_CYCLIC_GROUP); |
2204 | | wpabuf_put_le16(clear, 2); |
2205 | | wpabuf_put_le16(clear, ike_group); |
2206 | | |
2207 | | if (pc) { |
2208 | | wpa_printf(MSG_DEBUG, "DPP: Pc"); |
2209 | | wpabuf_put_le16(clear, DPP_ATTR_R_PROTOCOL_KEY); |
2210 | | wpabuf_put_le16(clear, wpabuf_len(pc)); |
2211 | | wpabuf_put_buf(clear, pc); |
2212 | | } |
2213 | | } |
2214 | | #endif /* CONFIG_DPP3 */ |
2215 | |
|
2216 | | #ifdef CONFIG_TESTING_OPTIONS |
2217 | | skip_config_obj: |
2218 | | if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { |
2219 | | wpa_printf(MSG_INFO, "DPP: TESTING - Status"); |
2220 | | goto skip_status; |
2221 | | } |
2222 | | if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { |
2223 | | wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); |
2224 | | status = 255; |
2225 | | } |
2226 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2227 | | |
2228 | | /* DPP Status */ |
2229 | 0 | dpp_build_attr_status(msg, status); |
2230 | |
|
2231 | | #ifdef CONFIG_TESTING_OPTIONS |
2232 | | skip_status: |
2233 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2234 | |
|
2235 | 0 | addr[0] = wpabuf_head(msg); |
2236 | 0 | len[0] = wpabuf_len(msg); |
2237 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); |
2238 | |
|
2239 | 0 | wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); |
2240 | 0 | wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
2241 | 0 | wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
2242 | |
|
2243 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); |
2244 | 0 | if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, |
2245 | 0 | wpabuf_head(clear), wpabuf_len(clear), |
2246 | 0 | 1, addr, len, wrapped) < 0) |
2247 | 0 | goto fail; |
2248 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
2249 | 0 | wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); |
2250 | |
|
2251 | | #ifdef CONFIG_TESTING_OPTIONS |
2252 | | if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { |
2253 | | wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); |
2254 | | dpp_build_attr_status(msg, DPP_STATUS_OK); |
2255 | | } |
2256 | | skip_wrapped_data: |
2257 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2258 | |
|
2259 | 0 | wpa_hexdump_buf(MSG_DEBUG, |
2260 | 0 | "DPP: Configuration Response attributes", msg); |
2261 | 0 | out: |
2262 | 0 | wpabuf_clear_free(conf); |
2263 | 0 | wpabuf_clear_free(conf2); |
2264 | 0 | wpabuf_clear_free(env_data); |
2265 | 0 | wpabuf_clear_free(clear); |
2266 | 0 | wpabuf_free(pc); |
2267 | |
|
2268 | 0 | return msg; |
2269 | 0 | fail: |
2270 | 0 | wpabuf_free(msg); |
2271 | 0 | msg = NULL; |
2272 | 0 | goto out; |
2273 | 0 | } |
2274 | | |
2275 | | |
2276 | | struct wpabuf * |
2277 | | dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, |
2278 | | size_t attr_len) |
2279 | 0 | { |
2280 | 0 | const u8 *wrapped_data, *e_nonce, *config_attr; |
2281 | 0 | u16 wrapped_data_len, e_nonce_len, config_attr_len; |
2282 | 0 | u8 *unwrapped = NULL; |
2283 | 0 | size_t unwrapped_len = 0; |
2284 | 0 | struct wpabuf *resp = NULL; |
2285 | 0 | struct json_token *root = NULL, *token; |
2286 | 0 | enum dpp_netrole netrole; |
2287 | 0 | struct wpabuf *cert_req = NULL; |
2288 | | #ifdef CONFIG_DPP3 |
2289 | | const u8 *i_proto; |
2290 | | u16 i_proto_len; |
2291 | | #endif /* CONFIG_DPP3 */ |
2292 | |
|
2293 | | #ifdef CONFIG_TESTING_OPTIONS |
2294 | | if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { |
2295 | | wpa_printf(MSG_INFO, |
2296 | | "DPP: TESTING - stop at Config Request"); |
2297 | | return NULL; |
2298 | | } |
2299 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2300 | |
|
2301 | 0 | if (dpp_check_attrs(attr_start, attr_len) < 0) { |
2302 | 0 | dpp_auth_fail(auth, "Invalid attribute in config request"); |
2303 | 0 | return NULL; |
2304 | 0 | } |
2305 | | |
2306 | 0 | wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, |
2307 | 0 | &wrapped_data_len); |
2308 | 0 | if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { |
2309 | 0 | dpp_auth_fail(auth, |
2310 | 0 | "Missing or invalid required Wrapped Data attribute"); |
2311 | 0 | return NULL; |
2312 | 0 | } |
2313 | | |
2314 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
2315 | 0 | wrapped_data, wrapped_data_len); |
2316 | 0 | unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; |
2317 | 0 | unwrapped = os_malloc(unwrapped_len); |
2318 | 0 | if (!unwrapped) |
2319 | 0 | return NULL; |
2320 | 0 | if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, |
2321 | 0 | wrapped_data, wrapped_data_len, |
2322 | 0 | 0, NULL, NULL, unwrapped) < 0) { |
2323 | 0 | dpp_auth_fail(auth, "AES-SIV decryption failed"); |
2324 | 0 | goto fail; |
2325 | 0 | } |
2326 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", |
2327 | 0 | unwrapped, unwrapped_len); |
2328 | |
|
2329 | 0 | if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { |
2330 | 0 | dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); |
2331 | 0 | goto fail; |
2332 | 0 | } |
2333 | | |
2334 | 0 | e_nonce = dpp_get_attr(unwrapped, unwrapped_len, |
2335 | 0 | DPP_ATTR_ENROLLEE_NONCE, |
2336 | 0 | &e_nonce_len); |
2337 | 0 | if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { |
2338 | 0 | dpp_auth_fail(auth, |
2339 | 0 | "Missing or invalid Enrollee Nonce attribute"); |
2340 | 0 | goto fail; |
2341 | 0 | } |
2342 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); |
2343 | 0 | os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); |
2344 | |
|
2345 | | #ifdef CONFIG_DPP3 |
2346 | | i_proto = dpp_get_attr(unwrapped, unwrapped_len, |
2347 | | DPP_ATTR_I_PROTOCOL_KEY, &i_proto_len); |
2348 | | if (i_proto && !auth->waiting_new_key) { |
2349 | | dpp_auth_fail(auth, |
2350 | | "Enrollee included a new protocol key even though one was not expected"); |
2351 | | goto fail; |
2352 | | } |
2353 | | if (i_proto) { |
2354 | | struct crypto_ec_key *pe; |
2355 | | u8 auth_i[DPP_MAX_HASH_LEN]; |
2356 | | const u8 *rx_auth_i; |
2357 | | u16 rx_auth_i_len; |
2358 | | |
2359 | | wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key (new Pe)", |
2360 | | i_proto, i_proto_len); |
2361 | | |
2362 | | pe = dpp_set_pubkey_point(auth->own_protocol_key, |
2363 | | i_proto, i_proto_len); |
2364 | | if (!pe) { |
2365 | | dpp_auth_fail(auth, |
2366 | | "Invalid Initiator Protocol Key (Pe)"); |
2367 | | goto fail; |
2368 | | } |
2369 | | dpp_debug_print_key("New Peer Protocol Key (Pe)", pe); |
2370 | | crypto_ec_key_deinit(auth->peer_protocol_key); |
2371 | | auth->peer_protocol_key = pe; |
2372 | | auth->new_key_received = true; |
2373 | | auth->waiting_new_key = false; |
2374 | | |
2375 | | if (dpp_derive_auth_i(auth, auth_i) < 0) |
2376 | | goto fail; |
2377 | | |
2378 | | rx_auth_i = dpp_get_attr(unwrapped, unwrapped_len, |
2379 | | DPP_ATTR_I_AUTH_TAG, &rx_auth_i_len); |
2380 | | if (!rx_auth_i) { |
2381 | | dpp_auth_fail(auth, |
2382 | | "Missing Initiator Authentication Tag"); |
2383 | | goto fail; |
2384 | | } |
2385 | | if (rx_auth_i_len != auth->curve->hash_len || |
2386 | | os_memcmp(rx_auth_i, auth_i, auth->curve->hash_len) != 0) { |
2387 | | dpp_auth_fail(auth, |
2388 | | "Mismatch in Initiator Authenticating Tag"); |
2389 | | wpa_hexdump(MSG_DEBUG, "DPP: Received Auth-I", |
2390 | | rx_auth_i, rx_auth_i_len); |
2391 | | wpa_hexdump(MSG_DEBUG, "DPP: Derived Auth-I'", |
2392 | | auth_i, auth->curve->hash_len); |
2393 | | goto fail; |
2394 | | } |
2395 | | } |
2396 | | #endif /* CONFIG_DPP3 */ |
2397 | |
|
2398 | 0 | config_attr = dpp_get_attr(unwrapped, unwrapped_len, |
2399 | 0 | DPP_ATTR_CONFIG_ATTR_OBJ, |
2400 | 0 | &config_attr_len); |
2401 | 0 | if (!config_attr) { |
2402 | 0 | dpp_auth_fail(auth, |
2403 | 0 | "Missing or invalid Config Attributes attribute"); |
2404 | 0 | goto fail; |
2405 | 0 | } |
2406 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", |
2407 | 0 | config_attr, config_attr_len); |
2408 | |
|
2409 | 0 | root = json_parse((const char *) config_attr, config_attr_len); |
2410 | 0 | if (!root) { |
2411 | 0 | dpp_auth_fail(auth, "Could not parse Config Attributes"); |
2412 | 0 | goto fail; |
2413 | 0 | } |
2414 | | |
2415 | 0 | token = json_get_member(root, "name"); |
2416 | 0 | if (!token || token->type != JSON_STRING) { |
2417 | 0 | dpp_auth_fail(auth, "No Config Attributes - name"); |
2418 | 0 | goto fail; |
2419 | 0 | } |
2420 | 0 | wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); |
2421 | 0 | os_free(auth->e_name); |
2422 | 0 | auth->e_name = os_strdup(token->string); |
2423 | |
|
2424 | 0 | token = json_get_member(root, "wi-fi_tech"); |
2425 | 0 | if (!token || token->type != JSON_STRING) { |
2426 | 0 | dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); |
2427 | 0 | goto fail; |
2428 | 0 | } |
2429 | 0 | wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); |
2430 | 0 | if (os_strcmp(token->string, "infra") != 0) { |
2431 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", |
2432 | 0 | token->string); |
2433 | 0 | dpp_auth_fail(auth, "Unsupported wi-fi_tech"); |
2434 | 0 | goto fail; |
2435 | 0 | } |
2436 | | |
2437 | 0 | token = json_get_member(root, "netRole"); |
2438 | 0 | if (!token || token->type != JSON_STRING) { |
2439 | 0 | dpp_auth_fail(auth, "No Config Attributes - netRole"); |
2440 | 0 | goto fail; |
2441 | 0 | } |
2442 | 0 | wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); |
2443 | 0 | if (os_strcmp(token->string, "sta") == 0) { |
2444 | 0 | netrole = DPP_NETROLE_STA; |
2445 | 0 | } else if (os_strcmp(token->string, "ap") == 0) { |
2446 | 0 | netrole = DPP_NETROLE_AP; |
2447 | 0 | } else if (os_strcmp(token->string, "configurator") == 0) { |
2448 | 0 | netrole = DPP_NETROLE_CONFIGURATOR; |
2449 | 0 | } else { |
2450 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", |
2451 | 0 | token->string); |
2452 | 0 | dpp_auth_fail(auth, "Unsupported netRole"); |
2453 | 0 | goto fail; |
2454 | 0 | } |
2455 | 0 | auth->e_netrole = netrole; |
2456 | |
|
2457 | 0 | token = json_get_member(root, "mudurl"); |
2458 | 0 | if (token && token->type == JSON_STRING) { |
2459 | 0 | wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string); |
2460 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s", |
2461 | 0 | token->string); |
2462 | 0 | os_free(auth->e_mud_url); |
2463 | 0 | auth->e_mud_url = os_strdup(token->string); |
2464 | 0 | } |
2465 | |
|
2466 | 0 | token = json_get_member(root, "bandSupport"); |
2467 | 0 | if (token && token->type == JSON_ARRAY) { |
2468 | 0 | int *opclass = NULL; |
2469 | 0 | char txt[200], *pos, *end; |
2470 | 0 | int i, res; |
2471 | |
|
2472 | 0 | wpa_printf(MSG_DEBUG, "DPP: bandSupport"); |
2473 | 0 | token = token->child; |
2474 | 0 | while (token) { |
2475 | 0 | if (token->type != JSON_NUMBER) { |
2476 | 0 | wpa_printf(MSG_DEBUG, |
2477 | 0 | "DPP: Invalid bandSupport array member type"); |
2478 | 0 | } else { |
2479 | 0 | wpa_printf(MSG_DEBUG, |
2480 | 0 | "DPP: Supported global operating class: %d", |
2481 | 0 | token->number); |
2482 | 0 | int_array_add_unique(&opclass, token->number); |
2483 | 0 | } |
2484 | 0 | token = token->sibling; |
2485 | 0 | } |
2486 | |
|
2487 | 0 | txt[0] = '\0'; |
2488 | 0 | pos = txt; |
2489 | 0 | end = txt + sizeof(txt); |
2490 | 0 | for (i = 0; opclass && opclass[i]; i++) { |
2491 | 0 | res = os_snprintf(pos, end - pos, "%s%d", |
2492 | 0 | pos == txt ? "" : ",", opclass[i]); |
2493 | 0 | if (os_snprintf_error(end - pos, res)) { |
2494 | 0 | *pos = '\0'; |
2495 | 0 | break; |
2496 | 0 | } |
2497 | 0 | pos += res; |
2498 | 0 | } |
2499 | 0 | os_free(auth->e_band_support); |
2500 | 0 | auth->e_band_support = opclass; |
2501 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s", |
2502 | 0 | txt); |
2503 | 0 | } |
2504 | |
|
2505 | 0 | #ifdef CONFIG_DPP2 |
2506 | 0 | cert_req = json_get_member_base64(root, "pkcs10"); |
2507 | 0 | if (cert_req) { |
2508 | 0 | char *txt; |
2509 | 0 | int id; |
2510 | |
|
2511 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req); |
2512 | 0 | if (dpp_validate_csr(auth, cert_req) < 0) { |
2513 | 0 | wpa_printf(MSG_DEBUG, "DPP: CSR is not valid"); |
2514 | 0 | auth->force_conf_resp_status = DPP_STATUS_CSR_BAD; |
2515 | 0 | goto cont; |
2516 | 0 | } |
2517 | | |
2518 | 0 | id = dpp_get_peer_bi_id(auth); |
2519 | 0 | if (id < 0) |
2520 | 0 | goto fail; |
2521 | | |
2522 | 0 | wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA"); |
2523 | 0 | txt = base64_encode_no_lf(wpabuf_head(cert_req), |
2524 | 0 | wpabuf_len(cert_req), NULL); |
2525 | 0 | if (!txt) |
2526 | 0 | goto fail; |
2527 | | |
2528 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s", |
2529 | 0 | id, txt); |
2530 | 0 | os_free(txt); |
2531 | 0 | auth->waiting_csr = false; |
2532 | 0 | auth->waiting_cert = true; |
2533 | 0 | goto fail; |
2534 | 0 | } |
2535 | 0 | cont: |
2536 | 0 | #endif /* CONFIG_DPP2 */ |
2537 | |
|
2538 | 0 | resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole, |
2539 | 0 | cert_req); |
2540 | |
|
2541 | 0 | fail: |
2542 | 0 | wpabuf_free(cert_req); |
2543 | 0 | json_free(root); |
2544 | 0 | os_free(unwrapped); |
2545 | 0 | return resp; |
2546 | 0 | } |
2547 | | |
2548 | | |
2549 | | static int dpp_parse_cred_legacy(struct dpp_config_obj *conf, |
2550 | | struct json_token *cred) |
2551 | 0 | { |
2552 | 0 | struct json_token *pass, *psk_hex; |
2553 | |
|
2554 | 0 | wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); |
2555 | |
|
2556 | 0 | pass = json_get_member(cred, "pass"); |
2557 | 0 | psk_hex = json_get_member(cred, "psk_hex"); |
2558 | |
|
2559 | 0 | if (pass && pass->type == JSON_STRING) { |
2560 | 0 | size_t len = os_strlen(pass->string); |
2561 | |
|
2562 | 0 | wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", |
2563 | 0 | pass->string, len); |
2564 | 0 | if (len < 8 || len > 63) |
2565 | 0 | return -1; |
2566 | 0 | os_strlcpy(conf->passphrase, pass->string, |
2567 | 0 | sizeof(conf->passphrase)); |
2568 | 0 | } else if (psk_hex && psk_hex->type == JSON_STRING) { |
2569 | 0 | if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) { |
2570 | 0 | wpa_printf(MSG_DEBUG, |
2571 | 0 | "DPP: Unexpected psk_hex with akm=sae"); |
2572 | 0 | return -1; |
2573 | 0 | } |
2574 | 0 | if (os_strlen(psk_hex->string) != PMK_LEN * 2 || |
2575 | 0 | hexstr2bin(psk_hex->string, conf->psk, PMK_LEN) < 0) { |
2576 | 0 | wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); |
2577 | 0 | return -1; |
2578 | 0 | } |
2579 | 0 | wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", |
2580 | 0 | conf->psk, PMK_LEN); |
2581 | 0 | conf->psk_set = 1; |
2582 | 0 | } else { |
2583 | 0 | wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); |
2584 | 0 | return -1; |
2585 | 0 | } |
2586 | | |
2587 | 0 | if (dpp_akm_sae(conf->akm) && !conf->passphrase[0]) { |
2588 | 0 | wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); |
2589 | 0 | return -1; |
2590 | 0 | } |
2591 | | |
2592 | 0 | return 0; |
2593 | 0 | } |
2594 | | |
2595 | | |
2596 | | struct crypto_ec_key * dpp_parse_jwk(struct json_token *jwk, |
2597 | | const struct dpp_curve_params **key_curve) |
2598 | 0 | { |
2599 | 0 | struct json_token *token; |
2600 | 0 | const struct dpp_curve_params *curve; |
2601 | 0 | struct wpabuf *x = NULL, *y = NULL; |
2602 | 0 | struct crypto_ec_key *key = NULL; |
2603 | |
|
2604 | 0 | token = json_get_member(jwk, "kty"); |
2605 | 0 | if (!token || token->type != JSON_STRING) { |
2606 | 0 | wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); |
2607 | 0 | goto fail; |
2608 | 0 | } |
2609 | 0 | if (os_strcmp(token->string, "EC") != 0) { |
2610 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", |
2611 | 0 | token->string); |
2612 | 0 | goto fail; |
2613 | 0 | } |
2614 | | |
2615 | 0 | token = json_get_member(jwk, "crv"); |
2616 | 0 | if (!token || token->type != JSON_STRING) { |
2617 | 0 | wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); |
2618 | 0 | goto fail; |
2619 | 0 | } |
2620 | 0 | curve = dpp_get_curve_jwk_crv(token->string); |
2621 | 0 | if (!curve) { |
2622 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", |
2623 | 0 | token->string); |
2624 | 0 | goto fail; |
2625 | 0 | } |
2626 | | |
2627 | 0 | x = json_get_member_base64url(jwk, "x"); |
2628 | 0 | if (!x) { |
2629 | 0 | wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); |
2630 | 0 | goto fail; |
2631 | 0 | } |
2632 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); |
2633 | 0 | if (wpabuf_len(x) != curve->prime_len) { |
2634 | 0 | wpa_printf(MSG_DEBUG, |
2635 | 0 | "DPP: Unexpected JWK x length %u (expected %u for curve %s)", |
2636 | 0 | (unsigned int) wpabuf_len(x), |
2637 | 0 | (unsigned int) curve->prime_len, curve->name); |
2638 | 0 | goto fail; |
2639 | 0 | } |
2640 | | |
2641 | 0 | y = json_get_member_base64url(jwk, "y"); |
2642 | 0 | if (!y) { |
2643 | 0 | wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); |
2644 | 0 | goto fail; |
2645 | 0 | } |
2646 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); |
2647 | 0 | if (wpabuf_len(y) != curve->prime_len) { |
2648 | 0 | wpa_printf(MSG_DEBUG, |
2649 | 0 | "DPP: Unexpected JWK y length %u (expected %u for curve %s)", |
2650 | 0 | (unsigned int) wpabuf_len(y), |
2651 | 0 | (unsigned int) curve->prime_len, curve->name); |
2652 | 0 | goto fail; |
2653 | 0 | } |
2654 | | |
2655 | 0 | key = crypto_ec_key_set_pub(curve->ike_group, wpabuf_head(x), |
2656 | 0 | wpabuf_head(y), wpabuf_len(x)); |
2657 | 0 | if (!key) |
2658 | 0 | goto fail; |
2659 | | |
2660 | 0 | *key_curve = curve; |
2661 | |
|
2662 | 0 | fail: |
2663 | 0 | wpabuf_free(x); |
2664 | 0 | wpabuf_free(y); |
2665 | |
|
2666 | 0 | return key; |
2667 | 0 | } |
2668 | | |
2669 | | |
2670 | | int dpp_key_expired(const char *timestamp, os_time_t *expiry) |
2671 | 0 | { |
2672 | 0 | struct os_time now; |
2673 | 0 | unsigned int year, month, day, hour, min, sec; |
2674 | 0 | os_time_t utime; |
2675 | 0 | const char *pos; |
2676 | | |
2677 | | /* ISO 8601 date and time: |
2678 | | * <date>T<time> |
2679 | | * YYYY-MM-DDTHH:MM:SSZ |
2680 | | * YYYY-MM-DDTHH:MM:SS+03:00 |
2681 | | */ |
2682 | 0 | if (os_strlen(timestamp) < 19) { |
2683 | 0 | wpa_printf(MSG_DEBUG, |
2684 | 0 | "DPP: Too short timestamp - assume expired key"); |
2685 | 0 | return 1; |
2686 | 0 | } |
2687 | 0 | if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", |
2688 | 0 | &year, &month, &day, &hour, &min, &sec) != 6) { |
2689 | 0 | wpa_printf(MSG_DEBUG, |
2690 | 0 | "DPP: Failed to parse expiration day - assume expired key"); |
2691 | 0 | return 1; |
2692 | 0 | } |
2693 | | |
2694 | 0 | if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) { |
2695 | 0 | wpa_printf(MSG_DEBUG, |
2696 | 0 | "DPP: Invalid date/time information - assume expired key"); |
2697 | 0 | return 1; |
2698 | 0 | } |
2699 | | |
2700 | 0 | pos = timestamp + 19; |
2701 | 0 | if (*pos == 'Z' || *pos == '\0') { |
2702 | | /* In UTC - no need to adjust */ |
2703 | 0 | } else if (*pos == '-' || *pos == '+') { |
2704 | 0 | int items; |
2705 | | |
2706 | | /* Adjust local time to UTC */ |
2707 | 0 | items = sscanf(pos + 1, "%02u:%02u", &hour, &min); |
2708 | 0 | if (items < 1) { |
2709 | 0 | wpa_printf(MSG_DEBUG, |
2710 | 0 | "DPP: Invalid time zone designator (%s) - assume expired key", |
2711 | 0 | pos); |
2712 | 0 | return 1; |
2713 | 0 | } |
2714 | 0 | if (*pos == '-') |
2715 | 0 | utime += 3600 * hour; |
2716 | 0 | if (*pos == '+') |
2717 | 0 | utime -= 3600 * hour; |
2718 | 0 | if (items > 1) { |
2719 | 0 | if (*pos == '-') |
2720 | 0 | utime += 60 * min; |
2721 | 0 | if (*pos == '+') |
2722 | 0 | utime -= 60 * min; |
2723 | 0 | } |
2724 | 0 | } else { |
2725 | 0 | wpa_printf(MSG_DEBUG, |
2726 | 0 | "DPP: Invalid time zone designator (%s) - assume expired key", |
2727 | 0 | pos); |
2728 | 0 | return 1; |
2729 | 0 | } |
2730 | 0 | if (expiry) |
2731 | 0 | *expiry = utime; |
2732 | |
|
2733 | 0 | if (os_get_time(&now) < 0) { |
2734 | 0 | wpa_printf(MSG_DEBUG, |
2735 | 0 | "DPP: Cannot get current time - assume expired key"); |
2736 | 0 | return 1; |
2737 | 0 | } |
2738 | | |
2739 | 0 | if (now.sec > utime) { |
2740 | 0 | wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)", |
2741 | 0 | utime, now.sec); |
2742 | 0 | return 1; |
2743 | 0 | } |
2744 | | |
2745 | 0 | return 0; |
2746 | 0 | } |
2747 | | |
2748 | | |
2749 | | static int dpp_parse_connector(struct dpp_authentication *auth, |
2750 | | struct dpp_config_obj *conf, |
2751 | | const unsigned char *payload, |
2752 | | u16 payload_len) |
2753 | 0 | { |
2754 | 0 | struct json_token *root, *groups, *netkey, *token; |
2755 | 0 | int ret = -1; |
2756 | 0 | struct crypto_ec_key *key = NULL; |
2757 | 0 | const struct dpp_curve_params *curve; |
2758 | 0 | unsigned int rules = 0; |
2759 | |
|
2760 | 0 | root = json_parse((const char *) payload, payload_len); |
2761 | 0 | if (!root) { |
2762 | 0 | wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); |
2763 | 0 | goto fail; |
2764 | 0 | } |
2765 | | |
2766 | 0 | groups = json_get_member(root, "groups"); |
2767 | 0 | if (!groups || groups->type != JSON_ARRAY) { |
2768 | 0 | wpa_printf(MSG_DEBUG, "DPP: No groups array found"); |
2769 | 0 | goto skip_groups; |
2770 | 0 | } |
2771 | 0 | for (token = groups->child; token; token = token->sibling) { |
2772 | 0 | struct json_token *id, *role; |
2773 | |
|
2774 | 0 | id = json_get_member(token, "groupId"); |
2775 | 0 | if (!id || id->type != JSON_STRING) { |
2776 | 0 | wpa_printf(MSG_DEBUG, "DPP: Missing groupId string"); |
2777 | 0 | goto fail; |
2778 | 0 | } |
2779 | | |
2780 | 0 | role = json_get_member(token, "netRole"); |
2781 | 0 | if (!role || role->type != JSON_STRING) { |
2782 | 0 | wpa_printf(MSG_DEBUG, "DPP: Missing netRole string"); |
2783 | 0 | goto fail; |
2784 | 0 | } |
2785 | 0 | wpa_printf(MSG_DEBUG, |
2786 | 0 | "DPP: connector group: groupId='%s' netRole='%s'", |
2787 | 0 | id->string, role->string); |
2788 | 0 | rules++; |
2789 | 0 | } |
2790 | 0 | skip_groups: |
2791 | |
|
2792 | 0 | if (!rules) { |
2793 | 0 | wpa_printf(MSG_DEBUG, |
2794 | 0 | "DPP: Connector includes no groups"); |
2795 | 0 | goto fail; |
2796 | 0 | } |
2797 | | |
2798 | 0 | token = json_get_member(root, "expiry"); |
2799 | 0 | if (!token || token->type != JSON_STRING) { |
2800 | 0 | wpa_printf(MSG_DEBUG, |
2801 | 0 | "DPP: No expiry string found - connector does not expire"); |
2802 | 0 | } else { |
2803 | 0 | wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); |
2804 | 0 | if (dpp_key_expired(token->string, |
2805 | 0 | &auth->net_access_key_expiry)) { |
2806 | 0 | wpa_printf(MSG_DEBUG, |
2807 | 0 | "DPP: Connector (netAccessKey) has expired"); |
2808 | 0 | goto fail; |
2809 | 0 | } |
2810 | 0 | } |
2811 | | |
2812 | 0 | netkey = json_get_member(root, "netAccessKey"); |
2813 | 0 | if (!netkey || netkey->type != JSON_OBJECT) { |
2814 | 0 | wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); |
2815 | 0 | goto fail; |
2816 | 0 | } |
2817 | | |
2818 | 0 | key = dpp_parse_jwk(netkey, &curve); |
2819 | 0 | if (!key) |
2820 | 0 | goto fail; |
2821 | 0 | dpp_debug_print_key("DPP: Received netAccessKey", key); |
2822 | |
|
2823 | 0 | if (crypto_ec_key_cmp(key, auth->own_protocol_key)) { |
2824 | 0 | wpa_printf(MSG_DEBUG, |
2825 | 0 | "DPP: netAccessKey in connector does not match own protocol key"); |
2826 | | #ifdef CONFIG_TESTING_OPTIONS |
2827 | | if (auth->ignore_netaccesskey_mismatch) { |
2828 | | wpa_printf(MSG_DEBUG, |
2829 | | "DPP: TESTING - skip netAccessKey mismatch"); |
2830 | | } else { |
2831 | | goto fail; |
2832 | | } |
2833 | | #else /* CONFIG_TESTING_OPTIONS */ |
2834 | 0 | goto fail; |
2835 | 0 | #endif /* CONFIG_TESTING_OPTIONS */ |
2836 | 0 | } |
2837 | | |
2838 | 0 | ret = 0; |
2839 | 0 | fail: |
2840 | 0 | crypto_ec_key_deinit(key); |
2841 | 0 | json_free(root); |
2842 | 0 | return ret; |
2843 | 0 | } |
2844 | | |
2845 | | |
2846 | | static void dpp_copy_csign(struct dpp_config_obj *conf, |
2847 | | struct crypto_ec_key *csign) |
2848 | 0 | { |
2849 | 0 | struct wpabuf *c_sign_key; |
2850 | |
|
2851 | 0 | c_sign_key = crypto_ec_key_get_subject_public_key(csign); |
2852 | 0 | if (!c_sign_key) |
2853 | 0 | return; |
2854 | | |
2855 | 0 | wpabuf_free(conf->c_sign_key); |
2856 | 0 | conf->c_sign_key = c_sign_key; |
2857 | 0 | } |
2858 | | |
2859 | | |
2860 | | static void dpp_copy_ppkey(struct dpp_config_obj *conf, |
2861 | | struct crypto_ec_key *ppkey) |
2862 | 0 | { |
2863 | 0 | struct wpabuf *pp_key; |
2864 | |
|
2865 | 0 | pp_key = crypto_ec_key_get_subject_public_key(ppkey); |
2866 | 0 | if (!pp_key) |
2867 | 0 | return; |
2868 | | |
2869 | 0 | wpabuf_free(conf->pp_key); |
2870 | 0 | conf->pp_key = pp_key; |
2871 | 0 | } |
2872 | | |
2873 | | |
2874 | | static void dpp_copy_netaccesskey(struct dpp_authentication *auth, |
2875 | | struct dpp_config_obj *conf) |
2876 | 0 | { |
2877 | 0 | struct wpabuf *net_access_key; |
2878 | 0 | struct crypto_ec_key *own_key; |
2879 | |
|
2880 | 0 | own_key = auth->own_protocol_key; |
2881 | 0 | #ifdef CONFIG_DPP2 |
2882 | 0 | if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY && |
2883 | 0 | auth->reconfig_old_protocol_key) |
2884 | 0 | own_key = auth->reconfig_old_protocol_key; |
2885 | 0 | #endif /* CONFIG_DPP2 */ |
2886 | |
|
2887 | 0 | net_access_key = crypto_ec_key_get_ecprivate_key(own_key, true); |
2888 | 0 | if (!net_access_key) |
2889 | 0 | return; |
2890 | | |
2891 | 0 | wpabuf_free(auth->net_access_key); |
2892 | 0 | auth->net_access_key = net_access_key; |
2893 | 0 | } |
2894 | | |
2895 | | |
2896 | | static int dpp_parse_cred_dpp(struct dpp_authentication *auth, |
2897 | | struct dpp_config_obj *conf, |
2898 | | struct json_token *cred) |
2899 | 0 | { |
2900 | 0 | struct dpp_signed_connector_info info; |
2901 | 0 | struct json_token *token, *csign, *ppkey; |
2902 | 0 | int ret = -1; |
2903 | 0 | struct crypto_ec_key *csign_pub = NULL, *pp_pub = NULL; |
2904 | 0 | const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL; |
2905 | 0 | const char *signed_connector; |
2906 | |
|
2907 | 0 | os_memset(&info, 0, sizeof(info)); |
2908 | |
|
2909 | 0 | if (dpp_akm_psk(conf->akm) || dpp_akm_sae(conf->akm)) { |
2910 | 0 | wpa_printf(MSG_DEBUG, |
2911 | 0 | "DPP: Legacy credential included in Connector credential"); |
2912 | 0 | if (dpp_parse_cred_legacy(conf, cred) < 0) |
2913 | 0 | return -1; |
2914 | 0 | } |
2915 | | |
2916 | 0 | wpa_printf(MSG_DEBUG, "DPP: Connector credential"); |
2917 | |
|
2918 | 0 | csign = json_get_member(cred, "csign"); |
2919 | 0 | if (!csign || csign->type != JSON_OBJECT) { |
2920 | 0 | wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON"); |
2921 | 0 | goto fail; |
2922 | 0 | } |
2923 | | |
2924 | 0 | csign_pub = dpp_parse_jwk(csign, &key_curve); |
2925 | 0 | if (!csign_pub) { |
2926 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK"); |
2927 | 0 | goto fail; |
2928 | 0 | } |
2929 | 0 | dpp_debug_print_key("DPP: Received C-sign-key", csign_pub); |
2930 | |
|
2931 | 0 | ppkey = json_get_member(cred, "ppKey"); |
2932 | 0 | if (ppkey && ppkey->type == JSON_OBJECT) { |
2933 | 0 | pp_pub = dpp_parse_jwk(ppkey, &pp_curve); |
2934 | 0 | if (!pp_pub) { |
2935 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK"); |
2936 | 0 | goto fail; |
2937 | 0 | } |
2938 | 0 | dpp_debug_print_key("DPP: Received ppKey", pp_pub); |
2939 | 0 | if (key_curve != pp_curve) { |
2940 | 0 | wpa_printf(MSG_DEBUG, |
2941 | 0 | "DPP: C-sign-key and ppKey do not use the same curve"); |
2942 | 0 | goto fail; |
2943 | 0 | } |
2944 | 0 | } |
2945 | | |
2946 | 0 | token = json_get_member(cred, "signedConnector"); |
2947 | 0 | if (!token || token->type != JSON_STRING) { |
2948 | 0 | wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found"); |
2949 | 0 | goto fail; |
2950 | 0 | } |
2951 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector", |
2952 | 0 | token->string, os_strlen(token->string)); |
2953 | 0 | signed_connector = token->string; |
2954 | |
|
2955 | 0 | if (os_strchr(signed_connector, '"') || |
2956 | 0 | os_strchr(signed_connector, '\n')) { |
2957 | 0 | wpa_printf(MSG_DEBUG, |
2958 | 0 | "DPP: Unexpected character in signedConnector"); |
2959 | 0 | goto fail; |
2960 | 0 | } |
2961 | | |
2962 | 0 | if (dpp_process_signed_connector(&info, csign_pub, |
2963 | 0 | signed_connector) != DPP_STATUS_OK) |
2964 | 0 | goto fail; |
2965 | | |
2966 | 0 | if (dpp_parse_connector(auth, conf, |
2967 | 0 | info.payload, info.payload_len) < 0) { |
2968 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector"); |
2969 | 0 | goto fail; |
2970 | 0 | } |
2971 | | |
2972 | 0 | os_free(conf->connector); |
2973 | 0 | conf->connector = os_strdup(signed_connector); |
2974 | |
|
2975 | 0 | dpp_copy_csign(conf, csign_pub); |
2976 | 0 | if (pp_pub) |
2977 | 0 | dpp_copy_ppkey(conf, pp_pub); |
2978 | 0 | if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2) |
2979 | 0 | dpp_copy_netaccesskey(auth, conf); |
2980 | |
|
2981 | 0 | ret = 0; |
2982 | 0 | fail: |
2983 | 0 | crypto_ec_key_deinit(csign_pub); |
2984 | 0 | crypto_ec_key_deinit(pp_pub); |
2985 | 0 | os_free(info.payload); |
2986 | 0 | return ret; |
2987 | 0 | } |
2988 | | |
2989 | | |
2990 | | #ifdef CONFIG_DPP2 |
2991 | | static int dpp_parse_cred_dot1x(struct dpp_authentication *auth, |
2992 | | struct dpp_config_obj *conf, |
2993 | | struct json_token *cred) |
2994 | 0 | { |
2995 | 0 | struct json_token *ent, *name; |
2996 | |
|
2997 | 0 | ent = json_get_member(cred, "entCreds"); |
2998 | 0 | if (!ent || ent->type != JSON_OBJECT) { |
2999 | 0 | dpp_auth_fail(auth, "No entCreds in JSON"); |
3000 | 0 | return -1; |
3001 | 0 | } |
3002 | | |
3003 | 0 | conf->certbag = json_get_member_base64(ent, "certBag"); |
3004 | 0 | if (!conf->certbag) { |
3005 | 0 | dpp_auth_fail(auth, "No certBag in JSON"); |
3006 | 0 | return -1; |
3007 | 0 | } |
3008 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag); |
3009 | 0 | conf->certs = crypto_pkcs7_get_certificates(conf->certbag); |
3010 | 0 | if (!conf->certs) { |
3011 | 0 | dpp_auth_fail(auth, "No certificates in certBag"); |
3012 | 0 | return -1; |
3013 | 0 | } |
3014 | | |
3015 | 0 | conf->cacert = json_get_member_base64(ent, "caCert"); |
3016 | 0 | if (conf->cacert) |
3017 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert", |
3018 | 0 | conf->cacert); |
3019 | |
|
3020 | 0 | name = json_get_member(ent, "trustedEapServerName"); |
3021 | 0 | if (name && |
3022 | 0 | (name->type != JSON_STRING || |
3023 | 0 | has_ctrl_char((const u8 *) name->string, |
3024 | 0 | os_strlen(name->string)))) { |
3025 | 0 | dpp_auth_fail(auth, |
3026 | 0 | "Invalid trustedEapServerName type in JSON"); |
3027 | 0 | return -1; |
3028 | 0 | } |
3029 | 0 | if (name && name->string) { |
3030 | 0 | wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s", |
3031 | 0 | name->string); |
3032 | 0 | conf->server_name = os_strdup(name->string); |
3033 | 0 | if (!conf->server_name) |
3034 | 0 | return -1; |
3035 | 0 | } |
3036 | | |
3037 | 0 | return 0; |
3038 | 0 | } |
3039 | | #endif /* CONFIG_DPP2 */ |
3040 | | |
3041 | | |
3042 | | const char * dpp_akm_str(enum dpp_akm akm) |
3043 | 0 | { |
3044 | 0 | switch (akm) { |
3045 | 0 | case DPP_AKM_DPP: |
3046 | 0 | return "dpp"; |
3047 | 0 | case DPP_AKM_PSK: |
3048 | 0 | return "psk"; |
3049 | 0 | case DPP_AKM_SAE: |
3050 | 0 | return "sae"; |
3051 | 0 | case DPP_AKM_PSK_SAE: |
3052 | 0 | return "psk+sae"; |
3053 | 0 | case DPP_AKM_SAE_DPP: |
3054 | 0 | return "dpp+sae"; |
3055 | 0 | case DPP_AKM_PSK_SAE_DPP: |
3056 | 0 | return "dpp+psk+sae"; |
3057 | 0 | case DPP_AKM_DOT1X: |
3058 | 0 | return "dot1x"; |
3059 | 0 | default: |
3060 | 0 | return "??"; |
3061 | 0 | } |
3062 | 0 | } |
3063 | | |
3064 | | |
3065 | | const char * dpp_akm_selector_str(enum dpp_akm akm) |
3066 | 0 | { |
3067 | 0 | switch (akm) { |
3068 | 0 | case DPP_AKM_DPP: |
3069 | 0 | return "506F9A02"; |
3070 | 0 | case DPP_AKM_PSK: |
3071 | 0 | return "000FAC02+000FAC06"; |
3072 | 0 | case DPP_AKM_SAE: |
3073 | 0 | return "000FAC08"; |
3074 | 0 | case DPP_AKM_PSK_SAE: |
3075 | 0 | return "000FAC02+000FAC06+000FAC08"; |
3076 | 0 | case DPP_AKM_SAE_DPP: |
3077 | 0 | return "506F9A02+000FAC08"; |
3078 | 0 | case DPP_AKM_PSK_SAE_DPP: |
3079 | 0 | return "506F9A02+000FAC08+000FAC02+000FAC06"; |
3080 | 0 | case DPP_AKM_DOT1X: |
3081 | 0 | return "000FAC01+000FAC05"; |
3082 | 0 | default: |
3083 | 0 | return "??"; |
3084 | 0 | } |
3085 | 0 | } |
3086 | | |
3087 | | |
3088 | | static enum dpp_akm dpp_akm_from_str(const char *akm) |
3089 | 0 | { |
3090 | 0 | const char *pos; |
3091 | 0 | int dpp = 0, psk = 0, sae = 0, dot1x = 0; |
3092 | |
|
3093 | 0 | if (os_strcmp(akm, "psk") == 0) |
3094 | 0 | return DPP_AKM_PSK; |
3095 | 0 | if (os_strcmp(akm, "sae") == 0) |
3096 | 0 | return DPP_AKM_SAE; |
3097 | 0 | if (os_strcmp(akm, "psk+sae") == 0) |
3098 | 0 | return DPP_AKM_PSK_SAE; |
3099 | 0 | if (os_strcmp(akm, "dpp") == 0) |
3100 | 0 | return DPP_AKM_DPP; |
3101 | 0 | if (os_strcmp(akm, "dpp+sae") == 0) |
3102 | 0 | return DPP_AKM_SAE_DPP; |
3103 | 0 | if (os_strcmp(akm, "dpp+psk+sae") == 0) |
3104 | 0 | return DPP_AKM_PSK_SAE_DPP; |
3105 | 0 | if (os_strcmp(akm, "dot1x") == 0) |
3106 | 0 | return DPP_AKM_DOT1X; |
3107 | | |
3108 | 0 | pos = akm; |
3109 | 0 | while (*pos) { |
3110 | 0 | if (os_strlen(pos) < 8) |
3111 | 0 | break; |
3112 | 0 | if (os_strncasecmp(pos, "506F9A02", 8) == 0) |
3113 | 0 | dpp = 1; |
3114 | 0 | else if (os_strncasecmp(pos, "000FAC02", 8) == 0) |
3115 | 0 | psk = 1; |
3116 | 0 | else if (os_strncasecmp(pos, "000FAC06", 8) == 0) |
3117 | 0 | psk = 1; |
3118 | 0 | else if (os_strncasecmp(pos, "000FAC08", 8) == 0) |
3119 | 0 | sae = 1; |
3120 | 0 | else if (os_strncasecmp(pos, "000FAC01", 8) == 0) |
3121 | 0 | dot1x = 1; |
3122 | 0 | else if (os_strncasecmp(pos, "000FAC05", 8) == 0) |
3123 | 0 | dot1x = 1; |
3124 | 0 | pos += 8; |
3125 | 0 | if (*pos != '+') |
3126 | 0 | break; |
3127 | 0 | pos++; |
3128 | 0 | } |
3129 | |
|
3130 | 0 | if (dpp && psk && sae) |
3131 | 0 | return DPP_AKM_PSK_SAE_DPP; |
3132 | 0 | if (dpp && sae) |
3133 | 0 | return DPP_AKM_SAE_DPP; |
3134 | 0 | if (dpp) |
3135 | 0 | return DPP_AKM_DPP; |
3136 | 0 | if (psk && sae) |
3137 | 0 | return DPP_AKM_PSK_SAE; |
3138 | 0 | if (sae) |
3139 | 0 | return DPP_AKM_SAE; |
3140 | 0 | if (psk) |
3141 | 0 | return DPP_AKM_PSK; |
3142 | 0 | if (dot1x) |
3143 | 0 | return DPP_AKM_DOT1X; |
3144 | | |
3145 | 0 | return DPP_AKM_UNKNOWN; |
3146 | 0 | } |
3147 | | |
3148 | | |
3149 | | static int dpp_parse_conf_obj(struct dpp_authentication *auth, |
3150 | | const u8 *conf_obj, u16 conf_obj_len) |
3151 | 0 | { |
3152 | 0 | int ret = -1; |
3153 | 0 | struct json_token *root, *token, *discovery, *cred; |
3154 | 0 | struct dpp_config_obj *conf; |
3155 | 0 | struct wpabuf *ssid64 = NULL; |
3156 | 0 | int legacy; |
3157 | |
|
3158 | 0 | root = json_parse((const char *) conf_obj, conf_obj_len); |
3159 | 0 | if (!root) |
3160 | 0 | return -1; |
3161 | 0 | if (root->type != JSON_OBJECT) { |
3162 | 0 | dpp_auth_fail(auth, "JSON root is not an object"); |
3163 | 0 | goto fail; |
3164 | 0 | } |
3165 | | |
3166 | 0 | token = json_get_member(root, "wi-fi_tech"); |
3167 | 0 | if (!token || token->type != JSON_STRING) { |
3168 | 0 | dpp_auth_fail(auth, "No wi-fi_tech string value found"); |
3169 | 0 | goto fail; |
3170 | 0 | } |
3171 | 0 | if (os_strcmp(token->string, "infra") != 0) { |
3172 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'", |
3173 | 0 | token->string); |
3174 | 0 | dpp_auth_fail(auth, "Unsupported wi-fi_tech value"); |
3175 | 0 | goto fail; |
3176 | 0 | } |
3177 | | |
3178 | 0 | discovery = json_get_member(root, "discovery"); |
3179 | 0 | if (!discovery || discovery->type != JSON_OBJECT) { |
3180 | 0 | dpp_auth_fail(auth, "No discovery object in JSON"); |
3181 | 0 | goto fail; |
3182 | 0 | } |
3183 | | |
3184 | 0 | ssid64 = json_get_member_base64url(discovery, "ssid64"); |
3185 | 0 | if (ssid64) { |
3186 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid64", |
3187 | 0 | wpabuf_head(ssid64), wpabuf_len(ssid64)); |
3188 | 0 | if (wpabuf_len(ssid64) > SSID_MAX_LEN) { |
3189 | 0 | dpp_auth_fail(auth, "Too long discovery::ssid64 value"); |
3190 | 0 | goto fail; |
3191 | 0 | } |
3192 | 0 | } else { |
3193 | 0 | token = json_get_member(discovery, "ssid"); |
3194 | 0 | if (!token || token->type != JSON_STRING) { |
3195 | 0 | dpp_auth_fail(auth, |
3196 | 0 | "No discovery::ssid string value found"); |
3197 | 0 | goto fail; |
3198 | 0 | } |
3199 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid", |
3200 | 0 | token->string, os_strlen(token->string)); |
3201 | 0 | if (os_strlen(token->string) > SSID_MAX_LEN) { |
3202 | 0 | dpp_auth_fail(auth, |
3203 | 0 | "Too long discovery::ssid string value"); |
3204 | 0 | goto fail; |
3205 | 0 | } |
3206 | 0 | } |
3207 | | |
3208 | 0 | if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) { |
3209 | 0 | wpa_printf(MSG_DEBUG, |
3210 | 0 | "DPP: No room for this many Config Objects - ignore this one"); |
3211 | 0 | ret = 0; |
3212 | 0 | goto fail; |
3213 | 0 | } |
3214 | 0 | conf = &auth->conf_obj[auth->num_conf_obj++]; |
3215 | |
|
3216 | 0 | if (ssid64) { |
3217 | 0 | conf->ssid_len = wpabuf_len(ssid64); |
3218 | 0 | os_memcpy(conf->ssid, wpabuf_head(ssid64), conf->ssid_len); |
3219 | 0 | } else { |
3220 | 0 | conf->ssid_len = os_strlen(token->string); |
3221 | 0 | os_memcpy(conf->ssid, token->string, conf->ssid_len); |
3222 | 0 | } |
3223 | |
|
3224 | 0 | token = json_get_member(discovery, "ssid_charset"); |
3225 | 0 | if (token && token->type == JSON_NUMBER) { |
3226 | 0 | conf->ssid_charset = token->number; |
3227 | 0 | wpa_printf(MSG_DEBUG, "DPP: ssid_charset=%d", |
3228 | 0 | conf->ssid_charset); |
3229 | 0 | } |
3230 | |
|
3231 | 0 | cred = json_get_member(root, "cred"); |
3232 | 0 | if (!cred || cred->type != JSON_OBJECT) { |
3233 | 0 | dpp_auth_fail(auth, "No cred object in JSON"); |
3234 | 0 | goto fail; |
3235 | 0 | } |
3236 | | |
3237 | 0 | token = json_get_member(cred, "akm"); |
3238 | 0 | if (!token || token->type != JSON_STRING) { |
3239 | 0 | dpp_auth_fail(auth, "No cred::akm string value found"); |
3240 | 0 | goto fail; |
3241 | 0 | } |
3242 | 0 | conf->akm = dpp_akm_from_str(token->string); |
3243 | |
|
3244 | 0 | legacy = dpp_akm_legacy(conf->akm); |
3245 | 0 | if (legacy && auth->peer_version >= 2) { |
3246 | 0 | struct json_token *csign, *s_conn; |
3247 | |
|
3248 | 0 | csign = json_get_member(cred, "csign"); |
3249 | 0 | s_conn = json_get_member(cred, "signedConnector"); |
3250 | 0 | if (csign && csign->type == JSON_OBJECT && |
3251 | 0 | s_conn && s_conn->type == JSON_STRING) |
3252 | 0 | legacy = 0; |
3253 | 0 | } |
3254 | 0 | if (legacy) { |
3255 | 0 | if (dpp_parse_cred_legacy(conf, cred) < 0) |
3256 | 0 | goto fail; |
3257 | 0 | } else if (dpp_akm_dpp(conf->akm) || |
3258 | 0 | (auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) { |
3259 | 0 | if (dpp_parse_cred_dpp(auth, conf, cred) < 0) |
3260 | 0 | goto fail; |
3261 | 0 | #ifdef CONFIG_DPP2 |
3262 | 0 | } else if (conf->akm == DPP_AKM_DOT1X) { |
3263 | 0 | if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 || |
3264 | 0 | dpp_parse_cred_dpp(auth, conf, cred) < 0) |
3265 | 0 | goto fail; |
3266 | 0 | #endif /* CONFIG_DPP2 */ |
3267 | 0 | } else { |
3268 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s", |
3269 | 0 | token->string); |
3270 | 0 | dpp_auth_fail(auth, "Unsupported akm"); |
3271 | 0 | goto fail; |
3272 | 0 | } |
3273 | | |
3274 | 0 | wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully"); |
3275 | 0 | ret = 0; |
3276 | 0 | fail: |
3277 | 0 | wpabuf_free(ssid64); |
3278 | 0 | json_free(root); |
3279 | 0 | return ret; |
3280 | 0 | } |
3281 | | |
3282 | | |
3283 | | #ifdef CONFIG_DPP2 |
3284 | | static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len) |
3285 | 0 | { |
3286 | 0 | const u8 *b64; |
3287 | 0 | u16 b64_len; |
3288 | |
|
3289 | 0 | b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len); |
3290 | 0 | if (!b64) |
3291 | 0 | return NULL; |
3292 | 0 | return base64_decode((const char *) b64, b64_len, len); |
3293 | 0 | } |
3294 | | #endif /* CONFIG_DPP2 */ |
3295 | | |
3296 | | |
3297 | | int dpp_conf_resp_rx(struct dpp_authentication *auth, |
3298 | | const struct wpabuf *resp) |
3299 | 0 | { |
3300 | 0 | const u8 *wrapped_data, *e_nonce, *status, *conf_obj; |
3301 | 0 | u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len; |
3302 | 0 | const u8 *env_data; |
3303 | 0 | u16 env_data_len; |
3304 | 0 | const u8 *addr[1]; |
3305 | 0 | size_t len[1]; |
3306 | 0 | u8 *unwrapped = NULL; |
3307 | 0 | size_t unwrapped_len = 0; |
3308 | 0 | int ret = -1; |
3309 | |
|
3310 | 0 | auth->conf_resp_status = 255; |
3311 | |
|
3312 | 0 | if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { |
3313 | 0 | dpp_auth_fail(auth, "Invalid attribute in config response"); |
3314 | 0 | return -1; |
3315 | 0 | } |
3316 | | |
3317 | 0 | wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), |
3318 | 0 | DPP_ATTR_WRAPPED_DATA, |
3319 | 0 | &wrapped_data_len); |
3320 | 0 | if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { |
3321 | 0 | dpp_auth_fail(auth, |
3322 | 0 | "Missing or invalid required Wrapped Data attribute"); |
3323 | 0 | return -1; |
3324 | 0 | } |
3325 | | |
3326 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
3327 | 0 | wrapped_data, wrapped_data_len); |
3328 | 0 | unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; |
3329 | 0 | unwrapped = os_malloc(unwrapped_len); |
3330 | 0 | if (!unwrapped) |
3331 | 0 | return -1; |
3332 | | |
3333 | 0 | addr[0] = wpabuf_head(resp); |
3334 | 0 | len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp); |
3335 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); |
3336 | |
|
3337 | 0 | if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, |
3338 | 0 | wrapped_data, wrapped_data_len, |
3339 | 0 | 1, addr, len, unwrapped) < 0) { |
3340 | 0 | dpp_auth_fail(auth, "AES-SIV decryption failed"); |
3341 | 0 | goto fail; |
3342 | 0 | } |
3343 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", |
3344 | 0 | unwrapped, unwrapped_len); |
3345 | |
|
3346 | 0 | if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { |
3347 | 0 | dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); |
3348 | 0 | goto fail; |
3349 | 0 | } |
3350 | | |
3351 | 0 | e_nonce = dpp_get_attr(unwrapped, unwrapped_len, |
3352 | 0 | DPP_ATTR_ENROLLEE_NONCE, |
3353 | 0 | &e_nonce_len); |
3354 | 0 | if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { |
3355 | 0 | dpp_auth_fail(auth, |
3356 | 0 | "Missing or invalid Enrollee Nonce attribute"); |
3357 | 0 | goto fail; |
3358 | 0 | } |
3359 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); |
3360 | 0 | if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { |
3361 | 0 | dpp_auth_fail(auth, "Enrollee Nonce mismatch"); |
3362 | 0 | goto fail; |
3363 | 0 | } |
3364 | | |
3365 | 0 | status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), |
3366 | 0 | DPP_ATTR_STATUS, &status_len); |
3367 | 0 | if (!status || status_len < 1) { |
3368 | 0 | dpp_auth_fail(auth, |
3369 | 0 | "Missing or invalid required DPP Status attribute"); |
3370 | 0 | goto fail; |
3371 | 0 | } |
3372 | 0 | auth->conf_resp_status = status[0]; |
3373 | 0 | wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); |
3374 | 0 | #ifdef CONFIG_DPP2 |
3375 | 0 | if (status[0] == DPP_STATUS_CSR_NEEDED) { |
3376 | 0 | u8 *csrattrs; |
3377 | 0 | size_t csrattrs_len; |
3378 | |
|
3379 | 0 | wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR"); |
3380 | |
|
3381 | 0 | csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len, |
3382 | 0 | &csrattrs_len); |
3383 | 0 | if (!csrattrs) { |
3384 | 0 | dpp_auth_fail(auth, |
3385 | 0 | "Missing or invalid CSR Attributes Request attribute"); |
3386 | 0 | goto fail; |
3387 | 0 | } |
3388 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len); |
3389 | 0 | os_free(auth->csrattrs); |
3390 | 0 | auth->csrattrs = csrattrs; |
3391 | 0 | auth->csrattrs_len = csrattrs_len; |
3392 | 0 | ret = -2; |
3393 | 0 | goto fail; |
3394 | 0 | } |
3395 | 0 | #endif /* CONFIG_DPP2 */ |
3396 | | #ifdef CONFIG_DPP3 |
3397 | | if (status[0] == DPP_STATUS_NEW_KEY_NEEDED) { |
3398 | | const u8 *fcgroup, *r_proto; |
3399 | | u16 fcgroup_len, r_proto_len; |
3400 | | u16 group; |
3401 | | const struct dpp_curve_params *curve; |
3402 | | struct crypto_ec_key *new_pe; |
3403 | | struct crypto_ec_key *pc; |
3404 | | |
3405 | | fcgroup = dpp_get_attr(unwrapped, unwrapped_len, |
3406 | | DPP_ATTR_FINITE_CYCLIC_GROUP, |
3407 | | &fcgroup_len); |
3408 | | if (!fcgroup || fcgroup_len != 2) { |
3409 | | dpp_auth_fail(auth, |
3410 | | "Missing or invalid required Finite Cyclic Group attribute"); |
3411 | | goto fail; |
3412 | | } |
3413 | | group = WPA_GET_LE16(fcgroup); |
3414 | | |
3415 | | wpa_printf(MSG_DEBUG, |
3416 | | "DPP: Configurator requested a new protocol key from group %u", |
3417 | | group); |
3418 | | curve = dpp_get_curve_ike_group(group); |
3419 | | if (!curve) { |
3420 | | dpp_auth_fail(auth, |
3421 | | "Unsupported group for new protocol key"); |
3422 | | goto fail; |
3423 | | } |
3424 | | |
3425 | | new_pe = dpp_gen_keypair(curve); |
3426 | | if (!new_pe) { |
3427 | | dpp_auth_fail(auth, |
3428 | | "Failed to generate a new protocol key"); |
3429 | | goto fail; |
3430 | | } |
3431 | | |
3432 | | crypto_ec_key_deinit(auth->own_protocol_key); |
3433 | | auth->own_protocol_key = new_pe; |
3434 | | auth->new_curve = curve; |
3435 | | |
3436 | | r_proto = dpp_get_attr(unwrapped, unwrapped_len, |
3437 | | DPP_ATTR_R_PROTOCOL_KEY, |
3438 | | &r_proto_len); |
3439 | | if (!r_proto) { |
3440 | | dpp_auth_fail(auth, |
3441 | | "Missing required Responder Protocol Key attribute (Pc)"); |
3442 | | goto fail; |
3443 | | } |
3444 | | wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key (new Pc)", |
3445 | | r_proto, r_proto_len); |
3446 | | |
3447 | | pc = dpp_set_pubkey_point(new_pe, r_proto, r_proto_len); |
3448 | | if (!pc) { |
3449 | | dpp_auth_fail(auth, "Invalid Responder Protocol Key (Pc)"); |
3450 | | goto fail; |
3451 | | } |
3452 | | dpp_debug_print_key("New Peer Protocol Key (Pc)", pc); |
3453 | | |
3454 | | crypto_ec_key_deinit(auth->peer_protocol_key); |
3455 | | auth->peer_protocol_key = pc; |
3456 | | |
3457 | | auth->waiting_new_key = true; |
3458 | | ret = -3; |
3459 | | goto fail; |
3460 | | } |
3461 | | #endif /* CONFIG_DPP3 */ |
3462 | 0 | if (status[0] != DPP_STATUS_OK) { |
3463 | 0 | dpp_auth_fail(auth, "Configurator rejected configuration"); |
3464 | 0 | goto fail; |
3465 | 0 | } |
3466 | | |
3467 | 0 | env_data = dpp_get_attr(unwrapped, unwrapped_len, |
3468 | 0 | DPP_ATTR_ENVELOPED_DATA, &env_data_len); |
3469 | 0 | #ifdef CONFIG_DPP2 |
3470 | 0 | if (env_data && |
3471 | 0 | dpp_conf_resp_env_data(auth, env_data, env_data_len) < 0) |
3472 | 0 | goto fail; |
3473 | 0 | #endif /* CONFIG_DPP2 */ |
3474 | | |
3475 | 0 | conf_obj = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_OBJ, |
3476 | 0 | &conf_obj_len); |
3477 | 0 | if (!conf_obj && !env_data) { |
3478 | 0 | dpp_auth_fail(auth, |
3479 | 0 | "Missing required Configuration Object attribute"); |
3480 | 0 | goto fail; |
3481 | 0 | } |
3482 | 0 | while (conf_obj) { |
3483 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", |
3484 | 0 | conf_obj, conf_obj_len); |
3485 | 0 | if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) |
3486 | 0 | goto fail; |
3487 | 0 | conf_obj = dpp_get_attr_next(conf_obj, unwrapped, unwrapped_len, |
3488 | 0 | DPP_ATTR_CONFIG_OBJ, |
3489 | 0 | &conf_obj_len); |
3490 | 0 | } |
3491 | | |
3492 | 0 | #ifdef CONFIG_DPP2 |
3493 | 0 | status = dpp_get_attr(unwrapped, unwrapped_len, |
3494 | 0 | DPP_ATTR_SEND_CONN_STATUS, &status_len); |
3495 | 0 | if (status) { |
3496 | 0 | wpa_printf(MSG_DEBUG, |
3497 | 0 | "DPP: Configurator requested connection status result"); |
3498 | 0 | auth->conn_status_requested = 1; |
3499 | 0 | } |
3500 | 0 | #endif /* CONFIG_DPP2 */ |
3501 | |
|
3502 | 0 | ret = 0; |
3503 | |
|
3504 | 0 | fail: |
3505 | 0 | os_free(unwrapped); |
3506 | 0 | return ret; |
3507 | 0 | } |
3508 | | |
3509 | | |
3510 | | #ifdef CONFIG_DPP2 |
3511 | | |
3512 | | enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, |
3513 | | const u8 *hdr, |
3514 | | const u8 *attr_start, size_t attr_len) |
3515 | 0 | { |
3516 | 0 | const u8 *wrapped_data, *status, *e_nonce; |
3517 | 0 | u16 wrapped_data_len, status_len, e_nonce_len; |
3518 | 0 | const u8 *addr[2]; |
3519 | 0 | size_t len[2]; |
3520 | 0 | u8 *unwrapped = NULL; |
3521 | 0 | size_t unwrapped_len = 0; |
3522 | 0 | enum dpp_status_error ret = 256; |
3523 | |
|
3524 | 0 | wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, |
3525 | 0 | &wrapped_data_len); |
3526 | 0 | if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { |
3527 | 0 | dpp_auth_fail(auth, |
3528 | 0 | "Missing or invalid required Wrapped Data attribute"); |
3529 | 0 | goto fail; |
3530 | 0 | } |
3531 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", |
3532 | 0 | wrapped_data, wrapped_data_len); |
3533 | |
|
3534 | 0 | attr_len = wrapped_data - 4 - attr_start; |
3535 | |
|
3536 | 0 | addr[0] = hdr; |
3537 | 0 | len[0] = DPP_HDR_LEN; |
3538 | 0 | addr[1] = attr_start; |
3539 | 0 | len[1] = attr_len; |
3540 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); |
3541 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); |
3542 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
3543 | 0 | wrapped_data, wrapped_data_len); |
3544 | 0 | unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; |
3545 | 0 | unwrapped = os_malloc(unwrapped_len); |
3546 | 0 | if (!unwrapped) |
3547 | 0 | goto fail; |
3548 | 0 | if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, |
3549 | 0 | wrapped_data, wrapped_data_len, |
3550 | 0 | 2, addr, len, unwrapped) < 0) { |
3551 | 0 | dpp_auth_fail(auth, "AES-SIV decryption failed"); |
3552 | 0 | goto fail; |
3553 | 0 | } |
3554 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", |
3555 | 0 | unwrapped, unwrapped_len); |
3556 | |
|
3557 | 0 | if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { |
3558 | 0 | dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); |
3559 | 0 | goto fail; |
3560 | 0 | } |
3561 | | |
3562 | 0 | e_nonce = dpp_get_attr(unwrapped, unwrapped_len, |
3563 | 0 | DPP_ATTR_ENROLLEE_NONCE, |
3564 | 0 | &e_nonce_len); |
3565 | 0 | if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { |
3566 | 0 | dpp_auth_fail(auth, |
3567 | 0 | "Missing or invalid Enrollee Nonce attribute"); |
3568 | 0 | goto fail; |
3569 | 0 | } |
3570 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); |
3571 | 0 | if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { |
3572 | 0 | dpp_auth_fail(auth, "Enrollee Nonce mismatch"); |
3573 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", |
3574 | 0 | auth->e_nonce, e_nonce_len); |
3575 | 0 | goto fail; |
3576 | 0 | } |
3577 | | |
3578 | 0 | status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, |
3579 | 0 | &status_len); |
3580 | 0 | if (!status || status_len < 1) { |
3581 | 0 | dpp_auth_fail(auth, |
3582 | 0 | "Missing or invalid required DPP Status attribute"); |
3583 | 0 | goto fail; |
3584 | 0 | } |
3585 | 0 | wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); |
3586 | 0 | ret = status[0]; |
3587 | |
|
3588 | 0 | fail: |
3589 | 0 | bin_clear_free(unwrapped, unwrapped_len); |
3590 | 0 | return ret; |
3591 | 0 | } |
3592 | | |
3593 | | |
3594 | | struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, |
3595 | | enum dpp_status_error status) |
3596 | 0 | { |
3597 | 0 | struct wpabuf *msg, *clear; |
3598 | 0 | size_t nonce_len, clear_len, attr_len; |
3599 | 0 | const u8 *addr[2]; |
3600 | 0 | size_t len[2]; |
3601 | 0 | u8 *wrapped; |
3602 | |
|
3603 | 0 | nonce_len = auth->curve->nonce_len; |
3604 | 0 | clear_len = 5 + 4 + nonce_len; |
3605 | 0 | attr_len = 4 + clear_len + AES_BLOCK_SIZE; |
3606 | 0 | clear = wpabuf_alloc(clear_len); |
3607 | 0 | msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); |
3608 | 0 | if (!clear || !msg) |
3609 | 0 | goto fail; |
3610 | | |
3611 | | /* DPP Status */ |
3612 | 0 | dpp_build_attr_status(clear, status); |
3613 | | |
3614 | | /* E-nonce */ |
3615 | 0 | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
3616 | 0 | wpabuf_put_le16(clear, nonce_len); |
3617 | 0 | wpabuf_put_data(clear, auth->e_nonce, nonce_len); |
3618 | | |
3619 | | /* OUI, OUI type, Crypto Suite, DPP frame type */ |
3620 | 0 | addr[0] = wpabuf_head_u8(msg) + 2; |
3621 | 0 | len[0] = 3 + 1 + 1 + 1; |
3622 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); |
3623 | | |
3624 | | /* Attributes before Wrapped Data (none) */ |
3625 | 0 | addr[1] = wpabuf_put(msg, 0); |
3626 | 0 | len[1] = 0; |
3627 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); |
3628 | | |
3629 | | /* Wrapped Data */ |
3630 | 0 | wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); |
3631 | 0 | wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
3632 | 0 | wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
3633 | |
|
3634 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); |
3635 | 0 | if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, |
3636 | 0 | wpabuf_head(clear), wpabuf_len(clear), |
3637 | 0 | 2, addr, len, wrapped) < 0) |
3638 | 0 | goto fail; |
3639 | | |
3640 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); |
3641 | 0 | wpabuf_free(clear); |
3642 | 0 | return msg; |
3643 | 0 | fail: |
3644 | 0 | wpabuf_free(clear); |
3645 | 0 | wpabuf_free(msg); |
3646 | 0 | return NULL; |
3647 | 0 | } |
3648 | | |
3649 | | |
3650 | | static int valid_channel_list(const char *val) |
3651 | 0 | { |
3652 | 0 | while (*val) { |
3653 | 0 | if (!((*val >= '0' && *val <= '9') || |
3654 | 0 | *val == '/' || *val == ',')) |
3655 | 0 | return 0; |
3656 | 0 | val++; |
3657 | 0 | } |
3658 | | |
3659 | 0 | return 1; |
3660 | 0 | } |
3661 | | |
3662 | | |
3663 | | enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth, |
3664 | | const u8 *hdr, |
3665 | | const u8 *attr_start, |
3666 | | size_t attr_len, |
3667 | | u8 *ssid, size_t *ssid_len, |
3668 | | char **channel_list) |
3669 | 0 | { |
3670 | 0 | const u8 *wrapped_data, *status, *e_nonce; |
3671 | 0 | u16 wrapped_data_len, status_len, e_nonce_len; |
3672 | 0 | const u8 *addr[2]; |
3673 | 0 | size_t len[2]; |
3674 | 0 | u8 *unwrapped = NULL; |
3675 | 0 | size_t unwrapped_len = 0; |
3676 | 0 | enum dpp_status_error ret = 256; |
3677 | 0 | struct json_token *root = NULL, *token; |
3678 | 0 | struct wpabuf *ssid64; |
3679 | |
|
3680 | 0 | *ssid_len = 0; |
3681 | 0 | *channel_list = NULL; |
3682 | |
|
3683 | 0 | wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, |
3684 | 0 | &wrapped_data_len); |
3685 | 0 | if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { |
3686 | 0 | dpp_auth_fail(auth, |
3687 | 0 | "Missing or invalid required Wrapped Data attribute"); |
3688 | 0 | goto fail; |
3689 | 0 | } |
3690 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", |
3691 | 0 | wrapped_data, wrapped_data_len); |
3692 | |
|
3693 | 0 | attr_len = wrapped_data - 4 - attr_start; |
3694 | |
|
3695 | 0 | addr[0] = hdr; |
3696 | 0 | len[0] = DPP_HDR_LEN; |
3697 | 0 | addr[1] = attr_start; |
3698 | 0 | len[1] = attr_len; |
3699 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); |
3700 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); |
3701 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", |
3702 | 0 | wrapped_data, wrapped_data_len); |
3703 | 0 | unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; |
3704 | 0 | unwrapped = os_malloc(unwrapped_len); |
3705 | 0 | if (!unwrapped) |
3706 | 0 | goto fail; |
3707 | 0 | if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, |
3708 | 0 | wrapped_data, wrapped_data_len, |
3709 | 0 | 2, addr, len, unwrapped) < 0) { |
3710 | 0 | dpp_auth_fail(auth, "AES-SIV decryption failed"); |
3711 | 0 | goto fail; |
3712 | 0 | } |
3713 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", |
3714 | 0 | unwrapped, unwrapped_len); |
3715 | |
|
3716 | 0 | if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { |
3717 | 0 | dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); |
3718 | 0 | goto fail; |
3719 | 0 | } |
3720 | | |
3721 | 0 | e_nonce = dpp_get_attr(unwrapped, unwrapped_len, |
3722 | 0 | DPP_ATTR_ENROLLEE_NONCE, |
3723 | 0 | &e_nonce_len); |
3724 | 0 | if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { |
3725 | 0 | dpp_auth_fail(auth, |
3726 | 0 | "Missing or invalid Enrollee Nonce attribute"); |
3727 | 0 | goto fail; |
3728 | 0 | } |
3729 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); |
3730 | 0 | if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { |
3731 | 0 | dpp_auth_fail(auth, "Enrollee Nonce mismatch"); |
3732 | 0 | wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", |
3733 | 0 | auth->e_nonce, e_nonce_len); |
3734 | 0 | goto fail; |
3735 | 0 | } |
3736 | | |
3737 | 0 | status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS, |
3738 | 0 | &status_len); |
3739 | 0 | if (!status) { |
3740 | 0 | dpp_auth_fail(auth, |
3741 | 0 | "Missing required DPP Connection Status attribute"); |
3742 | 0 | goto fail; |
3743 | 0 | } |
3744 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", |
3745 | 0 | status, status_len); |
3746 | |
|
3747 | 0 | root = json_parse((const char *) status, status_len); |
3748 | 0 | if (!root) { |
3749 | 0 | dpp_auth_fail(auth, "Could not parse connStatus"); |
3750 | 0 | goto fail; |
3751 | 0 | } |
3752 | | |
3753 | 0 | ssid64 = json_get_member_base64url(root, "ssid64"); |
3754 | 0 | if (ssid64 && wpabuf_len(ssid64) <= SSID_MAX_LEN) { |
3755 | 0 | *ssid_len = wpabuf_len(ssid64); |
3756 | 0 | os_memcpy(ssid, wpabuf_head(ssid64), *ssid_len); |
3757 | 0 | } |
3758 | 0 | wpabuf_free(ssid64); |
3759 | |
|
3760 | 0 | token = json_get_member(root, "channelList"); |
3761 | 0 | if (token && token->type == JSON_STRING && |
3762 | 0 | valid_channel_list(token->string)) |
3763 | 0 | *channel_list = os_strdup(token->string); |
3764 | |
|
3765 | 0 | token = json_get_member(root, "result"); |
3766 | 0 | if (!token || token->type != JSON_NUMBER) { |
3767 | 0 | dpp_auth_fail(auth, "No connStatus - result"); |
3768 | 0 | goto fail; |
3769 | 0 | } |
3770 | 0 | wpa_printf(MSG_DEBUG, "DPP: result %d", token->number); |
3771 | 0 | ret = token->number; |
3772 | |
|
3773 | 0 | fail: |
3774 | 0 | json_free(root); |
3775 | 0 | bin_clear_free(unwrapped, unwrapped_len); |
3776 | 0 | return ret; |
3777 | 0 | } |
3778 | | |
3779 | | |
3780 | | struct wpabuf * dpp_build_conn_status(enum dpp_status_error result, |
3781 | | const u8 *ssid, size_t ssid_len, |
3782 | | const char *channel_list) |
3783 | 0 | { |
3784 | 0 | struct wpabuf *json; |
3785 | |
|
3786 | 0 | json = wpabuf_alloc(1000); |
3787 | 0 | if (!json) |
3788 | 0 | return NULL; |
3789 | 0 | json_start_object(json, NULL); |
3790 | 0 | json_add_int(json, "result", result); |
3791 | 0 | if (ssid) { |
3792 | 0 | json_value_sep(json); |
3793 | 0 | if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) { |
3794 | 0 | wpabuf_free(json); |
3795 | 0 | return NULL; |
3796 | 0 | } |
3797 | 0 | } |
3798 | 0 | if (channel_list) { |
3799 | 0 | json_value_sep(json); |
3800 | 0 | json_add_string(json, "channelList", channel_list); |
3801 | 0 | } |
3802 | 0 | json_end_object(json); |
3803 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", |
3804 | 0 | wpabuf_head(json), wpabuf_len(json)); |
3805 | |
|
3806 | 0 | return json; |
3807 | 0 | } |
3808 | | |
3809 | | |
3810 | | struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth, |
3811 | | enum dpp_status_error result, |
3812 | | const u8 *ssid, size_t ssid_len, |
3813 | | const char *channel_list) |
3814 | 0 | { |
3815 | 0 | struct wpabuf *msg = NULL, *clear = NULL, *json; |
3816 | 0 | size_t nonce_len, clear_len, attr_len; |
3817 | 0 | const u8 *addr[2]; |
3818 | 0 | size_t len[2]; |
3819 | 0 | u8 *wrapped; |
3820 | |
|
3821 | 0 | json = dpp_build_conn_status(result, ssid, ssid_len, channel_list); |
3822 | 0 | if (!json) |
3823 | 0 | return NULL; |
3824 | | |
3825 | 0 | nonce_len = auth->curve->nonce_len; |
3826 | 0 | clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json); |
3827 | 0 | attr_len = 4 + clear_len + AES_BLOCK_SIZE; |
3828 | 0 | clear = wpabuf_alloc(clear_len); |
3829 | 0 | msg = dpp_alloc_msg(DPP_PA_CONNECTION_STATUS_RESULT, attr_len); |
3830 | 0 | if (!clear || !msg) |
3831 | 0 | goto fail; |
3832 | | |
3833 | | /* E-nonce */ |
3834 | 0 | wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); |
3835 | 0 | wpabuf_put_le16(clear, nonce_len); |
3836 | 0 | wpabuf_put_data(clear, auth->e_nonce, nonce_len); |
3837 | | |
3838 | | /* DPP Connection Status */ |
3839 | 0 | wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS); |
3840 | 0 | wpabuf_put_le16(clear, wpabuf_len(json)); |
3841 | 0 | wpabuf_put_buf(clear, json); |
3842 | | |
3843 | | /* OUI, OUI type, Crypto Suite, DPP frame type */ |
3844 | 0 | addr[0] = wpabuf_head_u8(msg) + 2; |
3845 | 0 | len[0] = 3 + 1 + 1 + 1; |
3846 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); |
3847 | | |
3848 | | /* Attributes before Wrapped Data (none) */ |
3849 | 0 | addr[1] = wpabuf_put(msg, 0); |
3850 | 0 | len[1] = 0; |
3851 | 0 | wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); |
3852 | | |
3853 | | /* Wrapped Data */ |
3854 | 0 | wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); |
3855 | 0 | wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
3856 | 0 | wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); |
3857 | |
|
3858 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); |
3859 | 0 | if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, |
3860 | 0 | wpabuf_head(clear), wpabuf_len(clear), |
3861 | 0 | 2, addr, len, wrapped) < 0) |
3862 | 0 | goto fail; |
3863 | | |
3864 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: Connection Status Result attributes", |
3865 | 0 | msg); |
3866 | 0 | wpabuf_free(json); |
3867 | 0 | wpabuf_free(clear); |
3868 | 0 | return msg; |
3869 | 0 | fail: |
3870 | 0 | wpabuf_free(json); |
3871 | 0 | wpabuf_free(clear); |
3872 | 0 | wpabuf_free(msg); |
3873 | 0 | return NULL; |
3874 | 0 | } |
3875 | | |
3876 | | #endif /* CONFIG_DPP2 */ |
3877 | | |
3878 | | |
3879 | | void dpp_configurator_free(struct dpp_configurator *conf) |
3880 | 0 | { |
3881 | 0 | if (!conf) |
3882 | 0 | return; |
3883 | 0 | crypto_ec_key_deinit(conf->csign); |
3884 | 0 | os_free(conf->kid); |
3885 | 0 | os_free(conf->connector); |
3886 | 0 | crypto_ec_key_deinit(conf->connector_key); |
3887 | 0 | crypto_ec_key_deinit(conf->pp_key); |
3888 | 0 | os_free(conf); |
3889 | 0 | } |
3890 | | |
3891 | | |
3892 | | int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, |
3893 | | size_t buflen) |
3894 | 0 | { |
3895 | 0 | struct wpabuf *key; |
3896 | 0 | int ret = -1; |
3897 | |
|
3898 | 0 | if (!conf->csign) |
3899 | 0 | return -1; |
3900 | | |
3901 | 0 | key = crypto_ec_key_get_ecprivate_key(conf->csign, true); |
3902 | 0 | if (!key) |
3903 | 0 | return -1; |
3904 | | |
3905 | 0 | ret = wpa_snprintf_hex(buf, buflen, wpabuf_head(key), wpabuf_len(key)); |
3906 | |
|
3907 | 0 | wpabuf_clear_free(key); |
3908 | 0 | return ret; |
3909 | 0 | } |
3910 | | |
3911 | | |
3912 | | static int dpp_configurator_gen_kid(struct dpp_configurator *conf) |
3913 | 0 | { |
3914 | 0 | struct wpabuf *csign_pub = NULL; |
3915 | 0 | const u8 *addr[1]; |
3916 | 0 | size_t len[1]; |
3917 | 0 | int res; |
3918 | |
|
3919 | 0 | csign_pub = crypto_ec_key_get_pubkey_point(conf->csign, 1); |
3920 | 0 | if (!csign_pub) { |
3921 | 0 | wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key"); |
3922 | 0 | return -1; |
3923 | 0 | } |
3924 | | |
3925 | | /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */ |
3926 | 0 | addr[0] = wpabuf_head(csign_pub); |
3927 | 0 | len[0] = wpabuf_len(csign_pub); |
3928 | 0 | res = sha256_vector(1, addr, len, conf->kid_hash); |
3929 | 0 | wpabuf_free(csign_pub); |
3930 | 0 | if (res < 0) { |
3931 | 0 | wpa_printf(MSG_DEBUG, |
3932 | 0 | "DPP: Failed to derive kid for C-sign-key"); |
3933 | 0 | return -1; |
3934 | 0 | } |
3935 | | |
3936 | 0 | conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash), |
3937 | 0 | NULL); |
3938 | 0 | return conf->kid ? 0 : -1; |
3939 | 0 | } |
3940 | | |
3941 | | |
3942 | | static struct dpp_configurator * |
3943 | | dpp_keygen_configurator(const char *curve, const u8 *privkey, |
3944 | | size_t privkey_len, const u8 *pp_key, size_t pp_key_len) |
3945 | 0 | { |
3946 | 0 | struct dpp_configurator *conf; |
3947 | |
|
3948 | 0 | conf = os_zalloc(sizeof(*conf)); |
3949 | 0 | if (!conf) |
3950 | 0 | return NULL; |
3951 | | |
3952 | 0 | conf->curve = dpp_get_curve_name(curve); |
3953 | 0 | if (!conf->curve) { |
3954 | 0 | wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve); |
3955 | 0 | os_free(conf); |
3956 | 0 | return NULL; |
3957 | 0 | } |
3958 | | |
3959 | 0 | if (privkey) |
3960 | 0 | conf->csign = dpp_set_keypair(&conf->curve, privkey, |
3961 | 0 | privkey_len); |
3962 | 0 | else |
3963 | 0 | conf->csign = dpp_gen_keypair(conf->curve); |
3964 | 0 | if (pp_key) |
3965 | 0 | conf->pp_key = dpp_set_keypair(&conf->curve, pp_key, |
3966 | 0 | pp_key_len); |
3967 | 0 | else |
3968 | 0 | conf->pp_key = dpp_gen_keypair(conf->curve); |
3969 | 0 | if (!conf->csign || !conf->pp_key) |
3970 | 0 | goto fail; |
3971 | 0 | conf->own = 1; |
3972 | |
|
3973 | 0 | if (dpp_configurator_gen_kid(conf) < 0) |
3974 | 0 | goto fail; |
3975 | 0 | return conf; |
3976 | 0 | fail: |
3977 | 0 | dpp_configurator_free(conf); |
3978 | 0 | return NULL; |
3979 | 0 | } |
3980 | | |
3981 | | |
3982 | | int dpp_configurator_own_config(struct dpp_authentication *auth, |
3983 | | const char *curve, int ap) |
3984 | 0 | { |
3985 | 0 | struct wpabuf *conf_obj; |
3986 | 0 | int ret = -1; |
3987 | |
|
3988 | 0 | if (!auth->conf) { |
3989 | 0 | wpa_printf(MSG_DEBUG, "DPP: No configurator specified"); |
3990 | 0 | return -1; |
3991 | 0 | } |
3992 | | |
3993 | 0 | auth->curve = dpp_get_curve_name(curve); |
3994 | 0 | if (!auth->curve) { |
3995 | 0 | wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve); |
3996 | 0 | return -1; |
3997 | 0 | } |
3998 | | |
3999 | 0 | wpa_printf(MSG_DEBUG, |
4000 | 0 | "DPP: Building own configuration/connector with curve %s", |
4001 | 0 | auth->curve->name); |
4002 | |
|
4003 | 0 | auth->own_protocol_key = dpp_gen_keypair(auth->curve); |
4004 | 0 | if (!auth->own_protocol_key) |
4005 | 0 | return -1; |
4006 | 0 | dpp_copy_netaccesskey(auth, &auth->conf_obj[0]); |
4007 | 0 | auth->peer_protocol_key = auth->own_protocol_key; |
4008 | 0 | dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign); |
4009 | |
|
4010 | 0 | conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL); |
4011 | 0 | if (!conf_obj) { |
4012 | 0 | wpabuf_free(auth->conf_obj[0].c_sign_key); |
4013 | 0 | auth->conf_obj[0].c_sign_key = NULL; |
4014 | 0 | goto fail; |
4015 | 0 | } |
4016 | 0 | ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj), |
4017 | 0 | wpabuf_len(conf_obj)); |
4018 | 0 | fail: |
4019 | 0 | wpabuf_free(conf_obj); |
4020 | 0 | auth->peer_protocol_key = NULL; |
4021 | 0 | return ret; |
4022 | 0 | } |
4023 | | |
4024 | | |
4025 | | static int dpp_compatible_netrole(const char *role1, const char *role2) |
4026 | 0 | { |
4027 | 0 | return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) || |
4028 | 0 | (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0); |
4029 | 0 | } |
4030 | | |
4031 | | |
4032 | | static int dpp_connector_compatible_group(struct json_token *root, |
4033 | | const char *group_id, |
4034 | | const char *net_role, |
4035 | | bool reconfig) |
4036 | 0 | { |
4037 | 0 | struct json_token *groups, *token; |
4038 | |
|
4039 | 0 | groups = json_get_member(root, "groups"); |
4040 | 0 | if (!groups || groups->type != JSON_ARRAY) |
4041 | 0 | return 0; |
4042 | | |
4043 | 0 | for (token = groups->child; token; token = token->sibling) { |
4044 | 0 | struct json_token *id, *role; |
4045 | |
|
4046 | 0 | id = json_get_member(token, "groupId"); |
4047 | 0 | if (!id || id->type != JSON_STRING) |
4048 | 0 | continue; |
4049 | | |
4050 | 0 | role = json_get_member(token, "netRole"); |
4051 | 0 | if (!role || role->type != JSON_STRING) |
4052 | 0 | continue; |
4053 | | |
4054 | 0 | if (os_strcmp(id->string, "*") != 0 && |
4055 | 0 | os_strcmp(group_id, "*") != 0 && |
4056 | 0 | os_strcmp(id->string, group_id) != 0) |
4057 | 0 | continue; |
4058 | | |
4059 | 0 | if (reconfig && os_strcmp(net_role, "configurator") == 0) |
4060 | 0 | return 1; |
4061 | 0 | if (!reconfig && dpp_compatible_netrole(role->string, net_role)) |
4062 | 0 | return 1; |
4063 | 0 | } |
4064 | | |
4065 | 0 | return 0; |
4066 | 0 | } |
4067 | | |
4068 | | |
4069 | | int dpp_connector_match_groups(struct json_token *own_root, |
4070 | | struct json_token *peer_root, bool reconfig) |
4071 | 0 | { |
4072 | 0 | struct json_token *groups, *token; |
4073 | |
|
4074 | 0 | groups = json_get_member(peer_root, "groups"); |
4075 | 0 | if (!groups || groups->type != JSON_ARRAY) { |
4076 | 0 | wpa_printf(MSG_DEBUG, "DPP: No peer groups array found"); |
4077 | 0 | return 0; |
4078 | 0 | } |
4079 | | |
4080 | 0 | for (token = groups->child; token; token = token->sibling) { |
4081 | 0 | struct json_token *id, *role; |
4082 | |
|
4083 | 0 | id = json_get_member(token, "groupId"); |
4084 | 0 | if (!id || id->type != JSON_STRING) { |
4085 | 0 | wpa_printf(MSG_DEBUG, |
4086 | 0 | "DPP: Missing peer groupId string"); |
4087 | 0 | continue; |
4088 | 0 | } |
4089 | | |
4090 | 0 | role = json_get_member(token, "netRole"); |
4091 | 0 | if (!role || role->type != JSON_STRING) { |
4092 | 0 | wpa_printf(MSG_DEBUG, |
4093 | 0 | "DPP: Missing peer groups::netRole string"); |
4094 | 0 | continue; |
4095 | 0 | } |
4096 | 0 | wpa_printf(MSG_DEBUG, |
4097 | 0 | "DPP: peer connector group: groupId='%s' netRole='%s'", |
4098 | 0 | id->string, role->string); |
4099 | 0 | if (dpp_connector_compatible_group(own_root, id->string, |
4100 | 0 | role->string, reconfig)) { |
4101 | 0 | wpa_printf(MSG_DEBUG, |
4102 | 0 | "DPP: Compatible group/netRole in own connector"); |
4103 | 0 | return 1; |
4104 | 0 | } |
4105 | 0 | } |
4106 | | |
4107 | 0 | return 0; |
4108 | 0 | } |
4109 | | |
4110 | | |
4111 | | struct json_token * dpp_parse_own_connector(const char *own_connector) |
4112 | 0 | { |
4113 | 0 | unsigned char *own_conn; |
4114 | 0 | size_t own_conn_len; |
4115 | 0 | const char *pos, *end; |
4116 | 0 | struct json_token *own_root; |
4117 | |
|
4118 | 0 | pos = os_strchr(own_connector, '.'); |
4119 | 0 | if (!pos) { |
4120 | 0 | wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)"); |
4121 | 0 | return NULL; |
4122 | 0 | } |
4123 | 0 | pos++; |
4124 | 0 | end = os_strchr(pos, '.'); |
4125 | 0 | if (!end) { |
4126 | 0 | wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)"); |
4127 | 0 | return NULL; |
4128 | 0 | } |
4129 | 0 | own_conn = base64_url_decode(pos, end - pos, &own_conn_len); |
4130 | 0 | if (!own_conn) { |
4131 | 0 | wpa_printf(MSG_DEBUG, |
4132 | 0 | "DPP: Failed to base64url decode own signedConnector JWS Payload"); |
4133 | 0 | return NULL; |
4134 | 0 | } |
4135 | | |
4136 | 0 | own_root = json_parse((const char *) own_conn, own_conn_len); |
4137 | 0 | os_free(own_conn); |
4138 | 0 | if (!own_root) |
4139 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector"); |
4140 | |
|
4141 | 0 | return own_root; |
4142 | 0 | } |
4143 | | |
4144 | | |
4145 | | enum dpp_status_error |
4146 | | dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, |
4147 | | const u8 *net_access_key, size_t net_access_key_len, |
4148 | | const u8 *csign_key, size_t csign_key_len, |
4149 | | const u8 *peer_connector, size_t peer_connector_len, |
4150 | | os_time_t *expiry, u8 *peer_key_hash) |
4151 | 0 | { |
4152 | 0 | struct json_token *root = NULL, *netkey, *token; |
4153 | 0 | struct json_token *own_root = NULL; |
4154 | 0 | enum dpp_status_error ret = 255, res; |
4155 | 0 | struct crypto_ec_key *own_key = NULL; |
4156 | 0 | struct wpabuf *own_key_pub = NULL; |
4157 | 0 | const struct dpp_curve_params *curve, *own_curve; |
4158 | 0 | struct dpp_signed_connector_info info; |
4159 | 0 | size_t Nx_len; |
4160 | 0 | u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; |
4161 | |
|
4162 | 0 | os_memset(intro, 0, sizeof(*intro)); |
4163 | 0 | os_memset(&info, 0, sizeof(info)); |
4164 | 0 | if (expiry) |
4165 | 0 | *expiry = 0; |
4166 | |
|
4167 | 0 | own_key = dpp_set_keypair(&own_curve, net_access_key, |
4168 | 0 | net_access_key_len); |
4169 | 0 | if (!own_key) { |
4170 | 0 | wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); |
4171 | 0 | goto fail; |
4172 | 0 | } |
4173 | | |
4174 | 0 | own_root = dpp_parse_own_connector(own_connector); |
4175 | 0 | if (!own_root) |
4176 | 0 | goto fail; |
4177 | | |
4178 | 0 | res = dpp_check_signed_connector(&info, csign_key, csign_key_len, |
4179 | 0 | peer_connector, peer_connector_len); |
4180 | 0 | if (res != DPP_STATUS_OK) { |
4181 | 0 | ret = res; |
4182 | 0 | goto fail; |
4183 | 0 | } |
4184 | | |
4185 | 0 | root = json_parse((const char *) info.payload, info.payload_len); |
4186 | 0 | if (!root) { |
4187 | 0 | wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); |
4188 | 0 | ret = DPP_STATUS_INVALID_CONNECTOR; |
4189 | 0 | goto fail; |
4190 | 0 | } |
4191 | | |
4192 | 0 | if (!dpp_connector_match_groups(own_root, root, false)) { |
4193 | 0 | wpa_printf(MSG_DEBUG, |
4194 | 0 | "DPP: Peer connector does not include compatible group netrole with own connector"); |
4195 | 0 | ret = DPP_STATUS_NO_MATCH; |
4196 | 0 | goto fail; |
4197 | 0 | } |
4198 | | |
4199 | 0 | token = json_get_member(root, "expiry"); |
4200 | 0 | if (!token || token->type != JSON_STRING) { |
4201 | 0 | wpa_printf(MSG_DEBUG, |
4202 | 0 | "DPP: No expiry string found - connector does not expire"); |
4203 | 0 | } else { |
4204 | 0 | wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); |
4205 | 0 | if (dpp_key_expired(token->string, expiry)) { |
4206 | 0 | wpa_printf(MSG_DEBUG, |
4207 | 0 | "DPP: Connector (netAccessKey) has expired"); |
4208 | 0 | ret = DPP_STATUS_INVALID_CONNECTOR; |
4209 | 0 | goto fail; |
4210 | 0 | } |
4211 | 0 | } |
4212 | | |
4213 | | #ifdef CONFIG_DPP3 |
4214 | | token = json_get_member(root, "version"); |
4215 | | if (token && token->type == JSON_NUMBER) { |
4216 | | wpa_printf(MSG_DEBUG, "DPP: version = %d", token->number); |
4217 | | intro->peer_version = token->number; |
4218 | | } |
4219 | | #endif /* CONFIG_DPP3 */ |
4220 | | |
4221 | 0 | netkey = json_get_member(root, "netAccessKey"); |
4222 | 0 | if (!netkey || netkey->type != JSON_OBJECT) { |
4223 | 0 | wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); |
4224 | 0 | ret = DPP_STATUS_INVALID_CONNECTOR; |
4225 | 0 | goto fail; |
4226 | 0 | } |
4227 | | |
4228 | 0 | intro->peer_key = dpp_parse_jwk(netkey, &curve); |
4229 | 0 | if (!intro->peer_key) { |
4230 | 0 | ret = DPP_STATUS_INVALID_CONNECTOR; |
4231 | 0 | goto fail; |
4232 | 0 | } |
4233 | 0 | dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key); |
4234 | |
|
4235 | 0 | if (own_curve != curve) { |
4236 | 0 | wpa_printf(MSG_DEBUG, |
4237 | 0 | "DPP: Mismatching netAccessKey curves (%s != %s)", |
4238 | 0 | own_curve->name, curve->name); |
4239 | 0 | ret = DPP_STATUS_INVALID_CONNECTOR; |
4240 | 0 | goto fail; |
4241 | 0 | } |
4242 | | |
4243 | | /* ECDH: N = nk * PK */ |
4244 | 0 | if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0) |
4245 | 0 | goto fail; |
4246 | | |
4247 | 0 | wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", |
4248 | 0 | Nx, Nx_len); |
4249 | | |
4250 | | /* PMK = HKDF(<>, "DPP PMK", N.x) */ |
4251 | 0 | if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) { |
4252 | 0 | wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK"); |
4253 | 0 | goto fail; |
4254 | 0 | } |
4255 | 0 | intro->pmk_len = curve->hash_len; |
4256 | | |
4257 | | /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ |
4258 | 0 | if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) < |
4259 | 0 | 0) { |
4260 | 0 | wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); |
4261 | 0 | goto fail; |
4262 | 0 | } |
4263 | | |
4264 | | #ifdef CONFIG_DPP3 |
4265 | | if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id, |
4266 | | &intro->aead_id) < 0) { |
4267 | | wpa_printf(MSG_ERROR, "DPP: Unsupported group %d", |
4268 | | curve->ike_group); |
4269 | | goto fail; |
4270 | | } |
4271 | | #endif /* CONFIG_DPP3 */ |
4272 | | |
4273 | 0 | if (peer_key_hash) |
4274 | 0 | dpp_get_pubkey_hash(intro->peer_key, peer_key_hash); |
4275 | |
|
4276 | 0 | ret = DPP_STATUS_OK; |
4277 | 0 | fail: |
4278 | 0 | if (ret != DPP_STATUS_OK) |
4279 | 0 | dpp_peer_intro_deinit(intro); |
4280 | 0 | os_memset(Nx, 0, sizeof(Nx)); |
4281 | 0 | os_free(info.payload); |
4282 | 0 | crypto_ec_key_deinit(own_key); |
4283 | 0 | wpabuf_free(own_key_pub); |
4284 | 0 | json_free(root); |
4285 | 0 | json_free(own_root); |
4286 | 0 | return ret; |
4287 | 0 | } |
4288 | | |
4289 | | |
4290 | | void dpp_peer_intro_deinit(struct dpp_introduction *intro) |
4291 | 0 | { |
4292 | 0 | if (!intro) |
4293 | 0 | return; |
4294 | | |
4295 | 0 | crypto_ec_key_deinit(intro->peer_key); |
4296 | 0 | os_memset(intro, 0, sizeof(*intro)); |
4297 | 0 | } |
4298 | | |
4299 | | |
4300 | | #ifdef CONFIG_DPP3 |
4301 | | int dpp_get_connector_version(const char *connector) |
4302 | | { |
4303 | | struct json_token *root, *token; |
4304 | | int ver = -1; |
4305 | | |
4306 | | root = dpp_parse_own_connector(connector); |
4307 | | if (!root) |
4308 | | return -1; |
4309 | | |
4310 | | token = json_get_member(root, "version"); |
4311 | | if (token && token->type == JSON_NUMBER) |
4312 | | ver = token->number; |
4313 | | |
4314 | | json_free(root); |
4315 | | return ver; |
4316 | | } |
4317 | | #endif /* CONFIG_DPP3 */ |
4318 | | |
4319 | | |
4320 | | unsigned int dpp_next_id(struct dpp_global *dpp) |
4321 | 41 | { |
4322 | 41 | struct dpp_bootstrap_info *bi; |
4323 | 41 | unsigned int max_id = 0; |
4324 | | |
4325 | 41 | dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { |
4326 | 0 | if (bi->id > max_id) |
4327 | 0 | max_id = bi->id; |
4328 | 0 | } |
4329 | 41 | return max_id + 1; |
4330 | 41 | } |
4331 | | |
4332 | | |
4333 | | static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) |
4334 | 1.67k | { |
4335 | 1.67k | struct dpp_bootstrap_info *bi, *tmp; |
4336 | 1.67k | int found = 0; |
4337 | | |
4338 | 1.67k | if (!dpp) |
4339 | 0 | return -1; |
4340 | | |
4341 | 1.67k | dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, |
4342 | 1.67k | struct dpp_bootstrap_info, list) { |
4343 | 41 | if (id && bi->id != id) |
4344 | 0 | continue; |
4345 | 41 | found = 1; |
4346 | 41 | #ifdef CONFIG_DPP2 |
4347 | 41 | if (dpp->remove_bi) |
4348 | 0 | dpp->remove_bi(dpp->cb_ctx, bi); |
4349 | 41 | #endif /* CONFIG_DPP2 */ |
4350 | 41 | dl_list_del(&bi->list); |
4351 | 41 | dpp_bootstrap_info_free(bi); |
4352 | 41 | } |
4353 | | |
4354 | 1.67k | if (id == 0) |
4355 | 1.67k | return 0; /* flush succeeds regardless of entries found */ |
4356 | 0 | return found ? 0 : -1; |
4357 | 1.67k | } |
4358 | | |
4359 | | |
4360 | | struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, |
4361 | | const char *uri) |
4362 | 1.67k | { |
4363 | 1.67k | struct dpp_bootstrap_info *bi; |
4364 | | |
4365 | 1.67k | if (!dpp) |
4366 | 0 | return NULL; |
4367 | | |
4368 | 1.67k | bi = dpp_parse_uri(uri); |
4369 | 1.67k | if (!bi) |
4370 | 1.63k | return NULL; |
4371 | | |
4372 | 41 | bi->type = DPP_BOOTSTRAP_QR_CODE; |
4373 | 41 | bi->id = dpp_next_id(dpp); |
4374 | 41 | dl_list_add(&dpp->bootstrap, &bi->list); |
4375 | 41 | return bi; |
4376 | 1.67k | } |
4377 | | |
4378 | | |
4379 | | struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp, |
4380 | | const char *uri) |
4381 | 0 | { |
4382 | 0 | struct dpp_bootstrap_info *bi; |
4383 | |
|
4384 | 0 | if (!dpp) |
4385 | 0 | return NULL; |
4386 | | |
4387 | 0 | bi = dpp_parse_uri(uri); |
4388 | 0 | if (!bi) |
4389 | 0 | return NULL; |
4390 | | |
4391 | 0 | bi->type = DPP_BOOTSTRAP_NFC_URI; |
4392 | 0 | bi->id = dpp_next_id(dpp); |
4393 | 0 | dl_list_add(&dpp->bootstrap, &bi->list); |
4394 | 0 | return bi; |
4395 | 0 | } |
4396 | | |
4397 | | |
4398 | | static int dpp_parse_supported_curves_list(struct dpp_bootstrap_info *bi, |
4399 | | char *txt) |
4400 | 0 | { |
4401 | 0 | char *token, *context = NULL; |
4402 | 0 | u8 curves = 0; |
4403 | |
|
4404 | 0 | if (!txt) |
4405 | 0 | return 0; |
4406 | | |
4407 | 0 | while ((token = str_token(txt, ":", &context))) { |
4408 | 0 | if (os_strcmp(token, "P-256") == 0) { |
4409 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_P_256); |
4410 | 0 | } else if (os_strcmp(token, "P-384") == 0) { |
4411 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_P_384); |
4412 | 0 | } else if (os_strcmp(token, "P-521") == 0) { |
4413 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_P_521); |
4414 | 0 | } else if (os_strcmp(token, "BP-256") == 0) { |
4415 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_256); |
4416 | 0 | } else if (os_strcmp(token, "BP-384") == 0) { |
4417 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_384); |
4418 | 0 | } else if (os_strcmp(token, "BP-512") == 0) { |
4419 | 0 | curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_512); |
4420 | 0 | } else { |
4421 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported curve '%s'", |
4422 | 0 | token); |
4423 | 0 | return -1; |
4424 | 0 | } |
4425 | 0 | } |
4426 | 0 | bi->supported_curves = curves; |
4427 | |
|
4428 | 0 | wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x", |
4429 | 0 | bi->supported_curves); |
4430 | |
|
4431 | 0 | return 0; |
4432 | 0 | } |
4433 | | |
4434 | | |
4435 | | int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) |
4436 | 0 | { |
4437 | 0 | char *mac = NULL, *info = NULL, *curve = NULL; |
4438 | 0 | char *key = NULL, *supported_curves = NULL, *host = NULL; |
4439 | 0 | u8 *privkey = NULL; |
4440 | 0 | size_t privkey_len = 0; |
4441 | 0 | int ret = -1; |
4442 | 0 | struct dpp_bootstrap_info *bi; |
4443 | |
|
4444 | 0 | if (!dpp) |
4445 | 0 | return -1; |
4446 | | |
4447 | 0 | bi = os_zalloc(sizeof(*bi)); |
4448 | 0 | if (!bi) |
4449 | 0 | goto fail; |
4450 | | |
4451 | 0 | if (os_strstr(cmd, "type=qrcode")) |
4452 | 0 | bi->type = DPP_BOOTSTRAP_QR_CODE; |
4453 | 0 | else if (os_strstr(cmd, "type=pkex")) |
4454 | 0 | bi->type = DPP_BOOTSTRAP_PKEX; |
4455 | 0 | else if (os_strstr(cmd, "type=nfc-uri")) |
4456 | 0 | bi->type = DPP_BOOTSTRAP_NFC_URI; |
4457 | 0 | else |
4458 | 0 | goto fail; |
4459 | | |
4460 | 0 | bi->chan = get_param(cmd, " chan="); |
4461 | 0 | mac = get_param(cmd, " mac="); |
4462 | 0 | info = get_param(cmd, " info="); |
4463 | 0 | curve = get_param(cmd, " curve="); |
4464 | 0 | key = get_param(cmd, " key="); |
4465 | 0 | supported_curves = get_param(cmd, " supported_curves="); |
4466 | 0 | host = get_param(cmd, " host="); |
4467 | |
|
4468 | 0 | if (key) { |
4469 | 0 | privkey_len = os_strlen(key) / 2; |
4470 | 0 | privkey = os_malloc(privkey_len); |
4471 | 0 | if (!privkey || |
4472 | 0 | hexstr2bin(key, privkey, privkey_len) < 0) |
4473 | 0 | goto fail; |
4474 | 0 | } |
4475 | | |
4476 | 0 | if (dpp_keygen(bi, curve, privkey, privkey_len) < 0 || |
4477 | 0 | dpp_parse_uri_chan_list(bi, bi->chan) < 0 || |
4478 | 0 | dpp_parse_uri_mac(bi, mac) < 0 || |
4479 | 0 | dpp_parse_uri_info(bi, info) < 0 || |
4480 | 0 | dpp_parse_supported_curves_list(bi, supported_curves) < 0 || |
4481 | 0 | dpp_parse_uri_host(bi, host) < 0 || |
4482 | 0 | dpp_gen_uri(bi) < 0) |
4483 | 0 | goto fail; |
4484 | | |
4485 | 0 | bi->id = dpp_next_id(dpp); |
4486 | 0 | dl_list_add(&dpp->bootstrap, &bi->list); |
4487 | 0 | ret = bi->id; |
4488 | 0 | bi = NULL; |
4489 | 0 | fail: |
4490 | 0 | os_free(curve); |
4491 | 0 | os_free(mac); |
4492 | 0 | os_free(info); |
4493 | 0 | str_clear_free(key); |
4494 | 0 | os_free(supported_curves); |
4495 | 0 | os_free(host); |
4496 | 0 | bin_clear_free(privkey, privkey_len); |
4497 | 0 | dpp_bootstrap_info_free(bi); |
4498 | 0 | return ret; |
4499 | 0 | } |
4500 | | |
4501 | | |
4502 | | struct dpp_bootstrap_info * |
4503 | | dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) |
4504 | 41 | { |
4505 | 41 | struct dpp_bootstrap_info *bi; |
4506 | | |
4507 | 41 | if (!dpp) |
4508 | 0 | return NULL; |
4509 | | |
4510 | 41 | dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { |
4511 | 41 | if (bi->id == id) |
4512 | 41 | return bi; |
4513 | 41 | } |
4514 | 0 | return NULL; |
4515 | 41 | } |
4516 | | |
4517 | | |
4518 | | int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) |
4519 | 0 | { |
4520 | 0 | unsigned int id_val; |
4521 | |
|
4522 | 0 | if (os_strcmp(id, "*") == 0) { |
4523 | 0 | id_val = 0; |
4524 | 0 | } else { |
4525 | 0 | id_val = atoi(id); |
4526 | 0 | if (id_val == 0) |
4527 | 0 | return -1; |
4528 | 0 | } |
4529 | | |
4530 | 0 | return dpp_bootstrap_del(dpp, id_val); |
4531 | 0 | } |
4532 | | |
4533 | | |
4534 | | const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) |
4535 | 0 | { |
4536 | 0 | struct dpp_bootstrap_info *bi; |
4537 | |
|
4538 | 0 | bi = dpp_bootstrap_get_id(dpp, id); |
4539 | 0 | if (!bi) |
4540 | 0 | return NULL; |
4541 | 0 | return bi->uri; |
4542 | 0 | } |
4543 | | |
4544 | | |
4545 | | int dpp_bootstrap_info(struct dpp_global *dpp, int id, |
4546 | | char *reply, int reply_size) |
4547 | 41 | { |
4548 | 41 | struct dpp_bootstrap_info *bi; |
4549 | 41 | char pkhash[2 * SHA256_MAC_LEN + 1]; |
4550 | 41 | char supp_curves[100]; |
4551 | 41 | char host[100]; |
4552 | 41 | int ret; |
4553 | | |
4554 | 41 | bi = dpp_bootstrap_get_id(dpp, id); |
4555 | 41 | if (!bi) |
4556 | 0 | return -1; |
4557 | 41 | wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash, |
4558 | 41 | SHA256_MAC_LEN); |
4559 | | |
4560 | 41 | supp_curves[0] = '\0'; |
4561 | 41 | if (bi->supported_curves) { |
4562 | 23 | size_t i; |
4563 | 23 | char *pos = supp_curves; |
4564 | 23 | char *end = &supp_curves[sizeof(supp_curves)]; |
4565 | 23 | const char *curve[6] = { "P-256", "P-384", "P-521", |
4566 | 23 | "BP-256", "BP-384", "BP-512" }; |
4567 | | |
4568 | 23 | ret = os_snprintf(pos, end - pos, "supp_curves="); |
4569 | 23 | if (os_snprintf_error(end - pos, ret)) |
4570 | 0 | return -1; |
4571 | 23 | pos += ret; |
4572 | | |
4573 | 161 | for (i = 0; i < ARRAY_SIZE(curve); i++) { |
4574 | 138 | if (!(bi->supported_curves & BIT(i))) |
4575 | 37 | continue; |
4576 | 101 | ret = os_snprintf(pos, end - pos, "%s:", curve[i]); |
4577 | 101 | if (os_snprintf_error(end - pos, ret)) |
4578 | 0 | return -1; |
4579 | 101 | pos += ret; |
4580 | 101 | } |
4581 | | |
4582 | 23 | if (pos[-1] == ':') |
4583 | 22 | pos[-1] = '\n'; |
4584 | 1 | else |
4585 | 1 | supp_curves[0] = '\0'; |
4586 | 23 | } |
4587 | | |
4588 | 41 | host[0] = '\0'; |
4589 | 41 | if (bi->host) { |
4590 | 24 | char buf[100]; |
4591 | | |
4592 | 24 | ret = os_snprintf(host, sizeof(host), "host=%s %u\n", |
4593 | 24 | hostapd_ip_txt(bi->host, buf, sizeof(buf)), |
4594 | 24 | bi->port); |
4595 | 24 | if (os_snprintf_error(sizeof(host), ret)) |
4596 | 0 | return -1; |
4597 | 24 | } |
4598 | | |
4599 | 41 | return os_snprintf(reply, reply_size, "type=%s\n" |
4600 | 41 | "mac_addr=" MACSTR "\n" |
4601 | 41 | "info=%s\n" |
4602 | 41 | "num_freq=%u\n" |
4603 | 41 | "use_freq=%u\n" |
4604 | 41 | "curve=%s\n" |
4605 | 41 | "pkhash=%s\n" |
4606 | 41 | "version=%d\n%s%s", |
4607 | 41 | dpp_bootstrap_type_txt(bi->type), |
4608 | 41 | MAC2STR(bi->mac_addr), |
4609 | 41 | bi->info ? bi->info : "", |
4610 | 41 | bi->num_freq, |
4611 | 41 | bi->num_freq == 1 ? bi->freq[0] : 0, |
4612 | 41 | bi->curve->name, |
4613 | 41 | pkhash, |
4614 | 41 | bi->version, |
4615 | 41 | supp_curves, |
4616 | 41 | host); |
4617 | 41 | } |
4618 | | |
4619 | | |
4620 | | int dpp_bootstrap_set(struct dpp_global *dpp, int id, const char *params) |
4621 | 0 | { |
4622 | 0 | struct dpp_bootstrap_info *bi; |
4623 | |
|
4624 | 0 | bi = dpp_bootstrap_get_id(dpp, id); |
4625 | 0 | if (!bi) |
4626 | 0 | return -1; |
4627 | | |
4628 | 0 | str_clear_free(bi->configurator_params); |
4629 | |
|
4630 | 0 | if (params) { |
4631 | 0 | bi->configurator_params = os_strdup(params); |
4632 | 0 | return bi->configurator_params ? 0 : -1; |
4633 | 0 | } |
4634 | | |
4635 | 0 | bi->configurator_params = NULL; |
4636 | 0 | return 0; |
4637 | 0 | } |
4638 | | |
4639 | | |
4640 | | void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, |
4641 | | const u8 *r_bootstrap, |
4642 | | struct dpp_bootstrap_info **own_bi, |
4643 | | struct dpp_bootstrap_info **peer_bi) |
4644 | 0 | { |
4645 | 0 | struct dpp_bootstrap_info *bi; |
4646 | |
|
4647 | 0 | *own_bi = NULL; |
4648 | 0 | *peer_bi = NULL; |
4649 | 0 | if (!dpp) |
4650 | 0 | return; |
4651 | | |
4652 | 0 | dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { |
4653 | 0 | if (!*own_bi && bi->own && |
4654 | 0 | os_memcmp(bi->pubkey_hash, r_bootstrap, |
4655 | 0 | SHA256_MAC_LEN) == 0) { |
4656 | 0 | wpa_printf(MSG_DEBUG, |
4657 | 0 | "DPP: Found matching own bootstrapping information"); |
4658 | 0 | *own_bi = bi; |
4659 | 0 | } |
4660 | |
|
4661 | 0 | if (!*peer_bi && !bi->own && |
4662 | 0 | os_memcmp(bi->pubkey_hash, i_bootstrap, |
4663 | 0 | SHA256_MAC_LEN) == 0) { |
4664 | 0 | wpa_printf(MSG_DEBUG, |
4665 | 0 | "DPP: Found matching peer bootstrapping information"); |
4666 | 0 | *peer_bi = bi; |
4667 | 0 | } |
4668 | |
|
4669 | 0 | if (*own_bi && *peer_bi) |
4670 | 0 | break; |
4671 | 0 | } |
4672 | 0 | } |
4673 | | |
4674 | | |
4675 | | #ifdef CONFIG_DPP2 |
4676 | | struct dpp_bootstrap_info * dpp_bootstrap_find_chirp(struct dpp_global *dpp, |
4677 | | const u8 *hash) |
4678 | 0 | { |
4679 | 0 | struct dpp_bootstrap_info *bi; |
4680 | |
|
4681 | 0 | if (!dpp) |
4682 | 0 | return NULL; |
4683 | | |
4684 | 0 | dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { |
4685 | 0 | if (!bi->own && os_memcmp(bi->pubkey_hash_chirp, hash, |
4686 | 0 | SHA256_MAC_LEN) == 0) |
4687 | 0 | return bi; |
4688 | 0 | } |
4689 | | |
4690 | 0 | return NULL; |
4691 | 0 | } |
4692 | | #endif /* CONFIG_DPP2 */ |
4693 | | |
4694 | | |
4695 | | static int dpp_nfc_update_bi_channel(struct dpp_bootstrap_info *own_bi, |
4696 | | struct dpp_bootstrap_info *peer_bi) |
4697 | 0 | { |
4698 | 0 | unsigned int i, freq = 0; |
4699 | 0 | enum hostapd_hw_mode mode; |
4700 | 0 | u8 op_class, channel; |
4701 | 0 | char chan[20]; |
4702 | |
|
4703 | 0 | if (peer_bi->num_freq == 0 && !peer_bi->channels_listed) |
4704 | 0 | return 0; /* no channel preference/constraint */ |
4705 | | |
4706 | 0 | for (i = 0; i < peer_bi->num_freq; i++) { |
4707 | 0 | if ((own_bi->num_freq == 0 && !own_bi->channels_listed) || |
4708 | 0 | freq_included(own_bi->freq, own_bi->num_freq, |
4709 | 0 | peer_bi->freq[i])) { |
4710 | 0 | freq = peer_bi->freq[i]; |
4711 | 0 | break; |
4712 | 0 | } |
4713 | 0 | } |
4714 | 0 | if (!freq) { |
4715 | 0 | wpa_printf(MSG_DEBUG, "DPP: No common channel found"); |
4716 | 0 | return -1; |
4717 | 0 | } |
4718 | | |
4719 | 0 | mode = ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, &channel); |
4720 | 0 | if (mode == NUM_HOSTAPD_MODES) { |
4721 | 0 | wpa_printf(MSG_DEBUG, |
4722 | 0 | "DPP: Could not determine operating class or channel number for %u MHz", |
4723 | 0 | freq); |
4724 | 0 | } |
4725 | |
|
4726 | 0 | wpa_printf(MSG_DEBUG, |
4727 | 0 | "DPP: Selected %u MHz (op_class %u channel %u) as the negotiation channel based on information from NFC negotiated handover", |
4728 | 0 | freq, op_class, channel); |
4729 | 0 | os_snprintf(chan, sizeof(chan), "%u/%u", op_class, channel); |
4730 | 0 | os_free(own_bi->chan); |
4731 | 0 | own_bi->chan = os_strdup(chan); |
4732 | 0 | own_bi->freq[0] = freq; |
4733 | 0 | own_bi->num_freq = 1; |
4734 | 0 | os_free(peer_bi->chan); |
4735 | 0 | peer_bi->chan = os_strdup(chan); |
4736 | 0 | peer_bi->freq[0] = freq; |
4737 | 0 | peer_bi->num_freq = 1; |
4738 | |
|
4739 | 0 | return dpp_gen_uri(own_bi); |
4740 | 0 | } |
4741 | | |
4742 | | |
4743 | | static int dpp_nfc_update_bi_key(struct dpp_bootstrap_info *own_bi, |
4744 | | struct dpp_bootstrap_info *peer_bi) |
4745 | 0 | { |
4746 | 0 | if (peer_bi->curve == own_bi->curve) |
4747 | 0 | return 0; |
4748 | | |
4749 | 0 | wpa_printf(MSG_DEBUG, |
4750 | 0 | "DPP: Update own bootstrapping key to match peer curve from NFC handover"); |
4751 | |
|
4752 | 0 | crypto_ec_key_deinit(own_bi->pubkey); |
4753 | 0 | own_bi->pubkey = NULL; |
4754 | |
|
4755 | 0 | if (dpp_keygen(own_bi, peer_bi->curve->name, NULL, 0) < 0 || |
4756 | 0 | dpp_gen_uri(own_bi) < 0) |
4757 | 0 | goto fail; |
4758 | | |
4759 | 0 | return 0; |
4760 | 0 | fail: |
4761 | 0 | dl_list_del(&own_bi->list); |
4762 | 0 | dpp_bootstrap_info_free(own_bi); |
4763 | 0 | return -1; |
4764 | 0 | } |
4765 | | |
4766 | | |
4767 | | int dpp_nfc_update_bi(struct dpp_bootstrap_info *own_bi, |
4768 | | struct dpp_bootstrap_info *peer_bi) |
4769 | 0 | { |
4770 | 0 | if (dpp_nfc_update_bi_channel(own_bi, peer_bi) < 0 || |
4771 | 0 | dpp_nfc_update_bi_key(own_bi, peer_bi) < 0) |
4772 | 0 | return -1; |
4773 | 0 | return 0; |
4774 | 0 | } |
4775 | | |
4776 | | |
4777 | | static unsigned int dpp_next_configurator_id(struct dpp_global *dpp) |
4778 | 0 | { |
4779 | 0 | struct dpp_configurator *conf; |
4780 | 0 | unsigned int max_id = 0; |
4781 | |
|
4782 | 0 | dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, |
4783 | 0 | list) { |
4784 | 0 | if (conf->id > max_id) |
4785 | 0 | max_id = conf->id; |
4786 | 0 | } |
4787 | 0 | return max_id + 1; |
4788 | 0 | } |
4789 | | |
4790 | | |
4791 | | int dpp_configurator_add(struct dpp_global *dpp, const char *cmd) |
4792 | 0 | { |
4793 | 0 | char *curve; |
4794 | 0 | char *key = NULL, *ppkey = NULL; |
4795 | 0 | u8 *privkey = NULL, *pp_key = NULL; |
4796 | 0 | size_t privkey_len = 0, pp_key_len = 0; |
4797 | 0 | int ret = -1; |
4798 | 0 | struct dpp_configurator *conf = NULL; |
4799 | 0 | const struct dpp_curve_params *net_access_key_curve = NULL; |
4800 | |
|
4801 | 0 | curve = get_param(cmd, " net_access_key_curve="); |
4802 | 0 | if (curve) { |
4803 | 0 | net_access_key_curve = dpp_get_curve_name(curve); |
4804 | 0 | if (!net_access_key_curve) { |
4805 | 0 | wpa_printf(MSG_DEBUG, |
4806 | 0 | "DPP: Unsupported net_access_key_curve: %s", |
4807 | 0 | curve); |
4808 | 0 | goto fail; |
4809 | 0 | } |
4810 | 0 | os_free(curve); |
4811 | 0 | } |
4812 | | |
4813 | 0 | curve = get_param(cmd, " curve="); |
4814 | 0 | key = get_param(cmd, " key="); |
4815 | 0 | ppkey = get_param(cmd, " ppkey="); |
4816 | |
|
4817 | 0 | if (key) { |
4818 | 0 | privkey_len = os_strlen(key) / 2; |
4819 | 0 | privkey = os_malloc(privkey_len); |
4820 | 0 | if (!privkey || |
4821 | 0 | hexstr2bin(key, privkey, privkey_len) < 0) |
4822 | 0 | goto fail; |
4823 | 0 | } |
4824 | | |
4825 | 0 | if (ppkey) { |
4826 | 0 | pp_key_len = os_strlen(ppkey) / 2; |
4827 | 0 | pp_key = os_malloc(pp_key_len); |
4828 | 0 | if (!pp_key || |
4829 | 0 | hexstr2bin(ppkey, pp_key, pp_key_len) < 0) |
4830 | 0 | goto fail; |
4831 | 0 | } |
4832 | | |
4833 | 0 | conf = dpp_keygen_configurator(curve, privkey, privkey_len, |
4834 | 0 | pp_key, pp_key_len); |
4835 | 0 | if (!conf) |
4836 | 0 | goto fail; |
4837 | | |
4838 | 0 | conf->net_access_key_curve = net_access_key_curve; |
4839 | 0 | conf->id = dpp_next_configurator_id(dpp); |
4840 | 0 | dl_list_add(&dpp->configurator, &conf->list); |
4841 | 0 | ret = conf->id; |
4842 | 0 | conf = NULL; |
4843 | 0 | fail: |
4844 | 0 | os_free(curve); |
4845 | 0 | str_clear_free(key); |
4846 | 0 | str_clear_free(ppkey); |
4847 | 0 | bin_clear_free(privkey, privkey_len); |
4848 | 0 | bin_clear_free(pp_key, pp_key_len); |
4849 | 0 | dpp_configurator_free(conf); |
4850 | 0 | return ret; |
4851 | 0 | } |
4852 | | |
4853 | | |
4854 | | int dpp_configurator_set(struct dpp_global *dpp, const char *cmd) |
4855 | 0 | { |
4856 | 0 | unsigned int id; |
4857 | 0 | struct dpp_configurator *conf; |
4858 | 0 | char *curve; |
4859 | |
|
4860 | 0 | id = atoi(cmd); |
4861 | 0 | conf = dpp_configurator_get_id(dpp, id); |
4862 | 0 | if (!conf) |
4863 | 0 | return -1; |
4864 | | |
4865 | 0 | curve = get_param(cmd, " net_access_key_curve="); |
4866 | 0 | if (curve) { |
4867 | 0 | const struct dpp_curve_params *net_access_key_curve; |
4868 | |
|
4869 | 0 | net_access_key_curve = dpp_get_curve_name(curve); |
4870 | 0 | os_free(curve); |
4871 | 0 | if (!net_access_key_curve) |
4872 | 0 | return -1; |
4873 | 0 | conf->net_access_key_curve = net_access_key_curve; |
4874 | 0 | } |
4875 | | |
4876 | 0 | return 0; |
4877 | 0 | } |
4878 | | |
4879 | | |
4880 | | static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) |
4881 | 1.67k | { |
4882 | 1.67k | struct dpp_configurator *conf, *tmp; |
4883 | 1.67k | int found = 0; |
4884 | | |
4885 | 1.67k | if (!dpp) |
4886 | 0 | return -1; |
4887 | | |
4888 | 1.67k | dl_list_for_each_safe(conf, tmp, &dpp->configurator, |
4889 | 1.67k | struct dpp_configurator, list) { |
4890 | 0 | if (id && conf->id != id) |
4891 | 0 | continue; |
4892 | 0 | found = 1; |
4893 | 0 | dl_list_del(&conf->list); |
4894 | 0 | dpp_configurator_free(conf); |
4895 | 0 | } |
4896 | | |
4897 | 1.67k | if (id == 0) |
4898 | 1.67k | return 0; /* flush succeeds regardless of entries found */ |
4899 | 0 | return found ? 0 : -1; |
4900 | 1.67k | } |
4901 | | |
4902 | | |
4903 | | int dpp_configurator_remove(struct dpp_global *dpp, const char *id) |
4904 | 0 | { |
4905 | 0 | unsigned int id_val; |
4906 | |
|
4907 | 0 | if (os_strcmp(id, "*") == 0) { |
4908 | 0 | id_val = 0; |
4909 | 0 | } else { |
4910 | 0 | id_val = atoi(id); |
4911 | 0 | if (id_val == 0) |
4912 | 0 | return -1; |
4913 | 0 | } |
4914 | | |
4915 | 0 | return dpp_configurator_del(dpp, id_val); |
4916 | 0 | } |
4917 | | |
4918 | | |
4919 | | int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, |
4920 | | char *buf, size_t buflen) |
4921 | 0 | { |
4922 | 0 | struct dpp_configurator *conf; |
4923 | |
|
4924 | 0 | conf = dpp_configurator_get_id(dpp, id); |
4925 | 0 | if (!conf) |
4926 | 0 | return -1; |
4927 | | |
4928 | 0 | return dpp_configurator_get_key(conf, buf, buflen); |
4929 | 0 | } |
4930 | | |
4931 | | |
4932 | | #ifdef CONFIG_DPP2 |
4933 | | |
4934 | | int dpp_configurator_from_backup(struct dpp_global *dpp, |
4935 | | struct dpp_asymmetric_key *key) |
4936 | 0 | { |
4937 | 0 | struct dpp_configurator *conf; |
4938 | 0 | const struct dpp_curve_params *curve, *curve_pp; |
4939 | |
|
4940 | 0 | if (!key->csign || !key->pp_key) |
4941 | 0 | return -1; |
4942 | | |
4943 | 0 | curve = dpp_get_curve_ike_group(crypto_ec_key_group(key->csign)); |
4944 | 0 | if (!curve) { |
4945 | 0 | wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key"); |
4946 | 0 | return -1; |
4947 | 0 | } |
4948 | | |
4949 | 0 | curve_pp = dpp_get_curve_ike_group(crypto_ec_key_group(key->pp_key)); |
4950 | 0 | if (!curve_pp) { |
4951 | 0 | wpa_printf(MSG_INFO, "DPP: Unsupported group in ppKey"); |
4952 | 0 | return -1; |
4953 | 0 | } |
4954 | | |
4955 | 0 | if (curve != curve_pp) { |
4956 | 0 | wpa_printf(MSG_INFO, |
4957 | 0 | "DPP: Mismatch in c-sign-key and ppKey groups"); |
4958 | 0 | return -1; |
4959 | 0 | } |
4960 | | |
4961 | 0 | conf = os_zalloc(sizeof(*conf)); |
4962 | 0 | if (!conf) |
4963 | 0 | return -1; |
4964 | 0 | conf->curve = curve; |
4965 | 0 | conf->csign = key->csign; |
4966 | 0 | key->csign = NULL; |
4967 | 0 | conf->pp_key = key->pp_key; |
4968 | 0 | key->pp_key = NULL; |
4969 | 0 | conf->own = 1; |
4970 | 0 | if (dpp_configurator_gen_kid(conf) < 0) { |
4971 | 0 | dpp_configurator_free(conf); |
4972 | 0 | return -1; |
4973 | 0 | } |
4974 | | |
4975 | 0 | conf->id = dpp_next_configurator_id(dpp); |
4976 | 0 | dl_list_add(&dpp->configurator, &conf->list); |
4977 | 0 | return conf->id; |
4978 | 0 | } |
4979 | | |
4980 | | |
4981 | | struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp, |
4982 | | const u8 *kid) |
4983 | 0 | { |
4984 | 0 | struct dpp_configurator *conf; |
4985 | |
|
4986 | 0 | if (!dpp) |
4987 | 0 | return NULL; |
4988 | | |
4989 | 0 | dl_list_for_each(conf, &dpp->configurator, |
4990 | 0 | struct dpp_configurator, list) { |
4991 | 0 | if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0) |
4992 | 0 | return conf; |
4993 | 0 | } |
4994 | 0 | return NULL; |
4995 | 0 | } |
4996 | | |
4997 | | #endif /* CONFIG_DPP2 */ |
4998 | | |
4999 | | |
5000 | | struct dpp_global * dpp_global_init(struct dpp_global_config *config) |
5001 | 1.67k | { |
5002 | 1.67k | struct dpp_global *dpp; |
5003 | | |
5004 | 1.67k | dpp = os_zalloc(sizeof(*dpp)); |
5005 | 1.67k | if (!dpp) |
5006 | 0 | return NULL; |
5007 | 1.67k | #ifdef CONFIG_DPP2 |
5008 | 1.67k | dpp->cb_ctx = config->cb_ctx; |
5009 | 1.67k | dpp->remove_bi = config->remove_bi; |
5010 | 1.67k | #endif /* CONFIG_DPP2 */ |
5011 | | |
5012 | 1.67k | dl_list_init(&dpp->bootstrap); |
5013 | 1.67k | dl_list_init(&dpp->configurator); |
5014 | 1.67k | #ifdef CONFIG_DPP2 |
5015 | 1.67k | dl_list_init(&dpp->controllers); |
5016 | 1.67k | dl_list_init(&dpp->tcp_init); |
5017 | 1.67k | dpp->relay_sock = -1; |
5018 | 1.67k | #endif /* CONFIG_DPP2 */ |
5019 | | |
5020 | 1.67k | return dpp; |
5021 | 1.67k | } |
5022 | | |
5023 | | |
5024 | | void dpp_global_clear(struct dpp_global *dpp) |
5025 | 1.67k | { |
5026 | 1.67k | if (!dpp) |
5027 | 0 | return; |
5028 | | |
5029 | 1.67k | dpp_bootstrap_del(dpp, 0); |
5030 | 1.67k | dpp_configurator_del(dpp, 0); |
5031 | 1.67k | #ifdef CONFIG_DPP2 |
5032 | 1.67k | dpp_tcp_init_flush(dpp); |
5033 | 1.67k | dpp_relay_flush_controllers(dpp); |
5034 | 1.67k | dpp_controller_stop(dpp); |
5035 | 1.67k | #endif /* CONFIG_DPP2 */ |
5036 | 1.67k | } |
5037 | | |
5038 | | |
5039 | | void dpp_global_deinit(struct dpp_global *dpp) |
5040 | 1.67k | { |
5041 | 1.67k | dpp_global_clear(dpp); |
5042 | 1.67k | os_free(dpp); |
5043 | 1.67k | } |
5044 | | |
5045 | | |
5046 | | void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator) |
5047 | 0 | { |
5048 | 0 | u8 hash[SHA256_MAC_LEN]; |
5049 | 0 | char hex[SHA256_MAC_LEN * 2 + 1]; |
5050 | |
|
5051 | 0 | if (auth->peer_protocol_key) { |
5052 | 0 | dpp_get_pubkey_hash(auth->peer_protocol_key, hash); |
5053 | 0 | wpa_snprintf_hex(hex, sizeof(hex), hash, sizeof(hash)); |
5054 | 0 | } else { |
5055 | 0 | hex[0] = '\0'; |
5056 | 0 | } |
5057 | 0 | wpa_msg(auth->msg_ctx, MSG_INFO, |
5058 | 0 | DPP_EVENT_AUTH_SUCCESS "init=%d pkhash=%s own=%d peer=%d", |
5059 | 0 | initiator, hex, auth->own_bi ? (int) auth->own_bi->id : -1, |
5060 | 0 | auth->peer_bi ? (int) auth->peer_bi->id : -1); |
5061 | 0 | } |
5062 | | |
5063 | | |
5064 | | #ifdef CONFIG_DPP2 |
5065 | | |
5066 | | struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi) |
5067 | 0 | { |
5068 | 0 | struct wpabuf *msg; |
5069 | |
|
5070 | 0 | wpa_printf(MSG_DEBUG, "DPP: Build Presence Announcement frame"); |
5071 | |
|
5072 | 0 | msg = dpp_alloc_msg(DPP_PA_PRESENCE_ANNOUNCEMENT, 4 + SHA256_MAC_LEN); |
5073 | 0 | if (!msg) |
5074 | 0 | return NULL; |
5075 | | |
5076 | | /* Responder Bootstrapping Key Hash */ |
5077 | 0 | dpp_build_attr_r_bootstrap_key_hash(msg, bi->pubkey_hash_chirp); |
5078 | 0 | wpa_hexdump_buf(MSG_DEBUG, |
5079 | 0 | "DPP: Presence Announcement frame attributes", msg); |
5080 | 0 | return msg; |
5081 | 0 | } |
5082 | | |
5083 | | |
5084 | | void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src, |
5085 | | unsigned int freq, const u8 *hash) |
5086 | 0 | { |
5087 | 0 | char hex[SHA256_MAC_LEN * 2 + 1]; |
5088 | |
|
5089 | 0 | wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN); |
5090 | 0 | wpa_msg(msg_ctx, MSG_INFO, |
5091 | 0 | DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s", |
5092 | 0 | id, MAC2STR(src), freq, hex); |
5093 | 0 | } |
5094 | | |
5095 | | #endif /* CONFIG_DPP2 */ |
5096 | | |
5097 | | |
5098 | | #ifdef CONFIG_DPP3 |
5099 | | |
5100 | | struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi) |
5101 | | { |
5102 | | struct wpabuf *msg; |
5103 | | const u8 *r_hash = bi->pubkey_hash_chirp; |
5104 | | #ifdef CONFIG_TESTING_OPTIONS |
5105 | | u8 test_hash[SHA256_MAC_LEN]; |
5106 | | #endif /* CONFIG_TESTING_OPTIONS */ |
5107 | | |
5108 | | wpa_printf(MSG_DEBUG, |
5109 | | "DPP: Build Push Button Presence Announcement frame"); |
5110 | | |
5111 | | msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT, |
5112 | | 4 + SHA256_MAC_LEN); |
5113 | | if (!msg) |
5114 | | return NULL; |
5115 | | |
5116 | | #ifdef CONFIG_TESTING_OPTIONS |
5117 | | if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ) { |
5118 | | wpa_printf(MSG_INFO, |
5119 | | "DPP: TESTING - invalid R-Bootstrap Key Hash"); |
5120 | | os_memcpy(test_hash, r_hash, SHA256_MAC_LEN); |
5121 | | test_hash[SHA256_MAC_LEN - 1] ^= 0x01; |
5122 | | r_hash = test_hash; |
5123 | | } |
5124 | | #endif /* CONFIG_TESTING_OPTIONS */ |
5125 | | |
5126 | | /* Responder Bootstrapping Key Hash */ |
5127 | | dpp_build_attr_r_bootstrap_key_hash(msg, r_hash); |
5128 | | wpa_hexdump_buf(MSG_DEBUG, |
5129 | | "DPP: Push Button Presence Announcement frame attributes", |
5130 | | msg); |
5131 | | return msg; |
5132 | | } |
5133 | | |
5134 | | |
5135 | | struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi, |
5136 | | const u8 *e_hash, |
5137 | | const u8 *c_nonce, |
5138 | | size_t c_nonce_len) |
5139 | | { |
5140 | | struct wpabuf *msg; |
5141 | | const u8 *i_hash = bi->pubkey_hash_chirp; |
5142 | | #ifdef CONFIG_TESTING_OPTIONS |
5143 | | u8 test_hash[SHA256_MAC_LEN]; |
5144 | | #endif /* CONFIG_TESTING_OPTIONS */ |
5145 | | |
5146 | | wpa_printf(MSG_DEBUG, |
5147 | | "DPP: Build Push Button Presence Announcement Response frame"); |
5148 | | |
5149 | | msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP, |
5150 | | 2 * (4 + SHA256_MAC_LEN) + 4 + c_nonce_len); |
5151 | | if (!msg) |
5152 | | return NULL; |
5153 | | |
5154 | | #ifdef CONFIG_TESTING_OPTIONS |
5155 | | if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP) { |
5156 | | wpa_printf(MSG_INFO, |
5157 | | "DPP: TESTING - invalid I-Bootstrap Key Hash"); |
5158 | | os_memcpy(test_hash, i_hash, SHA256_MAC_LEN); |
5159 | | test_hash[SHA256_MAC_LEN - 1] ^= 0x01; |
5160 | | i_hash = test_hash; |
5161 | | } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP) { |
5162 | | wpa_printf(MSG_INFO, |
5163 | | "DPP: TESTING - invalid R-Bootstrap Key Hash"); |
5164 | | os_memcpy(test_hash, e_hash, SHA256_MAC_LEN); |
5165 | | test_hash[SHA256_MAC_LEN - 1] ^= 0x01; |
5166 | | e_hash = test_hash; |
5167 | | } |
5168 | | #endif /* CONFIG_TESTING_OPTIONS */ |
5169 | | |
5170 | | /* Initiator Bootstrapping Key Hash */ |
5171 | | wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); |
5172 | | wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); |
5173 | | wpabuf_put_le16(msg, SHA256_MAC_LEN); |
5174 | | wpabuf_put_data(msg, i_hash, SHA256_MAC_LEN); |
5175 | | |
5176 | | /* Responder Bootstrapping Key Hash */ |
5177 | | dpp_build_attr_r_bootstrap_key_hash(msg, e_hash); |
5178 | | |
5179 | | /* Configurator Nonce */ |
5180 | | wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE); |
5181 | | wpabuf_put_le16(msg, c_nonce_len); |
5182 | | wpabuf_put_data(msg, c_nonce, c_nonce_len); |
5183 | | |
5184 | | wpa_hexdump_buf(MSG_DEBUG, |
5185 | | "DPP: Push Button Presence Announcement Response frame attributes", |
5186 | | msg); |
5187 | | return msg; |
5188 | | } |
5189 | | |
5190 | | #endif /* CONFIG_DPP3 */ |