3 * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
4 * Copyright (c) 2006 CACE Technologies, Davis (California)
7 * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
10 /****************************************************************************/
14 #include "dot11decrypt_debug.h"
15 #include "dot11decrypt_int.h"
16 #include "dot11decrypt_util.h"
18 /****************************************************************************/
19 /* Internal definitions */
21 #define FC0_AAD_MASK 0x8f
22 #define FC1_AAD_MASK 0xc7
23 #define FC1_AAD_QOS_MASK 0x47
25 /****************************************************************************/
28 /****************************************************************************/
29 /* Internal function prototypes declarations */
31 /****************************************************************************/
32 /* Function definitions */
34 /* From IEEE 802.11 2016 Chapter 12.5.3.3.3 and 12.5.5.3.3 Construct AAD */
35 void dot11decrypt_construct_aad(
36 PDOT11DECRYPT_MAC_FRAME wh
,
40 uint8_t mgmt
= (DOT11DECRYPT_TYPE(wh
->fc
[0]) == DOT11DECRYPT_TYPE_MANAGEMENT
);
44 * FC with bits 4..6 and 11..13 masked to zero; 14 is always one; 15 zero when QoS Control field present
46 * SC with bits 4..15 (seq#) masked to zero
51 /* NB: aad[1] set below */
53 aad
[0] = (uint8_t)(wh
->fc
[0] & FC0_AAD_MASK
);
57 if (DOT11DECRYPT_IS_QOS_DATA(wh
)) {
58 aad
[1] = (uint8_t)((wh
->fc
[1] & FC1_AAD_QOS_MASK
) | 0x40);
60 aad
[1] = (uint8_t)((wh
->fc
[1] & FC1_AAD_MASK
) | 0x40);
62 memcpy(aad
+ 2, (uint8_t *)wh
->addr1
, DOT11DECRYPT_MAC_LEN
);
63 memcpy(aad
+ 8, (uint8_t *)wh
->addr2
, DOT11DECRYPT_MAC_LEN
);
64 memcpy(aad
+ 14, (uint8_t *)wh
->addr3
, DOT11DECRYPT_MAC_LEN
);
65 aad
[20] = (uint8_t)(wh
->seq
[0] & DOT11DECRYPT_SEQ_FRAG_MASK
);
66 aad
[21] = 0; /* all bits masked */
69 * Construct variable-length portion of AAD based
70 * on whether this is a 4-address frame/QOS frame.
72 if (DOT11DECRYPT_IS_4ADDRESS(wh
)) {
74 DOT11DECRYPT_ADDR_COPY(aad
+ 22,
75 ((PDOT11DECRYPT_MAC_FRAME_ADDR4
)wh
)->addr4
);
76 if (DOT11DECRYPT_IS_QOS_DATA(wh
)) {
77 PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS qwh4
=
78 (PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS
) wh
;
79 aad
[28] = (uint8_t)(qwh4
->qos
[0] & 0x0f);/* just priority bits */
84 if (DOT11DECRYPT_IS_QOS_DATA(wh
)) {
85 PDOT11DECRYPT_MAC_FRAME_QOS qwh
=
86 (PDOT11DECRYPT_MAC_FRAME_QOS
) wh
;
87 aad
[22] = (uint8_t)(qwh
->qos
[0] & 0x0f); /* just priority bits */
96 * IEEE 802.11-2016 12.7.1.2 PRF (Pseudo Random Function)
98 * @param key Derivation input key.
99 * @param key_len Length of the key in bytes.
100 * @param label Unique label for each different purpose of the PRF (named 'A' in the standard).
101 * @param context Provides context to identify the derived key (named 'B' in the standard).
102 * @param context_len Length of context in bytes.
103 * @param hash_algo Hash algorithm to use for the PRF.
104 * See gcrypt available hash algorithms:
105 * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
106 * @param[out] output Derived key.
107 * @param output_len Length of derived key in bytes.
108 * @return false on error
110 #define MAX_R_LEN 256
111 #define MAX_TMP_LEN 1024
112 #define MAX_CONTEXT_LEN 256
115 dot11decrypt_prf(const uint8_t *key
, size_t key_len
,
117 const uint8_t *context
, size_t context_len
,
119 uint8_t *output
, size_t output_len
)
121 uint8_t R
[MAX_R_LEN
]; /* Will hold "label || 0 || context || i" */
122 size_t label_len
= strlen(label
);
123 uint8_t tmp
[MAX_TMP_LEN
];
124 uint16_t hash_len
= gcry_md_get_algo_dlen(hash_algo
);
128 if (!key
|| !label
|| !context
|| !output
) {
131 if (label_len
+ 1 + context_len
+ 1 > MAX_R_LEN
||
133 ws_warning("Invalid input or output sizes");
137 /* Fill R with "label || 0 || context || i" */
138 memcpy(R
+ offset
, label
, label_len
);
141 memcpy(R
+ offset
, context
, context_len
);
142 offset
+= context_len
;
144 for (i
= 0; i
<= output_len
* 8 / 160; i
++)
147 if (ws_hmac_buffer(hash_algo
, tmp
+ hash_len
* i
, R
, offset
+ 1, key
, key_len
)) {
151 memcpy(output
, tmp
, output_len
);
156 * 12.7.1.7.2 Key derivation function (KDF)
158 * @param key Derivation input key.
159 * @param key_len Length of the key in bytes.
160 * @param label A string identifying the purpose of the keys derived using this KDF.
161 * @param context Provides context to identify the derived key.
162 * @param context_len Length of context in bytes.
163 * @param hash_algo Hash algorithm to use for the KDF.
164 * See gcrypt available hash algorithms:
165 * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
166 * @param[out] output Derived key.
167 * @param output_len Length of derived key in bytes.
168 * @return false on error
171 dot11decrypt_kdf(const uint8_t *key
, size_t key_len
,
173 const uint8_t *context
, size_t context_len
,
175 uint8_t *output
, size_t output_len
)
177 uint8_t R
[MAX_R_LEN
]; /* Will hold "i || Label || Context || Length" */
178 uint8_t tmp
[MAX_TMP_LEN
];
179 size_t label_len
= strlen(label
);
180 uint16_t hash_len
= gcry_md_get_algo_dlen(hash_algo
);
181 unsigned iterations
= (unsigned)output_len
* 8 / hash_len
;
182 uint16_t len_le
= GUINT16_TO_LE(output_len
* 8);
186 if (!key
|| !label
|| !context
|| !output
) {
189 if (2 + label_len
+ context_len
+ 2 > MAX_R_LEN
||
190 iterations
* hash_len
> MAX_TMP_LEN
) {
191 ws_warning("Invalid input sizes");
195 /* Fill tmp with "i || Label || Context || Length" */
196 offset
+= 2; /* Skip "i" (will be copied in for loop below) */
197 memcpy(R
+ offset
, label
, label_len
);
199 memcpy(R
+ offset
, context
, context_len
);
200 offset
+= context_len
;
201 memcpy(R
+ offset
, &len_le
, 2);
204 for (i
= 0; i
< iterations
; i
++)
206 uint16_t count_le
= GUINT16_TO_LE(i
+ 1);
207 memcpy(R
, &count_le
, 2);
209 if (ws_hmac_buffer(hash_algo
, tmp
+ hash_len
* i
, R
, offset
, key
, key_len
)) {
213 memcpy(output
, tmp
, output_len
);
217 static bool sha256(const uint8_t *data
, size_t len
, uint8_t output
[32])
220 gcry_error_t result
= gcry_md_open(&ctx
, GCRY_MD_SHA256
, 0);
226 gcry_md_write(ctx
, data
, len
);
227 digest
= gcry_md_read(ctx
, GCRY_MD_SHA256
);
231 memcpy(output
, digest
, 32);
237 * Derive PMK-R0 and PMKR0Name. See IEEE 802.11-2016 12.7.1.7.3 PMK-R0
239 * @param xxkey PSK / MPMK or certain part of MSK.
240 * @param xxkey_len Length of xxkey in bytes.
242 * @param ssid_len Length of SSID in bytes.
243 * @param mdid MDID (Mobility Domain Identifier).
244 * @param r0kh_id PMK-R0 key holder identifier in the Authenticator.
245 * @param r0kh_id_len Length of r0kh_id in bytes.
246 * @param s0kh_id PMK-R0 key holder in the Supplicant (STA mac address)
247 * @param hash_algo Hash algorithm to use for the KDF.
248 * See gcrypt available hash algorithms:
249 * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
250 * @param[out] pmk_r0 Pairwise master key, first level
251 * @param pmk_r0_len Length of pmk_r0 in bytes.
252 * @param[out] pmk_r0_name Pairwise master key (PMK) R0 name.
255 dot11decrypt_derive_pmk_r0(const uint8_t *xxkey
, size_t xxkey_len
,
256 const uint8_t *ssid
, size_t ssid_len
,
257 const uint8_t mdid
[2],
258 const uint8_t *r0kh_id
, size_t r0kh_id_len
,
259 const uint8_t s0kh_id
[DOT11DECRYPT_MAC_LEN
],
263 uint8_t pmk_r0_name
[16])
265 const char *ft_r0n
= "FT-R0N";
266 const size_t ft_r0n_len
= strlen(ft_r0n
);
267 uint8_t context
[MAX_CONTEXT_LEN
];
268 uint8_t r0_key_data
[DOT11DECRYPT_WPA_PMK_MAX_LEN
+ 16];
269 uint8_t sha256_res
[32];
271 unsigned q
= gcry_md_get_algo_dlen(hash_algo
);
273 if (!xxkey
|| !ssid
|| !mdid
|| !r0kh_id
|| !s0kh_id
||
274 !pmk_r0
|| !pmk_r0_len
|| !pmk_r0_name
)
278 if (1 + ssid_len
+ 2 + 1 + r0kh_id_len
+ DOT11DECRYPT_MAC_LEN
> MAX_CONTEXT_LEN
)
280 ws_warning("Invalid input sizes");
285 // KDF-Hash-Length(XXKey, "FT-R0",
286 // SSIDlength || SSID || MDID || R0KHlength || R0KH-ID || S0KH-ID)
287 // PMK-R0 = L(R0-Key-Data, 0, Q) * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
288 context
[offset
++] = (uint8_t)ssid_len
;
289 memcpy(context
+ offset
, ssid
, ssid_len
);
291 memcpy(context
+ offset
, mdid
, 2);
293 context
[offset
++] = (uint8_t)r0kh_id_len
;
294 memcpy(context
+ offset
, r0kh_id
, r0kh_id_len
);
295 offset
+= r0kh_id_len
;
296 memcpy(context
+ offset
, s0kh_id
, DOT11DECRYPT_MAC_LEN
);
297 offset
+= DOT11DECRYPT_MAC_LEN
;
298 dot11decrypt_kdf(xxkey
, xxkey_len
, "FT-R0", context
, offset
, hash_algo
,
299 r0_key_data
, q
+ 16);
300 memcpy(pmk_r0
, r0_key_data
, q
);
303 // PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
304 // PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt))
306 memcpy(context
+ offset
, ft_r0n
, ft_r0n_len
);
307 offset
+= ft_r0n_len
;
308 memcpy(context
+ offset
, r0_key_data
+ q
, 16);
310 if(!sha256(context
, offset
, sha256_res
))
312 memcpy(pmk_r0_name
, sha256_res
, 16);
317 * Derive PMK-R1 and PMKR1Name. See IEEE 802.11-2016 12.7.1.7.4 PMK-R1
321 dot11decrypt_derive_pmk_r1(const uint8_t *pmk_r0
, size_t pmk_r0_len
,
322 const uint8_t *pmk_r0_name
,
323 const uint8_t *r1kh_id
, const uint8_t *s1kh_id
,
325 uint8_t *pmk_r1
, size_t *pmk_r1_len
,
326 uint8_t *pmk_r1_name
)
328 const char *ft_r1n
= "FT-R1N";
329 const size_t ft_r1n_len
= strlen(ft_r1n
);
330 // context len = MAX(R1KH-ID || S1KH-ID, "FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID)
331 uint8_t context
[6 + 16 + 6 + 6];
332 uint8_t sha256_res
[32];
335 if (!pmk_r0
|| !pmk_r0_name
|| !r1kh_id
|| !s1kh_id
||
336 !pmk_r1
|| !pmk_r1_len
|| !pmk_r1_name
)
340 *pmk_r1_len
= gcry_md_get_algo_dlen(hash_algo
);
342 // PMK-R1 = KDF-Hash-Length(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID)
343 memcpy(context
+ offset
, r1kh_id
, DOT11DECRYPT_MAC_LEN
);
344 offset
+= DOT11DECRYPT_MAC_LEN
;
345 memcpy(context
+ offset
, s1kh_id
, DOT11DECRYPT_MAC_LEN
);
346 offset
+= DOT11DECRYPT_MAC_LEN
;
347 dot11decrypt_kdf(pmk_r0
, pmk_r0_len
, "FT-R1", context
, offset
, hash_algo
,
348 pmk_r1
, *pmk_r1_len
);
350 // PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID))
352 memcpy(context
+ offset
, ft_r1n
, ft_r1n_len
);
353 offset
+= ft_r1n_len
;
354 memcpy(context
+ offset
, pmk_r0_name
, 16);
356 memcpy(context
+ offset
, r1kh_id
, DOT11DECRYPT_MAC_LEN
);
357 offset
+= DOT11DECRYPT_MAC_LEN
;
358 memcpy(context
+ offset
, s1kh_id
, DOT11DECRYPT_MAC_LEN
);
359 offset
+= DOT11DECRYPT_MAC_LEN
;
360 if(!sha256(context
, offset
, sha256_res
))
362 memcpy(pmk_r1_name
, sha256_res
, 16);
367 * Derive PTK for FT AKMS. See IEE 802.11-2016 12.7.1.7.5 PTK
369 * PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR)
370 * PTKName = Truncate-128(
371 * SHA-256(PMKR1Name || "FT-PTKN" || SNonce || ANonce || BSSID || STA-ADDR))
374 dot11decrypt_derive_ft_ptk(const uint8_t *pmk_r1
, size_t pmk_r1_len
,
375 const uint8_t *pmk_r1_name _U_
,
376 const uint8_t *snonce
, const uint8_t *anonce
,
377 const uint8_t *bssid
, const uint8_t *sta_addr
,
379 uint8_t *ptk
, const size_t ptk_len
, uint8_t *ptk_name _U_
)
381 uint8_t context
[32 + 32 + 6 + 6];
384 // PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR)
385 memcpy(context
+ offset
, snonce
, 32);
387 memcpy(context
+ offset
, anonce
, 32);
389 memcpy(context
+ offset
, bssid
, DOT11DECRYPT_MAC_LEN
);
390 offset
+= DOT11DECRYPT_MAC_LEN
;
391 memcpy(context
+ offset
, sta_addr
, DOT11DECRYPT_MAC_LEN
);
392 offset
+= DOT11DECRYPT_MAC_LEN
;
393 dot11decrypt_kdf(pmk_r1
, pmk_r1_len
, "FT-PTK", context
, offset
, hash_algo
,
396 // TODO derive PTKName
406 * indent-tabs-mode: nil
409 * ex: set shiftwidth=4 tabstop=8 expandtab:
410 * :indentSize=4:tabSize=8:noTabs=true: