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" 13325151a3SRui 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; 29325151a3SRui 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; 91325151a3SRui Paulo int pwhash; 92f05cddf9SRui Paulo 93325151a3SRui 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; 135325151a3SRui 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; 223325151a3SRui Paulo const u8 *password; 224325151a3SRui Paulo size_t password_len; 225325151a3SRui Paulo u8 pwhashhash[16]; 226325151a3SRui 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); 242325151a3SRui Paulo wpa_printf(MSG_DEBUG, 243325151a3SRui Paulo "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", 244325151a3SRui 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 252325151a3SRui Paulo if (id->prep != EAP_PWD_PREP_NONE && 253325151a3SRui Paulo id->prep != EAP_PWD_PREP_MS) { 254325151a3SRui Paulo wpa_printf(MSG_DEBUG, 255325151a3SRui Paulo "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", 256325151a3SRui Paulo id->prep); 257325151a3SRui Paulo eap_pwd_state(data, FAILURE); 258325151a3SRui Paulo return; 259325151a3SRui Paulo } 260325151a3SRui Paulo 261325151a3SRui Paulo if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) { 262325151a3SRui Paulo wpa_printf(MSG_DEBUG, 263325151a3SRui Paulo "EAP-PWD: Unhashed password not available"); 264325151a3SRui Paulo eap_pwd_state(data, FAILURE); 265325151a3SRui Paulo return; 266325151a3SRui Paulo } 267325151a3SRui 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 290325151a3SRui Paulo if (id->prep == EAP_PWD_PREP_MS) { 291325151a3SRui Paulo #ifdef CONFIG_FIPS 292325151a3SRui Paulo wpa_printf(MSG_ERROR, 293325151a3SRui Paulo "EAP-PWD (peer): MS password hash not supported in FIPS mode"); 294325151a3SRui Paulo eap_pwd_state(data, FAILURE); 295325151a3SRui Paulo return; 296325151a3SRui Paulo #else /* CONFIG_FIPS */ 297325151a3SRui Paulo if (data->password_hash) { 298325151a3SRui Paulo res = hash_nt_password_hash(data->password, pwhashhash); 299325151a3SRui Paulo } else { 300325151a3SRui Paulo u8 pwhash[16]; 301325151a3SRui Paulo 302325151a3SRui Paulo res = nt_password_hash(data->password, 303325151a3SRui Paulo data->password_len, pwhash); 304325151a3SRui Paulo if (res == 0) 305325151a3SRui Paulo res = hash_nt_password_hash(pwhash, pwhashhash); 306325151a3SRui Paulo os_memset(pwhash, 0, sizeof(pwhash)); 307325151a3SRui Paulo } 308325151a3SRui Paulo 309325151a3SRui Paulo if (res) { 310325151a3SRui Paulo eap_pwd_state(data, FAILURE); 311325151a3SRui Paulo return; 312325151a3SRui Paulo } 313325151a3SRui Paulo 314325151a3SRui Paulo password = pwhashhash; 315325151a3SRui Paulo password_len = sizeof(pwhashhash); 316325151a3SRui Paulo #endif /* CONFIG_FIPS */ 317325151a3SRui Paulo } else { 318325151a3SRui Paulo password = data->password; 319325151a3SRui Paulo password_len = data->password_len; 320325151a3SRui Paulo } 321325151a3SRui Paulo 322f05cddf9SRui Paulo /* compute PWE */ 323325151a3SRui Paulo res = compute_password_element(data->grp, data->group_num, 324325151a3SRui Paulo password, password_len, 325f05cddf9SRui Paulo data->id_server, data->id_server_len, 326f05cddf9SRui Paulo data->id_peer, data->id_peer_len, 327325151a3SRui Paulo id->token); 328325151a3SRui Paulo os_memset(pwhashhash, 0, sizeof(pwhashhash)); 329325151a3SRui 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; 365325151a3SRui Paulo size_t prime_len, order_len; 366325151a3SRui Paulo 367325151a3SRui Paulo if (data->state != PWD_Commit_Req) { 368325151a3SRui Paulo ret->ignore = TRUE; 369325151a3SRui Paulo goto fin; 370325151a3SRui Paulo } 371325151a3SRui Paulo 372325151a3SRui Paulo prime_len = BN_num_bytes(data->grp->prime); 373325151a3SRui Paulo order_len = BN_num_bytes(data->grp->order); 374325151a3SRui Paulo 375325151a3SRui Paulo if (payload_len != 2 * prime_len + order_len) { 376325151a3SRui Paulo wpa_printf(MSG_INFO, 377325151a3SRui Paulo "EAP-pwd: Unexpected Commit payload length %u (expected %u)", 378325151a3SRui Paulo (unsigned int) payload_len, 379325151a3SRui Paulo (unsigned int) (2 * prime_len + order_len)); 380325151a3SRui Paulo goto fin; 381325151a3SRui 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 } 421f05cddf9SRui Paulo 422f05cddf9SRui Paulo if (((x = BN_new()) == NULL) || 423f05cddf9SRui Paulo ((y = BN_new()) == NULL)) { 424f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); 425f05cddf9SRui Paulo goto fin; 426f05cddf9SRui Paulo } 427f05cddf9SRui Paulo 428f05cddf9SRui Paulo /* process the request */ 429f05cddf9SRui Paulo if (((data->server_scalar = BN_new()) == NULL) || 430f05cddf9SRui Paulo ((data->k = BN_new()) == NULL) || 431f05cddf9SRui Paulo ((K = EC_POINT_new(data->grp->group)) == NULL) || 432f05cddf9SRui Paulo ((point = EC_POINT_new(data->grp->group)) == NULL) || 433f05cddf9SRui Paulo ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) 434f05cddf9SRui Paulo { 435f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " 436f05cddf9SRui Paulo "fail"); 437f05cddf9SRui Paulo goto fin; 438f05cddf9SRui Paulo } 439f05cddf9SRui Paulo 440f05cddf9SRui Paulo /* element, x then y, followed by scalar */ 441f05cddf9SRui Paulo ptr = (u8 *) payload; 442f05cddf9SRui Paulo BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); 443f05cddf9SRui Paulo ptr += BN_num_bytes(data->grp->prime); 444f05cddf9SRui Paulo BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); 445f05cddf9SRui Paulo ptr += BN_num_bytes(data->grp->prime); 446f05cddf9SRui Paulo BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); 447f05cddf9SRui Paulo if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, 448f05cddf9SRui Paulo data->server_element, x, y, 449f05cddf9SRui Paulo data->bnctx)) { 450f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " 451f05cddf9SRui Paulo "fail"); 452f05cddf9SRui Paulo goto fin; 453f05cddf9SRui Paulo } 454f05cddf9SRui Paulo 455f05cddf9SRui Paulo /* check to ensure server's element is not in a small sub-group */ 456f05cddf9SRui Paulo if (BN_cmp(cofactor, BN_value_one())) { 457f05cddf9SRui Paulo if (!EC_POINT_mul(data->grp->group, point, NULL, 458f05cddf9SRui Paulo data->server_element, cofactor, NULL)) { 459f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 460f05cddf9SRui Paulo "server element by order!\n"); 461f05cddf9SRui Paulo goto fin; 462f05cddf9SRui Paulo } 463f05cddf9SRui Paulo if (EC_POINT_is_at_infinity(data->grp->group, point)) { 464f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " 465f05cddf9SRui Paulo "is at infinity!\n"); 466f05cddf9SRui Paulo goto fin; 467f05cddf9SRui Paulo } 468f05cddf9SRui Paulo } 469f05cddf9SRui Paulo 470f05cddf9SRui Paulo /* compute the shared key, k */ 471f05cddf9SRui Paulo if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, 472f05cddf9SRui Paulo data->server_scalar, data->bnctx)) || 473f05cddf9SRui Paulo (!EC_POINT_add(data->grp->group, K, K, data->server_element, 474f05cddf9SRui Paulo data->bnctx)) || 475f05cddf9SRui Paulo (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, 476f05cddf9SRui Paulo data->bnctx))) { 477f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " 478f05cddf9SRui Paulo "fail"); 479f05cddf9SRui Paulo goto fin; 480f05cddf9SRui Paulo } 481f05cddf9SRui Paulo 482f05cddf9SRui Paulo /* ensure that the shared key isn't in a small sub-group */ 483f05cddf9SRui Paulo if (BN_cmp(cofactor, BN_value_one())) { 484f05cddf9SRui Paulo if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, 485f05cddf9SRui Paulo NULL)) { 486f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " 487f05cddf9SRui Paulo "shared key point by order"); 488f05cddf9SRui Paulo goto fin; 489f05cddf9SRui Paulo } 490f05cddf9SRui Paulo } 491f05cddf9SRui Paulo 492f05cddf9SRui Paulo /* 493f05cddf9SRui Paulo * This check is strictly speaking just for the case above where 494f05cddf9SRui Paulo * co-factor > 1 but it was suggested that even though this is probably 495f05cddf9SRui Paulo * never going to happen it is a simple and safe check "just to be 496f05cddf9SRui Paulo * sure" so let's be safe. 497f05cddf9SRui Paulo */ 498f05cddf9SRui Paulo if (EC_POINT_is_at_infinity(data->grp->group, K)) { 499f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " 500f05cddf9SRui Paulo "infinity!\n"); 501f05cddf9SRui Paulo goto fin; 502f05cddf9SRui Paulo } 503f05cddf9SRui Paulo 504f05cddf9SRui Paulo if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, 505f05cddf9SRui Paulo NULL, data->bnctx)) { 506f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " 507f05cddf9SRui Paulo "shared secret from point"); 508f05cddf9SRui Paulo goto fin; 509f05cddf9SRui Paulo } 510f05cddf9SRui Paulo 511f05cddf9SRui Paulo /* now do the response */ 512f05cddf9SRui Paulo if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, 513f05cddf9SRui Paulo data->my_element, x, y, 514f05cddf9SRui Paulo data->bnctx)) { 515f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); 516f05cddf9SRui Paulo goto fin; 517f05cddf9SRui Paulo } 518f05cddf9SRui Paulo 519f05cddf9SRui Paulo if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || 520f05cddf9SRui Paulo ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == 521f05cddf9SRui Paulo NULL)) { 522f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); 523f05cddf9SRui Paulo goto fin; 524f05cddf9SRui Paulo } 525f05cddf9SRui Paulo 526f05cddf9SRui Paulo /* 527f05cddf9SRui Paulo * bignums occupy as little memory as possible so one that is 528f05cddf9SRui Paulo * sufficiently smaller than the prime or order might need pre-pending 529f05cddf9SRui Paulo * with zeros. 530f05cddf9SRui Paulo */ 531f05cddf9SRui Paulo os_memset(scalar, 0, BN_num_bytes(data->grp->order)); 532f05cddf9SRui Paulo os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); 533f05cddf9SRui Paulo offset = BN_num_bytes(data->grp->order) - 534f05cddf9SRui Paulo BN_num_bytes(data->my_scalar); 535f05cddf9SRui Paulo BN_bn2bin(data->my_scalar, scalar + offset); 536f05cddf9SRui Paulo 537f05cddf9SRui Paulo offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); 538f05cddf9SRui Paulo BN_bn2bin(x, element + offset); 539f05cddf9SRui Paulo offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); 540f05cddf9SRui Paulo BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); 541f05cddf9SRui Paulo 542f05cddf9SRui Paulo data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + 543f05cddf9SRui Paulo 2 * BN_num_bytes(data->grp->prime)); 544f05cddf9SRui Paulo if (data->outbuf == NULL) 545f05cddf9SRui Paulo goto fin; 546f05cddf9SRui Paulo 547f05cddf9SRui Paulo /* we send the element as (x,y) follwed by the scalar */ 548f05cddf9SRui Paulo wpabuf_put_data(data->outbuf, element, 549f05cddf9SRui Paulo 2 * BN_num_bytes(data->grp->prime)); 550f05cddf9SRui Paulo wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); 551f05cddf9SRui Paulo 552f05cddf9SRui Paulo fin: 553f05cddf9SRui Paulo os_free(scalar); 554f05cddf9SRui Paulo os_free(element); 5555b9c547cSRui Paulo BN_clear_free(x); 5565b9c547cSRui Paulo BN_clear_free(y); 557*780fb4a2SCy Schubert BN_clear_free(mask); 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 581325151a3SRui Paulo if (data->state != PWD_Confirm_Req) { 582325151a3SRui Paulo ret->ignore = TRUE; 583325151a3SRui Paulo goto fin; 584325151a3SRui Paulo } 585325151a3SRui Paulo 586325151a3SRui Paulo if (payload_len != SHA256_MAC_LEN) { 587325151a3SRui Paulo wpa_printf(MSG_INFO, 588325151a3SRui Paulo "EAP-pwd: Unexpected Confirm payload length %u (expected %u)", 589325151a3SRui Paulo (unsigned int) payload_len, SHA256_MAC_LEN); 590325151a3SRui Paulo goto fin; 591325151a3SRui Paulo } 592325151a3SRui 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: 777*780fb4a2SCy Schubert if (data->grp) 7785b9c547cSRui Paulo bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); 7795b9c547cSRui Paulo BN_clear_free(x); 7805b9c547cSRui Paulo BN_clear_free(y); 781f05cddf9SRui Paulo if (data->outbuf == NULL) { 7825b9c547cSRui Paulo ret->methodState = METHOD_DONE; 783f05cddf9SRui Paulo ret->decision = DECISION_FAIL; 784f05cddf9SRui Paulo eap_pwd_state(data, FAILURE); 785f05cddf9SRui Paulo } else { 7865b9c547cSRui Paulo eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); 787f05cddf9SRui Paulo } 788f05cddf9SRui Paulo } 789f05cddf9SRui Paulo 790f05cddf9SRui Paulo 791f05cddf9SRui Paulo static struct wpabuf * 792f05cddf9SRui Paulo eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, 793f05cddf9SRui Paulo const struct wpabuf *reqData) 794f05cddf9SRui Paulo { 795f05cddf9SRui Paulo struct eap_pwd_data *data = priv; 796f05cddf9SRui Paulo struct wpabuf *resp = NULL; 797f05cddf9SRui Paulo const u8 *pos, *buf; 798f05cddf9SRui Paulo size_t len; 799f05cddf9SRui Paulo u16 tot_len = 0; 800f05cddf9SRui Paulo u8 lm_exch; 801f05cddf9SRui Paulo 802f05cddf9SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); 803f05cddf9SRui Paulo if ((pos == NULL) || (len < 1)) { 804f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " 805f05cddf9SRui Paulo "len is %d", 806f05cddf9SRui Paulo pos == NULL ? "NULL" : "not NULL", (int) len); 807f05cddf9SRui Paulo ret->ignore = TRUE; 808f05cddf9SRui Paulo return NULL; 809f05cddf9SRui Paulo } 810f05cddf9SRui Paulo 811f05cddf9SRui Paulo ret->ignore = FALSE; 812f05cddf9SRui Paulo ret->methodState = METHOD_MAY_CONT; 813f05cddf9SRui Paulo ret->decision = DECISION_FAIL; 814f05cddf9SRui Paulo ret->allowNotifications = FALSE; 815f05cddf9SRui Paulo 816f05cddf9SRui Paulo lm_exch = *pos; 817f05cddf9SRui Paulo pos++; /* skip over the bits and the exch */ 818f05cddf9SRui Paulo len--; 819f05cddf9SRui Paulo 820f05cddf9SRui Paulo /* 821f05cddf9SRui Paulo * we're fragmenting so send out the next fragment 822f05cddf9SRui Paulo */ 823f05cddf9SRui Paulo if (data->out_frag_pos) { 824f05cddf9SRui Paulo /* 825f05cddf9SRui Paulo * this should be an ACK 826f05cddf9SRui Paulo */ 827f05cddf9SRui Paulo if (len) 828f05cddf9SRui Paulo wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " 829f05cddf9SRui Paulo "not an ACK"); 830f05cddf9SRui Paulo 831f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); 832f05cddf9SRui Paulo /* 833f05cddf9SRui Paulo * check if there are going to be more fragments 834f05cddf9SRui Paulo */ 835f05cddf9SRui Paulo len = wpabuf_len(data->outbuf) - data->out_frag_pos; 836f05cddf9SRui Paulo if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { 837f05cddf9SRui Paulo len = data->mtu - EAP_PWD_HDR_SIZE; 838f05cddf9SRui Paulo EAP_PWD_SET_MORE_BIT(lm_exch); 839f05cddf9SRui Paulo } 840f05cddf9SRui Paulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 841f05cddf9SRui Paulo EAP_PWD_HDR_SIZE + len, 842f05cddf9SRui Paulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 843f05cddf9SRui Paulo if (resp == NULL) { 844f05cddf9SRui Paulo wpa_printf(MSG_INFO, "Unable to allocate memory for " 845f05cddf9SRui Paulo "next fragment!"); 846f05cddf9SRui Paulo return NULL; 847f05cddf9SRui Paulo } 848f05cddf9SRui Paulo wpabuf_put_u8(resp, lm_exch); 849f05cddf9SRui Paulo buf = wpabuf_head_u8(data->outbuf); 850f05cddf9SRui Paulo wpabuf_put_data(resp, buf + data->out_frag_pos, len); 851f05cddf9SRui Paulo data->out_frag_pos += len; 852f05cddf9SRui Paulo /* 853f05cddf9SRui Paulo * this is the last fragment so get rid of the out buffer 854f05cddf9SRui Paulo */ 855f05cddf9SRui Paulo if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { 856f05cddf9SRui Paulo wpabuf_free(data->outbuf); 857f05cddf9SRui Paulo data->outbuf = NULL; 858f05cddf9SRui Paulo data->out_frag_pos = 0; 859f05cddf9SRui Paulo } 860f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", 861f05cddf9SRui Paulo data->out_frag_pos == 0 ? "last" : "next", 862f05cddf9SRui Paulo (int) len); 8635b9c547cSRui Paulo if (data->state == SUCCESS_ON_FRAG_COMPLETION) { 8645b9c547cSRui Paulo ret->methodState = METHOD_DONE; 8655b9c547cSRui Paulo ret->decision = DECISION_UNCOND_SUCC; 8665b9c547cSRui Paulo eap_pwd_state(data, SUCCESS); 8675b9c547cSRui Paulo } 868f05cddf9SRui Paulo return resp; 869f05cddf9SRui Paulo } 870f05cddf9SRui Paulo 871f05cddf9SRui Paulo /* 872f05cddf9SRui Paulo * see if this is a fragment that needs buffering 873f05cddf9SRui Paulo * 874f05cddf9SRui Paulo * if it's the first fragment there'll be a length field 875f05cddf9SRui Paulo */ 876f05cddf9SRui Paulo if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { 877325151a3SRui Paulo if (len < 2) { 878325151a3SRui Paulo wpa_printf(MSG_DEBUG, 879325151a3SRui Paulo "EAP-pwd: Frame too short to contain Total-Length field"); 880325151a3SRui Paulo ret->ignore = TRUE; 881325151a3SRui Paulo return NULL; 882325151a3SRui Paulo } 883f05cddf9SRui Paulo tot_len = WPA_GET_BE16(pos); 884f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " 885f05cddf9SRui Paulo "total length = %d", tot_len); 8865b9c547cSRui Paulo if (tot_len > 15000) 8875b9c547cSRui Paulo return NULL; 888325151a3SRui Paulo if (data->inbuf) { 889325151a3SRui Paulo wpa_printf(MSG_DEBUG, 890325151a3SRui Paulo "EAP-pwd: Unexpected new fragment start when previous fragment is still in use"); 891325151a3SRui Paulo ret->ignore = TRUE; 892325151a3SRui Paulo return NULL; 893325151a3SRui Paulo } 894f05cddf9SRui Paulo data->inbuf = wpabuf_alloc(tot_len); 895f05cddf9SRui Paulo if (data->inbuf == NULL) { 896f05cddf9SRui Paulo wpa_printf(MSG_INFO, "Out of memory to buffer " 897f05cddf9SRui Paulo "fragments!"); 898f05cddf9SRui Paulo return NULL; 899f05cddf9SRui Paulo } 900325151a3SRui Paulo data->in_frag_pos = 0; 901f05cddf9SRui Paulo pos += sizeof(u16); 902f05cddf9SRui Paulo len -= sizeof(u16); 903f05cddf9SRui Paulo } 904f05cddf9SRui Paulo /* 905f05cddf9SRui Paulo * buffer and ACK the fragment 906f05cddf9SRui Paulo */ 907*780fb4a2SCy Schubert if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { 908f05cddf9SRui Paulo data->in_frag_pos += len; 909f05cddf9SRui Paulo if (data->in_frag_pos > wpabuf_size(data->inbuf)) { 910f05cddf9SRui Paulo wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " 911f05cddf9SRui Paulo "detected (%d vs. %d)!", 912f05cddf9SRui Paulo (int) data->in_frag_pos, 913f05cddf9SRui Paulo (int) wpabuf_len(data->inbuf)); 914f05cddf9SRui Paulo wpabuf_free(data->inbuf); 9155b9c547cSRui Paulo data->inbuf = NULL; 916f05cddf9SRui Paulo data->in_frag_pos = 0; 917f05cddf9SRui Paulo return NULL; 918f05cddf9SRui Paulo } 919f05cddf9SRui Paulo wpabuf_put_data(data->inbuf, pos, len); 920*780fb4a2SCy Schubert } 921*780fb4a2SCy Schubert if (EAP_PWD_GET_MORE_BIT(lm_exch)) { 922f05cddf9SRui Paulo resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, 923f05cddf9SRui Paulo EAP_PWD_HDR_SIZE, 924f05cddf9SRui Paulo EAP_CODE_RESPONSE, eap_get_id(reqData)); 925f05cddf9SRui Paulo if (resp != NULL) 926f05cddf9SRui Paulo wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); 927f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", 928f05cddf9SRui Paulo (int) len); 929f05cddf9SRui Paulo return resp; 930f05cddf9SRui Paulo } 931f05cddf9SRui Paulo /* 932f05cddf9SRui Paulo * we're buffering and this is the last fragment 933f05cddf9SRui Paulo */ 934f05cddf9SRui Paulo if (data->in_frag_pos) { 935f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", 936f05cddf9SRui Paulo (int) 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 */ 979325151a3SRui 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 1058f05cddf9SRui Paulo eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1059f05cddf9SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); 1060f05cddf9SRui Paulo if (eap == NULL) 1061f05cddf9SRui Paulo return -1; 1062f05cddf9SRui Paulo 1063f05cddf9SRui Paulo eap->init = eap_pwd_init; 1064f05cddf9SRui Paulo eap->deinit = eap_pwd_deinit; 1065f05cddf9SRui Paulo eap->process = eap_pwd_process; 1066f05cddf9SRui Paulo eap->isKeyAvailable = eap_pwd_key_available; 1067f05cddf9SRui Paulo eap->getKey = eap_pwd_getkey; 10685b9c547cSRui Paulo eap->getSessionId = eap_pwd_get_session_id; 1069f05cddf9SRui Paulo eap->get_emsk = eap_pwd_get_emsk; 1070f05cddf9SRui Paulo 1071*780fb4a2SCy Schubert return eap_peer_method_register(eap); 1072f05cddf9SRui Paulo } 1073