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