xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_common/eap_gpsk_common.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP server/peer: EAP-GPSK shared routines
36d49e1aeSJan Lentfer  * Copyright (c) 2006-2007, 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/aes_wrap.h"
133ff40c12SJohn Marino #include "crypto/sha256.h"
146d49e1aeSJan Lentfer #include "eap_defs.h"
156d49e1aeSJan Lentfer #include "eap_gpsk_common.h"
166d49e1aeSJan Lentfer 
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer /**
196d49e1aeSJan Lentfer  * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
206d49e1aeSJan Lentfer  * @vendor: CSuite/Vendor
216d49e1aeSJan Lentfer  * @specifier: CSuite/Specifier
226d49e1aeSJan Lentfer  * Returns: 1 if ciphersuite is support, or 0 if not
236d49e1aeSJan Lentfer  */
eap_gpsk_supported_ciphersuite(int vendor,int specifier)246d49e1aeSJan Lentfer int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
256d49e1aeSJan Lentfer {
266d49e1aeSJan Lentfer 	if (vendor == EAP_GPSK_VENDOR_IETF &&
276d49e1aeSJan Lentfer 	    specifier == EAP_GPSK_CIPHER_AES)
286d49e1aeSJan Lentfer 		return 1;
296d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
306d49e1aeSJan Lentfer 	if (vendor == EAP_GPSK_VENDOR_IETF &&
316d49e1aeSJan Lentfer 	    specifier == EAP_GPSK_CIPHER_SHA256)
326d49e1aeSJan Lentfer 		return 1;
336d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
346d49e1aeSJan Lentfer 	return 0;
356d49e1aeSJan Lentfer }
366d49e1aeSJan Lentfer 
376d49e1aeSJan Lentfer 
eap_gpsk_gkdf_cmac(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)386d49e1aeSJan Lentfer static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
396d49e1aeSJan Lentfer 			      const u8 *data /* Z */, size_t data_len,
406d49e1aeSJan Lentfer 			      u8 *buf, size_t len /* X */)
416d49e1aeSJan Lentfer {
426d49e1aeSJan Lentfer 	u8 *opos;
436d49e1aeSJan Lentfer 	size_t i, n, hashlen, left, clen;
446d49e1aeSJan Lentfer 	u8 ibuf[2], hash[16];
456d49e1aeSJan Lentfer 	const u8 *addr[2];
466d49e1aeSJan Lentfer 	size_t vlen[2];
476d49e1aeSJan Lentfer 
486d49e1aeSJan Lentfer 	hashlen = sizeof(hash);
496d49e1aeSJan Lentfer 	/* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
506d49e1aeSJan Lentfer 	addr[0] = ibuf;
516d49e1aeSJan Lentfer 	vlen[0] = sizeof(ibuf);
526d49e1aeSJan Lentfer 	addr[1] = data;
536d49e1aeSJan Lentfer 	vlen[1] = data_len;
546d49e1aeSJan Lentfer 
556d49e1aeSJan Lentfer 	opos = buf;
566d49e1aeSJan Lentfer 	left = len;
576d49e1aeSJan Lentfer 	n = (len + hashlen - 1) / hashlen;
586d49e1aeSJan Lentfer 	for (i = 1; i <= n; i++) {
596d49e1aeSJan Lentfer 		WPA_PUT_BE16(ibuf, i);
606d49e1aeSJan Lentfer 		if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
616d49e1aeSJan Lentfer 			return -1;
626d49e1aeSJan Lentfer 		clen = left > hashlen ? hashlen : left;
636d49e1aeSJan Lentfer 		os_memcpy(opos, hash, clen);
646d49e1aeSJan Lentfer 		opos += clen;
656d49e1aeSJan Lentfer 		left -= clen;
666d49e1aeSJan Lentfer 	}
676d49e1aeSJan Lentfer 
686d49e1aeSJan Lentfer 	return 0;
696d49e1aeSJan Lentfer }
706d49e1aeSJan Lentfer 
716d49e1aeSJan Lentfer 
726d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
eap_gpsk_gkdf_sha256(const u8 * psk,const u8 * data,size_t data_len,u8 * buf,size_t len)736d49e1aeSJan Lentfer static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
746d49e1aeSJan Lentfer 				const u8 *data /* Z */, size_t data_len,
756d49e1aeSJan Lentfer 				u8 *buf, size_t len /* X */)
766d49e1aeSJan Lentfer {
776d49e1aeSJan Lentfer 	u8 *opos;
786d49e1aeSJan Lentfer 	size_t i, n, hashlen, left, clen;
796d49e1aeSJan Lentfer 	u8 ibuf[2], hash[SHA256_MAC_LEN];
806d49e1aeSJan Lentfer 	const u8 *addr[2];
816d49e1aeSJan Lentfer 	size_t vlen[2];
826d49e1aeSJan Lentfer 
836d49e1aeSJan Lentfer 	hashlen = SHA256_MAC_LEN;
846d49e1aeSJan Lentfer 	/* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
856d49e1aeSJan Lentfer 	addr[0] = ibuf;
866d49e1aeSJan Lentfer 	vlen[0] = sizeof(ibuf);
876d49e1aeSJan Lentfer 	addr[1] = data;
886d49e1aeSJan Lentfer 	vlen[1] = data_len;
896d49e1aeSJan Lentfer 
906d49e1aeSJan Lentfer 	opos = buf;
916d49e1aeSJan Lentfer 	left = len;
926d49e1aeSJan Lentfer 	n = (len + hashlen - 1) / hashlen;
936d49e1aeSJan Lentfer 	for (i = 1; i <= n; i++) {
946d49e1aeSJan Lentfer 		WPA_PUT_BE16(ibuf, i);
95*a1157835SDaniel Fojt 		if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
96*a1157835SDaniel Fojt 			return -1;
976d49e1aeSJan Lentfer 		clen = left > hashlen ? hashlen : left;
986d49e1aeSJan Lentfer 		os_memcpy(opos, hash, clen);
996d49e1aeSJan Lentfer 		opos += clen;
1006d49e1aeSJan Lentfer 		left -= clen;
1016d49e1aeSJan Lentfer 	}
1026d49e1aeSJan Lentfer 
1036d49e1aeSJan Lentfer 	return 0;
1046d49e1aeSJan Lentfer }
1056d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
1066d49e1aeSJan Lentfer 
1076d49e1aeSJan Lentfer 
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)1086d49e1aeSJan Lentfer static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
1096d49e1aeSJan Lentfer 				       u8 *kdf_out, size_t kdf_out_len,
1106d49e1aeSJan Lentfer 				       const u8 *psk, size_t psk_len,
1116d49e1aeSJan Lentfer 				       const u8 *seed, size_t seed_len,
1126d49e1aeSJan Lentfer 				       u8 *msk, u8 *emsk,
1136d49e1aeSJan Lentfer 				       u8 *sk, size_t sk_len,
1146d49e1aeSJan Lentfer 				       u8 *pk, size_t pk_len)
1156d49e1aeSJan Lentfer {
1166d49e1aeSJan Lentfer 	u8 mk[32], *pos, *data;
1176d49e1aeSJan Lentfer 	size_t data_len, mk_len;
1186d49e1aeSJan Lentfer 	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
1196d49e1aeSJan Lentfer 		    u8 *buf, size_t len);
1206d49e1aeSJan Lentfer 
1216d49e1aeSJan Lentfer 	gkdf = NULL;
1226d49e1aeSJan Lentfer 	switch (csuite_specifier) {
1236d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_AES:
1246d49e1aeSJan Lentfer 		gkdf = eap_gpsk_gkdf_cmac;
1256d49e1aeSJan Lentfer 		mk_len = 16;
1266d49e1aeSJan Lentfer 		break;
1276d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
1286d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_SHA256:
1296d49e1aeSJan Lentfer 		gkdf = eap_gpsk_gkdf_sha256;
1306d49e1aeSJan Lentfer 		mk_len = SHA256_MAC_LEN;
1316d49e1aeSJan Lentfer 		break;
1326d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
1336d49e1aeSJan Lentfer 	default:
1346d49e1aeSJan Lentfer 		return -1;
1356d49e1aeSJan Lentfer 	}
1366d49e1aeSJan Lentfer 
1376d49e1aeSJan Lentfer 	if (psk_len < mk_len)
1386d49e1aeSJan Lentfer 		return -1;
1396d49e1aeSJan Lentfer 
1406d49e1aeSJan Lentfer 	data_len = 2 + psk_len + 6 + seed_len;
1416d49e1aeSJan Lentfer 	data = os_malloc(data_len);
1426d49e1aeSJan Lentfer 	if (data == NULL)
1436d49e1aeSJan Lentfer 		return -1;
1446d49e1aeSJan Lentfer 	pos = data;
1456d49e1aeSJan Lentfer 	WPA_PUT_BE16(pos, psk_len);
1466d49e1aeSJan Lentfer 	pos += 2;
1476d49e1aeSJan Lentfer 	os_memcpy(pos, psk, psk_len);
1486d49e1aeSJan Lentfer 	pos += psk_len;
1496d49e1aeSJan Lentfer 	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
1506d49e1aeSJan Lentfer 	pos += 4;
1516d49e1aeSJan Lentfer 	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
1526d49e1aeSJan Lentfer 	pos += 2;
1536d49e1aeSJan Lentfer 	os_memcpy(pos, seed, seed_len); /* inputString */
1546d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
1556d49e1aeSJan Lentfer 			data, data_len);
1566d49e1aeSJan Lentfer 
1576d49e1aeSJan Lentfer 	if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
1586d49e1aeSJan Lentfer 		os_free(data);
1596d49e1aeSJan Lentfer 		return -1;
1606d49e1aeSJan Lentfer 	}
1616d49e1aeSJan Lentfer 	os_free(data);
1626d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
1636d49e1aeSJan Lentfer 
1646d49e1aeSJan Lentfer 	if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
1656d49e1aeSJan Lentfer 		return -1;
1666d49e1aeSJan Lentfer 
1676d49e1aeSJan Lentfer 	pos = kdf_out;
1686d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
1696d49e1aeSJan Lentfer 	os_memcpy(msk, pos, EAP_MSK_LEN);
1706d49e1aeSJan Lentfer 	pos += EAP_MSK_LEN;
1716d49e1aeSJan Lentfer 
1726d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
1736d49e1aeSJan Lentfer 	os_memcpy(emsk, pos, EAP_EMSK_LEN);
1746d49e1aeSJan Lentfer 	pos += EAP_EMSK_LEN;
1756d49e1aeSJan Lentfer 
1766d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
1776d49e1aeSJan Lentfer 	os_memcpy(sk, pos, sk_len);
1786d49e1aeSJan Lentfer 	pos += sk_len;
1796d49e1aeSJan Lentfer 
1806d49e1aeSJan Lentfer 	if (pk) {
1816d49e1aeSJan Lentfer 		wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
1826d49e1aeSJan Lentfer 		os_memcpy(pk, pos, pk_len);
1836d49e1aeSJan Lentfer 	}
1846d49e1aeSJan Lentfer 
1856d49e1aeSJan Lentfer 	return 0;
1866d49e1aeSJan Lentfer }
1876d49e1aeSJan Lentfer 
1886d49e1aeSJan Lentfer 
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)1896d49e1aeSJan Lentfer static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
1906d49e1aeSJan Lentfer 				    const u8 *seed, size_t seed_len,
1916d49e1aeSJan Lentfer 				    u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
1926d49e1aeSJan Lentfer 				    u8 *pk, size_t *pk_len)
1936d49e1aeSJan Lentfer {
1946d49e1aeSJan Lentfer #define EAP_GPSK_SK_LEN_AES 16
1956d49e1aeSJan Lentfer #define EAP_GPSK_PK_LEN_AES 16
1966d49e1aeSJan Lentfer 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
1976d49e1aeSJan Lentfer 		   EAP_GPSK_PK_LEN_AES];
1986d49e1aeSJan Lentfer 
1996d49e1aeSJan Lentfer 	/*
2006d49e1aeSJan Lentfer 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
2016d49e1aeSJan Lentfer 	 *            (= seed)
2026d49e1aeSJan Lentfer 	 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
2036d49e1aeSJan Lentfer 	 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
2046d49e1aeSJan Lentfer 	 * MSK = GKDF-160 (MK, inputString)[0..63]
2056d49e1aeSJan Lentfer 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
2066d49e1aeSJan Lentfer 	 * SK = GKDF-160 (MK, inputString)[128..143]
2076d49e1aeSJan Lentfer 	 * PK = GKDF-160 (MK, inputString)[144..159]
2086d49e1aeSJan Lentfer 	 * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
2096d49e1aeSJan Lentfer 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
2106d49e1aeSJan Lentfer 	 *                      CSuite_Sel || inputString)
2116d49e1aeSJan Lentfer 	 */
2126d49e1aeSJan Lentfer 
2136d49e1aeSJan Lentfer 	*sk_len = EAP_GPSK_SK_LEN_AES;
2146d49e1aeSJan Lentfer 	*pk_len = EAP_GPSK_PK_LEN_AES;
2156d49e1aeSJan Lentfer 
2166d49e1aeSJan Lentfer 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
2176d49e1aeSJan Lentfer 					   kdf_out, sizeof(kdf_out),
2186d49e1aeSJan Lentfer 					   psk, psk_len, seed, seed_len,
2196d49e1aeSJan Lentfer 					   msk, emsk, sk, *sk_len,
2206d49e1aeSJan Lentfer 					   pk, *pk_len);
2216d49e1aeSJan Lentfer }
2226d49e1aeSJan Lentfer 
2236d49e1aeSJan Lentfer 
2246d49e1aeSJan Lentfer #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)2256d49e1aeSJan Lentfer static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
2266d49e1aeSJan Lentfer 				       const u8 *seed, size_t seed_len,
2276d49e1aeSJan Lentfer 				       u8 *msk, u8 *emsk,
2286d49e1aeSJan Lentfer 				       u8 *sk, size_t *sk_len)
2296d49e1aeSJan Lentfer {
2306d49e1aeSJan Lentfer #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
2316d49e1aeSJan Lentfer #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
2326d49e1aeSJan Lentfer 	u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
2336d49e1aeSJan Lentfer 		   EAP_GPSK_PK_LEN_SHA256];
2346d49e1aeSJan Lentfer 
2356d49e1aeSJan Lentfer 	/*
2366d49e1aeSJan Lentfer 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
2376d49e1aeSJan Lentfer 	 *            (= seed)
2386d49e1aeSJan Lentfer 	 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
2396d49e1aeSJan Lentfer 	 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
2406d49e1aeSJan Lentfer 	 * MSK = GKDF-160 (MK, inputString)[0..63]
2416d49e1aeSJan Lentfer 	 * EMSK = GKDF-160 (MK, inputString)[64..127]
2426d49e1aeSJan Lentfer 	 * SK = GKDF-160 (MK, inputString)[128..159]
2436d49e1aeSJan Lentfer 	 * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
2446d49e1aeSJan Lentfer 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
2456d49e1aeSJan Lentfer 	 *                      CSuite_Sel || inputString)
2466d49e1aeSJan Lentfer 	 */
2476d49e1aeSJan Lentfer 
2486d49e1aeSJan Lentfer 	*sk_len = EAP_GPSK_SK_LEN_SHA256;
2496d49e1aeSJan Lentfer 
2506d49e1aeSJan Lentfer 	return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
2516d49e1aeSJan Lentfer 					   kdf_out, sizeof(kdf_out),
2526d49e1aeSJan Lentfer 					   psk, psk_len, seed, seed_len,
2536d49e1aeSJan Lentfer 					   msk, emsk, sk, *sk_len,
2546d49e1aeSJan Lentfer 					   NULL, 0);
2556d49e1aeSJan Lentfer }
2566d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
2576d49e1aeSJan Lentfer 
2586d49e1aeSJan Lentfer 
2596d49e1aeSJan Lentfer /**
2606d49e1aeSJan Lentfer  * eap_gpsk_derive_keys - Derive EAP-GPSK keys
2616d49e1aeSJan Lentfer  * @psk: Pre-shared key
2626d49e1aeSJan Lentfer  * @psk_len: Length of psk in bytes
2636d49e1aeSJan Lentfer  * @vendor: CSuite/Vendor
2646d49e1aeSJan Lentfer  * @specifier: CSuite/Specifier
2656d49e1aeSJan Lentfer  * @rand_peer: 32-byte RAND_Peer
2666d49e1aeSJan Lentfer  * @rand_server: 32-byte RAND_Server
2676d49e1aeSJan Lentfer  * @id_peer: ID_Peer
2686d49e1aeSJan Lentfer  * @id_peer_len: Length of ID_Peer
2696d49e1aeSJan Lentfer  * @id_server: ID_Server
2706d49e1aeSJan Lentfer  * @id_server_len: Length of ID_Server
2716d49e1aeSJan Lentfer  * @msk: Buffer for 64-byte MSK
2726d49e1aeSJan Lentfer  * @emsk: Buffer for 64-byte EMSK
2736d49e1aeSJan Lentfer  * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
2746d49e1aeSJan Lentfer  * @sk_len: Buffer for returning length of SK
2756d49e1aeSJan Lentfer  * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
2766d49e1aeSJan Lentfer  * @pk_len: Buffer for returning length of PK
2776d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
2786d49e1aeSJan Lentfer  */
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)2796d49e1aeSJan Lentfer int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
2806d49e1aeSJan Lentfer 			 int specifier,
2816d49e1aeSJan Lentfer 			 const u8 *rand_peer, const u8 *rand_server,
2826d49e1aeSJan Lentfer 			 const u8 *id_peer, size_t id_peer_len,
2836d49e1aeSJan Lentfer 			 const u8 *id_server, size_t id_server_len,
2846d49e1aeSJan Lentfer 			 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
2856d49e1aeSJan Lentfer 			 u8 *pk, size_t *pk_len)
2866d49e1aeSJan Lentfer {
2876d49e1aeSJan Lentfer 	u8 *seed, *pos;
2886d49e1aeSJan Lentfer 	int ret;
2896d49e1aeSJan Lentfer 
2906d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
2916d49e1aeSJan Lentfer 		   vendor, specifier);
2926d49e1aeSJan Lentfer 
2936d49e1aeSJan Lentfer 	if (vendor != EAP_GPSK_VENDOR_IETF)
2946d49e1aeSJan Lentfer 		return -1;
2956d49e1aeSJan Lentfer 
2966d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
2976d49e1aeSJan Lentfer 
2986d49e1aeSJan Lentfer 	/* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
299*a1157835SDaniel Fojt 	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
3006d49e1aeSJan Lentfer 	if (seed == NULL) {
3016d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
3026d49e1aeSJan Lentfer 			   "for key derivation");
3036d49e1aeSJan Lentfer 		return -1;
3046d49e1aeSJan Lentfer 	}
3056d49e1aeSJan Lentfer 
3066d49e1aeSJan Lentfer 	pos = seed;
3076d49e1aeSJan Lentfer 	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
3086d49e1aeSJan Lentfer 	pos += EAP_GPSK_RAND_LEN;
3096d49e1aeSJan Lentfer 	os_memcpy(pos, id_peer, id_peer_len);
3106d49e1aeSJan Lentfer 	pos += id_peer_len;
3116d49e1aeSJan Lentfer 	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
3126d49e1aeSJan Lentfer 	pos += EAP_GPSK_RAND_LEN;
3136d49e1aeSJan Lentfer 	os_memcpy(pos, id_server, id_server_len);
3146d49e1aeSJan Lentfer 	pos += id_server_len;
315*a1157835SDaniel Fojt 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
3166d49e1aeSJan Lentfer 
3176d49e1aeSJan Lentfer 	switch (specifier) {
3186d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_AES:
319*a1157835SDaniel Fojt 		ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
3206d49e1aeSJan Lentfer 					       msk, emsk, sk, sk_len,
3216d49e1aeSJan Lentfer 					       pk, pk_len);
3226d49e1aeSJan Lentfer 		break;
3236d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
3246d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_SHA256:
325*a1157835SDaniel Fojt 		ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
326*a1157835SDaniel Fojt 						  pos - seed,
3276d49e1aeSJan Lentfer 						  msk, emsk, sk, sk_len);
3286d49e1aeSJan Lentfer 		break;
3296d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
3306d49e1aeSJan Lentfer 	default:
3316d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
3326d49e1aeSJan Lentfer 			   "key derivation", vendor, specifier);
3336d49e1aeSJan Lentfer 		ret = -1;
3346d49e1aeSJan Lentfer 		break;
3356d49e1aeSJan Lentfer 	}
3366d49e1aeSJan Lentfer 
3376d49e1aeSJan Lentfer 	os_free(seed);
3386d49e1aeSJan Lentfer 
3396d49e1aeSJan Lentfer 	return ret;
3406d49e1aeSJan Lentfer }
3416d49e1aeSJan Lentfer 
3426d49e1aeSJan Lentfer 
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)3433ff40c12SJohn Marino static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
3443ff40c12SJohn Marino 				      u8 *kdf_out, size_t kdf_out_len,
3453ff40c12SJohn Marino 				      const u8 *psk, const u8 *seed,
3463ff40c12SJohn Marino 				      size_t seed_len, u8 method_type)
3473ff40c12SJohn Marino {
3483ff40c12SJohn Marino 	u8 *pos, *data;
3493ff40c12SJohn Marino 	size_t data_len;
3503ff40c12SJohn Marino 	int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
3513ff40c12SJohn Marino 		    u8 *buf, size_t len);
3523ff40c12SJohn Marino 
3533ff40c12SJohn Marino 	gkdf = NULL;
3543ff40c12SJohn Marino 	switch (csuite_specifier) {
3553ff40c12SJohn Marino 	case EAP_GPSK_CIPHER_AES:
3563ff40c12SJohn Marino 		gkdf = eap_gpsk_gkdf_cmac;
3573ff40c12SJohn Marino 		break;
3583ff40c12SJohn Marino #ifdef EAP_GPSK_SHA256
3593ff40c12SJohn Marino 	case EAP_GPSK_CIPHER_SHA256:
3603ff40c12SJohn Marino 		gkdf = eap_gpsk_gkdf_sha256;
3613ff40c12SJohn Marino 		break;
3623ff40c12SJohn Marino #endif /* EAP_GPSK_SHA256 */
3633ff40c12SJohn Marino 	default:
3643ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
3653ff40c12SJohn Marino 			   "Session-Id derivation", csuite_specifier);
3663ff40c12SJohn Marino 		return -1;
3673ff40c12SJohn Marino 	}
3683ff40c12SJohn Marino 
3693ff40c12SJohn Marino #define SID_LABEL "Method ID"
3703ff40c12SJohn Marino 	/* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
3713ff40c12SJohn Marino 	data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
3723ff40c12SJohn Marino 	data = os_malloc(data_len);
3733ff40c12SJohn Marino 	if (data == NULL)
3743ff40c12SJohn Marino 		return -1;
3753ff40c12SJohn Marino 	pos = data;
3763ff40c12SJohn Marino 	os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
3773ff40c12SJohn Marino 	pos += strlen(SID_LABEL);
3783ff40c12SJohn Marino #undef SID_LABEL
3793ff40c12SJohn Marino 	os_memcpy(pos, &method_type, 1);
3803ff40c12SJohn Marino 	pos += 1;
3813ff40c12SJohn Marino 	WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
3823ff40c12SJohn Marino 	pos += 4;
3833ff40c12SJohn Marino 	WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
3843ff40c12SJohn Marino 	pos += 2;
3853ff40c12SJohn Marino 	os_memcpy(pos, seed, seed_len); /* inputString */
3863ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
3873ff40c12SJohn Marino 		    data, data_len);
3883ff40c12SJohn Marino 
3893ff40c12SJohn Marino 	if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
3903ff40c12SJohn Marino 		os_free(data);
3913ff40c12SJohn Marino 		return -1;
3923ff40c12SJohn Marino 	}
3933ff40c12SJohn Marino 	os_free(data);
3943ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
3953ff40c12SJohn Marino 
3963ff40c12SJohn Marino 	return 0;
3973ff40c12SJohn Marino }
3983ff40c12SJohn Marino 
3993ff40c12SJohn Marino 
4003ff40c12SJohn Marino /**
4013ff40c12SJohn Marino  * eap_gpsk_session_id - Derive EAP-GPSK Session ID
4023ff40c12SJohn Marino  * @psk: Pre-shared key
4033ff40c12SJohn Marino  * @psk_len: Length of psk in bytes
4043ff40c12SJohn Marino  * @vendor: CSuite/Vendor
4053ff40c12SJohn Marino  * @specifier: CSuite/Specifier
4063ff40c12SJohn Marino  * @rand_peer: 32-byte RAND_Peer
4073ff40c12SJohn Marino  * @rand_server: 32-byte RAND_Server
4083ff40c12SJohn Marino  * @id_peer: ID_Peer
4093ff40c12SJohn Marino  * @id_peer_len: Length of ID_Peer
4103ff40c12SJohn Marino  * @id_server: ID_Server
4113ff40c12SJohn Marino  * @id_server_len: Length of ID_Server
4123ff40c12SJohn Marino  * @method_type: EAP Authentication Method Type
4133ff40c12SJohn Marino  * @sid: Buffer for 17-byte Session ID
4143ff40c12SJohn Marino  * @sid_len: Buffer for returning length of Session ID
4153ff40c12SJohn Marino  * Returns: 0 on success, -1 on failure
4163ff40c12SJohn Marino  */
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)4173ff40c12SJohn Marino int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
4183ff40c12SJohn Marino 			       int specifier,
4193ff40c12SJohn Marino 			       const u8 *rand_peer, const u8 *rand_server,
4203ff40c12SJohn Marino 			       const u8 *id_peer, size_t id_peer_len,
4213ff40c12SJohn Marino 			       const u8 *id_server, size_t id_server_len,
4223ff40c12SJohn Marino 			       u8 method_type, u8 *sid, size_t *sid_len)
4233ff40c12SJohn Marino {
4243ff40c12SJohn Marino 	u8 *seed, *pos;
4253ff40c12SJohn Marino 	u8 kdf_out[16];
4263ff40c12SJohn Marino 	int ret;
4273ff40c12SJohn Marino 
4283ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
4293ff40c12SJohn Marino 		   vendor, specifier);
4303ff40c12SJohn Marino 
4313ff40c12SJohn Marino 	if (vendor != EAP_GPSK_VENDOR_IETF)
4323ff40c12SJohn Marino 		return -1;
4333ff40c12SJohn Marino 
4343ff40c12SJohn Marino 	wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
4353ff40c12SJohn Marino 
4363ff40c12SJohn Marino 	/*
4373ff40c12SJohn Marino 	 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
4383ff40c12SJohn Marino 	 *            (= seed)
4393ff40c12SJohn Marino 	 * KS = 16, CSuite_Sel = 0x00000000 0x0001
4403ff40c12SJohn Marino 	 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
4413ff40c12SJohn Marino 	 *                      CSuite_Sel || inputString)
4423ff40c12SJohn Marino 	 */
443*a1157835SDaniel Fojt 	seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
4443ff40c12SJohn Marino 	if (seed == NULL) {
4453ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
4463ff40c12SJohn Marino 			   "for Session-Id derivation");
4473ff40c12SJohn Marino 		return -1;
4483ff40c12SJohn Marino 	}
4493ff40c12SJohn Marino 
4503ff40c12SJohn Marino 	pos = seed;
4513ff40c12SJohn Marino 	os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
4523ff40c12SJohn Marino 	pos += EAP_GPSK_RAND_LEN;
4533ff40c12SJohn Marino 	os_memcpy(pos, id_peer, id_peer_len);
4543ff40c12SJohn Marino 	pos += id_peer_len;
4553ff40c12SJohn Marino 	os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
4563ff40c12SJohn Marino 	pos += EAP_GPSK_RAND_LEN;
4573ff40c12SJohn Marino 	os_memcpy(pos, id_server, id_server_len);
4583ff40c12SJohn Marino 	pos += id_server_len;
459*a1157835SDaniel Fojt 	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
4603ff40c12SJohn Marino 
4613ff40c12SJohn Marino 	ret = eap_gpsk_derive_mid_helper(specifier,
4623ff40c12SJohn Marino 					 kdf_out, sizeof(kdf_out),
463*a1157835SDaniel Fojt 					 psk, seed, pos - seed,
4643ff40c12SJohn Marino 					 method_type);
4653ff40c12SJohn Marino 
4663ff40c12SJohn Marino 	sid[0] = method_type;
4673ff40c12SJohn Marino 	os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
4683ff40c12SJohn Marino 	*sid_len = 1 + sizeof(kdf_out);
4693ff40c12SJohn Marino 
4703ff40c12SJohn Marino 	os_free(seed);
4713ff40c12SJohn Marino 
4723ff40c12SJohn Marino 	return ret;
4733ff40c12SJohn Marino }
4743ff40c12SJohn Marino 
4753ff40c12SJohn Marino 
4766d49e1aeSJan Lentfer /**
4776d49e1aeSJan Lentfer  * eap_gpsk_mic_len - Get the length of the MIC
4786d49e1aeSJan Lentfer  * @vendor: CSuite/Vendor
4796d49e1aeSJan Lentfer  * @specifier: CSuite/Specifier
4806d49e1aeSJan Lentfer  * Returns: MIC length in bytes
4816d49e1aeSJan Lentfer  */
eap_gpsk_mic_len(int vendor,int specifier)4826d49e1aeSJan Lentfer size_t eap_gpsk_mic_len(int vendor, int specifier)
4836d49e1aeSJan Lentfer {
4846d49e1aeSJan Lentfer 	if (vendor != EAP_GPSK_VENDOR_IETF)
4856d49e1aeSJan Lentfer 		return 0;
4866d49e1aeSJan Lentfer 
4876d49e1aeSJan Lentfer 	switch (specifier) {
4886d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_AES:
4896d49e1aeSJan Lentfer 		return 16;
4906d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
4916d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_SHA256:
4926d49e1aeSJan Lentfer 		return 32;
4936d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
4946d49e1aeSJan Lentfer 	default:
4956d49e1aeSJan Lentfer 		return 0;
4966d49e1aeSJan Lentfer 	}
4976d49e1aeSJan Lentfer }
4986d49e1aeSJan Lentfer 
4996d49e1aeSJan Lentfer 
eap_gpsk_compute_mic_aes(const u8 * sk,size_t sk_len,const u8 * data,size_t len,u8 * mic)5006d49e1aeSJan Lentfer static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
5016d49e1aeSJan Lentfer 				    const u8 *data, size_t len, u8 *mic)
5026d49e1aeSJan Lentfer {
5036d49e1aeSJan Lentfer 	if (sk_len != 16) {
5046d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
5056d49e1aeSJan Lentfer 			   "AES-CMAC MIC", (unsigned long) sk_len);
5066d49e1aeSJan Lentfer 		return -1;
5076d49e1aeSJan Lentfer 	}
5086d49e1aeSJan Lentfer 
5096d49e1aeSJan Lentfer 	return omac1_aes_128(sk, data, len, mic);
5106d49e1aeSJan Lentfer }
5116d49e1aeSJan Lentfer 
5126d49e1aeSJan Lentfer 
5136d49e1aeSJan Lentfer /**
5146d49e1aeSJan Lentfer  * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
5156d49e1aeSJan Lentfer  * @sk: Session key SK from eap_gpsk_derive_keys()
5166d49e1aeSJan Lentfer  * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
5176d49e1aeSJan Lentfer  * @vendor: CSuite/Vendor
5186d49e1aeSJan Lentfer  * @specifier: CSuite/Specifier
5196d49e1aeSJan Lentfer  * @data: Input data to MIC
5206d49e1aeSJan Lentfer  * @len: Input data length in bytes
5216d49e1aeSJan Lentfer  * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
5226d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
5236d49e1aeSJan Lentfer  */
eap_gpsk_compute_mic(const u8 * sk,size_t sk_len,int vendor,int specifier,const u8 * data,size_t len,u8 * mic)5246d49e1aeSJan Lentfer int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
5256d49e1aeSJan Lentfer 			 int specifier, const u8 *data, size_t len, u8 *mic)
5266d49e1aeSJan Lentfer {
5276d49e1aeSJan Lentfer 	int ret;
5286d49e1aeSJan Lentfer 
5296d49e1aeSJan Lentfer 	if (vendor != EAP_GPSK_VENDOR_IETF)
5306d49e1aeSJan Lentfer 		return -1;
5316d49e1aeSJan Lentfer 
5326d49e1aeSJan Lentfer 	switch (specifier) {
5336d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_AES:
5346d49e1aeSJan Lentfer 		ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
5356d49e1aeSJan Lentfer 		break;
5366d49e1aeSJan Lentfer #ifdef EAP_GPSK_SHA256
5376d49e1aeSJan Lentfer 	case EAP_GPSK_CIPHER_SHA256:
538*a1157835SDaniel Fojt 		ret = hmac_sha256(sk, sk_len, data, len, mic);
5396d49e1aeSJan Lentfer 		break;
5406d49e1aeSJan Lentfer #endif /* EAP_GPSK_SHA256 */
5416d49e1aeSJan Lentfer 	default:
5426d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
5436d49e1aeSJan Lentfer 			   "MIC computation", vendor, specifier);
5446d49e1aeSJan Lentfer 		ret = -1;
5456d49e1aeSJan Lentfer 		break;
5466d49e1aeSJan Lentfer 	}
5476d49e1aeSJan Lentfer 
548*a1157835SDaniel Fojt 	if (ret)
549*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
550*a1157835SDaniel Fojt 
5516d49e1aeSJan Lentfer 	return ret;
5526d49e1aeSJan Lentfer }
553