TLS13,QUIC: prepare for QUIC decryption

Add interface to expand the QUIC cleartext secrets
    (quic_derive_cleartext_secrets),
an interface to create the cleartext ciphers
    (quic_create_cleartext_decoders),
an interface to decrypt messages using this cipher
    (quic_decrypt_message).

Change-Id: Id546150be2964959388b7ef69984b891521e5caa
Reviewed-on: https://code.wireshark.org/review/24435
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
This commit is contained in:
Peter Wu 2017-11-15 06:41:45 +00:00 committed by Alexis La Goutte
parent 5a3addd8eb
commit 56f1feb678
3 changed files with 264 additions and 1 deletions

View File

@ -32,7 +32,12 @@
#include <epan/expert.h>
#include "packet-ssl-utils.h"
#include <epan/prefs.h>
#include <wsutil/pint.h>
#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */
/* Whether to provide support for authentication in addition to decryption. */
#define HAVE_LIBGCRYPT_AEAD
#endif
/* Prototypes */
void proto_reg_handoff_quic(void);
@ -746,6 +751,175 @@ dissect_quic_frame_type(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *quic_
return offset;
}
/* TLS 1.3 draft used by the draft-ietf-quic-tls-07 */
#define QUIC_TLS13_VERSION 21
#define QUIC_LONG_HEADER_LENGTH 17
#ifdef HAVE_LIBGCRYPT_AEAD
/**
* Given a QUIC message (header + non-empty payload), the actual packet number,
* try to decrypt it using the cipher.
*
* The actual packet number must be constructed according to
* https://tools.ietf.org/html/draft-ietf-quic-transport-07#section-5.7
*
* If decryption succeeds, the decrypted buffer is added as data source and
* returned. Otherwise NULL is returned and an error message is set.
*/
static tvbuff_t *
quic_decrypt_message(tls13_cipher *cipher, tvbuff_t *head, packet_info *pinfo, guint header_length, guint64 packet_number, const gchar **error)
{
gcry_error_t err;
guint8 header[QUIC_LONG_HEADER_LENGTH];
guint8 nonce[TLS13_AEAD_NONCE_LENGTH];
guint8 *buffer;
guint8 *atag[16];
guint buffer_length;
tvbuff_t *decrypted;
DISSECTOR_ASSERT(cipher != NULL);
DISSECTOR_ASSERT(header_length <= sizeof(header));
tvb_memcpy(head, header, 0, header_length);
/* Input is "header || ciphertext (buffer) || auth tag (16 bytes)" */
buffer_length = tvb_captured_length_remaining(head, header_length + 16);
if (buffer_length == 0) {
*error = "Decryption not possible, ciphertext is too short";
return NULL;
}
buffer = (guint8 *)tvb_memdup(pinfo->pool, head, header_length, buffer_length);
tvb_memcpy(head, atag, header_length + buffer_length, 16);
memcpy(nonce, cipher->iv, TLS13_AEAD_NONCE_LENGTH);
/* Packet number is left-padded with zeroes and XORed with write_iv */
phton64(nonce + sizeof(nonce) - 8, pntoh64(nonce + sizeof(nonce) - 8) ^ packet_number);
gcry_cipher_reset(cipher->hd);
err = gcry_cipher_setiv(cipher->hd, nonce, TLS13_AEAD_NONCE_LENGTH);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (setiv) failed: %s", gcry_strerror(err));
return NULL;
}
/* associated data (A) is the contents of QUIC header */
err = gcry_cipher_authenticate(cipher->hd, header, header_length);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (authenticate) failed: %s", gcry_strerror(err));
return NULL;
}
/* Output ciphertext (C) */
err = gcry_cipher_decrypt(cipher->hd, buffer, buffer_length, NULL, 0);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (decrypt) failed: %s", gcry_strerror(err));
return NULL;
}
err = gcry_cipher_checktag(cipher->hd, atag, 16);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (checktag) failed: %s", gcry_strerror(err));
return NULL;
}
decrypted = tvb_new_child_real_data(head, buffer, buffer_length, buffer_length);
add_new_data_source(pinfo, decrypted, "Decrypted QUIC");
*error = NULL;
return decrypted;
}
/**
* Compute the client and server cleartext secrets given Connection ID "cid".
*
* On success TRUE is returned and the two cleartext secrets are returned (these
* must be freed with wmem_free(NULL, ...)). FALSE is returned on error.
*/
static gboolean
quic_derive_cleartext_secrets(guint64 cid,
guint8 **client_cleartext_secret,
guint8 **server_cleartext_secret,
const gchar **error)
{
/*
* https://tools.ietf.org/html/draft-ietf-quic-tls-07#section-5.2.1
*
* quic_version_1_salt = afc824ec5fc77eca1e9d36f37fb2d46518c36639
*
* cleartext_secret = HKDF-Extract(quic_version_1_salt,
* client_connection_id)
*
* client_cleartext_secret =
* HKDF-Expand-Label(cleartext_secret,
* "QUIC client cleartext Secret",
* "", Hash.length)
* server_cleartext_secret =
* HKDF-Expand-Label(cleartext_secret,
* "QUIC server cleartext Secret",
* "", Hash.length)
* Hash for cleartext packets is SHA-256 (output size 32).
*/
static const guint8 quic_version_1_salt[20] = {
0xaf, 0xc8, 0x24, 0xec, 0x5f, 0xc7, 0x7e, 0xca, 0x1e, 0x9d,
0x36, 0xf3, 0x7f, 0xb2, 0xd4, 0x65, 0x18, 0xc3, 0x66, 0x39
};
guint tls13_draft_version = QUIC_TLS13_VERSION;
gcry_error_t err;
guint8 secret_bytes[HASH_SHA2_256_LENGTH];
StringInfo secret = { (guchar *) &secret_bytes, HASH_SHA2_256_LENGTH };
guint8 cid_bytes[8];
phton64(cid_bytes, cid);
err = hkdf_extract(GCRY_MD_SHA256, quic_version_1_salt, sizeof(quic_version_1_salt),
cid_bytes, sizeof(cid_bytes), secret.data);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Failed to extract secrets: %s", gcry_strerror(err));
return FALSE;
}
if (!tls13_hkdf_expand_label(tls13_draft_version, GCRY_MD_SHA256, &secret, "QUIC client cleartext Secret",
"", HASH_SHA2_256_LENGTH, client_cleartext_secret)) {
*error = "Key expansion (client) failed";
return FALSE;
}
if (!tls13_hkdf_expand_label(tls13_draft_version, GCRY_MD_SHA256, &secret, "QUIC server cleartext Secret",
"", HASH_SHA2_256_LENGTH, server_cleartext_secret)) {
wmem_free(NULL, client_cleartext_secret);
*client_cleartext_secret = NULL;
*error = "Key expansion (server) failed";
return FALSE;
}
*error = NULL;
return TRUE;
}
static gboolean
quic_create_cleartext_decoders(guint64 cid, const gchar **error)
{
tls13_cipher *client_cipher, *server_cipher;
StringInfo client_secret = { NULL, HASH_SHA2_256_LENGTH };
StringInfo server_secret = { NULL, HASH_SHA2_256_LENGTH };
/* TODO extract from packet/conversation */
if (!quic_derive_cleartext_secrets(cid, &client_secret.data, &server_secret.data, error)) {
/* TODO handle error (expert info) */
return FALSE;
}
/* Cleartext packets are protected with AEAD_AES_128_GCM */
client_cipher = tls13_cipher_create(QUIC_TLS13_VERSION, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_MD_SHA256, &client_secret, error);
server_cipher = tls13_cipher_create(QUIC_TLS13_VERSION, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_MD_SHA256, &server_secret, error);
if (!client_cipher || !server_cipher) {
return FALSE;
}
/* TODO store ciphers in conversation, handle errors (expert info) */
return TRUE;
}
#endif /* HAVE_LIBGCRYPT_AEAD */
static int
dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, guint offset, quic_info_data_t *quic_info){
guint32 long_packet_type, pkn;

View File

@ -2091,6 +2091,7 @@ ssl_cipher_cleanup(gcry_cipher_hd_t *cipher)
gcry_cipher_close(*cipher);
*cipher = NULL;
}
/* }}} */
/* Digests, Ciphers and Cipher Suites registry {{{ */
static const SslDigestAlgo digests[]={
@ -3001,6 +3002,65 @@ ssl_decoder_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U
return FALSE;
}
static gboolean
tls13_cipher_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, void *user_data)
{
tls13_cipher *cipher = (tls13_cipher *) user_data;
gcry_cipher_close(cipher->hd);
return FALSE;
}
tls13_cipher *
tls13_cipher_create(guint8 tls13_draft_version, int cipher_algo, int cipher_mode, int hash_algo, StringInfo *secret, const gchar **error)
{
tls13_cipher *cipher = NULL;
guchar *write_key = NULL, *write_iv = NULL;
guint key_length, iv_length;
gcry_cipher_hd_t hd = NULL;
gcry_error_t err;
/*
* Calculate traffic keys based on
* https://tools.ietf.org/html/draft-ietf-tls-tls13-21#section-7
*/
key_length = (guint) gcry_cipher_get_algo_keylen(cipher_algo);
iv_length = TLS13_AEAD_NONCE_LENGTH;
if (!tls13_hkdf_expand_label(tls13_draft_version, hash_algo, secret, "key", "", key_length, &write_key)) {
*error = "Key expansion (key) failed";
return NULL;
}
if (!tls13_hkdf_expand_label(tls13_draft_version, hash_algo, secret, "iv", "", iv_length, &write_iv)) {
*error = "Key expansion (IV) failed";
goto end;
}
err = gcry_cipher_open(&hd, cipher_algo, cipher_mode, 0);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (initialization) failed: %s", gcry_strerror(err));
goto end;
}
err = gcry_cipher_setkey(hd, write_key, key_length);
if (err) {
*error = wmem_strdup_printf(wmem_packet_scope(), "Decryption (setkey) failed: %s", gcry_strerror(err));
gcry_cipher_close(hd);
goto end;
}
cipher = wmem_new(wmem_file_scope(), tls13_cipher);
wmem_register_callback(wmem_file_scope(), tls13_cipher_destroy_cb, cipher);
cipher->hd = hd;
memcpy(cipher->iv, write_iv, iv_length);
*error = NULL;
end:
wmem_free(NULL, write_key);
wmem_free(NULL, write_iv);
return cipher;
}
/* }}} */
/* (Pre-)master secrets calculations {{{ */
@ -4209,6 +4269,7 @@ skip_mac:
#if defined(HAVE_LIBGNUTLS)
/* RSA private key file processing {{{ */
static void
ssl_find_private_key_by_pubkey(SslDecryptSession *ssl, GHashTable *key_hash,
gnutls_datum_t *subjectPublicKeyInfo)
@ -4256,7 +4317,6 @@ end:
/* RSA private key file processing }}} */
#endif /* ! defined(HAVE_LIBGNUTLS) */
/*--- Start of dissector-related code below ---*/
/* get ssl data for this session. if no ssl data is found allocate a new one*/

View File

@ -313,6 +313,7 @@ typedef enum {
/* Explicit and implicit nonce length (RFC 5116 - Section 3.2.1) */
#define IMPLICIT_NONCE_LEN 4
#define EXPLICIT_NONCE_LEN 8
#define TLS13_AEAD_NONCE_LENGTH 12
/* TLS 1.3 Record type for selecting the appropriate secret. */
typedef enum {
@ -358,6 +359,15 @@ typedef struct _SslDecoder {
StringInfo app_traffic_secret; /**< TLS 1.3 application traffic secret (if applicable), wmem file scope. */
} SslDecoder;
/*
* TLS 1.3 Cipher context. Simpler than SslDecoder since no compression is
* required and all keys are calculated internally.
*/
typedef struct {
gcry_cipher_hd_t hd;
guint8 iv[TLS13_AEAD_NONCE_LENGTH];
} tls13_cipher;
#define KEX_DHE_DSS 0x10
#define KEX_DHE_PSK 0x11
#define KEX_DHE_RSA 0x12
@ -622,6 +632,25 @@ extern gint
ssl_decrypt_record(SslDecryptSession *ssl, SslDecoder *decoder, guint8 ct, guint16 record_version,
const guchar *in, guint16 inl, StringInfo *comp_str, StringInfo *out_str, guint *outl);
/**
* Given a cipher algorithm and its mode, a hash algorithm and the secret (with
* the same length as the hash algorithm), try to build a cipher. The algorithms
* and mode are Libgcrypt identifiers.
*/
tls13_cipher *
tls13_cipher_create(guint8 tls13_draft_version, int cipher_algo, int cipher_mode, int hash_algo, StringInfo *secret, const gchar **error);
/*
* Calculate HKDF-Extract(salt, IKM) -> PRK according to RFC 5869.
* Caller must ensure that 'prk' is large enough to store the digest.
*/
static inline gcry_error_t
hkdf_extract(int algo, const guint8 *salt, size_t salt_len, const guint8 *ikm, size_t ikm_len, guint8 *prk)
{
/* PRK = HMAC-Hash(salt, IKM) where salt is key, and IKM is input. */
return ws_hmac_buffer(algo, prk, ikm, ikm_len, salt, salt_len);
}
/* Common part bitween SSL and DTLS dissectors */
/* Hash Functions for RSA private keys table */