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