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