Updated to hostap_2_6
[mech_eap.git] / libeap / src / eap_peer / eap_gpsk.c
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
775         eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
776                                     EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
777         if (eap == NULL)
778                 return -1;
779
780         eap->init = eap_gpsk_init;
781         eap->deinit = eap_gpsk_deinit;
782         eap->process = eap_gpsk_process;
783         eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
784         eap->getKey = eap_gpsk_getKey;
785         eap->get_emsk = eap_gpsk_get_emsk;
786         eap->getSessionId = eap_gpsk_get_session_id;
787
788         return eap_peer_method_register(eap);
789 }