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