1252190Srpaulo/* 2252190Srpaulo * EAP server/peer: EAP-pwd shared routines 3252190Srpaulo * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo#include "common.h" 11252190Srpaulo#include "crypto/sha256.h" 12252190Srpaulo#include "crypto/crypto.h" 13252190Srpaulo#include "eap_defs.h" 14252190Srpaulo#include "eap_pwd_common.h" 15252190Srpaulo 16252190Srpaulo/* The random function H(x) = HMAC-SHA256(0^32, x) */ 17252190Srpaulostruct crypto_hash * eap_pwd_h_init(void) 18252190Srpaulo{ 19252190Srpaulo u8 allzero[SHA256_MAC_LEN]; 20252190Srpaulo os_memset(allzero, 0, SHA256_MAC_LEN); 21252190Srpaulo return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, 22252190Srpaulo SHA256_MAC_LEN); 23252190Srpaulo} 24252190Srpaulo 25252190Srpaulo 26252190Srpaulovoid eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) 27252190Srpaulo{ 28252190Srpaulo crypto_hash_update(hash, data, len); 29252190Srpaulo} 30252190Srpaulo 31252190Srpaulo 32252190Srpaulovoid eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) 33252190Srpaulo{ 34252190Srpaulo size_t len = SHA256_MAC_LEN; 35252190Srpaulo crypto_hash_finish(hash, digest, &len); 36252190Srpaulo} 37252190Srpaulo 38252190Srpaulo 39252190Srpaulo/* a counter-based KDF based on NIST SP800-108 */ 40252190Srpaulostatic int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, 41252190Srpaulo size_t labellen, u8 *result, size_t resultbitlen) 42252190Srpaulo{ 43252190Srpaulo struct crypto_hash *hash; 44252190Srpaulo u8 digest[SHA256_MAC_LEN]; 45252190Srpaulo u16 i, ctr, L; 46252190Srpaulo size_t resultbytelen, len = 0, mdlen; 47252190Srpaulo 48252190Srpaulo resultbytelen = (resultbitlen + 7) / 8; 49252190Srpaulo ctr = 0; 50252190Srpaulo L = htons(resultbitlen); 51252190Srpaulo while (len < resultbytelen) { 52252190Srpaulo ctr++; 53252190Srpaulo i = htons(ctr); 54252190Srpaulo hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, 55252190Srpaulo key, keylen); 56252190Srpaulo if (hash == NULL) 57252190Srpaulo return -1; 58252190Srpaulo if (ctr > 1) 59252190Srpaulo crypto_hash_update(hash, digest, SHA256_MAC_LEN); 60252190Srpaulo crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); 61252190Srpaulo crypto_hash_update(hash, label, labellen); 62252190Srpaulo crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); 63252190Srpaulo mdlen = SHA256_MAC_LEN; 64252190Srpaulo if (crypto_hash_finish(hash, digest, &mdlen) < 0) 65252190Srpaulo return -1; 66252190Srpaulo if ((len + mdlen) > resultbytelen) 67252190Srpaulo os_memcpy(result + len, digest, resultbytelen - len); 68252190Srpaulo else 69252190Srpaulo os_memcpy(result + len, digest, mdlen); 70252190Srpaulo len += mdlen; 71252190Srpaulo } 72252190Srpaulo 73252190Srpaulo /* since we're expanding to a bit length, mask off the excess */ 74252190Srpaulo if (resultbitlen % 8) { 75252190Srpaulo u8 mask = 0xff; 76252190Srpaulo mask <<= (8 - (resultbitlen % 8)); 77252190Srpaulo result[resultbytelen - 1] &= mask; 78252190Srpaulo } 79252190Srpaulo 80252190Srpaulo return 0; 81252190Srpaulo} 82252190Srpaulo 83252190Srpaulo 84252190Srpaulo/* 85252190Srpaulo * compute a "random" secret point on an elliptic curve based 86252190Srpaulo * on the password and identities. 87252190Srpaulo */ 88252190Srpauloint compute_password_element(EAP_PWD_group *grp, u16 num, 89252190Srpaulo u8 *password, int password_len, 90252190Srpaulo u8 *id_server, int id_server_len, 91252190Srpaulo u8 *id_peer, int id_peer_len, u8 *token) 92252190Srpaulo{ 93252190Srpaulo BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; 94252190Srpaulo struct crypto_hash *hash; 95252190Srpaulo unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; 96252190Srpaulo int nid, is_odd, ret = 0; 97252190Srpaulo size_t primebytelen, primebitlen; 98252190Srpaulo 99252190Srpaulo switch (num) { /* from IANA registry for IKE D-H groups */ 100252190Srpaulo case 19: 101252190Srpaulo nid = NID_X9_62_prime256v1; 102252190Srpaulo break; 103252190Srpaulo case 20: 104252190Srpaulo nid = NID_secp384r1; 105252190Srpaulo break; 106252190Srpaulo case 21: 107252190Srpaulo nid = NID_secp521r1; 108252190Srpaulo break; 109252190Srpaulo case 25: 110252190Srpaulo nid = NID_X9_62_prime192v1; 111252190Srpaulo break; 112252190Srpaulo case 26: 113252190Srpaulo nid = NID_secp224r1; 114252190Srpaulo break; 115252190Srpaulo default: 116252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); 117252190Srpaulo return -1; 118252190Srpaulo } 119252190Srpaulo 120252190Srpaulo grp->pwe = NULL; 121252190Srpaulo grp->order = NULL; 122252190Srpaulo grp->prime = NULL; 123252190Srpaulo 124252190Srpaulo if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { 125252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); 126252190Srpaulo goto fail; 127252190Srpaulo } 128252190Srpaulo 129252190Srpaulo if (((rnd = BN_new()) == NULL) || 130252190Srpaulo ((cofactor = BN_new()) == NULL) || 131252190Srpaulo ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || 132252190Srpaulo ((grp->order = BN_new()) == NULL) || 133252190Srpaulo ((grp->prime = BN_new()) == NULL) || 134252190Srpaulo ((x_candidate = BN_new()) == NULL)) { 135252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); 136252190Srpaulo goto fail; 137252190Srpaulo } 138252190Srpaulo 139252190Srpaulo if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) 140252190Srpaulo { 141252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " 142252190Srpaulo "curve"); 143252190Srpaulo goto fail; 144252190Srpaulo } 145252190Srpaulo if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { 146252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); 147252190Srpaulo goto fail; 148252190Srpaulo } 149252190Srpaulo if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { 150252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " 151252190Srpaulo "curve"); 152252190Srpaulo goto fail; 153252190Srpaulo } 154252190Srpaulo primebitlen = BN_num_bits(grp->prime); 155252190Srpaulo primebytelen = BN_num_bytes(grp->prime); 156252190Srpaulo if ((prfbuf = os_malloc(primebytelen)) == NULL) { 157252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " 158252190Srpaulo "buffer"); 159252190Srpaulo goto fail; 160252190Srpaulo } 161252190Srpaulo os_memset(prfbuf, 0, primebytelen); 162252190Srpaulo ctr = 0; 163252190Srpaulo while (1) { 164252190Srpaulo if (ctr > 30) { 165252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " 166252190Srpaulo "point on curve for group %d, something's " 167252190Srpaulo "fishy", num); 168252190Srpaulo goto fail; 169252190Srpaulo } 170252190Srpaulo ctr++; 171252190Srpaulo 172252190Srpaulo /* 173252190Srpaulo * compute counter-mode password value and stretch to prime 174252190Srpaulo * pwd-seed = H(token | peer-id | server-id | password | 175252190Srpaulo * counter) 176252190Srpaulo */ 177252190Srpaulo hash = eap_pwd_h_init(); 178252190Srpaulo if (hash == NULL) 179252190Srpaulo goto fail; 180252190Srpaulo eap_pwd_h_update(hash, token, sizeof(u32)); 181252190Srpaulo eap_pwd_h_update(hash, id_peer, id_peer_len); 182252190Srpaulo eap_pwd_h_update(hash, id_server, id_server_len); 183252190Srpaulo eap_pwd_h_update(hash, password, password_len); 184252190Srpaulo eap_pwd_h_update(hash, &ctr, sizeof(ctr)); 185252190Srpaulo eap_pwd_h_final(hash, pwe_digest); 186252190Srpaulo 187252190Srpaulo BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); 188252190Srpaulo 189252190Srpaulo if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, 190252190Srpaulo (u8 *) "EAP-pwd Hunting And Pecking", 191252190Srpaulo os_strlen("EAP-pwd Hunting And Pecking"), 192252190Srpaulo prfbuf, primebitlen) < 0) 193252190Srpaulo goto fail; 194252190Srpaulo 195252190Srpaulo BN_bin2bn(prfbuf, primebytelen, x_candidate); 196252190Srpaulo 197252190Srpaulo /* 198252190Srpaulo * eap_pwd_kdf() returns a string of bits 0..primebitlen but 199252190Srpaulo * BN_bin2bn will treat that string of bits as a big endian 200252190Srpaulo * number. If the primebitlen is not an even multiple of 8 201252190Srpaulo * then excessive bits-- those _after_ primebitlen-- so now 202252190Srpaulo * we have to shift right the amount we masked off. 203252190Srpaulo */ 204252190Srpaulo if (primebitlen % 8) 205252190Srpaulo BN_rshift(x_candidate, x_candidate, 206252190Srpaulo (8 - (primebitlen % 8))); 207252190Srpaulo 208252190Srpaulo if (BN_ucmp(x_candidate, grp->prime) >= 0) 209252190Srpaulo continue; 210252190Srpaulo 211252190Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", 212252190Srpaulo prfbuf, primebytelen); 213252190Srpaulo 214252190Srpaulo /* 215252190Srpaulo * need to unambiguously identify the solution, if there is 216252190Srpaulo * one... 217252190Srpaulo */ 218252190Srpaulo if (BN_is_odd(rnd)) 219252190Srpaulo is_odd = 1; 220252190Srpaulo else 221252190Srpaulo is_odd = 0; 222252190Srpaulo 223252190Srpaulo /* 224252190Srpaulo * solve the quadratic equation, if it's not solvable then we 225252190Srpaulo * don't have a point 226252190Srpaulo */ 227252190Srpaulo if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, 228252190Srpaulo grp->pwe, 229252190Srpaulo x_candidate, 230252190Srpaulo is_odd, NULL)) 231252190Srpaulo continue; 232252190Srpaulo /* 233252190Srpaulo * If there's a solution to the equation then the point must be 234252190Srpaulo * on the curve so why check again explicitly? OpenSSL code 235252190Srpaulo * says this is required by X9.62. We're not X9.62 but it can't 236252190Srpaulo * hurt just to be sure. 237252190Srpaulo */ 238252190Srpaulo if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { 239252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); 240252190Srpaulo continue; 241252190Srpaulo } 242252190Srpaulo 243252190Srpaulo if (BN_cmp(cofactor, BN_value_one())) { 244252190Srpaulo /* make sure the point is not in a small sub-group */ 245252190Srpaulo if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, 246252190Srpaulo cofactor, NULL)) { 247252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: cannot " 248252190Srpaulo "multiply generator by order"); 249252190Srpaulo continue; 250252190Srpaulo } 251252190Srpaulo if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { 252252190Srpaulo wpa_printf(MSG_INFO, "EAP-pwd: point is at " 253252190Srpaulo "infinity"); 254252190Srpaulo continue; 255252190Srpaulo } 256252190Srpaulo } 257252190Srpaulo /* if we got here then we have a new generator. */ 258252190Srpaulo break; 259252190Srpaulo } 260252190Srpaulo wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); 261252190Srpaulo grp->group_num = num; 262252190Srpaulo if (0) { 263252190Srpaulo fail: 264252190Srpaulo EC_GROUP_free(grp->group); 265252190Srpaulo grp->group = NULL; 266252190Srpaulo EC_POINT_free(grp->pwe); 267252190Srpaulo grp->pwe = NULL; 268252190Srpaulo BN_free(grp->order); 269252190Srpaulo grp->order = NULL; 270252190Srpaulo BN_free(grp->prime); 271252190Srpaulo grp->prime = NULL; 272252190Srpaulo ret = 1; 273252190Srpaulo } 274252190Srpaulo /* cleanliness and order.... */ 275252190Srpaulo BN_free(cofactor); 276252190Srpaulo BN_free(x_candidate); 277252190Srpaulo BN_free(rnd); 278252190Srpaulo os_free(prfbuf); 279252190Srpaulo 280252190Srpaulo return ret; 281252190Srpaulo} 282252190Srpaulo 283252190Srpaulo 284252190Srpauloint compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, 285252190Srpaulo BIGNUM *peer_scalar, BIGNUM *server_scalar, 286252190Srpaulo u8 *confirm_peer, u8 *confirm_server, 287252190Srpaulo u32 *ciphersuite, u8 *msk, u8 *emsk) 288252190Srpaulo{ 289252190Srpaulo struct crypto_hash *hash; 290252190Srpaulo u8 mk[SHA256_MAC_LEN], *cruft; 291252190Srpaulo u8 session_id[SHA256_MAC_LEN + 1]; 292252190Srpaulo u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; 293252190Srpaulo int offset; 294252190Srpaulo 295252190Srpaulo if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) 296252190Srpaulo return -1; 297252190Srpaulo 298252190Srpaulo /* 299252190Srpaulo * first compute the session-id = TypeCode | H(ciphersuite | scal_p | 300252190Srpaulo * scal_s) 301252190Srpaulo */ 302252190Srpaulo session_id[0] = EAP_TYPE_PWD; 303252190Srpaulo hash = eap_pwd_h_init(); 304252190Srpaulo if (hash == NULL) { 305252190Srpaulo os_free(cruft); 306252190Srpaulo return -1; 307252190Srpaulo } 308252190Srpaulo eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); 309252190Srpaulo offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); 310252190Srpaulo os_memset(cruft, 0, BN_num_bytes(grp->prime)); 311252190Srpaulo BN_bn2bin(peer_scalar, cruft + offset); 312252190Srpaulo eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); 313252190Srpaulo offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); 314252190Srpaulo os_memset(cruft, 0, BN_num_bytes(grp->prime)); 315252190Srpaulo BN_bn2bin(server_scalar, cruft + offset); 316252190Srpaulo eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); 317252190Srpaulo eap_pwd_h_final(hash, &session_id[1]); 318252190Srpaulo 319252190Srpaulo /* then compute MK = H(k | confirm-peer | confirm-server) */ 320252190Srpaulo hash = eap_pwd_h_init(); 321252190Srpaulo if (hash == NULL) { 322252190Srpaulo os_free(cruft); 323252190Srpaulo return -1; 324252190Srpaulo } 325252190Srpaulo offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); 326252190Srpaulo os_memset(cruft, 0, BN_num_bytes(grp->prime)); 327252190Srpaulo BN_bn2bin(k, cruft + offset); 328252190Srpaulo eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); 329252190Srpaulo os_free(cruft); 330252190Srpaulo eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); 331252190Srpaulo eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); 332252190Srpaulo eap_pwd_h_final(hash, mk); 333252190Srpaulo 334252190Srpaulo /* stretch the mk with the session-id to get MSK | EMSK */ 335252190Srpaulo if (eap_pwd_kdf(mk, SHA256_MAC_LEN, 336252190Srpaulo session_id, SHA256_MAC_LEN + 1, 337252190Srpaulo msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { 338252190Srpaulo return -1; 339252190Srpaulo } 340252190Srpaulo 341252190Srpaulo os_memcpy(msk, msk_emsk, EAP_MSK_LEN); 342252190Srpaulo os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); 343252190Srpaulo 344252190Srpaulo return 1; 345252190Srpaulo} 346