18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * EAP server/peer: EAP-pwd shared routines 38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> 48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * 5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * This software may be distributed under the terms of the BSD license. 6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * See README for more details. 78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h" 108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h" 1104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "crypto/sha256.h" 1204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "crypto/crypto.h" 138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eap_defs.h" 148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eap_pwd_common.h" 158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* The random function H(x) = HMAC-SHA256(0^32, x) */ 1704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct crypto_hash * eap_pwd_h_init(void) 188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 1904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt u8 allzero[SHA256_MAC_LEN]; 2004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt os_memset(allzero, 0, SHA256_MAC_LEN); 2104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, 2204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt SHA256_MAC_LEN); 238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) 278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 2804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_update(hash, data, len); 298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 3204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) 338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt size_t len = SHA256_MAC_LEN; 3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_finish(hash, digest, &len); 368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* a counter-based KDF based on NIST SP800-108 */ 4004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, 4104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt size_t labellen, u8 *result, size_t resultbitlen) 428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 4304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt struct crypto_hash *hash; 4404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt u8 digest[SHA256_MAC_LEN]; 458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u16 i, ctr, L; 4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt size_t resultbytelen, len = 0, mdlen; 478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 4804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt resultbytelen = (resultbitlen + 7) / 8; 498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ctr = 0; 508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt L = htons(resultbitlen); 518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (len < resultbytelen) { 5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt ctr++; 5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt i = htons(ctr); 5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, 5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt key, keylen); 5604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (hash == NULL) 5704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return -1; 588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (ctr > 1) 5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_update(hash, digest, SHA256_MAC_LEN); 6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); 6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_update(hash, label, labellen); 6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); 6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt mdlen = SHA256_MAC_LEN; 6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (crypto_hash_finish(hash, digest, &mdlen) < 0) 6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return -1; 6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if ((len + mdlen) > resultbytelen) 678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memcpy(result + len, digest, resultbytelen - len); 688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt else 698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memcpy(result + len, digest, mdlen); 708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt len += mdlen; 718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* since we're expanding to a bit length, mask off the excess */ 748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (resultbitlen % 8) { 7504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt u8 mask = 0xff; 761f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt mask <<= (8 - (resultbitlen % 8)); 771f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt result[resultbytelen - 1] &= mask; 788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 7904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt 8004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return 0; 818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* 858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * compute a "random" secret point on an elliptic curve based 868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * on the password and identities. 878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint compute_password_element(EAP_PWD_group *grp, u16 num, 898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u8 *password, int password_len, 908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u8 *id_server, int id_server_len, 918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u8 *id_peer, int id_peer_len, u8 *token) 928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; 9404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt struct crypto_hash *hash; 9504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; 9604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt int nid, is_odd, ret = 0; 9704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt size_t primebytelen, primebitlen; 988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt switch (num) { /* from IANA registry for IKE D-H groups */ 1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case 19: 1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt nid = NID_X9_62_prime256v1; 1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case 20: 1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt nid = NID_secp384r1; 1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case 21: 1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt nid = NID_secp521r1; 1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case 25: 1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt nid = NID_X9_62_prime192v1; 1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt case 26: 1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt nid = NID_secp224r1; 1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt default: 1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); 1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return -1; 1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt grp->pwe = NULL; 1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt grp->order = NULL; 1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt grp->prime = NULL; 1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { 1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); 1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (((rnd = BN_new()) == NULL) || 1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((cofactor = BN_new()) == NULL) || 1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || 1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((grp->order = BN_new()) == NULL) || 1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((grp->prime = BN_new()) == NULL) || 1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ((x_candidate = BN_new()) == NULL)) { 1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); 1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) 1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt { 1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " 1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "curve"); 1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { 1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); 1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { 1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " 1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "curve"); 1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt primebitlen = BN_num_bits(grp->prime); 1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt primebytelen = BN_num_bytes(grp->prime); 1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if ((prfbuf = os_malloc(primebytelen)) == NULL) { 1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " 1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "buffer"); 1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memset(prfbuf, 0, primebytelen); 1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ctr = 0; 1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt while (1) { 16404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (ctr > 30) { 1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " 1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "point on curve for group %d, something's " 1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "fishy", num); 1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt goto fail; 1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ctr++; 1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * compute counter-mode password value and stretch to prime 1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * pwd-seed = H(token | peer-id | server-id | password | 1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * counter) 1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 17704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt hash = eap_pwd_h_init(); 17804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (hash == NULL) 17904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt goto fail; 18004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, token, sizeof(u32)); 18104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, id_peer, id_peer_len); 18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, id_server, id_server_len); 18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, password, password_len); 18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, &ctr, sizeof(ctr)); 18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_final(hash, pwe_digest); 18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt 18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); 18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt 18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, 19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt (u8 *) "EAP-pwd Hunting And Pecking", 19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt os_strlen("EAP-pwd Hunting And Pecking"), 19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt prfbuf, primebitlen) < 0) 19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt goto fail; 1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_bin2bn(prfbuf, primebytelen, x_candidate); 1961f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt 1971f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt /* 1981f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt * eap_pwd_kdf() returns a string of bits 0..primebitlen but 1991f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt * BN_bin2bn will treat that string of bits as a big endian 2001f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt * number. If the primebitlen is not an even multiple of 8 2011f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt * then excessive bits-- those _after_ primebitlen-- so now 2021f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt * we have to shift right the amount we masked off. 2031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt */ 2041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt if (primebitlen % 8) 2051f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt BN_rshift(x_candidate, x_candidate, 2061f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt (8 - (primebitlen % 8))); 2071f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt 2088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (BN_ucmp(x_candidate, grp->prime) >= 0) 2098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt continue; 2108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", 2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt prfbuf, primebytelen); 2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * need to unambiguously identify the solution, if there is 2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * one... 2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (BN_is_odd(rnd)) 2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt is_odd = 1; 2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt else 2218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt is_odd = 0; 2228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 2248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * solve the quadratic equation, if it's not solvable then we 2258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * don't have a point 2268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 2278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, 2288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt grp->pwe, 2298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt x_candidate, 2308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt is_odd, NULL)) 2318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt continue; 2328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 2338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If there's a solution to the equation then the point must be 2348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * on the curve so why check again explicitly? OpenSSL code 2358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * says this is required by X9.62. We're not X9.62 but it can't 2368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * hurt just to be sure. 2378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 2388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { 2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); 2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt continue; 2418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (BN_cmp(cofactor, BN_value_one())) { 2448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* make sure the point is not in a small sub-group */ 2458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, 2468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt cofactor, NULL)) { 2478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: cannot " 2488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "multiply generator by order"); 2498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt continue; 2508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { 2528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_INFO, "EAP-pwd: point is at " 2538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt "infinity"); 2548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt continue; 2558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* if we got here then we have a new generator. */ 2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt break; 2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); 2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt grp->group_num = num; 2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if (0) { 2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fail: 2648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt EC_GROUP_free(grp->group); 26504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt grp->group = NULL; 2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt EC_POINT_free(grp->pwe); 26704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt grp->pwe = NULL; 2688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_free(grp->order); 26904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt grp->order = NULL; 2708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_free(grp->prime); 27104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt grp->prime = NULL; 2728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt ret = 1; 2738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt } 2748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* cleanliness and order.... */ 2758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_free(cofactor); 2768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_free(x_candidate); 2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BN_free(rnd); 2788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_free(prfbuf); 2798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return ret; 2818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, 2858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt BIGNUM *peer_scalar, BIGNUM *server_scalar, 286c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt u8 *confirm_peer, u8 *confirm_server, 2878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u32 *ciphersuite, u8 *msk, u8 *emsk) 2888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{ 28904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt struct crypto_hash *hash; 29004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt u8 mk[SHA256_MAC_LEN], *cruft; 29104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt u8 session_id[SHA256_MAC_LEN + 1]; 2928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; 2931f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt int offset; 2948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) 2968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return -1; 2978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 2988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* 2998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * first compute the session-id = TypeCode | H(ciphersuite | scal_p | 3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * scal_s) 3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */ 3028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt session_id[0] = EAP_TYPE_PWD; 30304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt hash = eap_pwd_h_init(); 30404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (hash == NULL) { 30504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt os_free(cruft); 30604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return -1; 30704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt } 30804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); 3091f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); 3101f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt os_memset(cruft, 0, BN_num_bytes(grp->prime)); 3111f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt BN_bn2bin(peer_scalar, cruft + offset); 31204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); 3131f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); 3141f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt os_memset(cruft, 0, BN_num_bytes(grp->prime)); 3151f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt BN_bn2bin(server_scalar, cruft + offset); 31604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); 31704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_final(hash, &session_id[1]); 3188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 319c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt /* then compute MK = H(k | confirm-peer | confirm-server) */ 32004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt hash = eap_pwd_h_init(); 32104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (hash == NULL) { 32204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt os_free(cruft); 32304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return -1; 32404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt } 3251f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); 3268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memset(cruft, 0, BN_num_bytes(grp->prime)); 3271f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt BN_bn2bin(k, cruft + offset); 32804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); 32904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt os_free(cruft); 33004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); 33104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); 33204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt eap_pwd_h_final(hash, mk); 3338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 3348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt /* stretch the mk with the session-id to get MSK | EMSK */ 33504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt if (eap_pwd_kdf(mk, SHA256_MAC_LEN, 33604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt session_id, SHA256_MAC_LEN + 1, 33704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { 33804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt return -1; 33904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt } 3408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 3418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memcpy(msk, msk_emsk, EAP_MSK_LEN); 3428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); 3438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt 3448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt return 1; 3458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} 346