/src/wireshark/epan/crypt/dot11decrypt.c
Line | Count | Source |
1 | | /* dot11decrypt.c |
2 | | * |
3 | | * Copyright (c) 2006 CACE Technologies, Davis (California) |
4 | | * All rights reserved. |
5 | | * |
6 | | * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) |
7 | | */ |
8 | | |
9 | | /****************************************************************************/ |
10 | | /* File includes */ |
11 | | |
12 | | #include "config.h" |
13 | | /* Keep this first after config.h so that WS_LOG_DOMAIN is set correctly. */ |
14 | | #include "dot11decrypt_debug.h" |
15 | | |
16 | | #include <stdint.h> |
17 | | #include <glib.h> |
18 | | |
19 | | #include <wsutil/wsgcrypt.h> |
20 | | #include <wsutil/pint.h> |
21 | | |
22 | | #include <epan/proto.h> /* for DISSECTOR_ASSERT. */ |
23 | | #include <epan/strutil.h> |
24 | | |
25 | | #include "dot11decrypt_util.h" |
26 | | #include "dot11decrypt_system.h" |
27 | | #include "dot11decrypt_int.h" |
28 | | |
29 | | #include "wep-wpadefs.h" |
30 | | |
31 | | |
32 | | /****************************************************************************/ |
33 | | static int Dot11DecryptGetKckLen(int akm, size_t pmk_len); |
34 | | static int Dot11DecryptGetTkLen(int cipher); |
35 | | static int Dot11DecryptGetKekLen(int akm, size_t pmk_len); |
36 | | static int Dot11DecryptGetPtkLen(int akm, int cipher, size_t pmk_len); |
37 | | static int Dot11DecryptGetHashAlgoFromAkm(int akm, size_t pmk_len); |
38 | | |
39 | | /****************************************************************************/ |
40 | | /* Constant definitions */ |
41 | | |
42 | | /* EAPOL definitions */ |
43 | | /** |
44 | | * Length of the EAPOL-Key key confirmation key (KCK) used to calculate |
45 | | * MIC over EAPOL frame and validate an EAPOL packet (128 bits) |
46 | | */ |
47 | | #define DOT11DECRYPT_WPA_KCK_LEN 16 |
48 | | /** |
49 | | *Offset of the Key MIC in the EAPOL packet body |
50 | | */ |
51 | 0 | #define DOT11DECRYPT_WPA_MICKEY_OFFSET 77 |
52 | | /** |
53 | | * Maximum length of the EAPOL packet (it depends on the maximum MAC |
54 | | * frame size) |
55 | | */ |
56 | | #define DOT11DECRYPT_WPA_MAX_EAPOL_LEN 4095 |
57 | | /** |
58 | | * EAPOL Key Descriptor Version 1, used for all EAPOL-Key frames to and |
59 | | * from a STA when neither the group nor pairwise ciphers are CCMP for |
60 | | * Key Descriptor 1. |
61 | | * @note |
62 | | * Defined in 802.11i-2004, page 78 |
63 | | */ |
64 | 0 | #define DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP 1 |
65 | | /** |
66 | | * EAPOL Key Descriptor Version 2, used for all EAPOL-Key frames to and |
67 | | * from a STA when either the pairwise or the group cipher is AES-CCMP |
68 | | * for Key Descriptor 2. |
69 | | * /note |
70 | | * Defined in 802.11i-2004, page 78 |
71 | | */ |
72 | 0 | #define DOT11DECRYPT_WPA_KEY_VER_AES_CCMP 2 |
73 | | |
74 | | /** Define EAPOL Key Descriptor type values: use 254 for WPA and 2 for WPA2 **/ |
75 | 0 | #define DOT11DECRYPT_RSN_WPA_KEY_DESCRIPTOR 254 |
76 | 0 | #define DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR 2 |
77 | | |
78 | | /* PMK to PTK derive functions */ |
79 | 0 | #define DOT11DECRYPT_DERIVE_USING_PRF 0 |
80 | 0 | #define DOT11DECRYPT_DERIVE_USING_KDF 1 |
81 | | /****************************************************************************/ |
82 | | |
83 | | |
84 | | /****************************************************************************/ |
85 | | /* Macro definitions */ |
86 | | |
87 | 0 | #define KCK_OFFSET(akm) (0) |
88 | 0 | #define KEK_OFFSET(akm, pmk_len) ((KCK_OFFSET(akm) + Dot11DecryptGetKckLen(akm, pmk_len) / 8)) |
89 | 0 | #define TK_OFFSET(akm, pmk_len) ((KEK_OFFSET(akm, pmk_len) + Dot11DecryptGetKekLen(akm, pmk_len) / 8)) |
90 | | |
91 | 0 | #define DOT11DECRYPT_GET_KCK(ptk, akm) (ptk + KCK_OFFSET(akm)) |
92 | 0 | #define DOT11DECRYPT_GET_KEK(ptk, akm, pmk_len) (ptk + KEK_OFFSET(akm, pmk_len)) |
93 | 0 | #define DOT11DECRYPT_GET_TK_TKIP(ptk) (ptk + 32) |
94 | 0 | #define DOT11DECRYPT_GET_TK(ptk, akm, pmk_len) (ptk + TK_OFFSET(akm, pmk_len)) |
95 | | |
96 | | #define DOT11DECRYPT_IEEE80211_OUI(oui) (pntohu24(oui) == 0x000fac) |
97 | | |
98 | | /****************************************************************************/ |
99 | | |
100 | | /****************************************************************************/ |
101 | | /* Type definitions */ |
102 | | |
103 | | /* Internal function prototype declarations */ |
104 | | |
105 | | #ifdef __cplusplus |
106 | | extern "C" { |
107 | | #endif |
108 | | |
109 | | /** |
110 | | * It calculates the passphrase-to-PSK mapping recommended for use with |
111 | | * RSNAs. This implementation uses the PBKDF2 method defined in the RFC |
112 | | * 2898. |
113 | | * @param userPwd [IN] pointer to the struct containing a password |
114 | | * (octet string between 8 and 63 octets) and optional SSID octet |
115 | | * string of up to 32 octets (both are usually ASCII but in fact |
116 | | * opaque and can be any encoding.) |
117 | | * @param output [OUT] calculated PSK (to use as PMK in WPA) |
118 | | * @note |
119 | | * Described in 802.11i-2004, page 165 |
120 | | */ |
121 | | static int Dot11DecryptRsnaPwd2Psk( |
122 | | const struct DOT11DECRYPT_KEY_ITEMDATA_PWD *userPwd, |
123 | | unsigned char *output) |
124 | | ; |
125 | | |
126 | | static int Dot11DecryptRsnaMng( |
127 | | unsigned char *decrypt_data, |
128 | | unsigned mac_header_len, |
129 | | unsigned *decrypt_len, |
130 | | PDOT11DECRYPT_KEY_ITEM key, |
131 | | DOT11DECRYPT_SEC_ASSOCIATION *sa) |
132 | | ; |
133 | | |
134 | | static int Dot11DecryptWepMng( |
135 | | PDOT11DECRYPT_CONTEXT ctx, |
136 | | unsigned char *decrypt_data, |
137 | | unsigned mac_header_len, |
138 | | unsigned *decrypt_len, |
139 | | PDOT11DECRYPT_KEY_ITEM key, |
140 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
141 | | ; |
142 | | |
143 | | static int Dot11DecryptRsna4WHandshake( |
144 | | PDOT11DECRYPT_CONTEXT ctx, |
145 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
146 | | const uint8_t *eapol_raw, |
147 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id, |
148 | | const unsigned tot_len); |
149 | | |
150 | | /** |
151 | | * It checks whether the specified key is corrected or not. |
152 | | * @note |
153 | | * For a standard WEP key the length will be changed to the standard |
154 | | * length, and the type changed in a generic WEP key. |
155 | | * @param key [IN] pointer to the key to validate |
156 | | * @return |
157 | | * - true: the key contains valid fields and values |
158 | | * - false: the key has some invalid field or value |
159 | | */ |
160 | | static int Dot11DecryptValidateKey( |
161 | | PDOT11DECRYPT_KEY_ITEM key) |
162 | | ; |
163 | | |
164 | | static int Dot11DecryptRsnaMicCheck( |
165 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
166 | | unsigned char *eapol, |
167 | | unsigned short eapol_len, |
168 | | unsigned char *KCK, |
169 | | unsigned short key_ver, |
170 | | int akm, |
171 | | int pmk_len) |
172 | | ; |
173 | | |
174 | | static int |
175 | | Dot11DecryptFtMicCheck( |
176 | | const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed, |
177 | | const uint8_t *kck, |
178 | | size_t kck_len); |
179 | | |
180 | | static PDOT11DECRYPT_SEC_ASSOCIATION |
181 | | Dot11DecryptGetSa( |
182 | | PDOT11DECRYPT_CONTEXT ctx, |
183 | | const DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
184 | | ; |
185 | | |
186 | | static int Dot11DecryptGetSaAddress( |
187 | | const uint8_t *mac_header, |
188 | | unsigned mac_header_len, |
189 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
190 | | ; |
191 | | |
192 | | static const unsigned char * Dot11DecryptGetStaAddress( |
193 | | const DOT11DECRYPT_MAC_FRAME *frame) |
194 | | ; |
195 | | |
196 | | static const unsigned char * Dot11DecryptGetBssidAddress( |
197 | | const DOT11DECRYPT_MAC_FRAME *frame) |
198 | | ; |
199 | | |
200 | | static uint8_t |
201 | | Dot11DecryptDerivePtk( |
202 | | const DOT11DECRYPT_SEC_ASSOCIATION *sa, |
203 | | const unsigned char *pmk, |
204 | | size_t pmk_len, |
205 | | const unsigned char snonce[32], |
206 | | int key_version, |
207 | | int akm, |
208 | | int cipher, |
209 | | uint8_t *ptk, size_t *ptk_len); |
210 | | |
211 | | static uint8_t |
212 | | Dot11DecryptFtDerivePtk( |
213 | | const PDOT11DECRYPT_CONTEXT ctx, |
214 | | const DOT11DECRYPT_SEC_ASSOCIATION *sa, |
215 | | const PDOT11DECRYPT_KEY_ITEM key, |
216 | | const uint8_t mdid[2], |
217 | | const uint8_t *snonce, |
218 | | const uint8_t *r0kh_id, size_t r0kh_id_len, |
219 | | const uint8_t *r1kh_id, size_t r1kh_id_len _U_, |
220 | | int akm, int cipher, |
221 | | uint8_t *ptk, size_t *ptk_len); |
222 | | |
223 | | /** |
224 | | * @param sa [IN/OUT] pointer to SA that will hold the key |
225 | | * @param data [IN] Frame |
226 | | * @param offset_rsne [IN] RSNE IE offset in the frame |
227 | | * @param offset_fte [IN] Fast BSS Transition IE offset in the frame |
228 | | * @param offset_timeout [IN] Timeout Interval IE offset in the frame |
229 | | * @param offset_link [IN] Link Identifier IE offset in the frame |
230 | | * @param action [IN] Tdls Action code (response or confirm) |
231 | | * |
232 | | * @return |
233 | | * DOT11DECRYPT_RET_SUCCESS if Key has been successfully derived (and MIC verified) |
234 | | * DOT11DECRYPT_RET_UNSUCCESS otherwise |
235 | | */ |
236 | | static int |
237 | | Dot11DecryptTDLSDeriveKey( |
238 | | PDOT11DECRYPT_SEC_ASSOCIATION sa, |
239 | | const uint8_t *data, |
240 | | unsigned offset_rsne, |
241 | | unsigned offset_fte, |
242 | | unsigned offset_timeout, |
243 | | unsigned offset_link, |
244 | | uint8_t action) |
245 | | ; |
246 | | #ifdef __cplusplus |
247 | | } |
248 | | #endif |
249 | | |
250 | | /****************************************************************************/ |
251 | | |
252 | | /****************************************************************************/ |
253 | | /* Exported function definitions */ |
254 | | |
255 | | #ifdef __cplusplus |
256 | | extern "C" { |
257 | | #endif |
258 | | |
259 | | static const uint8_t broadcast_mac[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
260 | | |
261 | 0 | #define TKIP_GROUP_KEY_LEN 32 |
262 | 0 | #define CCMP_GROUP_KEY_LEN 16 |
263 | | |
264 | 0 | #define EAPOL_RSN_KEY_LEN 95 |
265 | | |
266 | | /* Minimum possible key data size (at least one GTK KDE with CCMP key) */ |
267 | 0 | #define GROUP_KEY_MIN_LEN 8 + CCMP_GROUP_KEY_LEN |
268 | | /* Minimum possible group key msg size (group key msg using CCMP as cipher)*/ |
269 | | #define GROUP_KEY_PAYLOAD_LEN_MIN \ |
270 | 0 | (EAPOL_RSN_KEY_LEN + GROUP_KEY_MIN_LEN) |
271 | | |
272 | | static void |
273 | | Dot11DecryptCopyKey(PDOT11DECRYPT_SEC_ASSOCIATION sa, PDOT11DECRYPT_KEY_ITEM key) |
274 | 0 | { |
275 | 0 | if (key!=NULL) { |
276 | 0 | if (sa->key!=NULL) |
277 | 0 | memcpy(key, sa->key, sizeof(DOT11DECRYPT_KEY_ITEM)); |
278 | 0 | else |
279 | 0 | memset(key, 0, sizeof(DOT11DECRYPT_KEY_ITEM)); |
280 | 0 | key->KeyData.Wpa.PtkLen = sa->wpa.ptk_len; |
281 | 0 | memcpy(key->KeyData.Wpa.Ptk, sa->wpa.ptk, sa->wpa.ptk_len); |
282 | 0 | key->KeyData.Wpa.Akm = sa->wpa.akm; |
283 | 0 | key->KeyData.Wpa.Cipher = sa->wpa.cipher; |
284 | 0 | if (sa->wpa.key_ver==DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) |
285 | 0 | key->KeyType=DOT11DECRYPT_KEY_TYPE_TKIP; |
286 | 0 | else if (sa->wpa.key_ver == 0 || sa->wpa.key_ver == 3 || |
287 | 0 | sa->wpa.key_ver == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP) |
288 | 0 | { |
289 | 0 | switch (sa->wpa.cipher) { |
290 | 0 | case 1: |
291 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_WEP_40; |
292 | 0 | break; |
293 | 0 | case 2: |
294 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_TKIP; |
295 | 0 | break; |
296 | 0 | case 4: |
297 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_CCMP; |
298 | 0 | break; |
299 | 0 | case 5: |
300 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_WEP_104; |
301 | 0 | break; |
302 | 0 | case 8: |
303 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_GCMP; |
304 | 0 | break; |
305 | 0 | case 9: |
306 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_GCMP_256; |
307 | 0 | break; |
308 | 0 | case 10: |
309 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_CCMP_256; |
310 | 0 | break; |
311 | 0 | default: |
312 | 0 | key->KeyType = DOT11DECRYPT_KEY_TYPE_UNKNOWN; |
313 | 0 | break; |
314 | | /* NOT SUPPORTED YET |
315 | | case 3: Reserved |
316 | | case 6: BIP-CMAC-128 |
317 | | case 7: Group addressed traffic not allowed |
318 | | case 11: BIP-GMAC-128 |
319 | | case 12: BIP-GMAC-256 |
320 | | case 13: BIP-CMAC-256 */ |
321 | 0 | } |
322 | 0 | } |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | static uint8_t* |
327 | | Dot11DecryptRc4KeyData(const uint8_t *decryption_key, unsigned decryption_key_len, |
328 | | const uint8_t *encrypted_keydata, unsigned encrypted_keydata_len) |
329 | 0 | { |
330 | 0 | gcry_cipher_hd_t rc4_handle; |
331 | 0 | uint8_t dummy[256] = { 0 }; |
332 | 0 | uint8_t *decrypted_key = NULL; |
333 | |
|
334 | 0 | if (gcry_cipher_open (&rc4_handle, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) { |
335 | 0 | return NULL; |
336 | 0 | } |
337 | 0 | if (gcry_cipher_setkey(rc4_handle, decryption_key, decryption_key_len)) { |
338 | 0 | gcry_cipher_close(rc4_handle); |
339 | 0 | return NULL; |
340 | 0 | } |
341 | 0 | decrypted_key = (uint8_t *)g_memdup2(encrypted_keydata, encrypted_keydata_len); |
342 | 0 | if (!decrypted_key) { |
343 | 0 | gcry_cipher_close(rc4_handle); |
344 | 0 | return NULL; |
345 | 0 | } |
346 | | |
347 | | /* Do dummy 256 iterations of the RC4 algorithm (per 802.11i, Draft 3.0, p. 97 line 6) */ |
348 | 0 | gcry_cipher_decrypt(rc4_handle, dummy, 256, NULL, 0); |
349 | 0 | gcry_cipher_decrypt(rc4_handle, decrypted_key, encrypted_keydata_len, NULL, 0); |
350 | 0 | gcry_cipher_close(rc4_handle); |
351 | 0 | return decrypted_key; |
352 | 0 | } |
353 | | |
354 | | static int |
355 | | AES_unwrap( |
356 | | const uint8_t *kek, |
357 | | uint16_t kek_len, |
358 | | const uint8_t *cipher_text, |
359 | | uint16_t cipher_len, |
360 | | uint8_t *output, |
361 | | uint16_t *output_len) |
362 | 0 | { |
363 | 0 | gcry_cipher_hd_t handle; |
364 | |
|
365 | 0 | if (kek == NULL || cipher_len < 16 || cipher_text == NULL) { |
366 | 0 | return 1; /* "should not happen" */ |
367 | 0 | } |
368 | 0 | if (gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_AESWRAP, 0)) { |
369 | 0 | return 1; |
370 | 0 | } |
371 | 0 | if (gcry_cipher_setkey(handle, kek, kek_len)) { |
372 | 0 | gcry_cipher_close(handle); |
373 | 0 | return 1; |
374 | 0 | } |
375 | 0 | if (gcry_cipher_decrypt(handle, output, cipher_len - 8, cipher_text, cipher_len)) { |
376 | 0 | gcry_cipher_close(handle); |
377 | 0 | return 1; |
378 | 0 | } |
379 | 0 | *output_len = cipher_len - 8; |
380 | 0 | gcry_cipher_close(handle); |
381 | 0 | return 0; |
382 | 0 | } |
383 | | |
384 | | int |
385 | | Dot11DecryptDecryptKeyData(PDOT11DECRYPT_CONTEXT ctx, |
386 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
387 | | const unsigned char bssid[DOT11DECRYPT_MAC_LEN], |
388 | | const unsigned char sta[DOT11DECRYPT_MAC_LEN], |
389 | | unsigned char *decrypted_data, unsigned *decrypted_len, |
390 | | PDOT11DECRYPT_KEY_ITEM key) |
391 | 0 | { |
392 | 0 | uint8_t key_version; |
393 | 0 | const uint8_t *key_data; |
394 | 0 | uint16_t key_bytes_len = 0; /* Length of the total key data field */ |
395 | 0 | DOT11DECRYPT_SEC_ASSOCIATION_ID id; |
396 | 0 | PDOT11DECRYPT_SEC_ASSOCIATION sa; |
397 | | |
398 | | /* search for a cached Security Association for current BSSID and AP */ |
399 | 0 | memcpy(id.bssid, bssid, DOT11DECRYPT_MAC_LEN); |
400 | 0 | memcpy(id.sta, sta, DOT11DECRYPT_MAC_LEN); |
401 | 0 | sa = Dot11DecryptGetSa(ctx, &id); |
402 | 0 | if (sa == NULL || !sa->validKey) { |
403 | 0 | ws_debug("No valid SA for BSSID found"); |
404 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
405 | 0 | } |
406 | | |
407 | | /* Decrypt GTK using KEK portion of PTK */ |
408 | 0 | uint8_t *decryption_key = DOT11DECRYPT_GET_KEK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8); |
409 | 0 | unsigned decryption_key_len = Dot11DecryptGetKekLen(sa->wpa.akm, sa->wpa.pmk_len * 8) / 8; |
410 | | |
411 | | /* We skip verifying the MIC of the key. If we were implementing a WPA supplicant we'd want to verify, but for a sniffer it's not needed. */ |
412 | | |
413 | | /* Preparation for decrypting the group key - determine group key data length */ |
414 | | /* depending on whether the pairwise key is TKIP or AES encryption key */ |
415 | 0 | key_version = eapol_parsed->key_version; |
416 | 0 | if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP){ |
417 | | /* TKIP */ |
418 | 0 | key_bytes_len = eapol_parsed->key_len; |
419 | 0 | }else if (key_version == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP){ |
420 | | /* AES */ |
421 | 0 | key_bytes_len = eapol_parsed->key_data_len; |
422 | | |
423 | | /* AES keys must be at least 128 bits = 16 bytes. */ |
424 | 0 | if (key_bytes_len < 16) { |
425 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
426 | 0 | } |
427 | 0 | } else { |
428 | | /* XXX Ideally group cipher suite type from EAPOL message 2 of 4 should be used to */ |
429 | | /* determine key size. As we currently have no way to do this lookup check that key */ |
430 | | /* is at least 16 bytes (IEEE802.11-2024 Table 12-8 Cipher suite key lengths) */ |
431 | 0 | key_bytes_len = eapol_parsed->key_data_len; |
432 | |
|
433 | 0 | if (key_bytes_len < 16) { |
434 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | 0 | if (key_bytes_len > *decrypted_len) { |
439 | 0 | ws_debug("Too large EAPOL key data"); |
440 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
441 | 0 | } |
442 | | |
443 | 0 | if ((key_bytes_len < GROUP_KEY_MIN_LEN) || |
444 | 0 | (eapol_parsed->len < EAPOL_RSN_KEY_LEN) || |
445 | 0 | (key_bytes_len > eapol_parsed->len - EAPOL_RSN_KEY_LEN)) { |
446 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
447 | 0 | } |
448 | | |
449 | | /* Encrypted key is in the information element field of the EAPOL key packet */ |
450 | 0 | key_data = eapol_parsed->key_data; |
451 | |
|
452 | 0 | DEBUG_DUMP("Encrypted Broadcast key", key_data, key_bytes_len, LOG_LEVEL_DEBUG); |
453 | 0 | DEBUG_DUMP("KeyIV", eapol_parsed->key_iv, 16, LOG_LEVEL_DEBUG); |
454 | 0 | DEBUG_DUMP("decryption_key", decryption_key, decryption_key_len, LOG_LEVEL_DEBUG); |
455 | | |
456 | | /* As we have no concept of the prior association request at this point, we need to deduce the */ |
457 | | /* group key cipher from the length of the key bytes. In WPA this is straightforward as the */ |
458 | | /* keybytes just contain the GTK, and the GTK is only in the group handshake, NOT the M3. */ |
459 | | /* In WPA2 its a little more tricky as the M3 keybytes contain an RSN_IE, but the group handshake */ |
460 | | /* does not. Also there are other (variable length) items in the keybytes which we need to account */ |
461 | | /* for to determine the true key length, and thus the group cipher. */ |
462 | |
|
463 | 0 | if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP){ |
464 | | /* TKIP key */ |
465 | | /* Per 802.11i, Draft 3.0 spec, section 8.5.2, p. 97, line 4-8, */ |
466 | | /* group key is decrypted using RC4. Concatenate the IV with the 16 byte EK (PTK+16) to get the decryption key */ |
467 | 0 | uint8_t new_key[32]; |
468 | 0 | uint8_t *data; |
469 | | |
470 | | /* The WPA group key just contains the GTK bytes so deducing the type is straightforward */ |
471 | | /* Note - WPA M3 doesn't contain a group key so we'll only be here for the group handshake */ |
472 | 0 | sa->wpa.key_ver = (key_bytes_len >=TKIP_GROUP_KEY_LEN)?DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP:DOT11DECRYPT_WPA_KEY_VER_AES_CCMP; |
473 | | |
474 | | /* Build the full decryption key based on the IV and part of the pairwise key */ |
475 | 0 | memcpy(new_key, eapol_parsed->key_iv, 16); |
476 | 0 | memcpy(new_key+16, decryption_key, 16); |
477 | 0 | DEBUG_DUMP("FullDecrKey", new_key, 32, LOG_LEVEL_DEBUG); |
478 | 0 | data = Dot11DecryptRc4KeyData(new_key, 32, key_data, key_bytes_len); |
479 | 0 | if (!data) { |
480 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
481 | 0 | } |
482 | 0 | memcpy(decrypted_data, data, key_bytes_len); |
483 | 0 | g_free(data); |
484 | 0 | } else { |
485 | | /* Ideally AKM from EAPOL message 2 of 4 should be used to determine Key-wrap algorithm to use */ |
486 | | /* Though fortunately IEEE802.11-2024 Table 12-11 state that all AKMs use "NIST AES Key Wrap" */ |
487 | | /* algorithm so no AKM lookup is needed. */ |
488 | | |
489 | | /* Unwrap the key; the result is key_bytes_len in length */ |
490 | 0 | if (AES_unwrap(decryption_key, decryption_key_len, key_data, key_bytes_len, |
491 | 0 | decrypted_data, &key_bytes_len)) { |
492 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | 0 | Dot11DecryptCopyKey(sa, key); |
497 | 0 | *decrypted_len = key_bytes_len; |
498 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
499 | 0 | } |
500 | | |
501 | | /** |
502 | | * @param ctx [IN] pointer to the current context |
503 | | * @param id [IN] id of the association (composed by BSSID and MAC of |
504 | | * the station) |
505 | | * @return a pointer of the requested SA. NULL if it doesn't exist. |
506 | | */ |
507 | | static PDOT11DECRYPT_SEC_ASSOCIATION |
508 | | Dot11DecryptGetSa( |
509 | | PDOT11DECRYPT_CONTEXT ctx, |
510 | | const DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
511 | 188 | { |
512 | 188 | return (DOT11DECRYPT_SEC_ASSOCIATION *)g_hash_table_lookup(ctx->sa_hash, id); |
513 | 188 | } |
514 | | |
515 | | static PDOT11DECRYPT_SEC_ASSOCIATION |
516 | | Dot11DecryptNewSa(const DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
517 | 0 | { |
518 | 0 | PDOT11DECRYPT_SEC_ASSOCIATION sa = g_new0(DOT11DECRYPT_SEC_ASSOCIATION, 1); |
519 | 0 | if (sa != NULL) { |
520 | 0 | sa->saId = *id; |
521 | 0 | } |
522 | 0 | return sa; |
523 | 0 | } |
524 | | |
525 | | static PDOT11DECRYPT_SEC_ASSOCIATION |
526 | | Dot11DecryptDupSa(const DOT11DECRYPT_SEC_ASSOCIATION *sa) |
527 | 0 | { |
528 | 0 | PDOT11DECRYPT_SEC_ASSOCIATION new_sa = g_memdup2(sa, sizeof(*sa)); |
529 | 0 | if (new_sa != NULL) { |
530 | 0 | new_sa->next = NULL; |
531 | 0 | } |
532 | 0 | return new_sa; |
533 | 0 | } |
534 | | |
535 | | static DOT11DECRYPT_SEC_ASSOCIATION * |
536 | | Dot11DecryptPrependSa( |
537 | | DOT11DECRYPT_SEC_ASSOCIATION *existing_sa, |
538 | | DOT11DECRYPT_SEC_ASSOCIATION *new_sa) |
539 | 0 | { |
540 | 0 | DOT11DECRYPT_SEC_ASSOCIATION tmp_sa; |
541 | | |
542 | | /* Add new SA first in list, but copy by value into existing record |
543 | | * so that sa_hash need not be updated with new value */ |
544 | 0 | tmp_sa = *existing_sa; |
545 | 0 | *existing_sa = *new_sa; |
546 | 0 | *new_sa = tmp_sa; |
547 | 0 | existing_sa->next = new_sa; |
548 | 0 | return existing_sa; |
549 | 0 | } |
550 | | |
551 | | /* Add SA, keep existing (if any). Return pointer to newly inserted (first) SA */ |
552 | | static PDOT11DECRYPT_SEC_ASSOCIATION |
553 | | Dot11DecryptAddSa( |
554 | | PDOT11DECRYPT_CONTEXT ctx, |
555 | | const DOT11DECRYPT_SEC_ASSOCIATION_ID *id, |
556 | | DOT11DECRYPT_SEC_ASSOCIATION *sa) |
557 | 0 | { |
558 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *existing_sa = Dot11DecryptGetSa(ctx, id); |
559 | 0 | if (existing_sa != NULL) { |
560 | 0 | sa = Dot11DecryptPrependSa(existing_sa, sa); |
561 | 0 | } else { |
562 | 0 | void *key = g_memdup2(id, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID)); |
563 | 0 | g_hash_table_insert(ctx->sa_hash, key, sa); |
564 | 0 | } |
565 | 0 | return sa; |
566 | 0 | } |
567 | | |
568 | | int |
569 | | Dot11DecryptGetKCK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kck) |
570 | 0 | { |
571 | 0 | if (!key || !kck) { |
572 | 0 | return 0; |
573 | 0 | } |
574 | 0 | *kck = DOT11DECRYPT_GET_KCK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm); |
575 | 0 | return Dot11DecryptGetKckLen(key->KeyData.Wpa.Akm, key->KeyData.Wpa.PskLen * 8) / 8; |
576 | 0 | } |
577 | | |
578 | | int |
579 | | Dot11DecryptGetKEK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kek) |
580 | 0 | { |
581 | 0 | if (!key || !kek) { |
582 | 0 | return 0; |
583 | 0 | } |
584 | 0 | *kek = DOT11DECRYPT_GET_KEK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm, key->KeyData.Wpa.PskLen * 8); |
585 | 0 | return Dot11DecryptGetKekLen(key->KeyData.Wpa.Akm, key->KeyData.Wpa.PskLen * 8) / 8; |
586 | 0 | } |
587 | | |
588 | | int |
589 | | Dot11DecryptGetTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **tk) |
590 | 0 | { |
591 | 0 | int len; |
592 | 0 | if (!key || !tk) { |
593 | 0 | return 0; |
594 | 0 | } |
595 | 0 | if (key->KeyType == DOT11DECRYPT_KEY_TYPE_TKIP) { |
596 | 0 | *tk = DOT11DECRYPT_GET_TK_TKIP(key->KeyData.Wpa.Ptk); |
597 | 0 | len = 16; |
598 | 0 | } else { |
599 | 0 | *tk = DOT11DECRYPT_GET_TK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm, key->KeyData.Wpa.PskLen * 8); |
600 | 0 | len = Dot11DecryptGetTkLen(key->KeyData.Wpa.Cipher) / 8; |
601 | 0 | } |
602 | 0 | return len; |
603 | 0 | } |
604 | | |
605 | | int |
606 | | Dot11DecryptGetGTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **gtk) |
607 | 0 | { |
608 | 0 | int len; |
609 | 0 | if (!key || !gtk) { |
610 | 0 | return 0; |
611 | 0 | } |
612 | | |
613 | | /* GTK is stored just as PTK. See comment in Dot11DecryptCopyBroadcastKey */ |
614 | 0 | *gtk = DOT11DECRYPT_GET_TK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm, key->KeyData.Wpa.PskLen); |
615 | 0 | if (key->KeyType == DOT11DECRYPT_KEY_TYPE_TKIP) { |
616 | 0 | len = 16; |
617 | 0 | } else { |
618 | 0 | len = Dot11DecryptGetTkLen(key->KeyData.Wpa.Cipher) / 8; |
619 | 0 | } |
620 | 0 | return len; |
621 | 0 | } |
622 | | |
623 | | int Dot11DecryptScanTdlsForKeys( |
624 | | PDOT11DECRYPT_CONTEXT ctx, |
625 | | const uint8_t *data, |
626 | | const unsigned tot_len) |
627 | 40 | { |
628 | 40 | unsigned offset = 0; |
629 | 40 | unsigned tot_len_left = tot_len; |
630 | 40 | DOT11DECRYPT_SEC_ASSOCIATION_ID id; |
631 | 40 | PDOT11DECRYPT_SEC_ASSOCIATION sa; |
632 | 40 | const uint8_t *initiator, *responder; |
633 | 40 | uint8_t action; |
634 | 40 | unsigned status, offset_rsne = 0, offset_fte = 0, offset_link = 0, offset_timeout = 0; |
635 | 40 | ws_debug("Authentication: TDLS Action Frame"); |
636 | | |
637 | | /* TDLS payload contains a TDLS Action field (802.11-2016 9.6.13) */ |
638 | | |
639 | | /* check if the packet is a TDLS response or confirm */ |
640 | 40 | if (tot_len_left < 1) { |
641 | 0 | ws_debug("Not EAPOL-Key"); |
642 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
643 | 0 | } |
644 | 40 | action = data[offset]; |
645 | 40 | if (action != 1 && action != 2) { |
646 | 14 | ws_debug("Not Response nor confirm"); |
647 | 14 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
648 | 14 | } |
649 | 26 | offset++; |
650 | 26 | tot_len_left--; |
651 | | |
652 | | /* Check for SUCCESS (0) or SUCCESS_POWER_SAVE_MODE (85) Status Code */ |
653 | 26 | if (tot_len_left < 5) { |
654 | 1 | ws_debug("Not EAPOL-Key"); |
655 | 1 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
656 | 1 | } |
657 | 25 | status=pntohu16(data + offset); |
658 | 25 | if (status != 0 && status != 85) { |
659 | 2 | ws_debug("TDLS setup not successful"); |
660 | 2 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
661 | 2 | } |
662 | | |
663 | | /* skip Token + capabilities */ |
664 | 23 | offset += 5; |
665 | | |
666 | | /* search for RSN, Fast BSS Transition, Link Identifier and Timeout Interval IEs */ |
667 | | |
668 | 409 | while(offset < (tot_len - 2)) { |
669 | 406 | uint8_t element_id = data[offset]; |
670 | 406 | uint8_t length = data[offset + 1]; |
671 | 406 | unsigned min_length = length; |
672 | 406 | switch (element_id) { |
673 | 7 | case 48: /* RSN (802.11-2016 9.4.2.35) */ |
674 | 7 | offset_rsne = offset; |
675 | 7 | min_length = 1; |
676 | 7 | break; |
677 | 5 | case 55: /* FTE (802.11-2016 9.4.2.48) */ |
678 | 5 | offset_fte = offset; |
679 | | /* Plus variable length optional parameter(s) */ |
680 | 5 | min_length = 2 + 16 + 32 + 32; |
681 | 5 | break; |
682 | 3 | case 56: /* Timeout Interval (802.11-2016 9.4.2.49) */ |
683 | 3 | offset_timeout = offset; |
684 | 3 | min_length = 1 + 4; |
685 | 3 | break; |
686 | 2 | case 101: /* Link Identifier (802.11-2016 9.4.2.62) */ |
687 | 2 | offset_link = offset; |
688 | 2 | min_length = 6 + 6 + 6; |
689 | 2 | break; |
690 | 406 | } |
691 | | |
692 | 406 | if (length < min_length || tot_len < offset + 2 + length) { |
693 | 20 | ws_debug("Invalid length records in IEs"); |
694 | 20 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
695 | 20 | } |
696 | 386 | offset += 2 + length; |
697 | 386 | } |
698 | | |
699 | 3 | if (offset_rsne == 0 || offset_fte == 0 || |
700 | 0 | offset_timeout == 0 || offset_link == 0) |
701 | 3 | { |
702 | 3 | ws_debug("Cannot Find all necessary IEs"); |
703 | 3 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
704 | 3 | } |
705 | | |
706 | 0 | ws_debug("Found RSNE/Fast BSS/Timeout Interval/Link IEs"); |
707 | | |
708 | | /* Will create a Security Association between 2 STA. Need to get both MAC address */ |
709 | 0 | initiator = &data[offset_link + 8]; |
710 | 0 | responder = &data[offset_link + 14]; |
711 | |
|
712 | 0 | if (memcmp(initiator, responder, DOT11DECRYPT_MAC_LEN) < 0) { |
713 | 0 | memcpy(id.sta, initiator, DOT11DECRYPT_MAC_LEN); |
714 | 0 | memcpy(id.bssid, responder, DOT11DECRYPT_MAC_LEN); |
715 | 0 | } else { |
716 | 0 | memcpy(id.sta, responder, DOT11DECRYPT_MAC_LEN); |
717 | 0 | memcpy(id.bssid, initiator, DOT11DECRYPT_MAC_LEN); |
718 | 0 | } |
719 | | |
720 | | /* Check if already derived this key */ |
721 | 0 | sa = Dot11DecryptGetSa(ctx, &id); |
722 | 0 | PDOT11DECRYPT_SEC_ASSOCIATION iter_sa; |
723 | 0 | for (iter_sa = sa; iter_sa != NULL; iter_sa = iter_sa->next) { |
724 | 0 | if (iter_sa->validKey && |
725 | 0 | memcmp(iter_sa->wpa.nonce, data + offset_fte + 52, |
726 | 0 | DOT11DECRYPT_WPA_NONCE_LEN) == 0) |
727 | 0 | { |
728 | | /* Already have valid key for this SA, no need to redo key derivation */ |
729 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
730 | 0 | } |
731 | 0 | } |
732 | | /* We are opening a new session with the same two STA (previous sa will be kept if any) */ |
733 | 0 | sa = Dot11DecryptNewSa(&id); |
734 | 0 | if (sa == NULL) { |
735 | 0 | ws_warning("Failed to alloc new SA entry"); |
736 | 0 | return DOT11DECRYPT_RET_REQ_DATA; |
737 | 0 | } |
738 | 0 | if (Dot11DecryptTDLSDeriveKey(sa, data, offset_rsne, offset_fte, |
739 | 0 | offset_timeout, offset_link, action) == DOT11DECRYPT_RET_SUCCESS) { |
740 | 0 | Dot11DecryptAddSa(ctx, &id, sa); |
741 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
742 | 0 | } |
743 | 0 | g_free(sa); |
744 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
745 | 0 | } |
746 | | |
747 | | static int |
748 | | Dot11DecryptCopyBroadcastKey( |
749 | | PDOT11DECRYPT_CONTEXT ctx, |
750 | | const uint8_t *gtk, size_t gtk_len, |
751 | | const DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
752 | 0 | { |
753 | 0 | DOT11DECRYPT_SEC_ASSOCIATION_ID broadcast_id; |
754 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *sa; |
755 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *broadcast_sa; |
756 | 0 | int tk_len; |
757 | |
|
758 | 0 | if (!gtk || gtk_len == 0) { |
759 | 0 | ws_debug("No broadcast key found"); |
760 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
761 | 0 | } |
762 | 0 | sa = Dot11DecryptGetSa(ctx, id); |
763 | 0 | if (sa == NULL) { |
764 | 0 | ws_debug("No SA for BSSID found"); |
765 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
766 | 0 | } |
767 | | |
768 | 0 | tk_len = Dot11DecryptGetTkLen(sa->wpa.tmp_group_cipher); |
769 | 0 | if (tk_len < 0 || gtk_len != (size_t)tk_len / 8) { |
770 | 0 | ws_debug("Broadcast key with incorrect length"); |
771 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
772 | 0 | } |
773 | | |
774 | | /* Broadcast SA for the current BSSID */ |
775 | 0 | memcpy(broadcast_id.bssid, id->bssid, DOT11DECRYPT_MAC_LEN); |
776 | 0 | memcpy(broadcast_id.sta, broadcast_mac, DOT11DECRYPT_MAC_LEN); |
777 | |
|
778 | 0 | broadcast_sa = Dot11DecryptNewSa(&broadcast_id); |
779 | 0 | if (broadcast_sa == NULL) { |
780 | 0 | ws_warning("Failed to alloc broadcast sa"); |
781 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
782 | 0 | } |
783 | | |
784 | | /* Retrieve AKMS / cipher etc from handshake message 2 */ |
785 | | |
786 | 0 | broadcast_sa->wpa.key_ver = sa->wpa.key_ver; |
787 | 0 | broadcast_sa->wpa.akm = sa->wpa.akm; |
788 | 0 | broadcast_sa->wpa.cipher = sa->wpa.tmp_group_cipher; |
789 | 0 | broadcast_sa->wpa.ptk_len = sa->wpa.ptk_len; |
790 | 0 | broadcast_sa->validKey = true; |
791 | 0 | DEBUG_DUMP("Broadcast key", gtk, gtk_len, LOG_LEVEL_DEBUG); |
792 | | |
793 | | /* Since this is a GTK we fake it and put it just as PTK so the |
794 | | * Dot11DecryptRsnaMng() function will extract the right piece of |
795 | | * the GTK for decryption. */ |
796 | 0 | memset(broadcast_sa->wpa.ptk, 0, sizeof(broadcast_sa->wpa.ptk)); |
797 | 0 | memcpy(DOT11DECRYPT_GET_TK(broadcast_sa->wpa.ptk, broadcast_sa->wpa.akm, |
798 | 0 | broadcast_sa->wpa.pmk_len * 8), gtk, gtk_len); |
799 | 0 | Dot11DecryptAddSa(ctx, &broadcast_id, broadcast_sa); |
800 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
801 | 0 | } |
802 | | |
803 | | static void Dot11DecryptCreateMloGtkSa( |
804 | | PDOT11DECRYPT_CONTEXT ctx, |
805 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
806 | | DOT11DECRYPT_SEC_ASSOCIATION *ptk_sa |
807 | | ) |
808 | 0 | { |
809 | 0 | if (ptk_sa == NULL || !ptk_sa->validKey || !ptk_sa->wpa.mld) { |
810 | 0 | return; |
811 | 0 | } |
812 | 0 | for (int i = 0; i < eapol_parsed->mlo_gtk_count; ++i) { |
813 | 0 | struct DOT11DECRYPT_EAPOL_PARSED_MLO_GTK *gtk = &eapol_parsed->mlo_gtk[i]; |
814 | |
|
815 | 0 | for (struct DOT11DECRYPT_MLO_LINK_INFO *link = ptk_sa->wpa.mlo_links; |
816 | 0 | link < &ptk_sa->wpa.mlo_links[DOT11DECRYPT_MAX_MLO_LINKS]; |
817 | 0 | ++link) { |
818 | 0 | if (!(link->id_set && link->ap_mac_set && link->sta_mac_set)) |
819 | 0 | continue; |
820 | 0 | if (link->id == gtk->link_id) { |
821 | 0 | DOT11DECRYPT_SEC_ASSOCIATION_ID saId; |
822 | 0 | memcpy(&saId.bssid, link->ap_mac, DOT11DECRYPT_MAC_LEN); |
823 | 0 | memcpy(&saId.sta, link->sta_mac, DOT11DECRYPT_MAC_LEN); |
824 | 0 | ws_debug("Create GTKSA for LinkID %d", link->id); |
825 | 0 | Dot11DecryptCopyBroadcastKey(ctx, gtk->key, gtk->len, &saId); |
826 | 0 | } |
827 | 0 | } |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | | static int |
832 | | Dot11DecryptGroupHandshake( |
833 | | PDOT11DECRYPT_CONTEXT ctx, |
834 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
835 | | const DOT11DECRYPT_SEC_ASSOCIATION_ID *id, |
836 | | const unsigned tot_len) |
837 | 0 | { |
838 | |
|
839 | 0 | if (GROUP_KEY_PAYLOAD_LEN_MIN > tot_len) { |
840 | 0 | ws_debug("Message too short for Group Key"); |
841 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
842 | 0 | } |
843 | 0 | if (eapol_parsed->msg_type != DOT11DECRYPT_HS_MSG_TYPE_GHS_1){ |
844 | 0 | ws_warning("Not Group handshake message 1"); |
845 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
846 | 0 | } |
847 | | |
848 | 0 | Dot11DecryptCopyBroadcastKey(ctx, eapol_parsed->gtk, eapol_parsed->gtk_len, id); |
849 | |
|
850 | 0 | Dot11DecryptCreateMloGtkSa(ctx, eapol_parsed, Dot11DecryptGetSa(ctx, id)); |
851 | |
|
852 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
853 | 0 | } |
854 | | |
855 | | int Dot11DecryptScanEapolForKeys( |
856 | | PDOT11DECRYPT_CONTEXT ctx, |
857 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
858 | | const uint8_t *eapol_raw, |
859 | | const unsigned tot_len, |
860 | | const unsigned char bssid[DOT11DECRYPT_MAC_LEN], |
861 | | const unsigned char sta[DOT11DECRYPT_MAC_LEN]) |
862 | 0 | { |
863 | 0 | DOT11DECRYPT_SEC_ASSOCIATION_ID id; |
864 | | |
865 | | /* Callers provide these guarantees, so let's make them explicit. */ |
866 | 0 | DISSECTOR_ASSERT(tot_len <= DOT11DECRYPT_EAPOL_MAX_LEN); |
867 | |
|
868 | 0 | ws_debug("Authentication: EAPOL packet"); |
869 | | |
870 | | /* check if the key descriptor type is valid (IEEE 802.1X-2004, pg. 27) */ |
871 | 0 | if (/*eapol_parsed->key_type != 0x1 &&*/ /* RC4 Key Descriptor Type (deprecated) */ |
872 | 0 | eapol_parsed->key_type != DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR && /* IEEE 802.11 Key Descriptor Type (WPA2) */ |
873 | 0 | eapol_parsed->key_type != DOT11DECRYPT_RSN_WPA_KEY_DESCRIPTOR) /* 254 = RSN_KEY_DESCRIPTOR - WPA, */ |
874 | 0 | { |
875 | 0 | ws_debug("Not valid key descriptor type"); |
876 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
877 | 0 | } |
878 | | |
879 | | /* search for a cached Security Association for current BSSID and AP */ |
880 | 0 | memcpy(id.bssid, bssid, DOT11DECRYPT_MAC_LEN); |
881 | 0 | memcpy(id.sta, sta, DOT11DECRYPT_MAC_LEN); |
882 | |
|
883 | 0 | switch (eapol_parsed->msg_type) { |
884 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_4WHS_1: |
885 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_4WHS_2: |
886 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_4WHS_3: |
887 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_4WHS_4: |
888 | 0 | return Dot11DecryptRsna4WHandshake(ctx, eapol_parsed, eapol_raw, |
889 | 0 | &id, tot_len); |
890 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_GHS_1: |
891 | 0 | return Dot11DecryptGroupHandshake(ctx, eapol_parsed, &id, tot_len); |
892 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_GHS_2: |
893 | 0 | break; |
894 | 0 | case DOT11DECRYPT_HS_MSG_TYPE_INVALID: |
895 | 0 | default: |
896 | 0 | ws_warning("Invalid message type"); |
897 | 0 | break; |
898 | 0 | } |
899 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
900 | 0 | } |
901 | | |
902 | | static int |
903 | | Dot11DecryptGetNbrOfTkKeys(PDOT11DECRYPT_CONTEXT ctx) |
904 | 83 | { |
905 | 83 | int nbr = 0; |
906 | 83 | for (size_t i = 0; i < ctx->keys_nr; i++) { |
907 | 0 | if (ctx->keys[i].KeyType == DOT11DECRYPT_KEY_TYPE_TK) { |
908 | 0 | nbr++; |
909 | 0 | } |
910 | 0 | } |
911 | 83 | return nbr; |
912 | 83 | } |
913 | | |
914 | | static int |
915 | | Dot11DecryptUsingUserTk( |
916 | | PDOT11DECRYPT_CONTEXT ctx, |
917 | | unsigned char *decrypt_data, |
918 | | unsigned mac_header_len, |
919 | | unsigned *decrypt_len, |
920 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id, |
921 | | DOT11DECRYPT_KEY_ITEM *used_key) |
922 | 0 | { |
923 | 0 | int ret = DOT11DECRYPT_RET_REQ_DATA; |
924 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *sa = Dot11DecryptNewSa(id); |
925 | 0 | DOT11DECRYPT_KEY_ITEM *key; |
926 | 0 | if (sa == NULL) { |
927 | 0 | return ret; |
928 | 0 | } |
929 | | |
930 | 0 | sa->wpa.akm = 2; |
931 | 0 | sa->validKey = true; |
932 | | |
933 | | /* Try decrypt packet with all user TKs applicable ciphers */ |
934 | 0 | for (size_t key_index = 0; key_index < ctx->keys_nr; key_index++) { |
935 | 0 | key = &ctx->keys[key_index]; |
936 | 0 | if (key->KeyType != DOT11DECRYPT_KEY_TYPE_TK) { |
937 | 0 | continue; |
938 | 0 | } |
939 | 0 | int ciphers_to_try[4] = { 0 }; |
940 | 0 | switch (key->Tk.Len) { |
941 | 0 | case DOT11DECRYPT_WEP_40_KEY_LEN: |
942 | 0 | case DOT11DECRYPT_WEP_104_KEY_LEN: |
943 | | /* TBD implement */ |
944 | 0 | continue; |
945 | 0 | case 256 / 8: |
946 | 0 | ciphers_to_try[0] = 9; /* GCMP-256 */ |
947 | 0 | ciphers_to_try[1] = 10; /* CCMP-256 */ |
948 | 0 | break; |
949 | 0 | case 128 / 8: |
950 | 0 | ciphers_to_try[0] = 4; /* CCMP-128 */ |
951 | 0 | ciphers_to_try[1] = 8; /* GCMP-128 */ |
952 | | /* MLO does not allow TKIP */ |
953 | 0 | if (!key->Tk.mld) |
954 | 0 | ciphers_to_try[2] = 2; /* TKIP */ |
955 | 0 | break; |
956 | 0 | default: |
957 | 0 | continue; |
958 | 0 | } |
959 | | |
960 | 0 | sa->key = key; |
961 | |
|
962 | 0 | for (int i = 0; ciphers_to_try[i] != 0; i++) { |
963 | 0 | sa->wpa.cipher = ciphers_to_try[i]; |
964 | 0 | if (sa->wpa.cipher == DOT11DECRYPT_CIPHER_TKIP) { |
965 | 0 | sa->wpa.key_ver = 1; |
966 | 0 | memcpy(DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk), |
967 | 0 | key->Tk.Tk, key->Tk.Len); |
968 | 0 | } else { |
969 | 0 | sa->wpa.key_ver = 2; |
970 | 0 | sa->wpa.akm = 2; |
971 | 0 | memcpy(DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8), |
972 | 0 | key->Tk.Tk, key->Tk.Len); |
973 | 0 | sa->wpa.mld = key->Tk.mld; |
974 | 0 | if (key->Tk.mld) { |
975 | 0 | memcpy(sa->wpa.ap_mld_mac, key->Tk.ap_mld_mac, DOT11DECRYPT_MAC_LEN); |
976 | 0 | memcpy(sa->wpa.sta_mld_mac, key->Tk.sta_mld_mac, DOT11DECRYPT_MAC_LEN); |
977 | 0 | } |
978 | 0 | } |
979 | 0 | sa->wpa.ptk_len = Dot11DecryptGetPtkLen(sa->wpa.akm, sa->wpa.cipher, sa->wpa.pmk_len * 8) / 8; |
980 | 0 | ret = Dot11DecryptRsnaMng(decrypt_data, mac_header_len, decrypt_len, used_key, sa); |
981 | 0 | if (ret == DOT11DECRYPT_RET_SUCCESS) { |
982 | | /* Successfully decrypted using user TK. Add SA formed from user TK so that |
983 | | * subsequent frames can be decrypted much faster using normal code path |
984 | | * without trying each and every user TK entered. |
985 | | */ |
986 | 0 | Dot11DecryptAddSa(ctx, id, sa); |
987 | 0 | return ret; |
988 | 0 | } |
989 | 0 | } |
990 | 0 | } |
991 | 0 | g_free(sa); |
992 | 0 | return ret; |
993 | 0 | } |
994 | | |
995 | | int Dot11DecryptDecryptPacket( |
996 | | PDOT11DECRYPT_CONTEXT ctx, |
997 | | const uint8_t *data, |
998 | | const unsigned mac_header_len, |
999 | | const unsigned tot_len, |
1000 | | unsigned char *decrypt_data, |
1001 | | unsigned *decrypt_len, |
1002 | | PDOT11DECRYPT_KEY_ITEM key) |
1003 | 280 | { |
1004 | 280 | DOT11DECRYPT_SEC_ASSOCIATION_ID id; |
1005 | 280 | DISSECTOR_ASSERT(decrypt_data); |
1006 | 280 | DISSECTOR_ASSERT(decrypt_len); |
1007 | | |
1008 | 280 | if (decrypt_len) { |
1009 | 280 | *decrypt_len = 0; |
1010 | 280 | } |
1011 | 280 | if (ctx==NULL) { |
1012 | 0 | ws_warning("NULL context"); |
1013 | 0 | return DOT11DECRYPT_RET_REQ_DATA; |
1014 | 0 | } |
1015 | 280 | if (data==NULL || tot_len==0) { |
1016 | 0 | ws_debug("NULL data or length=0"); |
1017 | 0 | return DOT11DECRYPT_RET_REQ_DATA; |
1018 | 0 | } |
1019 | | |
1020 | | /* check correct packet size, to avoid wrong elaboration of encryption algorithms */ |
1021 | 280 | if (tot_len < (unsigned)(mac_header_len+DOT11DECRYPT_CRYPTED_DATA_MINLEN)) { |
1022 | 61 | ws_debug("minimum length violated"); |
1023 | 61 | return DOT11DECRYPT_RET_WRONG_DATA_SIZE; |
1024 | 61 | } |
1025 | | |
1026 | | /* Assume that the decrypt_data field is no more than this size. */ |
1027 | 219 | if (tot_len > DOT11DECRYPT_MAX_CAPLEN) { |
1028 | 0 | ws_debug("length too large"); |
1029 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1030 | 0 | } |
1031 | | |
1032 | | /* get STA/BSSID address */ |
1033 | 219 | if (Dot11DecryptGetSaAddress(data, mac_header_len, &id) != DOT11DECRYPT_RET_SUCCESS) { |
1034 | 28 | ws_noisy("STA/BSSID not found"); |
1035 | 28 | return DOT11DECRYPT_RET_REQ_DATA; |
1036 | 28 | } |
1037 | | |
1038 | | /* check if data is encrypted (use the WEP bit in the Frame Control field) */ |
1039 | 191 | if (DOT11DECRYPT_WEP(data[1])==0) { |
1040 | 3 | return DOT11DECRYPT_RET_NO_DATA_ENCRYPTED; |
1041 | 3 | } |
1042 | 188 | PDOT11DECRYPT_SEC_ASSOCIATION sa; |
1043 | | |
1044 | | /* create new header and data to modify */ |
1045 | 188 | *decrypt_len = tot_len; |
1046 | 188 | memcpy(decrypt_data, data, *decrypt_len); |
1047 | | |
1048 | | /* encrypted data */ |
1049 | 188 | ws_noisy("Encrypted data"); |
1050 | | |
1051 | | /* check the Extension IV to distinguish between WEP encryption and WPA encryption */ |
1052 | | /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */ |
1053 | | /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */ |
1054 | | /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */ |
1055 | 188 | if (DOT11DECRYPT_EXTIV(data[mac_header_len + 3]) == 0) { |
1056 | 105 | ws_noisy("WEP encryption"); |
1057 | 105 | return Dot11DecryptWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, &id); |
1058 | 105 | } else { |
1059 | 83 | ws_noisy("TKIP or CCMP encryption"); |
1060 | | |
1061 | | /* If the destination is a multicast address use the group key. This will not work if the AP is using |
1062 | | more than one group key simultaneously. I've not seen this in practice, however. |
1063 | | Usually an AP will rotate between the two key index values of 1 and 2 whenever |
1064 | | it needs to change the group key to be used. */ |
1065 | 83 | if (((const DOT11DECRYPT_MAC_FRAME_ADDR4 *)(data))->addr1[0] & 0x01) { |
1066 | 49 | ws_noisy("Broadcast/Multicast address. This is encrypted with a group key."); |
1067 | | |
1068 | | /* force STA address to broadcast MAC so we load the SA for the groupkey */ |
1069 | 49 | memcpy(id.sta, broadcast_mac, DOT11DECRYPT_MAC_LEN); |
1070 | 49 | } |
1071 | | /* search for a cached Security Association for current BSSID and STA/broadcast MAC */ |
1072 | 83 | int ret = DOT11DECRYPT_RET_REQ_DATA; |
1073 | 83 | sa = Dot11DecryptGetSa(ctx, &id); |
1074 | 83 | if (sa != NULL) { |
1075 | | /* Decrypt the packet using the appropriate SA */ |
1076 | 0 | ret = Dot11DecryptRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa); |
1077 | 0 | } |
1078 | 83 | if (ret != DOT11DECRYPT_RET_SUCCESS && Dot11DecryptGetNbrOfTkKeys(ctx) > 0) { |
1079 | | /* Decryption with known SAs failed. Try decrypt with TK user entries */ |
1080 | 0 | ret = Dot11DecryptUsingUserTk(ctx, decrypt_data, mac_header_len, decrypt_len, &id, key); |
1081 | 0 | } |
1082 | 83 | return ret; |
1083 | 83 | } |
1084 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1085 | 188 | } |
1086 | | |
1087 | | int Dot11DecryptSetKeys( |
1088 | | PDOT11DECRYPT_CONTEXT ctx, |
1089 | | DOT11DECRYPT_KEY_ITEM keys[], |
1090 | | const size_t keys_nr) |
1091 | 32 | { |
1092 | 32 | int i; |
1093 | 32 | int success; |
1094 | | |
1095 | 32 | if (ctx==NULL || keys==NULL) { |
1096 | 0 | ws_warning("NULL context or NULL keys array"); |
1097 | 0 | return 0; |
1098 | 0 | } |
1099 | | |
1100 | 32 | if (keys_nr>DOT11DECRYPT_MAX_KEYS_NR) { |
1101 | 0 | ws_warning("Keys number greater than maximum"); |
1102 | 0 | return 0; |
1103 | 0 | } |
1104 | | |
1105 | | /* clean key and SA collections before setting new ones */ |
1106 | 32 | Dot11DecryptInitContext(ctx); |
1107 | | |
1108 | | /* check and insert keys */ |
1109 | 32 | for (i=0, success=0; i<(int)keys_nr; i++) { |
1110 | 0 | if (Dot11DecryptValidateKey(keys+i)==true) { |
1111 | 0 | if (keys[i].KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PWD && keys[i].UserPwd.SsidLen > 0) { |
1112 | 0 | Dot11DecryptRsnaPwd2Psk(&keys[i].UserPwd, keys[i].KeyData.Wpa.Psk); |
1113 | 0 | keys[i].KeyData.Wpa.PskLen = DOT11DECRYPT_WPA_PWD_PSK_LEN; |
1114 | 0 | } |
1115 | 0 | memcpy(&ctx->keys[success], &keys[i], sizeof(keys[i])); |
1116 | 0 | success++; |
1117 | 0 | } |
1118 | 0 | } |
1119 | | |
1120 | 32 | ctx->keys_nr=success; |
1121 | 32 | return success; |
1122 | 32 | } |
1123 | | |
1124 | | static void |
1125 | | Dot11DecryptCleanKeys( |
1126 | | PDOT11DECRYPT_CONTEXT ctx) |
1127 | 32 | { |
1128 | 32 | if (ctx==NULL) { |
1129 | 0 | ws_warning("NULL context"); |
1130 | 0 | return; |
1131 | 0 | } |
1132 | | |
1133 | 32 | memset(ctx->keys, 0, sizeof(DOT11DECRYPT_KEY_ITEM) * DOT11DECRYPT_MAX_KEYS_NR); |
1134 | | |
1135 | 32 | ctx->keys_nr=0; |
1136 | 32 | ws_debug("Keys collection cleaned!"); |
1137 | 32 | } |
1138 | | |
1139 | | static void |
1140 | | Dot11DecryptCleanSA( |
1141 | | void * first_sa) |
1142 | 0 | { |
1143 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *cur_sa = (DOT11DECRYPT_SEC_ASSOCIATION *)first_sa; |
1144 | 0 | while (cur_sa) { |
1145 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *next_sa = cur_sa->next; |
1146 | 0 | g_free(cur_sa); |
1147 | 0 | cur_sa = next_sa; |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | | static void |
1152 | | Dot11DecryptCleanSecAssoc( |
1153 | | PDOT11DECRYPT_CONTEXT ctx) |
1154 | 32 | { |
1155 | 32 | if (ctx->sa_hash != NULL) { |
1156 | 16 | g_hash_table_destroy(ctx->sa_hash); |
1157 | 16 | ctx->sa_hash = NULL; |
1158 | 16 | } |
1159 | 32 | } |
1160 | | |
1161 | | /* |
1162 | | * XXX - This won't be reliable if a packet containing SSID "B" shows |
1163 | | * up in the middle of a 4-way handshake for SSID "A". |
1164 | | * We should probably use a small array or hash table to keep multiple |
1165 | | * SSIDs. |
1166 | | */ |
1167 | | int Dot11DecryptSetLastSSID( |
1168 | | PDOT11DECRYPT_CONTEXT ctx, |
1169 | | char *pkt_ssid, |
1170 | | size_t pkt_ssid_len) |
1171 | 3.05k | { |
1172 | 3.05k | if (!ctx || !pkt_ssid || pkt_ssid_len < 1 || pkt_ssid_len > WPA_SSID_MAX_SIZE) |
1173 | 2.65k | return DOT11DECRYPT_RET_UNSUCCESS; |
1174 | | |
1175 | 408 | memcpy(ctx->pkt_ssid, pkt_ssid, pkt_ssid_len); |
1176 | 408 | ctx->pkt_ssid_len = pkt_ssid_len; |
1177 | | |
1178 | 408 | return DOT11DECRYPT_RET_SUCCESS; |
1179 | 3.05k | } |
1180 | | |
1181 | | static unsigned |
1182 | | Dot11DecryptSaHash(const void *key) |
1183 | 188 | { |
1184 | 188 | GBytes *bytes = g_bytes_new_static(key, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID)); |
1185 | 188 | unsigned hash = g_bytes_hash(bytes); |
1186 | 188 | g_bytes_unref(bytes); |
1187 | 188 | return hash; |
1188 | 188 | } |
1189 | | |
1190 | | static gboolean |
1191 | | Dot11DecryptIsSaIdEqual(const void *key1, const void *key2) |
1192 | 0 | { |
1193 | 0 | return memcmp(key1, key2, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID)) == 0; |
1194 | 0 | } |
1195 | | |
1196 | | int Dot11DecryptInitContext( |
1197 | | PDOT11DECRYPT_CONTEXT ctx) |
1198 | 32 | { |
1199 | 32 | if (ctx==NULL) { |
1200 | 0 | ws_warning("NULL context"); |
1201 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1202 | 0 | } |
1203 | | |
1204 | 32 | Dot11DecryptCleanKeys(ctx); |
1205 | 32 | Dot11DecryptCleanSecAssoc(ctx); |
1206 | | |
1207 | 32 | ctx->pkt_ssid_len = 0; |
1208 | 32 | ctx->sa_hash = g_hash_table_new_full(Dot11DecryptSaHash, Dot11DecryptIsSaIdEqual, |
1209 | 32 | g_free, Dot11DecryptCleanSA); |
1210 | 32 | if (ctx->sa_hash == NULL) { |
1211 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1212 | 0 | } |
1213 | | |
1214 | 32 | ws_debug("Context initialized!"); |
1215 | 32 | return DOT11DECRYPT_RET_SUCCESS; |
1216 | 32 | } |
1217 | | |
1218 | | int Dot11DecryptDestroyContext( |
1219 | | PDOT11DECRYPT_CONTEXT ctx) |
1220 | 0 | { |
1221 | 0 | if (ctx==NULL) { |
1222 | 0 | ws_warning("NULL context"); |
1223 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1224 | 0 | } |
1225 | | |
1226 | 0 | Dot11DecryptCleanKeys(ctx); |
1227 | 0 | Dot11DecryptCleanSecAssoc(ctx); |
1228 | |
|
1229 | 0 | ws_debug("Context destroyed!"); |
1230 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
1231 | 0 | } |
1232 | | |
1233 | | #ifdef __cplusplus |
1234 | | } |
1235 | | #endif |
1236 | | |
1237 | | /****************************************************************************/ |
1238 | | |
1239 | | /****************************************************************************/ |
1240 | | /* Internal function definitions */ |
1241 | | |
1242 | | #ifdef __cplusplus |
1243 | | extern "C" { |
1244 | | #endif |
1245 | | |
1246 | | static int |
1247 | | Dot11DecryptRsnaMng( |
1248 | | unsigned char *decrypt_data, |
1249 | | unsigned mac_header_len, |
1250 | | unsigned *decrypt_len, |
1251 | | PDOT11DECRYPT_KEY_ITEM key, |
1252 | | DOT11DECRYPT_SEC_ASSOCIATION *sa) |
1253 | 0 | { |
1254 | 0 | int ret = 1; |
1255 | 0 | unsigned char *try_data; |
1256 | 0 | unsigned try_data_len = *decrypt_len; |
1257 | | |
1258 | | /* There should be at least 1 byte encrypted and TKIP/CCMP/GCMP header |
1259 | | should be present. Trailer is at least 8 bytes and it's precisely checked |
1260 | | afterwards. */ |
1261 | 0 | if (*decrypt_len < mac_header_len + DOT11DECRYPT_RSNA_HEADER + 1 + |
1262 | 0 | DOT11DECRYPT_RSNA_MIN_TRAILER) { |
1263 | 0 | ws_debug("Invalid decryption length"); |
1264 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1265 | 0 | } |
1266 | | |
1267 | | /* allocate a temp buffer for the decryption loop */ |
1268 | 0 | try_data=(unsigned char *)g_malloc(try_data_len); |
1269 | | |
1270 | | /* start of loop added by GCS */ |
1271 | 0 | for(/* sa */; sa != NULL ;sa=sa->next) { |
1272 | 0 | const uint8_t *ap_mld_mac = NULL, *sta_mld_mac = NULL; |
1273 | |
|
1274 | 0 | if (sa->validKey==false) { |
1275 | 0 | ws_noisy("Key not yet valid"); |
1276 | 0 | continue; |
1277 | 0 | } |
1278 | | |
1279 | | /* copy the encrypted data into a temp buffer */ |
1280 | 0 | memcpy(try_data, decrypt_data, *decrypt_len); |
1281 | |
|
1282 | 0 | if (sa->wpa.mld) { |
1283 | 0 | ap_mld_mac = sa->wpa.ap_mld_mac; |
1284 | 0 | sta_mld_mac = sa->wpa.sta_mld_mac; |
1285 | 0 | } |
1286 | | /* Select decryption method based on EAPOL Key Descriptor Version and negotiated AKM |
1287 | | * with selected cipher suite. Refer to IEEE 802.11-2020: |
1288 | | * 12.7.2 EAPOL-Key frames |
1289 | | * 12.2.4 RSNA establishment |
1290 | | * 12.7 Keys and key distribution |
1291 | | * Table 9-149-Cipher suite selectors |
1292 | | */ |
1293 | |
|
1294 | 0 | if (sa->wpa.key_ver == 1 || sa->wpa.cipher == DOT11DECRYPT_CIPHER_TKIP) { |
1295 | | /* CCMP -> HMAC-MD5 is the EAPOL-Key MIC, RC4 is the EAPOL-Key encryption algorithm */ |
1296 | 0 | ws_noisy("TKIP"); |
1297 | 0 | DEBUG_DUMP("ptk", sa->wpa.ptk, 64, LOG_LEVEL_NOISY); |
1298 | 0 | DEBUG_DUMP("ptk portion used", DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk), |
1299 | 0 | 16, LOG_LEVEL_NOISY); |
1300 | |
|
1301 | 0 | ret = Dot11DecryptTkipDecrypt(try_data, mac_header_len, *decrypt_len, |
1302 | 0 | DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk)); |
1303 | 0 | if (ret) { |
1304 | 0 | if (ret < 0) { |
1305 | 0 | ws_debug("Invalid decryption length"); |
1306 | 0 | g_free(try_data); |
1307 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1308 | 0 | } |
1309 | 0 | ws_noisy("TKIP failed!"); |
1310 | 0 | continue; |
1311 | 0 | } |
1312 | | |
1313 | 0 | ws_noisy("TKIP DECRYPTED!!!"); |
1314 | | /* remove MIC and ICV from the end of packet */ |
1315 | 0 | *decrypt_len -= DOT11DECRYPT_TKIP_MICLEN + DOT11DECRYPT_WEP_ICV; |
1316 | 0 | break; |
1317 | 0 | } else if (sa->wpa.cipher == DOT11DECRYPT_CIPHER_GCMP || |
1318 | 0 | sa->wpa.cipher == DOT11DECRYPT_CIPHER_GCMP256) |
1319 | 0 | { |
1320 | 0 | ws_noisy("GCMP"); |
1321 | |
|
1322 | 0 | ret = Dot11DecryptGcmpDecrypt(try_data, mac_header_len, (int)*decrypt_len, |
1323 | 0 | DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8), |
1324 | 0 | Dot11DecryptGetTkLen(sa->wpa.cipher) / 8, |
1325 | 0 | ap_mld_mac, sta_mld_mac); |
1326 | 0 | if (ret) { |
1327 | 0 | if (ret < 0) { |
1328 | 0 | ws_debug("Invalid decryption length"); |
1329 | 0 | g_free(try_data); |
1330 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1331 | 0 | } |
1332 | 0 | continue; |
1333 | 0 | } |
1334 | 0 | ws_noisy("GCMP DECRYPTED!!!"); |
1335 | | /* remove MIC from the end of packet */ |
1336 | 0 | *decrypt_len -= DOT11DECRYPT_GCMP_TRAILER; |
1337 | 0 | break; |
1338 | 0 | } else { |
1339 | | /* AES-CCMP -> HMAC-SHA1-128 is the EAPOL-Key MIC, AES wep_key wrap is the EAPOL-Key encryption algorithm */ |
1340 | 0 | ws_noisy("CCMP"); |
1341 | |
|
1342 | 0 | unsigned trailer = sa->wpa.cipher != 10 ? DOT11DECRYPT_CCMP_TRAILER : DOT11DECRYPT_CCMP_256_TRAILER; |
1343 | |
|
1344 | 0 | ret = Dot11DecryptCcmpDecrypt(try_data, mac_header_len, (int)*decrypt_len, |
1345 | 0 | DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8), |
1346 | 0 | Dot11DecryptGetTkLen(sa->wpa.cipher) / 8, |
1347 | 0 | trailer, ap_mld_mac, sta_mld_mac); |
1348 | 0 | if (ret) { |
1349 | 0 | if (ret < 0) { |
1350 | 0 | ws_debug("Invalid decryption length"); |
1351 | 0 | g_free(try_data); |
1352 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1353 | 0 | } |
1354 | 0 | continue; |
1355 | 0 | } |
1356 | 0 | ws_noisy("CCMP DECRYPTED!!!"); |
1357 | | /* remove MIC from the end of packet */ |
1358 | 0 | *decrypt_len -= trailer; |
1359 | 0 | break; |
1360 | 0 | } |
1361 | 0 | } |
1362 | | /* end of loop */ |
1363 | | |
1364 | | /* none of the keys worked */ |
1365 | 0 | if(sa == NULL) { |
1366 | 0 | g_free(try_data); |
1367 | 0 | return ret; |
1368 | 0 | } |
1369 | | |
1370 | | /* We should have decrypted at least 1 byte successfully and |
1371 | | subtracted security trailer. */ |
1372 | 0 | ws_assert(*decrypt_len > mac_header_len + DOT11DECRYPT_RSNA_HEADER && |
1373 | 0 | *decrypt_len < try_data_len); |
1374 | | |
1375 | | /* remove protection bit */ |
1376 | 0 | decrypt_data[1]&=0xBF; |
1377 | | |
1378 | | /* remove TKIP/CCMP/GCMP header */ |
1379 | 0 | *decrypt_len -= DOT11DECRYPT_RSNA_HEADER; |
1380 | | |
1381 | | /* copy the decrypted data into the decrypt buffer GCS*/ |
1382 | 0 | memcpy(decrypt_data + mac_header_len, |
1383 | 0 | try_data + mac_header_len + DOT11DECRYPT_RSNA_HEADER, |
1384 | 0 | *decrypt_len - mac_header_len); |
1385 | 0 | g_free(try_data); |
1386 | |
|
1387 | 0 | Dot11DecryptCopyKey(sa, key); |
1388 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
1389 | 0 | } |
1390 | | |
1391 | | static int |
1392 | | Dot11DecryptWepMng( |
1393 | | PDOT11DECRYPT_CONTEXT ctx, |
1394 | | unsigned char *decrypt_data, |
1395 | | unsigned mac_header_len, |
1396 | | unsigned *decrypt_len, |
1397 | | PDOT11DECRYPT_KEY_ITEM key, |
1398 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
1399 | 105 | { |
1400 | 105 | unsigned char wep_key[DOT11DECRYPT_WEP_KEY_MAXLEN+DOT11DECRYPT_WEP_IVLEN]; |
1401 | 105 | size_t keylen; |
1402 | 105 | int ret_value=1; |
1403 | 105 | int key_index; |
1404 | 105 | DOT11DECRYPT_KEY_ITEM *tmp_key; |
1405 | 105 | uint8_t useCache=false; |
1406 | 105 | unsigned char *try_data; |
1407 | 105 | DOT11DECRYPT_SEC_ASSOCIATION *sa; |
1408 | 105 | unsigned try_data_len = *decrypt_len; |
1409 | 105 | ssize_t ciphertext_len = (ssize_t)*decrypt_len - |
1410 | 105 | (ssize_t)(mac_header_len + DOT11DECRYPT_WEP_HEADER + DOT11DECRYPT_WEP_TRAILER); |
1411 | | |
1412 | 105 | if (ciphertext_len < 1) { |
1413 | 0 | ws_debug("Decryption length too short"); |
1414 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1415 | 0 | } |
1416 | | |
1417 | 105 | try_data = (unsigned char *)g_malloc(try_data_len); |
1418 | | |
1419 | | /* get the Security Association structure for the STA and AP */ |
1420 | | |
1421 | | /* For WEP the sa is used only for caching. When no sa exists all user |
1422 | | * entered WEP keys are checked and on successful packet decryption an |
1423 | | * sa is formed caching the key used for decryption. |
1424 | | */ |
1425 | 105 | sa = Dot11DecryptGetSa(ctx, id); |
1426 | 105 | if (sa != NULL && sa->key != NULL) { |
1427 | 0 | useCache = true; |
1428 | 0 | } |
1429 | | |
1430 | 105 | for (key_index=0; key_index<(int)ctx->keys_nr; key_index++) { |
1431 | | /* use the cached one, or try all keys */ |
1432 | 0 | if (!useCache) { |
1433 | 0 | tmp_key=&ctx->keys[key_index]; |
1434 | 0 | } else { |
1435 | 0 | if (sa->key!=NULL && sa->key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) { |
1436 | 0 | ws_noisy("Try cached WEP key..."); |
1437 | 0 | tmp_key=sa->key; |
1438 | 0 | } else { |
1439 | 0 | ws_noisy("Cached key is not valid, try another WEP key..."); |
1440 | 0 | tmp_key=&ctx->keys[key_index]; |
1441 | 0 | } |
1442 | 0 | } |
1443 | | |
1444 | | /* obviously, try only WEP keys... */ |
1445 | 0 | if (tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) { |
1446 | 0 | ws_noisy("Try WEP key..."); |
1447 | |
|
1448 | 0 | memset(wep_key, 0, sizeof(wep_key)); |
1449 | 0 | memcpy(try_data, decrypt_data, *decrypt_len); |
1450 | | |
1451 | | /* Construct the WEP seed: copy the IV in first 3 bytes and then the WEP key (refer to 802-11i-2004, 8.2.1.4.3, pag. 36) */ |
1452 | 0 | memcpy(wep_key, try_data+mac_header_len, DOT11DECRYPT_WEP_IVLEN); |
1453 | 0 | keylen=tmp_key->KeyData.Wep.WepKeyLen; |
1454 | 0 | memcpy(wep_key+DOT11DECRYPT_WEP_IVLEN, tmp_key->KeyData.Wep.WepKey, keylen); |
1455 | |
|
1456 | 0 | ret_value=Dot11DecryptWepDecrypt(wep_key, |
1457 | 0 | keylen+DOT11DECRYPT_WEP_IVLEN, |
1458 | 0 | try_data + (mac_header_len + DOT11DECRYPT_WEP_HEADER), |
1459 | 0 | ciphertext_len); |
1460 | |
|
1461 | 0 | if (ret_value == DOT11DECRYPT_RET_SUCCESS) |
1462 | 0 | memcpy(decrypt_data, try_data, *decrypt_len); |
1463 | 0 | } |
1464 | |
|
1465 | 0 | if (!ret_value && tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) { |
1466 | | /* the tried key is the correct one, cache it in the Security Association */ |
1467 | | |
1468 | | /* Form an SA if one does not exist already */ |
1469 | 0 | if (sa == NULL) { |
1470 | 0 | sa = Dot11DecryptNewSa(id); |
1471 | 0 | if (sa == NULL) { |
1472 | 0 | ws_warning("Failed to alloc sa for WEP"); |
1473 | 0 | ret_value = DOT11DECRYPT_RET_UNSUCCESS; |
1474 | 0 | break; |
1475 | 0 | } |
1476 | 0 | sa = Dot11DecryptAddSa(ctx, id, sa); |
1477 | 0 | } |
1478 | 0 | sa->key=tmp_key; |
1479 | |
|
1480 | 0 | if (key!=NULL) { |
1481 | 0 | memcpy(key, sa->key, sizeof(DOT11DECRYPT_KEY_ITEM)); |
1482 | 0 | key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP; |
1483 | 0 | } |
1484 | |
|
1485 | 0 | break; |
1486 | 0 | } else { |
1487 | | /* the cached key was not valid, try other keys */ |
1488 | |
|
1489 | 0 | if (useCache==true) { |
1490 | 0 | useCache=false; |
1491 | 0 | key_index--; |
1492 | 0 | } |
1493 | 0 | } |
1494 | 0 | } |
1495 | | |
1496 | 105 | g_free(try_data); |
1497 | 105 | if (ret_value) |
1498 | 105 | return DOT11DECRYPT_RET_UNSUCCESS; |
1499 | | |
1500 | 0 | ws_noisy("WEP DECRYPTED!!!"); |
1501 | | |
1502 | | /* remove IV(4bytes) and ICV (4bytes) from the packet */ |
1503 | 0 | *decrypt_len -= 4 + 4; |
1504 | | |
1505 | | /* remove protection bit */ |
1506 | 0 | decrypt_data[1]&=0xBF; |
1507 | |
|
1508 | 0 | memmove(decrypt_data + mac_header_len, |
1509 | 0 | decrypt_data + mac_header_len + DOT11DECRYPT_WEP_IVLEN + DOT11DECRYPT_WEP_KIDLEN, |
1510 | 0 | *decrypt_len - mac_header_len); |
1511 | |
|
1512 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
1513 | 105 | } |
1514 | | |
1515 | | /* From IEEE 802.11-2024 Table 9-190—AKM suite selectors */ |
1516 | | static bool Dot11DecryptIsFtAkm(int akm) |
1517 | 909 | { |
1518 | 909 | switch (akm) { |
1519 | 0 | case 3: |
1520 | 0 | case 4: |
1521 | 0 | case 9: |
1522 | 0 | case 13: |
1523 | 0 | case 25: /* FT SAE EXT KEY */ |
1524 | 0 | return true; |
1525 | 909 | } |
1526 | 909 | return false; |
1527 | 909 | } |
1528 | | |
1529 | | /* Get xxkey portion of MSK */ |
1530 | | /* From IEEE 802.11-2016 12.7.1.7.3 PMK-R0 */ |
1531 | | static const uint8_t * |
1532 | | Dot11DecryptGetXXKeyFromMSK(const uint8_t *msk, size_t msk_len, |
1533 | | int akm, size_t *xxkey_len) |
1534 | 0 | { |
1535 | 0 | if (!xxkey_len) { |
1536 | 0 | return NULL; |
1537 | 0 | } |
1538 | 0 | switch (akm) { |
1539 | 0 | case 3: |
1540 | 0 | if (msk_len < 64) { |
1541 | 0 | return NULL; |
1542 | 0 | } |
1543 | 0 | *xxkey_len = 32; |
1544 | 0 | return msk + 32; |
1545 | 0 | case 13: |
1546 | 0 | if (msk_len < 48) { |
1547 | 0 | return NULL; |
1548 | 0 | } |
1549 | 0 | *xxkey_len = 48; |
1550 | 0 | return msk; |
1551 | 0 | default: |
1552 | 0 | return NULL; |
1553 | 0 | } |
1554 | 0 | } |
1555 | | |
1556 | | /* From IEEE 802.11-2024 12.7.1.3 Pairwise key hierarchy */ |
1557 | | static void |
1558 | | Dot11DecryptDerivePmkFromMsk(const uint8_t *msk, uint8_t msk_len, int akm, |
1559 | | uint8_t *pmk, uint8_t *pmk_len) |
1560 | 0 | { |
1561 | 0 | if (!msk || !pmk || !pmk_len) { |
1562 | 0 | return; |
1563 | 0 | } |
1564 | | // When using AKM suite selector 00-0F-AC:12, the length of the PMK, PMK_bits, |
1565 | | // shall be 384 bits. With all other AKM suite selectors, the length of the PMK, |
1566 | | // PMK_bits, shall be 256 bits. |
1567 | 0 | if (akm == 12) { |
1568 | 0 | *pmk_len = 384 / 8; |
1569 | 0 | } else { |
1570 | 0 | *pmk_len = 256 / 8; |
1571 | 0 | } |
1572 | 0 | if ((uint8_t)(msk_len + *pmk_len) < msk_len) { |
1573 | 0 | *pmk_len = 0; |
1574 | 0 | return; |
1575 | 0 | } |
1576 | | // PMK = L(MSK, 0, PMK_bits). |
1577 | 0 | memcpy(pmk, msk, *pmk_len); |
1578 | 0 | } |
1579 | | |
1580 | | static bool |
1581 | | Dot11DecryptIsWpaKeyType(uint8_t key_type) |
1582 | 0 | { |
1583 | 0 | switch (key_type) { |
1584 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PWD: |
1585 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PSK: |
1586 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PMK: |
1587 | 0 | case DOT11DECRYPT_KEY_TYPE_MSK: |
1588 | 0 | return true; |
1589 | 0 | } |
1590 | 0 | return false; |
1591 | 0 | } |
1592 | | |
1593 | | static bool |
1594 | | Dot11DecryptIsPwdWildcardSsid(const PDOT11DECRYPT_CONTEXT ctx, |
1595 | | const DOT11DECRYPT_KEY_ITEM *key_item) |
1596 | 0 | { |
1597 | 0 | if (!ctx || !key_item || key_item->KeyType != DOT11DECRYPT_KEY_TYPE_WPA_PWD) { |
1598 | 0 | return false; |
1599 | 0 | } |
1600 | 0 | if (key_item->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && |
1601 | 0 | ctx->pkt_ssid_len <= DOT11DECRYPT_WPA_SSID_MAX_LEN) { |
1602 | 0 | return true; |
1603 | 0 | } |
1604 | 0 | return false; |
1605 | 0 | } |
1606 | | |
1607 | | /* Refer to IEEE 802.11i-2004, 8.5.3, pag. 85 */ |
1608 | | static int |
1609 | | Dot11DecryptRsna4WHandshake( |
1610 | | PDOT11DECRYPT_CONTEXT ctx, |
1611 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
1612 | | const uint8_t *eapol_raw, |
1613 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id, |
1614 | | const unsigned tot_len) |
1615 | 0 | { |
1616 | 0 | DOT11DECRYPT_KEY_ITEM *tmp_key, *tmp_pkt_key, pkt_key; |
1617 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *sa; |
1618 | 0 | int key_index; |
1619 | 0 | int ret = 1; |
1620 | 0 | unsigned char useCache=false; |
1621 | 0 | unsigned char eapol[DOT11DECRYPT_EAPOL_MAX_LEN]; |
1622 | |
|
1623 | 0 | if (eapol_parsed->len > DOT11DECRYPT_EAPOL_MAX_LEN || |
1624 | 0 | eapol_parsed->key_len > DOT11DECRYPT_EAPOL_MAX_LEN || |
1625 | 0 | eapol_parsed->key_data_len > DOT11DECRYPT_EAPOL_MAX_LEN) { |
1626 | 0 | ws_debug("Too large EAPOL frame and/or key data"); |
1627 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1628 | 0 | } |
1629 | | |
1630 | | /* TODO timeouts? */ |
1631 | | |
1632 | | /* TODO consider key-index */ |
1633 | | |
1634 | | /* TODO consider Deauthentications */ |
1635 | | |
1636 | 0 | ws_debug("4-way handshake..."); |
1637 | | |
1638 | | /* manage 4-way handshake packets; this step completes the 802.1X authentication process (IEEE 802.11i-2004, pag. 85) */ |
1639 | | |
1640 | | /* message 1: Authenticator->Supplicant (Sec=0, Mic=0, Ack=1, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=ANonce, MIC=0) */ |
1641 | 0 | if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_1) { |
1642 | 0 | ws_debug("4-way handshake message 1"); |
1643 | | |
1644 | | /* On reception of Message 1, the Supplicant determines whether the Key Replay Counter field value has been */ |
1645 | | /* used before with the current PMKSA. If the Key Replay Counter field value is less than or equal to the current */ |
1646 | | /* local value, the Supplicant discards the message. */ |
1647 | | /* -> not checked, the Authenticator will be send another Message 1 (hopefully!) */ |
1648 | | |
1649 | | /* save ANonce (from authenticator) to derive the PTK with the SNonce (from the 2 message) */ |
1650 | 0 | if (!eapol_parsed->nonce) { |
1651 | 0 | ws_debug("ANonce missing"); |
1652 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1653 | 0 | } |
1654 | | |
1655 | 0 | sa = Dot11DecryptGetSa(ctx, id); |
1656 | 0 | if (sa == NULL || sa->handshake >= 2) { |
1657 | | /* Either no SA exists or one exists but we're reauthenticating */ |
1658 | 0 | sa = Dot11DecryptNewSa(id); |
1659 | 0 | if (sa == NULL) { |
1660 | 0 | ws_warning("Failed to alloc broadcast sa"); |
1661 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1662 | 0 | } |
1663 | 0 | sa = Dot11DecryptAddSa(ctx, id, sa); |
1664 | 0 | } |
1665 | 0 | memcpy(sa->wpa.nonce, eapol_parsed->nonce, 32); |
1666 | |
|
1667 | 0 | if (eapol_parsed->mld_mac) { |
1668 | 0 | memcpy(sa->wpa.ap_mld_mac, eapol_parsed->mld_mac, 6); |
1669 | 0 | sa->wpa.ap_mld_mac_set = 1; |
1670 | 0 | } |
1671 | | /* get the Key Descriptor Version (to select algorithm used in decryption -CCMP or TKIP-) */ |
1672 | 0 | sa->wpa.key_ver = eapol_parsed->key_version; |
1673 | 0 | sa->handshake=1; |
1674 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
1675 | 0 | } |
1676 | | |
1677 | | /* message 2|4: Supplicant->Authenticator (Sec=0|1, Mic=1, Ack=0, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=SNonce|0, MIC=MIC(KCK,EAPOL)) */ |
1678 | 0 | if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_2) { |
1679 | 0 | ws_debug("4-way handshake message 2"); |
1680 | | |
1681 | | /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */ |
1682 | | /* outstanding Message 1. If not, it silently discards the message. */ |
1683 | | /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */ |
1684 | | /* the Authenticator silently discards Message 2. */ |
1685 | | /* -> not checked; the Supplicant will send another message 2 (hopefully!) */ |
1686 | |
|
1687 | 0 | sa = Dot11DecryptGetSa(ctx, id); |
1688 | 0 | if (sa == NULL) { |
1689 | 0 | ws_debug("No SA for BSSID found"); |
1690 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1691 | 0 | } |
1692 | 0 | if (!eapol_parsed->nonce) { |
1693 | 0 | ws_debug("SNonce missing"); |
1694 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1695 | 0 | } |
1696 | 0 | if (sa->key != NULL) { |
1697 | 0 | useCache = true; |
1698 | 0 | } |
1699 | | |
1700 | | // Save partial MLO link info using association link info and MLO Link KDEs |
1701 | 0 | if (sa->wpa.ap_mld_mac_set && eapol_parsed->mld_mac) { |
1702 | 0 | memcpy(sa->wpa.sta_mld_mac, eapol_parsed->mld_mac, DOT11DECRYPT_MAC_LEN); |
1703 | 0 | sa->wpa.sta_mld_mac_set = 1; |
1704 | 0 | sa->wpa.mld = 1; |
1705 | 0 | memcpy(sa->wpa.mlo_links[0].sta_mac, id->sta, DOT11DECRYPT_MAC_LEN); |
1706 | 0 | memcpy(sa->wpa.mlo_links[0].ap_mac, id->bssid, DOT11DECRYPT_MAC_LEN); |
1707 | 0 | sa->wpa.mlo_links[0].sta_mac_set = 1; |
1708 | 0 | sa->wpa.mlo_links[0].ap_mac_set = 1; |
1709 | |
|
1710 | 0 | int links = 1; |
1711 | 0 | for (int i = 0; i < eapol_parsed->mlo_link_count && links < DOT11DECRYPT_MAX_MLO_LINKS; ++i) { |
1712 | 0 | if (!memcmp(eapol_parsed->mlo_link[i].mac, id->sta, DOT11DECRYPT_MAC_LEN)) |
1713 | 0 | continue; |
1714 | 0 | sa->wpa.mlo_links[links].id = eapol_parsed->mlo_link[i].id; |
1715 | 0 | memcpy(sa->wpa.mlo_links[links].sta_mac, eapol_parsed->mlo_link[i].mac, DOT11DECRYPT_MAC_LEN); |
1716 | 0 | sa->wpa.mlo_links[links].id_set = 1; |
1717 | 0 | sa->wpa.mlo_links[links].sta_mac_set = 1; |
1718 | 0 | links++; |
1719 | 0 | } |
1720 | 0 | } |
1721 | |
|
1722 | 0 | int akm = -1; |
1723 | 0 | int cipher = -1; |
1724 | 0 | int group_cipher = -1; |
1725 | 0 | uint8_t ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN]; |
1726 | 0 | size_t ptk_len; |
1727 | | |
1728 | | /* now you can derive the PTK */ |
1729 | 0 | for (key_index=0; key_index<(int)ctx->keys_nr || useCache; key_index++) { |
1730 | | /* use the cached one, or try all keys */ |
1731 | 0 | if (useCache && Dot11DecryptIsWpaKeyType(sa->key->KeyType)) { |
1732 | 0 | ws_debug("Try cached WPA key..."); |
1733 | 0 | tmp_key = sa->key; |
1734 | | /* Step back loop counter as cached key is used instead */ |
1735 | 0 | key_index--; |
1736 | 0 | } else { |
1737 | 0 | ws_debug("Try WPA key..."); |
1738 | 0 | tmp_key = &ctx->keys[key_index]; |
1739 | 0 | } |
1740 | 0 | useCache = false; |
1741 | | |
1742 | | /* obviously, try only WPA keys... */ |
1743 | 0 | if (!Dot11DecryptIsWpaKeyType(tmp_key->KeyType)) { |
1744 | 0 | continue; |
1745 | 0 | } |
1746 | 0 | if (Dot11DecryptIsPwdWildcardSsid(ctx, tmp_key)) |
1747 | 0 | { |
1748 | | /* We have a "wildcard" SSID. Use the one from the packet. */ |
1749 | 0 | memcpy(&pkt_key, tmp_key, sizeof(pkt_key)); |
1750 | 0 | memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len); |
1751 | 0 | pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len; |
1752 | 0 | Dot11DecryptRsnaPwd2Psk(&pkt_key.UserPwd, pkt_key.KeyData.Wpa.Psk); |
1753 | 0 | pkt_key.KeyData.Wpa.PskLen = DOT11DECRYPT_WPA_PWD_PSK_LEN; |
1754 | 0 | tmp_pkt_key = &pkt_key; |
1755 | 0 | } else { |
1756 | 0 | tmp_pkt_key = tmp_key; |
1757 | 0 | } |
1758 | 0 | memcpy(eapol, eapol_raw, tot_len); |
1759 | | |
1760 | | /* From IEEE 802.11-2024 12.7.2 EAPOL-Key frames */ |
1761 | 0 | if (eapol_parsed->key_version == 0 || eapol_parsed->key_version == 3 || |
1762 | 0 | eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP) |
1763 | 0 | { |
1764 | | /* PTK derivation is based on Authentication Key Management Type */ |
1765 | 0 | akm = eapol_parsed->akm; |
1766 | 0 | cipher = eapol_parsed->cipher; |
1767 | 0 | group_cipher = eapol_parsed->group_cipher; |
1768 | 0 | } else if (eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) { |
1769 | | /* TKIP */ |
1770 | 0 | akm = 2; |
1771 | 0 | cipher = 2; |
1772 | 0 | group_cipher = 2; |
1773 | 0 | } else { |
1774 | 0 | ws_info("EAPOL key_version not supported"); |
1775 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1776 | 0 | } |
1777 | | |
1778 | 0 | if (tmp_pkt_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) { |
1779 | 0 | Dot11DecryptDerivePmkFromMsk(tmp_pkt_key->Msk.Msk, tmp_pkt_key->Msk.Len, akm, |
1780 | 0 | tmp_pkt_key->KeyData.Wpa.Psk, |
1781 | 0 | &tmp_pkt_key->KeyData.Wpa.PskLen); |
1782 | 0 | } |
1783 | |
|
1784 | 0 | ptk_len = DOT11DECRYPT_WPA_PTK_MAX_LEN; |
1785 | |
|
1786 | 0 | if (Dot11DecryptIsFtAkm(akm)) { |
1787 | 0 | ret = Dot11DecryptFtDerivePtk(ctx, sa, tmp_pkt_key, |
1788 | 0 | eapol_parsed->mdid, |
1789 | 0 | eapol_parsed->nonce, |
1790 | 0 | eapol_parsed->fte.r0kh_id, |
1791 | 0 | eapol_parsed->fte.r0kh_id_len, |
1792 | 0 | eapol_parsed->fte.r1kh_id, |
1793 | 0 | eapol_parsed->fte.r1kh_id_len, |
1794 | 0 | akm, cipher, ptk, &ptk_len); |
1795 | 0 | } else { |
1796 | | /* derive the PTK from the AA, SPA, PMK, SNonce, ANonce */ |
1797 | 0 | ret = Dot11DecryptDerivePtk(sa, /* authenticator nonce, AA, SPA */ |
1798 | 0 | tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */ |
1799 | 0 | tmp_pkt_key->KeyData.Wpa.PskLen, |
1800 | 0 | eapol_parsed->nonce, /* supplicant nonce */ |
1801 | 0 | eapol_parsed->key_version, |
1802 | 0 | akm, cipher, ptk, &ptk_len); |
1803 | 0 | } |
1804 | 0 | if (ret) { |
1805 | | /* Unsuccessful PTK derivation */ |
1806 | 0 | continue; |
1807 | 0 | } |
1808 | 0 | DEBUG_DUMP("TK", DOT11DECRYPT_GET_TK(ptk, akm, tmp_pkt_key->KeyData.Wpa.PskLen * 8), Dot11DecryptGetTkLen(cipher) / 8, |
1809 | 0 | LOG_LEVEL_DEBUG); |
1810 | |
|
1811 | 0 | ret = Dot11DecryptRsnaMicCheck(eapol_parsed, |
1812 | 0 | eapol, /* eapol frame (header also) */ |
1813 | 0 | tot_len, /* eapol frame length */ |
1814 | 0 | DOT11DECRYPT_GET_KCK(ptk, akm), |
1815 | 0 | eapol_parsed->key_version, |
1816 | 0 | akm, tmp_pkt_key->KeyData.Wpa.PskLen); |
1817 | | /* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */ |
1818 | | /* that from the (Re)Association Request message. */ |
1819 | | /* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */ |
1820 | | /* primitive to terminate the association. */ |
1821 | | /* ii) If they do match bit-wise, the Authenticator constructs Message 3. */ |
1822 | |
|
1823 | 0 | if (ret == DOT11DECRYPT_RET_SUCCESS) { |
1824 | | /* the key is the correct one, cache it in the Security Association */ |
1825 | 0 | sa->key = tmp_key; |
1826 | 0 | break; |
1827 | 0 | } |
1828 | 0 | } |
1829 | | |
1830 | 0 | if (ret) { |
1831 | 0 | ws_debug("handshake step failed"); |
1832 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1833 | 0 | } |
1834 | 0 | sa->wpa.key_ver = eapol_parsed->key_version; |
1835 | 0 | sa->wpa.akm = akm; |
1836 | 0 | sa->wpa.cipher = cipher; |
1837 | 0 | sa->wpa.tmp_group_cipher = group_cipher; |
1838 | 0 | memcpy(sa->wpa.ptk, ptk, ptk_len); |
1839 | 0 | sa->wpa.ptk_len = (int)ptk_len; |
1840 | 0 | sa->wpa.pmk_len = tmp_pkt_key->KeyData.Wpa.PskLen; |
1841 | 0 | sa->handshake = 2; |
1842 | 0 | sa->validKey = true; /* we can use the key to decode, even if we have not captured the other eapol packets */ |
1843 | |
|
1844 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
1845 | 0 | } |
1846 | | |
1847 | | /* message 3: Authenticator->Supplicant (Sec=1, Mic=1, Ack=1, Inst=0/1, Key=1(pairwise), KeyRSC=???, Nonce=ANonce, MIC=1) */ |
1848 | 0 | if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_3) { |
1849 | 0 | ws_debug("4-way handshake message 3"); |
1850 | | |
1851 | | /* On reception of Message 3, the Supplicant silently discards the message if the Key Replay Counter field */ |
1852 | | /* value has already been used or if the ANonce value in Message 3 differs from the ANonce value in Message 1. */ |
1853 | | /* -> not checked, the Authenticator will send another message 3 (hopefully!) */ |
1854 | | |
1855 | | /* TODO check page 88 (RNS) */ |
1856 | | |
1857 | | /* If using WPA2 PSK, message 3 will contain an RSN for the group key (GTK KDE). |
1858 | | In order to properly support decrypting WPA2-PSK packets, we need to parse this to get the group key. */ |
1859 | 0 | if (eapol_parsed->key_type == DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR) { |
1860 | 0 | Dot11DecryptCopyBroadcastKey(ctx, eapol_parsed->gtk, eapol_parsed->gtk_len, id); |
1861 | |
|
1862 | 0 | sa = Dot11DecryptGetSa(ctx, id); |
1863 | 0 | if (sa == NULL || sa->handshake != 2 || !sa->validKey || !sa->wpa.mld) { |
1864 | 0 | ws_debug("No MLD SA for BSSID found"); |
1865 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
1866 | 0 | } |
1867 | | |
1868 | | // Build complete MLO link info using MLO Link KDEs |
1869 | 0 | for (int i = 0; i < eapol_parsed->mlo_link_count; ++i) { |
1870 | 0 | struct DOT11DECRYPT_EAPOL_PARSED_MLO_LINK *parsed = &eapol_parsed->mlo_link[i]; |
1871 | |
|
1872 | 0 | for (struct DOT11DECRYPT_MLO_LINK_INFO *link = sa->wpa.mlo_links; |
1873 | 0 | link < &sa->wpa.mlo_links[DOT11DECRYPT_MAX_MLO_LINKS]; |
1874 | 0 | ++link) { |
1875 | 0 | if (link->ap_mac_set && !memcmp(parsed->mac, link->ap_mac, DOT11DECRYPT_MAC_LEN)) { |
1876 | 0 | link->id = parsed->id; |
1877 | 0 | link->id_set = 1; |
1878 | 0 | } else if (link->id_set && link->id == parsed->id) { |
1879 | 0 | memcpy(link->ap_mac, parsed->mac, DOT11DECRYPT_MAC_LEN); |
1880 | 0 | link->ap_mac_set = 1; |
1881 | 0 | } |
1882 | 0 | } |
1883 | 0 | } |
1884 | | |
1885 | | // Create PTKSA for non-association links |
1886 | 0 | for (struct DOT11DECRYPT_MLO_LINK_INFO *link = sa->wpa.mlo_links; |
1887 | 0 | link < &sa->wpa.mlo_links[DOT11DECRYPT_MAX_MLO_LINKS]; |
1888 | 0 | ++link) { |
1889 | 0 | if (!(link->id_set && link->ap_mac_set && link->sta_mac_set)) |
1890 | 0 | continue; |
1891 | 0 | if (!memcmp(link->ap_mac, id->bssid, DOT11DECRYPT_MAC_LEN)) |
1892 | 0 | continue; |
1893 | 0 | ws_debug("Create PTKSA for LinkID %d", link->id); |
1894 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *new_sa = Dot11DecryptDupSa(sa); |
1895 | 0 | DOT11DECRYPT_SEC_ASSOCIATION_ID saId; |
1896 | 0 | memcpy(&saId.bssid, link->ap_mac, DOT11DECRYPT_MAC_LEN); |
1897 | 0 | memcpy(&saId.sta, link->sta_mac, DOT11DECRYPT_MAC_LEN); |
1898 | 0 | memcpy(&new_sa->saId, &saId, sizeof(saId)); |
1899 | 0 | Dot11DecryptAddSa(ctx, &saId, new_sa); |
1900 | 0 | } |
1901 | | |
1902 | | // Create GTKSA for all MLO links |
1903 | 0 | Dot11DecryptCreateMloGtkSa(ctx, eapol_parsed, sa); |
1904 | 0 | } |
1905 | 0 | } |
1906 | | |
1907 | | /* message 4 */ |
1908 | 0 | if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_4) { |
1909 | | /* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */ |
1910 | | |
1911 | | /* TODO check MIC and Replay Counter */ |
1912 | | /* On reception of Message 4, the Authenticator verifies that the Key Replay Counter field value is one */ |
1913 | | /* that it used on this 4-Way Handshake; if it is not, it silently discards the message. */ |
1914 | | /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, the */ |
1915 | | /* Authenticator silently discards Message 4. */ |
1916 | |
|
1917 | 0 | ws_debug("4-way handshake message 4"); |
1918 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
1919 | 0 | } |
1920 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1921 | 0 | } |
1922 | | |
1923 | | /* Refer to IEEE 802.11-2016 Chapeter 13.8 FT authentication sequence */ |
1924 | | int |
1925 | | Dot11DecryptScanFtAssocForKeys( |
1926 | | const PDOT11DECRYPT_CONTEXT ctx, |
1927 | | const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed, |
1928 | | uint8_t *decrypted_gtk, size_t *decrypted_len, |
1929 | | DOT11DECRYPT_KEY_ITEM* used_key) |
1930 | 909 | { |
1931 | 909 | DOT11DECRYPT_SEC_ASSOCIATION_ID id; |
1932 | | |
1933 | 909 | ws_debug("(Re)Association packet"); |
1934 | | |
1935 | 909 | if (!ctx || !assoc_parsed) { |
1936 | 0 | ws_warning("Invalid input parameters"); |
1937 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1938 | 0 | } |
1939 | 909 | if (!Dot11DecryptIsFtAkm(assoc_parsed->akm)) { |
1940 | 909 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1941 | 909 | } |
1942 | 0 | if (!assoc_parsed->fte.anonce || !assoc_parsed->fte.snonce) { |
1943 | 0 | ws_debug("ANonce or SNonce missing"); |
1944 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1945 | 0 | } |
1946 | | |
1947 | 0 | switch (assoc_parsed->frame_subtype) { |
1948 | 0 | case DOT11DECRYPT_SUBTYPE_ASSOC_REQ: |
1949 | 0 | case DOT11DECRYPT_SUBTYPE_REASSOC_REQ: |
1950 | 0 | memcpy(id.sta, assoc_parsed->sa, DOT11DECRYPT_MAC_LEN); |
1951 | 0 | break; |
1952 | 0 | case DOT11DECRYPT_SUBTYPE_ASSOC_RESP: |
1953 | 0 | case DOT11DECRYPT_SUBTYPE_REASSOC_RESP: |
1954 | 0 | memcpy(id.sta, assoc_parsed->da, DOT11DECRYPT_MAC_LEN); |
1955 | 0 | break; |
1956 | 0 | default: |
1957 | 0 | ws_warning("Invalid frame subtype"); |
1958 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
1959 | 0 | } |
1960 | 0 | memcpy(id.bssid, assoc_parsed->bssid, DOT11DECRYPT_MAC_LEN); |
1961 | |
|
1962 | 0 | DOT11DECRYPT_KEY_ITEM *tmp_key, *tmp_pkt_key, pkt_key; |
1963 | 0 | DOT11DECRYPT_SEC_ASSOCIATION *sa; |
1964 | 0 | size_t key_index; |
1965 | 0 | unsigned ret = 1; |
1966 | 0 | bool useCache = false; |
1967 | |
|
1968 | 0 | sa = Dot11DecryptNewSa(&id); |
1969 | 0 | if (sa == NULL) { |
1970 | 0 | ws_warning("Failed to alloc sa"); |
1971 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
1972 | 0 | } |
1973 | | |
1974 | 0 | memcpy(sa->wpa.nonce, assoc_parsed->fte.anonce, 32); |
1975 | |
|
1976 | 0 | if (sa->key != NULL) { |
1977 | 0 | useCache = true; |
1978 | 0 | } |
1979 | |
|
1980 | 0 | uint8_t ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN]; |
1981 | 0 | size_t ptk_len; |
1982 | | |
1983 | | /* now you can derive the PTK */ |
1984 | 0 | for (key_index = 0; key_index < ctx->keys_nr || useCache; key_index++) { |
1985 | | /* use the cached one, or try all keys */ |
1986 | 0 | if (useCache && Dot11DecryptIsWpaKeyType(sa->key->KeyType)) { |
1987 | 0 | ws_debug("Try cached WPA key..."); |
1988 | 0 | tmp_key = sa->key; |
1989 | | /* Step back loop counter as cached key is used instead */ |
1990 | 0 | key_index--; |
1991 | 0 | } else { |
1992 | 0 | ws_debug("Try WPA key..."); |
1993 | 0 | tmp_key = &ctx->keys[key_index]; |
1994 | 0 | } |
1995 | 0 | useCache = false; |
1996 | | |
1997 | | /* Try only WPA keys... */ |
1998 | 0 | if (!Dot11DecryptIsWpaKeyType(tmp_key->KeyType)) { |
1999 | 0 | continue; |
2000 | 0 | } |
2001 | 0 | if (Dot11DecryptIsPwdWildcardSsid(ctx, tmp_key)) |
2002 | 0 | { |
2003 | | /* We have a "wildcard" SSID. Use the one from the packet. */ |
2004 | 0 | memcpy(&pkt_key, tmp_key, sizeof(pkt_key)); |
2005 | 0 | memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len); |
2006 | 0 | pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len; |
2007 | 0 | Dot11DecryptRsnaPwd2Psk(&pkt_key.UserPwd, pkt_key.KeyData.Wpa.Psk); |
2008 | 0 | pkt_key.KeyData.Wpa.PskLen = DOT11DECRYPT_WPA_PWD_PSK_LEN; |
2009 | 0 | tmp_pkt_key = &pkt_key; |
2010 | 0 | } else { |
2011 | 0 | tmp_pkt_key = tmp_key; |
2012 | 0 | } |
2013 | |
|
2014 | 0 | if (tmp_pkt_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) { |
2015 | 0 | Dot11DecryptDerivePmkFromMsk(tmp_pkt_key->Msk.Msk, tmp_pkt_key->Msk.Len, |
2016 | 0 | assoc_parsed->akm, |
2017 | 0 | tmp_pkt_key->KeyData.Wpa.Psk, |
2018 | 0 | &tmp_pkt_key->KeyData.Wpa.PskLen); |
2019 | 0 | } |
2020 | |
|
2021 | 0 | ptk_len = DOT11DECRYPT_WPA_PTK_MAX_LEN; |
2022 | 0 | ret = Dot11DecryptFtDerivePtk(ctx, sa, tmp_pkt_key, |
2023 | 0 | assoc_parsed->mdid, |
2024 | 0 | assoc_parsed->fte.snonce, |
2025 | 0 | assoc_parsed->fte.r0kh_id, |
2026 | 0 | assoc_parsed->fte.r0kh_id_len, |
2027 | 0 | assoc_parsed->fte.r1kh_id, |
2028 | 0 | assoc_parsed->fte.r1kh_id_len, |
2029 | 0 | assoc_parsed->akm, assoc_parsed->cipher, |
2030 | 0 | ptk, &ptk_len); |
2031 | 0 | if (ret != DOT11DECRYPT_RET_SUCCESS) { |
2032 | 0 | continue; |
2033 | 0 | } |
2034 | 0 | DEBUG_DUMP("TK", DOT11DECRYPT_GET_TK(ptk, assoc_parsed->akm, tmp_pkt_key->KeyData.Wpa.PskLen * 8), |
2035 | 0 | Dot11DecryptGetTkLen(assoc_parsed->cipher) / 8, |
2036 | 0 | LOG_LEVEL_DEBUG); |
2037 | |
|
2038 | 0 | ret = Dot11DecryptFtMicCheck(assoc_parsed, |
2039 | 0 | DOT11DECRYPT_GET_KCK(ptk, assoc_parsed->akm), |
2040 | 0 | Dot11DecryptGetKckLen(assoc_parsed->akm, tmp_pkt_key->KeyData.Wpa.PskLen * 8) / 8); |
2041 | 0 | if (ret == DOT11DECRYPT_RET_SUCCESS) { |
2042 | | /* the key is the correct one, cache it in the Security Association */ |
2043 | 0 | sa->key = tmp_key; |
2044 | 0 | break; |
2045 | 0 | } |
2046 | 0 | } |
2047 | |
|
2048 | 0 | if (ret) { |
2049 | 0 | ws_debug("handshake step failed"); |
2050 | 0 | g_free(sa); |
2051 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2052 | 0 | } |
2053 | 0 | sa = Dot11DecryptAddSa(ctx, &id, sa); |
2054 | |
|
2055 | 0 | sa->wpa.key_ver = 0; /* Determine key type from akms and cipher*/ |
2056 | 0 | sa->wpa.akm = assoc_parsed->akm; |
2057 | 0 | sa->wpa.cipher = assoc_parsed->cipher; |
2058 | 0 | sa->wpa.tmp_group_cipher = assoc_parsed->group_cipher; |
2059 | 0 | sa->wpa.pmk_len = tmp_pkt_key->KeyData.Wpa.PskLen; |
2060 | 0 | memcpy(sa->wpa.ptk, ptk, ptk_len); |
2061 | 0 | sa->wpa.ptk_len = (int)ptk_len; |
2062 | 0 | sa->validKey = true; |
2063 | |
|
2064 | 0 | if (assoc_parsed->gtk && assoc_parsed->gtk_len - 8 <= 32) { |
2065 | 0 | uint8_t decrypted_key[32]; |
2066 | 0 | uint16_t decrypted_key_len; |
2067 | 0 | if (AES_unwrap(DOT11DECRYPT_GET_KEK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8), |
2068 | 0 | Dot11DecryptGetKekLen(sa->wpa.akm, sa->wpa.pmk_len * 8) / 8, |
2069 | 0 | assoc_parsed->gtk, assoc_parsed->gtk_len, |
2070 | 0 | decrypted_key, &decrypted_key_len)) { |
2071 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2072 | 0 | } |
2073 | 0 | if (decrypted_key_len != assoc_parsed->gtk_subelem_key_len) { |
2074 | 0 | ws_debug("Unexpected GTK length"); |
2075 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2076 | 0 | } |
2077 | 0 | Dot11DecryptCopyBroadcastKey(ctx, decrypted_key, decrypted_key_len, &id); |
2078 | 0 | *decrypted_len = decrypted_key_len; |
2079 | 0 | memcpy(decrypted_gtk, decrypted_key, decrypted_key_len); |
2080 | 0 | } |
2081 | 0 | Dot11DecryptCopyKey(sa, used_key); |
2082 | 0 | return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE; |
2083 | 0 | } |
2084 | | |
2085 | | /* From IEEE 802.11-2024 Table 12-11 Integrity and key-wrap algorithms */ |
2086 | | static int |
2087 | | Dot11DecryptGetIntegrityAlgoFromAkm(int akm, size_t pmk_len, int *algo, bool *hmac) |
2088 | 0 | { |
2089 | 0 | int res = 0; |
2090 | 0 | switch (akm) { |
2091 | 0 | case 1: |
2092 | 0 | case 2: |
2093 | 0 | *algo = GCRY_MD_SHA1; |
2094 | 0 | *hmac = true; |
2095 | 0 | break; |
2096 | 0 | case 3: |
2097 | 0 | case 4: |
2098 | 0 | case 5: |
2099 | 0 | case 6: |
2100 | 0 | case 8: |
2101 | 0 | case 9: |
2102 | 0 | *algo = GCRY_MAC_CMAC_AES; |
2103 | 0 | *hmac = false; |
2104 | 0 | break; |
2105 | 0 | case 11: |
2106 | 0 | *algo = GCRY_MD_SHA256; |
2107 | 0 | *hmac = true; |
2108 | 0 | break; |
2109 | 0 | case 12: |
2110 | 0 | case 13: |
2111 | 0 | *algo = GCRY_MD_SHA384; |
2112 | 0 | *hmac = true; |
2113 | 0 | break; |
2114 | 0 | case 18: |
2115 | 0 | case 24: |
2116 | 0 | case 25: |
2117 | 0 | if (pmk_len == 256) |
2118 | 0 | *algo = GCRY_MD_SHA256; |
2119 | 0 | else if (pmk_len == 384) |
2120 | 0 | *algo = GCRY_MD_SHA384; |
2121 | 0 | else if (pmk_len == 512) |
2122 | 0 | *algo = GCRY_MD_SHA512; |
2123 | 0 | else |
2124 | 0 | res = -1; |
2125 | 0 | *hmac = true; |
2126 | 0 | break; |
2127 | 0 | default: |
2128 | | /* Unknown / Not supported yet */ |
2129 | 0 | res = -1; |
2130 | 0 | break; |
2131 | 0 | } |
2132 | 0 | return res; |
2133 | 0 | } |
2134 | | |
2135 | | static int |
2136 | | Dot11DecryptRsnaMicCheck( |
2137 | | PDOT11DECRYPT_EAPOL_PARSED eapol_parsed, |
2138 | | unsigned char *eapol, |
2139 | | unsigned short eapol_len, |
2140 | | unsigned char *KCK, |
2141 | | unsigned short key_ver, |
2142 | | int akm, |
2143 | | int pmk_len) |
2144 | 0 | { |
2145 | 0 | uint8_t *mic = eapol_parsed->mic; |
2146 | 0 | uint16_t mic_len = eapol_parsed->mic_len; |
2147 | 0 | uint16_t kck_len = Dot11DecryptGetKckLen(akm, pmk_len * 8) / 8; |
2148 | | /* MIC 16, 24 or 32 bytes, though HMAC-SHA256 / SHA384 / SHA512 algos need 32 / 48 / 64 bytes buffer */ |
2149 | 0 | unsigned char c_mic[64] = { 0 }; |
2150 | 0 | int algo = -1; |
2151 | 0 | bool hmac = true; |
2152 | |
|
2153 | 0 | if (!mic || mic_len > DOT11DECRYPT_WPA_MICKEY_MAX_LEN) { |
2154 | 0 | ws_debug("Not a valid mic"); |
2155 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2156 | 0 | } |
2157 | | |
2158 | | /* set to 0 the MIC in the EAPOL packet (to calculate the MIC) */ |
2159 | 0 | memset(eapol + DOT11DECRYPT_WPA_MICKEY_OFFSET + 4, 0, mic_len); |
2160 | |
|
2161 | 0 | if (key_ver==DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) { |
2162 | | /* use HMAC-MD5 for the EAPOL-Key MIC */ |
2163 | 0 | algo = GCRY_MD_MD5; |
2164 | 0 | hmac = true; |
2165 | 0 | } else if (key_ver==DOT11DECRYPT_WPA_KEY_VER_AES_CCMP) { |
2166 | | /* use HMAC-SHA1-128 for the EAPOL-Key MIC */ |
2167 | 0 | algo = GCRY_MD_SHA1; |
2168 | 0 | hmac = true; |
2169 | 0 | } else { |
2170 | | /* Mic check algorithm determined by AKM type */ |
2171 | 0 | if (Dot11DecryptGetIntegrityAlgoFromAkm(akm, pmk_len * 8, &algo, &hmac)) { |
2172 | 0 | ws_warning("Unknown Mic check algo"); |
2173 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2174 | 0 | }; |
2175 | 0 | } |
2176 | 0 | if (hmac) { |
2177 | 0 | if (ws_hmac_buffer(algo, c_mic, eapol, eapol_len, KCK, kck_len)) { |
2178 | 0 | ws_debug("HMAC_BUFFER"); |
2179 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2180 | 0 | } |
2181 | 0 | } else { |
2182 | 0 | if (ws_cmac_buffer(algo, c_mic, eapol, eapol_len, KCK, kck_len)) { |
2183 | 0 | ws_debug("HMAC_BUFFER"); |
2184 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2185 | 0 | } |
2186 | 0 | } |
2187 | | |
2188 | | /* compare calculated MIC with the Key MIC and return result (0 means success) */ |
2189 | 0 | DEBUG_DUMP("mic", mic, mic_len, LOG_LEVEL_DEBUG); |
2190 | 0 | DEBUG_DUMP("c_mic", c_mic, mic_len, LOG_LEVEL_DEBUG); |
2191 | 0 | return memcmp(mic, c_mic, mic_len); |
2192 | 0 | } |
2193 | | |
2194 | | /* IEEE 802.11-2020 Chapter 13.8.4 FT authentication sequence: contents of third message |
2195 | | * IEEE 802.11-2020 Chapter 13.8.5 FT authentication sequence: contents of fourth message |
2196 | | * The MIC shall be calculated on the concatenation of the following data, in the order given here: |
2197 | | * — |
2198 | | * — FTO’s MAC address (6 octets) |
2199 | | * — Target AP’s MAC address (6 octets) |
2200 | | * If third message: |
2201 | | * — Transaction sequence number (1 octet), which shall be set to the value 5 if this is a |
2202 | | * Reassociation Request frame and, otherwise, set to the value 3 |
2203 | | * If fourth message: |
2204 | | * — Transaction sequence number (1 octet), which shall be set to the value 6 if this is a |
2205 | | * Reassociation Response frame or, otherwise, set to the value 4 |
2206 | | * |
2207 | | * — RSNE |
2208 | | * — MDE |
2209 | | * — FTE, with the MIC field of the FTE set to 0 |
2210 | | * — Contents of the RIC-Response (if present) |
2211 | | * — RSNXE (if present) |
2212 | | */ |
2213 | | static int |
2214 | | Dot11DecryptFtMicCheck( |
2215 | | const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed, |
2216 | | const uint8_t *kck, |
2217 | | size_t kck_len) |
2218 | 0 | { |
2219 | 0 | uint8_t *sta; |
2220 | 0 | uint8_t seq_num; |
2221 | 0 | uint8_t fte_len; |
2222 | 0 | uint16_t mic_len; |
2223 | 0 | uint8_t zeros[32] = { 0 }; |
2224 | 0 | gcry_mac_hd_t handle; |
2225 | 0 | int mic_algo; |
2226 | 0 | bool hmac; |
2227 | |
|
2228 | 0 | fte_len = assoc_parsed->fte_tag[1] + 2; |
2229 | 0 | if (fte_len < 20) { |
2230 | 0 | ws_debug("FTE too short"); |
2231 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2232 | 0 | } |
2233 | | |
2234 | 0 | mic_len = assoc_parsed->fte.mic_len; |
2235 | 0 | if (mic_len > sizeof(zeros)) { |
2236 | 0 | ws_debug("MIC too long"); |
2237 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2238 | 0 | } |
2239 | | |
2240 | 0 | switch (assoc_parsed->frame_subtype) { |
2241 | 0 | case DOT11DECRYPT_SUBTYPE_ASSOC_REQ: |
2242 | 0 | sta = assoc_parsed->sa; |
2243 | 0 | seq_num = 3; |
2244 | 0 | break; |
2245 | 0 | case DOT11DECRYPT_SUBTYPE_ASSOC_RESP: |
2246 | 0 | sta = assoc_parsed->da; |
2247 | 0 | seq_num = 4; |
2248 | 0 | break; |
2249 | 0 | case DOT11DECRYPT_SUBTYPE_REASSOC_REQ: |
2250 | 0 | sta = assoc_parsed->sa; |
2251 | 0 | seq_num = 5; |
2252 | 0 | break; |
2253 | 0 | case DOT11DECRYPT_SUBTYPE_REASSOC_RESP: |
2254 | 0 | sta = assoc_parsed->da; |
2255 | 0 | seq_num = 6; |
2256 | 0 | break; |
2257 | 0 | default: |
2258 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2259 | 0 | } |
2260 | | |
2261 | | /* For AKM 25, length of KCK is half of PMK while for other AKMs, pmk_len is not used. */ |
2262 | 0 | if (Dot11DecryptGetIntegrityAlgoFromAkm(assoc_parsed->akm, kck_len * 2 * 8, &mic_algo, &hmac)) { |
2263 | 0 | ws_warning("unsupported AKM"); |
2264 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2265 | 0 | } |
2266 | | |
2267 | 0 | if (mic_algo == GCRY_MAC_CMAC_AES) |
2268 | 0 | /* nothing */; |
2269 | 0 | else if (mic_algo == GCRY_MD_SHA256) |
2270 | 0 | mic_algo = GCRY_MAC_HMAC_SHA256; |
2271 | 0 | else if (mic_algo == GCRY_MD_SHA384) |
2272 | 0 | mic_algo = GCRY_MAC_HMAC_SHA384; |
2273 | 0 | else if (mic_algo == GCRY_MD_SHA512) |
2274 | 0 | mic_algo = GCRY_MAC_HMAC_SHA512; |
2275 | 0 | else |
2276 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2277 | | |
2278 | 0 | if (gcry_mac_open(&handle, mic_algo, 0, NULL)) { |
2279 | 0 | ws_warning("gcry_mac_open failed"); |
2280 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2281 | 0 | } |
2282 | 0 | if (gcry_mac_setkey(handle, kck, kck_len)) { |
2283 | 0 | ws_warning("gcry_mac_setkey failed"); |
2284 | 0 | gcry_mac_close(handle); |
2285 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2286 | 0 | } |
2287 | 0 | gcry_mac_write(handle, sta, DOT11DECRYPT_MAC_LEN); |
2288 | 0 | gcry_mac_write(handle, assoc_parsed->bssid, DOT11DECRYPT_MAC_LEN); |
2289 | |
|
2290 | 0 | gcry_mac_write(handle, &seq_num, 1); |
2291 | |
|
2292 | 0 | gcry_mac_write(handle, assoc_parsed->rsne_tag, assoc_parsed->rsne_tag[1] + 2); |
2293 | 0 | gcry_mac_write(handle, assoc_parsed->mde_tag, assoc_parsed->mde_tag[1] + 2); |
2294 | |
|
2295 | 0 | gcry_mac_write(handle, assoc_parsed->fte_tag, 4); |
2296 | 0 | gcry_mac_write(handle, zeros, mic_len); /* MIC zeroed */ |
2297 | 0 | gcry_mac_write(handle, assoc_parsed->fte_tag + 4 + mic_len, fte_len - 4 - mic_len); |
2298 | |
|
2299 | 0 | if (assoc_parsed->rde_tag) { |
2300 | 0 | gcry_mac_write(handle, assoc_parsed->rde_tag, assoc_parsed->rde_tag[1] + 2); |
2301 | 0 | } |
2302 | 0 | if (assoc_parsed->rsnxe_tag) { |
2303 | 0 | gcry_mac_write(handle, assoc_parsed->rsnxe_tag, assoc_parsed->rsnxe_tag[1] + 2); |
2304 | 0 | } |
2305 | |
|
2306 | 0 | if (gcry_mac_verify(handle, assoc_parsed->fte.mic, mic_len) != 0) { |
2307 | 0 | DEBUG_DUMP("MIC", assoc_parsed->fte.mic, mic_len, LOG_LEVEL_DEBUG); |
2308 | 0 | ws_debug("MIC verification failed"); |
2309 | 0 | gcry_mac_close(handle); |
2310 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2311 | 0 | } |
2312 | 0 | DEBUG_DUMP("MIC", assoc_parsed->fte.mic, mic_len, LOG_LEVEL_DEBUG); |
2313 | 0 | gcry_mac_close(handle); |
2314 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
2315 | 0 | } |
2316 | | |
2317 | | static int |
2318 | | Dot11DecryptValidateKey( |
2319 | | PDOT11DECRYPT_KEY_ITEM key) |
2320 | 0 | { |
2321 | 0 | size_t len; |
2322 | 0 | unsigned char ret=true; |
2323 | |
|
2324 | 0 | if (key==NULL) { |
2325 | 0 | ws_warning("NULL key"); |
2326 | 0 | return false; |
2327 | 0 | } |
2328 | | |
2329 | 0 | switch (key->KeyType) { |
2330 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP: |
2331 | | /* check key size limits */ |
2332 | 0 | len=key->KeyData.Wep.WepKeyLen; |
2333 | 0 | if (len<DOT11DECRYPT_WEP_KEY_MINLEN || len>DOT11DECRYPT_WEP_KEY_MAXLEN) { |
2334 | 0 | ws_info("WEP key: key length not accepted"); |
2335 | 0 | ret=false; |
2336 | 0 | } |
2337 | 0 | break; |
2338 | | |
2339 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP_40: |
2340 | | /* set the standard length and use a generic WEP key type */ |
2341 | 0 | key->KeyData.Wep.WepKeyLen=DOT11DECRYPT_WEP_40_KEY_LEN; |
2342 | 0 | key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP; |
2343 | 0 | break; |
2344 | | |
2345 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP_104: |
2346 | | /* set the standard length and use a generic WEP key type */ |
2347 | 0 | key->KeyData.Wep.WepKeyLen=DOT11DECRYPT_WEP_104_KEY_LEN; |
2348 | 0 | key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP; |
2349 | 0 | break; |
2350 | | |
2351 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PWD: |
2352 | | /* check passphrase and SSID size limits */ |
2353 | 0 | len=strlen(key->UserPwd.Passphrase); |
2354 | 0 | if (len<DOT11DECRYPT_WPA_PASSPHRASE_MIN_LEN || len>DOT11DECRYPT_WPA_PASSPHRASE_MAX_LEN) { |
2355 | 0 | ws_info("WPA-PWD key: passphrase length not accepted"); |
2356 | 0 | ret=false; |
2357 | 0 | } |
2358 | |
|
2359 | 0 | len=key->UserPwd.SsidLen; |
2360 | 0 | if (len>DOT11DECRYPT_WPA_SSID_MAX_LEN) { |
2361 | 0 | ws_info("WPA-PWD key: ssid length not accepted"); |
2362 | 0 | ret=false; |
2363 | 0 | } |
2364 | |
|
2365 | 0 | break; |
2366 | | |
2367 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PSK: |
2368 | 0 | break; |
2369 | | |
2370 | 0 | case DOT11DECRYPT_KEY_TYPE_TK: |
2371 | 0 | break; |
2372 | | |
2373 | 0 | case DOT11DECRYPT_KEY_TYPE_MSK: |
2374 | 0 | break; |
2375 | | |
2376 | 0 | default: |
2377 | 0 | ret=false; |
2378 | 0 | } |
2379 | 0 | return ret; |
2380 | 0 | } |
2381 | | |
2382 | | static int |
2383 | | Dot11DecryptGetSaAddress( |
2384 | | const uint8_t *mac_header, |
2385 | | unsigned mac_header_len, |
2386 | | DOT11DECRYPT_SEC_ASSOCIATION_ID *id) |
2387 | 219 | { |
2388 | 219 | const DOT11DECRYPT_MAC_FRAME *frame; |
2389 | | |
2390 | 219 | if (mac_header_len < sizeof(DOT11DECRYPT_MAC_FRAME)) { |
2391 | 28 | return DOT11DECRYPT_RET_UNSUCCESS; |
2392 | 28 | } |
2393 | 191 | frame = (const DOT11DECRYPT_MAC_FRAME *)mac_header; |
2394 | | |
2395 | 191 | if ((DOT11DECRYPT_TYPE(frame->fc[0])==DOT11DECRYPT_TYPE_DATA) && |
2396 | 68 | (DOT11DECRYPT_DS_BITS(frame->fc[1]) == 0) && |
2397 | 49 | (memcmp(frame->addr2, frame->addr3, DOT11DECRYPT_MAC_LEN) != 0) && |
2398 | 47 | (memcmp(frame->addr1, frame->addr3, DOT11DECRYPT_MAC_LEN) != 0)) { |
2399 | | /* DATA frame with fromDS=0 ToDS=0 and neither RA or SA is BSSID |
2400 | | => TDLS traffic. Use highest MAC address for bssid */ |
2401 | 32 | if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) < 0) { |
2402 | 8 | memcpy(id->sta, frame->addr1, DOT11DECRYPT_MAC_LEN); |
2403 | 8 | memcpy(id->bssid, frame->addr2, DOT11DECRYPT_MAC_LEN); |
2404 | 24 | } else { |
2405 | 24 | memcpy(id->sta, frame->addr2, DOT11DECRYPT_MAC_LEN); |
2406 | 24 | memcpy(id->bssid, frame->addr1, DOT11DECRYPT_MAC_LEN); |
2407 | 24 | } |
2408 | 159 | } else { |
2409 | 159 | const unsigned char *addr; |
2410 | | |
2411 | | /* Normal Case: SA between STA and AP */ |
2412 | 159 | if ((addr = Dot11DecryptGetBssidAddress(frame)) != NULL) { |
2413 | 159 | memcpy(id->bssid, addr, DOT11DECRYPT_MAC_LEN); |
2414 | 159 | } else { |
2415 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2416 | 0 | } |
2417 | | |
2418 | 159 | if ((addr = Dot11DecryptGetStaAddress(frame)) != NULL) { |
2419 | 159 | memcpy(id->sta, addr, DOT11DECRYPT_MAC_LEN); |
2420 | 159 | } else { |
2421 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2422 | 0 | } |
2423 | 159 | } |
2424 | 191 | ws_noisy("BSSID_MAC: %02X.%02X.%02X.%02X.%02X.%02X\t", |
2425 | 191 | id->bssid[0],id->bssid[1],id->bssid[2],id->bssid[3],id->bssid[4],id->bssid[5]); |
2426 | 191 | ws_noisy("STA_MAC: %02X.%02X.%02X.%02X.%02X.%02X\t", |
2427 | 191 | id->sta[0],id->sta[1],id->sta[2],id->sta[3],id->sta[4],id->sta[5]); |
2428 | | |
2429 | 191 | return DOT11DECRYPT_RET_SUCCESS; |
2430 | 191 | } |
2431 | | |
2432 | | /* |
2433 | | * Dot11DecryptGetBssidAddress() and Dot11DecryptGetBssidAddress() are used for |
2434 | | * key caching. In each case, it's more important to return a value than |
2435 | | * to return a _correct_ value, so we fudge addresses in some cases, e.g. |
2436 | | * the BSSID in bridged connections. |
2437 | | * FromDS ToDS Sta BSSID |
2438 | | * 0 0 addr1/2 addr3 |
2439 | | * 0 1 addr2 addr1 |
2440 | | * 1 0 addr1 addr2 |
2441 | | * 1 1 addr2 addr1 |
2442 | | */ |
2443 | | |
2444 | | static const unsigned char * |
2445 | | Dot11DecryptGetStaAddress( |
2446 | | const DOT11DECRYPT_MAC_FRAME *frame) |
2447 | 159 | { |
2448 | 159 | switch(DOT11DECRYPT_DS_BITS(frame->fc[1])) { /* Bit 1 = FromDS, bit 0 = ToDS */ |
2449 | 80 | case 0: |
2450 | 80 | if (memcmp(frame->addr2, frame->addr3, DOT11DECRYPT_MAC_LEN) == 0) |
2451 | 17 | return frame->addr1; |
2452 | 63 | else |
2453 | 63 | return frame->addr2; |
2454 | 11 | case 1: |
2455 | 11 | return frame->addr2; |
2456 | 10 | case 2: |
2457 | 10 | return frame->addr1; |
2458 | 58 | case 3: |
2459 | 58 | if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) < 0) |
2460 | 30 | return frame->addr1; |
2461 | 28 | else |
2462 | 28 | return frame->addr2; |
2463 | | |
2464 | 0 | default: |
2465 | 0 | return NULL; |
2466 | 159 | } |
2467 | 159 | } |
2468 | | |
2469 | | static const unsigned char * |
2470 | | Dot11DecryptGetBssidAddress( |
2471 | | const DOT11DECRYPT_MAC_FRAME *frame) |
2472 | 159 | { |
2473 | 159 | switch(DOT11DECRYPT_DS_BITS(frame->fc[1])) { /* Bit 1 = FromDS, bit 0 = ToDS */ |
2474 | 80 | case 0: |
2475 | 80 | return frame->addr3; |
2476 | 11 | case 1: |
2477 | 11 | return frame->addr1; |
2478 | 10 | case 2: |
2479 | 10 | return frame->addr2; |
2480 | 58 | case 3: |
2481 | 58 | if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) > 0) |
2482 | 22 | return frame->addr1; |
2483 | 36 | else |
2484 | 36 | return frame->addr2; |
2485 | | |
2486 | 0 | default: |
2487 | 0 | return NULL; |
2488 | 159 | } |
2489 | 159 | } |
2490 | | |
2491 | | /* From IEEE 802.11-2024 Table 9-188 Cipher suite selectors and |
2492 | | * Table 12-8 Cipher suite key lengths */ |
2493 | | static int Dot11DecryptGetTkLen(int cipher) |
2494 | 0 | { |
2495 | 0 | switch (cipher) { |
2496 | 0 | case 1: return 40; /* WEP-40 */ |
2497 | 0 | case 2: return 256; /* TKIP */ |
2498 | 0 | case 3: return -1; /* Reserved */ |
2499 | 0 | case 4: return 128; /* CCMP-128 */ |
2500 | 0 | case 5: return 104; /* WEP-104 */ |
2501 | 0 | case 6: return 128; /* BIP-CMAC-128 */ |
2502 | 0 | case 7: return -1; /* Group addressed traffic not allowed */ |
2503 | 0 | case 8: return 128; /* GCMP-128 */ |
2504 | 0 | case 9: return 256; /* GCMP-256 */ |
2505 | 0 | case 10: return 256; /* CCMP-256 */ |
2506 | 0 | case 11: return 128; /* BIP-GMAC-128 */ |
2507 | 0 | case 12: return 256; /* BIP-GMAC-256 */ |
2508 | 0 | case 13: return 256; /* BIP-CMAC-256 */ |
2509 | 0 | default: |
2510 | 0 | ws_warning("Unknown cipher"); |
2511 | 0 | return -1; |
2512 | 0 | } |
2513 | 0 | } |
2514 | | |
2515 | | /* From IEEE 802.11-2024 Table 12-11 Integrity and key-wrap algorithms */ |
2516 | | static int Dot11DecryptGetKckLen(int akm, size_t pmk_len) |
2517 | 0 | { |
2518 | 0 | switch (akm) { |
2519 | 0 | case 1: return 128; |
2520 | 0 | case 2: return 128; |
2521 | 0 | case 3: return 128; |
2522 | 0 | case 4: return 128; |
2523 | 0 | case 5: return 128; |
2524 | 0 | case 6: return 128; |
2525 | 0 | case 8: return 128; |
2526 | 0 | case 9: return 128; |
2527 | 0 | case 11: return 128; |
2528 | 0 | case 12: return 192; |
2529 | 0 | case 13: return 192; |
2530 | 0 | case 18: |
2531 | 0 | case 24: |
2532 | 0 | case 25: |
2533 | 0 | return (int)pmk_len / 2; |
2534 | 0 | default: |
2535 | | /* Unknown / Not supported */ |
2536 | 0 | ws_warning("Unknown akm"); |
2537 | 0 | return -1; |
2538 | 0 | } |
2539 | 0 | } |
2540 | | |
2541 | | /* From IEEE 802.11-2024 Table 12-11 Integrity and key-wrap algorithms */ |
2542 | | static int Dot11DecryptGetKekLen(int akm, size_t pmk_len) |
2543 | 0 | { |
2544 | 0 | switch (akm) { |
2545 | 0 | case 1: return 128; |
2546 | 0 | case 2: return 128; |
2547 | 0 | case 3: return 128; |
2548 | 0 | case 4: return 128; |
2549 | 0 | case 5: return 128; |
2550 | 0 | case 6: return 128; |
2551 | 0 | case 8: return 128; |
2552 | 0 | case 9: return 128; |
2553 | 0 | case 11: return 128; |
2554 | 0 | case 12: return 256; |
2555 | 0 | case 13: return 256; |
2556 | 0 | case 18: |
2557 | 0 | case 24: |
2558 | 0 | case 25: |
2559 | 0 | return pmk_len <= 256 ? 128 : 256; |
2560 | 0 | default: |
2561 | | /* Unknown / Not supported */ |
2562 | 0 | ws_warning("Unknown akm"); |
2563 | 0 | return -1; |
2564 | 0 | } |
2565 | 0 | } |
2566 | | |
2567 | | /* From IEEE 802.11-2024 12.7.1.3 Pairwise key hierarchy and |
2568 | | * Table 12-11 Integrity and key-wrap algorithms */ |
2569 | | static int Dot11DecryptGetPtkLen(int akm, int cipher, size_t pmk_len) |
2570 | 0 | { |
2571 | 0 | int kck_len = Dot11DecryptGetKckLen(akm, pmk_len); |
2572 | 0 | int kek_len = Dot11DecryptGetKekLen(akm, pmk_len); |
2573 | 0 | int tk_len = Dot11DecryptGetTkLen(cipher); |
2574 | 0 | int ptk_len; |
2575 | |
|
2576 | 0 | if (kck_len == -1 || kek_len == -1 || tk_len == -1) { |
2577 | 0 | ws_warning("Invalid PTK len"); |
2578 | 0 | return -1; |
2579 | 0 | } |
2580 | 0 | ptk_len = kck_len + kek_len + tk_len; |
2581 | |
|
2582 | 0 | ws_assert(ptk_len <= DOT11DECRYPT_WPA_PTK_MAX_LEN * 8); |
2583 | |
|
2584 | 0 | return ptk_len; |
2585 | 0 | } |
2586 | | |
2587 | | /* From IEEE 802.11-2024 12.7.1.2 PRF and Table 9-190 AKM suite selectors */ |
2588 | | static int |
2589 | | Dot11DecryptGetDeriveFuncFromAkm(int akm) |
2590 | 0 | { |
2591 | 0 | int func = -1; |
2592 | 0 | switch (akm) { |
2593 | 0 | case 1: |
2594 | 0 | case 2: |
2595 | 0 | func = DOT11DECRYPT_DERIVE_USING_PRF; |
2596 | 0 | break; |
2597 | 0 | case 3: |
2598 | 0 | case 4: |
2599 | 0 | case 5: |
2600 | 0 | case 6: |
2601 | 0 | case 7: |
2602 | 0 | case 8: |
2603 | 0 | case 9: |
2604 | 0 | case 10: |
2605 | 0 | case 11: |
2606 | 0 | case 12: |
2607 | 0 | case 13: |
2608 | 0 | case 18: |
2609 | 0 | case 24: |
2610 | 0 | case 25: |
2611 | 0 | func = DOT11DECRYPT_DERIVE_USING_KDF; |
2612 | 0 | break; |
2613 | 0 | default: |
2614 | | /* Unknown / Not supported yet */ |
2615 | 0 | break; |
2616 | 0 | } |
2617 | 0 | return func; |
2618 | 0 | } |
2619 | | |
2620 | | /* From IEEE 802.11-2024 12.7.1.2 PRF and Table 9-190 AKM suite selectors */ |
2621 | | static int |
2622 | | Dot11DecryptGetHashAlgoFromAkm(int akm, size_t pmk_len) |
2623 | 0 | { |
2624 | 0 | int algo = -1; |
2625 | 0 | switch (akm) { |
2626 | 0 | case 1: |
2627 | 0 | case 2: |
2628 | 0 | algo = GCRY_MD_SHA1; |
2629 | 0 | break; |
2630 | 0 | case 3: |
2631 | 0 | case 4: |
2632 | 0 | case 5: |
2633 | 0 | case 6: |
2634 | 0 | case 7: |
2635 | 0 | case 8: |
2636 | 0 | case 9: |
2637 | 0 | case 10: |
2638 | 0 | case 11: |
2639 | 0 | algo = GCRY_MD_SHA256; |
2640 | 0 | break; |
2641 | 0 | case 12: |
2642 | 0 | case 13: |
2643 | 0 | algo = GCRY_MD_SHA384; |
2644 | 0 | break; |
2645 | 0 | case 18: |
2646 | 0 | case 24: |
2647 | 0 | case 25: |
2648 | 0 | if (pmk_len == 256) |
2649 | 0 | algo = GCRY_MD_SHA256; |
2650 | 0 | else if (pmk_len == 384) |
2651 | 0 | algo = GCRY_MD_SHA384; |
2652 | 0 | else |
2653 | 0 | algo = GCRY_MD_SHA512; |
2654 | 0 | break; |
2655 | 0 | default: |
2656 | | /* Unknown / Not supported yet */ |
2657 | 0 | break; |
2658 | 0 | } |
2659 | 0 | return algo; |
2660 | 0 | } |
2661 | | |
2662 | | /* derive the PTK from the AA, SPA, PMK, SNonce, ANonce */ |
2663 | | /** From IEEE 802.11-2024 12.7.1.3 Pairwise key hierarchy: |
2664 | | * PRF-Length(PMK, "Pairwise key expansion", |
2665 | | * Min(AA, SPA) || Max(AA, SPA) || |
2666 | | * Min(ANonce, SNonce) || Max(ANonce, SNonce)) |
2667 | | */ |
2668 | | static uint8_t |
2669 | | Dot11DecryptDerivePtk( |
2670 | | const DOT11DECRYPT_SEC_ASSOCIATION *sa, |
2671 | | const unsigned char *pmk, |
2672 | | size_t pmk_len, |
2673 | | const unsigned char snonce[32], |
2674 | | int key_version, |
2675 | | int akm, |
2676 | | int cipher, |
2677 | | uint8_t *ptk, size_t *ptk_len) |
2678 | 0 | { |
2679 | 0 | int algo = -1; |
2680 | 0 | int ptk_len_bits = -1; |
2681 | 0 | int derive_func; |
2682 | |
|
2683 | 0 | if (!sa || !pmk || !snonce || !ptk || !ptk_len) { |
2684 | 0 | ws_warning("Invalid input for PTK derivation"); |
2685 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2686 | 0 | } |
2687 | | |
2688 | 0 | if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) { |
2689 | | /* TKIP */ |
2690 | 0 | ptk_len_bits = 512; |
2691 | 0 | derive_func = DOT11DECRYPT_DERIVE_USING_PRF; |
2692 | 0 | algo = GCRY_MD_SHA1; |
2693 | 0 | } else { |
2694 | | /* From IEEE 802.11-2024 Table 12-11 Integrity and key-wrap algorithms */ |
2695 | 0 | ptk_len_bits = Dot11DecryptGetPtkLen(akm, cipher, pmk_len * 8); |
2696 | 0 | algo = Dot11DecryptGetHashAlgoFromAkm(akm, pmk_len * 8); |
2697 | 0 | derive_func = Dot11DecryptGetDeriveFuncFromAkm(akm); |
2698 | 0 | ws_debug("ptk_len_bits: %d, algo: %d, cipher: %d", ptk_len_bits, algo, cipher); |
2699 | 0 | } |
2700 | |
|
2701 | 0 | if (ptk_len_bits == -1 || algo == -1) { |
2702 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2703 | 0 | } |
2704 | 0 | if ((size_t)ptk_len_bits / 8 > *ptk_len) { |
2705 | 0 | ws_warning("Provided PTK buffer too small"); |
2706 | 0 | return DOT11DECRYPT_RET_WRONG_DATA_SIZE; |
2707 | 0 | } |
2708 | 0 | *ptk_len = ptk_len_bits / 8; |
2709 | |
|
2710 | 0 | static const char *const label = "Pairwise key expansion"; |
2711 | 0 | uint8_t context[DOT11DECRYPT_MAC_LEN * 2 + 32 * 2]; |
2712 | 0 | int offset = 0; |
2713 | 0 | const uint8_t *spa = sa->saId.sta, *aa = sa->saId.bssid; |
2714 | |
|
2715 | 0 | if (sa->wpa.mld) { |
2716 | 0 | spa = sa->wpa.sta_mld_mac; |
2717 | 0 | aa = sa->wpa.ap_mld_mac; |
2718 | 0 | } |
2719 | | |
2720 | | /* Min(AA, SPA) || Max(AA, SPA) */ |
2721 | 0 | if (memcmp(spa, aa, DOT11DECRYPT_MAC_LEN) < 0) |
2722 | 0 | { |
2723 | 0 | memcpy(context + offset, spa, DOT11DECRYPT_MAC_LEN); |
2724 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
2725 | 0 | memcpy(context + offset, aa, DOT11DECRYPT_MAC_LEN); |
2726 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
2727 | 0 | } |
2728 | 0 | else |
2729 | 0 | { |
2730 | 0 | memcpy(context + offset, aa, DOT11DECRYPT_MAC_LEN); |
2731 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
2732 | 0 | memcpy(context + offset, spa, DOT11DECRYPT_MAC_LEN); |
2733 | 0 | offset += DOT11DECRYPT_MAC_LEN; |
2734 | 0 | } |
2735 | | |
2736 | | /* Min(ANonce, SNonce) || Max(ANonce, SNonce) */ |
2737 | 0 | if (memcmp(snonce, sa->wpa.nonce, 32) < 0 ) |
2738 | 0 | { |
2739 | 0 | memcpy(context + offset, snonce, 32); |
2740 | 0 | offset += 32; |
2741 | 0 | memcpy(context + offset, sa->wpa.nonce, 32); |
2742 | 0 | offset += 32; |
2743 | 0 | } |
2744 | 0 | else |
2745 | 0 | { |
2746 | 0 | memcpy(context + offset, sa->wpa.nonce, 32); |
2747 | 0 | offset += 32; |
2748 | 0 | memcpy(context + offset, snonce, 32); |
2749 | 0 | offset += 32; |
2750 | 0 | } |
2751 | 0 | if (derive_func == DOT11DECRYPT_DERIVE_USING_PRF) { |
2752 | 0 | dot11decrypt_prf(pmk, pmk_len, label, context, offset, algo, |
2753 | 0 | ptk, *ptk_len); |
2754 | 0 | } else { |
2755 | 0 | dot11decrypt_kdf(pmk, pmk_len, label, context, offset, algo, |
2756 | 0 | ptk, *ptk_len); |
2757 | 0 | } |
2758 | 0 | DEBUG_DUMP("PTK", ptk, *ptk_len, LOG_LEVEL_DEBUG); |
2759 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
2760 | 0 | } |
2761 | | |
2762 | | /** |
2763 | | * For Fast BSS Transition AKMS derive PTK from sa, selected key and various information in |
2764 | | * eapol key frame. |
2765 | | * From IEEE 802.11-2016 12.7.1.7.1 |
2766 | | */ |
2767 | | static uint8_t |
2768 | | Dot11DecryptFtDerivePtk( |
2769 | | const PDOT11DECRYPT_CONTEXT ctx, |
2770 | | const DOT11DECRYPT_SEC_ASSOCIATION *sa, |
2771 | | const PDOT11DECRYPT_KEY_ITEM key, |
2772 | | const uint8_t mdid[2], |
2773 | | const uint8_t *snonce, |
2774 | | const uint8_t *r0kh_id, size_t r0kh_id_len, |
2775 | | const uint8_t *r1kh_id, size_t r1kh_id_len _U_, |
2776 | | int akm, int cipher, |
2777 | | uint8_t *ptk, size_t *ptk_len) |
2778 | 0 | { |
2779 | 0 | int hash_algo; |
2780 | 0 | uint8_t pmk_r0[DOT11DECRYPT_WPA_PMK_MAX_LEN]; |
2781 | 0 | uint8_t pmk_r1[DOT11DECRYPT_WPA_PMK_MAX_LEN]; |
2782 | 0 | uint8_t pmk_r0_name[16] = {0}; |
2783 | 0 | uint8_t pmk_r1_name[16] = {0}; |
2784 | 0 | uint8_t ptk_name[16]; |
2785 | 0 | size_t pmk_r0_len = 0; |
2786 | 0 | size_t pmk_r1_len = 0; |
2787 | 0 | const uint8_t *xxkey = NULL; |
2788 | 0 | size_t xxkey_len; |
2789 | 0 | int ptk_len_bits; |
2790 | |
|
2791 | 0 | if (!sa || !key || !mdid || !snonce || !r0kh_id || !r1kh_id || !ptk || !ptk_len) { |
2792 | 0 | ws_warning("Invalid input for FT PTK derivation"); |
2793 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2794 | 0 | } |
2795 | | |
2796 | 0 | if (key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) { |
2797 | 0 | xxkey = Dot11DecryptGetXXKeyFromMSK(key->Msk.Msk, |
2798 | 0 | key->Msk.Len, |
2799 | 0 | akm, |
2800 | 0 | &xxkey_len); |
2801 | 0 | } |
2802 | 0 | if (!xxkey && key->KeyData.Wpa.PskLen > 0) { |
2803 | 0 | xxkey = key->KeyData.Wpa.Psk; |
2804 | 0 | xxkey_len = key->KeyData.Wpa.PskLen; |
2805 | 0 | } |
2806 | 0 | if (!xxkey) { |
2807 | 0 | ws_debug("no xxkey. Skipping"); |
2808 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2809 | 0 | } |
2810 | 0 | hash_algo = Dot11DecryptGetHashAlgoFromAkm(akm, xxkey_len * 8); |
2811 | 0 | ptk_len_bits = Dot11DecryptGetPtkLen(akm, cipher, xxkey_len * 8); |
2812 | 0 | if (ptk_len_bits == -1) { |
2813 | 0 | ws_warning("Invalid akm or cipher"); |
2814 | 0 | return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE; |
2815 | 0 | } |
2816 | 0 | if ((size_t)ptk_len_bits / 8 > *ptk_len) { |
2817 | 0 | ws_warning("Provided PTK buffer too small"); |
2818 | 0 | return DOT11DECRYPT_RET_WRONG_DATA_SIZE; |
2819 | 0 | } |
2820 | 0 | *ptk_len = ptk_len_bits / 8; |
2821 | 0 | if (!dot11decrypt_derive_pmk_r0(xxkey, xxkey_len, |
2822 | 0 | ctx->pkt_ssid, ctx->pkt_ssid_len, |
2823 | 0 | mdid, |
2824 | 0 | r0kh_id, r0kh_id_len, |
2825 | 0 | sa->saId.sta, hash_algo, |
2826 | 0 | pmk_r0, &pmk_r0_len, pmk_r0_name)) { |
2827 | | /* This can fail for bad size or a bad SHA256 sum. */ |
2828 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2829 | 0 | } |
2830 | 0 | DEBUG_DUMP("PMK-R0", pmk_r0, pmk_r0_len, LOG_LEVEL_DEBUG); |
2831 | 0 | DEBUG_DUMP("PMKR0Name", pmk_r0_name, 16, LOG_LEVEL_DEBUG); |
2832 | |
|
2833 | 0 | if (!dot11decrypt_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, |
2834 | 0 | r1kh_id, sa->saId.sta, hash_algo, |
2835 | 0 | pmk_r1, &pmk_r1_len, pmk_r1_name)) { |
2836 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2837 | 0 | } |
2838 | 0 | DEBUG_DUMP("PMK-R1", pmk_r1, pmk_r1_len, LOG_LEVEL_DEBUG); |
2839 | 0 | DEBUG_DUMP("PMKR1Name", pmk_r1_name, 16, LOG_LEVEL_DEBUG); |
2840 | | |
2841 | | // Reference: IEEE 802.11be-2024 12.7.1.6.5 |
2842 | 0 | const uint8_t *sta_addr = sa->saId.sta, *bssid = sa->saId.bssid; |
2843 | 0 | if (sa->wpa.mld) { |
2844 | 0 | sta_addr = sa->wpa.sta_mld_mac; |
2845 | 0 | bssid = sa->wpa.ap_mld_mac; |
2846 | 0 | } |
2847 | |
|
2848 | 0 | if (!dot11decrypt_derive_ft_ptk(pmk_r1, pmk_r1_len, pmk_r1_name, |
2849 | 0 | snonce, sa->wpa.nonce, |
2850 | 0 | bssid, sta_addr, hash_algo, |
2851 | 0 | ptk, *ptk_len, ptk_name)) { |
2852 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2853 | 0 | } |
2854 | 0 | DEBUG_DUMP("PTK", ptk, *ptk_len, LOG_LEVEL_DEBUG); |
2855 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
2856 | 0 | } |
2857 | | |
2858 | 0 | #define MAX_SSID_LENGTH 32 /* maximum SSID length */ |
2859 | | |
2860 | | static int |
2861 | | Dot11DecryptRsnaPwd2Psk( |
2862 | | const struct DOT11DECRYPT_KEY_ITEMDATA_PWD *userPwd, |
2863 | | unsigned char *output) |
2864 | 0 | { |
2865 | 0 | if (userPwd->SsidLen> MAX_SSID_LENGTH) { |
2866 | | /* This "should not happen" */ |
2867 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2868 | 0 | } |
2869 | 0 | if (gcry_kdf_derive(userPwd->Passphrase, userPwd->PassphraseLen, GCRY_KDF_PBKDF2, |
2870 | 0 | GCRY_MD_SHA1, userPwd->Ssid, userPwd->SsidLen, 4096, |
2871 | 0 | DOT11DECRYPT_WPA_PWD_PSK_LEN, output)) { |
2872 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
2873 | 0 | } |
2874 | | |
2875 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
2876 | 0 | } |
2877 | | |
2878 | | /* |
2879 | | * Returns the decryption_key_t struct given a string describing the key. |
2880 | | * Returns NULL if the input_string cannot be parsed. |
2881 | | * XXX: Should return an error string explaining why parsing failed |
2882 | | */ |
2883 | | decryption_key_t* |
2884 | | parse_key_string(char* input_string, uint8_t key_type, char** error) |
2885 | 0 | { |
2886 | 0 | GByteArray *ssid_ba = NULL, *key_ba; |
2887 | |
|
2888 | 0 | char **tokens; |
2889 | 0 | unsigned n = 0; |
2890 | 0 | decryption_key_t *dk; |
2891 | |
|
2892 | 0 | if(input_string == NULL || (strcmp(input_string, "") == 0)) { |
2893 | 0 | if (error) { |
2894 | 0 | *error = g_strdup("Key cannot be empty"); |
2895 | 0 | } |
2896 | 0 | return NULL; |
2897 | 0 | } |
2898 | | |
2899 | | /* |
2900 | | * Parse the input_string. WEP and WPA will be just a string |
2901 | | * of hexadecimal characters (if key is wrong, null will be |
2902 | | * returned...). |
2903 | | * WPA-PWD should be in the form |
2904 | | * <key data>[:<ssid>] |
2905 | | * With WPA-PWD, we percent-decode the key data and ssid. |
2906 | | * The percent itself ("%25") and the colon ("%3a") must be |
2907 | | * percent-encoded, the latter so we can distinguish between the |
2908 | | * separator and a colon in the key or ssid. Percent-encoding |
2909 | | * for anything else is optional. (NUL is not allowed, either |
2910 | | * percent-encoded or not.) |
2911 | | */ |
2912 | | |
2913 | 0 | switch(key_type) |
2914 | 0 | { |
2915 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP: |
2916 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP_40: |
2917 | 0 | case DOT11DECRYPT_KEY_TYPE_WEP_104: |
2918 | |
|
2919 | 0 | key_ba = g_byte_array_new(); |
2920 | |
|
2921 | 0 | if (!hex_str_to_bytes(input_string, key_ba, false)) { |
2922 | 0 | if (error) { |
2923 | 0 | *error = g_strdup("WEP key must be a hexadecimal string"); |
2924 | 0 | } |
2925 | 0 | g_byte_array_free(key_ba, true); |
2926 | 0 | return NULL; |
2927 | 0 | } |
2928 | | |
2929 | 0 | if (key_ba->len > 0 && key_ba->len <= DOT11DECRYPT_WEP_KEY_MAXLEN) { |
2930 | | /* Key is correct! It was probably an 'old style' WEP key */ |
2931 | | /* Create the decryption_key_t structure, fill it and return it*/ |
2932 | 0 | dk = g_new0(decryption_key_t, 1); |
2933 | |
|
2934 | 0 | dk->type = DOT11DECRYPT_KEY_TYPE_WEP; |
2935 | 0 | dk->key = key_ba; |
2936 | 0 | dk->bits = key_ba->len * 8; |
2937 | 0 | dk->ssid = NULL; |
2938 | |
|
2939 | 0 | return dk; |
2940 | 0 | } |
2941 | | |
2942 | 0 | if (error) { |
2943 | 0 | *error = ws_strdup_printf("WEP key entered is %u bytes, and must be no more than %u", key_ba->len, DOT11DECRYPT_WEP_KEY_MAXLEN); |
2944 | 0 | } |
2945 | | /* Key doesn't work */ |
2946 | 0 | g_byte_array_free(key_ba, true); |
2947 | 0 | return NULL; |
2948 | | |
2949 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PWD: |
2950 | |
|
2951 | 0 | tokens = g_strsplit(input_string,":", 3); |
2952 | 0 | n = g_strv_length(tokens); |
2953 | |
|
2954 | 0 | if (n < 1 || n > 2) |
2955 | 0 | { |
2956 | | /* Require either one or two tokens; more, and the user |
2957 | | * may have meant a colon in the passphrase or SSID name |
2958 | | */ |
2959 | | /* Free the array of strings */ |
2960 | | /* XXX: Return why parsing failed (":" must be escaped) */ |
2961 | 0 | if (error) { |
2962 | 0 | *error = g_strdup("Only one ':' is allowed, as a separator between passphrase and SSID; others must be percent-encoded as \"%%3a\""); |
2963 | 0 | } |
2964 | 0 | g_strfreev(tokens); |
2965 | 0 | return NULL; |
2966 | 0 | } |
2967 | | |
2968 | | /* |
2969 | | * The first token is the key |
2970 | | */ |
2971 | 0 | key_ba = g_byte_array_new(); |
2972 | 0 | if (! uri_str_to_bytes(tokens[0], key_ba)) { |
2973 | | /* Failed parsing as percent-encoded */ |
2974 | 0 | if (error) { |
2975 | 0 | *error = g_strdup("WPA passphrase is treated as percent-encoded; use \"%%25\" for a literal \"%%\""); |
2976 | 0 | } |
2977 | 0 | g_byte_array_free(key_ba, true); |
2978 | 0 | g_strfreev(tokens); |
2979 | 0 | return NULL; |
2980 | 0 | } |
2981 | | |
2982 | | /* key length (after percent-decoding) should be between 8 and 63 |
2983 | | * octets (63 to distinguish from a PSK as 64 hex characters.) |
2984 | | * XXX: 802.11-2016 Annex J assumes that each character in the |
2985 | | * pass-phrase is ASCII printable ("has an encoding in the range |
2986 | | * 32 to 126"), though this (and the entire algorithm for that |
2987 | | * matter) is only considered a suggestion. |
2988 | | * It is possible to apply PBKDF2 to any octet string, e.g. UTF-8. |
2989 | | * (wpa_passphrase from wpa_supplicant will do so, for example.) |
2990 | | */ |
2991 | 0 | if( ((key_ba->len) > WPA_KEY_MAX_CHAR_SIZE) || ((key_ba->len) < WPA_KEY_MIN_CHAR_SIZE)) |
2992 | 0 | { |
2993 | 0 | if (error) { |
2994 | 0 | *error = ws_strdup_printf("WPA passphrase entered is %u characters after percent-decoding and must be between %u and %u", key_ba->len, WPA_KEY_MIN_CHAR_SIZE, WPA_KEY_MAX_CHAR_SIZE); |
2995 | 0 | } |
2996 | 0 | g_byte_array_free(key_ba, true); |
2997 | | |
2998 | | /* Free the array of strings */ |
2999 | 0 | g_strfreev(tokens); |
3000 | 0 | return NULL; |
3001 | 0 | } |
3002 | | |
3003 | 0 | ssid_ba = NULL; |
3004 | 0 | if (n >= 2) /* more than two tokens found, means that the user specified the ssid */ |
3005 | 0 | { |
3006 | 0 | ssid_ba = g_byte_array_new(); |
3007 | 0 | if (! uri_str_to_bytes(tokens[1], ssid_ba)) { |
3008 | 0 | if (error) { |
3009 | 0 | *error = g_strdup("WPA SSID is treated as percent-encoded; use \"%%25\" for a literal \"%%\"."); |
3010 | 0 | } |
3011 | 0 | g_byte_array_free(key_ba, true); |
3012 | 0 | g_byte_array_free(ssid_ba, true); |
3013 | | /* Free the array of strings */ |
3014 | 0 | g_strfreev(tokens); |
3015 | 0 | return NULL; |
3016 | 0 | } |
3017 | | |
3018 | 0 | if(ssid_ba->len > WPA_SSID_MAX_CHAR_SIZE) |
3019 | 0 | { |
3020 | 0 | if (error) { |
3021 | 0 | *error = ws_strdup_printf("WPA SSID entered is %u characters after percent-decoding and must be no more than %u", ssid_ba->len, WPA_SSID_MAX_CHAR_SIZE); |
3022 | 0 | } |
3023 | 0 | g_byte_array_free(key_ba, true); |
3024 | 0 | g_byte_array_free(ssid_ba, true); |
3025 | | |
3026 | | /* Free the array of strings */ |
3027 | 0 | g_strfreev(tokens); |
3028 | 0 | return NULL; |
3029 | 0 | } |
3030 | 0 | } |
3031 | | |
3032 | | /* Key was correct!!! Create the new decryption_key_t ... */ |
3033 | 0 | dk = g_new0(decryption_key_t, 1); |
3034 | |
|
3035 | 0 | dk->type = DOT11DECRYPT_KEY_TYPE_WPA_PWD; |
3036 | 0 | dk->key = key_ba; |
3037 | 0 | dk->bits = 256; /* This is the length of the array pf bytes that will be generated using key+ssid ...*/ |
3038 | 0 | dk->ssid = ssid_ba; /* NULL if ssid_ba is NULL */ |
3039 | | |
3040 | | /* Free the array of strings */ |
3041 | 0 | g_strfreev(tokens); |
3042 | 0 | return dk; |
3043 | | |
3044 | 0 | case DOT11DECRYPT_KEY_TYPE_WPA_PSK: |
3045 | |
|
3046 | 0 | key_ba = g_byte_array_new(); |
3047 | 0 | if (!hex_str_to_bytes(input_string, key_ba, false)) { |
3048 | 0 | if (error) { |
3049 | 0 | *error = g_strdup("WPA PSK/PMK must be a hexadecimal string"); |
3050 | 0 | } |
3051 | 0 | g_byte_array_free(key_ba, true); |
3052 | 0 | return NULL; |
3053 | 0 | } |
3054 | | |
3055 | | /* Two tokens means that the user should have entered a WPA-BIN key ... */ |
3056 | 0 | if((key_ba->len != DOT11DECRYPT_WPA_PWD_PSK_LEN && |
3057 | 0 | key_ba->len != 48 && |
3058 | 0 | key_ba->len != DOT11DECRYPT_WPA_PMK_MAX_LEN)) |
3059 | 0 | { |
3060 | 0 | if (error) { |
3061 | 0 | *error = ws_strdup_printf("WPA Pre-Master Key/Pairwise Master Key entered is %u bytes and must be 32, 48 or 64", key_ba->len); |
3062 | 0 | } |
3063 | 0 | g_byte_array_free(key_ba, true); |
3064 | 0 | return NULL; |
3065 | 0 | } |
3066 | | |
3067 | | /* Key was correct!!! Create the new decryption_key_t ... */ |
3068 | 0 | dk = g_new0(decryption_key_t, 1); |
3069 | |
|
3070 | 0 | dk->type = DOT11DECRYPT_KEY_TYPE_WPA_PSK; |
3071 | 0 | dk->key = key_ba; |
3072 | 0 | dk->bits = (unsigned) dk->key->len * 8; |
3073 | 0 | dk->ssid = NULL; |
3074 | |
|
3075 | 0 | return dk; |
3076 | | |
3077 | 0 | case DOT11DECRYPT_KEY_TYPE_TK: |
3078 | 0 | { |
3079 | 0 | tokens = g_strsplit(input_string,":", 3); |
3080 | 0 | n = g_strv_length(tokens); |
3081 | 0 | if (!(n == 1 || n == 3)) |
3082 | 0 | { |
3083 | | /* Free the array of strings */ |
3084 | 0 | if (error) { |
3085 | 0 | *error = g_strdup("TK must be in TK[:AP MLD MAC:STA MLD MAC] format"); |
3086 | 0 | } |
3087 | 0 | g_strfreev(tokens); |
3088 | 0 | return NULL; |
3089 | 0 | } |
3090 | | /* From IEEE 802.11-2024 Table 12-8 Cipher suite key lengths */ |
3091 | 0 | static const uint8_t allowed_key_lengths[] = { |
3092 | | // TBD 40 / 8, /* WEP-40 */ |
3093 | | // TBD 104 / 8, /* WEP-104 */ |
3094 | 0 | 128 / 8, /* CCMP-128, GCMP-128 */ |
3095 | 0 | 256 / 8, /* TKIP, GCMP-256, CCMP-256 */ |
3096 | 0 | }; |
3097 | 0 | bool key_length_ok = false; |
3098 | |
|
3099 | 0 | key_ba = g_byte_array_new(); |
3100 | 0 | if (!hex_str_to_bytes(tokens[0], key_ba, false)) { |
3101 | 0 | if (error) { |
3102 | 0 | *error = g_strdup("Temporal Key must be a hexadecimal string"); |
3103 | 0 | } |
3104 | 0 | g_byte_array_free(key_ba, true); |
3105 | 0 | g_strfreev(tokens); |
3106 | 0 | return NULL; |
3107 | 0 | } |
3108 | | |
3109 | 0 | for (size_t i = 0; i < sizeof(allowed_key_lengths); i++) { |
3110 | 0 | if (key_ba->len == allowed_key_lengths[i]) { |
3111 | 0 | key_length_ok = true; |
3112 | 0 | break; |
3113 | 0 | } |
3114 | 0 | } |
3115 | 0 | if (!key_length_ok) { |
3116 | 0 | if (error) { |
3117 | 0 | GString *err_string = g_string_new("Temporal Keys entered is "); |
3118 | 0 | g_string_append_printf(err_string, "%u bytes and must be ", key_ba->len); |
3119 | 0 | size_t i = 0; |
3120 | 0 | for (; i + 1 < sizeof(allowed_key_lengths); i++) { |
3121 | 0 | g_string_append_printf(err_string, "%u, ", allowed_key_lengths[i]); |
3122 | 0 | } |
3123 | 0 | g_string_append_printf(err_string, "or %u bytes.", allowed_key_lengths[i]); |
3124 | 0 | *error = g_string_free(err_string, FALSE); |
3125 | 0 | } |
3126 | 0 | g_byte_array_free(key_ba, true); |
3127 | 0 | g_strfreev(tokens); |
3128 | 0 | return NULL; |
3129 | 0 | } |
3130 | 0 | dk = g_new0(decryption_key_t, 1); |
3131 | |
|
3132 | 0 | if (n == 3) { |
3133 | 0 | dk->tk_mld = true; |
3134 | 0 | GByteArray *mac = g_byte_array_new(); |
3135 | 0 | for (size_t i = 1; i <= 2; ++i) { |
3136 | 0 | if (!hex_str_to_bytes(tokens[i], mac, false) || mac->len != 6) { |
3137 | 0 | if (error) { |
3138 | 0 | *error = g_strdup("MAC must be a 6 bytes hexadecimal string"); |
3139 | 0 | } |
3140 | 0 | g_byte_array_free(mac, true); |
3141 | 0 | g_free(dk); |
3142 | 0 | g_byte_array_free(key_ba, true); |
3143 | 0 | g_strfreev(tokens); |
3144 | 0 | return NULL; |
3145 | 0 | } |
3146 | 0 | if (i == 1) |
3147 | 0 | memcpy(dk->ap_mld_mac, mac->data, 6); |
3148 | 0 | else |
3149 | 0 | memcpy(dk->sta_mld_mac, mac->data, 6); |
3150 | 0 | } |
3151 | 0 | g_byte_array_free(mac, true); |
3152 | 0 | } |
3153 | | |
3154 | 0 | dk->type = DOT11DECRYPT_KEY_TYPE_TK; |
3155 | 0 | dk->key = key_ba; |
3156 | 0 | dk->bits = (unsigned) dk->key->len * 8; |
3157 | 0 | dk->ssid = NULL; |
3158 | | |
3159 | | /* Free the array of strings */ |
3160 | 0 | g_strfreev(tokens); |
3161 | 0 | return dk; |
3162 | 0 | } |
3163 | 0 | case DOT11DECRYPT_KEY_TYPE_MSK: |
3164 | 0 | { |
3165 | 0 | key_ba = g_byte_array_new(); |
3166 | 0 | if (!hex_str_to_bytes(input_string, key_ba, false)) { |
3167 | 0 | if (error) { |
3168 | 0 | *error = g_strdup("Master Session Key must be a hexadecimal string"); |
3169 | 0 | } |
3170 | 0 | g_byte_array_free(key_ba, true); |
3171 | 0 | return NULL; |
3172 | 0 | } |
3173 | | |
3174 | 0 | if (key_ba->len < DOT11DECRYPT_MSK_MIN_LEN || |
3175 | 0 | key_ba->len > DOT11DECRYPT_MSK_MAX_LEN) |
3176 | 0 | { |
3177 | 0 | if (error) { |
3178 | 0 | *error = ws_strdup_printf("Master Session Key entered is %u bytes and must be between %u and %u", key_ba->len, DOT11DECRYPT_MSK_MIN_LEN, DOT11DECRYPT_MSK_MAX_LEN); |
3179 | 0 | } |
3180 | 0 | g_byte_array_free(key_ba, true); |
3181 | 0 | return NULL; |
3182 | 0 | } |
3183 | 0 | dk = g_new0(decryption_key_t, 1); |
3184 | 0 | dk->type = DOT11DECRYPT_KEY_TYPE_MSK; |
3185 | 0 | dk->key = key_ba; |
3186 | 0 | dk->bits = (unsigned)dk->key->len * 8; |
3187 | 0 | dk->ssid = NULL; |
3188 | 0 | return dk; |
3189 | 0 | } |
3190 | 0 | } |
3191 | | |
3192 | | /* Type not supported */ |
3193 | 0 | if (error) { |
3194 | 0 | *error = g_strdup("Unknown key type not supported"); |
3195 | 0 | } |
3196 | 0 | return NULL; |
3197 | 0 | } |
3198 | | |
3199 | | void |
3200 | | free_key_string(decryption_key_t *dk) |
3201 | 0 | { |
3202 | 0 | if (dk->key) |
3203 | 0 | g_byte_array_free(dk->key, true); |
3204 | 0 | if (dk->ssid) |
3205 | 0 | g_byte_array_free(dk->ssid, true); |
3206 | 0 | g_free(dk); |
3207 | 0 | } |
3208 | | |
3209 | | static int |
3210 | | Dot11DecryptTDLSDeriveKey( |
3211 | | PDOT11DECRYPT_SEC_ASSOCIATION sa, |
3212 | | const uint8_t *data, |
3213 | | unsigned offset_rsne, |
3214 | | unsigned offset_fte, |
3215 | | unsigned offset_timeout, |
3216 | | unsigned offset_link, |
3217 | | uint8_t action) |
3218 | 0 | { |
3219 | |
|
3220 | 0 | gcry_md_hd_t sha256_handle; |
3221 | 0 | gcry_md_hd_t hmac_handle; |
3222 | 0 | const uint8_t *snonce, *anonce, *initiator, *responder, *bssid; |
3223 | 0 | uint8_t key_input[32]; |
3224 | 0 | uint8_t mic[16], seq_num = action + 1; |
3225 | 0 | uint8_t zeros[16] = { 0 }; |
3226 | 0 | gcry_mac_hd_t cmac_handle; |
3227 | 0 | size_t cmac_len = 16; |
3228 | 0 | size_t cmac_write_len; |
3229 | | |
3230 | | /* Get key input */ |
3231 | 0 | anonce = &data[offset_fte + 20]; |
3232 | 0 | snonce = &data[offset_fte + 52]; |
3233 | |
|
3234 | 0 | if (gcry_md_open (&sha256_handle, GCRY_MD_SHA256, 0)) { |
3235 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3236 | 0 | } |
3237 | 0 | if (memcmp(anonce, snonce, DOT11DECRYPT_WPA_NONCE_LEN) < 0) { |
3238 | 0 | gcry_md_write(sha256_handle, anonce, DOT11DECRYPT_WPA_NONCE_LEN); |
3239 | 0 | gcry_md_write(sha256_handle, snonce, DOT11DECRYPT_WPA_NONCE_LEN); |
3240 | 0 | } else { |
3241 | 0 | gcry_md_write(sha256_handle, snonce, DOT11DECRYPT_WPA_NONCE_LEN); |
3242 | 0 | gcry_md_write(sha256_handle, anonce, DOT11DECRYPT_WPA_NONCE_LEN); |
3243 | 0 | } |
3244 | 0 | memcpy(key_input, gcry_md_read(sha256_handle, 0), 32); |
3245 | 0 | gcry_md_close(sha256_handle); |
3246 | | |
3247 | | /* Derive key */ |
3248 | 0 | bssid = &data[offset_link + 2]; |
3249 | 0 | initiator = &data[offset_link + 8]; |
3250 | 0 | responder = &data[offset_link + 14]; |
3251 | 0 | if (gcry_md_open(&hmac_handle, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC)) { |
3252 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3253 | 0 | } |
3254 | 0 | if (gcry_md_setkey(hmac_handle, key_input, 32)) { |
3255 | 0 | gcry_md_close(hmac_handle); |
3256 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3257 | 0 | } |
3258 | 0 | gcry_md_putc(hmac_handle, 1); |
3259 | 0 | gcry_md_putc(hmac_handle, 0); |
3260 | 0 | gcry_md_write(hmac_handle, "TDLS PMK", 8); |
3261 | 0 | if (memcmp(initiator, responder, DOT11DECRYPT_MAC_LEN) < 0) { |
3262 | 0 | gcry_md_write(hmac_handle, initiator, DOT11DECRYPT_MAC_LEN); |
3263 | 0 | gcry_md_write(hmac_handle, responder, DOT11DECRYPT_MAC_LEN); |
3264 | 0 | } else { |
3265 | 0 | gcry_md_write(hmac_handle, responder, DOT11DECRYPT_MAC_LEN); |
3266 | 0 | gcry_md_write(hmac_handle, initiator, DOT11DECRYPT_MAC_LEN); |
3267 | 0 | } |
3268 | 0 | gcry_md_write(hmac_handle, bssid, DOT11DECRYPT_MAC_LEN); |
3269 | 0 | gcry_md_putc(hmac_handle, 0); |
3270 | 0 | gcry_md_putc(hmac_handle, 1); |
3271 | 0 | memcpy(key_input, gcry_md_read(hmac_handle, 0), 32); |
3272 | 0 | gcry_md_close(hmac_handle); |
3273 | | |
3274 | | /* Check MIC */ |
3275 | 0 | if (gcry_mac_open(&cmac_handle, GCRY_MAC_CMAC_AES, 0, NULL)) { |
3276 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3277 | 0 | } |
3278 | 0 | if (gcry_mac_setkey(cmac_handle, key_input, 16)) { |
3279 | 0 | gcry_mac_close(cmac_handle); |
3280 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3281 | 0 | } |
3282 | 0 | gcry_mac_write(cmac_handle, initiator, DOT11DECRYPT_MAC_LEN); |
3283 | 0 | gcry_mac_write(cmac_handle, responder, DOT11DECRYPT_MAC_LEN); |
3284 | 0 | gcry_mac_write(cmac_handle, &seq_num, 1); |
3285 | 0 | gcry_mac_write(cmac_handle, &data[offset_link], data[offset_link + 1] + 2); |
3286 | 0 | gcry_mac_write(cmac_handle, &data[offset_rsne], data[offset_rsne + 1] + 2); |
3287 | 0 | gcry_mac_write(cmac_handle, &data[offset_timeout], data[offset_timeout + 1] + 2); |
3288 | 0 | gcry_mac_write(cmac_handle, &data[offset_fte], 4); |
3289 | 0 | gcry_mac_write(cmac_handle, zeros, 16); |
3290 | 0 | cmac_write_len = data[offset_fte + 1] + 2; |
3291 | 0 | if (cmac_write_len < 20) { |
3292 | 0 | ws_warning("Bad MAC len"); |
3293 | 0 | gcry_mac_close(cmac_handle); |
3294 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3295 | 0 | } |
3296 | 0 | gcry_mac_write(cmac_handle, &data[offset_fte + 20], cmac_write_len - 20); |
3297 | 0 | if (gcry_mac_read(cmac_handle, mic, &cmac_len) != GPG_ERR_NO_ERROR) { |
3298 | 0 | ws_warning("MAC read error"); |
3299 | 0 | gcry_mac_close(cmac_handle); |
3300 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3301 | 0 | } |
3302 | 0 | if (memcmp(mic, &data[offset_fte + 4], 16)) { |
3303 | 0 | ws_debug("MIC verification failed"); |
3304 | 0 | gcry_mac_close(cmac_handle); |
3305 | 0 | return DOT11DECRYPT_RET_UNSUCCESS; |
3306 | 0 | } |
3307 | 0 | gcry_mac_close(cmac_handle); |
3308 | | /* TODO support other akm and ciphers? */ |
3309 | 0 | sa->wpa.akm = 2; |
3310 | 0 | sa->wpa.cipher = 4; |
3311 | 0 | sa->wpa.ptk_len = Dot11DecryptGetPtkLen(sa->wpa.akm, sa->wpa.cipher, sa->wpa.pmk_len * 8) / 8; |
3312 | 0 | memcpy(DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm, sa->wpa.pmk_len * 8), |
3313 | 0 | key_input + 16, Dot11DecryptGetTkLen(sa->wpa.cipher) / 8); |
3314 | 0 | memcpy(sa->wpa.nonce, snonce, DOT11DECRYPT_WPA_NONCE_LEN); |
3315 | 0 | sa->validKey = true; |
3316 | 0 | sa->wpa.key_ver = DOT11DECRYPT_WPA_KEY_VER_AES_CCMP; |
3317 | 0 | ws_debug("MIC verified"); |
3318 | 0 | return DOT11DECRYPT_RET_SUCCESS; |
3319 | 0 | } |
3320 | | |
3321 | | |
3322 | | #ifdef __cplusplus |
3323 | | } |
3324 | | #endif |
3325 | | |
3326 | | /****************************************************************************/ |
3327 | | |
3328 | | /* |
3329 | | * Editor modelines |
3330 | | * |
3331 | | * Local Variables: |
3332 | | * c-basic-offset: 4 |
3333 | | * tab-width: 8 |
3334 | | * indent-tabs-mode: nil |
3335 | | * End: |
3336 | | * |
3337 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
3338 | | * :indentSize=4:tabSize=8:noTabs=true: |
3339 | | */ |