eap_pwd.c revision 1.4
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 bin_clear_free(data->id_peer, data->id_peer_len); 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_clear_free(data->private_value); 152 BN_clear_free(data->server_scalar); 153 BN_clear_free(data->my_scalar); 154 BN_clear_free(data->k); 155 BN_CTX_free(data->bnctx); 156 EC_POINT_clear_free(data->my_element); 157 EC_POINT_clear_free(data->server_element); 158 bin_clear_free(data->id_peer, data->id_peer_len); 159 bin_clear_free(data->id_server, data->id_server_len); 160 bin_clear_free(data->password, data->password_len); 161 if (data->grp) { 162 EC_GROUP_free(data->grp->group); 163 EC_POINT_clear_free(data->grp->pwe); 164 BN_clear_free(data->grp->order); 165 BN_clear_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 size_t prime_len, order_len; 305 306 if (data->state != PWD_Commit_Req) { 307 ret->ignore = TRUE; 308 goto fin; 309 } 310 311 prime_len = BN_num_bytes(data->grp->prime); 312 order_len = BN_num_bytes(data->grp->order); 313 314 if (payload_len != 2 * prime_len + order_len) { 315 wpa_printf(MSG_INFO, 316 "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 317 (unsigned int) payload_len, 318 (unsigned int) (2 * prime_len + order_len)); 319 goto fin; 320 } 321 322 if (((data->private_value = BN_new()) == NULL) || 323 ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || 324 ((cofactor = BN_new()) == NULL) || 325 ((data->my_scalar = BN_new()) == NULL) || 326 ((mask = BN_new()) == NULL)) { 327 wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); 328 goto fin; 329 } 330 331 if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { 332 wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " 333 "for curve"); 334 goto fin; 335 } 336 337 if (BN_rand_range(data->private_value, data->grp->order) != 1 || 338 BN_rand_range(mask, data->grp->order) != 1 || 339 BN_add(data->my_scalar, data->private_value, mask) != 1 || 340 BN_mod(data->my_scalar, data->my_scalar, data->grp->order, 341 data->bnctx) != 1) { 342 wpa_printf(MSG_INFO, 343 "EAP-pwd (peer): unable to get randomness"); 344 goto fin; 345 } 346 347 if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, 348 data->grp->pwe, mask, data->bnctx)) { 349 wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " 350 "fail"); 351 eap_pwd_state(data, FAILURE); 352 goto fin; 353 } 354 355 if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) 356 { 357 wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); 358 goto fin; 359 } 360 BN_clear_free(mask); 361 362 if (((x = BN_new()) == NULL) || 363 ((y = BN_new()) == NULL)) { 364 wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); 365 goto fin; 366 } 367 368 /* process the request */ 369 if (((data->server_scalar = BN_new()) == NULL) || 370 ((data->k = BN_new()) == NULL) || 371 ((K = EC_POINT_new(data->grp->group)) == NULL) || 372 ((point = EC_POINT_new(data->grp->group)) == NULL) || 373 ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) 374 { 375 wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " 376 "fail"); 377 goto fin; 378 } 379 380 /* element, x then y, followed by scalar */ 381 ptr = (u8 *) payload; 382 BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); 383 ptr += BN_num_bytes(data->grp->prime); 384 BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); 385 ptr += BN_num_bytes(data->grp->prime); 386 BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); 387 if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, 388 data->server_element, x, y, 389 data->bnctx)) { 390 wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " 391 "fail"); 392 goto fin; 393 } 394 395 /* check to ensure server's element is not in a small sub-group */ 396 if (BN_cmp(cofactor, BN_value_one())) { 397 if (!EC_POINT_mul(data->grp->group, point, NULL, 398 data->server_element, cofactor, NULL)) { 399 wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 400 "server element by order!\n"); 401 goto fin; 402 } 403 if (EC_POINT_is_at_infinity(data->grp->group, point)) { 404 wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " 405 "is at infinity!\n"); 406 goto fin; 407 } 408 } 409 410 /* compute the shared key, k */ 411 if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, 412 data->server_scalar, data->bnctx)) || 413 (!EC_POINT_add(data->grp->group, K, K, data->server_element, 414 data->bnctx)) || 415 (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, 416 data->bnctx))) { 417 wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " 418 "fail"); 419 goto fin; 420 } 421 422 /* ensure that the shared key isn't in a small sub-group */ 423 if (BN_cmp(cofactor, BN_value_one())) { 424 if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, 425 NULL)) { 426 wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 427 "shared key point by order"); 428 goto fin; 429 } 430 } 431 432 /* 433 * This check is strictly speaking just for the case above where 434 * co-factor > 1 but it was suggested that even though this is probably 435 * never going to happen it is a simple and safe check "just to be 436 * sure" so let's be safe. 437 */ 438 if (EC_POINT_is_at_infinity(data->grp->group, K)) { 439 wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " 440 "infinity!\n"); 441 goto fin; 442 } 443 444 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, 445 NULL, data->bnctx)) { 446 wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " 447 "shared secret from point"); 448 goto fin; 449 } 450 451 /* now do the response */ 452 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 453 data->my_element, x, y, 454 data->bnctx)) { 455 wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); 456 goto fin; 457 } 458 459 if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || 460 ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == 461 NULL)) { 462 wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); 463 goto fin; 464 } 465 466 /* 467 * bignums occupy as little memory as possible so one that is 468 * sufficiently smaller than the prime or order might need pre-pending 469 * with zeros. 470 */ 471 os_memset(scalar, 0, BN_num_bytes(data->grp->order)); 472 os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); 473 offset = BN_num_bytes(data->grp->order) - 474 BN_num_bytes(data->my_scalar); 475 BN_bn2bin(data->my_scalar, scalar + offset); 476 477 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 478 BN_bn2bin(x, element + offset); 479 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 480 BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); 481 482 data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + 483 2 * BN_num_bytes(data->grp->prime)); 484 if (data->outbuf == NULL) 485 goto fin; 486 487 /* we send the element as (x,y) follwed by the scalar */ 488 wpabuf_put_data(data->outbuf, element, 489 2 * BN_num_bytes(data->grp->prime)); 490 wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); 491 492fin: 493 os_free(scalar); 494 os_free(element); 495 BN_clear_free(x); 496 BN_clear_free(y); 497 BN_clear_free(cofactor); 498 EC_POINT_clear_free(K); 499 EC_POINT_clear_free(point); 500 if (data->outbuf == NULL) 501 eap_pwd_state(data, FAILURE); 502 else 503 eap_pwd_state(data, PWD_Confirm_Req); 504} 505 506 507static void 508eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, 509 struct eap_method_ret *ret, 510 const struct wpabuf *reqData, 511 const u8 *payload, size_t payload_len) 512{ 513 BIGNUM *x = NULL, *y = NULL; 514 struct crypto_hash *hash; 515 u32 cs; 516 u16 grp; 517 u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; 518 int offset; 519 520 /* 521 * first build up the ciphersuite which is group | random_function | 522 * prf 523 */ 524 grp = htons(data->group_num); 525 ptr = (u8 *) &cs; 526 os_memcpy(ptr, &grp, sizeof(u16)); 527 ptr += sizeof(u16); 528 *ptr = EAP_PWD_DEFAULT_RAND_FUNC; 529 ptr += sizeof(u8); 530 *ptr = EAP_PWD_DEFAULT_PRF; 531 532 /* each component of the cruft will be at most as big as the prime */ 533 if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || 534 ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { 535 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " 536 "fail"); 537 goto fin; 538 } 539 540 /* 541 * server's commit is H(k | server_element | server_scalar | 542 * peer_element | peer_scalar | ciphersuite) 543 */ 544 hash = eap_pwd_h_init(); 545 if (hash == NULL) 546 goto fin; 547 548 /* 549 * zero the memory each time because this is mod prime math and some 550 * value may start with a few zeros and the previous one did not. 551 */ 552 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 553 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); 554 BN_bn2bin(data->k, cruft + offset); 555 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 556 557 /* server element: x, y */ 558 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 559 data->server_element, x, y, 560 data->bnctx)) { 561 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 562 "assignment fail"); 563 goto fin; 564 } 565 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 566 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 567 BN_bn2bin(x, cruft + offset); 568 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 569 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 570 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 571 BN_bn2bin(y, cruft + offset); 572 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 573 574 /* server scalar */ 575 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 576 offset = BN_num_bytes(data->grp->order) - 577 BN_num_bytes(data->server_scalar); 578 BN_bn2bin(data->server_scalar, cruft + offset); 579 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 580 581 /* my element: x, y */ 582 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 583 data->my_element, x, y, 584 data->bnctx)) { 585 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " 586 "assignment fail"); 587 goto fin; 588 } 589 590 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 591 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 592 BN_bn2bin(x, cruft + offset); 593 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 594 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 595 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 596 BN_bn2bin(y, cruft + offset); 597 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 598 599 /* my scalar */ 600 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 601 offset = BN_num_bytes(data->grp->order) - 602 BN_num_bytes(data->my_scalar); 603 BN_bn2bin(data->my_scalar, cruft + offset); 604 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 605 606 /* the ciphersuite */ 607 eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 608 609 /* random function fin */ 610 eap_pwd_h_final(hash, conf); 611 612 ptr = (u8 *) payload; 613 if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { 614 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); 615 goto fin; 616 } 617 618 wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); 619 620 /* 621 * compute confirm: 622 * H(k | peer_element | peer_scalar | server_element | server_scalar | 623 * ciphersuite) 624 */ 625 hash = eap_pwd_h_init(); 626 if (hash == NULL) 627 goto fin; 628 629 /* k */ 630 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 631 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); 632 BN_bn2bin(data->k, cruft + offset); 633 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 634 635 /* my element */ 636 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 637 data->my_element, x, y, 638 data->bnctx)) { 639 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 640 "assignment fail"); 641 goto fin; 642 } 643 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 644 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 645 BN_bn2bin(x, cruft + offset); 646 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 647 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 648 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 649 BN_bn2bin(y, cruft + offset); 650 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 651 652 /* my scalar */ 653 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 654 offset = BN_num_bytes(data->grp->order) - 655 BN_num_bytes(data->my_scalar); 656 BN_bn2bin(data->my_scalar, cruft + offset); 657 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 658 659 /* server element: x, y */ 660 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 661 data->server_element, x, y, 662 data->bnctx)) { 663 wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " 664 "assignment fail"); 665 goto fin; 666 } 667 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 668 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 669 BN_bn2bin(x, cruft + offset); 670 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 671 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 672 offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 673 BN_bn2bin(y, cruft + offset); 674 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); 675 676 /* server scalar */ 677 os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); 678 offset = BN_num_bytes(data->grp->order) - 679 BN_num_bytes(data->server_scalar); 680 BN_bn2bin(data->server_scalar, cruft + offset); 681 eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); 682 683 /* the ciphersuite */ 684 eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); 685 686 /* all done */ 687 eap_pwd_h_final(hash, conf); 688 689 if (compute_keys(data->grp, data->bnctx, data->k, 690 data->my_scalar, data->server_scalar, conf, ptr, 691 &cs, data->msk, data->emsk, data->session_id) < 0) { 692 wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " 693 "EMSK"); 694 goto fin; 695 } 696 697 data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); 698 if (data->outbuf == NULL) 699 goto fin; 700 701 wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); 702 703fin: 704 bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); 705 BN_clear_free(x); 706 BN_clear_free(y); 707 if (data->outbuf == NULL) { 708 ret->methodState = METHOD_DONE; 709 ret->decision = DECISION_FAIL; 710 eap_pwd_state(data, FAILURE); 711 } else { 712 eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); 713 } 714} 715 716 717static struct wpabuf * 718eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, 719 const struct wpabuf *reqData) 720{ 721 struct eap_pwd_data *data = priv; 722 struct wpabuf *resp = NULL; 723 const u8 *pos, *buf; 724 size_t len; 725 u16 tot_len = 0; 726 u8 lm_exch; 727 728 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); 729 if ((pos == NULL) || (len < 1)) { 730 wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " 731 "len is %d", 732 pos == NULL ? "NULL" : "not NULL", (int) len); 733 ret->ignore = TRUE; 734 return NULL; 735 } 736 737 ret->ignore = FALSE; 738 ret->methodState = METHOD_MAY_CONT; 739 ret->decision = DECISION_FAIL; 740 ret->allowNotifications = FALSE; 741 742 lm_exch = *pos; 743 pos++; /* skip over the bits and the exch */ 744 len--; 745 746 /* 747 * we're fragmenting so send out the next fragment 748 */ 749 if (data->out_frag_pos) { 750 /* 751 * this should be an ACK 752 */ 753 if (len) 754 wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " 755 "not an ACK"); 756 757 wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); 758 /* 759 * check if there are going to be more fragments 760 */ 761 len = wpabuf_len(data->outbuf) - data->out_frag_pos; 762 if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 763 len = data->mtu - EAP_PWD_HDR_SIZE; 764 EAP_PWD_SET_MORE_BIT(lm_exch); 765 } 766 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 767 EAP_PWD_HDR_SIZE + len, 768 EAP_CODE_RESPONSE, eap_get_id(reqData)); 769 if (resp == NULL) { 770 wpa_printf(MSG_INFO, "Unable to allocate memory for " 771 "next fragment!"); 772 return NULL; 773 } 774 wpabuf_put_u8(resp, lm_exch); 775 buf = wpabuf_head_u8(data->outbuf); 776 wpabuf_put_data(resp, buf + data->out_frag_pos, len); 777 data->out_frag_pos += len; 778 /* 779 * this is the last fragment so get rid of the out buffer 780 */ 781 if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { 782 wpabuf_free(data->outbuf); 783 data->outbuf = NULL; 784 data->out_frag_pos = 0; 785 } 786 wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", 787 data->out_frag_pos == 0 ? "last" : "next", 788 (int) len); 789 if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 790 ret->methodState = METHOD_DONE; 791 ret->decision = DECISION_UNCOND_SUCC; 792 eap_pwd_state(data, SUCCESS); 793 } 794 return resp; 795 } 796 797 /* 798 * see if this is a fragment that needs buffering 799 * 800 * if it's the first fragment there'll be a length field 801 */ 802 if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 803 if (len < 2) { 804 wpa_printf(MSG_DEBUG, 805 "EAP-pwd: Frame too short to contain Total-Length field"); 806 ret->ignore = TRUE; 807 return NULL; 808 } 809 tot_len = WPA_GET_BE16(pos); 810 wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " 811 "total length = %d", tot_len); 812 if (tot_len > 15000) 813 return NULL; 814 if (data->inbuf) { 815 wpa_printf(MSG_DEBUG, 816 "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); 817 ret->ignore = TRUE; 818 return NULL; 819 } 820 data->inbuf = wpabuf_alloc(tot_len); 821 if (data->inbuf == NULL) { 822 wpa_printf(MSG_INFO, "Out of memory to buffer " 823 "fragments!"); 824 return NULL; 825 } 826 pos += sizeof(u16); 827 len -= sizeof(u16); 828 } 829 /* 830 * buffer and ACK the fragment 831 */ 832 if (EAP_PWD_GET_MORE_BIT(lm_exch)) { 833 data->in_frag_pos += len; 834 if (data->in_frag_pos > wpabuf_size(data->inbuf)) { 835 wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " 836 "detected (%d vs. %d)!", 837 (int) data->in_frag_pos, 838 (int) wpabuf_len(data->inbuf)); 839 wpabuf_free(data->inbuf); 840 data->inbuf = NULL; 841 data->in_frag_pos = 0; 842 return NULL; 843 } 844 wpabuf_put_data(data->inbuf, pos, len); 845 846 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 847 EAP_PWD_HDR_SIZE, 848 EAP_CODE_RESPONSE, eap_get_id(reqData)); 849 if (resp != NULL) 850 wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); 851 wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", 852 (int) len); 853 return resp; 854 } 855 /* 856 * we're buffering and this is the last fragment 857 */ 858 if (data->in_frag_pos) { 859 wpabuf_put_data(data->inbuf, pos, len); 860 wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", 861 (int) len); 862 data->in_frag_pos += len; 863 pos = wpabuf_head_u8(data->inbuf); 864 len = data->in_frag_pos; 865 } 866 wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", 867 EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); 868 869 switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { 870 case EAP_PWD_OPCODE_ID_EXCH: 871 eap_pwd_perform_id_exchange(sm, data, ret, reqData, 872 pos, len); 873 break; 874 case EAP_PWD_OPCODE_COMMIT_EXCH: 875 eap_pwd_perform_commit_exchange(sm, data, ret, reqData, 876 pos, len); 877 break; 878 case EAP_PWD_OPCODE_CONFIRM_EXCH: 879 eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, 880 pos, len); 881 break; 882 default: 883 wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " 884 "opcode %d", lm_exch); 885 break; 886 } 887 /* 888 * if we buffered the just processed input now's the time to free it 889 */ 890 if (data->in_frag_pos) { 891 wpabuf_free(data->inbuf); 892 data->inbuf = NULL; 893 data->in_frag_pos = 0; 894 } 895 896 if (data->outbuf == NULL) { 897 ret->methodState = METHOD_DONE; 898 ret->decision = DECISION_FAIL; 899 return NULL; /* generic failure */ 900 } 901 902 /* 903 * we have output! Do we need to fragment it? 904 */ 905 len = wpabuf_len(data->outbuf); 906 lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch); 907 if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 908 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, 909 EAP_CODE_RESPONSE, eap_get_id(reqData)); 910 /* 911 * if so it's the first so include a length field 912 */ 913 EAP_PWD_SET_LENGTH_BIT(lm_exch); 914 EAP_PWD_SET_MORE_BIT(lm_exch); 915 tot_len = len; 916 /* 917 * keep the packet at the MTU 918 */ 919 len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); 920 wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " 921 "length = %d", tot_len); 922 } else { 923 resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 924 EAP_PWD_HDR_SIZE + len, 925 EAP_CODE_RESPONSE, eap_get_id(reqData)); 926 } 927 if (resp == NULL) 928 return NULL; 929 930 wpabuf_put_u8(resp, lm_exch); 931 if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 932 wpabuf_put_be16(resp, tot_len); 933 data->out_frag_pos += len; 934 } 935 buf = wpabuf_head_u8(data->outbuf); 936 wpabuf_put_data(resp, buf, len); 937 /* 938 * if we're not fragmenting then there's no need to carry this around 939 */ 940 if (data->out_frag_pos == 0) { 941 wpabuf_free(data->outbuf); 942 data->outbuf = NULL; 943 data->out_frag_pos = 0; 944 if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 945 ret->methodState = METHOD_DONE; 946 ret->decision = DECISION_UNCOND_SUCC; 947 eap_pwd_state(data, SUCCESS); 948 } 949 } 950 951 return resp; 952} 953 954 955static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) 956{ 957 struct eap_pwd_data *data = priv; 958 return data->state == SUCCESS; 959} 960 961 962static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 963{ 964 struct eap_pwd_data *data = priv; 965 u8 *key; 966 967 if (data->state != SUCCESS) 968 return NULL; 969 970 if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) 971 return NULL; 972 973 os_memcpy(key, data->emsk, EAP_EMSK_LEN); 974 *len = EAP_EMSK_LEN; 975 976 return key; 977} 978 979 980int eap_peer_pwd_register(void) 981{ 982 struct eap_method *eap; 983 int ret; 984 985 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 986 EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); 987 if (eap == NULL) 988 return -1; 989 990 eap->init = eap_pwd_init; 991 eap->deinit = eap_pwd_deinit; 992 eap->process = eap_pwd_process; 993 eap->isKeyAvailable = eap_pwd_key_available; 994 eap->getKey = eap_pwd_getkey; 995 eap->getSessionId = eap_pwd_get_session_id; 996 eap->get_emsk = eap_pwd_get_emsk; 997 998 ret = eap_peer_method_register(eap); 999 if (ret) 1000 eap_peer_method_free(eap); 1001 return ret; 1002} 1003