xref: /freebsd/contrib/wpa/src/eap_peer/eap_gpsk.c (revision a0ee8cc6)
1 /*
2  * EAP peer method: EAP-GPSK (RFC 5433)
3  * Copyright (c) 2006-2014, 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 "crypto/random.h"
13 #include "eap_peer/eap_i.h"
14 #include "eap_common/eap_gpsk_common.h"
15 
16 struct eap_gpsk_data {
17 	enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
18 	u8 rand_server[EAP_GPSK_RAND_LEN];
19 	u8 rand_peer[EAP_GPSK_RAND_LEN];
20 	u8 msk[EAP_MSK_LEN];
21 	u8 emsk[EAP_EMSK_LEN];
22 	u8 sk[EAP_GPSK_MAX_SK_LEN];
23 	size_t sk_len;
24 	u8 pk[EAP_GPSK_MAX_PK_LEN];
25 	size_t pk_len;
26 	u8 session_id[128];
27 	size_t id_len;
28 	u8 *id_peer;
29 	size_t id_peer_len;
30 	u8 *id_server;
31 	size_t id_server_len;
32 	int vendor; /* CSuite/Specifier */
33 	int specifier; /* CSuite/Specifier */
34 	u8 *psk;
35 	size_t psk_len;
36 	u16 forced_cipher; /* force cipher or 0 to allow all supported */
37 };
38 
39 
40 static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
41 					    u8 identifier,
42 					    const u8 *csuite_list,
43 					    size_t csuite_list_len);
44 static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
45 					    u8 identifier);
46 
47 
48 #ifndef CONFIG_NO_STDOUT_DEBUG
49 static const char * eap_gpsk_state_txt(int state)
50 {
51 	switch (state) {
52 	case GPSK_1:
53 		return "GPSK-1";
54 	case GPSK_3:
55 		return "GPSK-3";
56 	case SUCCESS:
57 		return "SUCCESS";
58 	case FAILURE:
59 		return "FAILURE";
60 	default:
61 		return "?";
62 	}
63 }
64 #endif /* CONFIG_NO_STDOUT_DEBUG */
65 
66 
67 static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
68 {
69 	wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
70 		   eap_gpsk_state_txt(data->state),
71 		   eap_gpsk_state_txt(state));
72 	data->state = state;
73 }
74 
75 
76 static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
77 
78 
79 static void * eap_gpsk_init(struct eap_sm *sm)
80 {
81 	struct eap_gpsk_data *data;
82 	const u8 *identity, *password;
83 	size_t identity_len, password_len;
84 	const char *phase1;
85 
86 	password = eap_get_config_password(sm, &password_len);
87 	if (password == NULL) {
88 		wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
89 		return NULL;
90 	}
91 
92 	data = os_zalloc(sizeof(*data));
93 	if (data == NULL)
94 		return NULL;
95 	data->state = GPSK_1;
96 
97 	identity = eap_get_config_identity(sm, &identity_len);
98 	if (identity) {
99 		data->id_peer = os_malloc(identity_len);
100 		if (data->id_peer == NULL) {
101 			eap_gpsk_deinit(sm, data);
102 			return NULL;
103 		}
104 		os_memcpy(data->id_peer, identity, identity_len);
105 		data->id_peer_len = identity_len;
106 	}
107 
108 	phase1 = eap_get_config_phase1(sm);
109 	if (phase1) {
110 		const char *pos;
111 
112 		pos = os_strstr(phase1, "cipher=");
113 		if (pos) {
114 			data->forced_cipher = atoi(pos + 7);
115 			wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u",
116 				   data->forced_cipher);
117 		}
118 	}
119 
120 	data->psk = os_malloc(password_len);
121 	if (data->psk == NULL) {
122 		eap_gpsk_deinit(sm, data);
123 		return NULL;
124 	}
125 	os_memcpy(data->psk, password, password_len);
126 	data->psk_len = password_len;
127 
128 	return data;
129 }
130 
131 
132 static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
133 {
134 	struct eap_gpsk_data *data = priv;
135 	os_free(data->id_server);
136 	os_free(data->id_peer);
137 	if (data->psk) {
138 		os_memset(data->psk, 0, data->psk_len);
139 		os_free(data->psk);
140 	}
141 	bin_clear_free(data, sizeof(*data));
142 }
143 
144 
145 static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
146 					     const u8 *pos, const u8 *end)
147 {
148 	u16 alen;
149 
150 	if (end - pos < 2) {
151 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
152 		return NULL;
153 	}
154 	alen = WPA_GET_BE16(pos);
155 	pos += 2;
156 	if (end - pos < alen) {
157 		wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
158 		return NULL;
159 	}
160 	os_free(data->id_server);
161 	data->id_server = os_malloc(alen);
162 	if (data->id_server == NULL) {
163 		wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
164 		return NULL;
165 	}
166 	os_memcpy(data->id_server, pos, alen);
167 	data->id_server_len = alen;
168 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
169 			  data->id_server, data->id_server_len);
170 	pos += alen;
171 
172 	return pos;
173 }
174 
175 
176 static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
177 					       const u8 *pos, const u8 *end)
178 {
179 	if (pos == NULL)
180 		return NULL;
181 
182 	if (end - pos < EAP_GPSK_RAND_LEN) {
183 		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
184 		return NULL;
185 	}
186 	os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
187 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
188 		    data->rand_server, EAP_GPSK_RAND_LEN);
189 	pos += EAP_GPSK_RAND_LEN;
190 
191 	return pos;
192 }
193 
194 
195 static int eap_gpsk_select_csuite(struct eap_sm *sm,
196 				  struct eap_gpsk_data *data,
197 				  const u8 *csuite_list,
198 				  size_t csuite_list_len)
199 {
200 	struct eap_gpsk_csuite *csuite;
201 	int i, count;
202 
203 	count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
204 	data->vendor = EAP_GPSK_VENDOR_IETF;
205 	data->specifier = EAP_GPSK_CIPHER_RESERVED;
206 	csuite = (struct eap_gpsk_csuite *) csuite_list;
207 	for (i = 0; i < count; i++) {
208 		int vendor, specifier;
209 		vendor = WPA_GET_BE32(csuite->vendor);
210 		specifier = WPA_GET_BE16(csuite->specifier);
211 		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
212 			   i, vendor, specifier);
213 		if (data->vendor == EAP_GPSK_VENDOR_IETF &&
214 		    data->specifier == EAP_GPSK_CIPHER_RESERVED &&
215 		    eap_gpsk_supported_ciphersuite(vendor, specifier) &&
216 		    (!data->forced_cipher || data->forced_cipher == specifier))
217 		{
218 			data->vendor = vendor;
219 			data->specifier = specifier;
220 		}
221 		csuite++;
222 	}
223 	if (data->vendor == EAP_GPSK_VENDOR_IETF &&
224 	    data->specifier == EAP_GPSK_CIPHER_RESERVED) {
225 		wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
226 			"ciphersuite found");
227 		return -1;
228 	}
229 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
230 		   data->vendor, data->specifier);
231 
232 	return 0;
233 }
234 
235 
236 static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
237 					       struct eap_gpsk_data *data,
238 					       const u8 **list,
239 					       size_t *list_len,
240 					       const u8 *pos, const u8 *end)
241 {
242 	size_t len;
243 
244 	if (pos == NULL)
245 		return NULL;
246 
247 	if (end - pos < 2) {
248 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
249 		return NULL;
250 	}
251 	len = WPA_GET_BE16(pos);
252 	pos += 2;
253 	if (len > (size_t) (end - pos)) {
254 		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
255 		return NULL;
256 	}
257 	if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
258 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
259 			   (unsigned long) len);
260 		return NULL;
261 	}
262 
263 	if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
264 		return NULL;
265 
266 	*list = pos;
267 	*list_len = len;
268 	pos += len;
269 
270 	return pos;
271 }
272 
273 
274 static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
275 					       struct eap_gpsk_data *data,
276 					       struct eap_method_ret *ret,
277 					       u8 identifier,
278 					       const u8 *payload,
279 					       size_t payload_len)
280 {
281 	size_t csuite_list_len;
282 	const u8 *csuite_list, *pos, *end;
283 	struct wpabuf *resp;
284 
285 	if (data->state != GPSK_1) {
286 		ret->ignore = TRUE;
287 		return NULL;
288 	}
289 
290 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
291 
292 	end = payload + payload_len;
293 
294 	pos = eap_gpsk_process_id_server(data, payload, end);
295 	pos = eap_gpsk_process_rand_server(data, pos, end);
296 	pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
297 					   &csuite_list_len, pos, end);
298 	if (pos == NULL) {
299 		ret->methodState = METHOD_DONE;
300 		eap_gpsk_state(data, FAILURE);
301 		return NULL;
302 	}
303 
304 	resp = eap_gpsk_send_gpsk_2(data, identifier,
305 				    csuite_list, csuite_list_len);
306 	if (resp == NULL)
307 		return NULL;
308 
309 	eap_gpsk_state(data, GPSK_3);
310 
311 	return resp;
312 }
313 
314 
315 static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
316 					    u8 identifier,
317 					    const u8 *csuite_list,
318 					    size_t csuite_list_len)
319 {
320 	struct wpabuf *resp;
321 	size_t len, miclen;
322 	u8 *rpos, *start;
323 	struct eap_gpsk_csuite *csuite;
324 
325 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
326 
327 	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
328 	len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
329 		2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
330 		sizeof(struct eap_gpsk_csuite) + 2 + miclen;
331 
332 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
333 			     EAP_CODE_RESPONSE, identifier);
334 	if (resp == NULL)
335 		return NULL;
336 
337 	wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
338 	start = wpabuf_put(resp, 0);
339 
340 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
341 			  data->id_peer, data->id_peer_len);
342 	wpabuf_put_be16(resp, data->id_peer_len);
343 	wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
344 
345 	wpabuf_put_be16(resp, data->id_server_len);
346 	wpabuf_put_data(resp, data->id_server, data->id_server_len);
347 
348 	if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
349 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
350 			   "for RAND_Peer");
351 		eap_gpsk_state(data, FAILURE);
352 		wpabuf_free(resp);
353 		return NULL;
354 	}
355 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
356 		    data->rand_peer, EAP_GPSK_RAND_LEN);
357 	wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
358 	wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
359 
360 	wpabuf_put_be16(resp, csuite_list_len);
361 	wpabuf_put_data(resp, csuite_list, csuite_list_len);
362 
363 	csuite = wpabuf_put(resp, sizeof(*csuite));
364 	WPA_PUT_BE32(csuite->vendor, data->vendor);
365 	WPA_PUT_BE16(csuite->specifier, data->specifier);
366 
367 	if (eap_gpsk_derive_keys(data->psk, data->psk_len,
368 				 data->vendor, data->specifier,
369 				 data->rand_peer, data->rand_server,
370 				 data->id_peer, data->id_peer_len,
371 				 data->id_server, data->id_server_len,
372 				 data->msk, data->emsk,
373 				 data->sk, &data->sk_len,
374 				 data->pk, &data->pk_len) < 0) {
375 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
376 		eap_gpsk_state(data, FAILURE);
377 		wpabuf_free(resp);
378 		return NULL;
379 	}
380 
381 	if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
382 				       data->vendor, data->specifier,
383 				       data->rand_peer, data->rand_server,
384 				       data->id_peer, data->id_peer_len,
385 				       data->id_server, data->id_server_len,
386 				       EAP_TYPE_GPSK,
387 				       data->session_id, &data->id_len) < 0) {
388 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
389 		eap_gpsk_state(data, FAILURE);
390 		wpabuf_free(resp);
391 		return NULL;
392 	}
393 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
394 		    data->session_id, data->id_len);
395 
396 	/* No PD_Payload_1 */
397 	wpabuf_put_be16(resp, 0);
398 
399 	rpos = wpabuf_put(resp, miclen);
400 	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
401 				 data->specifier, start, rpos - start, rpos) <
402 	    0) {
403 		eap_gpsk_state(data, FAILURE);
404 		wpabuf_free(resp);
405 		return NULL;
406 	}
407 
408 	return resp;
409 }
410 
411 
412 static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
413 					 const u8 *pos, const u8 *end)
414 {
415 	if (end - pos < EAP_GPSK_RAND_LEN) {
416 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
417 			   "RAND_Peer");
418 		return NULL;
419 	}
420 	if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
421 		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
422 			   "GPSK-3 did not match");
423 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
424 			    data->rand_peer, EAP_GPSK_RAND_LEN);
425 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
426 			    pos, EAP_GPSK_RAND_LEN);
427 		return NULL;
428 	}
429 	pos += EAP_GPSK_RAND_LEN;
430 
431 	if (end - pos < EAP_GPSK_RAND_LEN) {
432 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
433 			   "RAND_Server");
434 		return NULL;
435 	}
436 	if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
437 		wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
438 			   "GPSK-3 did not match");
439 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
440 			    data->rand_server, EAP_GPSK_RAND_LEN);
441 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
442 			    pos, EAP_GPSK_RAND_LEN);
443 		return NULL;
444 	}
445 	pos += EAP_GPSK_RAND_LEN;
446 
447 	return pos;
448 }
449 
450 
451 static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
452 					      const u8 *pos, const u8 *end)
453 {
454 	size_t len;
455 
456 	if (pos == NULL)
457 		return NULL;
458 
459 	if (end - pos < (int) 2) {
460 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
461 			   "length(ID_Server)");
462 		return NULL;
463 	}
464 
465 	len = WPA_GET_BE16(pos);
466 	pos += 2;
467 
468 	if (end - pos < (int) len) {
469 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
470 			   "ID_Server");
471 		return NULL;
472 	}
473 
474 	if (len != data->id_server_len ||
475 	    os_memcmp(pos, data->id_server, len) != 0) {
476 		wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
477 			   "the one used in GPSK-1");
478 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
479 				  data->id_server, data->id_server_len);
480 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
481 				  pos, len);
482 		return NULL;
483 	}
484 
485 	pos += len;
486 
487 	return pos;
488 }
489 
490 
491 static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
492 					   const u8 *pos, const u8 *end)
493 {
494 	int vendor, specifier;
495 	const struct eap_gpsk_csuite *csuite;
496 
497 	if (pos == NULL)
498 		return NULL;
499 
500 	if (end - pos < (int) sizeof(*csuite)) {
501 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
502 			   "CSuite_Sel");
503 		return NULL;
504 	}
505 	csuite = (const struct eap_gpsk_csuite *) pos;
506 	vendor = WPA_GET_BE32(csuite->vendor);
507 	specifier = WPA_GET_BE16(csuite->specifier);
508 	pos += sizeof(*csuite);
509 	if (vendor != data->vendor || specifier != data->specifier) {
510 		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
511 			   "match with the one sent in GPSK-2 (%d:%d)",
512 			   vendor, specifier, data->vendor, data->specifier);
513 		return NULL;
514 	}
515 
516 	return pos;
517 }
518 
519 
520 static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
521 						 const u8 *pos, const u8 *end)
522 {
523 	u16 alen;
524 
525 	if (pos == NULL)
526 		return NULL;
527 
528 	if (end - pos < 2) {
529 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
530 			   "PD_Payload_2 length");
531 		return NULL;
532 	}
533 	alen = WPA_GET_BE16(pos);
534 	pos += 2;
535 	if (end - pos < alen) {
536 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
537 			   "%d-octet PD_Payload_2", alen);
538 		return NULL;
539 	}
540 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
541 	pos += alen;
542 
543 	return pos;
544 }
545 
546 
547 static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
548 					       const u8 *payload,
549 					       const u8 *pos, const u8 *end)
550 {
551 	size_t miclen;
552 	u8 mic[EAP_GPSK_MAX_MIC_LEN];
553 
554 	if (pos == NULL)
555 		return NULL;
556 
557 	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
558 	if (end - pos < (int) miclen) {
559 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
560 			   "(left=%lu miclen=%lu)",
561 			   (unsigned long) (end - pos),
562 			   (unsigned long) miclen);
563 		return NULL;
564 	}
565 	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
566 				 data->specifier, payload, pos - payload, mic)
567 	    < 0) {
568 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
569 		return NULL;
570 	}
571 	if (os_memcmp_const(mic, pos, miclen) != 0) {
572 		wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
573 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
574 		wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
575 		return NULL;
576 	}
577 	pos += miclen;
578 
579 	return pos;
580 }
581 
582 
583 static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
584 					       struct eap_gpsk_data *data,
585 					       struct eap_method_ret *ret,
586 					       u8 identifier,
587 					       const u8 *payload,
588 					       size_t payload_len)
589 {
590 	struct wpabuf *resp;
591 	const u8 *pos, *end;
592 
593 	if (data->state != GPSK_3) {
594 		ret->ignore = TRUE;
595 		return NULL;
596 	}
597 
598 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
599 
600 	end = payload + payload_len;
601 
602 	pos = eap_gpsk_validate_rand(data, payload, end);
603 	pos = eap_gpsk_validate_id_server(data, pos, end);
604 	pos = eap_gpsk_validate_csuite(data, pos, end);
605 	pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
606 	pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
607 
608 	if (pos == NULL) {
609 		eap_gpsk_state(data, FAILURE);
610 		return NULL;
611 	}
612 	if (pos != end) {
613 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
614 			   "data in the end of GPSK-2",
615 			   (unsigned long) (end - pos));
616 	}
617 
618 	resp = eap_gpsk_send_gpsk_4(data, identifier);
619 	if (resp == NULL)
620 		return NULL;
621 
622 	eap_gpsk_state(data, SUCCESS);
623 	ret->methodState = METHOD_DONE;
624 	ret->decision = DECISION_UNCOND_SUCC;
625 
626 	return resp;
627 }
628 
629 
630 static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
631 					    u8 identifier)
632 {
633 	struct wpabuf *resp;
634 	u8 *rpos, *start;
635 	size_t mlen;
636 
637 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
638 
639 	mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
640 
641 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
642 			     EAP_CODE_RESPONSE, identifier);
643 	if (resp == NULL)
644 		return NULL;
645 
646 	wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
647 	start = wpabuf_put(resp, 0);
648 
649 	/* No PD_Payload_3 */
650 	wpabuf_put_be16(resp, 0);
651 
652 	rpos = wpabuf_put(resp, mlen);
653 	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
654 				 data->specifier, start, rpos - start, rpos) <
655 	    0) {
656 		eap_gpsk_state(data, FAILURE);
657 		wpabuf_free(resp);
658 		return NULL;
659 	}
660 
661 	return resp;
662 }
663 
664 
665 static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
666 					struct eap_method_ret *ret,
667 					const struct wpabuf *reqData)
668 {
669 	struct eap_gpsk_data *data = priv;
670 	struct wpabuf *resp;
671 	const u8 *pos;
672 	size_t len;
673 	u8 opcode, id;
674 
675 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
676 	if (pos == NULL || len < 1) {
677 		ret->ignore = TRUE;
678 		return NULL;
679 	}
680 
681 	id = eap_get_id(reqData);
682 	opcode = *pos++;
683 	len--;
684 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode);
685 
686 	ret->ignore = FALSE;
687 	ret->methodState = METHOD_MAY_CONT;
688 	ret->decision = DECISION_FAIL;
689 	ret->allowNotifications = FALSE;
690 
691 	switch (opcode) {
692 	case EAP_GPSK_OPCODE_GPSK_1:
693 		resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len);
694 		break;
695 	case EAP_GPSK_OPCODE_GPSK_3:
696 		resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len);
697 		break;
698 	default:
699 		wpa_printf(MSG_DEBUG,
700 			   "EAP-GPSK: Ignoring message with unknown opcode %d",
701 			   opcode);
702 		ret->ignore = TRUE;
703 		return NULL;
704 	}
705 
706 	return resp;
707 }
708 
709 
710 static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
711 {
712 	struct eap_gpsk_data *data = priv;
713 	return data->state == SUCCESS;
714 }
715 
716 
717 static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
718 {
719 	struct eap_gpsk_data *data = priv;
720 	u8 *key;
721 
722 	if (data->state != SUCCESS)
723 		return NULL;
724 
725 	key = os_malloc(EAP_MSK_LEN);
726 	if (key == NULL)
727 		return NULL;
728 	os_memcpy(key, data->msk, EAP_MSK_LEN);
729 	*len = EAP_MSK_LEN;
730 
731 	return key;
732 }
733 
734 
735 static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
736 {
737 	struct eap_gpsk_data *data = priv;
738 	u8 *key;
739 
740 	if (data->state != SUCCESS)
741 		return NULL;
742 
743 	key = os_malloc(EAP_EMSK_LEN);
744 	if (key == NULL)
745 		return NULL;
746 	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
747 	*len = EAP_EMSK_LEN;
748 
749 	return key;
750 }
751 
752 
753 static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
754 {
755 	struct eap_gpsk_data *data = priv;
756 	u8 *sid;
757 
758 	if (data->state != SUCCESS)
759 		return NULL;
760 
761 	sid = os_malloc(data->id_len);
762 	if (sid == NULL)
763 		return NULL;
764 	os_memcpy(sid, data->session_id, data->id_len);
765 	*len = data->id_len;
766 
767 	return sid;
768 }
769 
770 
771 int eap_peer_gpsk_register(void)
772 {
773 	struct eap_method *eap;
774 	int ret;
775 
776 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
777 				    EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
778 	if (eap == NULL)
779 		return -1;
780 
781 	eap->init = eap_gpsk_init;
782 	eap->deinit = eap_gpsk_deinit;
783 	eap->process = eap_gpsk_process;
784 	eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
785 	eap->getKey = eap_gpsk_getKey;
786 	eap->get_emsk = eap_gpsk_get_emsk;
787 	eap->getSessionId = eap_gpsk_get_session_id;
788 
789 	ret = eap_peer_method_register(eap);
790 	if (ret)
791 		eap_peer_method_free(eap);
792 	return ret;
793 }
794