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