SAE: Add processing of the commit message
[mech_eap.git] / src / common / sae.c
1 /*
2  * Simultaneous authentication of equals
3  * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
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 /* TODO: move OpenSSL dependencies into crypto/crypto_openssl.c */
11 #include <openssl/bn.h>
12 #include <openssl/ec.h>
13 #include <openssl/obj_mac.h>
14
15 #include "common.h"
16 #include "crypto/sha256.h"
17 #include "crypto/random.h"
18 #include "ieee802_11_defs.h"
19 #include "sae.h"
20
21
22 static const u8 group19_prime[] = {
23         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
24         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25         0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
26         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
27 };
28
29 static const u8 group19_order[] = {
30         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
31         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
32         0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
33         0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
34 };
35
36
37 static int val_zero_or_one(const u8 *val, size_t len)
38 {
39         size_t i;
40
41         for (i = 0; i < len - 1; i++) {
42                 if (val[i])
43                         return 0;
44         }
45
46         return val[len - 1] <= 1;
47 }
48
49
50 static int val_zero(const u8 *val, size_t len)
51 {
52         size_t i;
53         for (i = 0; i < len; i++) {
54                 if (val[i])
55                         return 0;
56         }
57         return 1;
58 }
59
60
61 static int sae_get_rand(u8 *val)
62 {
63         int iter = 0;
64
65         do {
66                 if (random_get_bytes(val, sizeof(group19_prime)) < 0)
67                         return -1;
68                 if (iter++ > 100)
69                         return -1;
70         } while (os_memcmp(val, group19_order, sizeof(group19_prime)) >= 0 ||
71                  val_zero_or_one(val, sizeof(group19_prime)));
72
73         return 0;
74 }
75
76
77 static EC_POINT * alloc_elem(EC_GROUP *group, const u8 *val, size_t len)
78 {
79         BIGNUM *x, *y;
80         EC_POINT *elem;
81
82         x = BN_bin2bn(val, len, NULL);
83         y = BN_bin2bn(val + len, len, NULL);
84         elem = EC_POINT_new(group);
85         if (x == NULL || y == NULL || elem == NULL) {
86                 BN_free(x);
87                 BN_free(y);
88                 EC_POINT_free(elem);
89                 return NULL;
90         }
91
92         if (!EC_POINT_set_affine_coordinates_GFp(group, elem, x, y, NULL)) {
93                 EC_POINT_free(elem);
94                 elem = NULL;
95         }
96
97         BN_free(x);
98         BN_free(y);
99
100         return elem;
101 }
102
103
104 static void sae_bn_to_bin(const BIGNUM *bn, u8 *bin, size_t len)
105 {
106         int offset = len - BN_num_bytes(bn);
107         os_memset(bin, 0, offset);
108         BN_bn2bin(bn, bin + offset);
109 }
110
111
112 static int sae_ec_point_to_bin(BN_CTX *bnctx, EC_GROUP *group, EC_POINT *point,
113                                u8 *bin)
114 {
115         BIGNUM *x, *y;
116         int ret = -1;
117
118         x = BN_new();
119         y = BN_new();
120
121         if (x && y &&
122             EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx)) {
123                 sae_bn_to_bin(x, bin, 32);
124                 sae_bn_to_bin(y, bin + 32, 32);
125                 ret = 0;
126         }
127
128         BN_free(x);
129         BN_free(y);
130         return ret;
131 }
132
133
134 static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
135 {
136         wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
137                    " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
138         if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
139                 os_memcpy(key, addr1, ETH_ALEN);
140                 os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
141         } else {
142                 os_memcpy(key, addr2, ETH_ALEN);
143                 os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
144         }
145 }
146
147
148 static int sae_test_pwd_seed(BN_CTX *bnctx, EC_GROUP *group, const u8 *pwd_seed,
149                              EC_POINT *pwe, u8 *pwe_bin)
150 {
151         u8 pwd_value[32];
152         BIGNUM *x;
153         int y_bit;
154
155         wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, 32);
156
157         /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
158         sha256_prf(pwd_seed, 32, "SAE Hunting and Pecking",
159                    group19_prime, sizeof(group19_prime),
160                    pwd_value, sizeof(pwd_value));
161         wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
162                         pwd_value, sizeof(pwd_value));
163
164         if (os_memcmp(pwd_value, group19_prime, sizeof(group19_prime)) >= 0)
165                 return 0;
166
167         y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
168
169         x = BN_bin2bn(pwd_value, sizeof(pwd_value), NULL);
170         if (x == NULL)
171                 return -1;
172         if (!EC_POINT_set_compressed_coordinates_GFp(group, pwe, x, y_bit,
173                                                      bnctx) ||
174             !EC_POINT_is_on_curve(group, pwe, bnctx)) {
175                 BN_free(x);
176                 wpa_printf(MSG_DEBUG, "SAE: No solution found");
177                 return 0;
178         }
179         BN_free(x);
180
181         wpa_printf(MSG_DEBUG, "SAE: PWE found");
182
183         if (sae_ec_point_to_bin(bnctx, group, pwe, pwe_bin) < 0)
184                 return -1;
185
186         wpa_hexdump_key(MSG_DEBUG, "SAE: PWE x", pwe_bin, 32);
187         wpa_hexdump_key(MSG_DEBUG, "SAE: PWE y", pwe_bin + 32, 32);
188         return 1;
189 }
190
191
192 static int sae_derive_pwe(BN_CTX *bnctx, EC_GROUP *group, const u8 *addr1,
193                           const u8 *addr2, const u8 *password,
194                           size_t password_len, EC_POINT *pwe, u8 *pwe_bin)
195 {
196         u8 counter, k = 4;
197         u8 addrs[2 * ETH_ALEN];
198         const u8 *addr[2];
199         size_t len[2];
200         int found = 0;
201         EC_POINT *pwe_tmp;
202         u8 pwe_bin_tmp[2 * 32];
203
204         pwe_tmp = EC_POINT_new(group);
205         if (pwe_tmp == NULL)
206                 return -1;
207
208         wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
209                               password, password_len);
210
211         /*
212          * H(salt, ikm) = HMAC-SHA256(salt, ikm)
213          * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
214          *              password || counter)
215          */
216         sae_pwd_seed_key(addr1, addr2, addrs);
217
218         addr[0] = password;
219         len[0] = password_len;
220         addr[1] = &counter;
221         len[1] = sizeof(counter);
222
223         /*
224          * Continue for at least k iterations to protect against side-channel
225          * attacks that attempt to determine the number of iterations required
226          * in the loop.
227          */
228         for (counter = 1; counter < k || !found; counter++) {
229                 u8 pwd_seed[SHA256_MAC_LEN];
230                 int res;
231
232                 wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
233                 if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
234                                        pwd_seed) < 0)
235                         break;
236                 res = sae_test_pwd_seed(bnctx, group, pwd_seed,
237                                         found ? pwe_tmp : pwe,
238                                         found ? pwe_bin_tmp : pwe_bin);
239                 if (res < 0)
240                         break;
241                 if (res == 0)
242                         continue;
243                 if (found) {
244                         wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
245                                    "already selected)");
246                 } else {
247                         wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
248                         found = 1;
249                 }
250
251                 if (counter > 200) {
252                         /* This should not happen in practice */
253                         wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
254                         break;
255                 }
256         }
257
258         EC_POINT_clear_free(pwe_tmp);
259
260         return found ? 0 : -1;
261 }
262
263
264 static int sae_derive_commit(struct sae_data *sae, BN_CTX *bnctx,
265                              EC_GROUP *group, EC_POINT *pwe)
266 {
267         BIGNUM *x, *bn_rand, *bn_mask, *order;
268         EC_POINT *elem;
269         u8 mask[32];
270         int ret = -1;
271
272         if (sae_get_rand(sae->sae_rand) < 0 || sae_get_rand(mask) < 0)
273                 return -1;
274         wpa_hexdump_key(MSG_DEBUG, "SAE: rand",
275                         sae->sae_rand, sizeof(sae->sae_rand));
276         wpa_hexdump_key(MSG_DEBUG, "SAE: mask", mask, sizeof(mask));
277
278         x = BN_new();
279         bn_rand = BN_bin2bn(sae->sae_rand, 32, NULL);
280         bn_mask = BN_bin2bn(mask, sizeof(mask), NULL);
281         order = BN_bin2bn(group19_order, sizeof(group19_order), NULL);
282         elem = EC_POINT_new(group);
283         if (x == NULL || bn_rand == NULL || bn_mask == NULL || order == NULL ||
284             elem == NULL)
285                 goto fail;
286
287         /* commit-scalar = (rand + mask) modulo r */
288         BN_add(x, bn_rand, bn_mask);
289         BN_mod(x, x, order, bnctx);
290         sae_bn_to_bin(x, sae->own_commit_scalar, 32);
291         wpa_hexdump(MSG_DEBUG, "SAE: commit-scalar",
292                     sae->own_commit_scalar, 32);
293
294         /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
295         if (!EC_POINT_mul(group, elem, NULL, pwe, bn_mask, bnctx) ||
296             !EC_POINT_invert(group, elem, bnctx) ||
297             sae_ec_point_to_bin(bnctx, group, elem, sae->own_commit_element) <
298             0)
299                 goto fail;
300
301         wpa_hexdump(MSG_DEBUG, "SAE: commit-element x",
302                     sae->own_commit_element, 32);
303         wpa_hexdump(MSG_DEBUG, "SAE: commit-element y",
304                     sae->own_commit_element + 32, 32);
305
306         ret = 0;
307 fail:
308         EC_POINT_free(elem);
309         BN_free(order);
310         BN_clear_free(bn_mask);
311         os_memset(mask, 0, sizeof(mask));
312         BN_clear_free(bn_rand);
313         BN_clear_free(x);
314         return ret;
315 }
316
317
318 int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
319                        const u8 *password, size_t password_len,
320                        struct sae_data *sae)
321 {
322         BN_CTX *bnctx;
323         EC_POINT *pwe;
324         EC_GROUP *group;
325         int ret = 0;
326
327         bnctx = BN_CTX_new();
328         group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
329         pwe = EC_POINT_new(group);
330         if (bnctx == NULL || group == NULL || pwe == NULL ||
331             sae_derive_pwe(bnctx, group, addr1, addr2, password, password_len,
332                            pwe, sae->pwe) < 0 ||
333             sae_derive_commit(sae, bnctx, group, pwe) < 0)
334                 ret = -1;
335
336         EC_POINT_clear_free(pwe);
337         EC_GROUP_free(group);
338         BN_CTX_free(bnctx);
339
340         return ret;
341 }
342
343
344 static int sae_check_peer_commit(struct sae_data *sae)
345 {
346         /* 0 < scalar < r */
347         if (val_zero(sae->peer_commit_scalar, 32) ||
348             os_memcmp(sae->peer_commit_scalar, group19_order,
349                       sizeof(group19_prime)) >= 0) {
350                 wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
351                 return -1;
352         }
353
354         /* element x and y coordinates < p */
355         if (os_memcmp(sae->peer_commit_element, group19_prime,
356                       sizeof(group19_prime)) >= 0 ||
357             os_memcmp(sae->peer_commit_element + 32, group19_prime,
358                       sizeof(group19_prime)) >= 0) {
359                 wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
360                            "element");
361                 return -1;
362         }
363
364         return 0;
365 }
366
367
368 static int sae_derive_k(struct sae_data *sae, u8 *k, BN_CTX *bnctx,
369                         EC_GROUP *group)
370 {
371         EC_POINT *pwe, *peer_elem, *K;
372         BIGNUM *k_bn, *rand_bn, *peer_scalar;
373         int ret = -1;
374
375         pwe = alloc_elem(group, sae->pwe, 32);
376         peer_scalar = BN_bin2bn(sae->peer_commit_scalar, 32, NULL);
377         peer_elem = alloc_elem(group, sae->peer_commit_element, 32);
378         K = EC_POINT_new(group);
379         k_bn = BN_new();
380         rand_bn = BN_bin2bn(sae->sae_rand, 32, NULL);
381         if (pwe == NULL || peer_elem == NULL || peer_scalar == NULL ||
382             K == NULL || k_bn == NULL || rand_bn == NULL)
383                 goto fail;
384
385         if (!EC_POINT_is_on_curve(group, peer_elem, NULL)) {
386                 wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
387                 goto fail;
388         }
389
390         /*
391          * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
392          *                                        PEER-COMMIT-ELEMENT)))
393          * If K is identity element (point-at-infinity), reject
394          * k = F(K) (= x coordinate)
395          */
396
397         if (!EC_POINT_mul(group, K, NULL, pwe, peer_scalar, bnctx) ||
398             !EC_POINT_add(group, K, K, peer_elem, bnctx) ||
399             !EC_POINT_mul(group, K, NULL, K, rand_bn, bnctx) ||
400             EC_POINT_is_at_infinity(group, K) ||
401             !EC_POINT_get_affine_coordinates_GFp(group, K, k_bn, NULL, bnctx)) {
402                 wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
403                 goto fail;
404         }
405
406         sae_bn_to_bin(k_bn, k, 32);
407         wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, 32);
408
409         ret = 0;
410 fail:
411         EC_POINT_free(pwe);
412         EC_POINT_free(peer_elem);
413         EC_POINT_clear_free(K);
414         BN_free(k_bn);
415         BN_free(rand_bn);
416         return ret;
417 }
418
419
420 static int sae_derive_keys(struct sae_data *sae, const u8 *k, BN_CTX *bnctx)
421 {
422         u8 null_key[32], val[32];
423         u8 keyseed[SHA256_MAC_LEN];
424         u8 keys[32 + 32];
425         BIGNUM *order, *own_scalar, *peer_scalar, *tmp;
426         int ret = -1;
427
428         order = BN_bin2bn(group19_order, sizeof(group19_order), NULL);
429         own_scalar = BN_bin2bn(sae->own_commit_scalar, 32, NULL);
430         peer_scalar = BN_bin2bn(sae->peer_commit_scalar, 32, NULL);
431         tmp = BN_new();
432         if (order == NULL || own_scalar == NULL || peer_scalar == NULL ||
433             tmp == NULL)
434                 goto fail;
435
436         /* keyseed = H(<0>32, k)
437          * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
438          *                      (commit-scalar + peer-commit-scalar) modulo r)
439          * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
440          */
441
442         os_memset(null_key, 0, sizeof(null_key));
443         hmac_sha256(null_key, sizeof(null_key), k, 32, keyseed);
444         wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
445
446         BN_add(tmp, own_scalar, peer_scalar);
447         BN_mod(tmp, tmp, order, bnctx);
448         sae_bn_to_bin(tmp, val, sizeof(group19_prime));
449         wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, 16);
450         sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
451                    val, sizeof(val), keys, sizeof(keys));
452         os_memcpy(sae->kck, keys, 32);
453         os_memcpy(sae->pmk, keys + 32, 32);
454         wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->kck, 32);
455         wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, 32);
456
457         ret = 0;
458 fail:
459         BN_free(order);
460         BN_free(own_scalar);
461         BN_free(tmp);
462         return ret;
463 }
464
465
466 int sae_process_commit(struct sae_data *sae)
467 {
468         BN_CTX *bnctx;
469         EC_GROUP *group;
470         int ret = 0;
471         u8 k[32];
472
473         if (sae_check_peer_commit(sae) < 0)
474                 return -1;
475
476         bnctx = BN_CTX_new();
477         group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
478         if (bnctx == NULL || group == NULL ||
479             sae_derive_k(sae, k, bnctx, group) < 0 ||
480             sae_derive_keys(sae, k, bnctx) < 0)
481                 ret = -1;
482
483         EC_GROUP_free(group);
484         BN_CTX_free(bnctx);
485
486         return ret;
487 }
488
489
490 void sae_write_commit(struct sae_data *sae, struct wpabuf *buf)
491 {
492         wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
493         /* TODO: Anti-Clogging Token (if requested) */
494         wpabuf_put_data(buf, sae->own_commit_scalar, 32);
495         wpabuf_put_data(buf, sae->own_commit_element, 2 * 32);
496 }
497
498
499 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len)
500 {
501         const u8 *pos = data, *end = data + len;
502         size_t val_len;
503
504         wpa_hexdump(MSG_DEBUG, "SAE: Commit fields", data, len);
505
506         /* Check Finite Cyclic Group */
507         if (pos + 2 > end)
508                 return WLAN_STATUS_UNSPECIFIED_FAILURE;
509         if (WPA_GET_LE16(pos) != 19) {
510                 wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
511                            WPA_GET_LE16(pos));
512                 return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
513         }
514         pos += 2;
515         val_len = 32;
516
517         if (pos + val_len > end) {
518                 wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
519                 return WLAN_STATUS_UNSPECIFIED_FAILURE;
520         }
521         os_memcpy(sae->peer_commit_scalar, pos, val_len);
522         wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
523                     sae->peer_commit_scalar, val_len);
524         pos += val_len;
525
526         if (pos + 2 * val_len > end) {
527                 wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
528                            "commit-element");
529                 return WLAN_STATUS_UNSPECIFIED_FAILURE;
530         }
531         os_memcpy(sae->peer_commit_element, pos, 2 * val_len);
532         wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
533                     sae->peer_commit_element, val_len);
534         wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
535                     sae->peer_commit_element + val_len, val_len);
536         pos += 2 * val_len;
537
538         if (end > pos) {
539                 wpa_hexdump(MSG_DEBUG, "SAE: Unexpected extra data in commit",
540                             pos, end - pos);
541         }
542
543         return WLAN_STATUS_SUCCESS;
544 }