1 /* 2 * EAP server/peer: EAP-GPSK shared routines 3 * Copyright (c) 2006-2007, Jouni Malinen <j (at) 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 */ 24 int 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 38 static 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 73 static 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 hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); 96 clen = left > hashlen ? hashlen : left; 97 os_memcpy(opos, hash, clen); 98 opos += clen; 99 left -= clen; 100 } 101 102 return 0; 103 } 104 #endif /* EAP_GPSK_SHA256 */ 105 106 107 static int eap_gpsk_derive_keys_helper(u32 csuite_specifier, 108 u8 *kdf_out, size_t kdf_out_len, 109 const u8 *psk, size_t psk_len, 110 const u8 *seed, size_t seed_len, 111 u8 *msk, u8 *emsk, 112 u8 *sk, size_t sk_len, 113 u8 *pk, size_t pk_len) 114 { 115 u8 mk[32], *pos, *data; 116 size_t data_len, mk_len; 117 int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 118 u8 *buf, size_t len); 119 120 gkdf = NULL; 121 switch (csuite_specifier) { 122 case EAP_GPSK_CIPHER_AES: 123 gkdf = eap_gpsk_gkdf_cmac; 124 mk_len = 16; 125 break; 126 #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 188 static 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 224 static 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 */ 278 int 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 static 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 */ 417 int 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 size_t seed_len; 427 int ret; 428 429 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", 430 vendor, specifier); 431 432 if (vendor != EAP_GPSK_VENDOR_IETF) 433 return -1; 434 435 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 436 437 /* 438 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 439 * (= seed) 440 * KS = 16, CSuite_Sel = 0x00000000 0x0001 441 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 442 * CSuite_Sel || inputString) 443 */ 444 seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; 445 seed = os_malloc(seed_len); 446 if (seed == NULL) { 447 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 448 "for Session-Id derivation"); 449 return -1; 450 } 451 452 pos = seed; 453 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 454 pos += EAP_GPSK_RAND_LEN; 455 os_memcpy(pos, id_peer, id_peer_len); 456 pos += id_peer_len; 457 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 458 pos += EAP_GPSK_RAND_LEN; 459 os_memcpy(pos, id_server, id_server_len); 460 pos += id_server_len; 461 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); 462 463 ret = eap_gpsk_derive_mid_helper(specifier, 464 kdf_out, sizeof(kdf_out), 465 psk, seed, seed_len, 466 method_type); 467 468 sid[0] = method_type; 469 os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); 470 *sid_len = 1 + sizeof(kdf_out); 471 472 os_free(seed); 473 474 return ret; 475 } 476 477 478 /** 479 * eap_gpsk_mic_len - Get the length of the MIC 480 * @vendor: CSuite/Vendor 481 * @specifier: CSuite/Specifier 482 * Returns: MIC length in bytes 483 */ 484 size_t eap_gpsk_mic_len(int vendor, int specifier) 485 { 486 if (vendor != EAP_GPSK_VENDOR_IETF) 487 return 0; 488 489 switch (specifier) { 490 case EAP_GPSK_CIPHER_AES: 491 return 16; 492 #ifdef EAP_GPSK_SHA256 493 case EAP_GPSK_CIPHER_SHA256: 494 return 32; 495 #endif /* EAP_GPSK_SHA256 */ 496 default: 497 return 0; 498 } 499 } 500 501 502 static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, 503 const u8 *data, size_t len, u8 *mic) 504 { 505 if (sk_len != 16) { 506 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " 507 "AES-CMAC MIC", (unsigned long) sk_len); 508 return -1; 509 } 510 511 return omac1_aes_128(sk, data, len, mic); 512 } 513 514 515 /** 516 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet 517 * @sk: Session key SK from eap_gpsk_derive_keys() 518 * @sk_len: SK length in bytes from eap_gpsk_derive_keys() 519 * @vendor: CSuite/Vendor 520 * @specifier: CSuite/Specifier 521 * @data: Input data to MIC 522 * @len: Input data length in bytes 523 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes 524 * Returns: 0 on success, -1 on failure 525 */ 526 int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, 527 int specifier, const u8 *data, size_t len, u8 *mic) 528 { 529 int ret; 530 531 if (vendor != EAP_GPSK_VENDOR_IETF) 532 return -1; 533 534 switch (specifier) { 535 case EAP_GPSK_CIPHER_AES: 536 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); 537 break; 538 #ifdef EAP_GPSK_SHA256 539 case EAP_GPSK_CIPHER_SHA256: 540 hmac_sha256(sk, sk_len, data, len, mic); 541 ret = 0; 542 break; 543 #endif /* EAP_GPSK_SHA256 */ 544 default: 545 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 546 "MIC computation", vendor, specifier); 547 ret = -1; 548 break; 549 } 550 551 return ret; 552 } 553