16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP-FAST common helper functions (RFC 4851)
36d49e1aeSJan Lentfer  * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  */
86d49e1aeSJan Lentfer 
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer 
116d49e1aeSJan Lentfer #include "common.h"
123ff40c12SJohn Marino #include "crypto/sha1.h"
133ff40c12SJohn Marino #include "crypto/tls.h"
146d49e1aeSJan Lentfer #include "eap_defs.h"
156d49e1aeSJan Lentfer #include "eap_tlv_common.h"
166d49e1aeSJan Lentfer #include "eap_fast_common.h"
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer 
eap_fast_put_tlv_hdr(struct wpabuf * buf,u16 type,u16 len)196d49e1aeSJan Lentfer void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
206d49e1aeSJan Lentfer {
216d49e1aeSJan Lentfer 	struct pac_tlv_hdr hdr;
226d49e1aeSJan Lentfer 	hdr.type = host_to_be16(type);
236d49e1aeSJan Lentfer 	hdr.len = host_to_be16(len);
246d49e1aeSJan Lentfer 	wpabuf_put_data(buf, &hdr, sizeof(hdr));
256d49e1aeSJan Lentfer }
266d49e1aeSJan Lentfer 
276d49e1aeSJan Lentfer 
eap_fast_put_tlv(struct wpabuf * buf,u16 type,const void * data,u16 len)286d49e1aeSJan Lentfer void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
296d49e1aeSJan Lentfer 			     u16 len)
306d49e1aeSJan Lentfer {
316d49e1aeSJan Lentfer 	eap_fast_put_tlv_hdr(buf, type, len);
326d49e1aeSJan Lentfer 	wpabuf_put_data(buf, data, len);
336d49e1aeSJan Lentfer }
346d49e1aeSJan Lentfer 
356d49e1aeSJan Lentfer 
eap_fast_put_tlv_buf(struct wpabuf * buf,u16 type,const struct wpabuf * data)366d49e1aeSJan Lentfer void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
376d49e1aeSJan Lentfer 				 const struct wpabuf *data)
386d49e1aeSJan Lentfer {
396d49e1aeSJan Lentfer 	eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
406d49e1aeSJan Lentfer 	wpabuf_put_buf(buf, data);
416d49e1aeSJan Lentfer }
426d49e1aeSJan Lentfer 
436d49e1aeSJan Lentfer 
eap_fast_tlv_eap_payload(struct wpabuf * buf)446d49e1aeSJan Lentfer struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
456d49e1aeSJan Lentfer {
466d49e1aeSJan Lentfer 	struct wpabuf *e;
476d49e1aeSJan Lentfer 
486d49e1aeSJan Lentfer 	if (buf == NULL)
496d49e1aeSJan Lentfer 		return NULL;
506d49e1aeSJan Lentfer 
516d49e1aeSJan Lentfer 	/* Encapsulate EAP packet in EAP-Payload TLV */
526d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
536d49e1aeSJan Lentfer 	e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
546d49e1aeSJan Lentfer 	if (e == NULL) {
556d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
566d49e1aeSJan Lentfer 			   "for TLV encapsulation");
576d49e1aeSJan Lentfer 		wpabuf_free(buf);
586d49e1aeSJan Lentfer 		return NULL;
596d49e1aeSJan Lentfer 	}
606d49e1aeSJan Lentfer 	eap_fast_put_tlv_buf(e,
616d49e1aeSJan Lentfer 			     EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
626d49e1aeSJan Lentfer 			     buf);
636d49e1aeSJan Lentfer 	wpabuf_free(buf);
646d49e1aeSJan Lentfer 	return e;
656d49e1aeSJan Lentfer }
666d49e1aeSJan Lentfer 
676d49e1aeSJan Lentfer 
eap_fast_derive_master_secret(const u8 * pac_key,const u8 * server_random,const u8 * client_random,u8 * master_secret)686d49e1aeSJan Lentfer void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
696d49e1aeSJan Lentfer 				   const u8 *client_random, u8 *master_secret)
706d49e1aeSJan Lentfer {
716d49e1aeSJan Lentfer #define TLS_RANDOM_LEN 32
726d49e1aeSJan Lentfer #define TLS_MASTER_SECRET_LEN 48
736d49e1aeSJan Lentfer 	u8 seed[2 * TLS_RANDOM_LEN];
746d49e1aeSJan Lentfer 
756d49e1aeSJan Lentfer 	wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
766d49e1aeSJan Lentfer 		    client_random, TLS_RANDOM_LEN);
776d49e1aeSJan Lentfer 	wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
786d49e1aeSJan Lentfer 		    server_random, TLS_RANDOM_LEN);
796d49e1aeSJan Lentfer 
806d49e1aeSJan Lentfer 	/*
816d49e1aeSJan Lentfer 	 * RFC 4851, Section 5.1:
826d49e1aeSJan Lentfer 	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
836d49e1aeSJan Lentfer 	 *                       server_random + client_random, 48)
846d49e1aeSJan Lentfer 	 */
856d49e1aeSJan Lentfer 	os_memcpy(seed, server_random, TLS_RANDOM_LEN);
866d49e1aeSJan Lentfer 	os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
876d49e1aeSJan Lentfer 	sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
886d49e1aeSJan Lentfer 		   "PAC to master secret label hash",
896d49e1aeSJan Lentfer 		   seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
906d49e1aeSJan Lentfer 
916d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
926d49e1aeSJan Lentfer 			master_secret, TLS_MASTER_SECRET_LEN);
936d49e1aeSJan Lentfer }
946d49e1aeSJan Lentfer 
956d49e1aeSJan Lentfer 
eap_fast_derive_key(void * ssl_ctx,struct tls_connection * conn,size_t len)96*a1157835SDaniel Fojt u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len)
976d49e1aeSJan Lentfer {
98*a1157835SDaniel Fojt 	u8 *out;
996d49e1aeSJan Lentfer 
100*a1157835SDaniel Fojt 	out = os_malloc(len);
1016d49e1aeSJan Lentfer 	if (out == NULL)
1026d49e1aeSJan Lentfer 		return NULL;
1036d49e1aeSJan Lentfer 
104*a1157835SDaniel Fojt 	if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) {
1056d49e1aeSJan Lentfer 		os_free(out);
1066d49e1aeSJan Lentfer 		return NULL;
1076d49e1aeSJan Lentfer 	}
1086d49e1aeSJan Lentfer 
109*a1157835SDaniel Fojt 	return out;
110*a1157835SDaniel Fojt }
1116d49e1aeSJan Lentfer 
112*a1157835SDaniel Fojt 
eap_fast_derive_eap_msk(const u8 * simck,u8 * msk)113*a1157835SDaniel Fojt int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
1146d49e1aeSJan Lentfer {
1156d49e1aeSJan Lentfer 	/*
1166d49e1aeSJan Lentfer 	 * RFC 4851, Section 5.4: EAP Master Session Key Generation
1176d49e1aeSJan Lentfer 	 * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
1186d49e1aeSJan Lentfer 	 */
1196d49e1aeSJan Lentfer 
120*a1157835SDaniel Fojt 	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
1216d49e1aeSJan Lentfer 		       "Session Key Generating Function", (u8 *) "", 0,
122*a1157835SDaniel Fojt 		       msk, EAP_FAST_KEY_LEN) < 0)
123*a1157835SDaniel Fojt 		return -1;
1246d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
1256d49e1aeSJan Lentfer 			msk, EAP_FAST_KEY_LEN);
126*a1157835SDaniel Fojt 	return 0;
1276d49e1aeSJan Lentfer }
1286d49e1aeSJan Lentfer 
1296d49e1aeSJan Lentfer 
eap_fast_derive_eap_emsk(const u8 * simck,u8 * emsk)130*a1157835SDaniel Fojt int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
1316d49e1aeSJan Lentfer {
1326d49e1aeSJan Lentfer 	/*
1336d49e1aeSJan Lentfer 	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
1346d49e1aeSJan Lentfer 	 * EMSK = T-PRF(S-IMCK[j],
1356d49e1aeSJan Lentfer 	 *        "Extended Session Key Generating Function", 64)
1366d49e1aeSJan Lentfer 	 */
1376d49e1aeSJan Lentfer 
138*a1157835SDaniel Fojt 	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
1396d49e1aeSJan Lentfer 		       "Extended Session Key Generating Function", (u8 *) "", 0,
140*a1157835SDaniel Fojt 		       emsk, EAP_EMSK_LEN) < 0)
141*a1157835SDaniel Fojt 		return -1;
1426d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
1436d49e1aeSJan Lentfer 			emsk, EAP_EMSK_LEN);
144*a1157835SDaniel Fojt 	return 0;
1456d49e1aeSJan Lentfer }
1466d49e1aeSJan Lentfer 
1476d49e1aeSJan Lentfer 
eap_fast_parse_tlv(struct eap_fast_tlv_parse * tlv,int tlv_type,u8 * pos,size_t len)1486d49e1aeSJan Lentfer int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
149*a1157835SDaniel Fojt 		       int tlv_type, u8 *pos, size_t len)
1506d49e1aeSJan Lentfer {
1516d49e1aeSJan Lentfer 	switch (tlv_type) {
1526d49e1aeSJan Lentfer 	case EAP_TLV_EAP_PAYLOAD_TLV:
1536d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
1546d49e1aeSJan Lentfer 			    pos, len);
1556d49e1aeSJan Lentfer 		if (tlv->eap_payload_tlv) {
1566d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
1576d49e1aeSJan Lentfer 				   "EAP-Payload TLV in the message");
1586d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
1596d49e1aeSJan Lentfer 			return -2;
1606d49e1aeSJan Lentfer 		}
1616d49e1aeSJan Lentfer 		tlv->eap_payload_tlv = pos;
1626d49e1aeSJan Lentfer 		tlv->eap_payload_tlv_len = len;
1636d49e1aeSJan Lentfer 		break;
1646d49e1aeSJan Lentfer 	case EAP_TLV_RESULT_TLV:
1656d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
1666d49e1aeSJan Lentfer 		if (tlv->result) {
1676d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
1686d49e1aeSJan Lentfer 				   "Result TLV in the message");
1696d49e1aeSJan Lentfer 			tlv->result = EAP_TLV_RESULT_FAILURE;
1706d49e1aeSJan Lentfer 			return -2;
1716d49e1aeSJan Lentfer 		}
1726d49e1aeSJan Lentfer 		if (len < 2) {
1736d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
1746d49e1aeSJan Lentfer 				   "Result TLV");
1756d49e1aeSJan Lentfer 			tlv->result = EAP_TLV_RESULT_FAILURE;
1766d49e1aeSJan Lentfer 			break;
1776d49e1aeSJan Lentfer 		}
1786d49e1aeSJan Lentfer 		tlv->result = WPA_GET_BE16(pos);
1796d49e1aeSJan Lentfer 		if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
1806d49e1aeSJan Lentfer 		    tlv->result != EAP_TLV_RESULT_FAILURE) {
1816d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
1826d49e1aeSJan Lentfer 				   tlv->result);
1836d49e1aeSJan Lentfer 			tlv->result = EAP_TLV_RESULT_FAILURE;
1846d49e1aeSJan Lentfer 		}
1856d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
1866d49e1aeSJan Lentfer 			   tlv->result == EAP_TLV_RESULT_SUCCESS ?
1876d49e1aeSJan Lentfer 			   "Success" : "Failure");
1886d49e1aeSJan Lentfer 		break;
1896d49e1aeSJan Lentfer 	case EAP_TLV_INTERMEDIATE_RESULT_TLV:
1906d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
1916d49e1aeSJan Lentfer 			    pos, len);
1926d49e1aeSJan Lentfer 		if (len < 2) {
1936d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
1946d49e1aeSJan Lentfer 				   "Intermediate-Result TLV");
1956d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
1966d49e1aeSJan Lentfer 			break;
1976d49e1aeSJan Lentfer 		}
1986d49e1aeSJan Lentfer 		if (tlv->iresult) {
1996d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
2006d49e1aeSJan Lentfer 				   "Intermediate-Result TLV in the message");
2016d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2026d49e1aeSJan Lentfer 			return -2;
2036d49e1aeSJan Lentfer 		}
2046d49e1aeSJan Lentfer 		tlv->iresult = WPA_GET_BE16(pos);
2056d49e1aeSJan Lentfer 		if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
2066d49e1aeSJan Lentfer 		    tlv->iresult != EAP_TLV_RESULT_FAILURE) {
2076d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
2086d49e1aeSJan Lentfer 				   "Result %d", tlv->iresult);
2096d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2106d49e1aeSJan Lentfer 		}
2116d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
2126d49e1aeSJan Lentfer 			   tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
2136d49e1aeSJan Lentfer 			   "Success" : "Failure");
2146d49e1aeSJan Lentfer 		break;
2156d49e1aeSJan Lentfer 	case EAP_TLV_CRYPTO_BINDING_TLV:
2166d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
2176d49e1aeSJan Lentfer 			    pos, len);
2186d49e1aeSJan Lentfer 		if (tlv->crypto_binding) {
2196d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
2206d49e1aeSJan Lentfer 				   "Crypto-Binding TLV in the message");
2216d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2226d49e1aeSJan Lentfer 			return -2;
2236d49e1aeSJan Lentfer 		}
2246d49e1aeSJan Lentfer 		tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
2256d49e1aeSJan Lentfer 		if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
2266d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
2276d49e1aeSJan Lentfer 				   "Crypto-Binding TLV");
2286d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2296d49e1aeSJan Lentfer 			return -2;
2306d49e1aeSJan Lentfer 		}
2316d49e1aeSJan Lentfer 		tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
2326d49e1aeSJan Lentfer 			(pos - sizeof(struct eap_tlv_hdr));
2336d49e1aeSJan Lentfer 		break;
2346d49e1aeSJan Lentfer 	case EAP_TLV_REQUEST_ACTION_TLV:
2356d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
2366d49e1aeSJan Lentfer 			    pos, len);
2376d49e1aeSJan Lentfer 		if (tlv->request_action) {
2386d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
2396d49e1aeSJan Lentfer 				   "Request-Action TLV in the message");
2406d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2416d49e1aeSJan Lentfer 			return -2;
2426d49e1aeSJan Lentfer 		}
2436d49e1aeSJan Lentfer 		if (len < 2) {
2446d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
2456d49e1aeSJan Lentfer 				   "Request-Action TLV");
2466d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2476d49e1aeSJan Lentfer 			break;
2486d49e1aeSJan Lentfer 		}
2496d49e1aeSJan Lentfer 		tlv->request_action = WPA_GET_BE16(pos);
2506d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
2516d49e1aeSJan Lentfer 			   tlv->request_action);
2526d49e1aeSJan Lentfer 		break;
2536d49e1aeSJan Lentfer 	case EAP_TLV_PAC_TLV:
2546d49e1aeSJan Lentfer 		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
2556d49e1aeSJan Lentfer 		if (tlv->pac) {
2566d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
2576d49e1aeSJan Lentfer 				   "PAC TLV in the message");
2586d49e1aeSJan Lentfer 			tlv->iresult = EAP_TLV_RESULT_FAILURE;
2596d49e1aeSJan Lentfer 			return -2;
2606d49e1aeSJan Lentfer 		}
2616d49e1aeSJan Lentfer 		tlv->pac = pos;
2626d49e1aeSJan Lentfer 		tlv->pac_len = len;
2636d49e1aeSJan Lentfer 		break;
2646d49e1aeSJan Lentfer 	default:
2656d49e1aeSJan Lentfer 		/* Unknown TLV */
2666d49e1aeSJan Lentfer 		return -1;
2676d49e1aeSJan Lentfer 	}
2686d49e1aeSJan Lentfer 
2696d49e1aeSJan Lentfer 	return 0;
2706d49e1aeSJan Lentfer }
271