Home | History | Annotate | Download | only in eap_common
      1 /*
      2  * EAP server/peer: EAP-pwd shared routines
      3  * Copyright (c) 2010, Dan Harkins <dharkins (at) lounge.org>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the BSD license.
      7  *
      8  * Alternatively, this software may be distributed under the terms of the
      9  * GNU General Public License version 2 as published by the Free Software
     10  * Foundation.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #include "includes.h"
     16 #include "common.h"
     17 #include "eap_defs.h"
     18 #include "eap_pwd_common.h"
     19 
     20 /* The random function H(x) = HMAC-SHA256(0^32, x) */
     21 void H_Init(HMAC_CTX *ctx)
     22 {
     23 	u8 allzero[SHA256_DIGEST_LENGTH];
     24 
     25 	os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
     26 	HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
     27 }
     28 
     29 
     30 void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
     31 {
     32 	HMAC_Update(ctx, data, len);
     33 }
     34 
     35 
     36 void H_Final(HMAC_CTX *ctx, u8 *digest)
     37 {
     38 	unsigned int mdlen = SHA256_DIGEST_LENGTH;
     39 
     40 	HMAC_Final(ctx, digest, &mdlen);
     41 	HMAC_CTX_cleanup(ctx);
     42 }
     43 
     44 
     45 /* a counter-based KDF based on NIST SP800-108 */
     46 void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
     47 		 u8 *result, int resultbitlen)
     48 {
     49 	HMAC_CTX hctx;
     50 	unsigned char digest[SHA256_DIGEST_LENGTH];
     51 	u16 i, ctr, L;
     52 	int resultbytelen, len = 0;
     53 	unsigned int mdlen = SHA256_DIGEST_LENGTH;
     54 	unsigned char mask = 0xff;
     55 
     56 	resultbytelen = (resultbitlen + 7)/8;
     57 	ctr = 0;
     58 	L = htons(resultbitlen);
     59 	while (len < resultbytelen) {
     60 		ctr++; i = htons(ctr);
     61 		HMAC_Init(&hctx, key, keylen, EVP_sha256());
     62 		if (ctr > 1)
     63 			HMAC_Update(&hctx, digest, mdlen);
     64 		HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
     65 		HMAC_Update(&hctx, label, labellen);
     66 		HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
     67 		HMAC_Final(&hctx, digest, &mdlen);
     68 		if ((len + (int) mdlen) > resultbytelen)
     69 			os_memcpy(result + len, digest, resultbytelen - len);
     70 		else
     71 			os_memcpy(result + len, digest, mdlen);
     72 		len += mdlen;
     73 		HMAC_CTX_cleanup(&hctx);
     74 	}
     75 
     76 	/* since we're expanding to a bit length, mask off the excess */
     77 	if (resultbitlen % 8) {
     78 		mask >>= ((resultbytelen * 8) - resultbitlen);
     79 		result[0] &= mask;
     80 	}
     81 }
     82 
     83 
     84 /*
     85  * compute a "random" secret point on an elliptic curve based
     86  * on the password and identities.
     87  */
     88 int compute_password_element(EAP_PWD_group *grp, u16 num,
     89 			     u8 *password, int password_len,
     90 			     u8 *id_server, int id_server_len,
     91 			     u8 *id_peer, int id_peer_len, u8 *token)
     92 {
     93 	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
     94 	HMAC_CTX ctx;
     95 	unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
     96 	int nid, is_odd, primebitlen, primebytelen, ret = 0;
     97 
     98 	switch (num) { /* from IANA registry for IKE D-H groups */
     99         case 19:
    100 		nid = NID_X9_62_prime256v1;
    101 		break;
    102         case 20:
    103 		nid = NID_secp384r1;
    104 		break;
    105         case 21:
    106 		nid = NID_secp521r1;
    107 		break;
    108         case 25:
    109 		nid = NID_X9_62_prime192v1;
    110 		break;
    111         case 26:
    112 		nid = NID_secp224r1;
    113 		break;
    114         default:
    115 		wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
    116 		return -1;
    117 	}
    118 
    119 	grp->pwe = NULL;
    120 	grp->order = NULL;
    121 	grp->prime = NULL;
    122 
    123 	if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
    124 		wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
    125 		goto fail;
    126 	}
    127 
    128 	if (((rnd = BN_new()) == NULL) ||
    129 	    ((cofactor = BN_new()) == NULL) ||
    130 	    ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
    131 	    ((grp->order = BN_new()) == NULL) ||
    132 	    ((grp->prime = BN_new()) == NULL) ||
    133 	    ((x_candidate = BN_new()) == NULL)) {
    134 		wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
    135 		goto fail;
    136 	}
    137 
    138 	if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
    139 	{
    140 		wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
    141 			   "curve");
    142 		goto fail;
    143 	}
    144 	if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
    145 		wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
    146 		goto fail;
    147 	}
    148 	if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
    149 		wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
    150 			   "curve");
    151 		goto fail;
    152 	}
    153 	primebitlen = BN_num_bits(grp->prime);
    154 	primebytelen = BN_num_bytes(grp->prime);
    155 	if ((prfbuf = os_malloc(primebytelen)) == NULL) {
    156 		wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
    157 			   "buffer");
    158 		goto fail;
    159 	}
    160 	os_memset(prfbuf, 0, primebytelen);
    161 	ctr = 0;
    162 	while (1) {
    163 		if (ctr > 10) {
    164 			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
    165 				   "point on curve for group %d, something's "
    166 				   "fishy", num);
    167 			goto fail;
    168 		}
    169 		ctr++;
    170 
    171 		/*
    172 		 * compute counter-mode password value and stretch to prime
    173 		 *    pwd-seed = H(token | peer-id | server-id | password |
    174 		 *		   counter)
    175 		 */
    176 		H_Init(&ctx);
    177 		H_Update(&ctx, token, sizeof(u32));
    178 		H_Update(&ctx, id_peer, id_peer_len);
    179 		H_Update(&ctx, id_server, id_server_len);
    180 		H_Update(&ctx, password, password_len);
    181 		H_Update(&ctx, &ctr, sizeof(ctr));
    182 		H_Final(&ctx, pwe_digest);
    183 
    184 		BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
    185 
    186 		eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
    187 			    (unsigned char *) "EAP-pwd Hunting And Pecking",
    188 			    os_strlen("EAP-pwd Hunting And Pecking"),
    189 			    prfbuf, primebitlen);
    190 
    191 		BN_bin2bn(prfbuf, primebytelen, x_candidate);
    192 		if (BN_ucmp(x_candidate, grp->prime) >= 0)
    193 			continue;
    194 
    195 		wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
    196 			    prfbuf, primebytelen);
    197 
    198 		/*
    199 		 * need to unambiguously identify the solution, if there is
    200 		 * one...
    201 		 */
    202 		if (BN_is_odd(rnd))
    203 			is_odd = 1;
    204 		else
    205 			is_odd = 0;
    206 
    207 		/*
    208 		 * solve the quadratic equation, if it's not solvable then we
    209 		 * don't have a point
    210 		 */
    211 		if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
    212 							     grp->pwe,
    213 							     x_candidate,
    214 							     is_odd, NULL))
    215 			continue;
    216 		/*
    217 		 * If there's a solution to the equation then the point must be
    218 		 * on the curve so why check again explicitly? OpenSSL code
    219 		 * says this is required by X9.62. We're not X9.62 but it can't
    220 		 * hurt just to be sure.
    221 		 */
    222 		if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
    223 			wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
    224 			continue;
    225 		}
    226 
    227 		if (BN_cmp(cofactor, BN_value_one())) {
    228 			/* make sure the point is not in a small sub-group */
    229 			if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
    230 					  cofactor, NULL)) {
    231 				wpa_printf(MSG_INFO, "EAP-pwd: cannot "
    232 					   "multiply generator by order");
    233 				continue;
    234 			}
    235 			if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
    236 				wpa_printf(MSG_INFO, "EAP-pwd: point is at "
    237 					   "infinity");
    238 				continue;
    239 			}
    240 		}
    241 		/* if we got here then we have a new generator. */
    242 		break;
    243 	}
    244 	wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
    245 	grp->group_num = num;
    246 	if (0) {
    247  fail:
    248 		EC_GROUP_free(grp->group);
    249 		EC_POINT_free(grp->pwe);
    250 		BN_free(grp->order);
    251 		BN_free(grp->prime);
    252 		os_free(grp);
    253 		grp = NULL;
    254 		ret = 1;
    255 	}
    256 	/* cleanliness and order.... */
    257 	BN_free(cofactor);
    258 	BN_free(x_candidate);
    259 	BN_free(rnd);
    260 	os_free(prfbuf);
    261 
    262 	return ret;
    263 }
    264 
    265 
    266 int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
    267 		 BIGNUM *peer_scalar, BIGNUM *server_scalar,
    268 		 u8 *commit_peer, u8 *commit_server,
    269 		 u32 *ciphersuite, u8 *msk, u8 *emsk)
    270 {
    271 	HMAC_CTX ctx;
    272 	u8 mk[SHA256_DIGEST_LENGTH], *cruft;
    273 	u8 session_id[SHA256_DIGEST_LENGTH + 1];
    274 	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
    275 
    276 	if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
    277 		return -1;
    278 
    279 	/*
    280 	 * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
    281 	 *	scal_s)
    282 	 */
    283 	session_id[0] = EAP_TYPE_PWD;
    284 	H_Init(&ctx);
    285 	H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
    286 	BN_bn2bin(peer_scalar, cruft);
    287 	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
    288 	BN_bn2bin(server_scalar, cruft);
    289 	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
    290 	H_Final(&ctx, &session_id[1]);
    291 
    292 	/* then compute MK = H(k | commit-peer | commit-server) */
    293 	H_Init(&ctx);
    294 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
    295 	BN_bn2bin(k, cruft);
    296 	H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
    297 	H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH);
    298 	H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH);
    299 	H_Final(&ctx, mk);
    300 
    301 	/* stretch the mk with the session-id to get MSK | EMSK */
    302 	eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
    303 		    session_id, SHA256_DIGEST_LENGTH+1,
    304 		    msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
    305 
    306 	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
    307 	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
    308 
    309 	os_free(cruft);
    310 
    311 	return 1;
    312 }
    313