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