Home | History | Annotate | Download | only in wpa_supplicant
      1 /*
      2  * EAP server/peer: EAP-GPSK shared routines
      3  * Copyright (c) 2006-2007, Jouni Malinen <j (at) 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  */
     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 
     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
     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 
    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 
    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
    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  */
    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  */
    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 
    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  */
    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