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