1189251Ssam/*
2189251Ssam * EAP-FAST common helper functions (RFC 4851)
3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12214734Srpaulo#include "crypto/sha1.h"
13214734Srpaulo#include "crypto/tls.h"
14189251Ssam#include "eap_defs.h"
15189251Ssam#include "eap_tlv_common.h"
16189251Ssam#include "eap_fast_common.h"
17189251Ssam
18189251Ssam
19189251Ssamvoid eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
20189251Ssam{
21189251Ssam	struct pac_tlv_hdr hdr;
22189251Ssam	hdr.type = host_to_be16(type);
23189251Ssam	hdr.len = host_to_be16(len);
24189251Ssam	wpabuf_put_data(buf, &hdr, sizeof(hdr));
25189251Ssam}
26189251Ssam
27189251Ssam
28189251Ssamvoid eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
29189251Ssam			     u16 len)
30189251Ssam{
31189251Ssam	eap_fast_put_tlv_hdr(buf, type, len);
32189251Ssam	wpabuf_put_data(buf, data, len);
33189251Ssam}
34189251Ssam
35189251Ssam
36189251Ssamvoid eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
37189251Ssam				 const struct wpabuf *data)
38189251Ssam{
39189251Ssam	eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
40189251Ssam	wpabuf_put_buf(buf, data);
41189251Ssam}
42189251Ssam
43189251Ssam
44189251Ssamstruct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
45189251Ssam{
46189251Ssam	struct wpabuf *e;
47189251Ssam
48189251Ssam	if (buf == NULL)
49189251Ssam		return NULL;
50189251Ssam
51189251Ssam	/* Encapsulate EAP packet in EAP-Payload TLV */
52189251Ssam	wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
53189251Ssam	e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
54189251Ssam	if (e == NULL) {
55189251Ssam		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
56189251Ssam			   "for TLV encapsulation");
57189251Ssam		wpabuf_free(buf);
58189251Ssam		return NULL;
59189251Ssam	}
60189251Ssam	eap_fast_put_tlv_buf(e,
61189251Ssam			     EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
62189251Ssam			     buf);
63189251Ssam	wpabuf_free(buf);
64189251Ssam	return e;
65189251Ssam}
66189251Ssam
67189251Ssam
68189251Ssamvoid eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
69189251Ssam				   const u8 *client_random, u8 *master_secret)
70189251Ssam{
71189251Ssam#define TLS_RANDOM_LEN 32
72189251Ssam#define TLS_MASTER_SECRET_LEN 48
73189251Ssam	u8 seed[2 * TLS_RANDOM_LEN];
74189251Ssam
75189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
76189251Ssam		    client_random, TLS_RANDOM_LEN);
77189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
78189251Ssam		    server_random, TLS_RANDOM_LEN);
79189251Ssam
80189251Ssam	/*
81189251Ssam	 * RFC 4851, Section 5.1:
82189251Ssam	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
83189251Ssam	 *                       server_random + client_random, 48)
84189251Ssam	 */
85189251Ssam	os_memcpy(seed, server_random, TLS_RANDOM_LEN);
86189251Ssam	os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
87189251Ssam	sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
88189251Ssam		   "PAC to master secret label hash",
89189251Ssam		   seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
90189251Ssam
91189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
92189251Ssam			master_secret, TLS_MASTER_SECRET_LEN);
93189251Ssam}
94189251Ssam
95189251Ssam
96189251Ssamu8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
97189251Ssam			 const char *label, size_t len)
98189251Ssam{
99189251Ssam	struct tls_keys keys;
100189251Ssam	u8 *rnd = NULL, *out;
101189251Ssam	int block_size;
102189251Ssam
103189251Ssam	block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
104189251Ssam	if (block_size < 0)
105189251Ssam		return NULL;
106189251Ssam
107189251Ssam	out = os_malloc(block_size + len);
108189251Ssam	if (out == NULL)
109189251Ssam		return NULL;
110189251Ssam
111189251Ssam	if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
112189251Ssam	    == 0) {
113189251Ssam		os_memmove(out, out + block_size, len);
114189251Ssam		return out;
115189251Ssam	}
116189251Ssam
117189251Ssam	if (tls_connection_get_keys(ssl_ctx, conn, &keys))
118189251Ssam		goto fail;
119189251Ssam
120189251Ssam	rnd = os_malloc(keys.client_random_len + keys.server_random_len);
121189251Ssam	if (rnd == NULL)
122189251Ssam		goto fail;
123189251Ssam
124189251Ssam	os_memcpy(rnd, keys.server_random, keys.server_random_len);
125189251Ssam	os_memcpy(rnd + keys.server_random_len, keys.client_random,
126189251Ssam		  keys.client_random_len);
127189251Ssam
128189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
129189251Ssam			"expansion", keys.master_key, keys.master_key_len);
130252726Srpaulo	if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
131252726Srpaulo			     label, rnd, keys.client_random_len +
132252726Srpaulo			     keys.server_random_len, out, block_size + len))
133189251Ssam		goto fail;
134189251Ssam	os_free(rnd);
135189251Ssam	os_memmove(out, out + block_size, len);
136189251Ssam	return out;
137189251Ssam
138189251Ssamfail:
139189251Ssam	os_free(rnd);
140189251Ssam	os_free(out);
141189251Ssam	return NULL;
142189251Ssam}
143189251Ssam
144189251Ssam
145189251Ssamvoid eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
146189251Ssam{
147189251Ssam	/*
148189251Ssam	 * RFC 4851, Section 5.4: EAP Master Session Key Generation
149189251Ssam	 * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
150189251Ssam	 */
151189251Ssam
152189251Ssam	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
153189251Ssam		   "Session Key Generating Function", (u8 *) "", 0,
154189251Ssam		   msk, EAP_FAST_KEY_LEN);
155189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
156189251Ssam			msk, EAP_FAST_KEY_LEN);
157189251Ssam}
158189251Ssam
159189251Ssam
160189251Ssamvoid eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
161189251Ssam{
162189251Ssam	/*
163189251Ssam	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
164189251Ssam	 * EMSK = T-PRF(S-IMCK[j],
165189251Ssam	 *        "Extended Session Key Generating Function", 64)
166189251Ssam	 */
167189251Ssam
168189251Ssam	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
169189251Ssam		   "Extended Session Key Generating Function", (u8 *) "", 0,
170189251Ssam		   emsk, EAP_EMSK_LEN);
171189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
172189251Ssam			emsk, EAP_EMSK_LEN);
173189251Ssam}
174189251Ssam
175189251Ssam
176189251Ssamint eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
177189251Ssam		       int tlv_type, u8 *pos, int len)
178189251Ssam{
179189251Ssam	switch (tlv_type) {
180189251Ssam	case EAP_TLV_EAP_PAYLOAD_TLV:
181189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
182189251Ssam			    pos, len);
183189251Ssam		if (tlv->eap_payload_tlv) {
184189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
185189251Ssam				   "EAP-Payload TLV in the message");
186189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
187189251Ssam			return -2;
188189251Ssam		}
189189251Ssam		tlv->eap_payload_tlv = pos;
190189251Ssam		tlv->eap_payload_tlv_len = len;
191189251Ssam		break;
192189251Ssam	case EAP_TLV_RESULT_TLV:
193189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
194189251Ssam		if (tlv->result) {
195189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
196189251Ssam				   "Result TLV in the message");
197189251Ssam			tlv->result = EAP_TLV_RESULT_FAILURE;
198189251Ssam			return -2;
199189251Ssam		}
200189251Ssam		if (len < 2) {
201189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
202189251Ssam				   "Result TLV");
203189251Ssam			tlv->result = EAP_TLV_RESULT_FAILURE;
204189251Ssam			break;
205189251Ssam		}
206189251Ssam		tlv->result = WPA_GET_BE16(pos);
207189251Ssam		if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
208189251Ssam		    tlv->result != EAP_TLV_RESULT_FAILURE) {
209189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
210189251Ssam				   tlv->result);
211189251Ssam			tlv->result = EAP_TLV_RESULT_FAILURE;
212189251Ssam		}
213189251Ssam		wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
214189251Ssam			   tlv->result == EAP_TLV_RESULT_SUCCESS ?
215189251Ssam			   "Success" : "Failure");
216189251Ssam		break;
217189251Ssam	case EAP_TLV_INTERMEDIATE_RESULT_TLV:
218189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
219189251Ssam			    pos, len);
220189251Ssam		if (len < 2) {
221189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
222189251Ssam				   "Intermediate-Result TLV");
223189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
224189251Ssam			break;
225189251Ssam		}
226189251Ssam		if (tlv->iresult) {
227189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
228189251Ssam				   "Intermediate-Result TLV in the message");
229189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
230189251Ssam			return -2;
231189251Ssam		}
232189251Ssam		tlv->iresult = WPA_GET_BE16(pos);
233189251Ssam		if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
234189251Ssam		    tlv->iresult != EAP_TLV_RESULT_FAILURE) {
235189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
236189251Ssam				   "Result %d", tlv->iresult);
237189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
238189251Ssam		}
239189251Ssam		wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
240189251Ssam			   tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
241189251Ssam			   "Success" : "Failure");
242189251Ssam		break;
243189251Ssam	case EAP_TLV_CRYPTO_BINDING_TLV:
244189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
245189251Ssam			    pos, len);
246189251Ssam		if (tlv->crypto_binding) {
247189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
248189251Ssam				   "Crypto-Binding TLV in the message");
249189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
250189251Ssam			return -2;
251189251Ssam		}
252189251Ssam		tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
253189251Ssam		if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
254189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
255189251Ssam				   "Crypto-Binding TLV");
256189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
257189251Ssam			return -2;
258189251Ssam		}
259189251Ssam		tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
260189251Ssam			(pos - sizeof(struct eap_tlv_hdr));
261189251Ssam		break;
262189251Ssam	case EAP_TLV_REQUEST_ACTION_TLV:
263189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
264189251Ssam			    pos, len);
265189251Ssam		if (tlv->request_action) {
266189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
267189251Ssam				   "Request-Action TLV in the message");
268189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
269189251Ssam			return -2;
270189251Ssam		}
271189251Ssam		if (len < 2) {
272189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
273189251Ssam				   "Request-Action TLV");
274189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
275189251Ssam			break;
276189251Ssam		}
277189251Ssam		tlv->request_action = WPA_GET_BE16(pos);
278189251Ssam		wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
279189251Ssam			   tlv->request_action);
280189251Ssam		break;
281189251Ssam	case EAP_TLV_PAC_TLV:
282189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
283189251Ssam		if (tlv->pac) {
284189251Ssam			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
285189251Ssam				   "PAC TLV in the message");
286189251Ssam			tlv->iresult = EAP_TLV_RESULT_FAILURE;
287189251Ssam			return -2;
288189251Ssam		}
289189251Ssam		tlv->pac = pos;
290189251Ssam		tlv->pac_len = len;
291189251Ssam		break;
292189251Ssam	default:
293189251Ssam		/* Unknown TLV */
294189251Ssam		return -1;
295189251Ssam	}
296189251Ssam
297189251Ssam	return 0;
298189251Ssam}
299