eap_pwd_common.c 9.0 KB

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