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 */ 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 int ret; 288 289 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", 290 vendor, specifier); 291 292 if (vendor != EAP_GPSK_VENDOR_IETF) 293 return -1; 294 295 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 296 297 /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ 298 seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); 299 if (seed == NULL) { 300 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 301 "for key derivation"); 302 return -1; 303 } 304 305 pos = seed; 306 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 307 pos += EAP_GPSK_RAND_LEN; 308 os_memcpy(pos, id_peer, id_peer_len); 309 pos += id_peer_len; 310 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 311 pos += EAP_GPSK_RAND_LEN; 312 os_memcpy(pos, id_server, id_server_len); 313 pos += id_server_len; 314 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); 315 316 switch (specifier) { 317 case EAP_GPSK_CIPHER_AES: 318 ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed, 319 msk, emsk, sk, sk_len, 320 pk, pk_len); 321 break; 322 #ifdef EAP_GPSK_SHA256 323 case EAP_GPSK_CIPHER_SHA256: 324 ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, 325 pos - seed, 326 msk, emsk, sk, sk_len); 327 break; 328 #endif /* EAP_GPSK_SHA256 */ 329 default: 330 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " 331 "key derivation", vendor, specifier); 332 ret = -1; 333 break; 334 } 335 336 os_free(seed); 337 338 return ret; 339 } 340 341 342 static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, 343 u8 *kdf_out, size_t kdf_out_len, 344 const u8 *psk, const u8 *seed, 345 size_t seed_len, u8 method_type) 346 { 347 u8 *pos, *data; 348 size_t data_len; 349 int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, 350 u8 *buf, size_t len); 351 352 gkdf = NULL; 353 switch (csuite_specifier) { 354 case EAP_GPSK_CIPHER_AES: 355 gkdf = eap_gpsk_gkdf_cmac; 356 break; 357 #ifdef EAP_GPSK_SHA256 358 case EAP_GPSK_CIPHER_SHA256: 359 gkdf = eap_gpsk_gkdf_sha256; 360 break; 361 #endif /* EAP_GPSK_SHA256 */ 362 default: 363 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " 364 "Session-Id derivation", csuite_specifier); 365 return -1; 366 } 367 368 #define SID_LABEL "Method ID" 369 /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ 370 data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; 371 data = os_malloc(data_len); 372 if (data == NULL) 373 return -1; 374 pos = data; 375 os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); 376 pos += strlen(SID_LABEL); 377 #undef SID_LABEL 378 os_memcpy(pos, &method_type, 1); 379 pos += 1; 380 WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ 381 pos += 4; 382 WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ 383 pos += 2; 384 os_memcpy(pos, seed, seed_len); /* inputString */ 385 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", 386 data, data_len); 387 388 if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { 389 os_free(data); 390 return -1; 391 } 392 os_free(data); 393 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); 394 395 return 0; 396 } 397 398 399 /** 400 * eap_gpsk_session_id - Derive EAP-GPSK Session ID 401 * @psk: Pre-shared key 402 * @psk_len: Length of psk in bytes 403 * @vendor: CSuite/Vendor 404 * @specifier: CSuite/Specifier 405 * @rand_peer: 32-byte RAND_Peer 406 * @rand_server: 32-byte RAND_Server 407 * @id_peer: ID_Peer 408 * @id_peer_len: Length of ID_Peer 409 * @id_server: ID_Server 410 * @id_server_len: Length of ID_Server 411 * @method_type: EAP Authentication Method Type 412 * @sid: Buffer for 17-byte Session ID 413 * @sid_len: Buffer for returning length of Session ID 414 * Returns: 0 on success, -1 on failure 415 */ 416 int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, 417 int specifier, 418 const u8 *rand_peer, const u8 *rand_server, 419 const u8 *id_peer, size_t id_peer_len, 420 const u8 *id_server, size_t id_server_len, 421 u8 method_type, u8 *sid, size_t *sid_len) 422 { 423 u8 *seed, *pos; 424 u8 kdf_out[16]; 425 int ret; 426 427 wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", 428 vendor, specifier); 429 430 if (vendor != EAP_GPSK_VENDOR_IETF) 431 return -1; 432 433 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); 434 435 /* 436 * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server 437 * (= seed) 438 * KS = 16, CSuite_Sel = 0x00000000 0x0001 439 * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || 440 * CSuite_Sel || inputString) 441 */ 442 seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); 443 if (seed == NULL) { 444 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " 445 "for Session-Id derivation"); 446 return -1; 447 } 448 449 pos = seed; 450 os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); 451 pos += EAP_GPSK_RAND_LEN; 452 os_memcpy(pos, id_peer, id_peer_len); 453 pos += id_peer_len; 454 os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); 455 pos += EAP_GPSK_RAND_LEN; 456 os_memcpy(pos, id_server, id_server_len); 457 pos += id_server_len; 458 wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); 459 460 ret = eap_gpsk_derive_mid_helper(specifier, 461 kdf_out, sizeof(kdf_out), 462 psk, seed, pos - seed, 463 method_type); 464 465 sid[0] = method_type; 466 os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); 467 *sid_len = 1 + sizeof(kdf_out); 468 469 os_free(seed); 470 471 return ret; 472 } 473 474 475 /** 476 * eap_gpsk_mic_len - Get the length of the MIC 477 * @vendor: CSuite/Vendor 478 * @specifier: CSuite/Specifier 479 * Returns: MIC length in bytes 480 */ 481 size_t eap_gpsk_mic_len(int vendor, int specifier) 482 { 483 if (vendor != EAP_GPSK_VENDOR_IETF) 484 return 0; 485 486 switch (specifier) { 487 case EAP_GPSK_CIPHER_AES: 488 return 16; 489 #ifdef EAP_GPSK_SHA256 490 case EAP_GPSK_CIPHER_SHA256: 491 return 32; 492 #endif /* EAP_GPSK_SHA256 */ 493 default: 494 return 0; 495 } 496 } 497 498 499 static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, 500 const u8 *data, size_t len, u8 *mic) 501 { 502 if (sk_len != 16) { 503 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " 504 "AES-CMAC MIC", (unsigned long) sk_len); 505 return -1; 506 } 507 508 return omac1_aes_128(sk, data, len, mic); 509 } 510 511 512 /** 513 * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet 514 * @sk: Session key SK from eap_gpsk_derive_keys() 515 * @sk_len: SK length in bytes from eap_gpsk_derive_keys() 516 * @vendor: CSuite/Vendor 517 * @specifier: CSuite/Specifier 518 * @data: Input data to MIC 519 * @len: Input data length in bytes 520 * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes 521 * Returns: 0 on success, -1 on failure 522 */ 523 int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, 524 int specifier, const u8 *data, size_t len, u8 *mic) 525 { 526 int ret; 527 528 if (vendor != EAP_GPSK_VENDOR_IETF) 529 return -1; 530 531 switch (specifier) { 532 case EAP_GPSK_CIPHER_AES: 533 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); 534 break; 535 #ifdef EAP_GPSK_SHA256 536 case EAP_GPSK_CIPHER_SHA256: 537 hmac_sha256(sk, sk_len, data, len, mic); 538 ret = 0; 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 return ret; 549 } 550