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