1*a90b9d01SCy Schubert /*
2*a90b9d01SCy Schubert * hostapd / Comeback token mechanism for SAE
3*a90b9d01SCy Schubert * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
4*a90b9d01SCy Schubert *
5*a90b9d01SCy Schubert * This software may be distributed under the terms of the BSD license.
6*a90b9d01SCy Schubert * See README for more details.
7*a90b9d01SCy Schubert */
8*a90b9d01SCy Schubert
9*a90b9d01SCy Schubert #include "utils/includes.h"
10*a90b9d01SCy Schubert
11*a90b9d01SCy Schubert #include "utils/common.h"
12*a90b9d01SCy Schubert #include "hostapd.h"
13*a90b9d01SCy Schubert #include "crypto/sha256.h"
14*a90b9d01SCy Schubert #include "crypto/random.h"
15*a90b9d01SCy Schubert #include "common/ieee802_11_defs.h"
16*a90b9d01SCy Schubert #include "comeback_token.h"
17*a90b9d01SCy Schubert
18*a90b9d01SCy Schubert
19*a90b9d01SCy Schubert #if defined(CONFIG_SAE) || defined(CONFIG_PASN)
20*a90b9d01SCy Schubert
comeback_token_hash(const u8 * comeback_key,const u8 * addr,u8 * idx)21*a90b9d01SCy Schubert static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
22*a90b9d01SCy Schubert {
23*a90b9d01SCy Schubert u8 hash[SHA256_MAC_LEN];
24*a90b9d01SCy Schubert
25*a90b9d01SCy Schubert if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
26*a90b9d01SCy Schubert addr, ETH_ALEN, hash) < 0)
27*a90b9d01SCy Schubert return -1;
28*a90b9d01SCy Schubert *idx = hash[0];
29*a90b9d01SCy Schubert return 0;
30*a90b9d01SCy Schubert }
31*a90b9d01SCy Schubert
32*a90b9d01SCy Schubert
check_comeback_token(const u8 * comeback_key,u16 * comeback_pending_idx,const u8 * addr,const u8 * token,size_t token_len)33*a90b9d01SCy Schubert int check_comeback_token(const u8 *comeback_key,
34*a90b9d01SCy Schubert u16 *comeback_pending_idx, const u8 *addr,
35*a90b9d01SCy Schubert const u8 *token, size_t token_len)
36*a90b9d01SCy Schubert {
37*a90b9d01SCy Schubert u8 mac[SHA256_MAC_LEN];
38*a90b9d01SCy Schubert const u8 *addrs[2];
39*a90b9d01SCy Schubert size_t len[2];
40*a90b9d01SCy Schubert u16 token_idx;
41*a90b9d01SCy Schubert u8 idx;
42*a90b9d01SCy Schubert
43*a90b9d01SCy Schubert if (token_len != SHA256_MAC_LEN ||
44*a90b9d01SCy Schubert comeback_token_hash(comeback_key, addr, &idx) < 0)
45*a90b9d01SCy Schubert return -1;
46*a90b9d01SCy Schubert token_idx = comeback_pending_idx[idx];
47*a90b9d01SCy Schubert if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
48*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
49*a90b9d01SCy Schubert "Comeback: Invalid anti-clogging token from "
50*a90b9d01SCy Schubert MACSTR " - token_idx 0x%04x, expected 0x%04x",
51*a90b9d01SCy Schubert MAC2STR(addr), WPA_GET_BE16(token), token_idx);
52*a90b9d01SCy Schubert return -1;
53*a90b9d01SCy Schubert }
54*a90b9d01SCy Schubert
55*a90b9d01SCy Schubert addrs[0] = addr;
56*a90b9d01SCy Schubert len[0] = ETH_ALEN;
57*a90b9d01SCy Schubert addrs[1] = token;
58*a90b9d01SCy Schubert len[1] = 2;
59*a90b9d01SCy Schubert if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
60*a90b9d01SCy Schubert 2, addrs, len, mac) < 0 ||
61*a90b9d01SCy Schubert os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
62*a90b9d01SCy Schubert return -1;
63*a90b9d01SCy Schubert
64*a90b9d01SCy Schubert comeback_pending_idx[idx] = 0; /* invalidate used token */
65*a90b9d01SCy Schubert
66*a90b9d01SCy Schubert return 0;
67*a90b9d01SCy Schubert }
68*a90b9d01SCy Schubert
69*a90b9d01SCy Schubert
70*a90b9d01SCy Schubert struct wpabuf *
auth_build_token_req(struct os_reltime * last_comeback_key_update,u8 * comeback_key,u16 comeback_idx,u16 * comeback_pending_idx,size_t idx_len,int group,const u8 * addr,int h2e)71*a90b9d01SCy Schubert auth_build_token_req(struct os_reltime *last_comeback_key_update,
72*a90b9d01SCy Schubert u8 *comeback_key, u16 comeback_idx,
73*a90b9d01SCy Schubert u16 *comeback_pending_idx, size_t idx_len,
74*a90b9d01SCy Schubert int group, const u8 *addr, int h2e)
75*a90b9d01SCy Schubert {
76*a90b9d01SCy Schubert struct wpabuf *buf;
77*a90b9d01SCy Schubert u8 *token;
78*a90b9d01SCy Schubert struct os_reltime now;
79*a90b9d01SCy Schubert u8 idx[2];
80*a90b9d01SCy Schubert const u8 *addrs[2];
81*a90b9d01SCy Schubert size_t len[2];
82*a90b9d01SCy Schubert u8 p_idx;
83*a90b9d01SCy Schubert u16 token_idx;
84*a90b9d01SCy Schubert
85*a90b9d01SCy Schubert os_get_reltime(&now);
86*a90b9d01SCy Schubert if (!os_reltime_initialized(last_comeback_key_update) ||
87*a90b9d01SCy Schubert os_reltime_expired(&now, last_comeback_key_update, 60) ||
88*a90b9d01SCy Schubert comeback_idx == 0xffff) {
89*a90b9d01SCy Schubert if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
90*a90b9d01SCy Schubert return NULL;
91*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
92*a90b9d01SCy Schubert comeback_key, COMEBACK_KEY_SIZE);
93*a90b9d01SCy Schubert *last_comeback_key_update = now;
94*a90b9d01SCy Schubert comeback_idx = 0;
95*a90b9d01SCy Schubert os_memset(comeback_pending_idx, 0, idx_len);
96*a90b9d01SCy Schubert }
97*a90b9d01SCy Schubert
98*a90b9d01SCy Schubert buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
99*a90b9d01SCy Schubert if (buf == NULL)
100*a90b9d01SCy Schubert return NULL;
101*a90b9d01SCy Schubert
102*a90b9d01SCy Schubert if (group)
103*a90b9d01SCy Schubert wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
104*a90b9d01SCy Schubert
105*a90b9d01SCy Schubert if (h2e) {
106*a90b9d01SCy Schubert /* Encapsulate Anti-clogging Token field in a container IE */
107*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
108*a90b9d01SCy Schubert wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
109*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
110*a90b9d01SCy Schubert }
111*a90b9d01SCy Schubert
112*a90b9d01SCy Schubert if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
113*a90b9d01SCy Schubert wpabuf_free(buf);
114*a90b9d01SCy Schubert return NULL;
115*a90b9d01SCy Schubert }
116*a90b9d01SCy Schubert
117*a90b9d01SCy Schubert token_idx = comeback_pending_idx[p_idx];
118*a90b9d01SCy Schubert if (!token_idx) {
119*a90b9d01SCy Schubert comeback_idx++;
120*a90b9d01SCy Schubert token_idx = comeback_idx;
121*a90b9d01SCy Schubert comeback_pending_idx[p_idx] = token_idx;
122*a90b9d01SCy Schubert }
123*a90b9d01SCy Schubert WPA_PUT_BE16(idx, token_idx);
124*a90b9d01SCy Schubert token = wpabuf_put(buf, SHA256_MAC_LEN);
125*a90b9d01SCy Schubert addrs[0] = addr;
126*a90b9d01SCy Schubert len[0] = ETH_ALEN;
127*a90b9d01SCy Schubert addrs[1] = idx;
128*a90b9d01SCy Schubert len[1] = sizeof(idx);
129*a90b9d01SCy Schubert if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
130*a90b9d01SCy Schubert 2, addrs, len, token) < 0) {
131*a90b9d01SCy Schubert wpabuf_free(buf);
132*a90b9d01SCy Schubert return NULL;
133*a90b9d01SCy Schubert }
134*a90b9d01SCy Schubert WPA_PUT_BE16(token, token_idx);
135*a90b9d01SCy Schubert
136*a90b9d01SCy Schubert return buf;
137*a90b9d01SCy Schubert }
138*a90b9d01SCy Schubert
139*a90b9d01SCy Schubert #endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
140