TLS13: implement trial decryption for early data
authorPeter Wu <peter@lekensteyn.nl>
Mon, 12 Mar 2018 15:10:04 +0000 (16:10 +0100)
committerAlexis La Goutte <alexis.lagoutte@gmail.com>
Thu, 15 Mar 2018 06:02:18 +0000 (06:02 +0000)
Rather than relying on the advertised ciphers in the Client Hello (which
might not match the early data cipher), try all TLS 1.3 ciphers when the
0rtt secret is available.

Whenever the client advertises the "early_data" extension, we will try
to decrypt it when keys are available. This is tried before decrypting
normal handshake/application data because a server might reject early
data and then no End Of Early Data (EOED) message is available. Care is
taken to decrypt as much 0RTT data as possible, only when when EOED is
seen *or* when 0RTT decryption fails, then it will switch to HS secrets.

Requires at least Libgcrypt 1.6 for verifying the auth tags, otherwise
it cannot recognize whether the "decrypted" result is correct.

Since the negotiated draft version is not known during Client Hello,
rely on heuristics to guess the actual draft. This is relevant since the
key expansion changed in draft 20. (Test with comment 56 in bug 12779.)

Change-Id: Ied3f2b4b9f38d1280a6158c3a3aff8296c035fc3
Ping-Bug: 12779
Bug: 14308
Reviewed-on: https://code.wireshark.org/review/26445
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
epan/dissectors/packet-ssl-utils.c
epan/dissectors/packet-ssl-utils.h
epan/dissectors/packet-ssl.c

index 2501a49abe066fbc5161dbbd3b4b121ef9803c0c..3438b815df82ddea0a04ae34c40ea088f12b0639 100644 (file)
@@ -6161,7 +6161,8 @@ ssl_dissect_hnd_hello_ext_early_data(ssl_common_dissect_t *hf, tvbuff_t *tvb, pa
 
 static gint
 ssl_dissect_hnd_hello_ext_supported_versions(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo,
-                                             proto_tree *tree, guint32 offset, guint32 offset_end)
+                                             proto_tree *tree, guint32 offset, guint32 offset_end,
+                                             SslSession *session)
 {
 
    /* https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.1
@@ -6178,14 +6179,25 @@ ssl_dissect_hnd_hello_ext_supported_versions(ssl_common_dissect_t *hf, tvbuff_t
     offset++;
     next_offset = offset + versions_length;
 
+    guint version;
+    guint8 draft_version, max_draft_version = 0;
     while (offset + 2 <= next_offset) {
-        proto_tree_add_item(tree, hf->hf.hs_ext_supported_version, tvb, offset, 2, ENC_BIG_ENDIAN);
+        proto_tree_add_item_ret_uint(tree, hf->hf.hs_ext_supported_version, tvb, offset, 2, ENC_BIG_ENDIAN, &version);
         offset += 2;
+
+        draft_version = tls13_draft_version(version);
+        max_draft_version = MAX(draft_version, max_draft_version);
     }
     if (!ssl_end_vector(hf, tvb, pinfo, tree, offset, next_offset)) {
         offset = next_offset;
     }
 
+    /* XXX remove this when draft 19 support is dropped,
+     * this is only required for early data decryption. */
+    if (max_draft_version) {
+        session->tls13_draft_version = max_draft_version;
+    }
+
     return offset;
 }
 
@@ -7322,13 +7334,6 @@ ssl_dissect_hnd_cli_hello(ssl_common_dissect_t *hf, tvbuff_t *tvb,
     }
     offset += 2;
     next_offset = offset + cipher_suite_length;
-    if (ssl && cipher_suite_length == 2) {
-        /*
-         * If there is only a single cipher, assume that this will be used
-         * (needed for 0-RTT decryption support in TLS 1.3).
-         */
-        ssl_set_cipher(ssl, tvb_get_ntohs(tvb, offset));
-    }
     ti = proto_tree_add_none_format(tree,
                                     hf->hf.hs_cipher_suites,
                                     tvb, offset, cipher_suite_length,
@@ -8147,7 +8152,7 @@ ssl_dissect_hnd_extension(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *t
         case SSL_HND_HELLO_EXT_SUPPORTED_VERSIONS:
             switch (hnd_type) {
             case SSL_HND_CLIENT_HELLO:
-                offset = ssl_dissect_hnd_hello_ext_supported_versions(hf, tvb, pinfo, ext_tree, offset, next_offset);
+                offset = ssl_dissect_hnd_hello_ext_supported_versions(hf, tvb, pinfo, ext_tree, offset, next_offset, session);
                 break;
             case SSL_HND_SERVER_HELLO:
             case SSL_HND_HELLO_RETRY_REQUEST:
index 33d7ebc7d99beb73858a4c28d061d571926d944d..2b7b79305ce977a1bce59f4a559faeb38a991779 100644 (file)
@@ -291,6 +291,7 @@ static inline guint8 tls13_draft_version(guint32 version) {
 #define SSL_SERVER_EXTENDED_MASTER_SECRET (1<<8)
 #define SSL_NEW_SESSION_TICKET  (1<<10)
 #define SSL_ENCRYPT_THEN_MAC    (1<<11)
+#define SSL_SEEN_0RTT_APPDATA   (1<<12)
 
 #define SSL_EXTENDED_MASTER_SECRET_MASK (SSL_CLIENT_EXTENDED_MASTER_SECRET|SSL_SERVER_EXTENDED_MASTER_SECRET)
 
index a23655f364540c957f34d8d76390112fd7ac9d25..0416b991dbebb7fa0be4375466db17b095fcd442 100644 (file)
 #include "packet-ssl.h"
 #include "packet-ssl-utils.h"
 #include "packet-ber.h"
+#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */
+/* Whether to provide support for authentication in addition to decryption. */
+#define HAVE_LIBGCRYPT_AEAD
+#endif
 
 void proto_register_ssl(void);
 
@@ -1067,6 +1071,83 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, SslDecryp
     return success;
 }
 
+#ifdef HAVE_LIBGCRYPT_AEAD
+/**
+ * Try to guess the early data cipher using trial decryption.
+ * Requires Libgcrypt 1.6 or newer for verifying that decryption is successful.
+ */
+static gboolean
+decrypt_tls13_early_data(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
+                         guint16 record_length, SslDecryptSession *ssl,
+                         guint8 curr_layer_num_ssl)
+
+{
+    gboolean        success = FALSE;
+
+    ssl_debug_printf("Trying early data encryption, first record / trial decryption: %s\n",
+                    !(ssl->state & SSL_SEEN_0RTT_APPDATA) ? "true" : "false");
+
+    /* Only try trial decryption for the first record. */
+    if (ssl->state & SSL_SEEN_0RTT_APPDATA) {
+        if (!ssl->client) {
+            return FALSE;       // sanity check, should not happen in valid captures.
+        }
+
+        ssl_decrypted_data_avail = ssl_decrypted_data.data_len;
+        success = ssl_decrypt_record(ssl, ssl->client, SSL_ID_APP_DATA, 0x303, FALSE,
+                                     tvb_get_ptr(tvb, offset, record_length), record_length,
+                                     &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0;
+        if (success) {
+            tls_save_decrypted_record(pinfo, tvb_raw_offset(tvb)+offset, ssl, SSL_ID_APP_DATA, ssl->client, TRUE, curr_layer_num_ssl);
+        } else {
+            ssl_debug_printf("early data decryption failed, end of early data?\n");
+        }
+        return success;
+    }
+    ssl->state |= SSL_SEEN_0RTT_APPDATA;
+
+    ssl_load_keyfile(ssl_options.keylog_filename, &ssl_keylog_file, &ssl_master_key_map);
+    StringInfo *secret = tls13_load_secret(ssl, &ssl_master_key_map, FALSE, TLS_SECRET_0RTT_APP);
+    if (!secret) {
+        ssl_debug_printf("Missing secrets, early data decryption not possible!\n");
+        return FALSE;
+    }
+
+    const guint16 tls13_ciphers[] = {
+        0x1301, /* TLS_AES_128_GCM_SHA256 */
+        0x1302, /* TLS_AES_256_GCM_SHA384 */
+        0x1303, /* TLS_CHACHA20_POLY1305_SHA256 */
+        0x1304, /* TLS_AES_128_CCM_SHA256 */
+        0x1305, /* TLS_AES_128_CCM_8_SHA256 */
+    };
+    const guchar   *record = tvb_get_ptr(tvb, offset, record_length);
+    for (guint i = 0; i < G_N_ELEMENTS(tls13_ciphers); i++) {
+        guint16 cipher = tls13_ciphers[i];
+
+        ssl_debug_printf("Performing early data trial decryption, cipher = %#x\n", cipher);
+        ssl->session.cipher = cipher;
+        ssl->cipher_suite = ssl_find_cipher(cipher);
+        if (!tls13_generate_keys(ssl, secret, FALSE)) {
+            /* Unable to create cipher (old Libgcrypt) */
+            continue;
+        }
+
+        ssl_decrypted_data_avail = ssl_decrypted_data.data_len;
+        success = ssl_decrypt_record(ssl, ssl->client, SSL_ID_APP_DATA, 0x303, FALSE, record, record_length,
+                                     &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0;
+        if (success) {
+            ssl_debug_printf("Early data decryption succeeded, cipher = %#x\n", cipher);
+            tls_save_decrypted_record(pinfo, tvb_raw_offset(tvb)+offset, ssl, SSL_ID_APP_DATA, ssl->client, TRUE, curr_layer_num_ssl);
+            break;
+        }
+    }
+    if (!success) {
+        ssl_debug_printf("Trial decryption of early data failed!\n");
+    }
+    return success;
+}
+#endif
+
 static void
 process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo,
                     proto_tree *tree, SslSession *session,
@@ -1803,11 +1884,32 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
      * In TLS 1.3, only "Application Data" records are encrypted.
      */
     if (ssl && (session->version != TLSV1DOT3_VERSION || content_type == SSL_ID_APP_DATA)) {
-        decrypt_ssl3_record(tvb, pinfo, offset, ssl,
+        gboolean    decrypt_ok = FALSE;
+
+        /* Try to decrypt TLS 1.3 early data first */
+        if (session->version == TLSV1DOT3_VERSION && content_type == SSL_ID_APP_DATA &&
+            ssl->has_early_data && !ssl_packet_from_server(session, ssl_associations, pinfo)) {
+#ifdef HAVE_LIBGCRYPT_AEAD
+            decrypt_ok = decrypt_tls13_early_data(tvb, pinfo, offset, record_length, ssl, curr_layer_num_ssl);
+#endif
+            if (!decrypt_ok) {
+                /* Either trial decryption failed (e.g. missing key) or end of
+                 * early data is reached. Switch to HS secrets if available. */
+                if (ssl->state & SSL_SERVER_RANDOM) {
+                    tls13_change_key(ssl, &ssl_master_key_map, FALSE, TLS_SECRET_HANDSHAKE);
+                }
+                ssl->has_early_data = FALSE;
+            }
+        }
+
+        if (!decrypt_ok) {
+            decrypt_ssl3_record(tvb, pinfo, offset, ssl,
                 content_type, record_version, record_length,
                 content_type == SSL_ID_APP_DATA ||
                 content_type == SSL_ID_HANDSHAKE, curr_layer_num_ssl);
+        }
     }
+
     /* try to retrieve and use decrypted alert/handshake/appdata record, if any. */
     decrypted = ssl_get_record_info(tvb, proto_ssl, pinfo, tvb_raw_offset(tvb)+offset, curr_layer_num_ssl, &record);
     if (decrypted) {
@@ -2196,8 +2298,6 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                     session->version = TLSV1DOT3_VERSION;
                     ssl->state |= SSL_VERSION;
                     ssl_debug_printf("%s forcing version 0x%04X -> state 0x%02X\n", G_STRFUNC, version, ssl->state);
-                    ssl_load_keyfile(ssl_options.keylog_filename, &ssl_keylog_file, &ssl_master_key_map);
-                    tls13_change_key(ssl, &ssl_master_key_map, FALSE, TLS_SECRET_0RTT_APP);
                 }
                 break;
 
@@ -2206,8 +2306,11 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                         offset, offset + length, session, ssl, FALSE, is_hrr);
                 if (ssl) {
                     ssl_load_keyfile(ssl_options.keylog_filename, &ssl_keylog_file, &ssl_master_key_map);
-                    /* Create client and server decoders for TLS 1.3. */
-                    if (!ssl->has_early_data) {
+                    /* Create client and server decoders for TLS 1.3.
+                     * Create client decoder based on HS secret only if there is
+                     * no early data, or if there is no decryptable early data. */
+                    if (!ssl->has_early_data ||
+                        ((ssl->state & SSL_SEEN_0RTT_APPDATA) && !ssl->client)) {
                         tls13_change_key(ssl, &ssl_master_key_map, FALSE, TLS_SECRET_HANDSHAKE);
                     }
                     tls13_change_key(ssl, &ssl_master_key_map, TRUE, TLS_SECRET_HANDSHAKE);