00c06878edabe6191e8bc9b16562b0a9ddac0464
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_pwd / eap_pwd.c
1 /*
2  * Copyright (c) Dan Harkins, 2012
3  *
4  *  Copyright holder grants permission for redistribution and use in source
5  *  and binary forms, with or without modification, provided that the
6  *  following conditions are met:
7  *     1. Redistribution of source code must retain the above copyright
8  *      notice, this list of conditions, and the following disclaimer
9  *      in all source files.
10  *     2. Redistribution in binary form must retain the above copyright
11  *      notice, this list of conditions, and the following disclaimer
12  *      in the documentation and/or other materials provided with the
13  *      distribution.
14  *
15  *  "DISCLAIMER OF LIABILITY
16  *
17  *  THIS SOFTWARE IS PROVIDED BY DAN HARKINS ``AS IS'' AND
18  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19  *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INDUSTRIAL LOUNGE BE LIABLE
21  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  *  SUCH DAMAGE."
28  *
29  * This license and distribution terms cannot be changed. In other words,
30  * this code cannot simply be copied and put under a different distribution
31  * license (including the GNU public license).
32  */
33
34 RCSID("$Id$")
35 USES_APPLE_DEPRECATED_API       /* OpenSSL API has been deprecated by Apple */
36
37 #include "eap_pwd.h"
38
39 #include <freeradius-devel/radiusd.h>
40 #include <freeradius-devel/modules.h>
41
42 /* The random function H(x) = HMAC-SHA256(0^32, x) */
43 static void
44 H_Init(HMAC_CTX *ctx)
45 {
46     uint8_t allzero[SHA256_DIGEST_LENGTH];
47
48     memset(allzero, 0, SHA256_DIGEST_LENGTH);
49     HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
50 }
51
52 static void
53 H_Update(HMAC_CTX *ctx, uint8_t const *data, int len)
54 {
55     HMAC_Update(ctx, data, len);
56 }
57
58 static void
59 H_Final(HMAC_CTX *ctx, uint8_t *digest)
60 {
61     unsigned int mdlen = SHA256_DIGEST_LENGTH;
62
63     HMAC_Final(ctx, digest, &mdlen);
64     HMAC_CTX_cleanup(ctx);
65 }
66
67 /* a counter-based KDF based on NIST SP800-108 */
68 static void
69 eap_pwd_kdf(uint8_t *key, int keylen, char const *label, int labellen,
70             uint8_t *result, int resultbitlen)
71 {
72     HMAC_CTX hctx;
73     uint8_t digest[SHA256_DIGEST_LENGTH];
74     uint16_t i, ctr, L;
75     int resultbytelen, len = 0;
76     unsigned int mdlen = SHA256_DIGEST_LENGTH;
77     uint8_t mask = 0xff;
78
79     resultbytelen = (resultbitlen + 7)/8;
80     ctr = 0;
81     L = htons(resultbitlen);
82     while (len < resultbytelen) {
83         ctr++; i = htons(ctr);
84         HMAC_Init(&hctx, key, keylen, EVP_sha256());
85         if (ctr > 1) {
86             HMAC_Update(&hctx, digest, mdlen);
87         }
88         HMAC_Update(&hctx, (uint8_t *) &i, sizeof(uint16_t));
89         HMAC_Update(&hctx, (uint8_t const *)label, labellen);
90         HMAC_Update(&hctx, (uint8_t *) &L, sizeof(uint16_t));
91         HMAC_Final(&hctx, digest, &mdlen);
92         if ((len + (int) mdlen) > resultbytelen) {
93             memcpy(result + len, digest, resultbytelen - len);
94         } else {
95             memcpy(result + len, digest, mdlen);
96         }
97         len += mdlen;
98         HMAC_CTX_cleanup(&hctx);
99     }
100
101     /* since we're expanding to a bit length, mask off the excess */
102     if (resultbitlen % 8) {
103         mask <<= (8 - (resultbitlen % 8));
104         result[resultbytelen - 1] &= mask;
105     }
106 }
107
108 int
109 compute_password_element (pwd_session_t *sess, uint16_t grp_num,
110                           char const *password, int password_len,
111                           char const *id_server, int id_server_len,
112                           char const *id_peer, int id_peer_len,
113                           uint32_t *token)
114 {
115     BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
116     HMAC_CTX ctx;
117     uint8_t pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
118     int nid, is_odd, primebitlen, primebytelen, ret = 0;
119
120     switch (grp_num) { /* from IANA registry for IKE D-H groups */
121         case 19:
122             nid = NID_X9_62_prime256v1;
123             break;
124         case 20:
125             nid = NID_secp384r1;
126             break;
127         case 21:
128             nid = NID_secp521r1;
129             break;
130         case 25:
131             nid = NID_X9_62_prime192v1;
132             break;
133         case 26:
134             nid = NID_secp224r1;
135             break;
136         default:
137             DEBUG("unknown group %d", grp_num);
138             goto fail;
139     }
140
141     sess->pwe = NULL;
142     sess->order = NULL;
143     sess->prime = NULL;
144
145     if ((sess->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
146         DEBUG("unable to create EC_GROUP");
147         goto fail;
148     }
149
150     if (((rnd = BN_new()) == NULL) ||
151         ((cofactor = BN_new()) == NULL) ||
152         ((sess->pwe = EC_POINT_new(sess->group)) == NULL) ||
153         ((sess->order = BN_new()) == NULL) ||
154         ((sess->prime = BN_new()) == NULL) ||
155         ((x_candidate = BN_new()) == NULL)) {
156         DEBUG("unable to create bignums");
157         goto fail;
158     }
159
160     if (!EC_GROUP_get_curve_GFp(sess->group, sess->prime, NULL, NULL, NULL))
161     {
162         DEBUG("unable to get prime for GFp curve");
163         goto fail;
164     }
165     if (!EC_GROUP_get_order(sess->group, sess->order, NULL)) {
166         DEBUG("unable to get order for curve");
167         goto fail;
168     }
169     if (!EC_GROUP_get_cofactor(sess->group, cofactor, NULL)) {
170         DEBUG("unable to get cofactor for curve");
171         goto fail;
172     }
173     primebitlen = BN_num_bits(sess->prime);
174     primebytelen = BN_num_bytes(sess->prime);
175     if ((prfbuf = talloc_zero_array(sess, uint8_t, primebytelen)) == NULL) {
176         DEBUG("unable to alloc space for prf buffer");
177         goto fail;
178     }
179     ctr = 0;
180     while (1) {
181         if (ctr > 10) {
182             DEBUG("unable to find random point on curve for group %d, something's fishy", grp_num);
183             goto fail;
184         }
185         ctr++;
186
187         /*
188          * compute counter-mode password value and stretch to prime
189          *    pwd-seed = H(token | peer-id | server-id | password |
190          *                 counter)
191          */
192         H_Init(&ctx);
193         H_Update(&ctx, (uint8_t *)token, sizeof(*token));
194         H_Update(&ctx, (uint8_t const *)id_peer, id_peer_len);
195         H_Update(&ctx, (uint8_t const *)id_server, id_server_len);
196         H_Update(&ctx, (uint8_t const *)password, password_len);
197         H_Update(&ctx, (uint8_t *)&ctr, sizeof(ctr));
198         H_Final(&ctx, pwe_digest);
199
200         BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
201         eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
202                     "EAP-pwd Hunting And Pecking",
203                     strlen("EAP-pwd Hunting And Pecking"),
204                     prfbuf, primebitlen);
205
206         BN_bin2bn(prfbuf, primebytelen, x_candidate);
207         /*
208          * eap_pwd_kdf() returns a string of bits 0..primebitlen but
209          * BN_bin2bn will treat that string of bits as a big endian
210          * number. If the primebitlen is not an even multiple of 8
211          * then excessive bits-- those _after_ primebitlen-- so now
212          * we have to shift right the amount we masked off.
213          */
214         if (primebitlen % 8) {
215             BN_rshift(x_candidate, x_candidate, (8 - (primebitlen % 8)));
216         }
217         if (BN_ucmp(x_candidate, sess->prime) >= 0) {
218             continue;
219         }
220         /*
221          * need to unambiguously identify the solution, if there is
222          * one...
223          */
224         if (BN_is_odd(rnd)) {
225             is_odd = 1;
226         } else {
227             is_odd = 0;
228         }
229         /*
230          * solve the quadratic equation, if it's not solvable then we
231          * don't have a point
232          */
233         if (!EC_POINT_set_compressed_coordinates_GFp(sess->group,
234                                                      sess->pwe,
235                                                      x_candidate,
236                                                      is_odd, NULL)) {
237             continue;
238         }
239         /*
240          * If there's a solution to the equation then the point must be
241          * on the curve so why check again explicitly? OpenSSL code
242          * says this is required by X9.62. We're not X9.62 but it can't
243          * hurt just to be sure.
244          */
245         if (!EC_POINT_is_on_curve(sess->group, sess->pwe, NULL)) {
246             DEBUG("EAP-pwd: point is not on curve");
247             continue;
248         }
249
250         if (BN_cmp(cofactor, BN_value_one())) {
251             /* make sure the point is not in a small sub-group */
252             if (!EC_POINT_mul(sess->group, sess->pwe, NULL, sess->pwe,
253                               cofactor, NULL)) {
254                 DEBUG("EAP-pwd: cannot multiply generator by order");
255                 continue;
256             }
257             if (EC_POINT_is_at_infinity(sess->group, sess->pwe)) {
258                 DEBUG("EAP-pwd: point is at infinity");
259                 continue;
260             }
261         }
262         /* if we got here then we have a new generator. */
263         break;
264     }
265     sess->group_num = grp_num;
266     if (0) {
267 fail:                           /* DON'T free sess, it's in handler->opaque */
268         ret = -1;
269     }
270     /* cleanliness and order.... */
271     BN_free(cofactor);
272     BN_free(x_candidate);
273     BN_free(rnd);
274     talloc_free(prfbuf);
275
276     return ret;
277 }
278
279 int
280 compute_scalar_element (pwd_session_t *sess, BN_CTX *bnctx)
281 {
282     BIGNUM *mask = NULL;
283     int ret = -1;
284
285     if (((sess->private_value = BN_new()) == NULL) ||
286         ((sess->my_element = EC_POINT_new(sess->group)) == NULL) ||
287         ((sess->my_scalar = BN_new()) == NULL) ||
288         ((mask = BN_new()) == NULL)) {
289         DEBUG2("server scalar allocation failed");
290         goto fail;
291     }
292
293     BN_rand_range(sess->private_value, sess->order);
294     BN_rand_range(mask, sess->order);
295     BN_add(sess->my_scalar, sess->private_value, mask);
296     BN_mod(sess->my_scalar, sess->my_scalar, sess->order,
297            bnctx);
298
299     if (!EC_POINT_mul(sess->group, sess->my_element, NULL,
300                       sess->pwe, mask, bnctx)) {
301         DEBUG2("server element allocation failed");
302         goto fail;
303     }
304
305     if (!EC_POINT_invert(sess->group, sess->my_element, bnctx)) {
306         DEBUG2("server element inversion failed");
307         goto fail;
308     }
309
310     ret = 0;
311 fail:
312     BN_free(mask);
313
314     return ret;
315 }
316
317 int
318 process_peer_commit (pwd_session_t *sess, uint8_t *commit, BN_CTX *bnctx)
319 {
320     uint8_t *ptr;
321     BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
322     EC_POINT *K = NULL, *point = NULL;
323     int res = 1;
324
325     if (((sess->peer_scalar = BN_new()) == NULL) ||
326         ((sess->k = BN_new()) == NULL) ||
327         ((cofactor = BN_new()) == NULL) ||
328         ((x = BN_new()) == NULL) ||
329         ((y = BN_new()) == NULL) ||
330         ((point = EC_POINT_new(sess->group)) == NULL) ||
331         ((K = EC_POINT_new(sess->group)) == NULL) ||
332         ((sess->peer_element = EC_POINT_new(sess->group)) == NULL)) {
333         DEBUG2("pwd: failed to allocate room to process peer's commit");
334         goto fin;
335     }
336
337     if (!EC_GROUP_get_cofactor(sess->group, cofactor, NULL)) {
338         DEBUG2("pwd: unable to get group co-factor");
339         goto fin;
340     }
341
342     /* element, x then y, followed by scalar */
343     ptr = (uint8_t *)commit;
344     BN_bin2bn(ptr, BN_num_bytes(sess->prime), x);
345     ptr += BN_num_bytes(sess->prime);
346     BN_bin2bn(ptr, BN_num_bytes(sess->prime), y);
347     ptr += BN_num_bytes(sess->prime);
348     BN_bin2bn(ptr, BN_num_bytes(sess->order), sess->peer_scalar);
349     if (!EC_POINT_set_affine_coordinates_GFp(sess->group,
350                                              sess->peer_element, x, y,
351                                              bnctx)) {
352         DEBUG2("pwd: unable to get coordinates of peer's element");
353         goto fin;
354     }
355
356     /* check to ensure peer's element is not in a small sub-group */
357     if (BN_cmp(cofactor, BN_value_one())) {
358         if (!EC_POINT_mul(sess->group, point, NULL,
359                           sess->peer_element, cofactor, NULL)) {
360             DEBUG2("pwd: unable to multiply element by co-factor");
361             goto fin;
362         }
363         if (EC_POINT_is_at_infinity(sess->group, point)) {
364             DEBUG2("pwd: peer's element is in small sub-group");
365             goto fin;
366         }
367     }
368
369     /* compute the shared key, k */
370     if ((!EC_POINT_mul(sess->group, K, NULL, sess->pwe,
371                        sess->peer_scalar, bnctx)) ||
372         (!EC_POINT_add(sess->group, K, K, sess->peer_element,
373                        bnctx)) ||
374         (!EC_POINT_mul(sess->group, K, NULL, K, sess->private_value,
375                        bnctx))) {
376         DEBUG2("pwd: unable to compute shared key, k");
377         goto fin;
378     }
379
380     /* ensure that the shared key isn't in a small sub-group */
381     if (BN_cmp(cofactor, BN_value_one())) {
382         if (!EC_POINT_mul(sess->group, K, NULL, K, cofactor,
383                           NULL)) {
384             DEBUG2("pwd: unable to multiply k by co-factor");
385             goto fin;
386         }
387     }
388
389     /*
390      * This check is strictly speaking just for the case above where
391      * co-factor > 1 but it was suggested that even though this is probably
392      * never going to happen it is a simple and safe check "just to be
393      * sure" so let's be safe.
394      */
395     if (EC_POINT_is_at_infinity(sess->group, K)) {
396         DEBUG2("pwd: k is point-at-infinity!");
397         goto fin;
398     }
399     if (!EC_POINT_get_affine_coordinates_GFp(sess->group, K, sess->k,
400                                              NULL, bnctx)) {
401         DEBUG2("pwd: unable to get shared secret from K");
402         goto fin;
403     }
404     res = 0;
405
406   fin:
407     EC_POINT_free(K);
408     EC_POINT_free(point);
409     BN_free(cofactor);
410     BN_free(x);
411     BN_free(y);
412
413     return res;
414 }
415
416 int
417 compute_server_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
418 {
419     BIGNUM *x = NULL, *y = NULL;
420     HMAC_CTX ctx;
421     uint8_t *cruft = NULL;
422     int offset, req = -1;
423
424     /*
425      * Each component of the cruft will be at most as big as the prime
426      */
427     if (((cruft = talloc_zero_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) ||
428         ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
429         DEBUG2("pwd: unable to allocate space to compute confirm!");
430         goto fin;
431     }
432
433     /*
434      * commit is H(k | server_element | server_scalar | peer_element |
435      *         peer_scalar | ciphersuite)
436      */
437     H_Init(&ctx);
438
439     /*
440      * Zero the memory each time because this is mod prime math and some
441      * value may start with a few zeros and the previous one did not.
442      *
443      * First is k
444      */
445     offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
446     BN_bn2bin(sess->k, cruft + offset);
447     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
448
449     /*
450      * next is server element: x, y
451      */
452     if (!EC_POINT_get_affine_coordinates_GFp(sess->group,
453                                              sess->my_element, x, y,
454                                              bnctx)) {
455         DEBUG2("pwd: unable to get coordinates of server element");
456         goto fin;
457     }
458     memset(cruft, 0, BN_num_bytes(sess->prime));
459     offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
460     BN_bn2bin(x, cruft + offset);
461     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
462
463     memset(cruft, 0, BN_num_bytes(sess->prime));
464     offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
465     BN_bn2bin(y, cruft + offset);
466     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
467
468     /*
469      * and server scalar
470      */
471     memset(cruft, 0, BN_num_bytes(sess->prime));
472     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
473     BN_bn2bin(sess->my_scalar, cruft + offset);
474     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
475
476     /*
477      * next is peer element: x, y
478      */
479     if (!EC_POINT_get_affine_coordinates_GFp(sess->group,
480                                              sess->peer_element, x, y,
481                                              bnctx)) {
482         DEBUG2("pwd: unable to get coordinates of peer's element");
483         goto fin;
484     }
485
486     memset(cruft, 0, BN_num_bytes(sess->prime));
487     offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
488     BN_bn2bin(x, cruft + offset);
489     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
490
491     memset(cruft, 0, BN_num_bytes(sess->prime));
492     offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
493     BN_bn2bin(y, cruft + offset);
494     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
495
496     /*
497      * and peer scalar
498      */
499     memset(cruft, 0, BN_num_bytes(sess->prime));
500     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
501     BN_bn2bin(sess->peer_scalar, cruft + offset);
502     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
503
504     /*
505      * finally, ciphersuite
506      */
507     H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
508
509     H_Final(&ctx, buf);
510
511     req = 0;
512 fin:
513     if (cruft != NULL) {
514             talloc_free(cruft);
515     }
516     BN_free(x);
517     BN_free(y);
518
519     return req;
520 }
521
522 int
523 compute_peer_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
524 {
525     BIGNUM *x = NULL, *y = NULL;
526     HMAC_CTX ctx;
527     uint8_t *cruft = NULL;
528     int offset, req = -1;
529
530     /*
531      * Each component of the cruft will be at most as big as the prime
532      */
533     if (((cruft = talloc_zero_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) ||
534         ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
535         DEBUG2("pwd: unable to allocate space to compute confirm!");
536         goto fin;
537     }
538
539     /*
540      * commit is H(k | server_element | server_scalar | peer_element |
541      *         peer_scalar | ciphersuite)
542      */
543     H_Init(&ctx);
544
545     /*
546      * Zero the memory each time because this is mod prime math and some
547      * value may start with a few zeros and the previous one did not.
548      *
549      * First is k
550      */
551     offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
552     BN_bn2bin(sess->k, cruft + offset);
553     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
554
555     /*
556      * then peer element: x, y
557      */
558     if (!EC_POINT_get_affine_coordinates_GFp(sess->group,
559                                              sess->peer_element, x, y,
560                                              bnctx)) {
561         DEBUG2("pwd: unable to get coordinates of peer's element");
562         goto fin;
563     }
564
565     memset(cruft, 0, BN_num_bytes(sess->prime));
566     offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
567     BN_bn2bin(x, cruft + offset);
568     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
569
570     memset(cruft, 0, BN_num_bytes(sess->prime));
571     offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
572     BN_bn2bin(y, cruft + offset);
573     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
574
575     /*
576      * and peer scalar
577      */
578     memset(cruft, 0, BN_num_bytes(sess->prime));
579     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
580     BN_bn2bin(sess->peer_scalar, cruft + offset);
581     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
582
583     /*
584      * then server element: x, y
585      */
586     if (!EC_POINT_get_affine_coordinates_GFp(sess->group,
587                                              sess->my_element, x, y,
588                                              bnctx)) {
589         DEBUG2("pwd: unable to get coordinates of server element");
590         goto fin;
591     }
592     memset(cruft, 0, BN_num_bytes(sess->prime));
593     offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
594     BN_bn2bin(x, cruft + offset);
595     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
596
597     memset(cruft, 0, BN_num_bytes(sess->prime));
598     offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
599     BN_bn2bin(y, cruft + offset);
600     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
601
602     /*
603      * and server scalar
604      */
605     memset(cruft, 0, BN_num_bytes(sess->prime));
606     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
607     BN_bn2bin(sess->my_scalar, cruft + offset);
608     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
609
610     /*
611      * finally, ciphersuite
612      */
613     H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
614
615     H_Final(&ctx, buf);
616
617     req = 0;
618 fin:
619     if (cruft != NULL) {
620             talloc_free(cruft);
621     }
622     BN_free(x);
623     BN_free(y);
624
625     return req;
626 }
627
628 int
629 compute_keys (pwd_session_t *sess, uint8_t *peer_confirm,
630               uint8_t *msk, uint8_t *emsk)
631 {
632     HMAC_CTX ctx;
633     uint8_t mk[SHA256_DIGEST_LENGTH], *cruft;
634     uint8_t session_id[SHA256_DIGEST_LENGTH + 1];
635     uint8_t msk_emsk[128];              /* 64 each */
636     int offset;
637
638     if ((cruft = talloc_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) {
639         DEBUG2("pwd: unable to allocate space to compute keys");
640         return -1;
641     }
642     /*
643      * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
644      *  scal_s)
645      */
646     session_id[0] = PW_EAP_PWD;
647     H_Init(&ctx);
648     H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
649     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
650     memset(cruft, 0, BN_num_bytes(sess->prime));
651     BN_bn2bin(sess->peer_scalar, cruft + offset);
652     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
653     offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
654     memset(cruft, 0, BN_num_bytes(sess->prime));
655     BN_bn2bin(sess->my_scalar, cruft + offset);
656     H_Update(&ctx, cruft, BN_num_bytes(sess->order));
657     H_Final(&ctx, (uint8_t *)&session_id[1]);
658
659     /* then compute MK = H(k | commit-peer | commit-server) */
660     H_Init(&ctx);
661
662     memset(cruft, 0, BN_num_bytes(sess->prime));
663     offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
664     BN_bn2bin(sess->k, cruft + offset);
665     H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
666
667     H_Update(&ctx, peer_confirm, SHA256_DIGEST_LENGTH);
668
669     H_Update(&ctx, sess->my_confirm, SHA256_DIGEST_LENGTH);
670
671     H_Final(&ctx, mk);
672
673     /* stretch the mk with the session-id to get MSK | EMSK */
674     eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
675                 (char const *)session_id, SHA256_DIGEST_LENGTH+1,
676                 msk_emsk, 1024);  /* it's bits, ((64 + 64) * 8) */
677
678     memcpy(msk, msk_emsk, 64);
679     memcpy(emsk, msk_emsk + 64, 64);
680
681     talloc_free(cruft);
682     return 0;
683 }
684
685
686
687