/src/hostap/src/wps/wps_common.c
Line | Count | Source |
1 | | /* |
2 | | * Wi-Fi Protected Setup - common functionality |
3 | | * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | */ |
8 | | |
9 | | #include "includes.h" |
10 | | |
11 | | #include "common.h" |
12 | | #include "common/defs.h" |
13 | | #include "common/ieee802_11_common.h" |
14 | | #include "crypto/aes_wrap.h" |
15 | | #include "crypto/crypto.h" |
16 | | #include "crypto/dh_group5.h" |
17 | | #include "crypto/sha1.h" |
18 | | #include "crypto/sha256.h" |
19 | | #include "crypto/random.h" |
20 | | #include "wps_i.h" |
21 | | #include "wps_dev_attr.h" |
22 | | |
23 | | |
24 | | void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, |
25 | | const char *label, u8 *res, size_t res_len) |
26 | 0 | { |
27 | 0 | u8 i_buf[4], key_bits[4]; |
28 | 0 | const u8 *addr[4]; |
29 | 0 | size_t len[4]; |
30 | 0 | int i, iter; |
31 | 0 | u8 hash[SHA256_MAC_LEN], *opos; |
32 | 0 | size_t left; |
33 | |
|
34 | 0 | WPA_PUT_BE32(key_bits, res_len * 8); |
35 | |
|
36 | 0 | addr[0] = i_buf; |
37 | 0 | len[0] = sizeof(i_buf); |
38 | 0 | addr[1] = label_prefix; |
39 | 0 | len[1] = label_prefix_len; |
40 | 0 | addr[2] = (const u8 *) label; |
41 | 0 | len[2] = os_strlen(label); |
42 | 0 | addr[3] = key_bits; |
43 | 0 | len[3] = sizeof(key_bits); |
44 | |
|
45 | 0 | iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN; |
46 | 0 | opos = res; |
47 | 0 | left = res_len; |
48 | |
|
49 | 0 | for (i = 1; i <= iter; i++) { |
50 | 0 | WPA_PUT_BE32(i_buf, i); |
51 | 0 | hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash); |
52 | 0 | if (i < iter) { |
53 | 0 | os_memcpy(opos, hash, SHA256_MAC_LEN); |
54 | 0 | opos += SHA256_MAC_LEN; |
55 | 0 | left -= SHA256_MAC_LEN; |
56 | 0 | } else |
57 | 0 | os_memcpy(opos, hash, left); |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | |
62 | | int wps_derive_keys(struct wps_data *wps) |
63 | 0 | { |
64 | 0 | struct wpabuf *pubkey, *dh_shared; |
65 | 0 | u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN]; |
66 | 0 | const u8 *addr[3]; |
67 | 0 | size_t len[3]; |
68 | 0 | u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN]; |
69 | |
|
70 | 0 | if (wps->dh_privkey == NULL) { |
71 | 0 | wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available"); |
72 | 0 | return -1; |
73 | 0 | } |
74 | | |
75 | 0 | pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r; |
76 | 0 | if (pubkey == NULL) { |
77 | 0 | wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available"); |
78 | 0 | return -1; |
79 | 0 | } |
80 | | |
81 | 0 | wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); |
82 | 0 | wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey); |
83 | 0 | dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); |
84 | 0 | dh5_free(wps->dh_ctx); |
85 | 0 | wps->dh_ctx = NULL; |
86 | 0 | dh_shared = wpabuf_zeropad(dh_shared, 192); |
87 | 0 | if (dh_shared == NULL) { |
88 | 0 | wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key"); |
89 | 0 | return -1; |
90 | 0 | } |
91 | | |
92 | | /* Own DH private key is not needed anymore */ |
93 | 0 | wpabuf_clear_free(wps->dh_privkey); |
94 | 0 | wps->dh_privkey = NULL; |
95 | |
|
96 | 0 | wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); |
97 | | |
98 | | /* DHKey = SHA-256(g^AB mod p) */ |
99 | 0 | addr[0] = wpabuf_head(dh_shared); |
100 | 0 | len[0] = wpabuf_len(dh_shared); |
101 | 0 | sha256_vector(1, addr, len, dhkey); |
102 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); |
103 | 0 | wpabuf_clear_free(dh_shared); |
104 | | |
105 | | /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ |
106 | 0 | addr[0] = wps->nonce_e; |
107 | 0 | len[0] = WPS_NONCE_LEN; |
108 | 0 | addr[1] = wps->mac_addr_e; |
109 | 0 | len[1] = ETH_ALEN; |
110 | 0 | addr[2] = wps->nonce_r; |
111 | 0 | len[2] = WPS_NONCE_LEN; |
112 | 0 | hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk); |
113 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk)); |
114 | |
|
115 | 0 | wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation", |
116 | 0 | keys, sizeof(keys)); |
117 | 0 | os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN); |
118 | 0 | os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN); |
119 | 0 | os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN, |
120 | 0 | WPS_EMSK_LEN); |
121 | |
|
122 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey", |
123 | 0 | wps->authkey, WPS_AUTHKEY_LEN); |
124 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey", |
125 | 0 | wps->keywrapkey, WPS_KEYWRAPKEY_LEN); |
126 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN); |
127 | |
|
128 | 0 | return 0; |
129 | 0 | } |
130 | | |
131 | | |
132 | | int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, |
133 | | size_t dev_passwd_len) |
134 | 0 | { |
135 | 0 | u8 hash[SHA256_MAC_LEN]; |
136 | |
|
137 | 0 | if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, |
138 | 0 | (dev_passwd_len + 1) / 2, hash) < 0) |
139 | 0 | return -1; |
140 | 0 | os_memcpy(wps->psk1, hash, WPS_PSK_LEN); |
141 | 0 | if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, |
142 | 0 | dev_passwd + (dev_passwd_len + 1) / 2, |
143 | 0 | dev_passwd_len / 2, hash) < 0) |
144 | 0 | return -1; |
145 | 0 | os_memcpy(wps->psk2, hash, WPS_PSK_LEN); |
146 | |
|
147 | 0 | wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password", |
148 | 0 | dev_passwd, dev_passwd_len); |
149 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN); |
150 | 0 | wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN); |
151 | 0 | return 0; |
152 | 0 | } |
153 | | |
154 | | |
155 | | struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, |
156 | | size_t encr_len) |
157 | 0 | { |
158 | 0 | struct wpabuf *decrypted; |
159 | 0 | const size_t block_size = 16; |
160 | 0 | size_t i; |
161 | 0 | u8 pad; |
162 | 0 | const u8 *pos; |
163 | | |
164 | | /* AES-128-CBC */ |
165 | 0 | if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size) |
166 | 0 | { |
167 | 0 | wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received"); |
168 | 0 | return NULL; |
169 | 0 | } |
170 | | |
171 | 0 | decrypted = wpabuf_alloc(encr_len - block_size); |
172 | 0 | if (decrypted == NULL) |
173 | 0 | return NULL; |
174 | | |
175 | 0 | wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len); |
176 | 0 | wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size); |
177 | 0 | if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted), |
178 | 0 | wpabuf_len(decrypted))) { |
179 | 0 | wpabuf_clear_free(decrypted); |
180 | 0 | return NULL; |
181 | 0 | } |
182 | | |
183 | 0 | wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings", |
184 | 0 | decrypted); |
185 | |
|
186 | 0 | pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1; |
187 | 0 | pad = *pos; |
188 | 0 | if (pad > wpabuf_len(decrypted)) { |
189 | 0 | wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value"); |
190 | 0 | wpabuf_clear_free(decrypted); |
191 | 0 | return NULL; |
192 | 0 | } |
193 | 0 | for (i = 0; i < pad; i++) { |
194 | 0 | if (*pos-- != pad) { |
195 | 0 | wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad " |
196 | 0 | "string"); |
197 | 0 | wpabuf_clear_free(decrypted); |
198 | 0 | return NULL; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | decrypted->used -= pad; |
202 | |
|
203 | 0 | return decrypted; |
204 | 0 | } |
205 | | |
206 | | |
207 | | /** |
208 | | * wps_pin_checksum - Compute PIN checksum |
209 | | * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit) |
210 | | * Returns: Checksum digit |
211 | | */ |
212 | | unsigned int wps_pin_checksum(unsigned int pin) |
213 | 0 | { |
214 | 0 | unsigned int accum = 0; |
215 | 0 | while (pin) { |
216 | 0 | accum += 3 * (pin % 10); |
217 | 0 | pin /= 10; |
218 | 0 | accum += pin % 10; |
219 | 0 | pin /= 10; |
220 | 0 | } |
221 | |
|
222 | 0 | return (10 - accum % 10) % 10; |
223 | 0 | } |
224 | | |
225 | | |
226 | | /** |
227 | | * wps_pin_valid - Check whether a PIN has a valid checksum |
228 | | * @pin: Eight digit PIN (i.e., including the checksum digit) |
229 | | * Returns: 1 if checksum digit is valid, or 0 if not |
230 | | */ |
231 | | unsigned int wps_pin_valid(unsigned int pin) |
232 | 0 | { |
233 | 0 | return wps_pin_checksum(pin / 10) == (pin % 10); |
234 | 0 | } |
235 | | |
236 | | |
237 | | /** |
238 | | * wps_generate_pin - Generate a random PIN |
239 | | * Returns: Eight digit PIN (i.e., including the checksum digit) |
240 | | */ |
241 | | int wps_generate_pin(unsigned int *pin) |
242 | 0 | { |
243 | 0 | unsigned int val; |
244 | | |
245 | | /* Generate seven random digits for the PIN */ |
246 | 0 | if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) |
247 | 0 | return -1; |
248 | 0 | val %= 10000000; |
249 | | |
250 | | /* Append checksum digit */ |
251 | 0 | *pin = val * 10 + wps_pin_checksum(val); |
252 | 0 | return 0; |
253 | 0 | } |
254 | | |
255 | | |
256 | | int wps_pin_str_valid(const char *pin) |
257 | 0 | { |
258 | 0 | const char *p; |
259 | 0 | size_t len; |
260 | |
|
261 | 0 | p = pin; |
262 | 0 | while (*p >= '0' && *p <= '9') |
263 | 0 | p++; |
264 | 0 | if (*p != '\0') |
265 | 0 | return 0; |
266 | | |
267 | 0 | len = p - pin; |
268 | 0 | return len == 4 || len == 8; |
269 | 0 | } |
270 | | |
271 | | |
272 | | void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, |
273 | | u16 config_error, u16 error_indication, const u8 *mac_addr) |
274 | 0 | { |
275 | 0 | union wps_event_data data; |
276 | |
|
277 | 0 | if (wps->event_cb == NULL) |
278 | 0 | return; |
279 | | |
280 | 0 | os_memset(&data, 0, sizeof(data)); |
281 | 0 | data.fail.msg = msg; |
282 | 0 | data.fail.config_error = config_error; |
283 | 0 | data.fail.error_indication = error_indication; |
284 | 0 | os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN); |
285 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); |
286 | 0 | } |
287 | | |
288 | | |
289 | | void wps_success_event(struct wps_context *wps, const u8 *mac_addr) |
290 | 0 | { |
291 | 0 | union wps_event_data data; |
292 | |
|
293 | 0 | if (wps->event_cb == NULL) |
294 | 0 | return; |
295 | | |
296 | 0 | os_memset(&data, 0, sizeof(data)); |
297 | 0 | os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN); |
298 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data); |
299 | 0 | } |
300 | | |
301 | | |
302 | | void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, |
303 | | const u8 *mac_addr) |
304 | 0 | { |
305 | 0 | union wps_event_data data; |
306 | |
|
307 | 0 | if (wps->event_cb == NULL) |
308 | 0 | return; |
309 | | |
310 | 0 | os_memset(&data, 0, sizeof(data)); |
311 | 0 | data.pwd_auth_fail.enrollee = enrollee; |
312 | 0 | data.pwd_auth_fail.part = part; |
313 | 0 | os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN); |
314 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); |
315 | 0 | } |
316 | | |
317 | | |
318 | | void wps_pbc_overlap_event(struct wps_context *wps) |
319 | 0 | { |
320 | 0 | if (wps->event_cb == NULL) |
321 | 0 | return; |
322 | | |
323 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL); |
324 | 0 | } |
325 | | |
326 | | |
327 | | void wps_pbc_timeout_event(struct wps_context *wps) |
328 | 0 | { |
329 | 0 | if (wps->event_cb == NULL) |
330 | 0 | return; |
331 | | |
332 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL); |
333 | 0 | } |
334 | | |
335 | | |
336 | | void wps_pbc_active_event(struct wps_context *wps) |
337 | 0 | { |
338 | 0 | if (wps->event_cb == NULL) |
339 | 0 | return; |
340 | | |
341 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL); |
342 | 0 | } |
343 | | |
344 | | |
345 | | void wps_pbc_disable_event(struct wps_context *wps) |
346 | 0 | { |
347 | 0 | if (wps->event_cb == NULL) |
348 | 0 | return; |
349 | | |
350 | 0 | wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL); |
351 | 0 | } |
352 | | |
353 | | |
354 | | #ifdef CONFIG_WPS_OOB |
355 | | |
356 | | struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, |
357 | | int channel) |
358 | 0 | { |
359 | 0 | struct wps_data data; |
360 | 0 | struct wpabuf *plain; |
361 | |
|
362 | 0 | plain = wpabuf_alloc(500); |
363 | 0 | if (plain == NULL) { |
364 | 0 | wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " |
365 | 0 | "credential"); |
366 | 0 | return NULL; |
367 | 0 | } |
368 | | |
369 | 0 | os_memset(&data, 0, sizeof(data)); |
370 | 0 | data.wps = wps; |
371 | 0 | data.auth_type = wps->auth_types; |
372 | 0 | data.encr_type = wps->encr_types; |
373 | 0 | if (wps_build_cred(&data, plain) || |
374 | 0 | (rf_band && wps_build_rf_bands_attr(plain, rf_band)) || |
375 | 0 | (channel && wps_build_ap_channel(plain, channel)) || |
376 | 0 | wps_build_mac_addr(plain, wps->dev.mac_addr) || |
377 | 0 | wps_build_wfa_ext(plain, 0, NULL, 0, 0)) { |
378 | 0 | os_free(data.new_psk); |
379 | 0 | wpabuf_clear_free(plain); |
380 | 0 | return NULL; |
381 | 0 | } |
382 | | |
383 | 0 | if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk && |
384 | 0 | wps->ap) { |
385 | 0 | struct wps_credential cred; |
386 | |
|
387 | 0 | wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based " |
388 | 0 | "on credential token generation"); |
389 | |
|
390 | 0 | os_memset(&cred, 0, sizeof(cred)); |
391 | 0 | os_memcpy(cred.ssid, wps->ssid, wps->ssid_len); |
392 | 0 | cred.ssid_len = wps->ssid_len; |
393 | 0 | cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; |
394 | 0 | cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; |
395 | 0 | os_memcpy(cred.key, data.new_psk, data.new_psk_len); |
396 | 0 | cred.key_len = data.new_psk_len; |
397 | |
|
398 | 0 | wps->wps_state = WPS_STATE_CONFIGURED; |
399 | 0 | wpa_hexdump_ascii_key(MSG_DEBUG, |
400 | 0 | "WPS: Generated random passphrase", |
401 | 0 | data.new_psk, data.new_psk_len); |
402 | 0 | if (wps->cred_cb) |
403 | 0 | wps->cred_cb(wps->cb_ctx, &cred); |
404 | 0 | } |
405 | |
|
406 | 0 | os_free(data.new_psk); |
407 | |
|
408 | 0 | return plain; |
409 | 0 | } |
410 | | |
411 | | |
412 | | struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, |
413 | | const struct wpabuf *pubkey, |
414 | | const struct wpabuf *dev_pw) |
415 | 0 | { |
416 | 0 | struct wpabuf *data; |
417 | |
|
418 | 0 | data = wpabuf_alloc(200); |
419 | 0 | if (data == NULL) |
420 | 0 | return NULL; |
421 | | |
422 | 0 | if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey, |
423 | 0 | wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || |
424 | 0 | wps_build_wfa_ext(data, 0, NULL, 0, 0)) { |
425 | 0 | wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " |
426 | 0 | "token"); |
427 | 0 | wpabuf_clear_free(data); |
428 | 0 | return NULL; |
429 | 0 | } |
430 | | |
431 | 0 | return data; |
432 | 0 | } |
433 | | |
434 | | |
435 | | int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr) |
436 | 0 | { |
437 | 0 | struct wpabuf msg; |
438 | 0 | size_t i; |
439 | |
|
440 | 0 | for (i = 0; i < attr->num_cred; i++) { |
441 | 0 | struct wps_credential local_cred; |
442 | 0 | struct wps_parse_attr cattr; |
443 | |
|
444 | 0 | os_memset(&local_cred, 0, sizeof(local_cred)); |
445 | 0 | wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]); |
446 | 0 | if (wps_parse_msg(&msg, &cattr) < 0 || |
447 | 0 | wps_process_cred(&cattr, &local_cred)) { |
448 | 0 | wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB " |
449 | 0 | "credential"); |
450 | 0 | return -1; |
451 | 0 | } |
452 | 0 | wps->cred_cb(wps->cb_ctx, &local_cred); |
453 | 0 | } |
454 | | |
455 | 0 | return 0; |
456 | 0 | } |
457 | | |
458 | | |
459 | | #endif /* CONFIG_WPS_OOB */ |
460 | | |
461 | | |
462 | | int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]) |
463 | 0 | { |
464 | 0 | const char *pos; |
465 | | |
466 | | /* <categ>-<OUI>-<subcateg> */ |
467 | 0 | WPA_PUT_BE16(dev_type, atoi(str)); |
468 | 0 | pos = os_strchr(str, '-'); |
469 | 0 | if (pos == NULL) |
470 | 0 | return -1; |
471 | 0 | pos++; |
472 | 0 | if (hexstr2bin(pos, &dev_type[2], 4)) |
473 | 0 | return -1; |
474 | 0 | pos = os_strchr(pos, '-'); |
475 | 0 | if (pos == NULL) |
476 | 0 | return -1; |
477 | 0 | pos++; |
478 | 0 | WPA_PUT_BE16(&dev_type[6], atoi(pos)); |
479 | | |
480 | |
|
481 | 0 | return 0; |
482 | 0 | } |
483 | | |
484 | | |
485 | | char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, |
486 | | size_t buf_len) |
487 | 0 | { |
488 | 0 | int ret; |
489 | |
|
490 | 0 | ret = os_snprintf(buf, buf_len, "%u-%08X-%u", |
491 | 0 | WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]), |
492 | 0 | WPA_GET_BE16(&dev_type[6])); |
493 | 0 | if (os_snprintf_error(buf_len, ret)) |
494 | 0 | return NULL; |
495 | | |
496 | 0 | return buf; |
497 | 0 | } |
498 | | |
499 | | |
500 | | void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid) |
501 | 0 | { |
502 | 0 | const u8 *addr[2]; |
503 | 0 | size_t len[2]; |
504 | 0 | u8 hash[SHA1_MAC_LEN]; |
505 | 0 | u8 nsid[16] = { |
506 | 0 | 0x52, 0x64, 0x80, 0xf8, |
507 | 0 | 0xc9, 0x9b, |
508 | 0 | 0x4b, 0xe5, |
509 | 0 | 0xa6, 0x55, |
510 | 0 | 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84 |
511 | 0 | }; |
512 | |
|
513 | 0 | addr[0] = nsid; |
514 | 0 | len[0] = sizeof(nsid); |
515 | 0 | addr[1] = mac_addr; |
516 | 0 | len[1] = 6; |
517 | 0 | sha1_vector(2, addr, len, hash); |
518 | 0 | os_memcpy(uuid, hash, 16); |
519 | | |
520 | | /* Version: 5 = named-based version using SHA-1 */ |
521 | 0 | uuid[6] = (5 << 4) | (uuid[6] & 0x0f); |
522 | | |
523 | | /* Variant specified in RFC 4122 */ |
524 | 0 | uuid[8] = 0x80 | (uuid[8] & 0x3f); |
525 | 0 | } |
526 | | |
527 | | |
528 | | u16 wps_config_methods_str2bin(const char *str) |
529 | 0 | { |
530 | 0 | u16 methods = 0; |
531 | |
|
532 | 0 | if (str == NULL || str[0] == '\0') { |
533 | | /* Default to enabling methods based on build configuration */ |
534 | 0 | methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; |
535 | 0 | methods |= WPS_CONFIG_VIRT_DISPLAY; |
536 | 0 | #ifdef CONFIG_WPS_NFC |
537 | 0 | methods |= WPS_CONFIG_NFC_INTERFACE; |
538 | 0 | #endif /* CONFIG_WPS_NFC */ |
539 | 0 | #ifdef CONFIG_P2P |
540 | 0 | methods |= WPS_CONFIG_P2PS; |
541 | 0 | #endif /* CONFIG_P2P */ |
542 | 0 | } else { |
543 | 0 | if (os_strstr(str, "ethernet")) |
544 | 0 | methods |= WPS_CONFIG_ETHERNET; |
545 | 0 | if (os_strstr(str, "label")) |
546 | 0 | methods |= WPS_CONFIG_LABEL; |
547 | 0 | if (os_strstr(str, "display")) |
548 | 0 | methods |= WPS_CONFIG_DISPLAY; |
549 | 0 | if (os_strstr(str, "ext_nfc_token")) |
550 | 0 | methods |= WPS_CONFIG_EXT_NFC_TOKEN; |
551 | 0 | if (os_strstr(str, "int_nfc_token")) |
552 | 0 | methods |= WPS_CONFIG_INT_NFC_TOKEN; |
553 | 0 | if (os_strstr(str, "nfc_interface")) |
554 | 0 | methods |= WPS_CONFIG_NFC_INTERFACE; |
555 | 0 | if (os_strstr(str, "push_button")) |
556 | 0 | methods |= WPS_CONFIG_PUSHBUTTON; |
557 | 0 | if (os_strstr(str, "keypad")) |
558 | 0 | methods |= WPS_CONFIG_KEYPAD; |
559 | 0 | if (os_strstr(str, "virtual_display")) |
560 | 0 | methods |= WPS_CONFIG_VIRT_DISPLAY; |
561 | 0 | if (os_strstr(str, "physical_display")) |
562 | 0 | methods |= WPS_CONFIG_PHY_DISPLAY; |
563 | 0 | if (os_strstr(str, "virtual_push_button")) |
564 | 0 | methods |= WPS_CONFIG_VIRT_PUSHBUTTON; |
565 | 0 | if (os_strstr(str, "physical_push_button")) |
566 | 0 | methods |= WPS_CONFIG_PHY_PUSHBUTTON; |
567 | 0 | if (os_strstr(str, "p2ps")) |
568 | 0 | methods |= WPS_CONFIG_P2PS; |
569 | 0 | } |
570 | |
|
571 | 0 | return methods; |
572 | 0 | } |
573 | | |
574 | | |
575 | | struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) |
576 | 0 | { |
577 | 0 | struct wpabuf *msg; |
578 | |
|
579 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); |
580 | |
|
581 | 0 | msg = wpabuf_alloc(1000); |
582 | 0 | if (msg == NULL) |
583 | 0 | return NULL; |
584 | | |
585 | 0 | if (wps_build_version(msg) || |
586 | 0 | wps_build_msg_type(msg, WPS_WSC_ACK) || |
587 | 0 | wps_build_enrollee_nonce(wps, msg) || |
588 | 0 | wps_build_registrar_nonce(wps, msg) || |
589 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
590 | 0 | wpabuf_free(msg); |
591 | 0 | return NULL; |
592 | 0 | } |
593 | | |
594 | 0 | return msg; |
595 | 0 | } |
596 | | |
597 | | |
598 | | struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) |
599 | 0 | { |
600 | 0 | struct wpabuf *msg; |
601 | |
|
602 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); |
603 | |
|
604 | 0 | msg = wpabuf_alloc(1000); |
605 | 0 | if (msg == NULL) |
606 | 0 | return NULL; |
607 | | |
608 | 0 | if (wps_build_version(msg) || |
609 | 0 | wps_build_msg_type(msg, WPS_WSC_NACK) || |
610 | 0 | wps_build_enrollee_nonce(wps, msg) || |
611 | 0 | wps_build_registrar_nonce(wps, msg) || |
612 | 0 | wps_build_config_error(msg, wps->config_error) || |
613 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
614 | 0 | wpabuf_free(msg); |
615 | 0 | return NULL; |
616 | 0 | } |
617 | | |
618 | 0 | return msg; |
619 | 0 | } |
620 | | |
621 | | |
622 | | #ifdef CONFIG_WPS_NFC |
623 | | |
624 | | struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, |
625 | | struct wpabuf *dev_pw) |
626 | 0 | { |
627 | 0 | struct wpabuf *ret; |
628 | |
|
629 | 0 | if (pubkey == NULL || dev_pw == NULL) |
630 | 0 | return NULL; |
631 | | |
632 | 0 | ret = wps_build_nfc_pw_token(id, pubkey, dev_pw); |
633 | 0 | if (ndef && ret) { |
634 | 0 | struct wpabuf *tmp; |
635 | 0 | tmp = ndef_build_wifi(ret); |
636 | 0 | wpabuf_free(ret); |
637 | 0 | if (tmp == NULL) |
638 | 0 | return NULL; |
639 | 0 | ret = tmp; |
640 | 0 | } |
641 | | |
642 | 0 | return ret; |
643 | 0 | } |
644 | | |
645 | | |
646 | | int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) |
647 | 0 | { |
648 | 0 | struct wpabuf *priv = NULL, *pub = NULL; |
649 | 0 | void *dh_ctx; |
650 | |
|
651 | 0 | dh_ctx = dh5_init(&priv, &pub); |
652 | 0 | if (dh_ctx == NULL) |
653 | 0 | return -1; |
654 | 0 | pub = wpabuf_zeropad(pub, 192); |
655 | 0 | if (pub == NULL) { |
656 | 0 | wpabuf_free(priv); |
657 | 0 | dh5_free(dh_ctx); |
658 | 0 | return -1; |
659 | 0 | } |
660 | 0 | wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub); |
661 | 0 | dh5_free(dh_ctx); |
662 | |
|
663 | 0 | wpabuf_free(*pubkey); |
664 | 0 | *pubkey = pub; |
665 | 0 | wpabuf_clear_free(*privkey); |
666 | 0 | *privkey = priv; |
667 | |
|
668 | 0 | return 0; |
669 | 0 | } |
670 | | |
671 | | |
672 | | struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, |
673 | | struct wpabuf **privkey, |
674 | | struct wpabuf **dev_pw) |
675 | 0 | { |
676 | 0 | struct wpabuf *pw; |
677 | 0 | u16 val; |
678 | |
|
679 | 0 | pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); |
680 | 0 | if (pw == NULL) |
681 | 0 | return NULL; |
682 | | |
683 | 0 | if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN), |
684 | 0 | WPS_OOB_DEVICE_PASSWORD_LEN) || |
685 | 0 | random_get_bytes((u8 *) &val, sizeof(val))) { |
686 | 0 | wpabuf_free(pw); |
687 | 0 | return NULL; |
688 | 0 | } |
689 | | |
690 | 0 | if (wps_nfc_gen_dh(pubkey, privkey) < 0) { |
691 | 0 | wpabuf_free(pw); |
692 | 0 | return NULL; |
693 | 0 | } |
694 | | |
695 | 0 | *id = 0x10 + val % 0xfff0; |
696 | 0 | wpabuf_clear_free(*dev_pw); |
697 | 0 | *dev_pw = pw; |
698 | |
|
699 | 0 | return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw); |
700 | 0 | } |
701 | | |
702 | | |
703 | | struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, |
704 | | struct wpabuf *nfc_dh_pubkey) |
705 | 0 | { |
706 | 0 | struct wpabuf *msg; |
707 | 0 | void *len; |
708 | |
|
709 | 0 | if (ctx == NULL) |
710 | 0 | return NULL; |
711 | | |
712 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " |
713 | 0 | "handover request"); |
714 | |
|
715 | 0 | if (nfc_dh_pubkey == NULL) { |
716 | 0 | wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " |
717 | 0 | "configured"); |
718 | 0 | return NULL; |
719 | 0 | } |
720 | | |
721 | 0 | msg = wpabuf_alloc(1000); |
722 | 0 | if (msg == NULL) |
723 | 0 | return msg; |
724 | 0 | len = wpabuf_put(msg, 2); |
725 | |
|
726 | 0 | if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, |
727 | 0 | nfc_dh_pubkey, NULL, 0) || |
728 | 0 | wps_build_uuid_e(msg, ctx->uuid) || |
729 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
730 | 0 | wpabuf_free(msg); |
731 | 0 | return NULL; |
732 | 0 | } |
733 | | |
734 | 0 | WPA_PUT_BE16(len, wpabuf_len(msg) - 2); |
735 | |
|
736 | 0 | return msg; |
737 | 0 | } |
738 | | |
739 | | |
740 | | static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps) |
741 | 0 | { |
742 | 0 | wpa_printf(MSG_DEBUG, "WPS: * SSID"); |
743 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select", |
744 | 0 | wps->ssid, wps->ssid_len); |
745 | 0 | wpabuf_put_be16(msg, ATTR_SSID); |
746 | 0 | wpabuf_put_be16(msg, wps->ssid_len); |
747 | 0 | wpabuf_put_data(msg, wps->ssid, wps->ssid_len); |
748 | 0 | return 0; |
749 | 0 | } |
750 | | |
751 | | |
752 | | static int wps_build_ap_freq(struct wpabuf *msg, int freq) |
753 | 0 | { |
754 | 0 | enum hostapd_hw_mode mode; |
755 | 0 | u8 channel, rf_band; |
756 | 0 | u16 ap_channel; |
757 | |
|
758 | 0 | if (freq <= 0) |
759 | 0 | return 0; |
760 | | |
761 | 0 | mode = ieee80211_freq_to_chan(freq, &channel); |
762 | 0 | if (mode == NUM_HOSTAPD_MODES) |
763 | 0 | return 0; /* Unknown channel */ |
764 | | |
765 | 0 | if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B) |
766 | 0 | rf_band = WPS_RF_24GHZ; |
767 | 0 | else if (mode == HOSTAPD_MODE_IEEE80211A) |
768 | 0 | rf_band = WPS_RF_50GHZ; |
769 | 0 | else if (mode == HOSTAPD_MODE_IEEE80211AD) |
770 | 0 | rf_band = WPS_RF_60GHZ; |
771 | 0 | else |
772 | 0 | return 0; /* Unknown band */ |
773 | 0 | ap_channel = channel; |
774 | |
|
775 | 0 | if (wps_build_rf_bands_attr(msg, rf_band) || |
776 | 0 | wps_build_ap_channel(msg, ap_channel)) |
777 | 0 | return -1; |
778 | | |
779 | 0 | return 0; |
780 | 0 | } |
781 | | |
782 | | |
783 | | struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, |
784 | | struct wpabuf *nfc_dh_pubkey, |
785 | | const u8 *bssid, int freq) |
786 | 0 | { |
787 | 0 | struct wpabuf *msg; |
788 | 0 | void *len; |
789 | |
|
790 | 0 | if (ctx == NULL) |
791 | 0 | return NULL; |
792 | | |
793 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " |
794 | 0 | "handover select"); |
795 | |
|
796 | 0 | if (nfc_dh_pubkey == NULL) { |
797 | 0 | wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " |
798 | 0 | "configured"); |
799 | 0 | return NULL; |
800 | 0 | } |
801 | | |
802 | 0 | msg = wpabuf_alloc(1000); |
803 | 0 | if (msg == NULL) |
804 | 0 | return msg; |
805 | 0 | len = wpabuf_put(msg, 2); |
806 | |
|
807 | 0 | if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, |
808 | 0 | nfc_dh_pubkey, NULL, 0) || |
809 | 0 | wps_build_ssid(msg, ctx) || |
810 | 0 | wps_build_ap_freq(msg, freq) || |
811 | 0 | (bssid && wps_build_mac_addr(msg, bssid)) || |
812 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
813 | 0 | wpabuf_free(msg); |
814 | 0 | return NULL; |
815 | 0 | } |
816 | | |
817 | 0 | WPA_PUT_BE16(len, wpabuf_len(msg) - 2); |
818 | |
|
819 | 0 | return msg; |
820 | 0 | } |
821 | | |
822 | | |
823 | | struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, |
824 | | struct wpabuf *nfc_dh_pubkey) |
825 | 0 | { |
826 | 0 | struct wpabuf *msg; |
827 | |
|
828 | 0 | if (ctx == NULL) |
829 | 0 | return NULL; |
830 | | |
831 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " |
832 | 0 | "handover request (P2P)"); |
833 | |
|
834 | 0 | if (nfc_dh_pubkey == NULL) { |
835 | 0 | wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured"); |
836 | 0 | return NULL; |
837 | 0 | } |
838 | | |
839 | 0 | msg = wpabuf_alloc(1000); |
840 | 0 | if (msg == NULL) |
841 | 0 | return msg; |
842 | | |
843 | 0 | if (wps_build_manufacturer(&ctx->dev, msg) || |
844 | 0 | wps_build_model_name(&ctx->dev, msg) || |
845 | 0 | wps_build_model_number(&ctx->dev, msg) || |
846 | 0 | wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, |
847 | 0 | nfc_dh_pubkey, NULL, 0) || |
848 | 0 | wps_build_rf_bands(&ctx->dev, msg, 0) || |
849 | 0 | wps_build_serial_number(&ctx->dev, msg) || |
850 | 0 | wps_build_uuid_e(msg, ctx->uuid) || |
851 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
852 | 0 | wpabuf_free(msg); |
853 | 0 | return NULL; |
854 | 0 | } |
855 | | |
856 | 0 | return msg; |
857 | 0 | } |
858 | | |
859 | | |
860 | | struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, |
861 | | int nfc_dev_pw_id, |
862 | | struct wpabuf *nfc_dh_pubkey, |
863 | | struct wpabuf *nfc_dev_pw) |
864 | 0 | { |
865 | 0 | struct wpabuf *msg; |
866 | 0 | const u8 *dev_pw; |
867 | 0 | size_t dev_pw_len; |
868 | |
|
869 | 0 | if (ctx == NULL) |
870 | 0 | return NULL; |
871 | | |
872 | 0 | wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " |
873 | 0 | "handover select (P2P)"); |
874 | |
|
875 | 0 | if (nfc_dh_pubkey == NULL || |
876 | 0 | (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && |
877 | 0 | nfc_dev_pw == NULL)) { |
878 | 0 | wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " |
879 | 0 | "configured"); |
880 | 0 | return NULL; |
881 | 0 | } |
882 | | |
883 | 0 | msg = wpabuf_alloc(1000); |
884 | 0 | if (msg == NULL) |
885 | 0 | return msg; |
886 | | |
887 | 0 | if (nfc_dev_pw) { |
888 | 0 | dev_pw = wpabuf_head(nfc_dev_pw); |
889 | 0 | dev_pw_len = wpabuf_len(nfc_dev_pw); |
890 | 0 | } else { |
891 | 0 | dev_pw = NULL; |
892 | 0 | dev_pw_len = 0; |
893 | 0 | } |
894 | |
|
895 | 0 | if (wps_build_manufacturer(&ctx->dev, msg) || |
896 | 0 | wps_build_model_name(&ctx->dev, msg) || |
897 | 0 | wps_build_model_number(&ctx->dev, msg) || |
898 | 0 | wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey, |
899 | 0 | dev_pw, dev_pw_len) || |
900 | 0 | wps_build_rf_bands(&ctx->dev, msg, 0) || |
901 | 0 | wps_build_serial_number(&ctx->dev, msg) || |
902 | 0 | wps_build_uuid_e(msg, ctx->uuid) || |
903 | 0 | wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { |
904 | 0 | wpabuf_free(msg); |
905 | 0 | return NULL; |
906 | 0 | } |
907 | | |
908 | 0 | return msg; |
909 | 0 | } |
910 | | |
911 | | #endif /* CONFIG_WPS_NFC */ |