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,
89912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt			     const u8 *password, size_t password_len,
90912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt			     const u8 *id_server, size_t id_server_len,
91912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt			     const u8 *id_peer, size_t id_peer_len,
92912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt			     const u8 *token)
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
9504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct crypto_hash *hash;
9604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
9704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int nid, is_odd, ret = 0;
9804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t primebytelen, primebitlen;
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	switch (num) { /* from IANA registry for IKE D-H groups */
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        case 19:
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		nid = NID_X9_62_prime256v1;
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        case 20:
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		nid = NID_secp384r1;
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        case 21:
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		nid = NID_secp521r1;
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
1109ead16e203b81d44a2d84eadc2901ceeb7daf805Dmitry Shmidt#ifndef OPENSSL_IS_BORINGSSL
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        case 25:
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		nid = NID_X9_62_prime192v1;
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
1149ead16e203b81d44a2d84eadc2901ceeb7daf805Dmitry Shmidt#endif /* OPENSSL_IS_BORINGSSL */
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        case 26:
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		nid = NID_secp224r1;
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        default:
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	grp->pwe = NULL;
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	grp->order = NULL;
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	grp->prime = NULL;
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (((rnd = BN_new()) == NULL) ||
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    ((cofactor = BN_new()) == NULL) ||
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    ((grp->order = BN_new()) == NULL) ||
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    ((grp->prime = BN_new()) == NULL) ||
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    ((x_candidate = BN_new()) == NULL)) {
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	{
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "curve");
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "curve");
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	primebitlen = BN_num_bits(grp->prime);
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	primebytelen = BN_num_bytes(grp->prime);
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if ((prfbuf = os_malloc(primebytelen)) == NULL) {
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "buffer");
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memset(prfbuf, 0, primebytelen);
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	ctr = 0;
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (1) {
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (ctr > 30) {
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   "point on curve for group %d, something's "
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   "fishy", num);
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto fail;
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		ctr++;
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/*
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * compute counter-mode password value and stretch to prime
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 *    pwd-seed = H(token | peer-id | server-id | password |
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 *		   counter)
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
18004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		hash = eap_pwd_h_init();
18104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hash == NULL)
18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_update(hash, token, sizeof(u32));
18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_update(hash, id_peer, id_peer_len);
18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_update(hash, id_server, id_server_len);
18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_update(hash, password, password_len);
18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eap_pwd_h_final(hash, pwe_digest);
18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				(u8 *) "EAP-pwd Hunting And Pecking",
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				os_strlen("EAP-pwd Hunting And Pecking"),
19504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				prfbuf, primebitlen) < 0)
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		BN_bin2bn(prfbuf, primebytelen, x_candidate);
1991f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2001f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		/*
2011f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 * eap_pwd_kdf() returns a string of bits 0..primebitlen but
2021f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 * BN_bin2bn will treat that string of bits as a big endian
2031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 * number. If the primebitlen is not an even multiple of 8
2041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 * then excessive bits-- those _after_ primebitlen-- so now
2051f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 * we have to shift right the amount we masked off.
2061f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		 */
2071f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt		if (primebitlen % 8)
2081f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt			BN_rshift(x_candidate, x_candidate,
2091f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt				  (8 - (primebitlen % 8)));
2101f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (BN_ucmp(x_candidate, grp->prime) >= 0)
2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			continue;
2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    prfbuf, primebytelen);
2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/*
2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * need to unambiguously identify the solution, if there is
2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * one...
2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
2218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (BN_is_odd(rnd))
2228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			is_odd = 1;
2238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		else
2248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			is_odd = 0;
2258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/*
2278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * solve the quadratic equation, if it's not solvable then we
2288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * don't have a point
2298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
2308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
2318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt							     grp->pwe,
2328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt							     x_candidate,
2338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt							     is_odd, NULL))
2348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			continue;
2358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/*
2368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * If there's a solution to the equation then the point must be
2378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * on the curve so why check again explicitly? OpenSSL code
2388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * says this is required by X9.62. We're not X9.62 but it can't
2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * hurt just to be sure.
2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
2418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
2428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
2438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			continue;
2448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
2458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (BN_cmp(cofactor, BN_value_one())) {
2478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* make sure the point is not in a small sub-group */
2488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
2498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					  cofactor, NULL)) {
2508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				wpa_printf(MSG_INFO, "EAP-pwd: cannot "
2518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					   "multiply generator by order");
2528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				continue;
2538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
2548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
2558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				wpa_printf(MSG_INFO, "EAP-pwd: point is at "
2568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					   "infinity");
2578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				continue;
2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* if we got here then we have a new generator. */
2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
2648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	grp->group_num = num;
2658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (0) {
2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt fail:
2678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		EC_GROUP_free(grp->group);
26804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		grp->group = NULL;
2697f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt		EC_POINT_clear_free(grp->pwe);
27004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		grp->pwe = NULL;
2717f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt		BN_clear_free(grp->order);
27204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		grp->order = NULL;
2737f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt		BN_clear_free(grp->prime);
27404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		grp->prime = NULL;
2758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		ret = 1;
2768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* cleanliness and order.... */
2787f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt	BN_clear_free(cofactor);
2797f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt	BN_clear_free(x_candidate);
2807f0b69e88015ca077ef7a417fde0a76c10df23a5Dmitry Shmidt	BN_clear_free(rnd);
2818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(prfbuf);
2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return ret;
2848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
2858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
287912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidtint compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
288912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
289912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt		 const u8 *confirm_peer, const u8 *confirm_server,
290912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
2918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
29204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct crypto_hash *hash;
29304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 mk[SHA256_MAC_LEN], *cruft;
2948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
2951f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	int offset;
2968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
2988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return -1;
2998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/*
3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
3028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 *	scal_s)
3038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
3048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	session_id[0] = EAP_TYPE_PWD;
30504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hash = eap_pwd_h_init();
30604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hash == NULL) {
30704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		os_free(cruft);
30804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
30904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
310912c6ecf72fb2c84fbf17dbd0666492778dbd9fcDmitry Shmidt	eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
3111f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
3121f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	os_memset(cruft, 0, BN_num_bytes(grp->prime));
3131f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	BN_bn2bin(peer_scalar, cruft + offset);
31404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
3151f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
3161f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	os_memset(cruft, 0, BN_num_bytes(grp->prime));
3171f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	BN_bn2bin(server_scalar, cruft + offset);
31804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
31904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_final(hash, &session_id[1]);
3208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
321c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt	/* then compute MK = H(k | confirm-peer | confirm-server) */
32204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hash = eap_pwd_h_init();
32304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (hash == NULL) {
32404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		os_free(cruft);
32504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
32604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
3271f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
3288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memset(cruft, 0, BN_num_bytes(grp->prime));
3291f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt	BN_bn2bin(k, cruft + offset);
33004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
33104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(cruft);
33204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
33304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
33404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	eap_pwd_h_final(hash, mk);
3358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* stretch the mk with the session-id to get MSK | EMSK */
33704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
33804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			session_id, SHA256_MAC_LEN + 1,
33904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
34004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
34104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
3428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
3448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
3458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 1;
3478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
348