1135446Strhodes/* 2193149Sdougb * EAP server/peer: EAP-GPSK shared routines 3135446Strhodes * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> 4135446Strhodes * 5193149Sdougb * This software may be distributed under the terms of the BSD license. 6135446Strhodes * See README for more details. 7135446Strhodes */ 8135446Strhodes 9135446Strhodes#include "includes.h" 10135446Strhodes 11135446Strhodes#include "common.h" 12135446Strhodes#include "crypto/aes_wrap.h" 13135446Strhodes#include "crypto/sha256.h" 14135446Strhodes#include "eap_defs.h" 15135446Strhodes#include "eap_gpsk_common.h" 16135446Strhodes 17135446Strhodes 18234010Sdougb/** 19135446Strhodes * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported 20135446Strhodes * @vendor: CSuite/Vendor 21135446Strhodes * @specifier: CSuite/Specifier 22135446Strhodes * Returns: 1 if ciphersuite is support, or 0 if not 23135446Strhodes */ 24135446Strhodesint eap_gpsk_supported_ciphersuite(int vendor, int specifier) 25135446Strhodes{ 26135446Strhodes if (vendor == EAP_GPSK_VENDOR_IETF && 27135446Strhodes specifier == EAP_GPSK_CIPHER_AES) 28135446Strhodes return 1; 29135446Strhodes#ifdef EAP_GPSK_SHA256 30135446Strhodes if (vendor == EAP_GPSK_VENDOR_IETF && 31135446Strhodes specifier == EAP_GPSK_CIPHER_SHA256) 32135446Strhodes return 1; 33135446Strhodes#endif /* EAP_GPSK_SHA256 */ 34135446Strhodes return 0; 35135446Strhodes} 36135446Strhodes 37135446Strhodes 38135446Strhodesstatic int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, 39135446Strhodes const u8 *data /* Z */, size_t data_len, 40135446Strhodes u8 *buf, size_t len /* X */) 41135446Strhodes{ 42135446Strhodes u8 *opos; 43135446Strhodes size_t i, n, hashlen, left, clen; 44135446Strhodes u8 ibuf[2], hash[16]; 45135446Strhodes const u8 *addr[2]; 46135446Strhodes size_t vlen[2]; 47135446Strhodes 48135446Strhodes hashlen = sizeof(hash); 49135446Strhodes /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */ 50135446Strhodes addr[0] = ibuf; 51135446Strhodes vlen[0] = sizeof(ibuf); 52135446Strhodes addr[1] = data; 53135446Strhodes vlen[1] = data_len; 54135446Strhodes 55135446Strhodes opos = buf; 56135446Strhodes left = len; 57135446Strhodes n = (len + hashlen - 1) / hashlen; 58135446Strhodes for (i = 1; i <= n; i++) { 59135446Strhodes WPA_PUT_BE16(ibuf, i); 60135446Strhodes if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) 61135446Strhodes return -1; 62135446Strhodes clen = left > hashlen ? hashlen : left; 63135446Strhodes os_memcpy(opos, hash, clen); 64135446Strhodes opos += clen; 65135446Strhodes left -= clen; 66135446Strhodes } 67135446Strhodes 68135446Strhodes return 0; 69135446Strhodes} 70135446Strhodes 71135446Strhodes 72135446Strhodes#ifdef EAP_GPSK_SHA256 73135446Strhodesstatic int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, 74135446Strhodes const u8 *data /* Z */, size_t data_len, 75135446Strhodes u8 *buf, size_t len /* X */) 76135446Strhodes{ 77135446Strhodes u8 *opos; 78135446Strhodes size_t i, n, hashlen, left, clen; 79135446Strhodes u8 ibuf[2], hash[SHA256_MAC_LEN]; 80135446Strhodes const u8 *addr[2]; 81135446Strhodes size_t vlen[2]; 82135446Strhodes 83135446Strhodes hashlen = SHA256_MAC_LEN; 84135446Strhodes /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */ 85135446Strhodes addr[0] = ibuf; 86135446Strhodes vlen[0] = sizeof(ibuf); 87135446Strhodes addr[1] = data; 88135446Strhodes vlen[1] = data_len; 89135446Strhodes 90135446Strhodes opos = buf; 91135446Strhodes left = len; 92135446Strhodes n = (len + hashlen - 1) / hashlen; 93135446Strhodes for (i = 1; i <= n; i++) { 94135446Strhodes WPA_PUT_BE16(ibuf, i); 95135446Strhodes hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); 96135446Strhodes clen = left > hashlen ? hashlen : left; 97135446Strhodes os_memcpy(opos, hash, clen); 98135446Strhodes opos += clen; 99135446Strhodes left -= clen; 100135446Strhodes } 101135446Strhodes 102135446Strhodes return 0; 103135446Strhodes} 104135446Strhodes#endif /* EAP_GPSK_SHA256 */ 105135446Strhodes 106135446Strhodes 107135446Strhodesstatic int eap_gpsk_derive_keys_helper(u32 csuite_specifier, 108135446Strhodes u8 *kdf_out, size_t kdf_out_len, 109135446Strhodes const u8 *psk, size_t psk_len, 110135446Strhodes const u8 *seed, size_t seed_len, 111135446Strhodes u8 *msk, u8 *emsk, 112135446Strhodes u8 *sk, size_t sk_len, 113135446Strhodes u8 *pk, size_t pk_len) 114135446Strhodes{ 115135446Strhodes u8 mk[32], *pos, *data; 116135446Strhodes size_t data_len, mk_len; 117135446Strhodes int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 118135446Strhodes u8 *buf, size_t len); 119135446Strhodes 120135446Strhodes gkdf = NULL; 121135446Strhodes switch (csuite_specifier) { 122135446Strhodes case EAP_GPSK_CIPHER_AES: 123135446Strhodes gkdf = eap_gpsk_gkdf_cmac; 124135446Strhodes mk_len = 16; 125135446Strhodes break; 126135446Strhodes#ifdef EAP_GPSK_SHA256 127 case EAP_GPSK_CIPHER_SHA256: 128 gkdf = eap_gpsk_gkdf_sha256; 129 mk_len = SHA256_MAC_LEN; 130 break; 131#endif /* EAP_GPSK_SHA256 */ 132 default: 133 return -1; 134 } 135 136 if (psk_len < mk_len) 137 return -1; 138 139 data_len = 2 + psk_len + 6 + seed_len; 140 data = os_malloc(data_len); 141 if (data == NULL) 142 return -1; 143 pos = data; 144 WPA_PUT_BE16(pos, psk_len); 145 pos += 2; 146 os_memcpy(pos, psk, psk_len); 147 pos += psk_len; 148 WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ 149 pos += 4; 150 WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ 151 pos += 2; 152 os_memcpy(pos, seed, seed_len); /* inputString */ 153 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation", 154 data, data_len); 155 156 if (gkdf(psk, data, data_len, mk, mk_len) < 0) { 157 os_free(data); 158 return -1; 159 } 160 os_free(data); 161 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len); 162 163 if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0) 164 return -1; 165 166 pos = kdf_out; 167 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); 168 os_memcpy(msk, pos, EAP_MSK_LEN); 169 pos += EAP_MSK_LEN; 170 171 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); 172 os_memcpy(emsk, pos, EAP_EMSK_LEN); 173 pos += EAP_EMSK_LEN; 174 175 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len); 176 os_memcpy(sk, pos, sk_len); 177 pos += sk_len; 178 179 if (pk) { 180 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len); 181 os_memcpy(pk, pos, pk_len); 182 } 183 184 return 0; 185} 186 187 188static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, 189 const u8 *seed, size_t seed_len, 190 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 191 u8 *pk, size_t *pk_len) 192{ 193#define EAP_GPSK_SK_LEN_AES 16 194#define EAP_GPSK_PK_LEN_AES 16 195 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + 196 EAP_GPSK_PK_LEN_AES]; 197 198 /* 199 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 200 * (= seed) 201 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001 202 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString) 203 * MSK = GKDF-160 (MK, inputString)[0..63] 204 * EMSK = GKDF-160 (MK, inputString)[64..127] 205 * SK = GKDF-160 (MK, inputString)[128..143] 206 * PK = GKDF-160 (MK, inputString)[144..159] 207 * zero = 0x00 || 0x00 || ... || 0x00 (16 times) 208 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 209 * CSuite_Sel || inputString) 210 */ 211 212 *sk_len = EAP_GPSK_SK_LEN_AES; 213 *pk_len = EAP_GPSK_PK_LEN_AES; 214 215 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES, 216 kdf_out, sizeof(kdf_out), 217 psk, psk_len, seed, seed_len, 218 msk, emsk, sk, *sk_len, 219 pk, *pk_len); 220} 221 222 223#ifdef EAP_GPSK_SHA256 224static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, 225 const u8 *seed, size_t seed_len, 226 u8 *msk, u8 *emsk, 227 u8 *sk, size_t *sk_len) 228{ 229#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN 230#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN 231 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + 232 EAP_GPSK_PK_LEN_SHA256]; 233 234 /* 235 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 236 * (= seed) 237 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002 238 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString) 239 * MSK = GKDF-160 (MK, inputString)[0..63] 240 * EMSK = GKDF-160 (MK, inputString)[64..127] 241 * SK = GKDF-160 (MK, inputString)[128..159] 242 * zero = 0x00 || 0x00 || ... || 0x00 (32 times) 243 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 244 * CSuite_Sel || inputString) 245 */ 246 247 *sk_len = EAP_GPSK_SK_LEN_SHA256; 248 249 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256, 250 kdf_out, sizeof(kdf_out), 251 psk, psk_len, seed, seed_len, 252 msk, emsk, sk, *sk_len, 253 NULL, 0); 254} 255#endif /* EAP_GPSK_SHA256 */ 256 257 258/** 259 * eap_gpsk_derive_keys - Derive EAP-GPSK keys 260 * @psk: Pre-shared key 261 * @psk_len: Length of psk in bytes 262 * @vendor: CSuite/Vendor 263 * @specifier: CSuite/Specifier 264 * @rand_peer: 32-byte RAND_Peer 265 * @rand_server: 32-byte RAND_Server 266 * @id_peer: ID_Peer 267 * @id_peer_len: Length of ID_Peer 268 * @id_server: ID_Server 269 * @id_server_len: Length of ID_Server 270 * @msk: Buffer for 64-byte MSK 271 * @emsk: Buffer for 64-byte EMSK 272 * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) 273 * @sk_len: Buffer for returning length of SK 274 * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes) 275 * @pk_len: Buffer for returning length of PK 276 * Returns: 0 on success, -1 on failure 277 */ 278int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, 279 int specifier, 280 const u8 *rand_peer, const u8 *rand_server, 281 const u8 *id_peer, size_t id_peer_len, 282 const u8 *id_server, size_t id_server_len, 283 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 284 u8 *pk, size_t *pk_len) 285{ 286 u8 *seed, *pos; 287 size_t seed_len; 288 int ret; 289 290 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", 291 vendor, specifier); 292 293 if (vendor != EAP_GPSK_VENDOR_IETF) 294 return -1; 295 296 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 297 298 /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ 299 seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; 300 seed = os_malloc(seed_len); 301 if (seed == NULL) { 302 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 303 "for key derivation"); 304 return -1; 305 } 306 307 pos = seed; 308 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 309 pos += EAP_GPSK_RAND_LEN; 310 os_memcpy(pos, id_peer, id_peer_len); 311 pos += id_peer_len; 312 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 313 pos += EAP_GPSK_RAND_LEN; 314 os_memcpy(pos, id_server, id_server_len); 315 pos += id_server_len; 316 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); 317 318 switch (specifier) { 319 case EAP_GPSK_CIPHER_AES: 320 ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, 321 msk, emsk, sk, sk_len, 322 pk, pk_len); 323 break; 324#ifdef EAP_GPSK_SHA256 325 case EAP_GPSK_CIPHER_SHA256: 326 ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, 327 msk, emsk, sk, sk_len); 328 break; 329#endif /* EAP_GPSK_SHA256 */ 330 default: 331 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 332 "key derivation", vendor, specifier); 333 ret = -1; 334 break; 335 } 336 337 os_free(seed); 338 339 return ret; 340} 341 342 343/** 344 * eap_gpsk_mic_len - Get the length of the MIC 345 * @vendor: CSuite/Vendor 346 * @specifier: CSuite/Specifier 347 * Returns: MIC length in bytes 348 */ 349size_t eap_gpsk_mic_len(int vendor, int specifier) 350{ 351 if (vendor != EAP_GPSK_VENDOR_IETF) 352 return 0; 353 354 switch (specifier) { 355 case EAP_GPSK_CIPHER_AES: 356 return 16; 357#ifdef EAP_GPSK_SHA256 358 case EAP_GPSK_CIPHER_SHA256: 359 return 32; 360#endif /* EAP_GPSK_SHA256 */ 361 default: 362 return 0; 363 } 364} 365 366 367static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, 368 const u8 *data, size_t len, u8 *mic) 369{ 370 if (sk_len != 16) { 371 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " 372 "AES-CMAC MIC", (unsigned long) sk_len); 373 return -1; 374 } 375 376 return omac1_aes_128(sk, data, len, mic); 377} 378 379 380/** 381 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet 382 * @sk: Session key SK from eap_gpsk_derive_keys() 383 * @sk_len: SK length in bytes from eap_gpsk_derive_keys() 384 * @vendor: CSuite/Vendor 385 * @specifier: CSuite/Specifier 386 * @data: Input data to MIC 387 * @len: Input data length in bytes 388 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes 389 * Returns: 0 on success, -1 on failure 390 */ 391int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, 392 int specifier, const u8 *data, size_t len, u8 *mic) 393{ 394 int ret; 395 396 if (vendor != EAP_GPSK_VENDOR_IETF) 397 return -1; 398 399 switch (specifier) { 400 case EAP_GPSK_CIPHER_AES: 401 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); 402 break; 403#ifdef EAP_GPSK_SHA256 404 case EAP_GPSK_CIPHER_SHA256: 405 hmac_sha256(sk, sk_len, data, len, mic); 406 ret = 0; 407 break; 408#endif /* EAP_GPSK_SHA256 */ 409 default: 410 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 411 "MIC computation", vendor, specifier); 412 ret = -1; 413 break; 414 } 415 416 return ret; 417} 418