aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2018-08-01 02:10:40 +0200
committerAnders Broman <a.broman58@gmail.com>2018-08-08 11:26:06 +0000
commite7549372515dbef74f4764a36a6b1a408087cf59 (patch)
treefb4f35df9b6e87184732d587214d8ffc39975353 /epan
parent50a0a710c4860084a129537d600b0180c4e6f0db (diff)
WireGuard: implement decryption with PSKs
This imposes an additional requirement on the key log file, PSKs are only linked to the most recently seen ephemeral key. This means that the key log might contain duplicate PSK lines, but at least the dissector won't have to try all keys and thereby save CPU time. Bug: 15011 Change-Id: I368fa16269c96c4a1ff3bcb4e376c21f38fa2689 Reviewed-on: https://code.wireshark.org/review/28993 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan')
-rw-r--r--epan/dissectors/packet-wireguard.c124
1 files changed, 113 insertions, 11 deletions
diff --git a/epan/dissectors/packet-wireguard.c b/epan/dissectors/packet-wireguard.c
index d4cdea8166..1d8192a14f 100644
--- a/epan/dissectors/packet-wireguard.c
+++ b/epan/dissectors/packet-wireguard.c
@@ -121,11 +121,26 @@ typedef struct wg_skey {
} wg_skey_t;
/*
+ * Pre-shared key, needed while processing the handshake response message. At
+ * that point, ephemeral keys (from either the initiator or responder) should be
+ * known. Thus link the PSK to such ephemeral keys.
+ *
+ * Usually a "wg_ekey_t" contains an empty list (if there is no PSK, i.e. an
+ * all-zeroes PSK) or one item (if a PSK is configured). In the unlikely event
+ * that an ephemeral key is reused, support more than one PSK.
+ */
+typedef struct wg_psk {
+ wg_qqword psk_data;
+ struct wg_psk *next;
+} wg_psk_t;
+
+/*
* Ephemeral key.
*/
typedef struct wg_ekey {
wg_qqword pub_key;
wg_qqword priv_key; /* Optional, set to all zeroes if missing. */
+ wg_psk_t *psk_list; /* Optional, possible PSKs to try. */
} wg_ekey_t;
/*
@@ -148,6 +163,29 @@ static wmem_map_t *wg_ephemeral_keys;
*/
static FILE *wg_keylog_file;
+/*
+ * The most recently parsed ephemeral key. If a PSK is configured, the key log
+ * file must have a PSK line after other keys. If not, then it is assumed that
+ * the session does not use a PSK.
+ *
+ * This pointer is cleared when the key log file is reset (i.e. when the capture
+ * file closes).
+ */
+static wg_ekey_t *wg_keylog_last_ekey;
+
+enum wg_psk_iter_state {
+ WG_PSK_ITER_STATE_ENTER = 0,
+ WG_PSK_ITER_STATE_INITIATOR,
+ WG_PSK_ITER_STATE_RESPONDER,
+ WG_PSK_ITER_STATE_EXIT
+};
+
+/* See wg_psk_iter_next. */
+typedef struct {
+ enum wg_psk_iter_state state;
+ wg_psk_t *next_psk;
+} wg_psk_iter_context;
+
/* UAT adapter for populating wg_static_keys. */
enum { WG_KEY_UAT_PUBLIC, WG_KEY_UAT_PRIVATE };
static const value_string wg_key_uat_type_vals[] = {
@@ -531,6 +569,54 @@ wg_add_ephemeral_privkey(const wg_qqword *priv_key)
return key;
}
+/* PSK handling. {{{ */
+static void
+wg_add_psk(wg_ekey_t *ekey, const wg_qqword *psk)
+{
+ wg_psk_t *psk_entry = wmem_new0(wmem_file_scope(), wg_psk_t);
+ psk_entry->psk_data = *psk;
+ psk_entry->next = ekey->psk_list;
+ ekey->psk_list = psk_entry;
+}
+
+/*
+ * Retrieves the next PSK to try and returns TRUE if one is found or FALSE if
+ * there are no more to try.
+ */
+static gboolean
+wg_psk_iter_next(wg_psk_iter_context *psk_iter, const wg_handshake_state_t *hs,
+ wg_qqword *psk_out)
+{
+ wg_psk_t *psk = psk_iter->next_psk;
+ while (!psk) {
+ /*
+ * Yield PSKs based on Epub_i, then those based on Epub_r, then yield an
+ * all-zeroes key and finally fail in the terminating state.
+ */
+ switch (psk_iter->state) {
+ case WG_PSK_ITER_STATE_ENTER:
+ psk = hs->initiator_ekey->psk_list;
+ psk_iter->state = WG_PSK_ITER_STATE_INITIATOR;
+ break;
+ case WG_PSK_ITER_STATE_INITIATOR:
+ psk = hs->responder_ekey->psk_list;
+ psk_iter->state = WG_PSK_ITER_STATE_RESPONDER;
+ break;
+ case WG_PSK_ITER_STATE_RESPONDER:
+ memset(psk_out->data, 0, WG_KEY_LEN);
+ psk_iter->state = WG_PSK_ITER_STATE_EXIT;
+ return TRUE;
+ case WG_PSK_ITER_STATE_EXIT:
+ return FALSE;
+ }
+ }
+
+ *psk_out = psk->psk_data;
+ psk_iter->next_psk = psk->next;
+ return TRUE;
+}
+/* PSK handling. }}} */
+
/* UAT and key configuration. {{{ */
/* XXX this is copied verbatim from packet-ssl-utils.c - create new common API
* for retrieval of runtime secrets? */
@@ -560,6 +646,7 @@ wg_keylog_reset(void)
if (wg_keylog_file) {
fclose(wg_keylog_file);
wg_keylog_file = NULL;
+ wg_keylog_last_ekey = NULL;
}
}
@@ -654,9 +741,15 @@ wg_keylog_read(void)
} else if (!strcmp(key_type, "REMOTE_STATIC_PUBLIC_KEY")) {
wg_add_static_key(&key, FALSE);
} else if (!strcmp(key_type, "LOCAL_EPHEMERAL_PRIVATE_KEY")) {
- wg_add_ephemeral_privkey(&key);
+ wg_keylog_last_ekey = wg_add_ephemeral_privkey(&key);
} else if (!strcmp(key_type, "PRESHARED_KEY")) {
- // TODO
+ /* Link the PSK to the last ephemeral key. */
+ if (wg_keylog_last_ekey) {
+ wg_add_psk(wg_keylog_last_ekey, &key);
+ wg_keylog_last_ekey = NULL;
+ } else {
+ g_debug("Ignored PSK as no new ephemeral key was found");
+ }
} else {
g_debug("Unrecognized key log line: %s", buf);
}
@@ -864,17 +957,26 @@ wg_process_response(tvbuff_t *tvb, wg_handshake_state_t *hs)
}
// c = KDF1(c, dh2)
wg_kdf(c, dh2.data, sizeof(dh2), 1, c);
- // c, t, k = KDF3(c, PSK)
- // TODO apply PSK from keylog file
- wg_qqword psk = {{ 0 }};
- wg_kdf(c, psk.data, WG_KEY_LEN, 3, ctk);
- // h = Hash(h || t)
- wg_mix_hash(&h, t, sizeof(wg_qqword));
- // empty = AEAD-Decrypt(k, 0, msg.empty, h)
- if (!aead_decrypt(k, 0, encrypted_empty, AUTH_TAG_LENGTH, h.data, sizeof(wg_qqword), NULL, 0)) {
+ wg_qqword h_before_psk = h, c_before_psk = *c, psk;
+ wg_psk_iter_context psk_iter = { WG_PSK_ITER_STATE_ENTER, NULL };
+ while (wg_psk_iter_next(&psk_iter, hs, &psk)) {
+ // c, t, k = KDF3(c, PSK)
+ wg_kdf(c, psk.data, WG_KEY_LEN, 3, ctk);
+ // h = Hash(h || t)
+ wg_mix_hash(&h, t, sizeof(wg_qqword));
+ // empty = AEAD-Decrypt(k, 0, msg.empty, h)
+ if (!aead_decrypt(k, 0, encrypted_empty, AUTH_TAG_LENGTH, h.data, sizeof(wg_qqword), NULL, 0)) {
+ /* Possibly bad PSK, reset and try another. */
+ h = h_before_psk;
+ *c = c_before_psk;
+ continue;
+ }
+ hs->empty_ok = TRUE;
+ break;
+ }
+ if (!hs->empty_ok) {
return;
}
- hs->empty_ok = TRUE;
// h = Hash(h || msg.empty)
wg_mix_hash(&h, encrypted_empty, AUTH_TAG_LENGTH);