/src/wireshark/epan/crypt/dot11decrypt_util.c
Line | Count | Source |
1 | | /* dot11decrypt_util.c |
2 | | * |
3 | | * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting |
4 | | * Copyright (c) 2006 CACE Technologies, Davis (California) |
5 | | * All rights reserved. |
6 | | * |
7 | | * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) |
8 | | */ |
9 | | |
10 | | /****************************************************************************/ |
11 | | /* File includes */ |
12 | | #include "config.h" |
13 | | |
14 | | #include "dot11decrypt_debug.h" |
15 | | #include "dot11decrypt_int.h" |
16 | | #include "dot11decrypt_util.h" |
17 | | |
18 | | /****************************************************************************/ |
19 | | /* Internal definitions */ |
20 | | |
21 | 0 | #define FC0_AAD_MASK 0x8f |
22 | 0 | #define FC1_AAD_MASK 0xc7 |
23 | 0 | #define FC1_AAD_QOS_MASK 0x47 |
24 | | |
25 | | /****************************************************************************/ |
26 | | /* Internal macros */ |
27 | | |
28 | | /****************************************************************************/ |
29 | | /* Internal function prototypes declarations */ |
30 | | |
31 | | /****************************************************************************/ |
32 | | /* Function definitions */ |
33 | | |
34 | | /* From IEEE 802.11 2016 Chapter 12.5.3.3.3 and 12.5.5.3.3 Construct AAD */ |
35 | | void dot11decrypt_construct_aad( |
36 | | PDOT11DECRYPT_MAC_FRAME wh, |
37 | | uint8_t *aad, |
38 | | size_t *aad_len) |
39 | 0 | { |
40 | 0 | uint8_t mgmt = (DOT11DECRYPT_TYPE(wh->fc[0]) == DOT11DECRYPT_TYPE_MANAGEMENT); |
41 | 0 | int alen = 22; |
42 | | |
43 | | /* AAD: |
44 | | * FC with bits 4..6 and 11..13 masked to zero; 14 is always one; 15 zero when QoS Control field present |
45 | | * A1 | A2 | A3 |
46 | | * SC with bits 4..15 (seq#) masked to zero |
47 | | * A4 (if present) |
48 | | * QC (if present) |
49 | | */ |
50 | | |
51 | | /* NB: aad[1] set below */ |
52 | 0 | if (!mgmt) { |
53 | 0 | aad[0] = (uint8_t)(wh->fc[0] & FC0_AAD_MASK); |
54 | 0 | } else { |
55 | 0 | aad[0] = wh->fc[0]; |
56 | 0 | } |
57 | 0 | if (DOT11DECRYPT_IS_QOS_DATA(wh)) { |
58 | 0 | aad[1] = (uint8_t)((wh->fc[1] & FC1_AAD_QOS_MASK) | 0x40); |
59 | 0 | } else { |
60 | 0 | aad[1] = (uint8_t)((wh->fc[1] & FC1_AAD_MASK) | 0x40); |
61 | 0 | } |
62 | 0 | memcpy(aad + 2, (uint8_t *)wh->addr1, DOT11DECRYPT_MAC_LEN); |
63 | 0 | memcpy(aad + 8, (uint8_t *)wh->addr2, DOT11DECRYPT_MAC_LEN); |
64 | 0 | memcpy(aad + 14, (uint8_t *)wh->addr3, DOT11DECRYPT_MAC_LEN); |
65 | 0 | aad[20] = (uint8_t)(wh->seq[0] & DOT11DECRYPT_SEQ_FRAG_MASK); |
66 | 0 | aad[21] = 0; /* all bits masked */ |
67 | | |
68 | | /* |
69 | | * Construct variable-length portion of AAD based |
70 | | * on whether this is a 4-address frame/QOS frame. |
71 | | */ |
72 | 0 | if (DOT11DECRYPT_IS_4ADDRESS(wh)) { |
73 | 0 | alen += 6; |
74 | 0 | DOT11DECRYPT_ADDR_COPY(aad + 22, |
75 | 0 | ((PDOT11DECRYPT_MAC_FRAME_ADDR4)wh)->addr4); |
76 | 0 | if (DOT11DECRYPT_IS_QOS_DATA(wh)) { |
77 | 0 | PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS qwh4 = |
78 | 0 | (PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS) wh; |
79 | 0 | aad[28] = (uint8_t)(qwh4->qos[0] & 0x0f);/* just priority bits */ |
80 | 0 | aad[29] = 0; |
81 | 0 | alen += 2; |
82 | 0 | } |
83 | 0 | } else { |
84 | 0 | if (DOT11DECRYPT_IS_QOS_DATA(wh)) { |
85 | 0 | PDOT11DECRYPT_MAC_FRAME_QOS qwh = |
86 | 0 | (PDOT11DECRYPT_MAC_FRAME_QOS) wh; |
87 | 0 | aad[22] = (uint8_t)(qwh->qos[0] & 0x0f); /* just priority bits */ |
88 | 0 | aad[23] = 0; |
89 | 0 | alen += 2; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | *aad_len = alen; |
93 | 0 | } |
94 | | |
95 | | /** |
96 | | * IEEE 802.11-2016 12.7.1.2 PRF (Pseudo Random Function) |
97 | | * |
98 | | * @param key Derivation input key. |
99 | | * @param key_len Length of the key in bytes. |
100 | | * @param label Unique label for each different purpose of the PRF (named 'A' in the standard). |
101 | | * @param context Provides context to identify the derived key (named 'B' in the standard). |
102 | | * @param context_len Length of context in bytes. |
103 | | * @param hash_algo Hash algorithm to use for the PRF. |
104 | | * See gcrypt available hash algorithms: |
105 | | * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html |
106 | | * @param[out] output Derived key. |
107 | | * @param output_len Length of derived key in bytes. |
108 | | * @return false on error |
109 | | */ |
110 | 0 | #define MAX_R_LEN 256 |
111 | 0 | #define MAX_TMP_LEN 1024 |
112 | 0 | #define MAX_CONTEXT_LEN 256 |
113 | | |
114 | | bool |
115 | | dot11decrypt_prf(const uint8_t *key, size_t key_len, |
116 | | const char *label, |
117 | | const uint8_t *context, size_t context_len, |
118 | | int hash_algo, |
119 | | uint8_t *output, size_t output_len) |
120 | 0 | { |
121 | 0 | uint8_t R[MAX_R_LEN]; /* Will hold "label || 0 || context || i" */ |
122 | 0 | size_t label_len; |
123 | 0 | uint8_t tmp[MAX_TMP_LEN]; |
124 | 0 | uint16_t hash_len = gcry_md_get_algo_dlen(hash_algo); |
125 | 0 | size_t offset = 0; |
126 | |
|
127 | 0 | if (!key || !label || !context || !output) { |
128 | 0 | return false; |
129 | 0 | } |
130 | | |
131 | 0 | label_len = strlen(label); |
132 | 0 | if (label_len + 1 + context_len + 1 > MAX_R_LEN || |
133 | 0 | output_len > 64) { |
134 | 0 | ws_warning("Invalid input or output sizes"); |
135 | 0 | return false; |
136 | 0 | } |
137 | | |
138 | | /* Fill R with "label || 0 || context || i" */ |
139 | 0 | memcpy(R + offset, label, label_len); |
140 | 0 | offset += label_len; |
141 | 0 | R[offset++] = 0; |
142 | 0 | memcpy(R + offset, context, context_len); |
143 | 0 | offset += context_len; |
144 | |
|
145 | 0 | for (size_t i = 0; i <= output_len * 8 / 160; i++) |
146 | 0 | { |
147 | 0 | R[offset] = (uint8_t)i; |
148 | 0 | if (ws_hmac_buffer(hash_algo, tmp + hash_len * i, R, offset + 1, key, key_len)) { |
149 | 0 | return false; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | memcpy(output, tmp, output_len); |
153 | 0 | return true; |
154 | 0 | } |
155 | | |
156 | | /** |
157 | | * 12.7.1.7.2 Key derivation function (KDF) |
158 | | * |
159 | | * @param key Derivation input key. |
160 | | * @param key_len Length of the key in bytes. |
161 | | * @param label A string identifying the purpose of the keys derived using this KDF. |
162 | | * @param context Provides context to identify the derived key. |
163 | | * @param context_len Length of context in bytes. |
164 | | * @param hash_algo Hash algorithm to use for the KDF. |
165 | | * See gcrypt available hash algorithms: |
166 | | * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html |
167 | | * @param[out] output Derived key. |
168 | | * @param output_len Length of derived key in bytes. |
169 | | * @return false on error |
170 | | */ |
171 | | bool |
172 | | dot11decrypt_kdf(const uint8_t *key, size_t key_len, |
173 | | const char *label, |
174 | | const uint8_t *context, size_t context_len, |
175 | | int hash_algo, |
176 | | uint8_t *output, size_t output_len) |
177 | 0 | { |
178 | 0 | uint8_t R[MAX_R_LEN]; /* Will hold "i || Label || Context || Length" */ |
179 | 0 | uint8_t tmp[MAX_TMP_LEN]; |
180 | 0 | size_t label_len; |
181 | 0 | size_t hash_len = gcry_md_get_algo_dlen(hash_algo); |
182 | 0 | size_t iterations = output_len * 8 / hash_len; |
183 | 0 | uint16_t len_le = GUINT16_TO_LE(output_len * 8); |
184 | 0 | size_t offset = 0; |
185 | 0 | size_t i; |
186 | |
|
187 | 0 | if (!key || !label || !context || !output) { |
188 | 0 | return false; |
189 | 0 | } |
190 | | |
191 | 0 | label_len = strlen(label); |
192 | 0 | if (2 + label_len + context_len + 2 > MAX_R_LEN || |
193 | 0 | iterations * hash_len > MAX_TMP_LEN) { |
194 | 0 | ws_warning("Invalid input sizes"); |
195 | 0 | return false; |
196 | 0 | } |
197 | | |
198 | | /* Fill tmp with "i || Label || Context || Length" */ |
199 | 0 | offset += 2; /* Skip "i" (will be copied in for loop below) */ |
200 | 0 | memcpy(R + offset, label, label_len); |
201 | 0 | offset += label_len; |
202 | 0 | memcpy(R + offset, context, context_len); |
203 | 0 | offset += context_len; |
204 | 0 | memcpy(R + offset, &len_le, 2); |
205 | 0 | offset += 2; |
206 | |
|
207 | 0 | for (i = 0; i < iterations; i++) |
208 | 0 | { |
209 | 0 | uint16_t count_le = GUINT16_TO_LE(i + 1); |
210 | 0 | memcpy(R, &count_le, 2); |
211 | |
|
212 | 0 | if (ws_hmac_buffer(hash_algo, tmp + hash_len * i, R, offset, key, key_len)) { |
213 | 0 | return false; |
214 | 0 | } |
215 | 0 | } |
216 | 0 | memcpy(output, tmp, output_len); |
217 | 0 | return true; |
218 | 0 | } |
219 | | |
220 | | static bool sha256(const uint8_t *data, size_t len, uint8_t output[32]) |
221 | 0 | { |
222 | 0 | gcry_md_hd_t ctx; |
223 | 0 | gcry_error_t result = gcry_md_open(&ctx, GCRY_MD_SHA256, 0); |
224 | 0 | uint8_t *digest; |
225 | |
|
226 | 0 | if (result) { |
227 | 0 | return false; |
228 | 0 | } |
229 | 0 | gcry_md_write(ctx, data, len); |
230 | 0 | digest = gcry_md_read(ctx, GCRY_MD_SHA256); |
231 | 0 | if (!digest) { |
232 | 0 | return false; |
233 | 0 | } |
234 | 0 | memcpy(output, digest, 32); |
235 | 0 | gcry_md_close(ctx); |
236 | 0 | return true; |
237 | 0 | } |
238 | | |
239 | | /** |
240 | | * Derive PMK-R0 and PMKR0Name. See IEEE 802.11-2016 12.7.1.7.3 PMK-R0 |
241 | | * |
242 | | * @param xxkey PSK / MPMK or certain part of MSK. |
243 | | * @param xxkey_len Length of xxkey in bytes. |
244 | | * @param ssid SSID |
245 | | * @param ssid_len Length of SSID in bytes. |
246 | | * @param mdid MDID (Mobility Domain Identifier). |
247 | | * @param r0kh_id PMK-R0 key holder identifier in the Authenticator. |
248 | | * @param r0kh_id_len Length of r0kh_id in bytes. |
249 | | * @param s0kh_id PMK-R0 key holder in the Supplicant (STA mac address) |
250 | | * @param hash_algo Hash algorithm to use for the KDF. |
251 | | * See gcrypt available hash algorithms: |
252 | | * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html |
253 | | * @param[out] pmk_r0 Pairwise master key, first level |
254 | | * @param pmk_r0_len Length of pmk_r0 in bytes. |
255 | | * @param[out] pmk_r0_name Pairwise master key (PMK) R0 name. |
256 | | */ |
257 | | bool |
258 | | dot11decrypt_derive_pmk_r0(const uint8_t *xxkey, size_t xxkey_len, |
259 | | const uint8_t *ssid, size_t ssid_len, |
260 | | const uint8_t mdid[2], |
261 | | const uint8_t *r0kh_id, size_t r0kh_id_len, |
262 | | const uint8_t s0kh_id[DOT11DECRYPT_MAC_LEN], |
263 | | int hash_algo, |
264 | | uint8_t *pmk_r0, |
265 | | size_t *pmk_r0_len, |
266 | | uint8_t pmk_r0_name[16]) |
267 | 0 | { |
268 | 0 | const char *ft_r0n = "FT-R0N"; |
269 | 0 | const size_t ft_r0n_len = strlen(ft_r0n); |
270 | 0 | uint8_t context[MAX_CONTEXT_LEN]; |
271 | 0 | uint8_t r0_key_data[DOT11DECRYPT_WPA_PMK_MAX_LEN + 16]; |
272 | 0 | uint8_t sha256_res[32]; |
273 | 0 | size_t offset = 0; |
274 | 0 | unsigned q = gcry_md_get_algo_dlen(hash_algo); |
275 | |
|
276 | 0 | if (!xxkey || !ssid || !mdid || !r0kh_id || !s0kh_id || |
277 | 0 | !pmk_r0 || !pmk_r0_len || !pmk_r0_name) |
278 | 0 | { |
279 | 0 | return false; |
280 | 0 | } |
281 | 0 | if (1 + ssid_len + 2 + 1 + r0kh_id_len + DOT11DECRYPT_MAC_LEN > MAX_CONTEXT_LEN) |
282 | 0 | { |
283 | 0 | ws_warning("Invalid input sizes"); |
284 | 0 | return false; |
285 | 0 | } |
286 | | |
287 | | // R0-Key-Data = |
288 | | // KDF-Hash-Length(XXKey, "FT-R0", |
289 | | // SSIDlength || SSID || MDID || R0KHlength || R0KH-ID || S0KH-ID) |
290 | | // PMK-R0 = L(R0-Key-Data, 0, Q) * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) |
291 | 0 | context[offset++] = (uint8_t)ssid_len; |
292 | 0 | memcpy(context + offset, ssid, ssid_len); |
293 | 0 | offset += ssid_len; |
294 | 0 | memcpy(context + offset, mdid, 2); |
295 | 0 | offset += 2; |
296 | 0 | context[offset++] = (uint8_t)r0kh_id_len; |
297 | 0 | memcpy(context + offset, r0kh_id, r0kh_id_len); |
298 | 0 | offset += r0kh_id_len; |
299 | 0 | memcpy(context + offset, s0kh_id, DOT11DECRYPT_MAC_LEN); |
300 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
301 | 0 | dot11decrypt_kdf(xxkey, xxkey_len, "FT-R0", context, offset, hash_algo, |
302 | 0 | r0_key_data, q + 16); |
303 | 0 | memcpy(pmk_r0, r0_key_data, q); |
304 | 0 | *pmk_r0_len = q; |
305 | | |
306 | | // PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) |
307 | | // PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)) |
308 | 0 | offset = 0; |
309 | 0 | memcpy(context + offset, ft_r0n, ft_r0n_len); |
310 | 0 | offset += ft_r0n_len; |
311 | 0 | memcpy(context + offset, r0_key_data + q, 16); |
312 | 0 | offset += 16; |
313 | 0 | if(!sha256(context, offset, sha256_res)) |
314 | 0 | return false; |
315 | 0 | memcpy(pmk_r0_name, sha256_res, 16); |
316 | 0 | return true; |
317 | 0 | } |
318 | | |
319 | | /** |
320 | | * Derive PMK-R1 and PMKR1Name. See IEEE 802.11-2016 12.7.1.7.4 PMK-R1 |
321 | | * |
322 | | */ |
323 | | bool |
324 | | dot11decrypt_derive_pmk_r1(const uint8_t *pmk_r0, size_t pmk_r0_len, |
325 | | const uint8_t *pmk_r0_name, |
326 | | const uint8_t *r1kh_id, const uint8_t *s1kh_id, |
327 | | int hash_algo, |
328 | | uint8_t *pmk_r1, size_t *pmk_r1_len, |
329 | | uint8_t *pmk_r1_name) |
330 | 0 | { |
331 | 0 | const char *ft_r1n = "FT-R1N"; |
332 | 0 | const size_t ft_r1n_len = strlen(ft_r1n); |
333 | | // context len = MAX(R1KH-ID || S1KH-ID, "FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID) |
334 | 0 | uint8_t context[6 + 16 + 6 + 6]; |
335 | 0 | uint8_t sha256_res[32]; |
336 | 0 | size_t offset = 0; |
337 | |
|
338 | 0 | if (!pmk_r0 || !pmk_r0_name || !r1kh_id || !s1kh_id || |
339 | 0 | !pmk_r1 || !pmk_r1_len || !pmk_r1_name) |
340 | 0 | { |
341 | 0 | return false; |
342 | 0 | } |
343 | 0 | *pmk_r1_len = gcry_md_get_algo_dlen(hash_algo); |
344 | | |
345 | | // PMK-R1 = KDF-Hash-Length(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) |
346 | 0 | memcpy(context + offset, r1kh_id, DOT11DECRYPT_MAC_LEN); |
347 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
348 | 0 | memcpy(context + offset, s1kh_id, DOT11DECRYPT_MAC_LEN); |
349 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
350 | 0 | dot11decrypt_kdf(pmk_r0, pmk_r0_len, "FT-R1", context, offset, hash_algo, |
351 | 0 | pmk_r1, *pmk_r1_len); |
352 | | |
353 | | // PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID)) |
354 | 0 | offset = 0; |
355 | 0 | memcpy(context + offset, ft_r1n, ft_r1n_len); |
356 | 0 | offset += ft_r1n_len; |
357 | 0 | memcpy(context + offset, pmk_r0_name, 16); |
358 | 0 | offset += 16; |
359 | 0 | memcpy(context + offset, r1kh_id, DOT11DECRYPT_MAC_LEN); |
360 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
361 | 0 | memcpy(context + offset, s1kh_id, DOT11DECRYPT_MAC_LEN); |
362 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
363 | 0 | if(!sha256(context, offset, sha256_res)) |
364 | 0 | return false; |
365 | 0 | memcpy(pmk_r1_name, sha256_res, 16); |
366 | 0 | return true; |
367 | 0 | } |
368 | | |
369 | | /** |
370 | | * Derive PTK for FT AKMS. See IEE 802.11-2016 12.7.1.7.5 PTK |
371 | | * |
372 | | * PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR) |
373 | | * PTKName = Truncate-128( |
374 | | * SHA-256(PMKR1Name || "FT-PTKN" || SNonce || ANonce || BSSID || STA-ADDR)) |
375 | | */ |
376 | | bool |
377 | | dot11decrypt_derive_ft_ptk(const uint8_t *pmk_r1, size_t pmk_r1_len, |
378 | | const uint8_t *pmk_r1_name _U_, |
379 | | const uint8_t *snonce, const uint8_t *anonce, |
380 | | const uint8_t *bssid, const uint8_t *sta_addr, |
381 | | int hash_algo, |
382 | | uint8_t *ptk, const size_t ptk_len, uint8_t *ptk_name _U_) |
383 | 0 | { |
384 | 0 | uint8_t context[32 + 32 + 6 + 6]; |
385 | 0 | unsigned offset = 0; |
386 | | |
387 | | // PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR) |
388 | 0 | memcpy(context + offset, snonce, 32); |
389 | 0 | offset += 32; |
390 | 0 | memcpy(context + offset, anonce, 32); |
391 | 0 | offset += 32; |
392 | 0 | memcpy(context + offset, bssid, DOT11DECRYPT_MAC_LEN); |
393 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
394 | 0 | memcpy(context + offset, sta_addr, DOT11DECRYPT_MAC_LEN); |
395 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
396 | 0 | dot11decrypt_kdf(pmk_r1, pmk_r1_len, "FT-PTK", context, offset, hash_algo, |
397 | 0 | ptk, ptk_len); |
398 | | |
399 | | // TODO derive PTKName |
400 | | return true; |
401 | 0 | } |
402 | | |
403 | | /* |
404 | | * Editor modelines |
405 | | * |
406 | | * Local Variables: |
407 | | * c-basic-offset: 4 |
408 | | * tab-width: 8 |
409 | | * indent-tabs-mode: nil |
410 | | * End: |
411 | | * |
412 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
413 | | * :indentSize=4:tabSize=8:noTabs=true: |
414 | | */ |