]>
Commit | Line | Data |
---|---|---|
7d7b8e96 JM |
1 | /* |
2 | * Wi-Fi Protected Setup - attribute building | |
3 | * Copyright (c) 2008, Jouni Malinen <j@w1.fi> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | */ | |
14 | ||
15 | #include "includes.h" | |
16 | ||
17 | #include "common.h" | |
03da66bd JM |
18 | #include "crypto/aes_wrap.h" |
19 | #include "crypto/crypto.h" | |
20 | #include "crypto/dh_group5.h" | |
21 | #include "crypto/sha256.h" | |
3642c431 | 22 | #include "crypto/random.h" |
6b633b4d | 23 | #include "common/ieee802_11_defs.h" |
7d7b8e96 JM |
24 | #include "wps_i.h" |
25 | ||
26 | ||
27 | int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) | |
28 | { | |
29 | struct wpabuf *pubkey; | |
30 | ||
31 | wpa_printf(MSG_DEBUG, "WPS: * Public Key"); | |
46bdb83a | 32 | wpabuf_free(wps->dh_privkey); |
d5e2b2d2 JM |
33 | if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) { |
34 | wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys"); | |
35 | wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey); | |
f042122a JM |
36 | wps->dh_ctx = wps->wps->dh_ctx; |
37 | wps->wps->dh_ctx = NULL; | |
d5e2b2d2 JM |
38 | pubkey = wpabuf_dup(wps->wps->dh_pubkey); |
39 | } else { | |
40 | wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); | |
41 | wps->dh_privkey = NULL; | |
f042122a JM |
42 | dh5_free(wps->dh_ctx); |
43 | wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey); | |
d5e2b2d2 JM |
44 | pubkey = wpabuf_zeropad(pubkey, 192); |
45 | } | |
f042122a | 46 | if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) { |
7d7b8e96 JM |
47 | wpa_printf(MSG_DEBUG, "WPS: Failed to initialize " |
48 | "Diffie-Hellman handshake"); | |
46bdb83a | 49 | wpabuf_free(pubkey); |
7d7b8e96 JM |
50 | return -1; |
51 | } | |
c01106f3 | 52 | wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); |
92afbe9d | 53 | wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey); |
7d7b8e96 JM |
54 | |
55 | wpabuf_put_be16(msg, ATTR_PUBLIC_KEY); | |
56 | wpabuf_put_be16(msg, wpabuf_len(pubkey)); | |
57 | wpabuf_put_buf(msg, pubkey); | |
58 | ||
59 | if (wps->registrar) { | |
60 | wpabuf_free(wps->dh_pubkey_r); | |
61 | wps->dh_pubkey_r = pubkey; | |
62 | } else { | |
63 | wpabuf_free(wps->dh_pubkey_e); | |
64 | wps->dh_pubkey_e = pubkey; | |
65 | } | |
66 | ||
67 | return 0; | |
68 | } | |
69 | ||
70 | ||
71 | int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type) | |
72 | { | |
73 | wpa_printf(MSG_DEBUG, "WPS: * Request Type"); | |
74 | wpabuf_put_be16(msg, ATTR_REQUEST_TYPE); | |
75 | wpabuf_put_be16(msg, 1); | |
76 | wpabuf_put_u8(msg, type); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | ||
ed7a09f9 JM |
81 | int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type) |
82 | { | |
83 | wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type); | |
84 | wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE); | |
85 | wpabuf_put_be16(msg, 1); | |
86 | wpabuf_put_u8(msg, type); | |
87 | return 0; | |
88 | } | |
89 | ||
90 | ||
7d7b8e96 JM |
91 | int wps_build_config_methods(struct wpabuf *msg, u16 methods) |
92 | { | |
93 | wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); | |
94 | wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); | |
95 | wpabuf_put_be16(msg, 2); | |
96 | wpabuf_put_be16(msg, methods); | |
97 | return 0; | |
98 | } | |
99 | ||
100 | ||
101 | int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid) | |
102 | { | |
103 | wpa_printf(MSG_DEBUG, "WPS: * UUID-E"); | |
104 | wpabuf_put_be16(msg, ATTR_UUID_E); | |
105 | wpabuf_put_be16(msg, WPS_UUID_LEN); | |
106 | wpabuf_put_data(msg, uuid, WPS_UUID_LEN); | |
107 | return 0; | |
108 | } | |
109 | ||
110 | ||
111 | int wps_build_dev_password_id(struct wpabuf *msg, u16 id) | |
112 | { | |
113 | wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id); | |
114 | wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); | |
115 | wpabuf_put_be16(msg, 2); | |
116 | wpabuf_put_be16(msg, id); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | ||
121 | int wps_build_config_error(struct wpabuf *msg, u16 err) | |
122 | { | |
123 | wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err); | |
124 | wpabuf_put_be16(msg, ATTR_CONFIG_ERROR); | |
125 | wpabuf_put_be16(msg, 2); | |
126 | wpabuf_put_be16(msg, err); | |
127 | return 0; | |
128 | } | |
129 | ||
130 | ||
131 | int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg) | |
132 | { | |
133 | u8 hash[SHA256_MAC_LEN]; | |
134 | const u8 *addr[2]; | |
135 | size_t len[2]; | |
136 | ||
137 | if (wps->last_msg == NULL) { | |
138 | wpa_printf(MSG_DEBUG, "WPS: Last message not available for " | |
139 | "building authenticator"); | |
140 | return -1; | |
141 | } | |
142 | ||
143 | /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*) | |
144 | * (M_curr* is M_curr without the Authenticator attribute) | |
145 | */ | |
146 | addr[0] = wpabuf_head(wps->last_msg); | |
147 | len[0] = wpabuf_len(wps->last_msg); | |
148 | addr[1] = wpabuf_head(msg); | |
149 | len[1] = wpabuf_len(msg); | |
150 | hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash); | |
151 | ||
152 | wpa_printf(MSG_DEBUG, "WPS: * Authenticator"); | |
153 | wpabuf_put_be16(msg, ATTR_AUTHENTICATOR); | |
154 | wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN); | |
155 | wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | ||
161 | int wps_build_version(struct wpabuf *msg) | |
162 | { | |
f439079e JM |
163 | /* |
164 | * Note: This attribute is deprecated and set to hardcoded 0x10 for | |
165 | * backwards compatibility reasons. The real version negotiation is | |
166 | * done with Version2. | |
167 | */ | |
168 | wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)"); | |
7d7b8e96 JM |
169 | wpabuf_put_be16(msg, ATTR_VERSION); |
170 | wpabuf_put_be16(msg, 1); | |
f439079e JM |
171 | wpabuf_put_u8(msg, 0x10); |
172 | return 0; | |
173 | } | |
174 | ||
175 | ||
fdc9eeb1 JM |
176 | int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, |
177 | const u8 *auth_macs, size_t auth_macs_count) | |
f439079e | 178 | { |
53587ec1 | 179 | #ifdef CONFIG_WPS2 |
fdc9eeb1 JM |
180 | u8 *len; |
181 | ||
182 | wpabuf_put_be16(msg, ATTR_VENDOR_EXT); | |
183 | len = wpabuf_put(msg, 2); /* to be filled */ | |
184 | wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA); | |
185 | ||
f439079e | 186 | wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION); |
fdc9eeb1 JM |
187 | wpabuf_put_u8(msg, WFA_ELEM_VERSION2); |
188 | wpabuf_put_u8(msg, 1); | |
7d7b8e96 | 189 | wpabuf_put_u8(msg, WPS_VERSION); |
fdc9eeb1 JM |
190 | |
191 | if (req_to_enroll) { | |
192 | wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)"); | |
193 | wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL); | |
194 | wpabuf_put_u8(msg, 1); | |
195 | wpabuf_put_u8(msg, 1); | |
196 | } | |
197 | ||
198 | if (auth_macs && auth_macs_count) { | |
199 | size_t i; | |
200 | wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)", | |
201 | (int) auth_macs_count); | |
202 | wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS); | |
203 | wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN); | |
204 | wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN); | |
205 | for (i = 0; i < auth_macs_count; i++) | |
206 | wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR, | |
207 | MAC2STR(&auth_macs[i * ETH_ALEN])); | |
208 | } | |
209 | ||
210 | WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); | |
211 | #endif /* CONFIG_WPS2 */ | |
212 | ||
b4e34f2f JM |
213 | #ifdef CONFIG_WPS_TESTING |
214 | if (WPS_VERSION > 0x20) { | |
215 | wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra " | |
216 | "attribute"); | |
217 | wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST); | |
218 | wpabuf_put_be16(msg, 1); | |
219 | wpabuf_put_u8(msg, 42); | |
220 | } | |
221 | #endif /* CONFIG_WPS_TESTING */ | |
7d7b8e96 JM |
222 | return 0; |
223 | } | |
224 | ||
225 | ||
226 | int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type) | |
227 | { | |
228 | wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type); | |
229 | wpabuf_put_be16(msg, ATTR_MSG_TYPE); | |
230 | wpabuf_put_be16(msg, 1); | |
231 | wpabuf_put_u8(msg, msg_type); | |
232 | return 0; | |
233 | } | |
234 | ||
235 | ||
236 | int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg) | |
237 | { | |
238 | wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce"); | |
239 | wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); | |
240 | wpabuf_put_be16(msg, WPS_NONCE_LEN); | |
241 | wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN); | |
242 | return 0; | |
243 | } | |
244 | ||
245 | ||
246 | int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg) | |
247 | { | |
248 | wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce"); | |
249 | wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); | |
250 | wpabuf_put_be16(msg, WPS_NONCE_LEN); | |
251 | wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN); | |
252 | return 0; | |
253 | } | |
254 | ||
255 | ||
256 | int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) | |
257 | { | |
7b23f0f3 JM |
258 | u16 auth_types = WPS_AUTH_TYPES; |
259 | #ifdef CONFIG_WPS2 | |
260 | auth_types &= ~WPS_AUTH_SHARED; | |
261 | #endif /* CONFIG_WPS2 */ | |
7d7b8e96 JM |
262 | wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); |
263 | wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); | |
264 | wpabuf_put_be16(msg, 2); | |
7b23f0f3 | 265 | wpabuf_put_be16(msg, auth_types); |
7d7b8e96 JM |
266 | return 0; |
267 | } | |
268 | ||
269 | ||
270 | int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) | |
271 | { | |
7b23f0f3 JM |
272 | u16 encr_types = WPS_ENCR_TYPES; |
273 | #ifdef CONFIG_WPS2 | |
274 | encr_types &= ~WPS_ENCR_WEP; | |
275 | #endif /* CONFIG_WPS2 */ | |
7d7b8e96 JM |
276 | wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); |
277 | wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); | |
278 | wpabuf_put_be16(msg, 2); | |
7b23f0f3 | 279 | wpabuf_put_be16(msg, encr_types); |
7d7b8e96 JM |
280 | return 0; |
281 | } | |
282 | ||
283 | ||
284 | int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg) | |
285 | { | |
286 | wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags"); | |
287 | wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS); | |
288 | wpabuf_put_be16(msg, 1); | |
289 | wpabuf_put_u8(msg, WPS_CONN_ESS); | |
290 | return 0; | |
291 | } | |
292 | ||
293 | ||
294 | int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg) | |
295 | { | |
296 | wpa_printf(MSG_DEBUG, "WPS: * Association State"); | |
297 | wpabuf_put_be16(msg, ATTR_ASSOC_STATE); | |
298 | wpabuf_put_be16(msg, 2); | |
299 | wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC); | |
300 | return 0; | |
301 | } | |
302 | ||
303 | ||
304 | int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg) | |
305 | { | |
306 | u8 hash[SHA256_MAC_LEN]; | |
307 | ||
308 | wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator"); | |
309 | hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg), | |
310 | wpabuf_len(msg), hash); | |
311 | ||
312 | wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH); | |
313 | wpabuf_put_be16(msg, WPS_KWA_LEN); | |
314 | wpabuf_put_data(msg, hash, WPS_KWA_LEN); | |
315 | return 0; | |
316 | } | |
317 | ||
318 | ||
319 | int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, | |
320 | struct wpabuf *plain) | |
321 | { | |
322 | size_t pad_len; | |
323 | const size_t block_size = 16; | |
324 | u8 *iv, *data; | |
325 | ||
326 | wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings"); | |
327 | ||
328 | /* PKCS#5 v2.0 pad */ | |
329 | pad_len = block_size - wpabuf_len(plain) % block_size; | |
330 | os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len); | |
331 | ||
332 | wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS); | |
333 | wpabuf_put_be16(msg, block_size + wpabuf_len(plain)); | |
334 | ||
335 | iv = wpabuf_put(msg, block_size); | |
3642c431 | 336 | if (random_get_bytes(iv, block_size) < 0) |
7d7b8e96 JM |
337 | return -1; |
338 | ||
339 | data = wpabuf_put(msg, 0); | |
340 | wpabuf_put_buf(msg, plain); | |
341 | if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain))) | |
342 | return -1; | |
343 | ||
344 | return 0; | |
345 | } | |
46bdb83a MH |
346 | |
347 | ||
116f7bb0 | 348 | #ifdef CONFIG_WPS_OOB |
46bdb83a MH |
349 | int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) |
350 | { | |
351 | size_t hash_len; | |
352 | const u8 *addr[1]; | |
353 | u8 pubkey_hash[WPS_HASH_LEN]; | |
354 | u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; | |
355 | ||
356 | wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); | |
357 | ||
358 | addr[0] = wpabuf_head(wps->dh_pubkey); | |
359 | hash_len = wpabuf_len(wps->dh_pubkey); | |
360 | sha256_vector(1, addr, &hash_len, pubkey_hash); | |
361 | ||
362 | if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { | |
363 | wpa_printf(MSG_ERROR, "WPS: device password id " | |
364 | "generation error"); | |
365 | return -1; | |
366 | } | |
367 | wps->oob_dev_pw_id |= 0x0010; | |
368 | ||
3642c431 JM |
369 | if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < |
370 | 0) { | |
46bdb83a MH |
371 | wpa_printf(MSG_ERROR, "WPS: OOB device password " |
372 | "generation error"); | |
373 | return -1; | |
374 | } | |
375 | ||
376 | wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); | |
377 | wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); | |
378 | wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); | |
379 | wpabuf_put_be16(msg, wps->oob_dev_pw_id); | |
380 | wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); | |
381 | ||
382 | wpa_snprintf_hex_uppercase( | |
383 | wpabuf_put(wps->oob_conf.dev_password, | |
384 | wpabuf_size(wps->oob_conf.dev_password)), | |
385 | wpabuf_size(wps->oob_conf.dev_password), | |
386 | dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); | |
387 | ||
388 | return 0; | |
389 | } | |
116f7bb0 | 390 | #endif /* CONFIG_WPS_OOB */ |
cfe1c3f1 JM |
391 | |
392 | ||
6b633b4d JM |
393 | /* Encapsulate WPS IE data with one (or more, if needed) IE headers */ |
394 | struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) | |
395 | { | |
396 | struct wpabuf *ie; | |
397 | const u8 *pos, *end; | |
398 | ||
399 | ie = wpabuf_alloc(wpabuf_len(data) + 100); | |
400 | if (ie == NULL) { | |
401 | wpabuf_free(data); | |
402 | return NULL; | |
403 | } | |
404 | ||
405 | pos = wpabuf_head(data); | |
406 | end = pos + wpabuf_len(data); | |
407 | ||
408 | while (end > pos) { | |
409 | size_t frag_len = end - pos; | |
410 | if (frag_len > 251) | |
411 | frag_len = 251; | |
412 | wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); | |
413 | wpabuf_put_u8(ie, 4 + frag_len); | |
414 | wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); | |
415 | wpabuf_put_data(ie, pos, frag_len); | |
416 | pos += frag_len; | |
417 | } | |
418 | ||
419 | wpabuf_free(data); | |
420 | ||
421 | return ie; | |
422 | } |