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