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