dcerpc-netlogon: improve NetrLogonGetCapabilities dissection
[wireshark-sm.git] / epan / crypt / dot11decrypt_util.c
blobe2eefa316cb45a29d06d5fe242362cda4aa80755
1 /* dot11decrypt_util.c
3 * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
4 * Copyright (c) 2006 CACE Technologies, Davis (California)
5 * All rights reserved.
7 * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
8 */
10 /****************************************************************************/
11 /* File includes */
12 #include "config.h"
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 /****************************************************************************/
26 /* Internal macros */
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,
37 uint8_t *aad,
38 size_t *aad_len)
40 uint8_t mgmt = (DOT11DECRYPT_TYPE(wh->fc[0]) == DOT11DECRYPT_TYPE_MANAGEMENT);
41 int alen = 22;
43 /* AAD:
44 * FC with bits 4..6 and 11..13 masked to zero; 14 is always one; 15 zero when QoS Control field present
45 * A1 | A2 | A3
46 * SC with bits 4..15 (seq#) masked to zero
47 * A4 (if present)
48 * QC (if present)
51 /* NB: aad[1] set below */
52 if (!mgmt) {
53 aad[0] = (uint8_t)(wh->fc[0] & FC0_AAD_MASK);
54 } else {
55 aad[0] = wh->fc[0];
57 if (DOT11DECRYPT_IS_QOS_DATA(wh)) {
58 aad[1] = (uint8_t)((wh->fc[1] & FC1_AAD_QOS_MASK) | 0x40);
59 } else {
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)) {
73 alen += 6;
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 */
80 aad[29] = 0;
81 alen += 2;
83 } else {
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 */
88 aad[23] = 0;
89 alen += 2;
92 *aad_len = alen;
95 /**
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
114 bool
115 dot11decrypt_prf(const uint8_t *key, size_t key_len,
116 const char *label,
117 const uint8_t *context, size_t context_len,
118 int hash_algo,
119 uint8_t *output, size_t output_len)
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);
125 size_t offset = 0;
126 uint8_t i;
128 if (!key || !label || !context || !output) {
129 return false;
131 if (label_len + 1 + context_len + 1 > MAX_R_LEN ||
132 output_len > 64) {
133 ws_warning("Invalid input or output sizes");
134 return false;
137 /* Fill R with "label || 0 || context || i" */
138 memcpy(R + offset, label, label_len);
139 offset += label_len;
140 R[offset++] = 0;
141 memcpy(R + offset, context, context_len);
142 offset += context_len;
144 for (i = 0; i <= output_len * 8 / 160; i++)
146 R[offset] = i;
147 if (ws_hmac_buffer(hash_algo, tmp + hash_len * i, R, offset + 1, key, key_len)) {
148 return false;
151 memcpy(output, tmp, output_len);
152 return true;
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
170 bool
171 dot11decrypt_kdf(const uint8_t *key, size_t key_len,
172 const char *label,
173 const uint8_t *context, size_t context_len,
174 int hash_algo,
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);
183 size_t offset = 0;
184 uint16_t i;
186 if (!key || !label || !context || !output) {
187 return false;
189 if (2 + label_len + context_len + 2 > MAX_R_LEN ||
190 iterations * hash_len > MAX_TMP_LEN) {
191 ws_warning("Invalid input sizes");
192 return false;
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);
198 offset += label_len;
199 memcpy(R + offset, context, context_len);
200 offset += context_len;
201 memcpy(R + offset, &len_le, 2);
202 offset += 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)) {
210 return false;
213 memcpy(output, tmp, output_len);
214 return true;
217 static bool sha256(const uint8_t *data, size_t len, uint8_t output[32])
219 gcry_md_hd_t ctx;
220 gcry_error_t result = gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
221 uint8_t *digest;
223 if (result) {
224 return false;
226 gcry_md_write(ctx, data, len);
227 digest = gcry_md_read(ctx, GCRY_MD_SHA256);
228 if (!digest) {
229 return false;
231 memcpy(output, digest, 32);
232 gcry_md_close(ctx);
233 return true;
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.
241 * @param ssid SSID
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.
254 bool
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],
260 int hash_algo,
261 uint8_t *pmk_r0,
262 size_t *pmk_r0_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];
270 size_t offset = 0;
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)
276 return false;
278 if (1 + ssid_len + 2 + 1 + r0kh_id_len + DOT11DECRYPT_MAC_LEN > MAX_CONTEXT_LEN)
280 ws_warning("Invalid input sizes");
281 return false;
284 // R0-Key-Data =
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);
290 offset += ssid_len;
291 memcpy(context + offset, mdid, 2);
292 offset += 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);
301 *pmk_r0_len = q;
303 // PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
304 // PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt))
305 offset = 0;
306 memcpy(context + offset, ft_r0n, ft_r0n_len);
307 offset += ft_r0n_len;
308 memcpy(context + offset, r0_key_data + q, 16);
309 offset += 16;
310 if(!sha256(context, offset, sha256_res))
311 return false;
312 memcpy(pmk_r0_name, sha256_res, 16);
313 return true;
317 * Derive PMK-R1 and PMKR1Name. See IEEE 802.11-2016 12.7.1.7.4 PMK-R1
320 bool
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,
324 int hash_algo,
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];
333 size_t offset = 0;
335 if (!pmk_r0 || !pmk_r0_name || !r1kh_id || !s1kh_id ||
336 !pmk_r1 || !pmk_r1_len || !pmk_r1_name)
338 return false;
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))
351 offset = 0;
352 memcpy(context + offset, ft_r1n, ft_r1n_len);
353 offset += ft_r1n_len;
354 memcpy(context + offset, pmk_r0_name, 16);
355 offset += 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))
361 return false;
362 memcpy(pmk_r1_name, sha256_res, 16);
363 return true;
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))
373 bool
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,
378 int hash_algo,
379 uint8_t *ptk, const size_t ptk_len, uint8_t *ptk_name _U_)
381 uint8_t context[32 + 32 + 6 + 6];
382 unsigned offset = 0;
384 // PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR)
385 memcpy(context + offset, snonce, 32);
386 offset += 32;
387 memcpy(context + offset, anonce, 32);
388 offset += 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,
394 ptk, ptk_len);
396 // TODO derive PTKName
397 return true;
401 * Editor modelines
403 * Local Variables:
404 * c-basic-offset: 4
405 * tab-width: 8
406 * indent-tabs-mode: nil
407 * End:
409 * ex: set shiftwidth=4 tabstop=8 expandtab:
410 * :indentSize=4:tabSize=8:noTabs=true: