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/
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>
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);
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
;
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
90 WG_TYPE_HANDSHAKE_INITIATION
= 1,
91 WG_TYPE_HANDSHAKE_RESPONSE
= 2,
92 WG_TYPE_COOKIE_REPLY
= 3,
93 WG_TYPE_TRANSPORT_DATA
= 4
96 static const value_string wg_type_names
[] = {
97 { 0x01, "Handshake Initiation" },
98 { 0x02, "Handshake Response" },
99 { 0x03, "Cookie Reply" },
100 { 0x04, "Transport Data" },
104 /* Decryption types. {{{ */
106 * Most operations operate on 32 byte units (keys and hash output).
109 #define WG_KEY_LEN 32
110 unsigned char data
[WG_KEY_LEN
];
114 * Static key with the MAC1 key pre-computed and an optional private key.
116 typedef struct wg_skey
{
119 wg_qqword priv_key
; /* Optional, set to all zeroes if missing. */
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
{
139 typedef struct wg_ekey
{
141 wg_qqword priv_key
; /* Optional, set to all zeroes if missing. */
142 wg_psk_t
*psk_list
; /* Optional, possible PSKs to try. */
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
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. */
184 enum wg_psk_iter_state state
;
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" },
197 unsigned key_type
; /* See "wg_key_uat_type_vals". */
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.
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.
241 address initiator_address
;
242 address responder_address
;
243 uint16_t initiator_port
;
244 uint16_t responder_port
;
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).
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. */
263 /* Per-packet state. */
265 wg_session_t
*session
;
266 bool receiver_is_initiator
; /* Whether this transport data packet is sent to an Initiator. */
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. */
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.
282 privkey
->data
[31] |= 64;
285 /* Whether a private key is initialized (see set_private_key). */
287 has_private_key(const wg_qqword
*secret
)
289 return !!(secret
->data
[31] & 64);
293 * Compute the Curve25519 public key from a private key.
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);
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.
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
);
328 decode_base64_key(wg_qqword
*out
, const char *str
)
333 if (strlen(str
) + 1 != sizeof(tmp
)) {
336 memcpy(tmp
, str
, sizeof(tmp
));
337 g_base64_decode_inplace(tmp
, &out_len
);
338 if (out_len
!= WG_KEY_LEN
) {
341 memcpy(out
->data
, tmp
, WG_KEY_LEN
);
344 /* Key conversion routines. }}} */
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.
360 wg_mac1_key(const wg_qqword
*static_public
, wg_qqword
*mac_key_out
)
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
));
371 // caller should have checked this.
372 DISSECTOR_ASSERT_NOT_REACHED();
376 * Verify that MAC(mac_key, data) matches "mac_output".
379 wg_mac_verify(const wg_qqword
*mac_key
,
380 const unsigned char *data
, unsigned data_len
, const uint8_t mac_output
[16])
384 if (gcry_md_open(&hd
, GCRY_MD_BLAKE2S_128
, 0) == 0) {
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;
393 // caller should have checked this.
394 DISSECTOR_ASSERT_NOT_REACHED();
400 * Update the new chained hash value: h = Hash(h || data).
403 wg_mix_hash(wg_qqword
*h
, const void *data
, size_t data_len
)
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
));
416 * Computes KDF_n(key, input) where n is the number of derived keys.
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. */
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.
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) {
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
));
449 static gcry_cipher_hd_t
450 wg_create_cipher(const wg_qqword
*key
)
453 if (gcry_cipher_open(&hd
, GCRY_CIPHER_CHACHA20
, GCRY_CIPHER_MODE_POLY1305
, 0)) {
457 if (gcry_cipher_setkey(hd
, key
->data
, sizeof(*key
))) {
458 gcry_cipher_close(hd
);
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
;
481 * Decrypt ciphertext using the ChaCha20-Poly1305 cipher. The auth tag must be
482 * included with the ciphertext.
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.
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
);
516 /* Protocol-specific crypto routines. }}} */
519 * Add a static public or private key to "wg_static_keys".
522 wg_add_static_key(const wg_qqword
*tmp_key
, bool is_private
)
524 if (!wg_decryption_supported
) {
528 wg_skey_t
*key
= g_new0(wg_skey_t
, 1);
530 set_private_key(&key
->priv_key
, tmp_key
);
531 priv_to_pub(&key
->pub_key
, tmp_key
);
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
);
540 if (!has_private_key(&oldkey
->priv_key
) && is_private
) {
541 oldkey
->priv_key
= key
->priv_key
;
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.
557 wg_add_ephemeral_privkey(const wg_qqword
*priv_key
)
559 if (!wg_decryption_supported
) {
564 priv_to_pub(&pub_key
, priv_key
);
565 wg_ekey_t
*key
= (wg_ekey_t
*)wmem_map_lookup(wg_ephemeral_keys
, &pub_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
);
575 /* PSK handling. {{{ */
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.
590 wg_psk_iter_next(wg_psk_iter_context
*psk_iter
, const wg_handshake_state_t
*hs
,
593 wg_psk_t
*psk
= psk_iter
->next_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
;
604 case WG_PSK_ITER_STATE_INITIATOR
:
605 psk
= hs
->responder_ekey
->psk_list
;
606 psk_iter
->state
= WG_PSK_ITER_STATE_RESPONDER
;
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
;
612 case WG_PSK_ITER_STATE_EXIT
:
617 *psk_out
= psk
->psk_data
;
618 psk_iter
->next_psk
= psk
->next
;
621 /* PSK handling. }}} */
623 /* UAT and key configuration. {{{ */
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
);
640 if (!wg_decryption_supported
) {
644 if (!pref_keylog_file
|| !*pref_keylog_file
) {
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.");
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
));
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
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=
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
);
687 wg_keylog_process_lines((const uint8_t *)buf
, (unsigned)strlen(buf
));
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
);
703 linelen
= next_line
- line
;
704 next_line
++; /* drop LF */
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
== ' ') {
719 char key_type
[sizeof("LOCAL_EPHEMERAL_PRIVATE_KEY")];
720 char key_value
[45] = { 0 };
722 p
= (const char *)memchr(p0
, '=', line_end
- 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] == ' ') {
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. */
735 while (p
< line_end
&& *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
);
746 if (!key_value
[0] || !decode_base64_key(&key
, key_value
)) {
747 ws_debug("Unrecognized key log line: %.*s", (int)linelen
, line
);
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
;
763 ws_debug("Ignored PSK as no new ephemeral key was found");
766 ws_debug("Unrecognized key log line: %.*s", (int)linelen
, line
);
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
);
784 wg_key_uat_record_update_cb(void *r
, char **error
)
786 wg_key_uat_record_t
*rec
= (wg_key_uat_record_t
*)r
;
789 /* Check for valid base64-encoding. */
790 if (!decode_base64_key(&key
, rec
->key
)) {
791 *error
= g_strdup("Invalid key");
799 wg_key_uat_record_free_cb(void *r
)
801 wg_key_uat_record_t
*rec
= (wg_key_uat_record_t
*)r
;
806 wg_key_uat_apply(void)
808 if (!wg_decryption_supported
) {
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
);
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.
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
);
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.
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
) {
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 }};
893 dh_x25519(&dh1
, &hs
->responder_skey
->priv_key
, ephemeral
);
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
))) {
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
);
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
)) {
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 }};
921 dh_x25519(&dh2
, &hs
->responder_skey
->priv_key
, &hs
->initiator_skey
->pub_key
);
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
))) {
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
;
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");
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
) {
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
);
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
980 if (has_Epriv_i
&& has_Spriv_i
) {
981 dh_x25519(&dh1
, &hs
->initiator_ekey
->priv_key
, ephemeral
);
983 dh_x25519(&dh1
, &hs
->responder_ekey
->priv_key
, &hs
->initiator_ekey
->pub_key
);
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
990 if (has_Epriv_i
&& has_Spriv_i
) {
991 dh_x25519(&dh2
, &hs
->initiator_skey
->priv_key
, ephemeral
);
993 dh_x25519(&dh2
, &hs
->responder_ekey
->priv_key
, &hs
->initiator_skey
->pub_key
);
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
);
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. */
1011 hs
->empty_ok
= true;
1014 if (!hs
->empty_ok
) {
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]);
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
));
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
++;
1049 /* Updates the peer address based on the source address. */
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
;
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
));
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. */
1088 if (session
->response_frame
&& session
->response_frame
!= pinfo
->num
) {
1089 /* This session was linked elsewhere. */
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.
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
));
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. */
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;
1129 /* Both peers do not match the destination, ignore. */
1139 * Finds the static public key for the receiver of this message based on the
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) {
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
;
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
)) {
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
) {
1189 // Even if Spriv_r is available, store Epub_i for Response decryption.
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
);
1203 * Processes a Response message, storing additional keys in the state.
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).
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.
1230 // TODO this can result in loss of precision
1231 nstime
->secs
= (time_t)(tai64_label
- pow2_62
);
1232 nstime
->nsecs
= (int)nanoseconds
;
1237 wg_dissect_key_extra(proto_tree
*tree
, tvbuff_t
*tvb
, const wg_qqword
*pubkey
, bool is_ephemeral
)
1239 uint32_t has_private
= false;
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
);
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
);
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
);
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
);
1272 expert_add_info(NULL
, ti
, &ei_wg_decryption_unsupported
);
1277 wg_dissect_decrypted_static(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*wg_tree
, wg_handshake_state_t
*hs
)
1281 if (!hs
|| !hs
->initiator_skey
) {
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);
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
;
1299 if (!hs
|| !hs
->timestamp_ok
) {
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
);
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
;
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
);
1333 if (plain_length
== 0) {
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
);
1345 call_dissector(ip_handle
, new_tvb
, pinfo
, tree
);
1350 wg_dissect_mac1_pubkey(proto_tree
*tree
, tvbuff_t
*tvb
, const wg_skey_t
*skey
)
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
);
1366 wg_dissect_handshake_initiation(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*wg_tree
, wg_packet_info_t
*wg_pinfo
)
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
)) {
1377 hs
= wg_prepare_handshake_keys(skey_r
, tvb
);
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);
1404 wg_sessions_insert(sender_id
, session
);
1405 wg_pinfo
->session
= session
;
1407 wg_session_t
*session
= wg_pinfo
? wg_pinfo
->session
: NULL
;
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
);
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
;
1425 wg_session_t
*session
;
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
);
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. */
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
;
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
);
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
;
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
);
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 */
1497 session
= wg_pinfo
? wg_pinfo
->session
: NULL
;
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
);
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
;
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
);
1541 wg_session_update_address(session
, pinfo
, !receiver_is_initiator
);
1542 wg_pinfo
->session
= session
;
1543 wg_pinfo
->receiver_is_initiator
= receiver_is_initiator
;
1546 session
= wg_pinfo
? wg_pinfo
->session
: NULL
;
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
;
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;
1578 dissect_wg(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
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
)
1591 if (!wg_is_valid_message_length(message_type
, tvb_reported_length(tvb
))) {
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
);
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
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();
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)
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
))) {
1663 switch (message_type
) {
1664 case WG_TYPE_COOKIE_REPLY
:
1665 case WG_TYPE_TRANSPORT_DATA
:
1666 if (!reserved_is_zeroes
)
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
);
1691 wg_session_count
= 0;
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 */
1703 { "Type", "wg.type",
1704 FT_UINT8
, BASE_DEC
, VALS(wg_type_names
), 0x0,
1708 { "Reserved", "wg.reserved",
1709 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1713 { "Sender", "wg.sender",
1714 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
1715 "Identifier as chosen by the sender", HFILL
}
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
}
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,
1737 { &hf_wg_timestamp_tai64_label
,
1738 { "TAI64 Label", "wg.timestamp.tai64_label",
1739 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
1742 { &hf_wg_timestamp_nanoseconds
,
1743 { "Nanoseconds", "wg.timestamp.nanoseconds",
1744 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1747 { &hf_wg_timestamp_value
,
1748 { "Timestamp", "wg.timestamp.value",
1749 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
, NULL
, 0x0,
1753 { "mac1", "wg.mac1",
1754 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1758 { "mac2", "wg.mac2",
1759 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1763 /* Response message */
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 */
1782 { "Nonce", "wg.nonce",
1783 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1786 { &hf_wg_encrypted_cookie
,
1787 { "Encrypted Cookie", "wg.encrypted_cookie",
1788 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1791 /* TODO decrypted cookie field. */
1795 { "Counter", "wg.counter",
1796 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
1799 { &hf_wg_encrypted_packet
,
1800 { "Encrypted Packet", "wg.encrypted_packet",
1801 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1805 /* Association tracking. */
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
[] = {
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
}
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"),
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 */
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",
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
);
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
1959 * indent-tabs-mode: nil
1962 * vi: set shiftwidth=4 tabstop=8 expandtab:
1963 * :indentSize=4:tabSize=8:noTabs=true: