Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-wireguard.c
Line
Count
Source
1
/* packet-wireguard.c
2
 * Routines for WireGuard dissection
3
 * Copyright 2018, Peter Wu <peter@lekensteyn.nl>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * Protocol details: https://www.wireguard.com/protocol/
14
 */
15
16
#include <config.h>
17
0
#define WS_LOG_DOMAIN "packet-wireguard"
18
19
#include <errno.h>
20
21
#include <epan/packet.h>
22
#include <epan/expert.h>
23
#include <epan/prefs.h>
24
#include <epan/proto_data.h>
25
#include <epan/conversation.h>
26
#include <epan/uat.h>
27
#include <wsutil/file_util.h>
28
#include <wsutil/filesystem.h>
29
#include <wsutil/wsgcrypt.h>
30
#include <wsutil/curve25519.h>
31
#include <wsutil/wslog.h>
32
#include <wsutil/array.h>
33
#include <epan/secrets.h>
34
#include <wiretap/secrets-types.h>
35
36
void proto_reg_handoff_wg(void);
37
void proto_register_wg(void);
38
39
static int proto_wg;
40
static int hf_wg_type;
41
static int hf_wg_reserved;
42
static int hf_wg_sender;
43
static int hf_wg_ephemeral;
44
static int hf_wg_encrypted_static;
45
static int hf_wg_static;
46
static int hf_wg_encrypted_timestamp;
47
static int hf_wg_timestamp_tai64_label;
48
static int hf_wg_timestamp_nanoseconds;
49
static int hf_wg_timestamp_value;
50
static int hf_wg_mac1;
51
static int hf_wg_mac2;
52
static int hf_wg_receiver;
53
static int hf_wg_encrypted_empty;
54
static int hf_wg_handshake_ok;
55
static int hf_wg_nonce;
56
static int hf_wg_encrypted_cookie;
57
static int hf_wg_counter;
58
static int hf_wg_encrypted_packet;
59
static int hf_wg_stream;
60
static int hf_wg_response_in;
61
static int hf_wg_response_to;
62
static int hf_wg_receiver_pubkey;
63
static int hf_wg_receiver_pubkey_known_privkey;
64
static int hf_wg_ephemeral_known_privkey;
65
static int hf_wg_static_known_pubkey;
66
static int hf_wg_static_known_privkey;
67
68
static int ett_wg;
69
static int ett_timestamp;
70
static int ett_key_info;
71
72
static expert_field ei_wg_bad_packet_length;
73
static expert_field ei_wg_keepalive;
74
static expert_field ei_wg_decryption_error;
75
static expert_field ei_wg_decryption_unsupported;
76
77
static bool     pref_dissect_packet = true;
78
static const char  *pref_keylog_file;
79
80
static dissector_handle_t ip_handle;
81
static dissector_handle_t wg_handle;
82
83
static bool wg_decryption_supported;
84
85
// Length of AEAD authentication tag
86
2
#define AUTH_TAG_LENGTH 16
87
88
typedef enum {
89
    WG_TYPE_HANDSHAKE_INITIATION = 1,
90
    WG_TYPE_HANDSHAKE_RESPONSE = 2,
91
    WG_TYPE_COOKIE_REPLY = 3,
92
    WG_TYPE_TRANSPORT_DATA = 4
93
} wg_message_type;
94
95
static const value_string wg_type_names[] = {
96
    { 0x01, "Handshake Initiation" },
97
    { 0x02, "Handshake Response" },
98
    { 0x03, "Cookie Reply" },
99
    { 0x04, "Transport Data" },
100
    { 0x00, NULL }
101
};
102
103
/* Decryption types. {{{ */
104
/*
105
 * Most operations operate on 32 byte units (keys and hash output).
106
 */
107
typedef struct {
108
0
#define WG_KEY_LEN  32
109
    unsigned char data[WG_KEY_LEN];
110
} wg_qqword;
111
112
/*
113
 * Static key with the MAC1 key pre-computed and an optional private key.
114
 */
115
typedef struct wg_skey {
116
    wg_qqword   pub_key;
117
    wg_qqword   mac1_key;
118
    wg_qqword   priv_key;   /* Optional, set to all zeroes if missing. */
119
} wg_skey_t;
120
121
/*
122
 * Pre-shared key, needed while processing the handshake response message. At
123
 * that point, ephemeral keys (from either the initiator or responder) should be
124
 * known. Thus link the PSK to such ephemeral keys.
125
 *
126
 * Usually a "wg_ekey_t" contains an empty list (if there is no PSK, i.e. an
127
 * all-zeroes PSK) or one item (if a PSK is configured). In the unlikely event
128
 * that an ephemeral key is reused, support more than one PSK.
129
 */
130
typedef struct wg_psk {
131
    wg_qqword psk_data;
132
    struct wg_psk *next;
133
} wg_psk_t;
134
135
/*
136
 * Ephemeral key.
137
 */
138
typedef struct wg_ekey {
139
    wg_qqword   pub_key;
140
    wg_qqword   priv_key;   /* Optional, set to all zeroes if missing. */
141
    wg_psk_t   *psk_list;   /* Optional, possible PSKs to try. */
142
} wg_ekey_t;
143
144
/*
145
 * Set of (long-term) static keys (for guessing the peer based on MAC1).
146
 * Maps the public key to the "wg_skey_t" structure.
147
 * Keys are populated from the UAT and key log file.
148
 */
149
static GHashTable *wg_static_keys;
150
151
/*
152
 * Set of ephemeral keys (for decryption). Maps the public key to the
153
 * "wg_ekey_t" structure. The private key MUST be available.
154
 * Keys are populated from the key log file and wmem_file_scope allocated.
155
 */
156
static wmem_map_t *wg_ephemeral_keys;
157
158
/*
159
 * Key log file handle. Opened on demand (when keys are actually looked up),
160
 * closed when the capture file closes.
161
 */
162
static FILE *wg_keylog_file;
163
164
/*
165
 * The most recently parsed ephemeral key. If a PSK is configured, the key log
166
 * file must have a PSK line after other keys. If not, then it is assumed that
167
 * the session does not use a PSK.
168
 *
169
 * This pointer is cleared when the key log file is reset (i.e. when the capture
170
 * file closes).
171
 */
172
static wg_ekey_t *wg_keylog_last_ekey;
173
174
enum wg_psk_iter_state {
175
    WG_PSK_ITER_STATE_ENTER = 0,
176
    WG_PSK_ITER_STATE_INITIATOR,
177
    WG_PSK_ITER_STATE_RESPONDER,
178
    WG_PSK_ITER_STATE_EXIT
179
};
180
181
/* See wg_psk_iter_next. */
182
typedef struct {
183
    enum wg_psk_iter_state state;
184
    wg_psk_t               *next_psk;
185
} wg_psk_iter_context;
186
187
/* UAT adapter for populating wg_static_keys. */
188
enum { WG_KEY_UAT_PUBLIC, WG_KEY_UAT_PRIVATE };
189
static const value_string wg_key_uat_type_vals[] = {
190
    { WG_KEY_UAT_PUBLIC, "Public" },
191
    { WG_KEY_UAT_PRIVATE, "Private" },
192
    { 0, NULL }
193
};
194
195
typedef struct {
196
    unsigned   key_type;   /* See "wg_key_uat_type_vals". */
197
    char   *key;
198
} wg_key_uat_record_t;
199
200
static wg_key_uat_record_t *wg_key_records;
201
static unsigned num_wg_key_records;
202
203
/*
204
 * Input keying material for key derivation/decryption during the handshake.
205
 * For the Initiation message, Spub_r and either Spriv_r or Epriv_i must be set.
206
 * For the Response message, Epriv_r + Spriv_r or Epriv_r + Epub_i.
207
 *
208
 * The static and ephemeral keys are reset upon UAT changes or are invalidated
209
 * when the capture file closes.
210
 */
211
typedef struct {
212
    const wg_skey_t    *initiator_skey;     /* Spub_i based on Initiation.static (decrypted, null if decryption failed) */
213
    const wg_skey_t    *responder_skey;     /* Spub_r based on Initiation.MAC1 (+Spriv_r if available) */
214
    uint8_t             timestamp[12];      /* Initiation.timestamp (decrypted) */
215
    bool                timestamp_ok : 1;   /* Whether the timestamp was successfully decrypted */
216
    bool                empty_ok : 1;       /* Whether the empty field was successfully decrypted */
217
218
    /* The following fields are only valid on the initial pass. */
219
    const wg_ekey_t    *initiator_ekey;     /* Epub_i matching Initiation.Ephemeral (+Epriv_i if available) */
220
    const wg_ekey_t    *responder_ekey;     /* Epub_r matching Response.Ephemeral (+Epriv_r if available) */
221
    wg_qqword           handshake_hash;     /* Handshake hash H_i */
222
    wg_qqword           chaining_key;       /* Chaining key C_i */
223
224
    /* Transport ciphers. */
225
    gcry_cipher_hd_t    initiator_recv_cipher;
226
    gcry_cipher_hd_t    responder_recv_cipher;
227
} wg_handshake_state_t;
228
229
/** Hash(CONSTRUCTION), initialized by wg_decrypt_init. */
230
static wg_qqword hash_of_construction;
231
/** Hash(Hash(CONSTRUCTION) || IDENTIFIER), initialized by wg_decrypt_init. */
232
static wg_qqword hash_of_c_identifier;
233
/* Decryption types. }}} */
234
235
/*
236
 * Information required to process and link messages as required on the first
237
 * sequential pass. After that it can be erased.
238
 */
239
typedef struct {
240
    address     initiator_address;
241
    address     responder_address;
242
    uint16_t    initiator_port;
243
    uint16_t    responder_port;
244
} wg_initial_info_t;
245
246
/*
247
 * A "session" between two peer is identified by a "sender" id as independently
248
 * chosen by each side. In case both peer IDs collide, the source IP and UDP
249
 * port number could be used to distinguish sessions. As IDs can be recycled
250
 * over time, lookups should use the most recent initiation (or response).
251
 *
252
 * XXX record timestamps (time since last message, for validating timers).
253
 */
254
typedef struct {
255
    uint32_t    stream;             /* Session identifier (akin to udp.stream). */
256
    uint32_t    initiator_frame;
257
    uint32_t    response_frame;     /* Responder or Cookie Reply message. */
258
    wg_initial_info_t initial;      /* Valid only on the first pass. */
259
    wg_handshake_state_t *hs;       /* Handshake state to enable decryption. */
260
} wg_session_t;
261
262
/* Per-packet state. */
263
typedef struct {
264
    wg_session_t   *session;
265
    bool            receiver_is_initiator;  /* Whether this transport data packet is sent to an Initiator. */
266
} wg_packet_info_t;
267
268
/* Map from Sender/Receiver IDs to a list of session information. */
269
static wmem_map_t *sessions;
270
static uint32_t wg_session_count;
271
272
273
/* Key conversion routines. {{{ */
274
/* Import external random data as private key. */
275
static void
276
set_private_key(wg_qqword *privkey, const wg_qqword *inkey)
277
0
{
278
    // The 254th bit of a Curve25519 secret will always be set in calculations,
279
    // use this property to recognize whether a private key is set.
280
0
    *privkey = *inkey;
281
0
    privkey->data[31] |= 64;
282
0
}
283
284
/* Whether a private key is initialized (see set_private_key). */
285
static inline bool
286
has_private_key(const wg_qqword *secret)
287
0
{
288
0
    return !!(secret->data[31] & 64);
289
0
}
290
291
/**
292
 * Compute the Curve25519 public key from a private key.
293
 */
294
static void
295
priv_to_pub(wg_qqword *pub, const wg_qqword *priv)
296
0
{
297
0
    int r = crypto_scalarmult_curve25519_base(pub->data, priv->data);
298
    /* The computation should always be possible. */
299
0
    DISSECTOR_ASSERT(r == 0);
300
0
}
301
302
static void
303
dh_x25519(wg_qqword *shared_secret, const wg_qqword *priv, const wg_qqword *pub)
304
0
{
305
    /*
306
     * If the point ("pub") is of small order, of if the result is all zeros, -1
307
     * could be returned with Sodium. We are just interpreting the trace, so
308
     * just ignore the condition for now.
309
     */
310
0
    (void)crypto_scalarmult_curve25519(shared_secret->data, priv->data, pub->data);
311
0
}
312
313
/*
314
 * Returns the string representation (base64) of a public key.
315
 * The returned value is allocated with wmem_allocator scope.
316
 */
317
static const char *
318
pubkey_to_string(wmem_allocator_t* allocator, const wg_qqword *pubkey)
319
0
{
320
0
    char *str = g_base64_encode(pubkey->data, WG_KEY_LEN);
321
0
    char *ret = wmem_strdup(allocator, str);
322
0
    g_free(str);
323
0
    return ret;
324
0
}
325
326
static bool
327
decode_base64_key(wg_qqword *out, const char *str)
328
0
{
329
0
    size_t out_len;
330
0
    char tmp[45];
331
332
0
    if (strlen(str) + 1 != sizeof(tmp)) {
333
0
        return false;
334
0
    }
335
0
    memcpy(tmp, str, sizeof(tmp));
336
0
    g_base64_decode_inplace(tmp, &out_len);
337
0
    if (out_len != WG_KEY_LEN) {
338
0
        return false;
339
0
    }
340
0
    memcpy(out->data, tmp, WG_KEY_LEN);
341
0
    return true;
342
0
}
343
/* Key conversion routines. }}} */
344
345
static uint32_t
346
wg_pubkey_hash(const void *v)
347
0
{
348
0
    const wg_qqword *pubkey = (const wg_qqword *)v;
349
0
    return wmem_strong_hash(pubkey->data, WG_KEY_LEN);
350
0
}
351
352
static gboolean
353
wg_pubkey_equal(const void *v1, const void *v2)
354
0
{
355
0
    const wg_qqword *pubkey1 = (const wg_qqword *)v1;
356
0
    const wg_qqword *pubkey2 = (const wg_qqword *)v2;
357
0
    return !memcmp(pubkey1->data, pubkey2->data, WG_KEY_LEN);
358
0
}
359
360
361
/* Protocol-specific crypto routines. {{{ */
362
/**
363
 * Computes MAC1. Caller must ensure that GCRY_MD_BLAKE2S_256 is available.
364
 */
365
static void
366
wg_mac1_key(const wg_qqword *static_public, wg_qqword *mac_key_out)
367
0
{
368
0
    gcry_md_hd_t hd;
369
0
    if (gcry_md_open(&hd, GCRY_MD_BLAKE2S_256, 0) == 0) {
370
0
        static const char wg_label_mac1[] = "mac1----";
371
0
        gcry_md_write(hd, wg_label_mac1, strlen(wg_label_mac1));
372
0
        gcry_md_write(hd, static_public->data, sizeof(wg_qqword));
373
0
        memcpy(mac_key_out->data, gcry_md_read(hd, 0), sizeof(wg_qqword));
374
0
        gcry_md_close(hd);
375
0
        return;
376
0
    }
377
    // caller should have checked this.
378
0
    DISSECTOR_ASSERT_NOT_REACHED();
379
0
}
380
381
/*
382
 * Verify that MAC(mac_key, data) matches "mac_output".
383
 */
384
static bool
385
wg_mac_verify(const wg_qqword *mac_key,
386
              const unsigned char *data, unsigned data_len, const uint8_t mac_output[16])
387
0
{
388
0
    bool ok = false;
389
0
    gcry_md_hd_t hd;
390
0
    if (gcry_md_open(&hd, GCRY_MD_BLAKE2S_128, 0) == 0) {
391
0
        gcry_error_t r;
392
        // not documented by Libgcrypt, but required for keyed blake2s
393
0
        r = gcry_md_setkey(hd, mac_key->data, WG_KEY_LEN);
394
0
        DISSECTOR_ASSERT(r == 0);
395
0
        gcry_md_write(hd, data, data_len);
396
0
        ok = memcmp(mac_output, gcry_md_read(hd, 0), 16) == 0;
397
0
        gcry_md_close(hd);
398
0
    } else {
399
        // caller should have checked this.
400
0
        DISSECTOR_ASSERT_NOT_REACHED();
401
0
    }
402
0
    return ok;
403
0
}
404
405
/**
406
 * Update the new chained hash value: h = Hash(h || data).
407
 */
408
static void
409
wg_mix_hash(wg_qqword *h, const void *data, size_t data_len)
410
16
{
411
16
    gcry_md_hd_t hd;
412
16
    if (gcry_md_open(&hd, GCRY_MD_BLAKE2S_256, 0)) {
413
0
        DISSECTOR_ASSERT_NOT_REACHED();
414
0
    }
415
16
    gcry_md_write(hd, h->data, sizeof(wg_qqword));
416
16
    gcry_md_write(hd, data, data_len);
417
16
    memcpy(h, gcry_md_read(hd, 0), sizeof(wg_qqword));
418
16
    gcry_md_close(hd);
419
16
}
420
421
/**
422
 * Computes KDF_n(key, input) where n is the number of derived keys.
423
 */
424
static void
425
wg_kdf(const wg_qqword *key, const uint8_t *input, unsigned input_len, unsigned n, wg_qqword *out)
426
0
{
427
0
    uint8_t         prk[32];    /* Blake2s_256 hash output. */
428
0
    gcry_error_t    err;
429
0
    err = hkdf_extract(GCRY_MD_BLAKE2S_256, key->data, sizeof(wg_qqword), input, input_len, prk);
430
0
    DISSECTOR_ASSERT(err == 0);
431
0
    err = hkdf_expand(GCRY_MD_BLAKE2S_256, prk, sizeof(prk), NULL, 0, out->data, 32 * n);
432
0
    DISSECTOR_ASSERT(err == 0);
433
0
}
434
435
/*
436
 * Must be called before attempting decryption.
437
 */
438
static bool
439
wg_decrypt_init(void)
440
16
{
441
16
    if (gcry_md_test_algo(GCRY_MD_BLAKE2S_128) != 0 ||
442
16
        gcry_md_test_algo(GCRY_MD_BLAKE2S_256) != 0 ||
443
16
        gcry_cipher_test_algo(GCRY_CIPHER_CHACHA20) != 0) {
444
0
        return false;
445
0
    }
446
16
    static const char construction[] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
447
16
    gcry_md_hash_buffer(GCRY_MD_BLAKE2S_256, hash_of_construction.data, construction, strlen(construction));
448
449
16
    static const char wg_identifier[] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
450
16
    memcpy(&hash_of_c_identifier, hash_of_construction.data, sizeof(wg_qqword));
451
16
    wg_mix_hash(&hash_of_c_identifier, wg_identifier, strlen(wg_identifier));
452
16
    return true;
453
16
}
454
455
static gcry_cipher_hd_t
456
wg_create_cipher(const wg_qqword *key)
457
0
{
458
0
    gcry_cipher_hd_t    hd;
459
0
    if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) {
460
0
        return NULL;
461
0
    }
462
463
0
    if (gcry_cipher_setkey(hd, key->data, sizeof(*key))) {
464
0
        gcry_cipher_close(hd);
465
0
        hd = NULL;
466
0
    }
467
0
    return hd;
468
0
}
469
470
static bool
471
wg_handshake_state_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, void *user_data)
472
0
{
473
0
    wg_handshake_state_t *hs = (wg_handshake_state_t *)user_data;
474
475
0
    if (hs->initiator_recv_cipher) {
476
0
        gcry_cipher_close(hs->initiator_recv_cipher);
477
0
        hs->initiator_recv_cipher = NULL;
478
0
    }
479
0
    if (hs->responder_recv_cipher) {
480
0
        gcry_cipher_close(hs->responder_recv_cipher);
481
0
        hs->responder_recv_cipher = NULL;
482
0
    }
483
0
    return false;
484
0
}
485
486
/*
487
 * Decrypt ciphertext using the ChaCha20-Poly1305 cipher. The auth tag must be
488
 * included with the ciphertext.
489
 */
490
static bool
491
wg_aead_decrypt(gcry_cipher_hd_t hd, uint64_t counter, const unsigned char *ctext, unsigned ctext_len, const unsigned char *aad, unsigned aad_len, unsigned char *out, unsigned out_len)
492
0
{
493
0
    DISSECTOR_ASSERT(ctext_len >= AUTH_TAG_LENGTH);
494
0
    ctext_len -= AUTH_TAG_LENGTH;
495
0
    const unsigned char *auth_tag = ctext + ctext_len;
496
497
0
    counter = GUINT64_TO_LE(counter);
498
0
    unsigned char nonce[12] = { 0 };
499
0
    memcpy(nonce + 4, &counter, 8);
500
501
0
    return gcry_cipher_setiv(hd, nonce, sizeof(nonce)) == 0 &&
502
0
        gcry_cipher_authenticate(hd, aad, aad_len) == 0 &&
503
0
        gcry_cipher_decrypt(hd, out, out_len, ctext, ctext_len) == 0 &&
504
0
        gcry_cipher_checktag(hd, auth_tag, AUTH_TAG_LENGTH) == 0;
505
0
}
506
507
/**
508
 * Decrypt ciphertext using the ChaCha20-Poly1305 cipher. The auth tag must be
509
 * included with the ciphertext.
510
 */
511
static bool
512
aead_decrypt(const wg_qqword *key, uint64_t counter, const unsigned char *ctext, unsigned ctext_len, const unsigned char *aad, unsigned aad_len, unsigned char *out, unsigned out_len)
513
0
{
514
0
    DISSECTOR_ASSERT(ctext_len >= AUTH_TAG_LENGTH);
515
516
0
    gcry_cipher_hd_t hd = wg_create_cipher(key);
517
0
    DISSECTOR_ASSERT(hd);
518
0
    bool ok = wg_aead_decrypt(hd, counter, ctext, ctext_len, aad, aad_len, out, out_len);
519
0
    gcry_cipher_close(hd);
520
0
    return ok;
521
0
}
522
/* Protocol-specific crypto routines. }}} */
523
524
/*
525
 * Add a static public or private key to "wg_static_keys".
526
 */
527
static void
528
wg_add_static_key(const wg_qqword *tmp_key, bool is_private)
529
0
{
530
0
    if (!wg_decryption_supported) {
531
0
        return;
532
0
    }
533
534
0
    wg_skey_t *key = g_new0(wg_skey_t, 1);
535
0
    if (is_private) {
536
0
        set_private_key(&key->priv_key, tmp_key);
537
0
        priv_to_pub(&key->pub_key, tmp_key);
538
0
    } else {
539
0
        key->pub_key = *tmp_key;
540
0
    }
541
542
    // If a previous pubkey exists, skip adding the new key. Do add the
543
    // secret if it has become known in meantime.
544
0
    wg_skey_t *oldkey = (wg_skey_t *)g_hash_table_lookup(wg_static_keys, &key->pub_key);
545
0
    if (oldkey) {
546
0
        if (!has_private_key(&oldkey->priv_key) && is_private) {
547
0
            oldkey->priv_key = key->priv_key;
548
0
        }
549
0
        g_free(key);
550
0
        return;
551
0
    }
552
553
    // New key, precompute the MAC1 label.
554
0
    wg_mac1_key(&key->pub_key, &key->mac1_key);
555
556
0
    g_hash_table_insert(wg_static_keys, &key->pub_key, key);
557
0
}
558
559
/**
560
 * Stores the given ephemeral private key.
561
 */
562
static wg_ekey_t *
563
wg_add_ephemeral_privkey(const wg_qqword *priv_key)
564
0
{
565
0
    if (!wg_decryption_supported) {
566
0
        return NULL;
567
0
    }
568
569
0
    wg_qqword pub_key;
570
0
    priv_to_pub(&pub_key, priv_key);
571
0
    wg_ekey_t *key = (wg_ekey_t *)wmem_map_lookup(wg_ephemeral_keys, &pub_key);
572
0
    if (!key) {
573
0
        key = wmem_new0(wmem_file_scope(), wg_ekey_t);
574
0
        key->pub_key = pub_key;
575
0
        set_private_key(&key->priv_key, priv_key);
576
0
        wmem_map_insert(wg_ephemeral_keys, &key->pub_key, key);
577
0
    }
578
0
    return key;
579
0
}
580
581
/* PSK handling. {{{ */
582
static void
583
wg_add_psk(wg_ekey_t *ekey, const wg_qqword *psk)
584
0
{
585
0
    wg_psk_t *psk_entry = wmem_new0(wmem_file_scope(), wg_psk_t);
586
0
    psk_entry->psk_data = *psk;
587
0
    psk_entry->next = ekey->psk_list;
588
0
    ekey->psk_list = psk_entry;
589
0
}
590
591
/*
592
 * Retrieves the next PSK to try and returns true if one is found or false if
593
 * there are no more to try.
594
 */
595
static bool
596
wg_psk_iter_next(wg_psk_iter_context *psk_iter, const wg_handshake_state_t *hs,
597
                 wg_qqword *psk_out)
598
0
{
599
0
    wg_psk_t *psk = psk_iter->next_psk;
600
0
    while (!psk) {
601
        /*
602
         * Yield PSKs based on Epub_i, then those based on Epub_r, then yield an
603
         * all-zeroes key and finally fail in the terminating state.
604
         */
605
0
        switch (psk_iter->state) {
606
0
            case WG_PSK_ITER_STATE_ENTER:
607
0
                psk = hs->initiator_ekey->psk_list;
608
0
                psk_iter->state = WG_PSK_ITER_STATE_INITIATOR;
609
0
                break;
610
0
            case WG_PSK_ITER_STATE_INITIATOR:
611
0
                psk = hs->responder_ekey->psk_list;
612
0
                psk_iter->state = WG_PSK_ITER_STATE_RESPONDER;
613
0
                break;
614
0
            case WG_PSK_ITER_STATE_RESPONDER:
615
0
                memset(psk_out->data, 0, WG_KEY_LEN);
616
0
                psk_iter->state = WG_PSK_ITER_STATE_EXIT;
617
0
                return true;
618
0
            case WG_PSK_ITER_STATE_EXIT:
619
0
                return false;
620
0
        }
621
0
    }
622
623
0
    *psk_out = psk->psk_data;
624
0
    psk_iter->next_psk = psk->next;
625
0
    return true;
626
0
}
627
/* PSK handling. }}} */
628
629
/* UAT and key configuration. {{{ */
630
631
static void
632
wg_keylog_reset(void)
633
16
{
634
16
    if (wg_keylog_file) {
635
0
        fclose(wg_keylog_file);
636
0
        wg_keylog_file = NULL;
637
0
        wg_keylog_last_ekey = NULL;
638
0
    }
639
16
}
640
641
static void wg_keylog_process_lines(const void *data, unsigned datalen);
642
643
static void
644
wg_keylog_read(void)
645
2
{
646
2
    if (!wg_decryption_supported) {
647
0
        return;
648
0
    }
649
650
2
    if (!pref_keylog_file || !*pref_keylog_file) {
651
2
        return;
652
2
    }
653
654
    // Reopen file if it got deleted/overwritten.
655
0
    if (wg_keylog_file && file_needs_reopen(ws_fileno(wg_keylog_file), pref_keylog_file)) {
656
0
        ws_debug("Key log file got changed or deleted, trying to re-open.");
657
0
        wg_keylog_reset();
658
0
    }
659
660
0
    if (!wg_keylog_file) {
661
0
        wg_keylog_file = ws_fopen(pref_keylog_file, "r");
662
0
        if (!wg_keylog_file) {
663
0
            ws_debug("Failed to open key log file %s: %s", pref_keylog_file, g_strerror(errno));
664
0
            return;
665
0
        }
666
0
        ws_debug("Opened key log file %s", pref_keylog_file);
667
0
    }
668
669
    /* File format: each line follows the format "<type>=<key>" (leading spaces
670
     * and spaces around '=' as produced by extract-handshakes.sh are ignored).
671
     * For available <type>s, see below. <key> is the base64-encoded key (44
672
     * characters).
673
     *
674
     * Example:
675
     *  LOCAL_STATIC_PRIVATE_KEY = AKeZaHwBxjiKLFnkY2unvEdOTtg4AL+M9dQXfopFVFk=
676
     *  REMOTE_STATIC_PUBLIC_KEY = YDCttCs9e1J52/g9vEnwJJa+2x6RqaayAYMpSVQfGEY=
677
     *  LOCAL_EPHEMERAL_PRIVATE_KEY = sLGLJSOQfyz7JNJ5ZDzFf3Uz1rkiCMMjbWerNYcPFFU=
678
     *  PRESHARED_KEY = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
679
     */
680
681
0
    for (;;) {
682
0
        char buf[512];
683
0
        if (!fgets(buf, sizeof(buf), wg_keylog_file)) {
684
0
            if (feof(wg_keylog_file)) {
685
0
                clearerr(wg_keylog_file);
686
0
            } else if (ferror(wg_keylog_file)) {
687
0
                ws_debug("Error while reading %s, closing it.", pref_keylog_file);
688
0
                wg_keylog_reset();
689
0
            }
690
0
            break;
691
0
        }
692
693
0
        wg_keylog_process_lines((const uint8_t *)buf, (unsigned)strlen(buf));
694
0
    }
695
0
}
696
697
static void
698
wg_keylog_process_lines(const void *data, unsigned datalen)
699
0
{
700
0
    const char *next_line = (const char *)data;
701
0
    const char *line_end = next_line + datalen;
702
0
    while (next_line && next_line < line_end) {
703
        /* Note: line is NOT nul-terminated. */
704
0
        const char *line = next_line;
705
0
        next_line = (const char *)memchr(line, '\n', line_end - line);
706
0
        ssize_t linelen;
707
708
0
        if (next_line) {
709
0
            linelen = next_line - line;
710
0
            next_line++;    /* drop LF */
711
0
        } else {
712
0
            linelen = (ssize_t)(line_end - line);
713
0
        }
714
0
        if (linelen > 0 && line[linelen - 1] == '\r') {
715
0
            linelen--;      /* drop CR */
716
0
        }
717
718
0
        ws_debug("Read WG key log line: %.*s", (int)linelen, line);
719
720
        /* Strip leading spaces. */
721
0
        const char *p = line;
722
0
        while (p < line_end && *p == ' ') {
723
0
            ++p;
724
0
        }
725
0
        char key_type[sizeof("LOCAL_EPHEMERAL_PRIVATE_KEY")];
726
0
        char key_value[45] = { 0 };
727
0
        const char *p0 = p;
728
0
        p = (const char *)memchr(p0, '=', line_end - p);
729
0
        if (p && p0 != p) {
730
            /* Extract "key-type" from "key-type = key-value" */
731
0
            size_t key_type_len = p - p0;
732
0
            while (key_type_len && p0[key_type_len - 1] == ' ') {
733
0
                --key_type_len;
734
0
            }
735
0
            if (key_type_len && key_type_len < sizeof(key_type)) {
736
0
                memcpy(key_type, p0, key_type_len);
737
0
                key_type[key_type_len] = '\0';
738
739
                /* Skip '=' and any spaces. */
740
0
                p = p + 1;
741
0
                while (p < line_end && *p == ' ') {
742
0
                    ++p;
743
0
                }
744
0
                size_t key_value_len = (line + linelen) - p;
745
0
                if (key_value_len && key_value_len < sizeof(key_value)) {
746
0
                    memcpy(key_value, p, key_value_len);
747
0
                }
748
0
            }
749
0
        }
750
751
0
        wg_qqword key;
752
0
        if (!key_value[0] || !decode_base64_key(&key, key_value)) {
753
0
            ws_debug("Unrecognized key log line: %.*s", (int)linelen, line);
754
0
            continue;
755
0
        }
756
757
0
        if (!strcmp(key_type, "LOCAL_STATIC_PRIVATE_KEY")) {
758
0
            wg_add_static_key(&key, true);
759
0
        } else if (!strcmp(key_type, "REMOTE_STATIC_PUBLIC_KEY")) {
760
0
            wg_add_static_key(&key, false);
761
0
        } else if (!strcmp(key_type, "LOCAL_EPHEMERAL_PRIVATE_KEY")) {
762
0
            wg_keylog_last_ekey = wg_add_ephemeral_privkey(&key);
763
0
        } else if (!strcmp(key_type, "PRESHARED_KEY")) {
764
            /* Link the PSK to the last ephemeral key. */
765
0
            if (wg_keylog_last_ekey) {
766
0
                wg_add_psk(wg_keylog_last_ekey, &key);
767
0
                wg_keylog_last_ekey = NULL;
768
0
            } else {
769
0
                ws_debug("Ignored PSK as no new ephemeral key was found");
770
0
            }
771
0
        } else {
772
0
            ws_debug("Unrecognized key log line: %.*s", (int)linelen, line);
773
0
        }
774
0
    }
775
0
}
776
777
static void*
778
wg_key_uat_record_copy_cb(void *dest, const void *source, size_t len _U_)
779
0
{
780
0
    const wg_key_uat_record_t* o = (const wg_key_uat_record_t*)source;
781
0
    wg_key_uat_record_t* d = (wg_key_uat_record_t*)dest;
782
783
0
    d->key_type = o->key_type;
784
0
    d->key = g_strdup(o->key);
785
786
0
    return dest;
787
0
}
788
789
static bool
790
wg_key_uat_record_update_cb(void *r, char **error)
791
0
{
792
0
    wg_key_uat_record_t *rec = (wg_key_uat_record_t *)r;
793
0
    wg_qqword key;
794
795
    /* Check for valid base64-encoding. */
796
0
    if (!decode_base64_key(&key, rec->key)) {
797
0
        *error = g_strdup("Invalid key");
798
0
        return false;
799
0
    }
800
801
0
    return true;
802
0
}
803
804
static void
805
wg_key_uat_record_free_cb(void *r)
806
0
{
807
0
    wg_key_uat_record_t *rec = (wg_key_uat_record_t *)r;
808
0
    g_free(rec->key);
809
0
}
810
811
static void
812
wg_key_uat_apply(void)
813
16
{
814
16
    if (!wg_decryption_supported) {
815
0
        return;
816
0
    }
817
818
16
    if (!wg_static_keys) {
819
16
        wg_static_keys = g_hash_table_new_full(wg_pubkey_hash, wg_pubkey_equal, NULL, g_free);
820
16
    } else {
821
0
        g_hash_table_remove_all(wg_static_keys);
822
0
    }
823
824
    // As static keys from the key log file also end up in "wg_static_keys",
825
    // reset the file pointer such that it will be fully read later.
826
16
    wg_keylog_reset();
827
828
    /* Convert base64-encoded strings to wg_skey_t and derive pubkey. */
829
16
    for (unsigned i = 0; i < num_wg_key_records; i++) {
830
0
        wg_key_uat_record_t *rec = &wg_key_records[i];
831
0
        wg_qqword tmp_key;  /* Either public or private, not sure yet. */
832
833
        /* Populate public (and private) keys. */
834
0
        bool decoded = decode_base64_key(&tmp_key, rec->key);
835
0
        DISSECTOR_ASSERT(decoded);
836
0
        wg_add_static_key(&tmp_key, rec->key_type == WG_KEY_UAT_PRIVATE);
837
0
    }
838
16
}
839
840
static void
841
wg_key_uat_reset(void)
842
0
{
843
    /* Erase keys when the UAT is unloaded. */
844
0
    if (wg_static_keys != NULL) {
845
0
        g_hash_table_destroy(wg_static_keys);
846
0
        wg_static_keys = NULL;
847
0
    }
848
0
}
849
850
0
UAT_VS_DEF(wg_key_uat, key_type, wg_key_uat_record_t, unsigned, WG_KEY_UAT_PUBLIC, "Public")
Unexecuted instantiation: packet-wireguard.c:wg_key_uat_key_type_set_cb
Unexecuted instantiation: packet-wireguard.c:wg_key_uat_key_type_tostr_cb
851
0
UAT_CSTRING_CB_DEF(wg_key_uat, key, wg_key_uat_record_t)
852
/* UAT and key configuration. }}} */
853
854
/**
855
 * Tries to decrypt the initiation message.
856
 * Assumes responder_skey and initiator_ekey to be set.
857
 */
858
static void
859
wg_process_initiation(tvbuff_t *tvb, wg_handshake_state_t *hs)
860
0
{
861
0
    DISSECTOR_ASSERT(hs->responder_skey);
862
0
    DISSECTOR_ASSERT(hs->initiator_ekey);
863
0
    DISSECTOR_ASSERT(hs->initiator_skey == NULL);
864
865
0
    wg_qqword decrypted_static = {{ 0 }};
866
0
    const bool has_Spriv_r = has_private_key(&hs->responder_skey->priv_key);
867
0
    const bool has_Epriv_i = has_private_key(&hs->initiator_ekey->priv_key);
868
869
    // Either Spriv_r or Epriv_i + Spriv_i are needed. If the first two are not
870
    // available, fail early. Spriv_i will be looked up later.
871
0
    if (!has_Spriv_r && !has_Epriv_i) {
872
0
        return;
873
0
    }
874
875
0
    const wg_qqword *ephemeral = (const wg_qqword *)tvb_get_ptr(tvb, 8, WG_KEY_LEN);
876
0
#define WG_ENCRYPTED_STATIC_LENGTH      (32 + AUTH_TAG_LENGTH)
877
0
    const uint8_t *encrypted_static = (const uint8_t *)tvb_get_ptr(tvb, 40, WG_ENCRYPTED_STATIC_LENGTH);
878
0
#define WG_ENCRYPTED_TIMESTAMP_LENGTH   (12 + AUTH_TAG_LENGTH)
879
0
    const uint8_t *encrypted_timestamp = (const uint8_t *)tvb_get_ptr(tvb, 88, WG_ENCRYPTED_TIMESTAMP_LENGTH);
880
881
0
    wg_qqword c_and_k[2], h;
882
0
    wg_qqword *c = &c_and_k[0], *k = &c_and_k[1];
883
    // c = Hash(CONSTRUCTION)
884
0
    memcpy(c->data, hash_of_construction.data, sizeof(wg_qqword));
885
    // h = Hash(c || IDENTIFIER)
886
0
    memcpy(h.data, hash_of_c_identifier.data, sizeof(wg_qqword));
887
    // h = Hash(h || Spub_r)
888
0
    wg_mix_hash(&h, hs->responder_skey->pub_key.data, sizeof(wg_qqword));
889
    // c = KDF1(c, msg.ephemeral)
890
0
    wg_kdf(c, ephemeral->data, WG_KEY_LEN, 1, c);
891
    // h = Hash(h || msg.ephemeral)
892
0
    wg_mix_hash(&h, ephemeral, WG_KEY_LEN);
893
    //  dh1 = DH(Spriv_r, msg.ephemeral)    if kType = R
894
    //  dh1 = DH(Epriv_i, Spub_r)           if kType = I
895
0
    wg_qqword dh1 = {{ 0 }};
896
0
    if (has_Spriv_r) {
897
0
        dh_x25519(&dh1, &hs->responder_skey->priv_key, ephemeral);
898
0
    } else {
899
0
        dh_x25519(&dh1, &hs->initiator_ekey->priv_key, &hs->responder_skey->pub_key);
900
0
    }
901
    // (c, k) = KDF2(c, dh1)
902
0
    wg_kdf(c, dh1.data, sizeof(dh1), 2, c_and_k);
903
    // Spub_i = AEAD-Decrypt(k, 0, msg.static, h)
904
0
    if (!aead_decrypt(k, 0, encrypted_static, WG_ENCRYPTED_STATIC_LENGTH, h.data, sizeof(wg_qqword), decrypted_static.data, sizeof(decrypted_static))) {
905
0
        return;
906
0
    }
907
    // Save static public key to the context and lookup private key if possible.
908
0
    wg_skey_t *skey_i = (wg_skey_t *)g_hash_table_lookup(wg_static_keys, &decrypted_static);
909
0
    if (!skey_i) {
910
0
        skey_i = wmem_new0(wmem_file_scope(), wg_skey_t);
911
0
        skey_i->pub_key = decrypted_static;
912
0
    }
913
0
    hs->initiator_skey = skey_i;
914
    // If Spriv_r is not available, then Epriv_i + Spriv_i must be available.
915
0
    if (!has_Spriv_r && !has_private_key(&hs->initiator_skey->priv_key)) {
916
0
        return;
917
0
    }
918
919
    // h = Hash(h || msg.static)
920
0
    wg_mix_hash(&h, encrypted_static, WG_ENCRYPTED_STATIC_LENGTH);
921
    //  dh2 = DH(Spriv_r, Spub_i)           if kType = R
922
    //  dh2 = DH(Spriv_i, Spub_r)           if kType = I
923
0
    wg_qqword dh2 = {{ 0 }};
924
0
    if (has_Spriv_r) {
925
0
        dh_x25519(&dh2, &hs->responder_skey->priv_key, &hs->initiator_skey->pub_key);
926
0
    } else {
927
0
        dh_x25519(&dh2, &hs->initiator_skey->priv_key, &hs->responder_skey->pub_key);
928
0
    }
929
    // (c, k) = KDF2(c, dh2)
930
0
    wg_kdf(c, dh2.data, sizeof(wg_qqword), 2, c_and_k);
931
    // timestamp = AEAD-Decrypt(k, 0, msg.timestamp, h)
932
0
    if (!aead_decrypt(k, 0, encrypted_timestamp, WG_ENCRYPTED_TIMESTAMP_LENGTH, h.data, sizeof(wg_qqword), hs->timestamp, sizeof(hs->timestamp))) {
933
0
        return;
934
0
    }
935
0
    hs->timestamp_ok = true;
936
    // h = Hash(h || msg.timestamp)
937
0
    wg_mix_hash(&h, encrypted_timestamp, WG_ENCRYPTED_TIMESTAMP_LENGTH);
938
939
    // save (h, k) context for responder message processing
940
0
    hs->handshake_hash = h;
941
0
    hs->chaining_key = *c;
942
0
}
943
944
static void
945
wg_process_response(tvbuff_t *tvb, wg_handshake_state_t *hs)
946
0
{
947
0
    DISSECTOR_ASSERT(hs->initiator_ekey);
948
0
    DISSECTOR_ASSERT(hs->initiator_skey);
949
0
    DISSECTOR_ASSERT(hs->responder_ekey);
950
0
    DISSECTOR_ASSERT(hs->responder_skey);
951
    // XXX when multiple responses are linkable to a single handshake state,
952
    // they should probably fork into a new state or be discarded when equal.
953
0
    if (hs->initiator_recv_cipher || hs->responder_recv_cipher) {
954
0
        ws_warning("FIXME multiple responses linked to a single session");
955
0
        return;
956
0
    }
957
0
    DISSECTOR_ASSERT(!hs->initiator_recv_cipher);
958
0
    DISSECTOR_ASSERT(!hs->responder_recv_cipher);
959
960
0
    const bool has_Epriv_i = has_private_key(&hs->initiator_ekey->priv_key);
961
0
    const bool has_Spriv_i = has_private_key(&hs->initiator_skey->priv_key);
962
0
    const bool has_Epriv_r = has_private_key(&hs->responder_ekey->priv_key);
963
964
    // Either Epriv_i + Spriv_i or Epriv_r + Epub_i + Spub_i are required.
965
0
    if (!(has_Epriv_i && has_Spriv_i) && !has_Epriv_r) {
966
0
        return;
967
0
    }
968
969
0
    const wg_qqword *ephemeral = (const wg_qqword *)tvb_get_ptr(tvb, 12, WG_KEY_LEN);
970
0
    const uint8_t *encrypted_empty = (const uint8_t *)tvb_get_ptr(tvb, 44, AUTH_TAG_LENGTH);
971
972
0
    wg_qqword ctk[3], h;
973
0
    wg_qqword *c = &ctk[0], *t = &ctk[1], *k = &ctk[2];
974
0
    h = hs->handshake_hash;
975
0
    *c = hs->chaining_key;
976
977
    // c = KDF1(c, msg.ephemeral)
978
0
    wg_kdf(c, ephemeral->data, WG_KEY_LEN, 1, c);
979
    // h = Hash(h || msg.ephemeral)
980
0
    wg_mix_hash(&h, ephemeral, WG_KEY_LEN);
981
    //  dh1 = DH(Epriv_i, msg.ephemeral)    if kType == I
982
    //  dh1 = DH(Epriv_r, Epub_i)           if kType == R
983
0
    wg_qqword dh1;
984
0
    if (has_Epriv_i && has_Spriv_i) {
985
0
        dh_x25519(&dh1, &hs->initiator_ekey->priv_key, ephemeral);
986
0
    } else {
987
0
        dh_x25519(&dh1, &hs->responder_ekey->priv_key, &hs->initiator_ekey->pub_key);
988
0
    }
989
    // c = KDF1(c, dh1)
990
0
    wg_kdf(c, dh1.data, sizeof(dh1), 1, c);
991
    //  dh2 = DH(Spriv_i, msg.ephemeral)    if kType == I
992
    //  dh2 = DH(Epriv_r, Spub_i)           if kType == R
993
0
    wg_qqword dh2;
994
0
    if (has_Epriv_i && has_Spriv_i) {
995
0
        dh_x25519(&dh2, &hs->initiator_skey->priv_key, ephemeral);
996
0
    } else {
997
0
        dh_x25519(&dh2, &hs->responder_ekey->priv_key, &hs->initiator_skey->pub_key);
998
0
    }
999
    // c = KDF1(c, dh2)
1000
0
    wg_kdf(c, dh2.data, sizeof(dh2), 1, c);
1001
0
    wg_qqword h_before_psk = h, c_before_psk = *c, psk;
1002
0
    wg_psk_iter_context psk_iter = { WG_PSK_ITER_STATE_ENTER, NULL };
1003
0
    while (wg_psk_iter_next(&psk_iter, hs, &psk)) {
1004
        // c, t, k = KDF3(c, PSK)
1005
0
        wg_kdf(c, psk.data, WG_KEY_LEN, 3, ctk);
1006
        // h = Hash(h || t)
1007
0
        wg_mix_hash(&h, t, sizeof(wg_qqword));
1008
        // empty = AEAD-Decrypt(k, 0, msg.empty, h)
1009
0
        if (!aead_decrypt(k, 0, encrypted_empty, AUTH_TAG_LENGTH, h.data, sizeof(wg_qqword), NULL, 0)) {
1010
            /* Possibly bad PSK, reset and try another. */
1011
0
            h = h_before_psk;
1012
0
            *c = c_before_psk;
1013
0
            continue;
1014
0
        }
1015
0
        hs->empty_ok = true;
1016
0
        break;
1017
0
    }
1018
0
    if (!hs->empty_ok) {
1019
0
        return;
1020
0
    }
1021
    // h = Hash(h || msg.empty)
1022
0
    wg_mix_hash(&h, encrypted_empty, AUTH_TAG_LENGTH);
1023
1024
    // Calculate transport keys and create ciphers.
1025
    // (Tsend_i = Trecv_r, Trecv_i = Tsend_r) = KDF2(C, "")
1026
0
    wg_qqword transport_keys[2];
1027
0
    wg_kdf(c, NULL, 0, 2, transport_keys);
1028
1029
0
    hs->initiator_recv_cipher = wg_create_cipher(&transport_keys[1]);
1030
0
    hs->responder_recv_cipher = wg_create_cipher(&transport_keys[0]);
1031
0
}
1032
1033
1034
static void
1035
wg_sessions_insert(uint32_t id, wg_session_t *session)
1036
1
{
1037
1
    wmem_list_t *list = (wmem_list_t *)wmem_map_lookup(sessions, GUINT_TO_POINTER(id));
1038
1
    if (!list) {
1039
1
        list = wmem_list_new(wmem_file_scope());
1040
1
        wmem_map_insert(sessions, GUINT_TO_POINTER(id), list);
1041
1
    }
1042
1
    wmem_list_append(list, session);
1043
1
}
1044
1045
static wg_session_t *
1046
wg_session_new(void)
1047
1
{
1048
1
    wg_session_t *session = wmem_new0(wmem_file_scope(), wg_session_t);
1049
1
    session->stream = wg_session_count++;
1050
1
    return session;
1051
1
}
1052
1053
/* Updates the peer address based on the source address. */
1054
static void
1055
wg_session_update_address(wg_session_t *session, packet_info *pinfo, bool sender_is_initiator)
1056
1
{
1057
1
    DISSECTOR_ASSERT(!PINFO_FD_VISITED(pinfo));
1058
1059
1
    if (sender_is_initiator) {
1060
1
        copy_address_wmem(wmem_file_scope(), &session->initial.initiator_address, &pinfo->src);
1061
1
        session->initial.initiator_port = (uint16_t)pinfo->srcport;
1062
1
    } else {
1063
0
        copy_address_wmem(wmem_file_scope(), &session->initial.responder_address, &pinfo->src);
1064
0
        session->initial.responder_port = (uint16_t)pinfo->srcport;
1065
0
    }
1066
1
}
1067
1068
/* Finds an initiation message based on the given Receiver ID that was not
1069
 * previously associated with a responder message. Returns the session if a
1070
 * matching initiation message can be found or NULL otherwise.
1071
 */
1072
static wg_session_t *
1073
wg_sessions_lookup_initiation(packet_info *pinfo, uint32_t receiver_id)
1074
1
{
1075
1
    DISSECTOR_ASSERT(!PINFO_FD_VISITED(pinfo));
1076
1077
    /* Look for the initiation message matching this Receiver ID. */
1078
1
    wmem_list_t *list = (wmem_list_t *)wmem_map_lookup(sessions, GUINT_TO_POINTER(receiver_id));
1079
1
    if (!list) {
1080
1
        return NULL;
1081
1
    }
1082
1083
    /* Walk backwards to find the most recent message first. All packets are
1084
     * guaranteed to arrive before this frame because this is the first pass. */
1085
0
    for (wmem_list_frame_t *item = wmem_list_tail(list); item; item = wmem_list_frame_prev(item)) {
1086
0
        wg_session_t *session = (wg_session_t *)wmem_list_frame_data(item);
1087
0
        if (session->initial.initiator_port != pinfo->destport ||
1088
0
            !addresses_equal(&session->initial.initiator_address, &pinfo->dst)) {
1089
            /* Responder messages are expected to be sent to the initiator. */
1090
0
            continue;
1091
0
        }
1092
0
        if (session->response_frame && session->response_frame != pinfo->num) {
1093
            /* This session was linked elsewhere. */
1094
0
            continue;
1095
0
        }
1096
1097
        /* This assumes no malicious messages and no contrived sequences:
1098
         * Any initiator or responder message is not duplicated nor are these
1099
         * mutated. If this must be detected, the caller could decrypt or check
1100
         * mac1 to distinguish valid messages.
1101
         */
1102
0
        return session;
1103
0
    }
1104
1105
0
    return NULL;
1106
0
}
1107
1108
/* Finds a session with a completed handshake that matches the Receiver ID. */
1109
static wg_session_t *
1110
wg_sessions_lookup(packet_info *pinfo, uint32_t receiver_id, bool *receiver_is_initiator)
1111
0
{
1112
0
    DISSECTOR_ASSERT(!PINFO_FD_VISITED(pinfo));
1113
1114
0
    wmem_list_t *list = (wmem_list_t *)wmem_map_lookup(sessions, GUINT_TO_POINTER(receiver_id));
1115
0
    if (!list) {
1116
0
        return NULL;
1117
0
    }
1118
1119
    /* Walk backwards to find the most recent message first. */
1120
0
    for (wmem_list_frame_t *item = wmem_list_tail(list); item; item = wmem_list_frame_prev(item)) {
1121
0
        wg_session_t *session = (wg_session_t *)wmem_list_frame_data(item);
1122
0
        if (!session->response_frame) {
1123
            /* Ignore sessions that are not fully established. */
1124
0
            continue;
1125
0
        }
1126
0
        if (session->initial.initiator_port == pinfo->destport &&
1127
0
            addresses_equal(&session->initial.initiator_address, &pinfo->dst)) {
1128
0
            *receiver_is_initiator = true;
1129
0
        } else if (session->initial.responder_port == pinfo->destport &&
1130
0
                   addresses_equal(&session->initial.responder_address, &pinfo->dst)) {
1131
0
            *receiver_is_initiator = false;
1132
0
        } else {
1133
            /* Both peers do not match the destination, ignore. */
1134
0
            continue;
1135
0
        }
1136
0
        return session;
1137
0
    }
1138
1139
0
    return NULL;
1140
0
}
1141
1142
/*
1143
 * Finds the static public key for the receiver of this message based on the
1144
 * MAC1 value.
1145
 * TODO on PINFO_FD_VISITED, reuse previously discovered keys from session?
1146
 */
1147
static const wg_skey_t *
1148
wg_mac1_key_probe(wmem_allocator_t* allocator, tvbuff_t *tvb, bool is_initiation)
1149
2
{
1150
2
    const int mac1_offset = is_initiation ? 116 : 60;
1151
1152
    // Shortcut: skip MAC1 validation if no pubkeys are configured.
1153
2
    if (!wg_static_keys || g_hash_table_size(wg_static_keys) == 0) {
1154
2
        return NULL;
1155
2
    }
1156
1157
0
    uint8_t *mac1_msgdata = (uint8_t *)tvb_memdup(allocator, tvb, 0, mac1_offset);
1158
0
    const uint8_t *mac1_output = tvb_get_ptr(tvb, mac1_offset, 16);
1159
1160
    // MAC1 is computed over a message with three reserved bytes set to zero.
1161
0
    mac1_msgdata[1] = mac1_msgdata[2] = mac1_msgdata[3] = 0;
1162
1163
    // Find public key that matches the 16-byte MAC1 field.
1164
0
    GHashTableIter iter;
1165
0
    void *value;
1166
0
    g_hash_table_iter_init(&iter, wg_static_keys);
1167
0
    while (g_hash_table_iter_next(&iter, NULL, &value)) {
1168
0
        const wg_skey_t *skey = (wg_skey_t *)value;
1169
0
        if (wg_mac_verify(&skey->mac1_key, mac1_msgdata, (unsigned)mac1_offset, mac1_output)) {
1170
0
            return skey;
1171
0
        }
1172
0
    }
1173
1174
0
    return NULL;
1175
0
}
1176
1177
/*
1178
 * Builds the handshake decryption state when sufficient keying material is
1179
 * available from the initiation message.
1180
 */
1181
static wg_handshake_state_t *
1182
wg_prepare_handshake_keys(const wg_skey_t *skey_r, tvbuff_t *tvb)
1183
0
{
1184
0
    wg_handshake_state_t *hs;
1185
0
    bool has_r_keys = skey_r && has_private_key(&skey_r->priv_key);
1186
0
    wg_ekey_t *ekey_i = (wg_ekey_t *)wmem_map_lookup(wg_ephemeral_keys, tvb_get_ptr(tvb, 8, WG_KEY_LEN));
1187
1188
    // If neither private keys are available, do not create a session.
1189
0
    if (!has_r_keys && !ekey_i) {
1190
0
        return NULL;
1191
0
    }
1192
1193
    // Even if Spriv_r is available, store Epub_i for Response decryption.
1194
0
    if (!ekey_i) {
1195
0
        ekey_i = wmem_new0(wmem_file_scope(), wg_ekey_t);
1196
0
        tvb_memcpy(tvb, ekey_i->pub_key.data, 8, WG_KEY_LEN);
1197
0
    }
1198
1199
0
    hs = wmem_new0(wmem_file_scope(), wg_handshake_state_t);
1200
0
    hs->responder_skey = skey_r;
1201
0
    hs->initiator_ekey = ekey_i;
1202
0
    wmem_register_callback(wmem_file_scope(), wg_handshake_state_destroy_cb, hs);
1203
0
    return hs;
1204
0
}
1205
1206
/*
1207
 * Processes a Response message, storing additional keys in the state.
1208
 */
1209
static void
1210
wg_prepare_handshake_responder_keys(wg_handshake_state_t *hs, tvbuff_t *tvb)
1211
0
{
1212
0
    wg_ekey_t *ekey_r = (wg_ekey_t *)wmem_map_lookup(wg_ephemeral_keys, tvb_get_ptr(tvb, 12, WG_KEY_LEN));
1213
1214
    // Response decryption needs Epriv_r (or Epub_r + additional secrets).
1215
0
    if (!ekey_r) {
1216
0
        ekey_r = wmem_new0(wmem_file_scope(), wg_ekey_t);
1217
0
        tvb_memcpy(tvb, ekey_r->pub_key.data, 12, WG_KEY_LEN);
1218
0
    }
1219
1220
0
    hs->responder_ekey = ekey_r;
1221
0
}
1222
1223
/* Converts a TAI64 label to the seconds since the Unix epoch.
1224
 * See https://cr.yp.to/libtai/tai64.html */
1225
static bool tai64n_to_unix(uint64_t tai64_label, uint32_t nanoseconds, nstime_t *nstime)
1226
0
{
1227
0
    const uint64_t pow2_62 = 1ULL << 62;
1228
0
    if (tai64_label < pow2_62 || tai64_label >= (1ULL << 63) || nanoseconds > 999999999) {
1229
        // Seconds before 1970 and values larger than 2^63 (reserved) cannot
1230
        // be represented. Nanoseconds must also be valid.
1231
0
        return false;
1232
0
    }
1233
1234
    // TODO this can result in loss of precision
1235
0
    nstime->secs = (time_t)(tai64_label - pow2_62);
1236
0
    nstime->nsecs = (int)nanoseconds;
1237
0
    return true;
1238
0
}
1239
1240
static void
1241
wg_dissect_key_extra(proto_tree *tree, tvbuff_t *tvb, const wg_qqword *pubkey, bool is_ephemeral)
1242
2
{
1243
2
    uint32_t has_private = false;
1244
2
    proto_item *ti;
1245
1246
2
    if (is_ephemeral) {
1247
2
        wg_ekey_t *ekey = (wg_ekey_t *)wmem_map_lookup(wg_ephemeral_keys, pubkey->data);
1248
2
        has_private = ekey && has_private_key(&ekey->priv_key);
1249
2
    } else {
1250
0
        wg_skey_t *skey = (wg_skey_t *)g_hash_table_lookup(wg_static_keys, pubkey->data);
1251
0
        has_private = skey && has_private_key(&skey->priv_key);
1252
0
        ti = proto_tree_add_boolean(tree, hf_wg_static_known_pubkey, tvb, 0, 0, !!skey);
1253
0
        proto_item_set_generated(ti);
1254
0
    }
1255
1256
2
    int hf_known_privkey = is_ephemeral ? hf_wg_ephemeral_known_privkey : hf_wg_static_known_privkey;
1257
2
    ti = proto_tree_add_boolean(tree, hf_known_privkey, tvb, 0, 0, has_private);
1258
2
    proto_item_set_generated(ti);
1259
2
}
1260
1261
1262
static void
1263
wg_dissect_pubkey(proto_tree *tree, packet_info* pinfo, tvbuff_t *tvb, int offset, bool is_ephemeral)
1264
2
{
1265
2
    const uint8_t *pubkey = tvb_get_ptr(tvb, offset, 32);
1266
2
    char *str = g_base64_encode(pubkey, 32);
1267
2
    char *key_str = wmem_strdup(pinfo->pool, str);
1268
2
    g_free(str);
1269
1270
2
    int hf_id = is_ephemeral ? hf_wg_ephemeral : hf_wg_static;
1271
2
    proto_item *ti = proto_tree_add_string(tree, hf_id, tvb, offset, 32, key_str);
1272
2
    if (wg_decryption_supported) {
1273
2
        proto_tree *key_tree = proto_item_add_subtree(ti, ett_key_info);
1274
2
        wg_dissect_key_extra(key_tree, tvb, (const wg_qqword *)pubkey, is_ephemeral);
1275
2
    } else {
1276
0
        expert_add_info(NULL, ti, &ei_wg_decryption_unsupported);
1277
0
    }
1278
2
}
1279
1280
static void
1281
wg_dissect_decrypted_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_handshake_state_t *hs)
1282
1
{
1283
1
    tvbuff_t   *new_tvb;
1284
1285
1
    if (!hs || !hs->initiator_skey) {
1286
1
        return;
1287
1
    }
1288
1289
0
    new_tvb = tvb_new_child_real_data(tvb, hs->initiator_skey->pub_key.data, WG_KEY_LEN, WG_KEY_LEN);
1290
0
    add_new_data_source(pinfo, new_tvb, "Decrypted Static");
1291
0
    wg_dissect_pubkey(wg_tree, pinfo, new_tvb, 0, false);
1292
0
}
1293
1294
static void
1295
wg_dissect_decrypted_timestamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, wg_handshake_state_t *hs)
1296
1
{
1297
1
    uint64_t    tai64_label;
1298
1
    uint32_t    nanoseconds;
1299
1
    nstime_t    nstime;
1300
1
    proto_item *ti;
1301
1
    tvbuff_t   *new_tvb;
1302
1303
1
    if (!hs || !hs->timestamp_ok) {
1304
1
        return;
1305
1
    }
1306
1307
0
    new_tvb = tvb_new_child_real_data(tvb, hs->timestamp, sizeof(hs->timestamp), sizeof(hs->timestamp));
1308
0
    add_new_data_source(pinfo, new_tvb, "Decrypted Timestamp");
1309
1310
0
    tai64_label = tvb_get_uint64(new_tvb, 0, ENC_BIG_ENDIAN);
1311
0
    nanoseconds = tvb_get_uint32(new_tvb, 8, ENC_BIG_ENDIAN);
1312
0
    if (tai64n_to_unix(tai64_label, nanoseconds, &nstime)) {
1313
0
        ti = proto_tree_add_time(tree, hf_wg_timestamp_value, new_tvb, 0, 12, &nstime);
1314
0
        tree = proto_item_add_subtree(ti, ett_timestamp);
1315
0
    }
1316
0
    proto_tree_add_item(tree, hf_wg_timestamp_tai64_label, new_tvb, 0, 8, ENC_BIG_ENDIAN);
1317
0
    proto_tree_add_item(tree, hf_wg_timestamp_nanoseconds, new_tvb, 8, 4, ENC_BIG_ENDIAN);
1318
0
}
1319
1320
static void
1321
wg_dissect_decrypted_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_packet_info_t *wg_pinfo, uint64_t counter, int plain_length)
1322
0
{
1323
0
    wg_handshake_state_t *hs = wg_pinfo->session->hs;
1324
0
    gcry_cipher_hd_t cipher = wg_pinfo->receiver_is_initiator ? hs->initiator_recv_cipher : hs->responder_recv_cipher;
1325
0
    if (!cipher) {
1326
0
        return;
1327
0
    }
1328
1329
0
    DISSECTOR_ASSERT(plain_length >= 0);
1330
0
    const int ctext_len = plain_length + AUTH_TAG_LENGTH;
1331
0
    const unsigned char *ctext = tvb_get_ptr(tvb, 16, ctext_len);
1332
0
    unsigned char *plain = (unsigned char *)wmem_alloc0(pinfo->pool, (unsigned)plain_length);
1333
0
    if (!wg_aead_decrypt(cipher, counter, ctext, (unsigned)ctext_len, NULL, 0, plain, (unsigned)plain_length)) {
1334
0
        proto_tree_add_expert(wg_tree, pinfo, &ei_wg_decryption_error, tvb, 16, ctext_len);
1335
0
        return;
1336
0
    }
1337
0
    if (plain_length == 0) {
1338
0
        return;
1339
0
    }
1340
1341
0
    tvbuff_t *new_tvb = tvb_new_child_real_data(tvb, plain, (unsigned)plain_length, plain_length);
1342
0
    add_new_data_source(pinfo, new_tvb, "Decrypted Packet");
1343
1344
0
    proto_tree *tree = proto_item_get_parent(wg_tree);
1345
0
    if (!pref_dissect_packet) {
1346
        // (IP packet not shown, preference "Dissect transport data" is disabled)
1347
0
        call_data_dissector(new_tvb, pinfo, tree);
1348
0
    } else {
1349
0
        call_dissector(ip_handle, new_tvb, pinfo, tree);
1350
0
    }
1351
0
}
1352
1353
static void
1354
wg_dissect_mac1_pubkey(proto_tree *tree, packet_info* pinfo, tvbuff_t *tvb, const wg_skey_t *skey)
1355
2
{
1356
2
    proto_item *ti;
1357
1358
2
    if (!skey) {
1359
2
        return;
1360
2
    }
1361
1362
0
    ti = proto_tree_add_string(tree, hf_wg_receiver_pubkey, tvb, 0, 0, pubkey_to_string(pinfo->pool, &skey->pub_key));
1363
0
    proto_item_set_generated(ti);
1364
0
    proto_tree *key_tree = proto_item_add_subtree(ti, ett_key_info);
1365
0
    ti = proto_tree_add_boolean(key_tree, hf_wg_receiver_pubkey_known_privkey, tvb, 0, 0, !!has_private_key(&skey->priv_key));
1366
0
    proto_item_set_generated(ti);
1367
0
}
1368
1369
static int
1370
wg_dissect_handshake_initiation(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_packet_info_t *wg_pinfo)
1371
1
{
1372
1
    uint32_t sender_id;
1373
1
    proto_item *ti;
1374
1375
1
    wg_keylog_read();
1376
1
    const wg_skey_t *skey_r = wg_mac1_key_probe(pinfo->pool, tvb, true);
1377
1
    wg_handshake_state_t *hs = NULL;
1378
1379
1
    if (!PINFO_FD_VISITED(pinfo)) {
1380
1
        if (skey_r) {
1381
0
            hs = wg_prepare_handshake_keys(skey_r, tvb);
1382
0
            if (hs) {
1383
0
                wg_process_initiation(tvb, hs);
1384
0
            }
1385
0
        }
1386
1
    } else if (wg_pinfo && wg_pinfo->session) {
1387
0
        hs = wg_pinfo->session->hs;
1388
0
    }
1389
1390
1
    proto_tree_add_item_ret_uint(wg_tree, hf_wg_sender, tvb, 4, 4, ENC_LITTLE_ENDIAN, &sender_id);
1391
1
    col_append_fstr(pinfo->cinfo, COL_INFO, ", sender=0x%08X", sender_id);
1392
1
    wg_dissect_pubkey(wg_tree, pinfo, tvb, 8, true);
1393
1
    proto_tree_add_item(wg_tree, hf_wg_encrypted_static, tvb, 40, 32 + AUTH_TAG_LENGTH, ENC_NA);
1394
1
    wg_dissect_decrypted_static(tvb, pinfo, wg_tree, hs);
1395
1
    proto_tree_add_item(wg_tree, hf_wg_encrypted_timestamp, tvb, 88, 12 + AUTH_TAG_LENGTH, ENC_NA);
1396
1
    wg_dissect_decrypted_timestamp(tvb, pinfo, wg_tree, hs);
1397
1
    proto_tree_add_item(wg_tree, hf_wg_mac1, tvb, 116, 16, ENC_NA);
1398
1
    wg_dissect_mac1_pubkey(wg_tree, pinfo, tvb, skey_r);
1399
1
    proto_tree_add_item(wg_tree, hf_wg_mac2, tvb, 132, 16, ENC_NA);
1400
1401
1
    if (!PINFO_FD_VISITED(pinfo)) {
1402
        /* XXX should an initiation message with the same contents (except MAC2) be
1403
         * considered part of the same "session"? */
1404
1
        wg_session_t *session = wg_session_new();
1405
1
        session->initiator_frame = pinfo->num;
1406
1
        wg_session_update_address(session, pinfo, true);
1407
1
        session->hs = hs;
1408
1
        wg_sessions_insert(sender_id, session);
1409
1
        wg_pinfo->session = session;
1410
1
    }
1411
1
    wg_session_t *session = wg_pinfo ? wg_pinfo->session : NULL;
1412
1
    if (session) {
1413
1
        ti = proto_tree_add_uint(wg_tree, hf_wg_stream, tvb, 0, 0, session->stream);
1414
1
        proto_item_set_generated(ti);
1415
1
    }
1416
1
    if (session && session->response_frame) {
1417
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_response_in, tvb, 0, 0, session->response_frame);
1418
0
        proto_item_set_generated(ti);
1419
0
    }
1420
1421
1
    return 148;
1422
1
}
1423
1424
static int
1425
wg_dissect_handshake_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_packet_info_t *wg_pinfo)
1426
1
{
1427
1
    uint32_t sender_id, receiver_id;
1428
1
    proto_item *ti;
1429
1
    wg_session_t *session;
1430
1431
1
    wg_keylog_read();
1432
1
    const wg_skey_t *skey_i = wg_mac1_key_probe(pinfo->pool, tvb, false);
1433
1434
1
    proto_tree_add_item_ret_uint(wg_tree, hf_wg_sender, tvb, 4, 4, ENC_LITTLE_ENDIAN, &sender_id);
1435
1
    col_append_fstr(pinfo->cinfo, COL_INFO, ", sender=0x%08X", sender_id);
1436
1
    proto_tree_add_item_ret_uint(wg_tree, hf_wg_receiver, tvb, 8, 4, ENC_LITTLE_ENDIAN, &receiver_id);
1437
1
    col_append_fstr(pinfo->cinfo, COL_INFO, ", receiver=0x%08X", receiver_id);
1438
1439
1
    if (!PINFO_FD_VISITED(pinfo)) {
1440
1
        session = wg_sessions_lookup_initiation(pinfo, receiver_id);
1441
1
        if (session && session->hs) {
1442
0
            wg_prepare_handshake_responder_keys(session->hs, tvb);
1443
0
            wg_process_response(tvb, session->hs);
1444
0
        }
1445
1
    } else {
1446
0
        session = wg_pinfo ? wg_pinfo->session : NULL;
1447
0
    }
1448
1449
1
    wg_dissect_pubkey(wg_tree, pinfo, tvb, 12, true);
1450
1
    proto_tree_add_item(wg_tree, hf_wg_encrypted_empty, tvb, 44, 16, ENC_NA);
1451
1
    if (session && session->hs) {
1452
0
        ti = proto_tree_add_boolean(wg_tree, hf_wg_handshake_ok, tvb, 0, 0, !!session->hs->empty_ok);
1453
0
        proto_item_set_generated(ti);
1454
0
    }
1455
1
    proto_tree_add_item(wg_tree, hf_wg_mac1, tvb, 60, 16, ENC_NA);
1456
1
    wg_dissect_mac1_pubkey(wg_tree, pinfo, tvb, skey_i);
1457
1
    proto_tree_add_item(wg_tree, hf_wg_mac2, tvb, 76, 16, ENC_NA);
1458
1459
1
    if (!PINFO_FD_VISITED(pinfo)) {
1460
        /* XXX should probably check whether decryption succeeds before linking
1461
         * and somehow mark that this response is related but not correct. */
1462
1
        if (session) {
1463
0
            session->response_frame = pinfo->num;
1464
0
            wg_session_update_address(session, pinfo, false);
1465
0
            wg_sessions_insert(sender_id, session);
1466
0
            wg_pinfo->session = session;
1467
0
        }
1468
1
    }
1469
1
    if (session) {
1470
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_stream, tvb, 0, 0, session->stream);
1471
0
        proto_item_set_generated(ti);
1472
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_response_to, tvb, 0, 0, session->initiator_frame);
1473
0
        proto_item_set_generated(ti);
1474
0
    }
1475
1476
1
    return 92;
1477
1
}
1478
1479
static int
1480
wg_dissect_handshake_cookie(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_packet_info_t *wg_pinfo)
1481
0
{
1482
0
    uint32_t receiver_id;
1483
0
    proto_item *ti;
1484
1485
0
    proto_tree_add_item_ret_uint(wg_tree, hf_wg_receiver, tvb, 4, 4, ENC_LITTLE_ENDIAN, &receiver_id);
1486
0
    col_append_fstr(pinfo->cinfo, COL_INFO, ", receiver=0x%08X", receiver_id);
1487
0
    proto_tree_add_item(wg_tree, hf_wg_nonce, tvb, 8, 24, ENC_NA);
1488
0
    proto_tree_add_item(wg_tree, hf_wg_encrypted_cookie, tvb, 32, 16 + AUTH_TAG_LENGTH, ENC_NA);
1489
1490
0
    wg_session_t *session;
1491
0
    if (!PINFO_FD_VISITED(pinfo)) {
1492
        /* Check for Cookie Reply from Responder to Initiator. */
1493
0
        session = wg_sessions_lookup_initiation(pinfo, receiver_id);
1494
0
        if (session) {
1495
0
            session->response_frame = pinfo->num;
1496
0
            wg_session_update_address(session, pinfo, false);
1497
0
            wg_pinfo->session = session;
1498
0
        }
1499
        /* XXX check for cookie reply from Initiator to Responder */
1500
0
    } else {
1501
0
        session = wg_pinfo ? wg_pinfo->session : NULL;
1502
0
    }
1503
0
    if (session) {
1504
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_stream, tvb, 0, 0, session->stream);
1505
0
        proto_item_set_generated(ti);
1506
        /* XXX check for cookie reply from Initiator to Responder */
1507
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_response_to, tvb, 0, 0, session->initiator_frame);
1508
0
        proto_item_set_generated(ti);
1509
0
    }
1510
1511
0
    return 64;
1512
0
}
1513
1514
static int
1515
wg_dissect_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *wg_tree, wg_packet_info_t *wg_pinfo)
1516
0
{
1517
0
    uint32_t receiver_id;
1518
0
    uint64_t counter;
1519
0
    proto_item *ti;
1520
1521
0
    proto_tree_add_item_ret_uint(wg_tree, hf_wg_receiver, tvb, 4, 4, ENC_LITTLE_ENDIAN, &receiver_id);
1522
0
    col_append_fstr(pinfo->cinfo, COL_INFO, ", receiver=0x%08X", receiver_id);
1523
0
    proto_tree_add_item_ret_uint64(wg_tree, hf_wg_counter, tvb, 8, 8, ENC_LITTLE_ENDIAN, &counter);
1524
0
    col_append_fstr(pinfo->cinfo, COL_INFO, ", counter=%" PRIu64, counter);
1525
1526
0
    int packet_length = tvb_captured_length_remaining(tvb, 16);
1527
0
    if (packet_length < AUTH_TAG_LENGTH) {
1528
0
        proto_tree_add_expert(wg_tree, pinfo, &ei_wg_bad_packet_length, tvb, 16, packet_length);
1529
0
        return 16 + packet_length;
1530
0
    } else if (packet_length != AUTH_TAG_LENGTH) {
1531
        /* Keepalive messages are already marked, no need to append data length. */
1532
0
        col_append_fstr(pinfo->cinfo, COL_INFO, ", datalen=%d", packet_length - AUTH_TAG_LENGTH);
1533
0
    }
1534
0
    ti = proto_tree_add_item(wg_tree, hf_wg_encrypted_packet, tvb, 16, packet_length, ENC_NA);
1535
1536
0
    if (packet_length == AUTH_TAG_LENGTH) {
1537
0
        expert_add_info(pinfo, ti, &ei_wg_keepalive);
1538
0
    }
1539
1540
0
    wg_session_t *session;
1541
0
    if (!PINFO_FD_VISITED(pinfo)) {
1542
0
        bool receiver_is_initiator;
1543
0
        session = wg_sessions_lookup(pinfo, receiver_id, &receiver_is_initiator);
1544
0
        if (session) {
1545
0
            wg_session_update_address(session, pinfo, !receiver_is_initiator);
1546
0
            wg_pinfo->session = session;
1547
0
            wg_pinfo->receiver_is_initiator = receiver_is_initiator;
1548
0
        }
1549
0
    } else {
1550
0
        session = wg_pinfo ? wg_pinfo->session : NULL;
1551
0
    }
1552
0
    if (session) {
1553
0
        ti = proto_tree_add_uint(wg_tree, hf_wg_stream, tvb, 0, 0, session->stream);
1554
0
        proto_item_set_generated(ti);
1555
0
    }
1556
1557
0
    if (session && session->hs) {
1558
0
        wg_dissect_decrypted_packet(tvb, pinfo, wg_tree, wg_pinfo, counter, packet_length - AUTH_TAG_LENGTH);
1559
0
    }
1560
1561
0
    return 16 + packet_length;
1562
0
}
1563
1564
static bool
1565
wg_is_valid_message_length(uint8_t message_type, unsigned length)
1566
1.21k
{
1567
1.21k
    switch (message_type) {
1568
26
    case WG_TYPE_HANDSHAKE_INITIATION:
1569
26
        return length == 148;
1570
13
    case WG_TYPE_HANDSHAKE_RESPONSE:
1571
13
        return length == 92;
1572
46
    case WG_TYPE_COOKIE_REPLY:
1573
46
        return length == 64;
1574
46
    case WG_TYPE_TRANSPORT_DATA:
1575
46
        return length >= 32;
1576
1.08k
    default:
1577
1.08k
        return false;
1578
1.21k
    }
1579
1.21k
}
1580
1581
static int
1582
dissect_wg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1583
2
{
1584
2
    proto_item *ti;
1585
2
    proto_tree *wg_tree;
1586
2
    uint32_t    message_type;
1587
2
    const char *message_type_str;
1588
2
    wg_packet_info_t *wg_pinfo;
1589
1590
2
    message_type = tvb_get_uint8(tvb, 0);
1591
2
    message_type_str = try_val_to_str(message_type, wg_type_names);
1592
2
    if (!message_type_str)
1593
0
        return 0;
1594
1595
2
    if (!wg_is_valid_message_length(message_type, tvb_reported_length(tvb))) {
1596
0
        return 0;
1597
0
    }
1598
1599
    /* Special case: zero-length data message is a Keepalive message. */
1600
2
    if (message_type == WG_TYPE_TRANSPORT_DATA && tvb_reported_length(tvb) == 32) {
1601
0
        message_type_str = "Keepalive";
1602
0
    }
1603
1604
2
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "WireGuard");
1605
2
    col_set_str(pinfo->cinfo, COL_INFO, message_type_str);
1606
1607
2
    ti = proto_tree_add_item(tree, proto_wg, tvb, 0, -1, ENC_NA);
1608
2
    wg_tree = proto_item_add_subtree(ti, ett_wg);
1609
1610
2
    proto_tree_add_item(wg_tree, hf_wg_type, tvb, 0, 1, ENC_NA);
1611
2
    proto_tree_add_item(wg_tree, hf_wg_reserved, tvb, 1, 3, ENC_NA);
1612
1613
2
    if (!PINFO_FD_VISITED(pinfo)) {
1614
2
        wg_pinfo = wmem_new0(wmem_file_scope(), wg_packet_info_t);
1615
2
        p_add_proto_data(wmem_file_scope(), pinfo, proto_wg, 0, wg_pinfo);
1616
2
    } else {
1617
        /*
1618
         * Note: this may be NULL if the heuristics dissector sets a
1619
         * conversation dissector later in the stream, for example due to a new
1620
         * Handshake Initiation message. Previous messages are potentially
1621
         * Transport Data messages which might not be detected through
1622
         * heuristics.
1623
         */
1624
0
        wg_pinfo = (wg_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_wg, 0);
1625
0
    }
1626
1627
2
    switch ((wg_message_type)message_type) {
1628
1
    case WG_TYPE_HANDSHAKE_INITIATION:
1629
1
        return wg_dissect_handshake_initiation(tvb, pinfo, wg_tree, wg_pinfo);
1630
1
    case WG_TYPE_HANDSHAKE_RESPONSE:
1631
1
        return wg_dissect_handshake_response(tvb, pinfo, wg_tree, wg_pinfo);
1632
0
    case WG_TYPE_COOKIE_REPLY:
1633
0
        return wg_dissect_handshake_cookie(tvb, pinfo, wg_tree, wg_pinfo);
1634
0
    case WG_TYPE_TRANSPORT_DATA:
1635
0
        return wg_dissect_data(tvb, pinfo, wg_tree, wg_pinfo);
1636
2
    }
1637
1638
0
    DISSECTOR_ASSERT_NOT_REACHED();
1639
0
}
1640
1641
static bool
1642
dissect_wg_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1643
1.36k
{
1644
    /*
1645
     * Heuristics to detect the WireGuard protocol:
1646
     * - The first byte must be one of the valid four messages.
1647
     * - The total packet length depends on the message type, and is fixed for
1648
     *   three of them. The Data type has a minimum length however.
1649
     * - The next three bytes are reserved and zero in the official protocol.
1650
     *   Cloudflare's implementation however uses this field for load balancing
1651
     *   purposes, so this condition is not checked here for most messages.
1652
     *   It is checked for data messages to avoid false positives.
1653
     */
1654
1.36k
    uint32_t    message_type;
1655
1.36k
    bool        reserved_is_zeroes;
1656
1657
1.36k
    if (tvb_reported_length(tvb) < 4)
1658
151
        return false;
1659
1660
1.21k
    message_type = tvb_get_uint8(tvb, 0);
1661
1.21k
    reserved_is_zeroes = tvb_get_ntoh24(tvb, 1) == 0;
1662
1663
1.21k
    if (!wg_is_valid_message_length(message_type, tvb_reported_length(tvb))) {
1664
1.17k
        return false;
1665
1.17k
    }
1666
1667
39
    switch (message_type) {
1668
1
        case WG_TYPE_COOKIE_REPLY:
1669
37
        case WG_TYPE_TRANSPORT_DATA:
1670
37
            if (!reserved_is_zeroes)
1671
37
                return false;
1672
0
            break;
1673
39
    }
1674
1675
    /*
1676
     * Assuming that this is a new handshake, make sure that future messages are
1677
     * directed to our dissector. This ensures that cookie replies and data
1678
     * messages using non-zero reserved bytes are still properly recognized.
1679
     * An edge case occurs when the address or port change. In that case, Data
1680
     * messages using non-zero reserved bytes will not be recognized. The user
1681
     * can use Decode As for this case.
1682
     */
1683
2
    if (message_type == WG_TYPE_HANDSHAKE_INITIATION) {
1684
1
        conversation_t *conversation = find_or_create_conversation(pinfo);
1685
1
        conversation_set_dissector(conversation, wg_handle);
1686
1
    }
1687
1688
2
    dissect_wg(tvb, pinfo, tree, data);
1689
2
    return true;
1690
39
}
1691
1692
static void
1693
wg_init(void)
1694
16
{
1695
16
    wg_session_count = 0;
1696
16
}
1697
1698
void
1699
proto_register_wg(void)
1700
16
{
1701
16
    module_t        *wg_module;
1702
16
    expert_module_t *expert_wg;
1703
1704
16
    static hf_register_info hf[] = {
1705
        /* Initiation message */
1706
16
        { &hf_wg_type,
1707
16
          { "Type", "wg.type",
1708
16
            FT_UINT8, BASE_DEC, VALS(wg_type_names), 0x0,
1709
16
            NULL, HFILL }
1710
16
        },
1711
16
        { &hf_wg_reserved,
1712
16
          { "Reserved", "wg.reserved",
1713
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
1714
16
            NULL, HFILL }
1715
16
        },
1716
16
        { &hf_wg_sender,
1717
16
          { "Sender", "wg.sender",
1718
16
            FT_UINT32, BASE_HEX, NULL, 0x0,
1719
16
            "Identifier as chosen by the sender", HFILL }
1720
16
        },
1721
16
        { &hf_wg_ephemeral,
1722
16
          { "Ephemeral", "wg.ephemeral",
1723
16
            FT_STRING, BASE_NONE, NULL, 0x0,
1724
16
            "Ephemeral public key of sender", HFILL }
1725
16
        },
1726
16
        { &hf_wg_encrypted_static,
1727
16
          { "Encrypted Static", "wg.encrypted_static",
1728
16
            FT_NONE, BASE_NONE, NULL, 0x0,
1729
16
            "Encrypted long-term static public key of sender", HFILL }
1730
16
        },
1731
16
        { &hf_wg_static,
1732
16
          { "Static Public Key", "wg.static",
1733
16
            FT_STRING, BASE_NONE, NULL, 0x0,
1734
16
            "Long-term static public key of sender", HFILL }
1735
16
        },
1736
16
        { &hf_wg_encrypted_timestamp,
1737
16
          { "Encrypted Timestamp", "wg.encrypted_timestamp",
1738
16
            FT_NONE, BASE_NONE, NULL, 0x0,
1739
16
            NULL, HFILL }
1740
16
        },
1741
16
        { &hf_wg_timestamp_tai64_label,
1742
16
          { "TAI64 Label", "wg.timestamp.tai64_label",
1743
16
            FT_UINT64, BASE_DEC, NULL, 0x0,
1744
16
            NULL, HFILL }
1745
16
        },
1746
16
        { &hf_wg_timestamp_nanoseconds,
1747
16
          { "Nanoseconds", "wg.timestamp.nanoseconds",
1748
16
            FT_UINT32, BASE_DEC, NULL, 0x0,
1749
16
            NULL, HFILL }
1750
16
        },
1751
16
        { &hf_wg_timestamp_value,
1752
16
          { "Timestamp", "wg.timestamp.value",
1753
16
            FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0x0,
1754
16
            NULL, HFILL }
1755
16
        },
1756
16
        { &hf_wg_mac1,
1757
16
          { "mac1", "wg.mac1",
1758
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
1759
16
            NULL, HFILL }
1760
16
        },
1761
16
        { &hf_wg_mac2,
1762
16
          { "mac2", "wg.mac2",
1763
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
1764
16
            NULL, HFILL }
1765
16
        },
1766
1767
        /* Response message */
1768
16
        { &hf_wg_receiver,
1769
16
          { "Receiver", "wg.receiver",
1770
16
            FT_UINT32, BASE_HEX, NULL, 0x0,
1771
16
            "Identifier as chosen by receiver", HFILL }
1772
16
        },
1773
16
        { &hf_wg_encrypted_empty,
1774
16
          { "Encrypted Empty", "wg.encrypted_empty",
1775
16
            FT_NONE, BASE_NONE, NULL, 0x0,
1776
16
            "Authenticated encryption of an empty string", HFILL }
1777
16
        },
1778
16
        { &hf_wg_handshake_ok,
1779
16
          { "Handshake decryption successful", "wg.handshake_ok",
1780
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1781
16
            "Whether decryption keys were successfully derived", HFILL }
1782
16
        },
1783
1784
        /* Cookie message */
1785
16
        { &hf_wg_nonce,
1786
16
          { "Nonce", "wg.nonce",
1787
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
1788
16
            NULL, HFILL }
1789
16
        },
1790
16
        { &hf_wg_encrypted_cookie,
1791
16
          { "Encrypted Cookie", "wg.encrypted_cookie",
1792
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
1793
16
            NULL, HFILL }
1794
16
        },
1795
        /* TODO decrypted cookie field. */
1796
1797
        /* Data message */
1798
16
        { &hf_wg_counter,
1799
16
          { "Counter", "wg.counter",
1800
16
            FT_UINT64, BASE_DEC, NULL, 0x0,
1801
16
            NULL, HFILL }
1802
16
        },
1803
16
        { &hf_wg_encrypted_packet,
1804
16
          { "Encrypted Packet", "wg.encrypted_packet",
1805
16
            FT_NONE, BASE_NONE, NULL, 0x0,
1806
16
            NULL, HFILL }
1807
16
        },
1808
1809
        /* Association tracking. */
1810
16
        { &hf_wg_stream,
1811
16
          { "Stream index", "wg.stream",
1812
16
            FT_UINT32, BASE_DEC, NULL, 0x0,
1813
16
            "Identifies a session in this capture file", HFILL }
1814
16
        },
1815
16
        { &hf_wg_response_in,
1816
16
          { "Response in Frame", "wg.response_in",
1817
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
1818
16
            "The response to this initiation message is in this frame", HFILL }
1819
16
        },
1820
16
        { &hf_wg_response_to,
1821
16
          { "Response to Frame", "wg.response_to",
1822
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
1823
16
            "This is a response to the initiation message in this frame", HFILL }
1824
16
        },
1825
1826
        /* Additional fields. */
1827
16
        { &hf_wg_receiver_pubkey,
1828
16
          { "Receiver Static Public Key", "wg.receiver_pubkey",
1829
16
            FT_STRING, BASE_NONE, NULL, 0x0,
1830
16
            "Public key of the receiver (matched based on MAC1)", HFILL }
1831
16
        },
1832
16
        { &hf_wg_receiver_pubkey_known_privkey,
1833
16
          { "Has Private Key", "wg.receiver_pubkey.known_privkey",
1834
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1835
16
            "Whether the corresponding private key is known (configured via prefs)", HFILL }
1836
16
        },
1837
16
        { &hf_wg_ephemeral_known_privkey,
1838
16
          { "Has Private Key", "wg.ephemeral.known_privkey",
1839
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1840
16
            "Whether the corresponding private key is known (configured via prefs)", HFILL }
1841
16
        },
1842
16
        { &hf_wg_static_known_pubkey,
1843
16
          { "Known Public Key", "wg.static.known_pubkey",
1844
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1845
16
            "Whether this public key is known (configured via prefs)", HFILL }
1846
16
        },
1847
16
        { &hf_wg_static_known_privkey,
1848
16
          { "Has Private Key", "wg.static.known_privkey",
1849
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1850
16
            "Whether the corresponding private key is known (configured via prefs)", HFILL }
1851
16
        },
1852
16
    };
1853
1854
16
    static int *ett[] = {
1855
16
        &ett_wg,
1856
16
        &ett_timestamp,
1857
16
        &ett_key_info,
1858
16
    };
1859
1860
16
    static ei_register_info ei[] = {
1861
16
        { &ei_wg_bad_packet_length,
1862
16
          { "wg.bad_packet_length", PI_MALFORMED, PI_ERROR,
1863
16
            "Packet length is too small", EXPFILL }
1864
16
        },
1865
16
        { &ei_wg_keepalive,
1866
16
          { "wg.keepalive", PI_SEQUENCE, PI_CHAT,
1867
16
            "This is a Keepalive message", EXPFILL }
1868
16
        },
1869
16
        { &ei_wg_decryption_error,
1870
16
          { "wg.decryption_error", PI_DECRYPTION, PI_WARN,
1871
16
            "Packet data decryption failed", EXPFILL }
1872
16
        },
1873
16
        { &ei_wg_decryption_unsupported,
1874
16
          { "wg.decryption_unsupported", PI_DECRYPTION, PI_WARN,
1875
16
            "Decryption unsupported (disable FIPS mode or upgrade Libgcrypt to 1.10.0 or higher)", EXPFILL }
1876
16
        },
1877
16
    };
1878
1879
    /* UAT for header fields */
1880
16
    static uat_field_t wg_key_uat_fields[] = {
1881
16
        UAT_FLD_VS(wg_key_uat, key_type, "Key type", wg_key_uat_type_vals, "Public or Private"),
1882
16
        UAT_FLD_CSTRING(wg_key_uat, key, "Key", "Base64-encoded key"),
1883
16
        UAT_END_FIELDS
1884
16
    };
1885
1886
16
    proto_wg = proto_register_protocol("WireGuard Protocol", "WireGuard", "wg");
1887
1888
16
    proto_register_field_array(proto_wg, hf, array_length(hf));
1889
16
    proto_register_subtree_array(ett, array_length(ett));
1890
1891
16
    expert_wg = expert_register_protocol(proto_wg);
1892
16
    expert_register_field_array(expert_wg, ei, array_length(ei));
1893
1894
16
    wg_handle = register_dissector("wg", dissect_wg, proto_wg);
1895
1896
16
    wg_module = prefs_register_protocol(proto_wg, NULL);
1897
1898
16
    uat_t *wg_keys_uat = uat_new("WireGuard static keys",
1899
16
            sizeof(wg_key_uat_record_t),
1900
16
            "wg_keys",                      /* filename */
1901
16
            true,                           /* from_profile */
1902
16
            &wg_key_records,                /* data_ptr */
1903
16
            &num_wg_key_records,            /* numitems_ptr */
1904
16
            UAT_AFFECTS_DISSECTION,         /* affects dissection of packets, but not set of named fields */
1905
16
            NULL,                           /* Help section (currently a wiki page) */
1906
16
            wg_key_uat_record_copy_cb,      /* copy_cb */
1907
16
            wg_key_uat_record_update_cb,    /* update_cb */
1908
16
            wg_key_uat_record_free_cb,      /* free_cb */
1909
16
            wg_key_uat_apply,               /* post_update_cb */
1910
16
            wg_key_uat_reset,               /* reset_cb */
1911
16
            wg_key_uat_fields);
1912
1913
16
    prefs_register_uat_preference(wg_module, "keys",
1914
16
            "WireGuard static keys",
1915
16
            "A table of long-term static keys to enable WireGuard peer identification or partial decryption",
1916
16
            wg_keys_uat);
1917
1918
16
    prefs_register_bool_preference(wg_module, "dissect_packet",
1919
16
            "Dissect transport data",
1920
16
            "Whether the IP dissector should dissect decrypted transport data.",
1921
16
            &pref_dissect_packet);
1922
1923
16
    prefs_register_filename_preference(wg_module, "keylog_file", "Key log filename",
1924
16
            "The path to the file which contains a list of secrets in the following format:\n"
1925
16
            "\"<key-type> = <base64-encoded-key>\" (without quotes, leading spaces and spaces around '=' are ignored).\n"
1926
16
            "<key-type> is one of: LOCAL_STATIC_PRIVATE_KEY, REMOTE_STATIC_PUBLIC_KEY, "
1927
16
            "LOCAL_EPHEMERAL_PRIVATE_KEY or PRESHARED_KEY.",
1928
16
            &pref_keylog_file, false);
1929
1930
16
    wg_decryption_supported = wg_decrypt_init();
1931
    /* We require libgcrypt 1.8.0, so if the algorithms aren't supported
1932
     * that's almost surely because FIPS mode is on. For libgcrypt 1.10.0
1933
     * and higher we turn it off in epan_init() when initializing gcrypt.
1934
     * We could verify that's the reason by calling gcry_fips_mode_active()
1935
     */
1936
1937
16
    if (wg_decryption_supported) {
1938
16
        secrets_register_type(SECRETS_TYPE_WIREGUARD, wg_keylog_process_lines);
1939
16
    }
1940
1941
16
    wg_ephemeral_keys = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wg_pubkey_hash, wg_pubkey_equal);
1942
1943
16
    register_init_routine(wg_init);
1944
16
    register_cleanup_routine(wg_keylog_reset);
1945
16
    sessions = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
1946
16
}
1947
1948
void
1949
proto_reg_handoff_wg(void)
1950
16
{
1951
16
    dissector_add_uint_with_preference("udp.port", 0, wg_handle);
1952
16
    heur_dissector_add("udp", dissect_wg_heur, "WireGuard", "wg", proto_wg, HEURISTIC_ENABLE);
1953
1954
16
    ip_handle = find_dissector("ip");
1955
16
}
1956
1957
/*
1958
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1959
 *
1960
 * Local variables:
1961
 * c-basic-offset: 4
1962
 * tab-width: 8
1963
 * indent-tabs-mode: nil
1964
 * End:
1965
 *
1966
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1967
 * :indentSize=4:tabSize=8:noTabs=true:
1968
 */