1/* 2 * EAP server/peer: EAP-GPSK shared routines 3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10 11#include "common.h" 12#include "crypto/aes_wrap.h" 13#include "crypto/sha256.h" 14#include "eap_defs.h" 15#include "eap_gpsk_common.h" 16 17 18/** 19 * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported 20 * @vendor: CSuite/Vendor 21 * @specifier: CSuite/Specifier 22 * Returns: 1 if ciphersuite is support, or 0 if not 23 */ 24int eap_gpsk_supported_ciphersuite(int vendor, int specifier) 25{ 26 if (vendor == EAP_GPSK_VENDOR_IETF && 27 specifier == EAP_GPSK_CIPHER_AES) 28 return 1; 29#ifdef EAP_GPSK_SHA256 30 if (vendor == EAP_GPSK_VENDOR_IETF && 31 specifier == EAP_GPSK_CIPHER_SHA256) 32 return 1; 33#endif /* EAP_GPSK_SHA256 */ 34 return 0; 35} 36 37 38static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, 39 const u8 *data /* Z */, size_t data_len, 40 u8 *buf, size_t len /* X */) 41{ 42 u8 *opos; 43 size_t i, n, hashlen, left, clen; 44 u8 ibuf[2], hash[16]; 45 const u8 *addr[2]; 46 size_t vlen[2]; 47 48 hashlen = sizeof(hash); 49 /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */ 50 addr[0] = ibuf; 51 vlen[0] = sizeof(ibuf); 52 addr[1] = data; 53 vlen[1] = data_len; 54 55 opos = buf; 56 left = len; 57 n = (len + hashlen - 1) / hashlen; 58 for (i = 1; i <= n; i++) { 59 WPA_PUT_BE16(ibuf, i); 60 if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) 61 return -1; 62 clen = left > hashlen ? hashlen : left; 63 os_memcpy(opos, hash, clen); 64 opos += clen; 65 left -= clen; 66 } 67 68 return 0; 69} 70 71 72#ifdef EAP_GPSK_SHA256 73static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, 74 const u8 *data /* Z */, size_t data_len, 75 u8 *buf, size_t len /* X */) 76{ 77 u8 *opos; 78 size_t i, n, hashlen, left, clen; 79 u8 ibuf[2], hash[SHA256_MAC_LEN]; 80 const u8 *addr[2]; 81 size_t vlen[2]; 82 83 hashlen = SHA256_MAC_LEN; 84 /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */ 85 addr[0] = ibuf; 86 vlen[0] = sizeof(ibuf); 87 addr[1] = data; 88 vlen[1] = data_len; 89 90 opos = buf; 91 left = len; 92 n = (len + hashlen - 1) / hashlen; 93 for (i = 1; i <= n; i++) { 94 WPA_PUT_BE16(ibuf, i); 95 if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash)) 96 return -1; 97 clen = left > hashlen ? hashlen : left; 98 os_memcpy(opos, hash, clen); 99 opos += clen; 100 left -= clen; 101 } 102 103 return 0; 104} 105#endif /* EAP_GPSK_SHA256 */ 106 107 108static int eap_gpsk_derive_keys_helper(u32 csuite_specifier, 109 u8 *kdf_out, size_t kdf_out_len, 110 const u8 *psk, size_t psk_len, 111 const u8 *seed, size_t seed_len, 112 u8 *msk, u8 *emsk, 113 u8 *sk, size_t sk_len, 114 u8 *pk, size_t pk_len) 115{ 116 u8 mk[32], *pos, *data; 117 size_t data_len, mk_len; 118 int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 119 u8 *buf, size_t len); 120 121 gkdf = NULL; 122 switch (csuite_specifier) { 123 case EAP_GPSK_CIPHER_AES: 124 gkdf = eap_gpsk_gkdf_cmac; 125 mk_len = 16; 126 break; 127#ifdef EAP_GPSK_SHA256 128 case EAP_GPSK_CIPHER_SHA256: 129 gkdf = eap_gpsk_gkdf_sha256; 130 mk_len = SHA256_MAC_LEN; 131 break; 132#endif /* EAP_GPSK_SHA256 */ 133 default: 134 return -1; 135 } 136 137 if (psk_len < mk_len) 138 return -1; 139 140 data_len = 2 + psk_len + 6 + seed_len; 141 data = os_malloc(data_len); 142 if (data == NULL) 143 return -1; 144 pos = data; 145 WPA_PUT_BE16(pos, psk_len); 146 pos += 2; 147 os_memcpy(pos, psk, psk_len); 148 pos += psk_len; 149 WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ 150 pos += 4; 151 WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ 152 pos += 2; 153 os_memcpy(pos, seed, seed_len); /* inputString */ 154 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation", 155 data, data_len); 156 157 if (gkdf(psk, data, data_len, mk, mk_len) < 0) { 158 os_free(data); 159 return -1; 160 } 161 os_free(data); 162 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len); 163 164 if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0) 165 return -1; 166 167 pos = kdf_out; 168 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); 169 os_memcpy(msk, pos, EAP_MSK_LEN); 170 pos += EAP_MSK_LEN; 171 172 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); 173 os_memcpy(emsk, pos, EAP_EMSK_LEN); 174 pos += EAP_EMSK_LEN; 175 176 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len); 177 os_memcpy(sk, pos, sk_len); 178 pos += sk_len; 179 180 if (pk) { 181 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len); 182 os_memcpy(pk, pos, pk_len); 183 } 184 185 return 0; 186} 187 188 189static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, 190 const u8 *seed, size_t seed_len, 191 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 192 u8 *pk, size_t *pk_len) 193{ 194#define EAP_GPSK_SK_LEN_AES 16 195#define EAP_GPSK_PK_LEN_AES 16 196 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + 197 EAP_GPSK_PK_LEN_AES]; 198 199 /* 200 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 201 * (= seed) 202 * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001 203 * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString) 204 * MSK = GKDF-160 (MK, inputString)[0..63] 205 * EMSK = GKDF-160 (MK, inputString)[64..127] 206 * SK = GKDF-160 (MK, inputString)[128..143] 207 * PK = GKDF-160 (MK, inputString)[144..159] 208 * zero = 0x00 || 0x00 || ... || 0x00 (16 times) 209 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 210 * CSuite_Sel || inputString) 211 */ 212 213 *sk_len = EAP_GPSK_SK_LEN_AES; 214 *pk_len = EAP_GPSK_PK_LEN_AES; 215 216 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES, 217 kdf_out, sizeof(kdf_out), 218 psk, psk_len, seed, seed_len, 219 msk, emsk, sk, *sk_len, 220 pk, *pk_len); 221} 222 223 224#ifdef EAP_GPSK_SHA256 225static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, 226 const u8 *seed, size_t seed_len, 227 u8 *msk, u8 *emsk, 228 u8 *sk, size_t *sk_len) 229{ 230#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN 231#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN 232 u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + 233 EAP_GPSK_PK_LEN_SHA256]; 234 235 /* 236 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 237 * (= seed) 238 * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002 239 * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString) 240 * MSK = GKDF-160 (MK, inputString)[0..63] 241 * EMSK = GKDF-160 (MK, inputString)[64..127] 242 * SK = GKDF-160 (MK, inputString)[128..159] 243 * zero = 0x00 || 0x00 || ... || 0x00 (32 times) 244 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 245 * CSuite_Sel || inputString) 246 */ 247 248 *sk_len = EAP_GPSK_SK_LEN_SHA256; 249 250 return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256, 251 kdf_out, sizeof(kdf_out), 252 psk, psk_len, seed, seed_len, 253 msk, emsk, sk, *sk_len, 254 NULL, 0); 255} 256#endif /* EAP_GPSK_SHA256 */ 257 258 259/** 260 * eap_gpsk_derive_keys - Derive EAP-GPSK keys 261 * @psk: Pre-shared key 262 * @psk_len: Length of psk in bytes 263 * @vendor: CSuite/Vendor 264 * @specifier: CSuite/Specifier 265 * @rand_peer: 32-byte RAND_Peer 266 * @rand_server: 32-byte RAND_Server 267 * @id_peer: ID_Peer 268 * @id_peer_len: Length of ID_Peer 269 * @id_server: ID_Server 270 * @id_server_len: Length of ID_Server 271 * @msk: Buffer for 64-byte MSK 272 * @emsk: Buffer for 64-byte EMSK 273 * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) 274 * @sk_len: Buffer for returning length of SK 275 * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes) 276 * @pk_len: Buffer for returning length of PK 277 * Returns: 0 on success, -1 on failure 278 */ 279int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, 280 int specifier, 281 const u8 *rand_peer, const u8 *rand_server, 282 const u8 *id_peer, size_t id_peer_len, 283 const u8 *id_server, size_t id_server_len, 284 u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, 285 u8 *pk, size_t *pk_len) 286{ 287 u8 *seed, *pos; 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 = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); 300 if (seed == NULL) { 301 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 302 "for key derivation"); 303 return -1; 304 } 305 306 pos = seed; 307 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 308 pos += EAP_GPSK_RAND_LEN; 309 os_memcpy(pos, id_peer, id_peer_len); 310 pos += id_peer_len; 311 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 312 pos += EAP_GPSK_RAND_LEN; 313 os_memcpy(pos, id_server, id_server_len); 314 pos += id_server_len; 315 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); 316 317 switch (specifier) { 318 case EAP_GPSK_CIPHER_AES: 319 ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed, 320 msk, emsk, sk, sk_len, 321 pk, pk_len); 322 break; 323#ifdef EAP_GPSK_SHA256 324 case EAP_GPSK_CIPHER_SHA256: 325 ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, 326 pos - seed, 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 343static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, 344 u8 *kdf_out, size_t kdf_out_len, 345 const u8 *psk, const u8 *seed, 346 size_t seed_len, u8 method_type) 347{ 348 u8 *pos, *data; 349 size_t data_len; 350 int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 351 u8 *buf, size_t len); 352 353 gkdf = NULL; 354 switch (csuite_specifier) { 355 case EAP_GPSK_CIPHER_AES: 356 gkdf = eap_gpsk_gkdf_cmac; 357 break; 358#ifdef EAP_GPSK_SHA256 359 case EAP_GPSK_CIPHER_SHA256: 360 gkdf = eap_gpsk_gkdf_sha256; 361 break; 362#endif /* EAP_GPSK_SHA256 */ 363 default: 364 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " 365 "Session-Id derivation", csuite_specifier); 366 return -1; 367 } 368 369#define SID_LABEL "Method ID" 370 /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ 371 data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; 372 data = os_malloc(data_len); 373 if (data == NULL) 374 return -1; 375 pos = data; 376 os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); 377 pos += strlen(SID_LABEL); 378#undef SID_LABEL 379 os_memcpy(pos, &method_type, 1); 380 pos += 1; 381 WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ 382 pos += 4; 383 WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ 384 pos += 2; 385 os_memcpy(pos, seed, seed_len); /* inputString */ 386 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", 387 data, data_len); 388 389 if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { 390 os_free(data); 391 return -1; 392 } 393 os_free(data); 394 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); 395 396 return 0; 397} 398 399 400/** 401 * eap_gpsk_session_id - Derive EAP-GPSK Session ID 402 * @psk: Pre-shared key 403 * @psk_len: Length of psk in bytes 404 * @vendor: CSuite/Vendor 405 * @specifier: CSuite/Specifier 406 * @rand_peer: 32-byte RAND_Peer 407 * @rand_server: 32-byte RAND_Server 408 * @id_peer: ID_Peer 409 * @id_peer_len: Length of ID_Peer 410 * @id_server: ID_Server 411 * @id_server_len: Length of ID_Server 412 * @method_type: EAP Authentication Method Type 413 * @sid: Buffer for 17-byte Session ID 414 * @sid_len: Buffer for returning length of Session ID 415 * Returns: 0 on success, -1 on failure 416 */ 417int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, 418 int specifier, 419 const u8 *rand_peer, const u8 *rand_server, 420 const u8 *id_peer, size_t id_peer_len, 421 const u8 *id_server, size_t id_server_len, 422 u8 method_type, u8 *sid, size_t *sid_len) 423{ 424 u8 *seed, *pos; 425 u8 kdf_out[16]; 426 int ret; 427 428 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", 429 vendor, specifier); 430 431 if (vendor != EAP_GPSK_VENDOR_IETF) 432 return -1; 433 434 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 435 436 /* 437 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 438 * (= seed) 439 * KS = 16, CSuite_Sel = 0x00000000 0x0001 440 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 441 * CSuite_Sel || inputString) 442 */ 443 seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); 444 if (seed == NULL) { 445 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 446 "for Session-Id derivation"); 447 return -1; 448 } 449 450 pos = seed; 451 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 452 pos += EAP_GPSK_RAND_LEN; 453 os_memcpy(pos, id_peer, id_peer_len); 454 pos += id_peer_len; 455 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 456 pos += EAP_GPSK_RAND_LEN; 457 os_memcpy(pos, id_server, id_server_len); 458 pos += id_server_len; 459 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); 460 461 ret = eap_gpsk_derive_mid_helper(specifier, 462 kdf_out, sizeof(kdf_out), 463 psk, seed, pos - seed, 464 method_type); 465 466 sid[0] = method_type; 467 os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); 468 *sid_len = 1 + sizeof(kdf_out); 469 470 os_free(seed); 471 472 return ret; 473} 474 475 476/** 477 * eap_gpsk_mic_len - Get the length of the MIC 478 * @vendor: CSuite/Vendor 479 * @specifier: CSuite/Specifier 480 * Returns: MIC length in bytes 481 */ 482size_t eap_gpsk_mic_len(int vendor, int specifier) 483{ 484 if (vendor != EAP_GPSK_VENDOR_IETF) 485 return 0; 486 487 switch (specifier) { 488 case EAP_GPSK_CIPHER_AES: 489 return 16; 490#ifdef EAP_GPSK_SHA256 491 case EAP_GPSK_CIPHER_SHA256: 492 return 32; 493#endif /* EAP_GPSK_SHA256 */ 494 default: 495 return 0; 496 } 497} 498 499 500static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, 501 const u8 *data, size_t len, u8 *mic) 502{ 503 if (sk_len != 16) { 504 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " 505 "AES-CMAC MIC", (unsigned long) sk_len); 506 return -1; 507 } 508 509 return omac1_aes_128(sk, data, len, mic); 510} 511 512 513/** 514 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet 515 * @sk: Session key SK from eap_gpsk_derive_keys() 516 * @sk_len: SK length in bytes from eap_gpsk_derive_keys() 517 * @vendor: CSuite/Vendor 518 * @specifier: CSuite/Specifier 519 * @data: Input data to MIC 520 * @len: Input data length in bytes 521 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes 522 * Returns: 0 on success, -1 on failure 523 */ 524int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, 525 int specifier, const u8 *data, size_t len, u8 *mic) 526{ 527 int ret; 528 529 if (vendor != EAP_GPSK_VENDOR_IETF) 530 return -1; 531 532 switch (specifier) { 533 case EAP_GPSK_CIPHER_AES: 534 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); 535 break; 536#ifdef EAP_GPSK_SHA256 537 case EAP_GPSK_CIPHER_SHA256: 538 ret = hmac_sha256(sk, sk_len, data, len, mic); 539 break; 540#endif /* EAP_GPSK_SHA256 */ 541 default: 542 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 543 "MIC computation", vendor, specifier); 544 ret = -1; 545 break; 546 } 547 548 if (ret) 549 wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC"); 550 551 return ret; 552} 553