1189251Ssam/* 2189251Ssam * EAP server/peer: EAP-GPSK shared routines 3189251Ssam * Copyright (c) 2006-2007, 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/aes_wrap.h" 13214734Srpaulo#include "crypto/sha256.h" 14189251Ssam#include "eap_defs.h" 15189251Ssam#include "eap_gpsk_common.h" 16189251Ssam 17189251Ssam 18189251Ssam/** 19189251Ssam * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported 20189251Ssam * @vendor: CSuite/Vendor 21189251Ssam * @specifier: CSuite/Specifier 22189251Ssam * Returns: 1 if ciphersuite is support, or 0 if not 23189251Ssam */ 24189251Ssamint eap_gpsk_supported_ciphersuite(int vendor, int specifier) 25189251Ssam{ 26189251Ssam if (vendor == EAP_GPSK_VENDOR_IETF && 27189251Ssam specifier == EAP_GPSK_CIPHER_AES) 28189251Ssam return 1; 29189251Ssam#ifdef EAP_GPSK_SHA256 30189251Ssam if (vendor == EAP_GPSK_VENDOR_IETF && 31189251Ssam specifier == EAP_GPSK_CIPHER_SHA256) 32189251Ssam return 1; 33189251Ssam#endif /* EAP_GPSK_SHA256 */ 34189251Ssam return 0; 35189251Ssam} 36189251Ssam 37189251Ssam 38189251Ssamstatic int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, 39189251Ssam const u8 *data /* Z */, size_t data_len, 40189251Ssam u8 *buf, size_t len /* X */) 41189251Ssam{ 42189251Ssam u8 *opos; 43189251Ssam size_t i, n, hashlen, left, clen; 44189251Ssam u8 ibuf[2], hash[16]; 45189251Ssam const u8 *addr[2]; 46189251Ssam size_t vlen[2]; 47189251Ssam 48189251Ssam hashlen = sizeof(hash); 49189251Ssam /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */ 50189251Ssam addr[0] = ibuf; 51189251Ssam vlen[0] = sizeof(ibuf); 52189251Ssam addr[1] = data; 53189251Ssam vlen[1] = data_len; 54189251Ssam 55189251Ssam opos = buf; 56189251Ssam left = len; 57189251Ssam n = (len + hashlen - 1) / hashlen; 58189251Ssam for (i = 1; i <= n; i++) { 59189251Ssam WPA_PUT_BE16(ibuf, i); 60189251Ssam if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) 61189251Ssam return -1; 62189251Ssam clen = left > hashlen ? hashlen : left; 63189251Ssam os_memcpy(opos, hash, clen); 64189251Ssam opos += clen; 65189251Ssam left -= clen; 66189251Ssam } 67189251Ssam 68189251Ssam return 0; 69189251Ssam} 70189251Ssam 71189251Ssam 72189251Ssam#ifdef EAP_GPSK_SHA256 73189251Ssamstatic int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, 74189251Ssam const u8 *data /* Z */, size_t data_len, 75189251Ssam u8 *buf, size_t len /* X */) 76189251Ssam{ 77189251Ssam u8 *opos; 78189251Ssam size_t i, n, hashlen, left, clen; 79189251Ssam u8 ibuf[2], hash[SHA256_MAC_LEN]; 80189251Ssam const u8 *addr[2]; 81189251Ssam size_t vlen[2]; 82189251Ssam 83189251Ssam hashlen = SHA256_MAC_LEN; 84189251Ssam /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */ 85189251Ssam addr[0] = ibuf; 86189251Ssam vlen[0] = sizeof(ibuf); 87189251Ssam addr[1] = data; 88189251Ssam vlen[1] = data_len; 89189251Ssam 90189251Ssam opos = buf; 91189251Ssam left = len; 92189251Ssam n = (len + hashlen - 1) / hashlen; 93189251Ssam for (i = 1; i <= n; i++) { 94189251Ssam WPA_PUT_BE16(ibuf, i); 95189251Ssam hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); 96189251Ssam clen = left > hashlen ? hashlen : left; 97189251Ssam os_memcpy(opos, hash, clen); 98189251Ssam opos += clen; 99189251Ssam left -= clen; 100189251Ssam } 101189251Ssam 102189251Ssam return 0; 103189251Ssam} 104189251Ssam#endif /* EAP_GPSK_SHA256 */ 105189251Ssam 106189251Ssam 107189251Ssamstatic int eap_gpsk_derive_keys_helper(u32 csuite_specifier, 108189251Ssam u8 *kdf_out, size_t kdf_out_len, 109189251Ssam const u8 *psk, size_t psk_len, 110189251Ssam const u8 *seed, size_t seed_len, 111189251Ssam u8 *msk, u8 *emsk, 112189251Ssam u8 *sk, size_t sk_len, 113189251Ssam u8 *pk, size_t pk_len) 114189251Ssam{ 115189251Ssam u8 mk[32], *pos, *data; 116189251Ssam size_t data_len, mk_len; 117189251Ssam int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 118189251Ssam u8 *buf, size_t len); 119189251Ssam 120189251Ssam gkdf = NULL; 121189251Ssam switch (csuite_specifier) { 122189251Ssam case EAP_GPSK_CIPHER_AES: 123189251Ssam gkdf = eap_gpsk_gkdf_cmac; 124189251Ssam mk_len = 16; 125189251Ssam break; 126189251Ssam#ifdef EAP_GPSK_SHA256 127189251Ssam case EAP_GPSK_CIPHER_SHA256: 128189251Ssam gkdf = eap_gpsk_gkdf_sha256; 129189251Ssam mk_len = SHA256_MAC_LEN; 130189251Ssam break; 131189251Ssam#endif /* EAP_GPSK_SHA256 */ 132189251Ssam default: 133189251Ssam return -1; 134189251Ssam } 135189251Ssam 136189251Ssam if (psk_len < mk_len) 137189251Ssam return -1; 138189251Ssam 139189251Ssam data_len = 2 + psk_len + 6 + seed_len; 140189251Ssam data = os_malloc(data_len); 141189251Ssam if (data == NULL) 142189251Ssam return -1; 143189251Ssam pos = data; 144189251Ssam WPA_PUT_BE16(pos, psk_len); 145189251Ssam pos += 2; 146189251Ssam os_memcpy(pos, psk, psk_len); 147189251Ssam pos += psk_len; 148189251Ssam WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ 149189251Ssam pos += 4; 150189251Ssam WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ 151189251Ssam pos += 2; 152189251Ssam os_memcpy(pos, seed, seed_len); /* inputString */ 153189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation", 154189251Ssam data, data_len); 155189251Ssam 156189251Ssam if (gkdf(psk, data, data_len, mk, mk_len) < 0) { 157189251Ssam os_free(data); 158189251Ssam return -1; 159189251Ssam } 160189251Ssam os_free(data); 161189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len); 162189251Ssam 163189251Ssam if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0) 164189251Ssam return -1; 165189251Ssam 166189251Ssam pos = kdf_out; 167189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); 168189251Ssam os_memcpy(msk, pos, EAP_MSK_LEN); 169189251Ssam pos += EAP_MSK_LEN; 170189251Ssam 171189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); 172189251Ssam os_memcpy(emsk, pos, EAP_EMSK_LEN); 173189251Ssam pos += EAP_EMSK_LEN; 174189251Ssam 175189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len); 176189251Ssam os_memcpy(sk, pos, sk_len); 177189251Ssam pos += sk_len; 178189251Ssam 179189251Ssam if (pk) { 180189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len); 181189251Ssam os_memcpy(pk, pos, pk_len); 182189251Ssam } 183189251Ssam 184189251Ssam return 0; 185189251Ssam} 186189251Ssam 187189251Ssam 188189251Ssamstatic int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, 189189251Ssam const u8 *seed, size_t seed_len, 190189251Ssam u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 191189251Ssam u8 *pk, size_t *pk_len) 192189251Ssam{ 193189251Ssam#define EAP_GPSK_SK_LEN_AES 16 194189251Ssam#define EAP_GPSK_PK_LEN_AES 16 195189251Ssam u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + 196189251Ssam EAP_GPSK_PK_LEN_AES]; 197189251Ssam 198189251Ssam /* 199189251Ssam * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 200189251Ssam * (= seed) 201189251Ssam * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001 202189251Ssam * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString) 203189251Ssam * MSK = GKDF-160 (MK, inputString)[0..63] 204189251Ssam * EMSK = GKDF-160 (MK, inputString)[64..127] 205189251Ssam * SK = GKDF-160 (MK, inputString)[128..143] 206189251Ssam * PK = GKDF-160 (MK, inputString)[144..159] 207189251Ssam * zero = 0x00 || 0x00 || ... || 0x00 (16 times) 208189251Ssam * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 209189251Ssam * CSuite_Sel || inputString) 210189251Ssam */ 211189251Ssam 212189251Ssam *sk_len = EAP_GPSK_SK_LEN_AES; 213189251Ssam *pk_len = EAP_GPSK_PK_LEN_AES; 214189251Ssam 215189251Ssam return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES, 216189251Ssam kdf_out, sizeof(kdf_out), 217189251Ssam psk, psk_len, seed, seed_len, 218189251Ssam msk, emsk, sk, *sk_len, 219189251Ssam pk, *pk_len); 220189251Ssam} 221189251Ssam 222189251Ssam 223189251Ssam#ifdef EAP_GPSK_SHA256 224189251Ssamstatic int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, 225189251Ssam const u8 *seed, size_t seed_len, 226189251Ssam u8 *msk, u8 *emsk, 227189251Ssam u8 *sk, size_t *sk_len) 228189251Ssam{ 229189251Ssam#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN 230189251Ssam#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN 231189251Ssam u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + 232189251Ssam EAP_GPSK_PK_LEN_SHA256]; 233189251Ssam 234189251Ssam /* 235189251Ssam * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 236189251Ssam * (= seed) 237189251Ssam * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002 238189251Ssam * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString) 239189251Ssam * MSK = GKDF-160 (MK, inputString)[0..63] 240189251Ssam * EMSK = GKDF-160 (MK, inputString)[64..127] 241189251Ssam * SK = GKDF-160 (MK, inputString)[128..159] 242189251Ssam * zero = 0x00 || 0x00 || ... || 0x00 (32 times) 243189251Ssam * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 244189251Ssam * CSuite_Sel || inputString) 245189251Ssam */ 246189251Ssam 247189251Ssam *sk_len = EAP_GPSK_SK_LEN_SHA256; 248189251Ssam 249189251Ssam return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256, 250189251Ssam kdf_out, sizeof(kdf_out), 251189251Ssam psk, psk_len, seed, seed_len, 252189251Ssam msk, emsk, sk, *sk_len, 253189251Ssam NULL, 0); 254189251Ssam} 255189251Ssam#endif /* EAP_GPSK_SHA256 */ 256189251Ssam 257189251Ssam 258189251Ssam/** 259189251Ssam * eap_gpsk_derive_keys - Derive EAP-GPSK keys 260189251Ssam * @psk: Pre-shared key 261189251Ssam * @psk_len: Length of psk in bytes 262189251Ssam * @vendor: CSuite/Vendor 263189251Ssam * @specifier: CSuite/Specifier 264189251Ssam * @rand_peer: 32-byte RAND_Peer 265189251Ssam * @rand_server: 32-byte RAND_Server 266189251Ssam * @id_peer: ID_Peer 267189251Ssam * @id_peer_len: Length of ID_Peer 268189251Ssam * @id_server: ID_Server 269189251Ssam * @id_server_len: Length of ID_Server 270189251Ssam * @msk: Buffer for 64-byte MSK 271189251Ssam * @emsk: Buffer for 64-byte EMSK 272189251Ssam * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) 273189251Ssam * @sk_len: Buffer for returning length of SK 274189251Ssam * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes) 275189251Ssam * @pk_len: Buffer for returning length of PK 276189251Ssam * Returns: 0 on success, -1 on failure 277189251Ssam */ 278189251Ssamint eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, 279189251Ssam int specifier, 280189251Ssam const u8 *rand_peer, const u8 *rand_server, 281189251Ssam const u8 *id_peer, size_t id_peer_len, 282189251Ssam const u8 *id_server, size_t id_server_len, 283189251Ssam u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 284189251Ssam u8 *pk, size_t *pk_len) 285189251Ssam{ 286189251Ssam u8 *seed, *pos; 287189251Ssam size_t seed_len; 288189251Ssam int ret; 289189251Ssam 290189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", 291189251Ssam vendor, specifier); 292189251Ssam 293189251Ssam if (vendor != EAP_GPSK_VENDOR_IETF) 294189251Ssam return -1; 295189251Ssam 296189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 297189251Ssam 298189251Ssam /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ 299189251Ssam seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; 300189251Ssam seed = os_malloc(seed_len); 301189251Ssam if (seed == NULL) { 302189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 303189251Ssam "for key derivation"); 304189251Ssam return -1; 305189251Ssam } 306189251Ssam 307189251Ssam pos = seed; 308189251Ssam os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 309189251Ssam pos += EAP_GPSK_RAND_LEN; 310189251Ssam os_memcpy(pos, id_peer, id_peer_len); 311189251Ssam pos += id_peer_len; 312189251Ssam os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 313189251Ssam pos += EAP_GPSK_RAND_LEN; 314189251Ssam os_memcpy(pos, id_server, id_server_len); 315189251Ssam pos += id_server_len; 316189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); 317189251Ssam 318189251Ssam switch (specifier) { 319189251Ssam case EAP_GPSK_CIPHER_AES: 320189251Ssam ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, 321189251Ssam msk, emsk, sk, sk_len, 322189251Ssam pk, pk_len); 323189251Ssam break; 324189251Ssam#ifdef EAP_GPSK_SHA256 325189251Ssam case EAP_GPSK_CIPHER_SHA256: 326189251Ssam ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, 327189251Ssam msk, emsk, sk, sk_len); 328189251Ssam break; 329189251Ssam#endif /* EAP_GPSK_SHA256 */ 330189251Ssam default: 331189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 332189251Ssam "key derivation", vendor, specifier); 333189251Ssam ret = -1; 334189251Ssam break; 335189251Ssam } 336189251Ssam 337189251Ssam os_free(seed); 338189251Ssam 339189251Ssam return ret; 340189251Ssam} 341189251Ssam 342189251Ssam 343189251Ssam/** 344189251Ssam * eap_gpsk_mic_len - Get the length of the MIC 345189251Ssam * @vendor: CSuite/Vendor 346189251Ssam * @specifier: CSuite/Specifier 347189251Ssam * Returns: MIC length in bytes 348189251Ssam */ 349189251Ssamsize_t eap_gpsk_mic_len(int vendor, int specifier) 350189251Ssam{ 351189251Ssam if (vendor != EAP_GPSK_VENDOR_IETF) 352189251Ssam return 0; 353189251Ssam 354189251Ssam switch (specifier) { 355189251Ssam case EAP_GPSK_CIPHER_AES: 356189251Ssam return 16; 357189251Ssam#ifdef EAP_GPSK_SHA256 358189251Ssam case EAP_GPSK_CIPHER_SHA256: 359189251Ssam return 32; 360189251Ssam#endif /* EAP_GPSK_SHA256 */ 361189251Ssam default: 362189251Ssam return 0; 363189251Ssam } 364189251Ssam} 365189251Ssam 366189251Ssam 367189251Ssamstatic int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, 368189251Ssam const u8 *data, size_t len, u8 *mic) 369189251Ssam{ 370189251Ssam if (sk_len != 16) { 371189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " 372189251Ssam "AES-CMAC MIC", (unsigned long) sk_len); 373189251Ssam return -1; 374189251Ssam } 375189251Ssam 376189251Ssam return omac1_aes_128(sk, data, len, mic); 377189251Ssam} 378189251Ssam 379189251Ssam 380189251Ssam/** 381189251Ssam * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet 382189251Ssam * @sk: Session key SK from eap_gpsk_derive_keys() 383189251Ssam * @sk_len: SK length in bytes from eap_gpsk_derive_keys() 384189251Ssam * @vendor: CSuite/Vendor 385189251Ssam * @specifier: CSuite/Specifier 386189251Ssam * @data: Input data to MIC 387189251Ssam * @len: Input data length in bytes 388189251Ssam * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes 389189251Ssam * Returns: 0 on success, -1 on failure 390189251Ssam */ 391189251Ssamint eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, 392189251Ssam int specifier, const u8 *data, size_t len, u8 *mic) 393189251Ssam{ 394189251Ssam int ret; 395189251Ssam 396189251Ssam if (vendor != EAP_GPSK_VENDOR_IETF) 397189251Ssam return -1; 398189251Ssam 399189251Ssam switch (specifier) { 400189251Ssam case EAP_GPSK_CIPHER_AES: 401189251Ssam ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); 402189251Ssam break; 403189251Ssam#ifdef EAP_GPSK_SHA256 404189251Ssam case EAP_GPSK_CIPHER_SHA256: 405189251Ssam hmac_sha256(sk, sk_len, data, len, mic); 406189251Ssam ret = 0; 407189251Ssam break; 408189251Ssam#endif /* EAP_GPSK_SHA256 */ 409189251Ssam default: 410189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 411189251Ssam "MIC computation", vendor, specifier); 412189251Ssam ret = -1; 413189251Ssam break; 414189251Ssam } 415189251Ssam 416189251Ssam return ret; 417189251Ssam} 418