1 /* 2 * EAP peer method: EAP-pwd (RFC 5931) 3 * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> 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/sha256.h" 13 #include "crypto/ms_funcs.h" 14 #include "eap_peer/eap_i.h" 15 #include "eap_common/eap_pwd_common.h" 16 17 18 struct eap_pwd_data { 19 enum { 20 PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, 21 SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE 22 } state; 23 u8 *id_peer; 24 size_t id_peer_len; 25 u8 *id_server; 26 size_t id_server_len; 27 u8 *password; 28 size_t password_len; 29 int password_hash; 30 u16 group_num; 31 EAP_PWD_group *grp; 32 33 struct wpabuf *inbuf; 34 size_t in_frag_pos; 35 struct wpabuf *outbuf; 36 size_t out_frag_pos; 37 size_t mtu; 38 39 BIGNUM *k; 40 BIGNUM *private_value; 41 BIGNUM *server_scalar; 42 BIGNUM *my_scalar; 43 EC_POINT *my_element; 44 EC_POINT *server_element; 45 46 u8 msk[EAP_MSK_LEN]; 47 u8 emsk[EAP_EMSK_LEN]; 48 u8 session_id[1 + SHA256_MAC_LEN]; 49 50 BN_CTX *bnctx; 51 }; 52 53 54 #ifndef CONFIG_NO_STDOUT_DEBUG 55 static const char * eap_pwd_state_txt(int state) 56 { 57 switch (state) { 58 case PWD_ID_Req: 59 return "PWD-ID-Req"; 60 case PWD_Commit_Req: 61 return "PWD-Commit-Req"; 62 case PWD_Confirm_Req: 63 return "PWD-Confirm-Req"; 64 case SUCCESS_ON_FRAG_COMPLETION: 65 return "SUCCESS_ON_FRAG_COMPLETION"; 66 case SUCCESS: 67 return "SUCCESS"; 68 case FAILURE: 69 return "FAILURE"; 70 default: 71 return "PWD-UNK"; 72 } 73 } 74 #endif /* CONFIG_NO_STDOUT_DEBUG */ 75 76 77 static void eap_pwd_state(struct eap_pwd_data *data, int state) 78 { 79 wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", 80 eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); 81 data->state = state; 82 } 83 84 85 static void * eap_pwd_init(struct eap_sm *sm) 86 { 87 struct eap_pwd_data *data; 88 const u8 *identity, *password; 89 size_t identity_len, password_len; 90 int fragment_size; 91 int pwhash; 92 93 password = eap_get_config_password2(sm, &password_len, &pwhash); 94 if (password == NULL) { 95 wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); 96 return NULL; 97 } 98 99 identity = eap_get_config_identity(sm, &identity_len); 100 if (identity == NULL) { 101 wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); 102 return NULL; 103 } 104 105 if ((data = os_zalloc(sizeof(*data))) == NULL) { 106 wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); 107 return NULL; 108 } 109 110 if ((data->bnctx = BN_CTX_new()) == NULL) { 111 wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); 112 os_free(data); 113 return NULL; 114 } 115 116 if ((data->id_peer = os_malloc(identity_len)) == NULL) { 117 wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); 118 BN_CTX_free(data->bnctx); 119 os_free(data); 120 return NULL; 121 } 122 123 os_memcpy(data->id_peer, identity, identity_len); 124 data->id_peer_len = identity_len; 125 126 if ((data->password = os_malloc(password_len)) == NULL) { 127 wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); 128 BN_CTX_free(data->bnctx); 129 bin_clear_free(data->id_peer, data->id_peer_len); 130 os_free(data); 131 return NULL; 132 } 133 os_memcpy(data->password, password, password_len); 134 data->password_len = password_len; 135 data->password_hash = pwhash; 136 137 data->out_frag_pos = data->in_frag_pos = 0; 138 data->inbuf = data->outbuf = NULL; 139 fragment_size = eap_get_config_fragment_size(sm); 140 if (fragment_size <= 0) 141 data->mtu = 1020; /* default from RFC 5931 */ 142 else 143 data->mtu = fragment_size; 144 145 data->state = PWD_ID_Req; 146 147 return data; 148 } 149 150 151 static void eap_pwd_deinit(struct eap_sm *sm, void *priv) 152 { 153 struct eap_pwd_data *data = priv; 154 155 BN_clear_free(data->private_value); 156 BN_clear_free(data->server_scalar); 157 BN_clear_free(data->my_scalar); 158 BN_clear_free(data->k); 159 BN_CTX_free(data->bnctx); 160 EC_POINT_clear_free(data->my_element); 161 EC_POINT_clear_free(data->server_element); 162 bin_clear_free(data->id_peer, data->id_peer_len); 163 bin_clear_free(data->id_server, data->id_server_len); 164 bin_clear_free(data->password, data->password_len); 165 if (data->grp) { 166 EC_GROUP_free(data->grp->group); 167 EC_POINT_clear_free(data->grp->pwe); 168 BN_clear_free(data->grp->order); 169 BN_clear_free(data->grp->prime); 170 os_free(data->grp); 171 } 172 wpabuf_free(data->inbuf); 173 wpabuf_free(data->outbuf); 174 bin_clear_free(data, sizeof(*data)); 175 } 176 177 178 static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) 179 { 180 struct eap_pwd_data *data = priv; 181 u8 *key; 182 183 if (data->state != SUCCESS) 184 return NULL; 185 186 key = os_malloc(EAP_MSK_LEN); 187 if (key == NULL) 188 return NULL; 189 190 os_memcpy(key, data->msk, EAP_MSK_LEN); 191 *len = EAP_MSK_LEN; 192 193 return key; 194 } 195 196 197 static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 198 { 199 struct eap_pwd_data *data = priv; 200 u8 *id; 201 202 if (data->state != SUCCESS) 203 return NULL; 204 205 id = os_malloc(1 + SHA256_MAC_LEN); 206 if (id == NULL) 207 return NULL; 208 209 os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); 210 *len = 1 + SHA256_MAC_LEN; 211 212 return id; 213 } 214 215 216 static void 217 eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 218 struct eap_method_ret *ret, 219 const struct wpabuf *reqData, 220 const u8 *payload, size_t payload_len) 221 { 222 struct eap_pwd_id *id; 223 const u8 *password; 224 size_t password_len; 225 u8 pwhashhash[16]; 226 int res; 227 228 if (data->state != PWD_ID_Req) { 229 ret->ignore = TRUE; 230 eap_pwd_state(data, FAILURE); 231 return; 232 } 233 234 if (payload_len < sizeof(struct eap_pwd_id)) { 235 ret->ignore = TRUE; 236 eap_pwd_state(data, FAILURE); 237 return; 238 } 239 240 id = (struct eap_pwd_id *) payload; 241 data->group_num = be_to_host16(id->group_num); 242 wpa_printf(MSG_DEBUG, 243 "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", 244 data->group_num, id->random_function, id->prf, id->prep); 245 if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || 246 (id->prf != EAP_PWD_DEFAULT_PRF)) { 247 ret->ignore = TRUE; 248 eap_pwd_state(data, FAILURE); 249 return; 250 } 251 252 if (id->prep != EAP_PWD_PREP_NONE && 253 id->prep != EAP_PWD_PREP_MS) { 254 wpa_printf(MSG_DEBUG, 255 "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", 256 id->prep); 257 eap_pwd_state(data, FAILURE); 258 return; 259 } 260 261 if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { 262 wpa_printf(MSG_DEBUG, 263 "EAP-PWD: Unhashed password not available"); 264 eap_pwd_state(data, FAILURE); 265 return; 266 } 267 268 wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", 269 data->group_num); 270 271 data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); 272 if (data->id_server == NULL) { 273 wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); 274 eap_pwd_state(data, FAILURE); 275 return; 276 } 277 data->id_server_len = payload_len - sizeof(struct eap_pwd_id); 278 os_memcpy(data->id_server, id->identity, data->id_server_len); 279 wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", 280 data->id_server, data->id_server_len); 281 282 data->grp = os_zalloc(sizeof(EAP_PWD_group)); 283 if (data->grp == NULL) { 284 wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " 285 "group"); 286 eap_pwd_state(data, FAILURE); 287 return; 288 } 289 290 if (id->prep == EAP_PWD_PREP_MS) { 291 #ifdef CONFIG_FIPS 292 wpa_printf(MSG_ERROR, 293 "EAP-PWD (peer): MS password hash not supported in FIPS mode"); 294 eap_pwd_state(data, FAILURE); 295 return; 296 #else /* CONFIG_FIPS */ 297 if (data->password_hash) { 298 res = hash_nt_password_hash(data->password, pwhashhash); 299 } else { 300 u8 pwhash[16]; 301 302 res = nt_password_hash(data->password, 303 data->password_len, pwhash); 304 if (res == 0) 305 res = hash_nt_password_hash(pwhash, pwhashhash); 306 os_memset(pwhash, 0, sizeof(pwhash)); 307 } 308 309 if (res) { 310 eap_pwd_state(data, FAILURE); 311 return; 312 } 313 314 password = pwhashhash; 315 password_len = sizeof(pwhashhash); 316 #endif /* CONFIG_FIPS */ 317 } else { 318 password = data->password; 319 password_len = data->password_len; 320 } 321 322 /* compute PWE */ 323 res = compute_password_element(data->grp, data->group_num, 324 password, password_len, 325 data->id_server, data->id_server_len, 326 data->id_peer, data->id_peer_len, 327 id->token); 328 os_memset(pwhashhash, 0, sizeof(pwhashhash)); 329 if (res) { 330 wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); 331 eap_pwd_state(data, FAILURE); 332 return; 333 } 334 335 wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", 336 BN_num_bits(data->grp->prime)); 337 338 data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + 339 data->id_peer_len); 340 if (data->outbuf == NULL) { 341 eap_pwd_state(data, FAILURE); 342 return; 343 } 344 wpabuf_put_be16(data->outbuf, data->group_num); 345 wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); 346 wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); 347 wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); 348 wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); 349 wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); 350 351 eap_pwd_state(data, PWD_Commit_Req); 352 } 353 354 355 static void 356 eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 357 struct eap_method_ret *ret, 358 const struct wpabuf *reqData, 359 const u8 *payload, size_t payload_len) 360 { 361 EC_POINT *K = NULL, *point = NULL; 362 BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; 363 u16 offset; 364 u8 *ptr, *scalar = NULL, *element = NULL; 365 size_t prime_len, order_len; 366 367 if (data->state != PWD_Commit_Req) { 368 ret->ignore = TRUE; 369 goto fin; 370 } 371 372 prime_len = BN_num_bytes(data->grp->prime); 373 order_len = BN_num_bytes(data->grp->order); 374 375 if (payload_len != 2 * prime_len + order_len) { 376 wpa_printf(MSG_INFO, 377 "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 378 (unsigned int) payload_len, 379 (unsigned int) (2 * prime_len + order_len)); 380 goto fin; 381 } 382 383 if (((data->private_value = BN_new()) == NULL) || 384 ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || 385 ((cofactor = BN_new()) == NULL) || 386 ((data->my_scalar = BN_new()) == NULL) || 387 ((mask = BN_new()) == NULL)) { 388 wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); 389 goto fin; 390 } 391 392 if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { 393 wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " 394 "for curve"); 395 goto fin; 396 } 397 398 if (BN_rand_range(data->private_value, data->grp->order) != 1 || 399 BN_rand_range(mask, data->grp->order) != 1 || 400 BN_add(data->my_scalar, data->private_value, mask) != 1 || 401 BN_mod(data->my_scalar, data->my_scalar, data->grp->order, 402 data->bnctx) != 1) { 403 wpa_printf(MSG_INFO, 404 "EAP-pwd (peer): unable to get randomness"); 405 goto fin; 406 } 407 408 if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, 409 data->grp->pwe, mask, data->bnctx)) { 410 wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " 411 "fail"); 412 eap_pwd_state(data, FAILURE); 413 goto fin; 414 } 415 416 if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) 417 { 418 wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); 419 goto fin; 420 } 421 BN_clear_free(mask); 422 423 if (((x = BN_new()) == NULL) || 424 ((y = BN_new()) == NULL)) { 425 wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); 426 goto fin; 427 } 428 429 /* process the request */ 430 if (((data->server_scalar = BN_new()) == NULL) || 431 ((data->k = BN_new()) == NULL) || 432 ((K = EC_POINT_new(data->grp->group)) == NULL) || 433 ((point = EC_POINT_new(data->grp->group)) == NULL) || 434 ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) 435 { 436 wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " 437 "fail"); 438 goto fin; 439 } 440 441 /* element, x then y, followed by scalar */ 442 ptr = (u8 *) payload; 443 BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); 444 ptr += BN_num_bytes(data->grp->prime); 445 BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); 446 ptr += BN_num_bytes(data->grp->prime); 447 BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); 448 if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, 449 data->server_element, x, y, 450 data->bnctx)) { 451 wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " 452 "fail"); 453 goto fin; 454 } 455 456 /* check to ensure server's element is not in a small sub-group */ 457 if (BN_cmp(cofactor, BN_value_one())) { 458 if (!EC_POINT_mul(data->grp->group, point, NULL, 459 data->server_element, cofactor, NULL)) { 460 wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 461 "server element by order!\n"); 462 goto fin; 463 } 464 if (EC_POINT_is_at_infinity(data->grp->group, point)) { 465 wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " 466 "is at infinity!\n"); 467 goto fin; 468 } 469 } 470 471 /* compute the shared key, k */ 472 if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, 473 data->server_scalar, data->bnctx)) || 474 (!EC_POINT_add(data->grp->group, K, K, data->server_element, 475 data->bnctx)) || 476 (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, 477 data->bnctx))) { 478 wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " 479 "fail"); 480 goto fin; 481 } 482 483 /* ensure that the shared key isn't in a small sub-group */ 484 if (BN_cmp(cofactor, BN_value_one())) { 485 if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, 486 NULL)) { 487 wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 488 "shared key point by order"); 489 goto fin; 490 } 491 } 492 493 /* 494 * This check is strictly speaking just for the case above where 495 * co-factor > 1 but it was suggested that even though this is probably 496 * never going to happen it is a simple and safe check "just to be 497 * sure" so let's be safe. 498 */ 499 if (EC_POINT_is_at_infinity(data->grp->group, K)) { 500 wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " 501 "infinity!\n"); 502 goto fin; 503 } 504 505 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, 506 NULL, data->bnctx)) { 507 wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " 508 "shared secret from point"); 509 goto fin; 510 } 511 512 /* now do the response */ 513 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 514 data->my_element, x, y, 515 data->bnctx)) { 516 wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); 517 goto fin; 518 } 519 520 if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || 521 ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == 522 NULL)) { 523 wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); 524 goto fin; 525 } 526 527 /* 528 * bignums occupy as little memory as possible so one that is 529 * sufficiently smaller than the prime or order might need pre-pending 530 * with zeros. 531 */ 532 os_memset(scalar, 0, BN_num_bytes(data->grp->order)); 533 os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); 534 offset = BN_num_bytes(data->grp->order) - 535 BN_num_bytes(data->my_scalar); 536 BN_bn2bin(data->my_scalar, scalar + offset); 537 538 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 539 BN_bn2bin(x, element + offset); 540 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 541 BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); 542 543 data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + 544 2 * BN_num_bytes(data->grp->prime)); 545 if (data->outbuf == NULL) 546 goto fin; 547 548 /* we send the element as (x,y) follwed by the scalar */ 549 wpabuf_put_data(data->outbuf, element, 550 2 * BN_num_bytes(data->grp->prime)); 551 wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); 552 553 fin: 554 os_free(scalar); 555 os_free(element); 556 BN_clear_free(x); 557 BN_clear_free(y); 558 BN_clear_free(cofactor); 559 EC_POINT_clear_free(K); 560 EC_POINT_clear_free(point); 561 if (data->outbuf == NULL) 562 eap_pwd_state(data, FAILURE); 563 else 564 eap_pwd_state(data, PWD_Confirm_Req); 565 } 566 567 568 static void 569 eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 570 struct eap_method_ret *ret, 571 const struct wpabuf *reqData, 572 const u8 *payload, size_t payload_len) 573 { 574 BIGNUM *x = NULL, *y = NULL; 575 struct crypto_hash *hash; 576 u32 cs; 577 u16 grp; 578 u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; 579 int offset; 580 581 if (data->state != PWD_Confirm_Req) { 582 ret->ignore = TRUE; 583 goto fin; 584 } 585 586 if (payload_len != SHA256_MAC_LEN) { 587 wpa_printf(MSG_INFO, 588 "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", 589 (unsigned int) payload_len, SHA256_MAC_LEN); 590 goto fin; 591 } 592 593 /* 594 * first build up the ciphersuite which is group | random_function | 595 * prf 596 */ 597 grp = htons(data->group_num); 598 ptr = (u8 *) &cs; 599 os_memcpy(ptr, &grp, sizeof(u16)); 600 ptr += sizeof(u16); 601 *ptr = EAP_PWD_DEFAULT_RAND_FUNC; 602 ptr += sizeof(u8); 603 *ptr = EAP_PWD_DEFAULT_PRF; 604 605 /* each component of the cruft will be at most as big as the prime */ 606 if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || 607 ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { 608 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " 609 "fail"); 610 goto fin; 611 } 612 613 /* 614 * server's commit is H(k | server_element | server_scalar | 615 * peer_element | peer_scalar | ciphersuite) 616 */ 617 hash = eap_pwd_h_init(); 618 if (hash == NULL) 619 goto fin; 620 621 /* 622 * zero the memory each time because this is mod prime math and some 623 * value may start with a few zeros and the previous one did not. 624 */ 625 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 626 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); 627 BN_bn2bin(data->k, cruft + offset); 628 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 629 630 /* server element: x, y */ 631 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 632 data->server_element, x, y, 633 data->bnctx)) { 634 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 635 "assignment fail"); 636 goto fin; 637 } 638 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 639 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 640 BN_bn2bin(x, cruft + offset); 641 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 642 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 643 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 644 BN_bn2bin(y, cruft + offset); 645 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 646 647 /* server scalar */ 648 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 649 offset = BN_num_bytes(data->grp->order) - 650 BN_num_bytes(data->server_scalar); 651 BN_bn2bin(data->server_scalar, cruft + offset); 652 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 653 654 /* my element: x, y */ 655 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 656 data->my_element, x, y, 657 data->bnctx)) { 658 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 659 "assignment fail"); 660 goto fin; 661 } 662 663 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 664 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 665 BN_bn2bin(x, cruft + offset); 666 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 667 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 668 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 669 BN_bn2bin(y, cruft + offset); 670 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 671 672 /* my scalar */ 673 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 674 offset = BN_num_bytes(data->grp->order) - 675 BN_num_bytes(data->my_scalar); 676 BN_bn2bin(data->my_scalar, cruft + offset); 677 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 678 679 /* the ciphersuite */ 680 eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 681 682 /* random function fin */ 683 eap_pwd_h_final(hash, conf); 684 685 ptr = (u8 *) payload; 686 if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { 687 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); 688 goto fin; 689 } 690 691 wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); 692 693 /* 694 * compute confirm: 695 * H(k | peer_element | peer_scalar | server_element | server_scalar | 696 * ciphersuite) 697 */ 698 hash = eap_pwd_h_init(); 699 if (hash == NULL) 700 goto fin; 701 702 /* k */ 703 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 704 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); 705 BN_bn2bin(data->k, cruft + offset); 706 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 707 708 /* my element */ 709 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 710 data->my_element, x, y, 711 data->bnctx)) { 712 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 713 "assignment fail"); 714 goto fin; 715 } 716 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 717 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 718 BN_bn2bin(x, cruft + offset); 719 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 720 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 721 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 722 BN_bn2bin(y, cruft + offset); 723 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 724 725 /* my scalar */ 726 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 727 offset = BN_num_bytes(data->grp->order) - 728 BN_num_bytes(data->my_scalar); 729 BN_bn2bin(data->my_scalar, cruft + offset); 730 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 731 732 /* server element: x, y */ 733 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 734 data->server_element, x, y, 735 data->bnctx)) { 736 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 737 "assignment fail"); 738 goto fin; 739 } 740 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 741 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 742 BN_bn2bin(x, cruft + offset); 743 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 744 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 745 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 746 BN_bn2bin(y, cruft + offset); 747 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 748 749 /* server scalar */ 750 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 751 offset = BN_num_bytes(data->grp->order) - 752 BN_num_bytes(data->server_scalar); 753 BN_bn2bin(data->server_scalar, cruft + offset); 754 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 755 756 /* the ciphersuite */ 757 eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 758 759 /* all done */ 760 eap_pwd_h_final(hash, conf); 761 762 if (compute_keys(data->grp, data->bnctx, data->k, 763 data->my_scalar, data->server_scalar, conf, ptr, 764 &cs, data->msk, data->emsk, data->session_id) < 0) { 765 wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " 766 "EMSK"); 767 goto fin; 768 } 769 770 data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); 771 if (data->outbuf == NULL) 772 goto fin; 773 774 wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); 775 776 fin: 777 bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); 778 BN_clear_free(x); 779 BN_clear_free(y); 780 if (data->outbuf == NULL) { 781 ret->methodState = METHOD_DONE; 782 ret->decision = DECISION_FAIL; 783 eap_pwd_state(data, FAILURE); 784 } else { 785 eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); 786 } 787 } 788 789 790 static struct wpabuf * 791 eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, 792 const struct wpabuf *reqData) 793 { 794 struct eap_pwd_data *data = priv; 795 struct wpabuf *resp = NULL; 796 const u8 *pos, *buf; 797 size_t len; 798 u16 tot_len = 0; 799 u8 lm_exch; 800 801 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); 802 if ((pos == NULL) || (len < 1)) { 803 wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " 804 "len is %d", 805 pos == NULL ? "NULL" : "not NULL", (int) len); 806 ret->ignore = TRUE; 807 return NULL; 808 } 809 810 ret->ignore = FALSE; 811 ret->methodState = METHOD_MAY_CONT; 812 ret->decision = DECISION_FAIL; 813 ret->allowNotifications = FALSE; 814 815 lm_exch = *pos; 816 pos++; /* skip over the bits and the exch */ 817 len--; 818 819 /* 820 * we're fragmenting so send out the next fragment 821 */ 822 if (data->out_frag_pos) { 823 /* 824 * this should be an ACK 825 */ 826 if (len) 827 wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " 828 "not an ACK"); 829 830 wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); 831 /* 832 * check if there are going to be more fragments 833 */ 834 len = wpabuf_len(data->outbuf) - data->out_frag_pos; 835 if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 836 len = data->mtu - EAP_PWD_HDR_SIZE; 837 EAP_PWD_SET_MORE_BIT(lm_exch); 838 } 839 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 840 EAP_PWD_HDR_SIZE + len, 841 EAP_CODE_RESPONSE, eap_get_id(reqData)); 842 if (resp == NULL) { 843 wpa_printf(MSG_INFO, "Unable to allocate memory for " 844 "next fragment!"); 845 return NULL; 846 } 847 wpabuf_put_u8(resp, lm_exch); 848 buf = wpabuf_head_u8(data->outbuf); 849 wpabuf_put_data(resp, buf + data->out_frag_pos, len); 850 data->out_frag_pos += len; 851 /* 852 * this is the last fragment so get rid of the out buffer 853 */ 854 if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { 855 wpabuf_free(data->outbuf); 856 data->outbuf = NULL; 857 data->out_frag_pos = 0; 858 } 859 wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", 860 data->out_frag_pos == 0 ? "last" : "next", 861 (int) len); 862 if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 863 ret->methodState = METHOD_DONE; 864 ret->decision = DECISION_UNCOND_SUCC; 865 eap_pwd_state(data, SUCCESS); 866 } 867 return resp; 868 } 869 870 /* 871 * see if this is a fragment that needs buffering 872 * 873 * if it's the first fragment there'll be a length field 874 */ 875 if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 876 if (len < 2) { 877 wpa_printf(MSG_DEBUG, 878 "EAP-pwd: Frame too short to contain Total-Length field"); 879 ret->ignore = TRUE; 880 return NULL; 881 } 882 tot_len = WPA_GET_BE16(pos); 883 wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " 884 "total length = %d", tot_len); 885 if (tot_len > 15000) 886 return NULL; 887 if (data->inbuf) { 888 wpa_printf(MSG_DEBUG, 889 "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); 890 ret->ignore = TRUE; 891 return NULL; 892 } 893 data->inbuf = wpabuf_alloc(tot_len); 894 if (data->inbuf == NULL) { 895 wpa_printf(MSG_INFO, "Out of memory to buffer " 896 "fragments!"); 897 return NULL; 898 } 899 data->in_frag_pos = 0; 900 pos += sizeof(u16); 901 len -= sizeof(u16); 902 } 903 /* 904 * buffer and ACK the fragment 905 */ 906 if (EAP_PWD_GET_MORE_BIT(lm_exch)) { 907 data->in_frag_pos += len; 908 if (data->in_frag_pos > wpabuf_size(data->inbuf)) { 909 wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " 910 "detected (%d vs. %d)!", 911 (int) data->in_frag_pos, 912 (int) wpabuf_len(data->inbuf)); 913 wpabuf_free(data->inbuf); 914 data->inbuf = NULL; 915 data->in_frag_pos = 0; 916 return NULL; 917 } 918 wpabuf_put_data(data->inbuf, pos, len); 919 920 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 921 EAP_PWD_HDR_SIZE, 922 EAP_CODE_RESPONSE, eap_get_id(reqData)); 923 if (resp != NULL) 924 wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); 925 wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", 926 (int) len); 927 return resp; 928 } 929 /* 930 * we're buffering and this is the last fragment 931 */ 932 if (data->in_frag_pos) { 933 wpabuf_put_data(data->inbuf, pos, len); 934 wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", 935 (int) len); 936 data->in_frag_pos += len; 937 pos = wpabuf_head_u8(data->inbuf); 938 len = data->in_frag_pos; 939 } 940 wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", 941 EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); 942 943 switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { 944 case EAP_PWD_OPCODE_ID_EXCH: 945 eap_pwd_perform_id_exchange(sm, data, ret, reqData, 946 pos, len); 947 break; 948 case EAP_PWD_OPCODE_COMMIT_EXCH: 949 eap_pwd_perform_commit_exchange(sm, data, ret, reqData, 950 pos, len); 951 break; 952 case EAP_PWD_OPCODE_CONFIRM_EXCH: 953 eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, 954 pos, len); 955 break; 956 default: 957 wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " 958 "opcode %d", lm_exch); 959 break; 960 } 961 /* 962 * if we buffered the just processed input now's the time to free it 963 */ 964 if (data->in_frag_pos) { 965 wpabuf_free(data->inbuf); 966 data->inbuf = NULL; 967 data->in_frag_pos = 0; 968 } 969 970 if (data->outbuf == NULL) { 971 ret->methodState = METHOD_DONE; 972 ret->decision = DECISION_FAIL; 973 return NULL; /* generic failure */ 974 } 975 976 /* 977 * we have output! Do we need to fragment it? 978 */ 979 lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); 980 len = wpabuf_len(data->outbuf); 981 if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 982 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, 983 EAP_CODE_RESPONSE, eap_get_id(reqData)); 984 /* 985 * if so it's the first so include a length field 986 */ 987 EAP_PWD_SET_LENGTH_BIT(lm_exch); 988 EAP_PWD_SET_MORE_BIT(lm_exch); 989 tot_len = len; 990 /* 991 * keep the packet at the MTU 992 */ 993 len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); 994 wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " 995 "length = %d", tot_len); 996 } else { 997 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 998 EAP_PWD_HDR_SIZE + len, 999 EAP_CODE_RESPONSE, eap_get_id(reqData)); 1000 } 1001 if (resp == NULL) 1002 return NULL; 1003 1004 wpabuf_put_u8(resp, lm_exch); 1005 if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 1006 wpabuf_put_be16(resp, tot_len); 1007 data->out_frag_pos += len; 1008 } 1009 buf = wpabuf_head_u8(data->outbuf); 1010 wpabuf_put_data(resp, buf, len); 1011 /* 1012 * if we're not fragmenting then there's no need to carry this around 1013 */ 1014 if (data->out_frag_pos == 0) { 1015 wpabuf_free(data->outbuf); 1016 data->outbuf = NULL; 1017 data->out_frag_pos = 0; 1018 if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 1019 ret->methodState = METHOD_DONE; 1020 ret->decision = DECISION_UNCOND_SUCC; 1021 eap_pwd_state(data, SUCCESS); 1022 } 1023 } 1024 1025 return resp; 1026 } 1027 1028 1029 static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) 1030 { 1031 struct eap_pwd_data *data = priv; 1032 return data->state == SUCCESS; 1033 } 1034 1035 1036 static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1037 { 1038 struct eap_pwd_data *data = priv; 1039 u8 *key; 1040 1041 if (data->state != SUCCESS) 1042 return NULL; 1043 1044 if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) 1045 return NULL; 1046 1047 os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1048 *len = EAP_EMSK_LEN; 1049 1050 return key; 1051 } 1052 1053 1054 int eap_peer_pwd_register(void) 1055 { 1056 struct eap_method *eap; 1057 int ret; 1058 1059 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1060 EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); 1061 if (eap == NULL) 1062 return -1; 1063 1064 eap->init = eap_pwd_init; 1065 eap->deinit = eap_pwd_deinit; 1066 eap->process = eap_pwd_process; 1067 eap->isKeyAvailable = eap_pwd_key_available; 1068 eap->getKey = eap_pwd_getkey; 1069 eap->getSessionId = eap_pwd_get_session_id; 1070 eap->get_emsk = eap_pwd_get_emsk; 1071 1072 ret = eap_peer_method_register(eap); 1073 if (ret) 1074 eap_peer_method_free(eap); 1075 return ret; 1076 } 1077