xref: /freebsd/contrib/wpa/src/eap_common/eap_gpsk_common.c (revision 780fb4a2fa9a9aee5ac48a60b790f567c0dc13e9)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP server/peer: EAP-GPSK shared routines
339beb93cSSam Leffler  * Copyright (c) 2006-2007, 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"
12e28a4053SRui Paulo #include "crypto/aes_wrap.h"
13e28a4053SRui Paulo #include "crypto/sha256.h"
1439beb93cSSam Leffler #include "eap_defs.h"
1539beb93cSSam Leffler #include "eap_gpsk_common.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler 
1839beb93cSSam Leffler /**
1939beb93cSSam Leffler  * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
2039beb93cSSam Leffler  * @vendor: CSuite/Vendor
2139beb93cSSam Leffler  * @specifier: CSuite/Specifier
2239beb93cSSam Leffler  * Returns: 1 if ciphersuite is support, or 0 if not
2339beb93cSSam Leffler  */
eap_gpsk_supported_ciphersuite(int vendor,int specifier)2439beb93cSSam Leffler int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
2539beb93cSSam Leffler {
2639beb93cSSam Leffler 	if (vendor == EAP_GPSK_VENDOR_IETF &&
2739beb93cSSam Leffler 	    specifier == EAP_GPSK_CIPHER_AES)
2839beb93cSSam Leffler 		return 1;
2939beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
3039beb93cSSam Leffler 	if (vendor == EAP_GPSK_VENDOR_IETF &&
3139beb93cSSam Leffler 	    specifier == EAP_GPSK_CIPHER_SHA256)
3239beb93cSSam Leffler 		return 1;
3339beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
3439beb93cSSam Leffler 	return 0;
3539beb93cSSam Leffler }
3639beb93cSSam Leffler 
3739beb93cSSam Leffler 
eap_gpsk_gkdf_cmac(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)3839beb93cSSam Leffler static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
3939beb93cSSam Leffler 			      const u8 *data /* Z */, size_t data_len,
4039beb93cSSam Leffler 			      u8 *buf, size_t len /* X */)
4139beb93cSSam Leffler {
4239beb93cSSam Leffler 	u8 *opos;
4339beb93cSSam Leffler 	size_t i, n, hashlen, left, clen;
4439beb93cSSam Leffler 	u8 ibuf[2], hash[16];
4539beb93cSSam Leffler 	const u8 *addr[2];
4639beb93cSSam Leffler 	size_t vlen[2];
4739beb93cSSam Leffler 
4839beb93cSSam Leffler 	hashlen = sizeof(hash);
4939beb93cSSam Leffler 	/* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
5039beb93cSSam Leffler 	addr[0] = ibuf;
5139beb93cSSam Leffler 	vlen[0] = sizeof(ibuf);
5239beb93cSSam Leffler 	addr[1] = data;
5339beb93cSSam Leffler 	vlen[1] = data_len;
5439beb93cSSam Leffler 
5539beb93cSSam Leffler 	opos = buf;
5639beb93cSSam Leffler 	left = len;
5739beb93cSSam Leffler 	n = (len + hashlen - 1) / hashlen;
5839beb93cSSam Leffler 	for (i = 1; i <= n; i++) {
5939beb93cSSam Leffler 		WPA_PUT_BE16(ibuf, i);
6039beb93cSSam Leffler 		if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
6139beb93cSSam Leffler 			return -1;
6239beb93cSSam Leffler 		clen = left > hashlen ? hashlen : left;
6339beb93cSSam Leffler 		os_memcpy(opos, hash, clen);
6439beb93cSSam Leffler 		opos += clen;
6539beb93cSSam Leffler 		left -= clen;
6639beb93cSSam Leffler 	}
6739beb93cSSam Leffler 
6839beb93cSSam Leffler 	return 0;
6939beb93cSSam Leffler }
7039beb93cSSam Leffler 
7139beb93cSSam Leffler 
7239beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
eap_gpsk_gkdf_sha256(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)7339beb93cSSam Leffler static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
7439beb93cSSam Leffler 				const u8 *data /* Z */, size_t data_len,
7539beb93cSSam Leffler 				u8 *buf, size_t len /* X */)
7639beb93cSSam Leffler {
7739beb93cSSam Leffler 	u8 *opos;
7839beb93cSSam Leffler 	size_t i, n, hashlen, left, clen;
7939beb93cSSam Leffler 	u8 ibuf[2], hash[SHA256_MAC_LEN];
8039beb93cSSam Leffler 	const u8 *addr[2];
8139beb93cSSam Leffler 	size_t vlen[2];
8239beb93cSSam Leffler 
8339beb93cSSam Leffler 	hashlen = SHA256_MAC_LEN;
8439beb93cSSam Leffler 	/* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
8539beb93cSSam Leffler 	addr[0] = ibuf;
8639beb93cSSam Leffler 	vlen[0] = sizeof(ibuf);
8739beb93cSSam Leffler 	addr[1] = data;
8839beb93cSSam Leffler 	vlen[1] = data_len;
8939beb93cSSam Leffler 
9039beb93cSSam Leffler 	opos = buf;
9139beb93cSSam Leffler 	left = len;
9239beb93cSSam Leffler 	n = (len + hashlen - 1) / hashlen;
9339beb93cSSam Leffler 	for (i = 1; i <= n; i++) {
9439beb93cSSam Leffler 		WPA_PUT_BE16(ibuf, i);
95*780fb4a2SCy Schubert 		if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
96*780fb4a2SCy Schubert 			return -1;
9739beb93cSSam Leffler 		clen = left > hashlen ? hashlen : left;
9839beb93cSSam Leffler 		os_memcpy(opos, hash, clen);
9939beb93cSSam Leffler 		opos += clen;
10039beb93cSSam Leffler 		left -= clen;
10139beb93cSSam Leffler 	}
10239beb93cSSam Leffler 
10339beb93cSSam Leffler 	return 0;
10439beb93cSSam Leffler }
10539beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 
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)10839beb93cSSam Leffler static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
10939beb93cSSam Leffler 				       u8 *kdf_out, size_t kdf_out_len,
11039beb93cSSam Leffler 				       const u8 *psk, size_t psk_len,
11139beb93cSSam Leffler 				       const u8 *seed, size_t seed_len,
11239beb93cSSam Leffler 				       u8 *msk, u8 *emsk,
11339beb93cSSam Leffler 				       u8 *sk, size_t sk_len,
11439beb93cSSam Leffler 				       u8 *pk, size_t pk_len)
11539beb93cSSam Leffler {
11639beb93cSSam Leffler 	u8 mk[32], *pos, *data;
11739beb93cSSam Leffler 	size_t data_len, mk_len;
11839beb93cSSam Leffler 	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
11939beb93cSSam Leffler 		    u8 *buf, size_t len);
12039beb93cSSam Leffler 
12139beb93cSSam Leffler 	gkdf = NULL;
12239beb93cSSam Leffler 	switch (csuite_specifier) {
12339beb93cSSam Leffler 	case EAP_GPSK_CIPHER_AES:
12439beb93cSSam Leffler 		gkdf = eap_gpsk_gkdf_cmac;
12539beb93cSSam Leffler 		mk_len = 16;
12639beb93cSSam Leffler 		break;
12739beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
12839beb93cSSam Leffler 	case EAP_GPSK_CIPHER_SHA256:
12939beb93cSSam Leffler 		gkdf = eap_gpsk_gkdf_sha256;
13039beb93cSSam Leffler 		mk_len = SHA256_MAC_LEN;
13139beb93cSSam Leffler 		break;
13239beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
13339beb93cSSam Leffler 	default:
13439beb93cSSam Leffler 		return -1;
13539beb93cSSam Leffler 	}
13639beb93cSSam Leffler 
13739beb93cSSam Leffler 	if (psk_len < mk_len)
13839beb93cSSam Leffler 		return -1;
13939beb93cSSam Leffler 
14039beb93cSSam Leffler 	data_len = 2 + psk_len + 6 + seed_len;
14139beb93cSSam Leffler 	data = os_malloc(data_len);
14239beb93cSSam Leffler 	if (data == NULL)
14339beb93cSSam Leffler 		return -1;
14439beb93cSSam Leffler 	pos = data;
14539beb93cSSam Leffler 	WPA_PUT_BE16(pos, psk_len);
14639beb93cSSam Leffler 	pos += 2;
14739beb93cSSam Leffler 	os_memcpy(pos, psk, psk_len);
14839beb93cSSam Leffler 	pos += psk_len;
14939beb93cSSam Leffler 	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
15039beb93cSSam Leffler 	pos += 4;
15139beb93cSSam Leffler 	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
15239beb93cSSam Leffler 	pos += 2;
15339beb93cSSam Leffler 	os_memcpy(pos, seed, seed_len); /* inputString */
15439beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
15539beb93cSSam Leffler 			data, data_len);
15639beb93cSSam Leffler 
15739beb93cSSam Leffler 	if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
15839beb93cSSam Leffler 		os_free(data);
15939beb93cSSam Leffler 		return -1;
16039beb93cSSam Leffler 	}
16139beb93cSSam Leffler 	os_free(data);
16239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
16339beb93cSSam Leffler 
16439beb93cSSam Leffler 	if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
16539beb93cSSam Leffler 		return -1;
16639beb93cSSam Leffler 
16739beb93cSSam Leffler 	pos = kdf_out;
16839beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
16939beb93cSSam Leffler 	os_memcpy(msk, pos, EAP_MSK_LEN);
17039beb93cSSam Leffler 	pos += EAP_MSK_LEN;
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
17339beb93cSSam Leffler 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
17439beb93cSSam Leffler 	pos += EAP_EMSK_LEN;
17539beb93cSSam Leffler 
17639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
17739beb93cSSam Leffler 	os_memcpy(sk, pos, sk_len);
17839beb93cSSam Leffler 	pos += sk_len;
17939beb93cSSam Leffler 
18039beb93cSSam Leffler 	if (pk) {
18139beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
18239beb93cSSam Leffler 		os_memcpy(pk, pos, pk_len);
18339beb93cSSam Leffler 	}
18439beb93cSSam Leffler 
18539beb93cSSam Leffler 	return 0;
18639beb93cSSam Leffler }
18739beb93cSSam Leffler 
18839beb93cSSam Leffler 
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)18939beb93cSSam Leffler static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
19039beb93cSSam Leffler 				    const u8 *seed, size_t seed_len,
19139beb93cSSam Leffler 				    u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
19239beb93cSSam Leffler 				    u8 *pk, size_t *pk_len)
19339beb93cSSam Leffler {
19439beb93cSSam Leffler #define EAP_GPSK_SK_LEN_AES 16
19539beb93cSSam Leffler #define EAP_GPSK_PK_LEN_AES 16
19639beb93cSSam Leffler 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
19739beb93cSSam Leffler 		   EAP_GPSK_PK_LEN_AES];
19839beb93cSSam Leffler 
19939beb93cSSam Leffler 	/*
20039beb93cSSam Leffler 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
20139beb93cSSam Leffler 	 *            (= seed)
20239beb93cSSam Leffler 	 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
20339beb93cSSam Leffler 	 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
20439beb93cSSam Leffler 	 * MSK = GKDF-160 (MK, inputString)[0..63]
20539beb93cSSam Leffler 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
20639beb93cSSam Leffler 	 * SK = GKDF-160 (MK, inputString)[128..143]
20739beb93cSSam Leffler 	 * PK = GKDF-160 (MK, inputString)[144..159]
20839beb93cSSam Leffler 	 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
20939beb93cSSam Leffler 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
21039beb93cSSam Leffler 	 *                      CSuite_Sel || inputString)
21139beb93cSSam Leffler 	 */
21239beb93cSSam Leffler 
21339beb93cSSam Leffler 	*sk_len = EAP_GPSK_SK_LEN_AES;
21439beb93cSSam Leffler 	*pk_len = EAP_GPSK_PK_LEN_AES;
21539beb93cSSam Leffler 
21639beb93cSSam Leffler 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
21739beb93cSSam Leffler 					   kdf_out, sizeof(kdf_out),
21839beb93cSSam Leffler 					   psk, psk_len, seed, seed_len,
21939beb93cSSam Leffler 					   msk, emsk, sk, *sk_len,
22039beb93cSSam Leffler 					   pk, *pk_len);
22139beb93cSSam Leffler }
22239beb93cSSam Leffler 
22339beb93cSSam Leffler 
22439beb93cSSam Leffler #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)22539beb93cSSam Leffler static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
22639beb93cSSam Leffler 				       const u8 *seed, size_t seed_len,
22739beb93cSSam Leffler 				       u8 *msk, u8 *emsk,
22839beb93cSSam Leffler 				       u8 *sk, size_t *sk_len)
22939beb93cSSam Leffler {
23039beb93cSSam Leffler #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
23139beb93cSSam Leffler #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
23239beb93cSSam Leffler 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
23339beb93cSSam Leffler 		   EAP_GPSK_PK_LEN_SHA256];
23439beb93cSSam Leffler 
23539beb93cSSam Leffler 	/*
23639beb93cSSam Leffler 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
23739beb93cSSam Leffler 	 *            (= seed)
23839beb93cSSam Leffler 	 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
23939beb93cSSam Leffler 	 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
24039beb93cSSam Leffler 	 * MSK = GKDF-160 (MK, inputString)[0..63]
24139beb93cSSam Leffler 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
24239beb93cSSam Leffler 	 * SK = GKDF-160 (MK, inputString)[128..159]
24339beb93cSSam Leffler 	 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
24439beb93cSSam Leffler 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
24539beb93cSSam Leffler 	 *                      CSuite_Sel || inputString)
24639beb93cSSam Leffler 	 */
24739beb93cSSam Leffler 
24839beb93cSSam Leffler 	*sk_len = EAP_GPSK_SK_LEN_SHA256;
24939beb93cSSam Leffler 
25039beb93cSSam Leffler 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
25139beb93cSSam Leffler 					   kdf_out, sizeof(kdf_out),
25239beb93cSSam Leffler 					   psk, psk_len, seed, seed_len,
25339beb93cSSam Leffler 					   msk, emsk, sk, *sk_len,
25439beb93cSSam Leffler 					   NULL, 0);
25539beb93cSSam Leffler }
25639beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
25739beb93cSSam Leffler 
25839beb93cSSam Leffler 
25939beb93cSSam Leffler /**
26039beb93cSSam Leffler  * eap_gpsk_derive_keys - Derive EAP-GPSK keys
26139beb93cSSam Leffler  * @psk: Pre-shared key
26239beb93cSSam Leffler  * @psk_len: Length of psk in bytes
26339beb93cSSam Leffler  * @vendor: CSuite/Vendor
26439beb93cSSam Leffler  * @specifier: CSuite/Specifier
26539beb93cSSam Leffler  * @rand_peer: 32-byte RAND_Peer
26639beb93cSSam Leffler  * @rand_server: 32-byte RAND_Server
26739beb93cSSam Leffler  * @id_peer: ID_Peer
26839beb93cSSam Leffler  * @id_peer_len: Length of ID_Peer
26939beb93cSSam Leffler  * @id_server: ID_Server
27039beb93cSSam Leffler  * @id_server_len: Length of ID_Server
27139beb93cSSam Leffler  * @msk: Buffer for 64-byte MSK
27239beb93cSSam Leffler  * @emsk: Buffer for 64-byte EMSK
27339beb93cSSam Leffler  * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
27439beb93cSSam Leffler  * @sk_len: Buffer for returning length of SK
27539beb93cSSam Leffler  * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
27639beb93cSSam Leffler  * @pk_len: Buffer for returning length of PK
27739beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
27839beb93cSSam Leffler  */
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)27939beb93cSSam Leffler int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
28039beb93cSSam Leffler 			 int specifier,
28139beb93cSSam Leffler 			 const u8 *rand_peer, const u8 *rand_server,
28239beb93cSSam Leffler 			 const u8 *id_peer, size_t id_peer_len,
28339beb93cSSam Leffler 			 const u8 *id_server, size_t id_server_len,
28439beb93cSSam Leffler 			 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
28539beb93cSSam Leffler 			 u8 *pk, size_t *pk_len)
28639beb93cSSam Leffler {
28739beb93cSSam Leffler 	u8 *seed, *pos;
28839beb93cSSam Leffler 	int ret;
28939beb93cSSam Leffler 
29039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
29139beb93cSSam Leffler 		   vendor, specifier);
29239beb93cSSam Leffler 
29339beb93cSSam Leffler 	if (vendor != EAP_GPSK_VENDOR_IETF)
29439beb93cSSam Leffler 		return -1;
29539beb93cSSam Leffler 
29639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
29739beb93cSSam Leffler 
29839beb93cSSam Leffler 	/* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
2995b9c547cSRui Paulo 	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
30039beb93cSSam Leffler 	if (seed == NULL) {
30139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
30239beb93cSSam Leffler 			   "for key derivation");
30339beb93cSSam Leffler 		return -1;
30439beb93cSSam Leffler 	}
30539beb93cSSam Leffler 
30639beb93cSSam Leffler 	pos = seed;
30739beb93cSSam Leffler 	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
30839beb93cSSam Leffler 	pos += EAP_GPSK_RAND_LEN;
30939beb93cSSam Leffler 	os_memcpy(pos, id_peer, id_peer_len);
31039beb93cSSam Leffler 	pos += id_peer_len;
31139beb93cSSam Leffler 	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
31239beb93cSSam Leffler 	pos += EAP_GPSK_RAND_LEN;
31339beb93cSSam Leffler 	os_memcpy(pos, id_server, id_server_len);
31439beb93cSSam Leffler 	pos += id_server_len;
3155b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
31639beb93cSSam Leffler 
31739beb93cSSam Leffler 	switch (specifier) {
31839beb93cSSam Leffler 	case EAP_GPSK_CIPHER_AES:
3195b9c547cSRui Paulo 		ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
32039beb93cSSam Leffler 					       msk, emsk, sk, sk_len,
32139beb93cSSam Leffler 					       pk, pk_len);
32239beb93cSSam Leffler 		break;
32339beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
32439beb93cSSam Leffler 	case EAP_GPSK_CIPHER_SHA256:
3255b9c547cSRui Paulo 		ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
3265b9c547cSRui Paulo 						  pos - seed,
32739beb93cSSam Leffler 						  msk, emsk, sk, sk_len);
32839beb93cSSam Leffler 		break;
32939beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
33039beb93cSSam Leffler 	default:
33139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
33239beb93cSSam Leffler 			   "key derivation", vendor, specifier);
33339beb93cSSam Leffler 		ret = -1;
33439beb93cSSam Leffler 		break;
33539beb93cSSam Leffler 	}
33639beb93cSSam Leffler 
33739beb93cSSam Leffler 	os_free(seed);
33839beb93cSSam Leffler 
33939beb93cSSam Leffler 	return ret;
34039beb93cSSam Leffler }
34139beb93cSSam Leffler 
34239beb93cSSam Leffler 
eap_gpsk_derive_mid_helper(u32 csuite_specifier,u8 * kdf_out,size_t kdf_out_len,const u8 * psk,const u8 * seed,size_t seed_len,u8 method_type)3435b9c547cSRui Paulo static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
3445b9c547cSRui Paulo 				      u8 *kdf_out, size_t kdf_out_len,
3455b9c547cSRui Paulo 				      const u8 *psk, const u8 *seed,
3465b9c547cSRui Paulo 				      size_t seed_len, u8 method_type)
3475b9c547cSRui Paulo {
3485b9c547cSRui Paulo 	u8 *pos, *data;
3495b9c547cSRui Paulo 	size_t data_len;
3505b9c547cSRui Paulo 	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
3515b9c547cSRui Paulo 		    u8 *buf, size_t len);
3525b9c547cSRui Paulo 
3535b9c547cSRui Paulo 	gkdf = NULL;
3545b9c547cSRui Paulo 	switch (csuite_specifier) {
3555b9c547cSRui Paulo 	case EAP_GPSK_CIPHER_AES:
3565b9c547cSRui Paulo 		gkdf = eap_gpsk_gkdf_cmac;
3575b9c547cSRui Paulo 		break;
3585b9c547cSRui Paulo #ifdef EAP_GPSK_SHA256
3595b9c547cSRui Paulo 	case EAP_GPSK_CIPHER_SHA256:
3605b9c547cSRui Paulo 		gkdf = eap_gpsk_gkdf_sha256;
3615b9c547cSRui Paulo 		break;
3625b9c547cSRui Paulo #endif /* EAP_GPSK_SHA256 */
3635b9c547cSRui Paulo 	default:
3645b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
3655b9c547cSRui Paulo 			   "Session-Id derivation", csuite_specifier);
3665b9c547cSRui Paulo 		return -1;
3675b9c547cSRui Paulo 	}
3685b9c547cSRui Paulo 
3695b9c547cSRui Paulo #define SID_LABEL "Method ID"
3705b9c547cSRui Paulo 	/* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
3715b9c547cSRui Paulo 	data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
3725b9c547cSRui Paulo 	data = os_malloc(data_len);
3735b9c547cSRui Paulo 	if (data == NULL)
3745b9c547cSRui Paulo 		return -1;
3755b9c547cSRui Paulo 	pos = data;
3765b9c547cSRui Paulo 	os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
3775b9c547cSRui Paulo 	pos += strlen(SID_LABEL);
3785b9c547cSRui Paulo #undef SID_LABEL
3795b9c547cSRui Paulo 	os_memcpy(pos, &method_type, 1);
3805b9c547cSRui Paulo 	pos += 1;
3815b9c547cSRui Paulo 	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
3825b9c547cSRui Paulo 	pos += 4;
3835b9c547cSRui Paulo 	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
3845b9c547cSRui Paulo 	pos += 2;
3855b9c547cSRui Paulo 	os_memcpy(pos, seed, seed_len); /* inputString */
3865b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
3875b9c547cSRui Paulo 		    data, data_len);
3885b9c547cSRui Paulo 
3895b9c547cSRui Paulo 	if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
3905b9c547cSRui Paulo 		os_free(data);
3915b9c547cSRui Paulo 		return -1;
3925b9c547cSRui Paulo 	}
3935b9c547cSRui Paulo 	os_free(data);
3945b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
3955b9c547cSRui Paulo 
3965b9c547cSRui Paulo 	return 0;
3975b9c547cSRui Paulo }
3985b9c547cSRui Paulo 
3995b9c547cSRui Paulo 
4005b9c547cSRui Paulo /**
4015b9c547cSRui Paulo  * eap_gpsk_session_id - Derive EAP-GPSK Session ID
4025b9c547cSRui Paulo  * @psk: Pre-shared key
4035b9c547cSRui Paulo  * @psk_len: Length of psk in bytes
4045b9c547cSRui Paulo  * @vendor: CSuite/Vendor
4055b9c547cSRui Paulo  * @specifier: CSuite/Specifier
4065b9c547cSRui Paulo  * @rand_peer: 32-byte RAND_Peer
4075b9c547cSRui Paulo  * @rand_server: 32-byte RAND_Server
4085b9c547cSRui Paulo  * @id_peer: ID_Peer
4095b9c547cSRui Paulo  * @id_peer_len: Length of ID_Peer
4105b9c547cSRui Paulo  * @id_server: ID_Server
4115b9c547cSRui Paulo  * @id_server_len: Length of ID_Server
4125b9c547cSRui Paulo  * @method_type: EAP Authentication Method Type
4135b9c547cSRui Paulo  * @sid: Buffer for 17-byte Session ID
4145b9c547cSRui Paulo  * @sid_len: Buffer for returning length of Session ID
4155b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure
4165b9c547cSRui Paulo  */
eap_gpsk_derive_session_id(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 method_type,u8 * sid,size_t * sid_len)4175b9c547cSRui Paulo int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
4185b9c547cSRui Paulo 			       int specifier,
4195b9c547cSRui Paulo 			       const u8 *rand_peer, const u8 *rand_server,
4205b9c547cSRui Paulo 			       const u8 *id_peer, size_t id_peer_len,
4215b9c547cSRui Paulo 			       const u8 *id_server, size_t id_server_len,
4225b9c547cSRui Paulo 			       u8 method_type, u8 *sid, size_t *sid_len)
4235b9c547cSRui Paulo {
4245b9c547cSRui Paulo 	u8 *seed, *pos;
4255b9c547cSRui Paulo 	u8 kdf_out[16];
4265b9c547cSRui Paulo 	int ret;
4275b9c547cSRui Paulo 
4285b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
4295b9c547cSRui Paulo 		   vendor, specifier);
4305b9c547cSRui Paulo 
4315b9c547cSRui Paulo 	if (vendor != EAP_GPSK_VENDOR_IETF)
4325b9c547cSRui Paulo 		return -1;
4335b9c547cSRui Paulo 
4345b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
4355b9c547cSRui Paulo 
4365b9c547cSRui Paulo 	/*
4375b9c547cSRui Paulo 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
4385b9c547cSRui Paulo 	 *            (= seed)
4395b9c547cSRui Paulo 	 * KS = 16, CSuite_Sel = 0x00000000 0x0001
4405b9c547cSRui Paulo 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
4415b9c547cSRui Paulo 	 *                      CSuite_Sel || inputString)
4425b9c547cSRui Paulo 	 */
4435b9c547cSRui Paulo 	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
4445b9c547cSRui Paulo 	if (seed == NULL) {
4455b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
4465b9c547cSRui Paulo 			   "for Session-Id derivation");
4475b9c547cSRui Paulo 		return -1;
4485b9c547cSRui Paulo 	}
4495b9c547cSRui Paulo 
4505b9c547cSRui Paulo 	pos = seed;
4515b9c547cSRui Paulo 	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
4525b9c547cSRui Paulo 	pos += EAP_GPSK_RAND_LEN;
4535b9c547cSRui Paulo 	os_memcpy(pos, id_peer, id_peer_len);
4545b9c547cSRui Paulo 	pos += id_peer_len;
4555b9c547cSRui Paulo 	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
4565b9c547cSRui Paulo 	pos += EAP_GPSK_RAND_LEN;
4575b9c547cSRui Paulo 	os_memcpy(pos, id_server, id_server_len);
4585b9c547cSRui Paulo 	pos += id_server_len;
4595b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
4605b9c547cSRui Paulo 
4615b9c547cSRui Paulo 	ret = eap_gpsk_derive_mid_helper(specifier,
4625b9c547cSRui Paulo 					 kdf_out, sizeof(kdf_out),
4635b9c547cSRui Paulo 					 psk, seed, pos - seed,
4645b9c547cSRui Paulo 					 method_type);
4655b9c547cSRui Paulo 
4665b9c547cSRui Paulo 	sid[0] = method_type;
4675b9c547cSRui Paulo 	os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
4685b9c547cSRui Paulo 	*sid_len = 1 + sizeof(kdf_out);
4695b9c547cSRui Paulo 
4705b9c547cSRui Paulo 	os_free(seed);
4715b9c547cSRui Paulo 
4725b9c547cSRui Paulo 	return ret;
4735b9c547cSRui Paulo }
4745b9c547cSRui Paulo 
4755b9c547cSRui Paulo 
47639beb93cSSam Leffler /**
47739beb93cSSam Leffler  * eap_gpsk_mic_len - Get the length of the MIC
47839beb93cSSam Leffler  * @vendor: CSuite/Vendor
47939beb93cSSam Leffler  * @specifier: CSuite/Specifier
48039beb93cSSam Leffler  * Returns: MIC length in bytes
48139beb93cSSam Leffler  */
eap_gpsk_mic_len(int vendor,int specifier)48239beb93cSSam Leffler size_t eap_gpsk_mic_len(int vendor, int specifier)
48339beb93cSSam Leffler {
48439beb93cSSam Leffler 	if (vendor != EAP_GPSK_VENDOR_IETF)
48539beb93cSSam Leffler 		return 0;
48639beb93cSSam Leffler 
48739beb93cSSam Leffler 	switch (specifier) {
48839beb93cSSam Leffler 	case EAP_GPSK_CIPHER_AES:
48939beb93cSSam Leffler 		return 16;
49039beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
49139beb93cSSam Leffler 	case EAP_GPSK_CIPHER_SHA256:
49239beb93cSSam Leffler 		return 32;
49339beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
49439beb93cSSam Leffler 	default:
49539beb93cSSam Leffler 		return 0;
49639beb93cSSam Leffler 	}
49739beb93cSSam Leffler }
49839beb93cSSam Leffler 
49939beb93cSSam Leffler 
eap_gpsk_compute_mic_aes(const u8 * sk,size_t sk_len,const u8 * data,size_t len,u8 * mic)50039beb93cSSam Leffler static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
50139beb93cSSam Leffler 				    const u8 *data, size_t len, u8 *mic)
50239beb93cSSam Leffler {
50339beb93cSSam Leffler 	if (sk_len != 16) {
50439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
50539beb93cSSam Leffler 			   "AES-CMAC MIC", (unsigned long) sk_len);
50639beb93cSSam Leffler 		return -1;
50739beb93cSSam Leffler 	}
50839beb93cSSam Leffler 
50939beb93cSSam Leffler 	return omac1_aes_128(sk, data, len, mic);
51039beb93cSSam Leffler }
51139beb93cSSam Leffler 
51239beb93cSSam Leffler 
51339beb93cSSam Leffler /**
51439beb93cSSam Leffler  * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
51539beb93cSSam Leffler  * @sk: Session key SK from eap_gpsk_derive_keys()
51639beb93cSSam Leffler  * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
51739beb93cSSam Leffler  * @vendor: CSuite/Vendor
51839beb93cSSam Leffler  * @specifier: CSuite/Specifier
51939beb93cSSam Leffler  * @data: Input data to MIC
52039beb93cSSam Leffler  * @len: Input data length in bytes
52139beb93cSSam Leffler  * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
52239beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
52339beb93cSSam Leffler  */
eap_gpsk_compute_mic(const u8 * sk,size_t sk_len,int vendor,int specifier,const u8 * data,size_t len,u8 * mic)52439beb93cSSam Leffler int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
52539beb93cSSam Leffler 			 int specifier, const u8 *data, size_t len, u8 *mic)
52639beb93cSSam Leffler {
52739beb93cSSam Leffler 	int ret;
52839beb93cSSam Leffler 
52939beb93cSSam Leffler 	if (vendor != EAP_GPSK_VENDOR_IETF)
53039beb93cSSam Leffler 		return -1;
53139beb93cSSam Leffler 
53239beb93cSSam Leffler 	switch (specifier) {
53339beb93cSSam Leffler 	case EAP_GPSK_CIPHER_AES:
53439beb93cSSam Leffler 		ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
53539beb93cSSam Leffler 		break;
53639beb93cSSam Leffler #ifdef EAP_GPSK_SHA256
53739beb93cSSam Leffler 	case EAP_GPSK_CIPHER_SHA256:
538*780fb4a2SCy Schubert 		ret = hmac_sha256(sk, sk_len, data, len, mic);
53939beb93cSSam Leffler 		break;
54039beb93cSSam Leffler #endif /* EAP_GPSK_SHA256 */
54139beb93cSSam Leffler 	default:
54239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
54339beb93cSSam Leffler 			   "MIC computation", vendor, specifier);
54439beb93cSSam Leffler 		ret = -1;
54539beb93cSSam Leffler 		break;
54639beb93cSSam Leffler 	}
54739beb93cSSam Leffler 
548*780fb4a2SCy Schubert 	if (ret)
549*780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
550*780fb4a2SCy Schubert 
55139beb93cSSam Leffler 	return ret;
55239beb93cSSam Leffler }
553