• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * EAP server/peer: EAP-GPSK shared routines
3  * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "includes.h"
16 
17 #include "common.h"
18 #include "eap_defs.h"
19 #include "aes_wrap.h"
20 #include "crypto.h"
21 #ifdef EAP_GPSK_SHA256
22 #include "sha256.h"
23 #endif /* EAP_GPSK_SHA256 */
24 #include "eap_gpsk_common.h"
25 
26 
27 /**
28  * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
29  * @vendor: CSuite/Vendor
30  * @specifier: CSuite/Specifier
31  * Returns: 1 if ciphersuite is support, or 0 if not
32  */
eap_gpsk_supported_ciphersuite(int vendor,int specifier)33 int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
34 {
35 	if (vendor == EAP_GPSK_VENDOR_IETF &&
36 	    specifier == EAP_GPSK_CIPHER_AES)
37 		return 1;
38 #ifdef EAP_GPSK_SHA256
39 	if (vendor == EAP_GPSK_VENDOR_IETF &&
40 	    specifier == EAP_GPSK_CIPHER_SHA256)
41 		return 1;
42 #endif /* EAP_GPSK_SHA256 */
43 	return 0;
44 }
45 
46 
eap_gpsk_gkdf_cmac(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)47 static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
48 			      const u8 *data /* Z */, size_t data_len,
49 			      u8 *buf, size_t len /* X */)
50 {
51 	u8 *opos;
52 	size_t i, n, hashlen, left, clen;
53 	u8 ibuf[2], hash[16];
54 	const u8 *addr[2];
55 	size_t vlen[2];
56 
57 	hashlen = sizeof(hash);
58 	/* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
59 	addr[0] = ibuf;
60 	vlen[0] = sizeof(ibuf);
61 	addr[1] = data;
62 	vlen[1] = data_len;
63 
64 	opos = buf;
65 	left = len;
66 	n = (len + hashlen - 1) / hashlen;
67 	for (i = 1; i <= n; i++) {
68 		WPA_PUT_BE16(ibuf, i);
69 		omac1_aes_128_vector(psk, 2, addr, vlen, hash);
70 		clen = left > hashlen ? hashlen : left;
71 		os_memcpy(opos, hash, clen);
72 		opos += clen;
73 		left -= clen;
74 	}
75 
76 	return 0;
77 }
78 
79 
80 #ifdef EAP_GPSK_SHA256
eap_gpsk_gkdf_sha256(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)81 static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
82 				const u8 *data /* Z */, size_t data_len,
83 				u8 *buf, size_t len /* X */)
84 {
85 	u8 *opos;
86 	size_t i, n, hashlen, left, clen;
87 	u8 ibuf[2], hash[SHA256_MAC_LEN];
88 	const u8 *addr[2];
89 	size_t vlen[2];
90 
91 	hashlen = SHA256_MAC_LEN;
92 	/* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
93 	addr[0] = ibuf;
94 	vlen[0] = sizeof(ibuf);
95 	addr[1] = data;
96 	vlen[1] = data_len;
97 
98 	opos = buf;
99 	left = len;
100 	n = (len + hashlen - 1) / hashlen;
101 	for (i = 1; i <= n; i++) {
102 		WPA_PUT_BE16(ibuf, i);
103 		hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
104 		clen = left > hashlen ? hashlen : left;
105 		os_memcpy(opos, hash, clen);
106 		opos += clen;
107 		left -= clen;
108 	}
109 
110 	return 0;
111 }
112 #endif /* EAP_GPSK_SHA256 */
113 
114 
eap_gpsk_derive_keys_helper(u32 csuite_specifier,u8 * kdf_out,size_t kdf_out_len,const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t sk_len,u8 * pk,size_t pk_len)115 static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
116 				       u8 *kdf_out, size_t kdf_out_len,
117 				       const u8 *psk, size_t psk_len,
118 				       const u8 *seed, size_t seed_len,
119 				       u8 *msk, u8 *emsk,
120 				       u8 *sk, size_t sk_len,
121 				       u8 *pk, size_t pk_len)
122 {
123 	u8 mk[32], *pos, *data;
124 	size_t data_len, mk_len;
125 	int (*gkdf)(const u8 *psk, const u8 *data, size_t data_len,
126 		    u8 *buf, size_t len);
127 
128 	gkdf = NULL;
129 	switch (csuite_specifier) {
130 	case EAP_GPSK_CIPHER_AES:
131 		gkdf = eap_gpsk_gkdf_cmac;
132 		mk_len = 16;
133 		break;
134 #ifdef EAP_GPSK_SHA256
135 	case EAP_GPSK_CIPHER_SHA256:
136 		gkdf = eap_gpsk_gkdf_sha256;
137 		mk_len = SHA256_MAC_LEN;
138 		break;
139 #endif /* EAP_GPSK_SHA256 */
140 	default:
141 		return -1;
142 	}
143 
144 	if (psk_len < mk_len)
145 		return -1;
146 
147 	data_len = 2 + psk_len + 6 + seed_len;
148 	data = os_malloc(data_len);
149 	if (data == NULL)
150 		return -1;
151 	pos = data;
152 	WPA_PUT_BE16(pos, psk_len);
153 	pos += 2;
154 	os_memcpy(pos, psk, psk_len);
155 	pos += psk_len;
156 	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
157 	pos += 4;
158 	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
159 	pos += 2;
160 	os_memcpy(pos, seed, seed_len); /* inputString */
161 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
162 			data, data_len);
163 
164 	if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
165 		os_free(data);
166 		return -1;
167 	}
168 	os_free(data);
169 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
170 
171 	if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
172 		return -1;
173 
174 	pos = kdf_out;
175 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
176 	os_memcpy(msk, pos, EAP_MSK_LEN);
177 	pos += EAP_MSK_LEN;
178 
179 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
180 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
181 	pos += EAP_EMSK_LEN;
182 
183 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
184 	os_memcpy(sk, pos, sk_len);
185 	pos += sk_len;
186 
187 	if (pk) {
188 		wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
189 		os_memcpy(pk, pos, pk_len);
190 	}
191 
192 	return 0;
193 }
194 
195 
eap_gpsk_derive_keys_aes(const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len,u8 * pk,size_t * pk_len)196 static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
197 				    const u8 *seed, size_t seed_len,
198 				    u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
199 				    u8 *pk, size_t *pk_len)
200 {
201 #define EAP_GPSK_SK_LEN_AES 16
202 #define EAP_GPSK_PK_LEN_AES 16
203 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
204 		   EAP_GPSK_PK_LEN_AES];
205 
206 	/*
207 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
208 	 *            (= seed)
209 	 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
210 	 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
211 	 * MSK = GKDF-160 (MK, inputString)[0..63]
212 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
213 	 * SK = GKDF-160 (MK, inputString)[128..143]
214 	 * PK = GKDF-160 (MK, inputString)[144..159]
215 	 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
216 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
217 	 *                      CSuite_Sel || inputString)
218 	 */
219 
220 	*sk_len = EAP_GPSK_SK_LEN_AES;
221 	*pk_len = EAP_GPSK_PK_LEN_AES;
222 
223 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
224 					   kdf_out, sizeof(kdf_out),
225 					   psk, psk_len, seed, seed_len,
226 					   msk, emsk, sk, *sk_len,
227 					   pk, *pk_len);
228 }
229 
230 
231 #ifdef EAP_GPSK_SHA256
eap_gpsk_derive_keys_sha256(const u8 * psk,size_t psk_len,const u8 * seed,size_t seed_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len)232 static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
233 				       const u8 *seed, size_t seed_len,
234 				       u8 *msk, u8 *emsk,
235 				       u8 *sk, size_t *sk_len)
236 {
237 #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
238 #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
239 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
240 		   EAP_GPSK_PK_LEN_SHA256];
241 
242 	/*
243 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
244 	 *            (= seed)
245 	 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
246 	 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
247 	 * MSK = GKDF-160 (MK, inputString)[0..63]
248 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
249 	 * SK = GKDF-160 (MK, inputString)[128..159]
250 	 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
251 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
252 	 *                      CSuite_Sel || inputString)
253 	 */
254 
255 	*sk_len = EAP_GPSK_SK_LEN_SHA256;
256 
257 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
258 					   kdf_out, sizeof(kdf_out),
259 					   psk, psk_len, seed, seed_len,
260 					   msk, emsk, sk, *sk_len,
261 					   NULL, 0);
262 }
263 #endif /* EAP_GPSK_SHA256 */
264 
265 
266 /**
267  * eap_gpsk_derive_keys - Derive EAP-GPSK keys
268  * @psk: Pre-shared key
269  * @psk_len: Length of psk in bytes
270  * @vendor: CSuite/Vendor
271  * @specifier: CSuite/Specifier
272  * @rand_peer: 32-byte RAND_Peer
273  * @rand_server: 32-byte RAND_Server
274  * @id_peer: ID_Peer
275  * @id_peer_len: Length of ID_Peer
276  * @id_server: ID_Server
277  * @id_server_len: Length of ID_Server
278  * @msk: Buffer for 64-byte MSK
279  * @emsk: Buffer for 64-byte EMSK
280  * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
281  * @sk_len: Buffer for returning length of SK
282  * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
283  * @pk_len: Buffer for returning length of PK
284  * Returns: 0 on success, -1 on failure
285  */
eap_gpsk_derive_keys(const u8 * psk,size_t psk_len,int vendor,int specifier,const u8 * rand_peer,const u8 * rand_server,const u8 * id_peer,size_t id_peer_len,const u8 * id_server,size_t id_server_len,u8 * msk,u8 * emsk,u8 * sk,size_t * sk_len,u8 * pk,size_t * pk_len)286 int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
287 			 int specifier,
288 			 const u8 *rand_peer, const u8 *rand_server,
289 			 const u8 *id_peer, size_t id_peer_len,
290 			 const u8 *id_server, size_t id_server_len,
291 			 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
292 			 u8 *pk, size_t *pk_len)
293 {
294 	u8 *seed, *pos;
295 	size_t seed_len;
296 	int ret;
297 
298 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
299 		   vendor, specifier);
300 
301 	if (vendor != EAP_GPSK_VENDOR_IETF)
302 		return -1;
303 
304 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
305 
306 	/* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
307 	seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
308 	seed = os_malloc(seed_len);
309 	if (seed == NULL) {
310 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
311 			   "for key derivation");
312 		return -1;
313 	}
314 
315 	pos = seed;
316 	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
317 	pos += EAP_GPSK_RAND_LEN;
318 	os_memcpy(pos, id_peer, id_peer_len);
319 	pos += id_peer_len;
320 	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
321 	pos += EAP_GPSK_RAND_LEN;
322 	os_memcpy(pos, id_server, id_server_len);
323 	pos += id_server_len;
324 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
325 
326 	switch (specifier) {
327 	case EAP_GPSK_CIPHER_AES:
328 		ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
329 					       msk, emsk, sk, sk_len,
330 					       pk, pk_len);
331 		break;
332 #ifdef EAP_GPSK_SHA256
333 	case EAP_GPSK_CIPHER_SHA256:
334 		ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
335 						  msk, emsk, sk, sk_len);
336 		break;
337 #endif /* EAP_GPSK_SHA256 */
338 	default:
339 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
340 			   "key derivation", vendor, specifier);
341 		ret = -1;
342 		break;
343 	}
344 
345 	os_free(seed);
346 
347 	return ret;
348 }
349 
350 
351 /**
352  * eap_gpsk_mic_len - Get the length of the MIC
353  * @vendor: CSuite/Vendor
354  * @specifier: CSuite/Specifier
355  * Returns: MIC length in bytes
356  */
eap_gpsk_mic_len(int vendor,int specifier)357 size_t eap_gpsk_mic_len(int vendor, int specifier)
358 {
359 	if (vendor != EAP_GPSK_VENDOR_IETF)
360 		return 0;
361 
362 	switch (specifier) {
363 	case EAP_GPSK_CIPHER_AES:
364 		return 16;
365 #ifdef EAP_GPSK_SHA256
366 	case EAP_GPSK_CIPHER_SHA256:
367 		return 32;
368 #endif /* EAP_GPSK_SHA256 */
369 	default:
370 		return 0;
371 	}
372 }
373 
374 
eap_gpsk_compute_mic_aes(const u8 * sk,size_t sk_len,const u8 * data,size_t len,u8 * mic)375 static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
376 				    const u8 *data, size_t len, u8 *mic)
377 {
378 	if (sk_len != 16) {
379 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
380 			   "AES-CMAC MIC", (unsigned long) sk_len);
381 		return -1;
382 	}
383 
384 	return omac1_aes_128(sk, data, len, mic);
385 }
386 
387 
388 /**
389  * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
390  * @sk: Session key SK from eap_gpsk_derive_keys()
391  * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
392  * @vendor: CSuite/Vendor
393  * @specifier: CSuite/Specifier
394  * @data: Input data to MIC
395  * @len: Input data length in bytes
396  * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
397  * Returns: 0 on success, -1 on failure
398  */
eap_gpsk_compute_mic(const u8 * sk,size_t sk_len,int vendor,int specifier,const u8 * data,size_t len,u8 * mic)399 int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
400 			 int specifier, const u8 *data, size_t len, u8 *mic)
401 {
402 	int ret;
403 
404 	if (vendor != EAP_GPSK_VENDOR_IETF)
405 		return -1;
406 
407 	switch (specifier) {
408 	case EAP_GPSK_CIPHER_AES:
409 		ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
410 		break;
411 #ifdef EAP_GPSK_SHA256
412 	case EAP_GPSK_CIPHER_SHA256:
413 		hmac_sha256(sk, sk_len, data, len, mic);
414 		ret = 0;
415 		break;
416 #endif /* EAP_GPSK_SHA256 */
417 	default:
418 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
419 			   "MIC computation", vendor, specifier);
420 		ret = -1;
421 		break;
422 	}
423 
424 	return ret;
425 }
426