Coverage Report

Created: 2025-12-27 06:52

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