]>
git.ipfire.org Git - thirdparty/hostap.git/blob - src/eap_common/eap_pwd_common.c
c32f9fb93012150bb8d0e2a73b1b0a89d6fc7884
2 * EAP server/peer: EAP-pwd shared routines
3 * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
12 #include "eap_pwd_common.h"
14 /* The random function H(x) = HMAC-SHA256(0^32, x) */
15 void H_Init(HMAC_CTX
*ctx
)
17 u8 allzero
[SHA256_DIGEST_LENGTH
];
19 os_memset(allzero
, 0, SHA256_DIGEST_LENGTH
);
20 HMAC_Init(ctx
, allzero
, SHA256_DIGEST_LENGTH
, EVP_sha256());
24 void H_Update(HMAC_CTX
*ctx
, const u8
*data
, int len
)
26 HMAC_Update(ctx
, data
, len
);
30 void H_Final(HMAC_CTX
*ctx
, u8
*digest
)
32 unsigned int mdlen
= SHA256_DIGEST_LENGTH
;
34 HMAC_Final(ctx
, digest
, &mdlen
);
35 HMAC_CTX_cleanup(ctx
);
39 /* a counter-based KDF based on NIST SP800-108 */
40 void eap_pwd_kdf(u8
*key
, int keylen
, u8
*label
, int labellen
,
41 u8
*result
, int resultbitlen
)
44 unsigned char digest
[SHA256_DIGEST_LENGTH
];
46 int resultbytelen
, len
= 0;
47 unsigned int mdlen
= SHA256_DIGEST_LENGTH
;
48 unsigned char mask
= 0xff;
50 resultbytelen
= (resultbitlen
+ 7)/8;
52 L
= htons(resultbitlen
);
53 while (len
< resultbytelen
) {
54 ctr
++; i
= htons(ctr
);
55 HMAC_Init(&hctx
, key
, keylen
, EVP_sha256());
57 HMAC_Update(&hctx
, digest
, mdlen
);
58 HMAC_Update(&hctx
, (u8
*) &i
, sizeof(u16
));
59 HMAC_Update(&hctx
, label
, labellen
);
60 HMAC_Update(&hctx
, (u8
*) &L
, sizeof(u16
));
61 HMAC_Final(&hctx
, digest
, &mdlen
);
62 if ((len
+ (int) mdlen
) > resultbytelen
)
63 os_memcpy(result
+ len
, digest
, resultbytelen
- len
);
65 os_memcpy(result
+ len
, digest
, mdlen
);
67 HMAC_CTX_cleanup(&hctx
);
70 /* since we're expanding to a bit length, mask off the excess */
71 if (resultbitlen
% 8) {
72 mask
<<= (8 - (resultbitlen
% 8));
73 result
[resultbytelen
- 1] &= mask
;
79 * compute a "random" secret point on an elliptic curve based
80 * on the password and identities.
82 int compute_password_element(EAP_PWD_group
*grp
, u16 num
,
83 u8
*password
, int password_len
,
84 u8
*id_server
, int id_server_len
,
85 u8
*id_peer
, int id_peer_len
, u8
*token
)
87 BIGNUM
*x_candidate
= NULL
, *rnd
= NULL
, *cofactor
= NULL
;
89 unsigned char pwe_digest
[SHA256_DIGEST_LENGTH
], *prfbuf
= NULL
, ctr
;
90 int nid
, is_odd
, primebitlen
, primebytelen
, ret
= 0;
92 switch (num
) { /* from IANA registry for IKE D-H groups */
94 nid
= NID_X9_62_prime256v1
;
103 nid
= NID_X9_62_prime192v1
;
109 wpa_printf(MSG_INFO
, "EAP-pwd: unsupported group %d", num
);
117 if ((grp
->group
= EC_GROUP_new_by_curve_name(nid
)) == NULL
) {
118 wpa_printf(MSG_INFO
, "EAP-pwd: unable to create EC_GROUP");
122 if (((rnd
= BN_new()) == NULL
) ||
123 ((cofactor
= BN_new()) == NULL
) ||
124 ((grp
->pwe
= EC_POINT_new(grp
->group
)) == NULL
) ||
125 ((grp
->order
= BN_new()) == NULL
) ||
126 ((grp
->prime
= BN_new()) == NULL
) ||
127 ((x_candidate
= BN_new()) == NULL
)) {
128 wpa_printf(MSG_INFO
, "EAP-pwd: unable to create bignums");
132 if (!EC_GROUP_get_curve_GFp(grp
->group
, grp
->prime
, NULL
, NULL
, NULL
))
134 wpa_printf(MSG_INFO
, "EAP-pwd: unable to get prime for GFp "
138 if (!EC_GROUP_get_order(grp
->group
, grp
->order
, NULL
)) {
139 wpa_printf(MSG_INFO
, "EAP-pwd: unable to get order for curve");
142 if (!EC_GROUP_get_cofactor(grp
->group
, cofactor
, NULL
)) {
143 wpa_printf(MSG_INFO
, "EAP-pwd: unable to get cofactor for "
147 primebitlen
= BN_num_bits(grp
->prime
);
148 primebytelen
= BN_num_bytes(grp
->prime
);
149 if ((prfbuf
= os_malloc(primebytelen
)) == NULL
) {
150 wpa_printf(MSG_INFO
, "EAP-pwd: unable to malloc space for prf "
154 os_memset(prfbuf
, 0, primebytelen
);
158 wpa_printf(MSG_INFO
, "EAP-pwd: unable to find random "
159 "point on curve for group %d, something's "
166 * compute counter-mode password value and stretch to prime
167 * pwd-seed = H(token | peer-id | server-id | password |
171 H_Update(&ctx
, token
, sizeof(u32
));
172 H_Update(&ctx
, id_peer
, id_peer_len
);
173 H_Update(&ctx
, id_server
, id_server_len
);
174 H_Update(&ctx
, password
, password_len
);
175 H_Update(&ctx
, &ctr
, sizeof(ctr
));
176 H_Final(&ctx
, pwe_digest
);
178 BN_bin2bn(pwe_digest
, SHA256_DIGEST_LENGTH
, rnd
);
180 eap_pwd_kdf(pwe_digest
, SHA256_DIGEST_LENGTH
,
181 (unsigned char *) "EAP-pwd Hunting And Pecking",
182 os_strlen("EAP-pwd Hunting And Pecking"),
183 prfbuf
, primebitlen
);
185 BN_bin2bn(prfbuf
, primebytelen
, x_candidate
);
188 * eap_pwd_kdf() returns a string of bits 0..primebitlen but
189 * BN_bin2bn will treat that string of bits as a big endian
190 * number. If the primebitlen is not an even multiple of 8
191 * then excessive bits-- those _after_ primebitlen-- so now
192 * we have to shift right the amount we masked off.
195 BN_rshift(x_candidate
, x_candidate
,
196 (8 - (primebitlen
% 8)));
198 if (BN_ucmp(x_candidate
, grp
->prime
) >= 0)
201 wpa_hexdump(MSG_DEBUG
, "EAP-pwd: x_candidate",
202 prfbuf
, primebytelen
);
205 * need to unambiguously identify the solution, if there is
214 * solve the quadratic equation, if it's not solvable then we
217 if (!EC_POINT_set_compressed_coordinates_GFp(grp
->group
,
223 * If there's a solution to the equation then the point must be
224 * on the curve so why check again explicitly? OpenSSL code
225 * says this is required by X9.62. We're not X9.62 but it can't
226 * hurt just to be sure.
228 if (!EC_POINT_is_on_curve(grp
->group
, grp
->pwe
, NULL
)) {
229 wpa_printf(MSG_INFO
, "EAP-pwd: point is not on curve");
233 if (BN_cmp(cofactor
, BN_value_one())) {
234 /* make sure the point is not in a small sub-group */
235 if (!EC_POINT_mul(grp
->group
, grp
->pwe
, NULL
, grp
->pwe
,
237 wpa_printf(MSG_INFO
, "EAP-pwd: cannot "
238 "multiply generator by order");
241 if (EC_POINT_is_at_infinity(grp
->group
, grp
->pwe
)) {
242 wpa_printf(MSG_INFO
, "EAP-pwd: point is at "
247 /* if we got here then we have a new generator. */
250 wpa_printf(MSG_DEBUG
, "EAP-pwd: found a PWE in %d tries", ctr
);
251 grp
->group_num
= num
;
254 EC_GROUP_free(grp
->group
);
256 EC_POINT_free(grp
->pwe
);
264 /* cleanliness and order.... */
266 BN_free(x_candidate
);
274 int compute_keys(EAP_PWD_group
*grp
, BN_CTX
*bnctx
, BIGNUM
*k
,
275 BIGNUM
*peer_scalar
, BIGNUM
*server_scalar
,
276 u8
*confirm_peer
, u8
*confirm_server
,
277 u32
*ciphersuite
, u8
*msk
, u8
*emsk
)
280 u8 mk
[SHA256_DIGEST_LENGTH
], *cruft
;
281 u8 session_id
[SHA256_DIGEST_LENGTH
+ 1];
282 u8 msk_emsk
[EAP_MSK_LEN
+ EAP_EMSK_LEN
];
285 if ((cruft
= os_malloc(BN_num_bytes(grp
->prime
))) == NULL
)
289 * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
292 session_id
[0] = EAP_TYPE_PWD
;
294 H_Update(&ctx
, (u8
*)ciphersuite
, sizeof(u32
));
295 offset
= BN_num_bytes(grp
->order
) - BN_num_bytes(peer_scalar
);
296 os_memset(cruft
, 0, BN_num_bytes(grp
->prime
));
297 BN_bn2bin(peer_scalar
, cruft
+ offset
);
298 H_Update(&ctx
, cruft
, BN_num_bytes(grp
->order
));
299 offset
= BN_num_bytes(grp
->order
) - BN_num_bytes(server_scalar
);
300 os_memset(cruft
, 0, BN_num_bytes(grp
->prime
));
301 BN_bn2bin(server_scalar
, cruft
+ offset
);
302 H_Update(&ctx
, cruft
, BN_num_bytes(grp
->order
));
303 H_Final(&ctx
, &session_id
[1]);
305 /* then compute MK = H(k | confirm-peer | confirm-server) */
307 offset
= BN_num_bytes(grp
->prime
) - BN_num_bytes(k
);
308 os_memset(cruft
, 0, BN_num_bytes(grp
->prime
));
309 BN_bn2bin(k
, cruft
+ offset
);
310 H_Update(&ctx
, cruft
, BN_num_bytes(grp
->prime
));
311 H_Update(&ctx
, confirm_peer
, SHA256_DIGEST_LENGTH
);
312 H_Update(&ctx
, confirm_server
, SHA256_DIGEST_LENGTH
);
315 /* stretch the mk with the session-id to get MSK | EMSK */
316 eap_pwd_kdf(mk
, SHA256_DIGEST_LENGTH
,
317 session_id
, SHA256_DIGEST_LENGTH
+1,
318 msk_emsk
, (EAP_MSK_LEN
+ EAP_EMSK_LEN
) * 8);
320 os_memcpy(msk
, msk_emsk
, EAP_MSK_LEN
);
321 os_memcpy(emsk
, msk_emsk
+ EAP_MSK_LEN
, EAP_EMSK_LEN
);