xref: /freebsd/contrib/wpa/src/eap_peer/eap_pax.c (revision c1d255d3)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP peer method: EAP-PAX (RFC 4746)
339beb93cSSam Leffler  * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
12f05cddf9SRui Paulo #include "crypto/random.h"
1339beb93cSSam Leffler #include "eap_common/eap_pax_common.h"
14e28a4053SRui Paulo #include "eap_i.h"
1539beb93cSSam Leffler 
1639beb93cSSam Leffler /*
1739beb93cSSam Leffler  * Note: only PAX_STD subprotocol is currently supported
1839beb93cSSam Leffler  *
1939beb93cSSam Leffler  * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
2039beb93cSSam Leffler  * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
2139beb93cSSam Leffler  * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
2239beb93cSSam Leffler  * RSAES-OAEP).
2339beb93cSSam Leffler  */
2439beb93cSSam Leffler 
2539beb93cSSam Leffler struct eap_pax_data {
2639beb93cSSam Leffler 	enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
2739beb93cSSam Leffler 	u8 mac_id, dh_group_id, public_key_id;
2839beb93cSSam Leffler 	union {
2939beb93cSSam Leffler 		u8 e[2 * EAP_PAX_RAND_LEN];
3039beb93cSSam Leffler 		struct {
3139beb93cSSam Leffler 			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
3239beb93cSSam Leffler 			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
3339beb93cSSam Leffler 		} r;
3439beb93cSSam Leffler 	} rand;
3539beb93cSSam Leffler 	char *cid;
3639beb93cSSam Leffler 	size_t cid_len;
3739beb93cSSam Leffler 	u8 ak[EAP_PAX_AK_LEN];
3839beb93cSSam Leffler 	u8 mk[EAP_PAX_MK_LEN];
3939beb93cSSam Leffler 	u8 ck[EAP_PAX_CK_LEN];
4039beb93cSSam Leffler 	u8 ick[EAP_PAX_ICK_LEN];
415b9c547cSRui Paulo 	u8 mid[EAP_PAX_MID_LEN];
4239beb93cSSam Leffler };
4339beb93cSSam Leffler 
4439beb93cSSam Leffler 
4539beb93cSSam Leffler static void eap_pax_deinit(struct eap_sm *sm, void *priv);
4639beb93cSSam Leffler 
4739beb93cSSam Leffler 
eap_pax_init(struct eap_sm * sm)4839beb93cSSam Leffler static void * eap_pax_init(struct eap_sm *sm)
4939beb93cSSam Leffler {
5039beb93cSSam Leffler 	struct eap_pax_data *data;
5139beb93cSSam Leffler 	const u8 *identity, *password;
5239beb93cSSam Leffler 	size_t identity_len, password_len;
5339beb93cSSam Leffler 
5439beb93cSSam Leffler 	identity = eap_get_config_identity(sm, &identity_len);
5539beb93cSSam Leffler 	password = eap_get_config_password(sm, &password_len);
5639beb93cSSam Leffler 	if (!identity || !password) {
5739beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
5839beb93cSSam Leffler 			   "not configured");
5939beb93cSSam Leffler 		return NULL;
6039beb93cSSam Leffler 	}
6139beb93cSSam Leffler 
6239beb93cSSam Leffler 	if (password_len != EAP_PAX_AK_LEN) {
6339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
6439beb93cSSam Leffler 		return NULL;
6539beb93cSSam Leffler 	}
6639beb93cSSam Leffler 
6739beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
6839beb93cSSam Leffler 	if (data == NULL)
6939beb93cSSam Leffler 		return NULL;
7039beb93cSSam Leffler 	data->state = PAX_INIT;
7139beb93cSSam Leffler 
7285732ac8SCy Schubert 	data->cid = os_memdup(identity, identity_len);
7339beb93cSSam Leffler 	if (data->cid == NULL) {
7439beb93cSSam Leffler 		eap_pax_deinit(sm, data);
7539beb93cSSam Leffler 		return NULL;
7639beb93cSSam Leffler 	}
7739beb93cSSam Leffler 	data->cid_len = identity_len;
7839beb93cSSam Leffler 
7939beb93cSSam Leffler 	os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
8039beb93cSSam Leffler 
8139beb93cSSam Leffler 	return data;
8239beb93cSSam Leffler }
8339beb93cSSam Leffler 
8439beb93cSSam Leffler 
eap_pax_deinit(struct eap_sm * sm,void * priv)8539beb93cSSam Leffler static void eap_pax_deinit(struct eap_sm *sm, void *priv)
8639beb93cSSam Leffler {
8739beb93cSSam Leffler 	struct eap_pax_data *data = priv;
8839beb93cSSam Leffler 	os_free(data->cid);
895b9c547cSRui Paulo 	bin_clear_free(data, sizeof(*data));
9039beb93cSSam Leffler }
9139beb93cSSam Leffler 
9239beb93cSSam Leffler 
eap_pax_alloc_resp(const struct eap_pax_hdr * req,u8 id,u8 op_code,size_t plen)9339beb93cSSam Leffler static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
9439beb93cSSam Leffler 					  u8 id, u8 op_code, size_t plen)
9539beb93cSSam Leffler {
9639beb93cSSam Leffler 	struct wpabuf *resp;
9739beb93cSSam Leffler 	struct eap_pax_hdr *pax;
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
10039beb93cSSam Leffler 			     sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
10139beb93cSSam Leffler 	if (resp == NULL)
10239beb93cSSam Leffler 		return NULL;
10339beb93cSSam Leffler 
10439beb93cSSam Leffler 	pax = wpabuf_put(resp, sizeof(*pax));
10539beb93cSSam Leffler 	pax->op_code = op_code;
10639beb93cSSam Leffler 	pax->flags = 0;
10739beb93cSSam Leffler 	pax->mac_id = req->mac_id;
10839beb93cSSam Leffler 	pax->dh_group_id = req->dh_group_id;
10939beb93cSSam Leffler 	pax->public_key_id = req->public_key_id;
11039beb93cSSam Leffler 
11139beb93cSSam Leffler 	return resp;
11239beb93cSSam Leffler }
11339beb93cSSam Leffler 
11439beb93cSSam Leffler 
eap_pax_process_std_1(struct eap_pax_data * data,struct eap_method_ret * ret,u8 id,const struct eap_pax_hdr * req,size_t req_plen)11539beb93cSSam Leffler static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
11639beb93cSSam Leffler 					     struct eap_method_ret *ret, u8 id,
11739beb93cSSam Leffler 					     const struct eap_pax_hdr *req,
11839beb93cSSam Leffler 					     size_t req_plen)
11939beb93cSSam Leffler {
12039beb93cSSam Leffler 	struct wpabuf *resp;
12139beb93cSSam Leffler 	const u8 *pos;
12239beb93cSSam Leffler 	u8 *rpos;
12339beb93cSSam Leffler 	size_t left, plen;
12439beb93cSSam Leffler 
12539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
12639beb93cSSam Leffler 
12739beb93cSSam Leffler 	if (data->state != PAX_INIT) {
12839beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
12939beb93cSSam Leffler 			   "unexpected state (%d) - ignored", data->state);
130*c1d255d3SCy Schubert 		ret->ignore = true;
13139beb93cSSam Leffler 		return NULL;
13239beb93cSSam Leffler 	}
13339beb93cSSam Leffler 
13439beb93cSSam Leffler 	if (req->flags & EAP_PAX_FLAGS_CE) {
13539beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
13639beb93cSSam Leffler 			   "ignored");
137*c1d255d3SCy Schubert 		ret->ignore = true;
13839beb93cSSam Leffler 		return NULL;
13939beb93cSSam Leffler 	}
14039beb93cSSam Leffler 
14139beb93cSSam Leffler 	left = req_plen - sizeof(*req);
14239beb93cSSam Leffler 
14339beb93cSSam Leffler 	if (left < 2 + EAP_PAX_RAND_LEN) {
14439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
14539beb93cSSam Leffler 			   "payload");
146*c1d255d3SCy Schubert 		ret->ignore = true;
14739beb93cSSam Leffler 		return NULL;
14839beb93cSSam Leffler 	}
14939beb93cSSam Leffler 
15039beb93cSSam Leffler 	pos = (const u8 *) (req + 1);
15139beb93cSSam Leffler 	if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
15239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
15339beb93cSSam Leffler 			   "length %d (expected %d)",
15439beb93cSSam Leffler 			   WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
155*c1d255d3SCy Schubert 		ret->ignore = true;
15639beb93cSSam Leffler 		return NULL;
15739beb93cSSam Leffler 	}
15839beb93cSSam Leffler 
15939beb93cSSam Leffler 	pos += 2;
16039beb93cSSam Leffler 	left -= 2;
16139beb93cSSam Leffler 	os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
16239beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
16339beb93cSSam Leffler 		    data->rand.r.x, EAP_PAX_RAND_LEN);
16439beb93cSSam Leffler 	pos += EAP_PAX_RAND_LEN;
16539beb93cSSam Leffler 	left -= EAP_PAX_RAND_LEN;
16639beb93cSSam Leffler 
16739beb93cSSam Leffler 	if (left > 0) {
16839beb93cSSam Leffler 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
16939beb93cSSam Leffler 			    pos, left);
17039beb93cSSam Leffler 	}
17139beb93cSSam Leffler 
172f05cddf9SRui Paulo 	if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
17339beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
174*c1d255d3SCy Schubert 		ret->ignore = true;
17539beb93cSSam Leffler 		return NULL;
17639beb93cSSam Leffler 	}
17739beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
17839beb93cSSam Leffler 		    data->rand.r.y, EAP_PAX_RAND_LEN);
17939beb93cSSam Leffler 
18039beb93cSSam Leffler 	if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
1815b9c547cSRui Paulo 					   data->mk, data->ck, data->ick,
1825b9c547cSRui Paulo 					   data->mid) < 0) {
183*c1d255d3SCy Schubert 		ret->ignore = true;
18439beb93cSSam Leffler 		return NULL;
18539beb93cSSam Leffler 	}
18639beb93cSSam Leffler 
18739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
18839beb93cSSam Leffler 
18939beb93cSSam Leffler 	plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
19039beb93cSSam Leffler 		EAP_PAX_ICV_LEN;
19139beb93cSSam Leffler 	resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
19239beb93cSSam Leffler 	if (resp == NULL)
19339beb93cSSam Leffler 		return NULL;
19439beb93cSSam Leffler 
19539beb93cSSam Leffler 	wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
19639beb93cSSam Leffler 	wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
19739beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
19839beb93cSSam Leffler 		    data->rand.r.y, EAP_PAX_RAND_LEN);
19939beb93cSSam Leffler 
20039beb93cSSam Leffler 	wpabuf_put_be16(resp, data->cid_len);
20139beb93cSSam Leffler 	wpabuf_put_data(resp, data->cid, data->cid_len);
20239beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
20339beb93cSSam Leffler 			  (u8 *) data->cid, data->cid_len);
20439beb93cSSam Leffler 
20539beb93cSSam Leffler 	wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
20639beb93cSSam Leffler 	rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
20739beb93cSSam Leffler 	eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
20839beb93cSSam Leffler 		    data->rand.r.x, EAP_PAX_RAND_LEN,
20939beb93cSSam Leffler 		    data->rand.r.y, EAP_PAX_RAND_LEN,
21039beb93cSSam Leffler 		    (u8 *) data->cid, data->cid_len, rpos);
21139beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
21239beb93cSSam Leffler 		    rpos, EAP_PAX_MAC_LEN);
21339beb93cSSam Leffler 
21439beb93cSSam Leffler 	/* Optional ADE could be added here, if needed */
21539beb93cSSam Leffler 
21639beb93cSSam Leffler 	rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
21739beb93cSSam Leffler 	eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
21839beb93cSSam Leffler 		    wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
21939beb93cSSam Leffler 		    NULL, 0, NULL, 0, rpos);
22039beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
22139beb93cSSam Leffler 
22239beb93cSSam Leffler 	data->state = PAX_STD_2_SENT;
22339beb93cSSam Leffler 	data->mac_id = req->mac_id;
22439beb93cSSam Leffler 	data->dh_group_id = req->dh_group_id;
22539beb93cSSam Leffler 	data->public_key_id = req->public_key_id;
22639beb93cSSam Leffler 
22739beb93cSSam Leffler 	return resp;
22839beb93cSSam Leffler }
22939beb93cSSam Leffler 
23039beb93cSSam Leffler 
eap_pax_process_std_3(struct eap_pax_data * data,struct eap_method_ret * ret,u8 id,const struct eap_pax_hdr * req,size_t req_plen)23139beb93cSSam Leffler static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
23239beb93cSSam Leffler 					     struct eap_method_ret *ret, u8 id,
23339beb93cSSam Leffler 					     const struct eap_pax_hdr *req,
23439beb93cSSam Leffler 					     size_t req_plen)
23539beb93cSSam Leffler {
23639beb93cSSam Leffler 	struct wpabuf *resp;
23739beb93cSSam Leffler 	u8 *rpos, mac[EAP_PAX_MAC_LEN];
23839beb93cSSam Leffler 	const u8 *pos;
23939beb93cSSam Leffler 	size_t left;
24039beb93cSSam Leffler 
24139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
24239beb93cSSam Leffler 
24339beb93cSSam Leffler 	if (data->state != PAX_STD_2_SENT) {
24439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
24539beb93cSSam Leffler 			   "unexpected state (%d) - ignored", data->state);
246*c1d255d3SCy Schubert 		ret->ignore = true;
24739beb93cSSam Leffler 		return NULL;
24839beb93cSSam Leffler 	}
24939beb93cSSam Leffler 
25039beb93cSSam Leffler 	if (req->flags & EAP_PAX_FLAGS_CE) {
25139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
25239beb93cSSam Leffler 			   "ignored");
253*c1d255d3SCy Schubert 		ret->ignore = true;
25439beb93cSSam Leffler 		return NULL;
25539beb93cSSam Leffler 	}
25639beb93cSSam Leffler 
25739beb93cSSam Leffler 	left = req_plen - sizeof(*req);
25839beb93cSSam Leffler 
25939beb93cSSam Leffler 	if (left < 2 + EAP_PAX_MAC_LEN) {
26039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
26139beb93cSSam Leffler 			   "payload");
262*c1d255d3SCy Schubert 		ret->ignore = true;
26339beb93cSSam Leffler 		return NULL;
26439beb93cSSam Leffler 	}
26539beb93cSSam Leffler 
26639beb93cSSam Leffler 	pos = (const u8 *) (req + 1);
26739beb93cSSam Leffler 	if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
26839beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
26939beb93cSSam Leffler 			   "MAC_CK length %d (expected %d)",
27039beb93cSSam Leffler 			   WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
271*c1d255d3SCy Schubert 		ret->ignore = true;
27239beb93cSSam Leffler 		return NULL;
27339beb93cSSam Leffler 	}
27439beb93cSSam Leffler 	pos += 2;
27539beb93cSSam Leffler 	left -= 2;
27639beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
27739beb93cSSam Leffler 		    pos, EAP_PAX_MAC_LEN);
278780fb4a2SCy Schubert 	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
27939beb93cSSam Leffler 			data->rand.r.y, EAP_PAX_RAND_LEN,
280780fb4a2SCy Schubert 			(u8 *) data->cid, data->cid_len, NULL, 0, mac) < 0) {
281780fb4a2SCy Schubert 		wpa_printf(MSG_INFO,
282780fb4a2SCy Schubert 			   "EAP-PAX: Could not derive MAC_CK(B, CID)");
283780fb4a2SCy Schubert 		ret->methodState = METHOD_DONE;
284780fb4a2SCy Schubert 		ret->decision = DECISION_FAIL;
285780fb4a2SCy Schubert 		return NULL;
286780fb4a2SCy Schubert 	}
287780fb4a2SCy Schubert 
2885b9c547cSRui Paulo 	if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) {
28939beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
29039beb93cSSam Leffler 			   "received");
29139beb93cSSam Leffler 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
29239beb93cSSam Leffler 			    mac, EAP_PAX_MAC_LEN);
29339beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
29439beb93cSSam Leffler 		ret->decision = DECISION_FAIL;
29539beb93cSSam Leffler 		return NULL;
29639beb93cSSam Leffler 	}
29739beb93cSSam Leffler 
29839beb93cSSam Leffler 	pos += EAP_PAX_MAC_LEN;
29939beb93cSSam Leffler 	left -= EAP_PAX_MAC_LEN;
30039beb93cSSam Leffler 
30139beb93cSSam Leffler 	if (left > 0) {
30239beb93cSSam Leffler 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
30339beb93cSSam Leffler 			    pos, left);
30439beb93cSSam Leffler 	}
30539beb93cSSam Leffler 
30639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
30739beb93cSSam Leffler 
30839beb93cSSam Leffler 	resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
30939beb93cSSam Leffler 	if (resp == NULL)
31039beb93cSSam Leffler 		return NULL;
31139beb93cSSam Leffler 
31239beb93cSSam Leffler 	/* Optional ADE could be added here, if needed */
31339beb93cSSam Leffler 
31439beb93cSSam Leffler 	rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
315780fb4a2SCy Schubert 	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
31639beb93cSSam Leffler 			wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
317780fb4a2SCy Schubert 			NULL, 0, NULL, 0, rpos) < 0) {
318780fb4a2SCy Schubert 		wpabuf_free(resp);
319780fb4a2SCy Schubert 		return NULL;
320780fb4a2SCy Schubert 	}
32139beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
32239beb93cSSam Leffler 
32339beb93cSSam Leffler 	data->state = PAX_DONE;
32439beb93cSSam Leffler 	ret->methodState = METHOD_DONE;
32539beb93cSSam Leffler 	ret->decision = DECISION_UNCOND_SUCC;
326*c1d255d3SCy Schubert 	ret->allowNotifications = false;
32739beb93cSSam Leffler 
32839beb93cSSam Leffler 	return resp;
32939beb93cSSam Leffler }
33039beb93cSSam Leffler 
33139beb93cSSam Leffler 
eap_pax_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)33239beb93cSSam Leffler static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
33339beb93cSSam Leffler 				       struct eap_method_ret *ret,
33439beb93cSSam Leffler 				       const struct wpabuf *reqData)
33539beb93cSSam Leffler {
33639beb93cSSam Leffler 	struct eap_pax_data *data = priv;
33739beb93cSSam Leffler 	const struct eap_pax_hdr *req;
33839beb93cSSam Leffler 	struct wpabuf *resp;
33939beb93cSSam Leffler 	u8 icvbuf[EAP_PAX_ICV_LEN], id;
34039beb93cSSam Leffler 	const u8 *icv, *pos;
34139beb93cSSam Leffler 	size_t len;
34239beb93cSSam Leffler 	u16 flen, mlen;
34339beb93cSSam Leffler 
34439beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
345325151a3SRui Paulo 	if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) {
346*c1d255d3SCy Schubert 		ret->ignore = true;
34739beb93cSSam Leffler 		return NULL;
34839beb93cSSam Leffler 	}
34939beb93cSSam Leffler 	id = eap_get_id(reqData);
35039beb93cSSam Leffler 	req = (const struct eap_pax_hdr *) pos;
35139beb93cSSam Leffler 	flen = len - EAP_PAX_ICV_LEN;
35239beb93cSSam Leffler 	mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
35339beb93cSSam Leffler 
35439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
35539beb93cSSam Leffler 		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
35639beb93cSSam Leffler 		   "public_key_id 0x%x",
35739beb93cSSam Leffler 		   req->op_code, req->flags, req->mac_id, req->dh_group_id,
35839beb93cSSam Leffler 		   req->public_key_id);
35939beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
36039beb93cSSam Leffler 		    pos, len - EAP_PAX_ICV_LEN);
36139beb93cSSam Leffler 
36239beb93cSSam Leffler 	if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
36339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
36439beb93cSSam Leffler 			   "authentication (was 0x%d, is 0x%d)",
36539beb93cSSam Leffler 			   data->mac_id, req->mac_id);
366*c1d255d3SCy Schubert 		ret->ignore = true;
36739beb93cSSam Leffler 		return NULL;
36839beb93cSSam Leffler 	}
36939beb93cSSam Leffler 
37039beb93cSSam Leffler 	if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
37139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
37239beb93cSSam Leffler 			   "authentication (was 0x%d, is 0x%d)",
37339beb93cSSam Leffler 			   data->dh_group_id, req->dh_group_id);
374*c1d255d3SCy Schubert 		ret->ignore = true;
37539beb93cSSam Leffler 		return NULL;
37639beb93cSSam Leffler 	}
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 	if (data->state != PAX_INIT &&
37939beb93cSSam Leffler 	    data->public_key_id != req->public_key_id) {
38039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
38139beb93cSSam Leffler 			   "authentication (was 0x%d, is 0x%d)",
38239beb93cSSam Leffler 			   data->public_key_id, req->public_key_id);
383*c1d255d3SCy Schubert 		ret->ignore = true;
38439beb93cSSam Leffler 		return NULL;
38539beb93cSSam Leffler 	}
38639beb93cSSam Leffler 
38739beb93cSSam Leffler 	/* TODO: add support EAP_PAX_HMAC_SHA256_128 */
38839beb93cSSam Leffler 	if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
38939beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
39039beb93cSSam Leffler 			   req->mac_id);
391*c1d255d3SCy Schubert 		ret->ignore = true;
39239beb93cSSam Leffler 		return NULL;
39339beb93cSSam Leffler 	}
39439beb93cSSam Leffler 
39539beb93cSSam Leffler 	if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
39639beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
39739beb93cSSam Leffler 			   req->dh_group_id);
398*c1d255d3SCy Schubert 		ret->ignore = true;
39939beb93cSSam Leffler 		return NULL;
40039beb93cSSam Leffler 	}
40139beb93cSSam Leffler 
40239beb93cSSam Leffler 	if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
40339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
40439beb93cSSam Leffler 			   req->public_key_id);
405*c1d255d3SCy Schubert 		ret->ignore = true;
40639beb93cSSam Leffler 		return NULL;
40739beb93cSSam Leffler 	}
40839beb93cSSam Leffler 
40939beb93cSSam Leffler 	if (req->flags & EAP_PAX_FLAGS_MF) {
41039beb93cSSam Leffler 		/* TODO: add support for reassembling fragments */
41139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
41239beb93cSSam Leffler 			   "ignored packet");
413*c1d255d3SCy Schubert 		ret->ignore = true;
41439beb93cSSam Leffler 		return NULL;
41539beb93cSSam Leffler 	}
41639beb93cSSam Leffler 
41739beb93cSSam Leffler 	icv = pos + len - EAP_PAX_ICV_LEN;
41839beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
41939beb93cSSam Leffler 	if (req->op_code == EAP_PAX_OP_STD_1) {
42039beb93cSSam Leffler 		eap_pax_mac(req->mac_id, (u8 *) "", 0,
42139beb93cSSam Leffler 			    wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
42239beb93cSSam Leffler 			    icvbuf);
42339beb93cSSam Leffler 	} else {
42439beb93cSSam Leffler 		eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
42539beb93cSSam Leffler 			    wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
42639beb93cSSam Leffler 			    icvbuf);
42739beb93cSSam Leffler 	}
4285b9c547cSRui Paulo 	if (os_memcmp_const(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
42939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
43039beb93cSSam Leffler 			   "message");
43139beb93cSSam Leffler 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
43239beb93cSSam Leffler 			    icvbuf, EAP_PAX_ICV_LEN);
433*c1d255d3SCy Schubert 		ret->ignore = true;
43439beb93cSSam Leffler 		return NULL;
43539beb93cSSam Leffler 	}
43639beb93cSSam Leffler 
437*c1d255d3SCy Schubert 	ret->ignore = false;
43839beb93cSSam Leffler 	ret->methodState = METHOD_MAY_CONT;
43939beb93cSSam Leffler 	ret->decision = DECISION_FAIL;
440*c1d255d3SCy Schubert 	ret->allowNotifications = true;
44139beb93cSSam Leffler 
44239beb93cSSam Leffler 	switch (req->op_code) {
44339beb93cSSam Leffler 	case EAP_PAX_OP_STD_1:
44439beb93cSSam Leffler 		resp = eap_pax_process_std_1(data, ret, id, req, flen);
44539beb93cSSam Leffler 		break;
44639beb93cSSam Leffler 	case EAP_PAX_OP_STD_3:
44739beb93cSSam Leffler 		resp = eap_pax_process_std_3(data, ret, id, req, flen);
44839beb93cSSam Leffler 		break;
44939beb93cSSam Leffler 	default:
45039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
45139beb93cSSam Leffler 			   "op_code %d", req->op_code);
452*c1d255d3SCy Schubert 		ret->ignore = true;
45339beb93cSSam Leffler 		return NULL;
45439beb93cSSam Leffler 	}
45539beb93cSSam Leffler 
45639beb93cSSam Leffler 	if (ret->methodState == METHOD_DONE) {
457*c1d255d3SCy Schubert 		ret->allowNotifications = false;
45839beb93cSSam Leffler 	}
45939beb93cSSam Leffler 
46039beb93cSSam Leffler 	return resp;
46139beb93cSSam Leffler }
46239beb93cSSam Leffler 
46339beb93cSSam Leffler 
eap_pax_isKeyAvailable(struct eap_sm * sm,void * priv)464*c1d255d3SCy Schubert static bool eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
46539beb93cSSam Leffler {
46639beb93cSSam Leffler 	struct eap_pax_data *data = priv;
46739beb93cSSam Leffler 	return data->state == PAX_DONE;
46839beb93cSSam Leffler }
46939beb93cSSam Leffler 
47039beb93cSSam Leffler 
eap_pax_getKey(struct eap_sm * sm,void * priv,size_t * len)47139beb93cSSam Leffler static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
47239beb93cSSam Leffler {
47339beb93cSSam Leffler 	struct eap_pax_data *data = priv;
47439beb93cSSam Leffler 	u8 *key;
47539beb93cSSam Leffler 
47639beb93cSSam Leffler 	if (data->state != PAX_DONE)
47739beb93cSSam Leffler 		return NULL;
47839beb93cSSam Leffler 
47939beb93cSSam Leffler 	key = os_malloc(EAP_MSK_LEN);
48039beb93cSSam Leffler 	if (key == NULL)
48139beb93cSSam Leffler 		return NULL;
48239beb93cSSam Leffler 
48339beb93cSSam Leffler 	*len = EAP_MSK_LEN;
484780fb4a2SCy Schubert 	if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
485780fb4a2SCy Schubert 			"Master Session Key",
486780fb4a2SCy Schubert 			data->rand.e, 2 * EAP_PAX_RAND_LEN,
487780fb4a2SCy Schubert 			EAP_MSK_LEN, key) < 0) {
488780fb4a2SCy Schubert 		os_free(key);
489780fb4a2SCy Schubert 		return NULL;
490780fb4a2SCy Schubert 	}
49139beb93cSSam Leffler 
49239beb93cSSam Leffler 	return key;
49339beb93cSSam Leffler }
49439beb93cSSam Leffler 
49539beb93cSSam Leffler 
eap_pax_get_emsk(struct eap_sm * sm,void * priv,size_t * len)49639beb93cSSam Leffler static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
49739beb93cSSam Leffler {
49839beb93cSSam Leffler 	struct eap_pax_data *data = priv;
49939beb93cSSam Leffler 	u8 *key;
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 	if (data->state != PAX_DONE)
50239beb93cSSam Leffler 		return NULL;
50339beb93cSSam Leffler 
50439beb93cSSam Leffler 	key = os_malloc(EAP_EMSK_LEN);
50539beb93cSSam Leffler 	if (key == NULL)
50639beb93cSSam Leffler 		return NULL;
50739beb93cSSam Leffler 
50839beb93cSSam Leffler 	*len = EAP_EMSK_LEN;
509780fb4a2SCy Schubert 	if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
51039beb93cSSam Leffler 			"Extended Master Session Key",
51139beb93cSSam Leffler 			data->rand.e, 2 * EAP_PAX_RAND_LEN,
512780fb4a2SCy Schubert 			EAP_EMSK_LEN, key) < 0) {
513780fb4a2SCy Schubert 		os_free(key);
514780fb4a2SCy Schubert 		return NULL;
515780fb4a2SCy Schubert 	}
51639beb93cSSam Leffler 
51739beb93cSSam Leffler 	return key;
51839beb93cSSam Leffler }
51939beb93cSSam Leffler 
52039beb93cSSam Leffler 
eap_pax_get_session_id(struct eap_sm * sm,void * priv,size_t * len)5215b9c547cSRui Paulo static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
5225b9c547cSRui Paulo {
5235b9c547cSRui Paulo 	struct eap_pax_data *data = priv;
5245b9c547cSRui Paulo 	u8 *sid;
5255b9c547cSRui Paulo 
5265b9c547cSRui Paulo 	if (data->state != PAX_DONE)
5275b9c547cSRui Paulo 		return NULL;
5285b9c547cSRui Paulo 
5295b9c547cSRui Paulo 	sid = os_malloc(1 + EAP_PAX_MID_LEN);
5305b9c547cSRui Paulo 	if (sid == NULL)
5315b9c547cSRui Paulo 		return NULL;
5325b9c547cSRui Paulo 
5335b9c547cSRui Paulo 	*len = 1 + EAP_PAX_MID_LEN;
5345b9c547cSRui Paulo 	sid[0] = EAP_TYPE_PAX;
5355b9c547cSRui Paulo 	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
5365b9c547cSRui Paulo 
5375b9c547cSRui Paulo 	return sid;
5385b9c547cSRui Paulo }
5395b9c547cSRui Paulo 
5405b9c547cSRui Paulo 
eap_peer_pax_register(void)54139beb93cSSam Leffler int eap_peer_pax_register(void)
54239beb93cSSam Leffler {
54339beb93cSSam Leffler 	struct eap_method *eap;
54439beb93cSSam Leffler 
54539beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
54639beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
54739beb93cSSam Leffler 	if (eap == NULL)
54839beb93cSSam Leffler 		return -1;
54939beb93cSSam Leffler 
55039beb93cSSam Leffler 	eap->init = eap_pax_init;
55139beb93cSSam Leffler 	eap->deinit = eap_pax_deinit;
55239beb93cSSam Leffler 	eap->process = eap_pax_process;
55339beb93cSSam Leffler 	eap->isKeyAvailable = eap_pax_isKeyAvailable;
55439beb93cSSam Leffler 	eap->getKey = eap_pax_getKey;
55539beb93cSSam Leffler 	eap->get_emsk = eap_pax_get_emsk;
5565b9c547cSRui Paulo 	eap->getSessionId = eap_pax_get_session_id;
55739beb93cSSam Leffler 
558780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
55939beb93cSSam Leffler }
560