]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * EAP peer method: EAP-PSK (RFC 4764) | |
3 | * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | * |
8 | * Note: EAP-PSK is an EAP authentication method and as such, completely | |
9 | * different from WPA-PSK. This file is not needed for WPA-PSK functionality. | |
10 | */ | |
11 | ||
12 | #include "includes.h" | |
13 | ||
14 | #include "common.h" | |
03da66bd | 15 | #include "crypto/aes_wrap.h" |
3642c431 | 16 | #include "crypto/random.h" |
6fc6879b | 17 | #include "eap_common/eap_psk_common.h" |
03da66bd | 18 | #include "eap_i.h" |
6fc6879b JM |
19 | |
20 | ||
21 | struct eap_psk_data { | |
22 | enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; | |
23 | u8 rand_p[EAP_PSK_RAND_LEN]; | |
24 | u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; | |
25 | u8 *id_s, *id_p; | |
26 | size_t id_s_len, id_p_len; | |
27 | u8 msk[EAP_MSK_LEN]; | |
28 | u8 emsk[EAP_EMSK_LEN]; | |
29 | }; | |
30 | ||
31 | ||
32 | static void * eap_psk_init(struct eap_sm *sm) | |
33 | { | |
34 | struct eap_psk_data *data; | |
35 | const u8 *identity, *password; | |
36 | size_t identity_len, password_len; | |
37 | ||
38 | password = eap_get_config_password(sm, &password_len); | |
39 | if (!password || password_len != 16) { | |
40 | wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " | |
41 | "configured"); | |
42 | return NULL; | |
43 | } | |
44 | ||
45 | data = os_zalloc(sizeof(*data)); | |
46 | if (data == NULL) | |
47 | return NULL; | |
48 | if (eap_psk_key_setup(password, data->ak, data->kdk)) { | |
49 | os_free(data); | |
50 | return NULL; | |
51 | } | |
52 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); | |
53 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); | |
54 | data->state = PSK_INIT; | |
55 | ||
56 | identity = eap_get_config_identity(sm, &identity_len); | |
57 | if (identity) { | |
58 | data->id_p = os_malloc(identity_len); | |
59 | if (data->id_p) | |
60 | os_memcpy(data->id_p, identity, identity_len); | |
61 | data->id_p_len = identity_len; | |
62 | } | |
63 | if (data->id_p == NULL) { | |
64 | wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); | |
65 | os_free(data); | |
66 | return NULL; | |
67 | } | |
68 | ||
69 | return data; | |
70 | } | |
71 | ||
72 | ||
73 | static void eap_psk_deinit(struct eap_sm *sm, void *priv) | |
74 | { | |
75 | struct eap_psk_data *data = priv; | |
76 | os_free(data->id_s); | |
77 | os_free(data->id_p); | |
78 | os_free(data); | |
79 | } | |
80 | ||
81 | ||
82 | static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, | |
83 | struct eap_method_ret *ret, | |
84 | const struct wpabuf *reqData) | |
85 | { | |
86 | const struct eap_psk_hdr_1 *hdr1; | |
87 | struct eap_psk_hdr_2 *hdr2; | |
88 | struct wpabuf *resp; | |
89 | u8 *buf, *pos; | |
90 | size_t buflen, len; | |
91 | const u8 *cpos; | |
92 | ||
93 | wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); | |
94 | ||
95 | cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); | |
96 | hdr1 = (const struct eap_psk_hdr_1 *) cpos; | |
97 | if (cpos == NULL || len < sizeof(*hdr1)) { | |
98 | wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " | |
99 | "length (%lu; expected %lu or more)", | |
100 | (unsigned long) len, | |
101 | (unsigned long) sizeof(*hdr1)); | |
102 | ret->ignore = TRUE; | |
103 | return NULL; | |
104 | } | |
105 | wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); | |
106 | if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { | |
107 | wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", | |
108 | EAP_PSK_FLAGS_GET_T(hdr1->flags)); | |
109 | ret->methodState = METHOD_DONE; | |
110 | ret->decision = DECISION_FAIL; | |
111 | return NULL; | |
112 | } | |
113 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, | |
114 | EAP_PSK_RAND_LEN); | |
115 | os_free(data->id_s); | |
116 | data->id_s_len = len - sizeof(*hdr1); | |
117 | data->id_s = os_malloc(data->id_s_len); | |
118 | if (data->id_s == NULL) { | |
119 | wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " | |
120 | "ID_S (len=%lu)", (unsigned long) data->id_s_len); | |
121 | ret->ignore = TRUE; | |
122 | return NULL; | |
123 | } | |
124 | os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); | |
125 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", | |
126 | data->id_s, data->id_s_len); | |
127 | ||
3642c431 | 128 | if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { |
6fc6879b JM |
129 | wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); |
130 | ret->ignore = TRUE; | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, | |
135 | sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, | |
136 | eap_get_id(reqData)); | |
137 | if (resp == NULL) | |
138 | return NULL; | |
139 | hdr2 = wpabuf_put(resp, sizeof(*hdr2)); | |
140 | hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ | |
141 | os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); | |
142 | os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); | |
143 | wpabuf_put_data(resp, data->id_p, data->id_p_len); | |
144 | /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ | |
145 | buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; | |
146 | buf = os_malloc(buflen); | |
147 | if (buf == NULL) { | |
148 | wpabuf_free(resp); | |
149 | return NULL; | |
150 | } | |
151 | os_memcpy(buf, data->id_p, data->id_p_len); | |
152 | pos = buf + data->id_p_len; | |
153 | os_memcpy(pos, data->id_s, data->id_s_len); | |
154 | pos += data->id_s_len; | |
155 | os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); | |
156 | pos += EAP_PSK_RAND_LEN; | |
157 | os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); | |
158 | if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { | |
159 | os_free(buf); | |
160 | wpabuf_free(resp); | |
161 | return NULL; | |
162 | } | |
163 | os_free(buf); | |
164 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, | |
165 | EAP_PSK_RAND_LEN); | |
166 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); | |
167 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", | |
168 | data->id_p, data->id_p_len); | |
169 | ||
170 | data->state = PSK_MAC_SENT; | |
171 | ||
172 | return resp; | |
173 | } | |
174 | ||
175 | ||
176 | static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, | |
177 | struct eap_method_ret *ret, | |
178 | const struct wpabuf *reqData) | |
179 | { | |
180 | const struct eap_psk_hdr_3 *hdr3; | |
181 | struct eap_psk_hdr_4 *hdr4; | |
182 | struct wpabuf *resp; | |
183 | u8 *buf, *rpchannel, nonce[16], *decrypted; | |
184 | const u8 *pchannel, *tag, *msg; | |
185 | u8 mac[EAP_PSK_MAC_LEN]; | |
186 | size_t buflen, left, data_len, len, plen; | |
187 | int failed = 0; | |
188 | const u8 *pos; | |
189 | ||
190 | wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); | |
191 | ||
192 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, | |
193 | reqData, &len); | |
194 | hdr3 = (const struct eap_psk_hdr_3 *) pos; | |
195 | if (pos == NULL || len < sizeof(*hdr3)) { | |
196 | wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " | |
197 | "length (%lu; expected %lu or more)", | |
198 | (unsigned long) len, | |
199 | (unsigned long) sizeof(*hdr3)); | |
200 | ret->ignore = TRUE; | |
201 | return NULL; | |
202 | } | |
203 | left = len - sizeof(*hdr3); | |
204 | pchannel = (const u8 *) (hdr3 + 1); | |
205 | wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); | |
206 | if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { | |
207 | wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", | |
208 | EAP_PSK_FLAGS_GET_T(hdr3->flags)); | |
209 | ret->methodState = METHOD_DONE; | |
210 | ret->decision = DECISION_FAIL; | |
211 | return NULL; | |
212 | } | |
213 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, | |
214 | EAP_PSK_RAND_LEN); | |
215 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); | |
216 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); | |
217 | ||
218 | if (left < 4 + 16 + 1) { | |
219 | wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " | |
220 | "third message (len=%lu, expected 21)", | |
221 | (unsigned long) left); | |
222 | ret->ignore = TRUE; | |
223 | return NULL; | |
224 | } | |
225 | ||
226 | /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ | |
227 | buflen = data->id_s_len + EAP_PSK_RAND_LEN; | |
228 | buf = os_malloc(buflen); | |
229 | if (buf == NULL) | |
230 | return NULL; | |
231 | os_memcpy(buf, data->id_s, data->id_s_len); | |
232 | os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); | |
233 | if (omac1_aes_128(data->ak, buf, buflen, mac)) { | |
234 | os_free(buf); | |
235 | return NULL; | |
236 | } | |
237 | os_free(buf); | |
238 | if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { | |
239 | wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " | |
240 | "message"); | |
241 | ret->methodState = METHOD_DONE; | |
242 | ret->decision = DECISION_FAIL; | |
243 | return NULL; | |
244 | } | |
245 | wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); | |
246 | ||
247 | if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, | |
248 | data->msk, data->emsk)) { | |
249 | ret->methodState = METHOD_DONE; | |
250 | ret->decision = DECISION_FAIL; | |
251 | return NULL; | |
252 | } | |
253 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); | |
254 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); | |
255 | wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); | |
256 | ||
257 | os_memset(nonce, 0, 12); | |
258 | os_memcpy(nonce + 12, pchannel, 4); | |
259 | pchannel += 4; | |
260 | left -= 4; | |
261 | ||
262 | tag = pchannel; | |
263 | pchannel += 16; | |
264 | left -= 16; | |
265 | ||
266 | msg = pchannel; | |
267 | ||
268 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", | |
269 | nonce, sizeof(nonce)); | |
270 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", | |
271 | wpabuf_head(reqData), 5); | |
272 | wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); | |
273 | ||
274 | decrypted = os_malloc(left); | |
275 | if (decrypted == NULL) { | |
276 | ret->methodState = METHOD_DONE; | |
277 | ret->decision = DECISION_FAIL; | |
278 | return NULL; | |
279 | } | |
280 | os_memcpy(decrypted, msg, left); | |
281 | ||
282 | if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), | |
283 | wpabuf_head(reqData), | |
284 | sizeof(struct eap_hdr) + 1 + | |
285 | sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, | |
286 | left, tag)) { | |
287 | wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); | |
288 | os_free(decrypted); | |
289 | return NULL; | |
290 | } | |
291 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", | |
292 | decrypted, left); | |
293 | ||
294 | /* Verify R flag */ | |
295 | switch (decrypted[0] >> 6) { | |
296 | case EAP_PSK_R_FLAG_CONT: | |
297 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); | |
298 | failed = 1; | |
299 | break; | |
300 | case EAP_PSK_R_FLAG_DONE_SUCCESS: | |
301 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); | |
302 | break; | |
303 | case EAP_PSK_R_FLAG_DONE_FAILURE: | |
304 | wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); | |
305 | wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " | |
306 | "authentication"); | |
307 | failed = 1; | |
308 | break; | |
309 | } | |
310 | ||
311 | data_len = 1; | |
312 | if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) | |
313 | data_len++; | |
314 | plen = sizeof(*hdr4) + 4 + 16 + data_len; | |
315 | resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, | |
316 | EAP_CODE_RESPONSE, eap_get_id(reqData)); | |
317 | if (resp == NULL) { | |
318 | os_free(decrypted); | |
319 | return NULL; | |
320 | } | |
321 | hdr4 = wpabuf_put(resp, sizeof(*hdr4)); | |
322 | hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ | |
323 | os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); | |
324 | rpchannel = wpabuf_put(resp, 4 + 16 + data_len); | |
325 | ||
326 | /* nonce++ */ | |
327 | inc_byte_array(nonce, sizeof(nonce)); | |
328 | os_memcpy(rpchannel, nonce + 12, 4); | |
329 | ||
330 | if (decrypted[0] & EAP_PSK_E_FLAG) { | |
331 | wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); | |
332 | failed = 1; | |
333 | rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | | |
334 | EAP_PSK_E_FLAG; | |
335 | if (left > 1) { | |
336 | /* Add empty EXT_Payload with same EXT_Type */ | |
337 | rpchannel[4 + 16 + 1] = decrypted[1]; | |
338 | } | |
339 | } else if (failed) | |
340 | rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; | |
341 | else | |
342 | rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; | |
343 | ||
344 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", | |
345 | rpchannel + 4 + 16, data_len); | |
346 | if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), | |
347 | wpabuf_head(resp), | |
348 | sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), | |
349 | rpchannel + 4 + 16, data_len, rpchannel + 4)) { | |
350 | os_free(decrypted); | |
351 | wpabuf_free(resp); | |
352 | return NULL; | |
353 | } | |
354 | wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", | |
355 | rpchannel, 4 + 16 + data_len); | |
356 | ||
357 | wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", | |
358 | failed ? "un" : ""); | |
359 | data->state = PSK_DONE; | |
360 | ret->methodState = METHOD_DONE; | |
361 | ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; | |
362 | ||
363 | os_free(decrypted); | |
364 | ||
365 | return resp; | |
366 | } | |
367 | ||
368 | ||
369 | static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, | |
370 | struct eap_method_ret *ret, | |
371 | const struct wpabuf *reqData) | |
372 | { | |
373 | struct eap_psk_data *data = priv; | |
374 | const u8 *pos; | |
375 | struct wpabuf *resp = NULL; | |
376 | size_t len; | |
377 | ||
378 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); | |
379 | if (pos == NULL) { | |
380 | ret->ignore = TRUE; | |
381 | return NULL; | |
382 | } | |
383 | ||
384 | ret->ignore = FALSE; | |
385 | ret->methodState = METHOD_MAY_CONT; | |
386 | ret->decision = DECISION_FAIL; | |
387 | ret->allowNotifications = TRUE; | |
388 | ||
389 | switch (data->state) { | |
390 | case PSK_INIT: | |
391 | resp = eap_psk_process_1(data, ret, reqData); | |
392 | break; | |
393 | case PSK_MAC_SENT: | |
394 | resp = eap_psk_process_3(data, ret, reqData); | |
395 | break; | |
396 | case PSK_DONE: | |
397 | wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " | |
398 | "unexpected message"); | |
399 | ret->ignore = TRUE; | |
400 | return NULL; | |
401 | } | |
402 | ||
403 | if (ret->methodState == METHOD_DONE) { | |
404 | ret->allowNotifications = FALSE; | |
405 | } | |
406 | ||
407 | return resp; | |
408 | } | |
409 | ||
410 | ||
411 | static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) | |
412 | { | |
413 | struct eap_psk_data *data = priv; | |
414 | return data->state == PSK_DONE; | |
415 | } | |
416 | ||
417 | ||
418 | static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
419 | { | |
420 | struct eap_psk_data *data = priv; | |
421 | u8 *key; | |
422 | ||
423 | if (data->state != PSK_DONE) | |
424 | return NULL; | |
425 | ||
426 | key = os_malloc(EAP_MSK_LEN); | |
427 | if (key == NULL) | |
428 | return NULL; | |
429 | ||
430 | *len = EAP_MSK_LEN; | |
431 | os_memcpy(key, data->msk, EAP_MSK_LEN); | |
432 | ||
433 | return key; | |
434 | } | |
435 | ||
436 | ||
437 | static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) | |
438 | { | |
439 | struct eap_psk_data *data = priv; | |
440 | u8 *key; | |
441 | ||
442 | if (data->state != PSK_DONE) | |
443 | return NULL; | |
444 | ||
445 | key = os_malloc(EAP_EMSK_LEN); | |
446 | if (key == NULL) | |
447 | return NULL; | |
448 | ||
449 | *len = EAP_EMSK_LEN; | |
450 | os_memcpy(key, data->emsk, EAP_EMSK_LEN); | |
451 | ||
452 | return key; | |
453 | } | |
454 | ||
455 | ||
456 | int eap_peer_psk_register(void) | |
457 | { | |
458 | struct eap_method *eap; | |
459 | int ret; | |
460 | ||
461 | eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, | |
462 | EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); | |
463 | if (eap == NULL) | |
464 | return -1; | |
465 | ||
466 | eap->init = eap_psk_init; | |
467 | eap->deinit = eap_psk_deinit; | |
468 | eap->process = eap_psk_process; | |
469 | eap->isKeyAvailable = eap_psk_isKeyAvailable; | |
470 | eap->getKey = eap_psk_getKey; | |
471 | eap->get_emsk = eap_psk_get_emsk; | |
472 | ||
473 | ret = eap_peer_method_register(eap); | |
474 | if (ret) | |
475 | eap_peer_method_free(eap); | |
476 | return ret; | |
477 | } |