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