13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * hostapd / EAP-GPSK (RFC 5433) server
33ff40c12SJohn Marino * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino */
83ff40c12SJohn Marino
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "crypto/random.h"
133ff40c12SJohn Marino #include "eap_server/eap_i.h"
143ff40c12SJohn Marino #include "eap_common/eap_gpsk_common.h"
153ff40c12SJohn Marino
163ff40c12SJohn Marino
173ff40c12SJohn Marino struct eap_gpsk_data {
183ff40c12SJohn Marino enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
193ff40c12SJohn Marino u8 rand_server[EAP_GPSK_RAND_LEN];
203ff40c12SJohn Marino u8 rand_peer[EAP_GPSK_RAND_LEN];
213ff40c12SJohn Marino u8 msk[EAP_MSK_LEN];
223ff40c12SJohn Marino u8 emsk[EAP_EMSK_LEN];
233ff40c12SJohn Marino u8 sk[EAP_GPSK_MAX_SK_LEN];
243ff40c12SJohn Marino size_t sk_len;
253ff40c12SJohn Marino u8 pk[EAP_GPSK_MAX_PK_LEN];
263ff40c12SJohn Marino size_t pk_len;
27*a1157835SDaniel Fojt u8 session_id[128];
28*a1157835SDaniel Fojt size_t id_len;
293ff40c12SJohn Marino u8 *id_peer;
303ff40c12SJohn Marino size_t id_peer_len;
313ff40c12SJohn Marino #define MAX_NUM_CSUITES 2
323ff40c12SJohn Marino struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
333ff40c12SJohn Marino size_t csuite_count;
343ff40c12SJohn Marino int vendor; /* CSuite/Vendor */
353ff40c12SJohn Marino int specifier; /* CSuite/Specifier */
363ff40c12SJohn Marino };
373ff40c12SJohn Marino
383ff40c12SJohn Marino
eap_gpsk_state_txt(int state)393ff40c12SJohn Marino static const char * eap_gpsk_state_txt(int state)
403ff40c12SJohn Marino {
413ff40c12SJohn Marino switch (state) {
423ff40c12SJohn Marino case GPSK_1:
433ff40c12SJohn Marino return "GPSK-1";
443ff40c12SJohn Marino case GPSK_3:
453ff40c12SJohn Marino return "GPSK-3";
463ff40c12SJohn Marino case SUCCESS:
473ff40c12SJohn Marino return "SUCCESS";
483ff40c12SJohn Marino case FAILURE:
493ff40c12SJohn Marino return "FAILURE";
503ff40c12SJohn Marino default:
513ff40c12SJohn Marino return "?";
523ff40c12SJohn Marino }
533ff40c12SJohn Marino }
543ff40c12SJohn Marino
553ff40c12SJohn Marino
eap_gpsk_state(struct eap_gpsk_data * data,int state)563ff40c12SJohn Marino static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
573ff40c12SJohn Marino {
583ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
593ff40c12SJohn Marino eap_gpsk_state_txt(data->state),
603ff40c12SJohn Marino eap_gpsk_state_txt(state));
613ff40c12SJohn Marino data->state = state;
623ff40c12SJohn Marino }
633ff40c12SJohn Marino
643ff40c12SJohn Marino
eap_gpsk_init(struct eap_sm * sm)653ff40c12SJohn Marino static void * eap_gpsk_init(struct eap_sm *sm)
663ff40c12SJohn Marino {
673ff40c12SJohn Marino struct eap_gpsk_data *data;
683ff40c12SJohn Marino
693ff40c12SJohn Marino data = os_zalloc(sizeof(*data));
703ff40c12SJohn Marino if (data == NULL)
713ff40c12SJohn Marino return NULL;
723ff40c12SJohn Marino data->state = GPSK_1;
733ff40c12SJohn Marino
743ff40c12SJohn Marino data->csuite_count = 0;
753ff40c12SJohn Marino if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
763ff40c12SJohn Marino EAP_GPSK_CIPHER_AES)) {
773ff40c12SJohn Marino WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
783ff40c12SJohn Marino EAP_GPSK_VENDOR_IETF);
793ff40c12SJohn Marino WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
803ff40c12SJohn Marino EAP_GPSK_CIPHER_AES);
813ff40c12SJohn Marino data->csuite_count++;
823ff40c12SJohn Marino }
833ff40c12SJohn Marino if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
843ff40c12SJohn Marino EAP_GPSK_CIPHER_SHA256)) {
853ff40c12SJohn Marino WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
863ff40c12SJohn Marino EAP_GPSK_VENDOR_IETF);
873ff40c12SJohn Marino WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
883ff40c12SJohn Marino EAP_GPSK_CIPHER_SHA256);
893ff40c12SJohn Marino data->csuite_count++;
903ff40c12SJohn Marino }
913ff40c12SJohn Marino
923ff40c12SJohn Marino return data;
933ff40c12SJohn Marino }
943ff40c12SJohn Marino
953ff40c12SJohn Marino
eap_gpsk_reset(struct eap_sm * sm,void * priv)963ff40c12SJohn Marino static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
973ff40c12SJohn Marino {
983ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
993ff40c12SJohn Marino os_free(data->id_peer);
100*a1157835SDaniel Fojt bin_clear_free(data, sizeof(*data));
1013ff40c12SJohn Marino }
1023ff40c12SJohn Marino
1033ff40c12SJohn Marino
eap_gpsk_build_gpsk_1(struct eap_sm * sm,struct eap_gpsk_data * data,u8 id)1043ff40c12SJohn Marino static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
1053ff40c12SJohn Marino struct eap_gpsk_data *data, u8 id)
1063ff40c12SJohn Marino {
1073ff40c12SJohn Marino size_t len;
1083ff40c12SJohn Marino struct wpabuf *req;
1093ff40c12SJohn Marino
1103ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
1113ff40c12SJohn Marino
1123ff40c12SJohn Marino if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
1133ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
1143ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
1153ff40c12SJohn Marino return NULL;
1163ff40c12SJohn Marino }
1173ff40c12SJohn Marino wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
1183ff40c12SJohn Marino data->rand_server, EAP_GPSK_RAND_LEN);
1193ff40c12SJohn Marino
1203ff40c12SJohn Marino len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 +
1213ff40c12SJohn Marino data->csuite_count * sizeof(struct eap_gpsk_csuite);
1223ff40c12SJohn Marino req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
1233ff40c12SJohn Marino EAP_CODE_REQUEST, id);
1243ff40c12SJohn Marino if (req == NULL) {
1253ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
1263ff40c12SJohn Marino "for request/GPSK-1");
1273ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
1283ff40c12SJohn Marino return NULL;
1293ff40c12SJohn Marino }
1303ff40c12SJohn Marino
1313ff40c12SJohn Marino wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
1323ff40c12SJohn Marino wpabuf_put_be16(req, sm->server_id_len);
1333ff40c12SJohn Marino wpabuf_put_data(req, sm->server_id, sm->server_id_len);
1343ff40c12SJohn Marino wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
1353ff40c12SJohn Marino wpabuf_put_be16(req,
1363ff40c12SJohn Marino data->csuite_count * sizeof(struct eap_gpsk_csuite));
1373ff40c12SJohn Marino wpabuf_put_data(req, data->csuite_list,
1383ff40c12SJohn Marino data->csuite_count * sizeof(struct eap_gpsk_csuite));
1393ff40c12SJohn Marino
1403ff40c12SJohn Marino return req;
1413ff40c12SJohn Marino }
1423ff40c12SJohn Marino
1433ff40c12SJohn Marino
eap_gpsk_build_gpsk_3(struct eap_sm * sm,struct eap_gpsk_data * data,u8 id)1443ff40c12SJohn Marino static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
1453ff40c12SJohn Marino struct eap_gpsk_data *data, u8 id)
1463ff40c12SJohn Marino {
1473ff40c12SJohn Marino u8 *pos, *start;
1483ff40c12SJohn Marino size_t len, miclen;
1493ff40c12SJohn Marino struct eap_gpsk_csuite *csuite;
1503ff40c12SJohn Marino struct wpabuf *req;
1513ff40c12SJohn Marino
1523ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
1533ff40c12SJohn Marino
1543ff40c12SJohn Marino miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
1553ff40c12SJohn Marino len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len +
1563ff40c12SJohn Marino sizeof(struct eap_gpsk_csuite) + 2 + miclen;
1573ff40c12SJohn Marino req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
1583ff40c12SJohn Marino EAP_CODE_REQUEST, id);
1593ff40c12SJohn Marino if (req == NULL) {
1603ff40c12SJohn Marino wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
1613ff40c12SJohn Marino "for request/GPSK-3");
1623ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
1633ff40c12SJohn Marino return NULL;
1643ff40c12SJohn Marino }
1653ff40c12SJohn Marino
1663ff40c12SJohn Marino wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3);
1673ff40c12SJohn Marino start = wpabuf_put(req, 0);
1683ff40c12SJohn Marino
1693ff40c12SJohn Marino wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
1703ff40c12SJohn Marino wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
1713ff40c12SJohn Marino wpabuf_put_be16(req, sm->server_id_len);
1723ff40c12SJohn Marino wpabuf_put_data(req, sm->server_id, sm->server_id_len);
1733ff40c12SJohn Marino csuite = wpabuf_put(req, sizeof(*csuite));
1743ff40c12SJohn Marino WPA_PUT_BE32(csuite->vendor, data->vendor);
1753ff40c12SJohn Marino WPA_PUT_BE16(csuite->specifier, data->specifier);
1763ff40c12SJohn Marino
1773ff40c12SJohn Marino /* no PD_Payload_2 */
1783ff40c12SJohn Marino wpabuf_put_be16(req, 0);
1793ff40c12SJohn Marino
1803ff40c12SJohn Marino pos = wpabuf_put(req, miclen);
1813ff40c12SJohn Marino if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
1823ff40c12SJohn Marino data->specifier, start, pos - start, pos) < 0)
1833ff40c12SJohn Marino {
184*a1157835SDaniel Fojt wpabuf_free(req);
1853ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
1863ff40c12SJohn Marino return NULL;
1873ff40c12SJohn Marino }
1883ff40c12SJohn Marino
1893ff40c12SJohn Marino return req;
1903ff40c12SJohn Marino }
1913ff40c12SJohn Marino
1923ff40c12SJohn Marino
eap_gpsk_buildReq(struct eap_sm * sm,void * priv,u8 id)1933ff40c12SJohn Marino static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id)
1943ff40c12SJohn Marino {
1953ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
1963ff40c12SJohn Marino
1973ff40c12SJohn Marino switch (data->state) {
1983ff40c12SJohn Marino case GPSK_1:
1993ff40c12SJohn Marino return eap_gpsk_build_gpsk_1(sm, data, id);
2003ff40c12SJohn Marino case GPSK_3:
2013ff40c12SJohn Marino return eap_gpsk_build_gpsk_3(sm, data, id);
2023ff40c12SJohn Marino default:
2033ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq",
2043ff40c12SJohn Marino data->state);
2053ff40c12SJohn Marino break;
2063ff40c12SJohn Marino }
2073ff40c12SJohn Marino return NULL;
2083ff40c12SJohn Marino }
2093ff40c12SJohn Marino
2103ff40c12SJohn Marino
eap_gpsk_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)2113ff40c12SJohn Marino static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv,
2123ff40c12SJohn Marino struct wpabuf *respData)
2133ff40c12SJohn Marino {
2143ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
2153ff40c12SJohn Marino const u8 *pos;
2163ff40c12SJohn Marino size_t len;
2173ff40c12SJohn Marino
2183ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
2193ff40c12SJohn Marino if (pos == NULL || len < 1) {
2203ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame");
2213ff40c12SJohn Marino return TRUE;
2223ff40c12SJohn Marino }
2233ff40c12SJohn Marino
2243ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos);
2253ff40c12SJohn Marino
2263ff40c12SJohn Marino if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2)
2273ff40c12SJohn Marino return FALSE;
2283ff40c12SJohn Marino
2293ff40c12SJohn Marino if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4)
2303ff40c12SJohn Marino return FALSE;
2313ff40c12SJohn Marino
2323ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d",
2333ff40c12SJohn Marino *pos, data->state);
2343ff40c12SJohn Marino
2353ff40c12SJohn Marino return TRUE;
2363ff40c12SJohn Marino }
2373ff40c12SJohn Marino
2383ff40c12SJohn Marino
eap_gpsk_process_gpsk_2(struct eap_sm * sm,struct eap_gpsk_data * data,const u8 * payload,size_t payloadlen)2393ff40c12SJohn Marino static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
2403ff40c12SJohn Marino struct eap_gpsk_data *data,
2413ff40c12SJohn Marino const u8 *payload, size_t payloadlen)
2423ff40c12SJohn Marino {
2433ff40c12SJohn Marino const u8 *pos, *end;
2443ff40c12SJohn Marino u16 alen;
2453ff40c12SJohn Marino const struct eap_gpsk_csuite *csuite;
2463ff40c12SJohn Marino size_t i, miclen;
2473ff40c12SJohn Marino u8 mic[EAP_GPSK_MAX_MIC_LEN];
2483ff40c12SJohn Marino
2493ff40c12SJohn Marino if (data->state != GPSK_1)
2503ff40c12SJohn Marino return;
2513ff40c12SJohn Marino
2523ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2");
2533ff40c12SJohn Marino
2543ff40c12SJohn Marino pos = payload;
2553ff40c12SJohn Marino end = payload + payloadlen;
2563ff40c12SJohn Marino
2573ff40c12SJohn Marino if (end - pos < 2) {
2583ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
2593ff40c12SJohn Marino "ID_Peer length");
2603ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
2613ff40c12SJohn Marino return;
2623ff40c12SJohn Marino }
2633ff40c12SJohn Marino alen = WPA_GET_BE16(pos);
2643ff40c12SJohn Marino pos += 2;
2653ff40c12SJohn Marino if (end - pos < alen) {
2663ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
2673ff40c12SJohn Marino "ID_Peer");
2683ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
2693ff40c12SJohn Marino return;
2703ff40c12SJohn Marino }
2713ff40c12SJohn Marino os_free(data->id_peer);
272*a1157835SDaniel Fojt data->id_peer = os_memdup(pos, alen);
2733ff40c12SJohn Marino if (data->id_peer == NULL) {
2743ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
2753ff40c12SJohn Marino "%d-octet ID_Peer", alen);
2763ff40c12SJohn Marino return;
2773ff40c12SJohn Marino }
2783ff40c12SJohn Marino data->id_peer_len = alen;
2793ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
2803ff40c12SJohn Marino data->id_peer, data->id_peer_len);
2813ff40c12SJohn Marino pos += alen;
2823ff40c12SJohn Marino
2833ff40c12SJohn Marino if (end - pos < 2) {
2843ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
2853ff40c12SJohn Marino "ID_Server length");
2863ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
2873ff40c12SJohn Marino return;
2883ff40c12SJohn Marino }
2893ff40c12SJohn Marino alen = WPA_GET_BE16(pos);
2903ff40c12SJohn Marino pos += 2;
2913ff40c12SJohn Marino if (end - pos < alen) {
2923ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
2933ff40c12SJohn Marino "ID_Server");
2943ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
2953ff40c12SJohn Marino return;
2963ff40c12SJohn Marino }
2973ff40c12SJohn Marino if (alen != sm->server_id_len ||
2983ff40c12SJohn Marino os_memcmp(pos, sm->server_id, alen) != 0) {
2993ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
3003ff40c12SJohn Marino "GPSK-2 did not match");
3013ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3023ff40c12SJohn Marino return;
3033ff40c12SJohn Marino }
3043ff40c12SJohn Marino pos += alen;
3053ff40c12SJohn Marino
3063ff40c12SJohn Marino if (end - pos < EAP_GPSK_RAND_LEN) {
3073ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3083ff40c12SJohn Marino "RAND_Peer");
3093ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3103ff40c12SJohn Marino return;
3113ff40c12SJohn Marino }
3123ff40c12SJohn Marino os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN);
3133ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
3143ff40c12SJohn Marino data->rand_peer, EAP_GPSK_RAND_LEN);
3153ff40c12SJohn Marino pos += EAP_GPSK_RAND_LEN;
3163ff40c12SJohn Marino
3173ff40c12SJohn Marino if (end - pos < EAP_GPSK_RAND_LEN) {
3183ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3193ff40c12SJohn Marino "RAND_Server");
3203ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3213ff40c12SJohn Marino return;
3223ff40c12SJohn Marino }
3233ff40c12SJohn Marino if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) {
3243ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
3253ff40c12SJohn Marino "GPSK-2 did not match");
3263ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
3273ff40c12SJohn Marino data->rand_server, EAP_GPSK_RAND_LEN);
3283ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2",
3293ff40c12SJohn Marino pos, EAP_GPSK_RAND_LEN);
3303ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3313ff40c12SJohn Marino return;
3323ff40c12SJohn Marino }
3333ff40c12SJohn Marino pos += EAP_GPSK_RAND_LEN;
3343ff40c12SJohn Marino
3353ff40c12SJohn Marino if (end - pos < 2) {
3363ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3373ff40c12SJohn Marino "CSuite_List length");
3383ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3393ff40c12SJohn Marino return;
3403ff40c12SJohn Marino }
3413ff40c12SJohn Marino alen = WPA_GET_BE16(pos);
3423ff40c12SJohn Marino pos += 2;
3433ff40c12SJohn Marino if (end - pos < alen) {
3443ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3453ff40c12SJohn Marino "CSuite_List");
3463ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3473ff40c12SJohn Marino return;
3483ff40c12SJohn Marino }
3493ff40c12SJohn Marino if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) ||
3503ff40c12SJohn Marino os_memcmp(pos, data->csuite_list, alen) != 0) {
3513ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and "
3523ff40c12SJohn Marino "GPSK-2 did not match");
3533ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3543ff40c12SJohn Marino return;
3553ff40c12SJohn Marino }
3563ff40c12SJohn Marino pos += alen;
3573ff40c12SJohn Marino
3583ff40c12SJohn Marino if (end - pos < (int) sizeof(*csuite)) {
3593ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3603ff40c12SJohn Marino "CSuite_Sel");
3613ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3623ff40c12SJohn Marino return;
3633ff40c12SJohn Marino }
3643ff40c12SJohn Marino csuite = (const struct eap_gpsk_csuite *) pos;
3653ff40c12SJohn Marino for (i = 0; i < data->csuite_count; i++) {
3663ff40c12SJohn Marino if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite))
3673ff40c12SJohn Marino == 0)
3683ff40c12SJohn Marino break;
3693ff40c12SJohn Marino }
3703ff40c12SJohn Marino if (i == data->csuite_count) {
3713ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported "
3723ff40c12SJohn Marino "ciphersuite %d:%d",
3733ff40c12SJohn Marino WPA_GET_BE32(csuite->vendor),
3743ff40c12SJohn Marino WPA_GET_BE16(csuite->specifier));
3753ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3763ff40c12SJohn Marino return;
3773ff40c12SJohn Marino }
3783ff40c12SJohn Marino data->vendor = WPA_GET_BE32(csuite->vendor);
3793ff40c12SJohn Marino data->specifier = WPA_GET_BE16(csuite->specifier);
3803ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
3813ff40c12SJohn Marino data->vendor, data->specifier);
3823ff40c12SJohn Marino pos += sizeof(*csuite);
3833ff40c12SJohn Marino
3843ff40c12SJohn Marino if (end - pos < 2) {
3853ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3863ff40c12SJohn Marino "PD_Payload_1 length");
3873ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3883ff40c12SJohn Marino return;
3893ff40c12SJohn Marino }
3903ff40c12SJohn Marino alen = WPA_GET_BE16(pos);
3913ff40c12SJohn Marino pos += 2;
3923ff40c12SJohn Marino if (end - pos < alen) {
3933ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
3943ff40c12SJohn Marino "PD_Payload_1");
3953ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
3963ff40c12SJohn Marino return;
3973ff40c12SJohn Marino }
3983ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
3993ff40c12SJohn Marino pos += alen;
4003ff40c12SJohn Marino
4013ff40c12SJohn Marino if (sm->user == NULL || sm->user->password == NULL) {
4023ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured "
4033ff40c12SJohn Marino "for the user");
4043ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4053ff40c12SJohn Marino return;
4063ff40c12SJohn Marino }
4073ff40c12SJohn Marino
4083ff40c12SJohn Marino if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len,
4093ff40c12SJohn Marino data->vendor, data->specifier,
4103ff40c12SJohn Marino data->rand_peer, data->rand_server,
4113ff40c12SJohn Marino data->id_peer, data->id_peer_len,
4123ff40c12SJohn Marino sm->server_id, sm->server_id_len,
4133ff40c12SJohn Marino data->msk, data->emsk,
4143ff40c12SJohn Marino data->sk, &data->sk_len,
4153ff40c12SJohn Marino data->pk, &data->pk_len) < 0) {
4163ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
4173ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4183ff40c12SJohn Marino return;
4193ff40c12SJohn Marino }
4203ff40c12SJohn Marino
421*a1157835SDaniel Fojt if (eap_gpsk_derive_session_id(sm->user->password,
422*a1157835SDaniel Fojt sm->user->password_len,
423*a1157835SDaniel Fojt data->vendor, data->specifier,
424*a1157835SDaniel Fojt data->rand_peer, data->rand_server,
425*a1157835SDaniel Fojt data->id_peer, data->id_peer_len,
426*a1157835SDaniel Fojt sm->server_id, sm->server_id_len,
427*a1157835SDaniel Fojt EAP_TYPE_GPSK,
428*a1157835SDaniel Fojt data->session_id, &data->id_len) < 0) {
429*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
430*a1157835SDaniel Fojt eap_gpsk_state(data, FAILURE);
431*a1157835SDaniel Fojt return;
432*a1157835SDaniel Fojt }
433*a1157835SDaniel Fojt wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
434*a1157835SDaniel Fojt data->session_id, data->id_len);
435*a1157835SDaniel Fojt
4363ff40c12SJohn Marino miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
4373ff40c12SJohn Marino if (end - pos < (int) miclen) {
4383ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
4393ff40c12SJohn Marino "(left=%lu miclen=%lu)",
4403ff40c12SJohn Marino (unsigned long) (end - pos),
4413ff40c12SJohn Marino (unsigned long) miclen);
4423ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4433ff40c12SJohn Marino return;
4443ff40c12SJohn Marino }
4453ff40c12SJohn Marino if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
4463ff40c12SJohn Marino data->specifier, payload, pos - payload, mic)
4473ff40c12SJohn Marino < 0) {
4483ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
4493ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4503ff40c12SJohn Marino return;
4513ff40c12SJohn Marino }
452*a1157835SDaniel Fojt if (os_memcmp_const(mic, pos, miclen) != 0) {
4533ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
4543ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
4553ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
4563ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4573ff40c12SJohn Marino return;
4583ff40c12SJohn Marino }
4593ff40c12SJohn Marino pos += miclen;
4603ff40c12SJohn Marino
4613ff40c12SJohn Marino if (pos != end) {
4623ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
4633ff40c12SJohn Marino "data in the end of GPSK-2",
4643ff40c12SJohn Marino (unsigned long) (end - pos));
4653ff40c12SJohn Marino }
4663ff40c12SJohn Marino
4673ff40c12SJohn Marino eap_gpsk_state(data, GPSK_3);
4683ff40c12SJohn Marino }
4693ff40c12SJohn Marino
4703ff40c12SJohn Marino
eap_gpsk_process_gpsk_4(struct eap_sm * sm,struct eap_gpsk_data * data,const u8 * payload,size_t payloadlen)4713ff40c12SJohn Marino static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
4723ff40c12SJohn Marino struct eap_gpsk_data *data,
4733ff40c12SJohn Marino const u8 *payload, size_t payloadlen)
4743ff40c12SJohn Marino {
4753ff40c12SJohn Marino const u8 *pos, *end;
4763ff40c12SJohn Marino u16 alen;
4773ff40c12SJohn Marino size_t miclen;
4783ff40c12SJohn Marino u8 mic[EAP_GPSK_MAX_MIC_LEN];
4793ff40c12SJohn Marino
4803ff40c12SJohn Marino if (data->state != GPSK_3)
4813ff40c12SJohn Marino return;
4823ff40c12SJohn Marino
4833ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4");
4843ff40c12SJohn Marino
4853ff40c12SJohn Marino pos = payload;
4863ff40c12SJohn Marino end = payload + payloadlen;
4873ff40c12SJohn Marino
4883ff40c12SJohn Marino if (end - pos < 2) {
4893ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
4903ff40c12SJohn Marino "PD_Payload_1 length");
4913ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
4923ff40c12SJohn Marino return;
4933ff40c12SJohn Marino }
4943ff40c12SJohn Marino alen = WPA_GET_BE16(pos);
4953ff40c12SJohn Marino pos += 2;
4963ff40c12SJohn Marino if (end - pos < alen) {
4973ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
4983ff40c12SJohn Marino "PD_Payload_1");
4993ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
5003ff40c12SJohn Marino return;
5013ff40c12SJohn Marino }
5023ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
5033ff40c12SJohn Marino pos += alen;
5043ff40c12SJohn Marino
5053ff40c12SJohn Marino miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
5063ff40c12SJohn Marino if (end - pos < (int) miclen) {
5073ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
5083ff40c12SJohn Marino "(left=%lu miclen=%lu)",
5093ff40c12SJohn Marino (unsigned long) (end - pos),
5103ff40c12SJohn Marino (unsigned long) miclen);
5113ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
5123ff40c12SJohn Marino return;
5133ff40c12SJohn Marino }
5143ff40c12SJohn Marino if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
5153ff40c12SJohn Marino data->specifier, payload, pos - payload, mic)
5163ff40c12SJohn Marino < 0) {
5173ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
5183ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
5193ff40c12SJohn Marino return;
5203ff40c12SJohn Marino }
521*a1157835SDaniel Fojt if (os_memcmp_const(mic, pos, miclen) != 0) {
5223ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
5233ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
5243ff40c12SJohn Marino wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
5253ff40c12SJohn Marino eap_gpsk_state(data, FAILURE);
5263ff40c12SJohn Marino return;
5273ff40c12SJohn Marino }
5283ff40c12SJohn Marino pos += miclen;
5293ff40c12SJohn Marino
5303ff40c12SJohn Marino if (pos != end) {
5313ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
5323ff40c12SJohn Marino "data in the end of GPSK-4",
5333ff40c12SJohn Marino (unsigned long) (end - pos));
5343ff40c12SJohn Marino }
5353ff40c12SJohn Marino
5363ff40c12SJohn Marino eap_gpsk_state(data, SUCCESS);
5373ff40c12SJohn Marino }
5383ff40c12SJohn Marino
5393ff40c12SJohn Marino
eap_gpsk_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)5403ff40c12SJohn Marino static void eap_gpsk_process(struct eap_sm *sm, void *priv,
5413ff40c12SJohn Marino struct wpabuf *respData)
5423ff40c12SJohn Marino {
5433ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
5443ff40c12SJohn Marino const u8 *pos;
5453ff40c12SJohn Marino size_t len;
5463ff40c12SJohn Marino
5473ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
5483ff40c12SJohn Marino if (pos == NULL || len < 1)
5493ff40c12SJohn Marino return;
5503ff40c12SJohn Marino
5513ff40c12SJohn Marino switch (*pos) {
5523ff40c12SJohn Marino case EAP_GPSK_OPCODE_GPSK_2:
5533ff40c12SJohn Marino eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1);
5543ff40c12SJohn Marino break;
5553ff40c12SJohn Marino case EAP_GPSK_OPCODE_GPSK_4:
5563ff40c12SJohn Marino eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1);
5573ff40c12SJohn Marino break;
5583ff40c12SJohn Marino }
5593ff40c12SJohn Marino }
5603ff40c12SJohn Marino
5613ff40c12SJohn Marino
eap_gpsk_isDone(struct eap_sm * sm,void * priv)5623ff40c12SJohn Marino static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv)
5633ff40c12SJohn Marino {
5643ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
5653ff40c12SJohn Marino return data->state == SUCCESS || data->state == FAILURE;
5663ff40c12SJohn Marino }
5673ff40c12SJohn Marino
5683ff40c12SJohn Marino
eap_gpsk_getKey(struct eap_sm * sm,void * priv,size_t * len)5693ff40c12SJohn Marino static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
5703ff40c12SJohn Marino {
5713ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
5723ff40c12SJohn Marino u8 *key;
5733ff40c12SJohn Marino
5743ff40c12SJohn Marino if (data->state != SUCCESS)
5753ff40c12SJohn Marino return NULL;
5763ff40c12SJohn Marino
577*a1157835SDaniel Fojt key = os_memdup(data->msk, EAP_MSK_LEN);
5783ff40c12SJohn Marino if (key == NULL)
5793ff40c12SJohn Marino return NULL;
5803ff40c12SJohn Marino *len = EAP_MSK_LEN;
5813ff40c12SJohn Marino
5823ff40c12SJohn Marino return key;
5833ff40c12SJohn Marino }
5843ff40c12SJohn Marino
5853ff40c12SJohn Marino
eap_gpsk_get_emsk(struct eap_sm * sm,void * priv,size_t * len)5863ff40c12SJohn Marino static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
5873ff40c12SJohn Marino {
5883ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
5893ff40c12SJohn Marino u8 *key;
5903ff40c12SJohn Marino
5913ff40c12SJohn Marino if (data->state != SUCCESS)
5923ff40c12SJohn Marino return NULL;
5933ff40c12SJohn Marino
594*a1157835SDaniel Fojt key = os_memdup(data->emsk, EAP_EMSK_LEN);
5953ff40c12SJohn Marino if (key == NULL)
5963ff40c12SJohn Marino return NULL;
5973ff40c12SJohn Marino *len = EAP_EMSK_LEN;
5983ff40c12SJohn Marino
5993ff40c12SJohn Marino return key;
6003ff40c12SJohn Marino }
6013ff40c12SJohn Marino
6023ff40c12SJohn Marino
eap_gpsk_isSuccess(struct eap_sm * sm,void * priv)6033ff40c12SJohn Marino static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
6043ff40c12SJohn Marino {
6053ff40c12SJohn Marino struct eap_gpsk_data *data = priv;
6063ff40c12SJohn Marino return data->state == SUCCESS;
6073ff40c12SJohn Marino }
6083ff40c12SJohn Marino
6093ff40c12SJohn Marino
eap_gpsk_get_session_id(struct eap_sm * sm,void * priv,size_t * len)610*a1157835SDaniel Fojt static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
611*a1157835SDaniel Fojt {
612*a1157835SDaniel Fojt struct eap_gpsk_data *data = priv;
613*a1157835SDaniel Fojt u8 *sid;
614*a1157835SDaniel Fojt
615*a1157835SDaniel Fojt if (data->state != SUCCESS)
616*a1157835SDaniel Fojt return NULL;
617*a1157835SDaniel Fojt
618*a1157835SDaniel Fojt sid = os_memdup(data->session_id, data->id_len);
619*a1157835SDaniel Fojt if (sid == NULL)
620*a1157835SDaniel Fojt return NULL;
621*a1157835SDaniel Fojt *len = data->id_len;
622*a1157835SDaniel Fojt
623*a1157835SDaniel Fojt return sid;
624*a1157835SDaniel Fojt }
625*a1157835SDaniel Fojt
626*a1157835SDaniel Fojt
eap_server_gpsk_register(void)6273ff40c12SJohn Marino int eap_server_gpsk_register(void)
6283ff40c12SJohn Marino {
6293ff40c12SJohn Marino struct eap_method *eap;
6303ff40c12SJohn Marino
6313ff40c12SJohn Marino eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
6323ff40c12SJohn Marino EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
6333ff40c12SJohn Marino if (eap == NULL)
6343ff40c12SJohn Marino return -1;
6353ff40c12SJohn Marino
6363ff40c12SJohn Marino eap->init = eap_gpsk_init;
6373ff40c12SJohn Marino eap->reset = eap_gpsk_reset;
6383ff40c12SJohn Marino eap->buildReq = eap_gpsk_buildReq;
6393ff40c12SJohn Marino eap->check = eap_gpsk_check;
6403ff40c12SJohn Marino eap->process = eap_gpsk_process;
6413ff40c12SJohn Marino eap->isDone = eap_gpsk_isDone;
6423ff40c12SJohn Marino eap->getKey = eap_gpsk_getKey;
6433ff40c12SJohn Marino eap->isSuccess = eap_gpsk_isSuccess;
6443ff40c12SJohn Marino eap->get_emsk = eap_gpsk_get_emsk;
645*a1157835SDaniel Fojt eap->getSessionId = eap_gpsk_get_session_id;
6463ff40c12SJohn Marino
647*a1157835SDaniel Fojt return eap_server_method_register(eap);
6483ff40c12SJohn Marino }
649