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