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