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