diff options
author | Peter Wu <peter@lekensteyn.nl> | 2018-09-12 23:49:15 +0200 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2018-09-13 21:26:24 +0000 |
commit | aedf4a7dd7755eb825409bb506084a5595fb05b6 (patch) | |
tree | 5a44e04b34b63f5732f844d0ea12d15982beb528 | |
parent | 81710c7d3cb6eb8ec1a38e42d8ea1aff19f732b5 (diff) |
QUIC: use HKDF-Expand-Label (draft -13)
Prepare for draft -13 support. A comment references draft -14 since the
draft -13 text is wrong (it lacks the context argument).
Change-Id: I3f2c59d2640693959372ebe0de3b58f67b5588b9
Ping-Bug: 13881
Reviewed-on: https://code.wireshark.org/review/29640
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
-rw-r--r-- | epan/dissectors/packet-quic.c | 89 |
1 files changed, 68 insertions, 21 deletions
diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c index 3ab6ab375b..5262c85ea6 100644 --- a/epan/dissectors/packet-quic.c +++ b/epan/dissectors/packet-quic.c @@ -1130,7 +1130,7 @@ qhkdf_expand(int md, const guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len); static gboolean -quic_cipher_init(quic_cipher *cipher, int hash_algo, guint8 key_length, guint8 *secret); +quic_cipher_init(guint32 version, quic_cipher *cipher, int hash_algo, guint8 key_length, guint8 *secret); /** @@ -1209,6 +1209,19 @@ quic_decrypt_message(quic_cipher *cipher, tvbuff_t *head, guint header_length, g result->data_len = buffer_length; } +static gboolean +quic_hkdf_expand_label(int hash_algo, guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len) +{ + const StringInfo secret_si = { secret, secret_len }; + guchar *out_mem = NULL; + if (tls13_hkdf_expand_label(hash_algo, &secret_si, "quic ", label, out_len, &out_mem)) { + memcpy(out, out_mem, out_len); + wmem_free(NULL, out_mem); + return TRUE; + } + return FALSE; +} + /** * Compute the client and server handshake secrets given Connection ID "cid". * @@ -1219,7 +1232,7 @@ static gboolean quic_derive_handshake_secrets(const quic_cid_t *cid, guint8 client_handshake_secret[HASH_SHA2_256_LENGTH], guint8 server_handshake_secret[HASH_SHA2_256_LENGTH], - quic_info_data_t *quic_info _U_, + quic_info_data_t *quic_info, const gchar **error) { /* @@ -1234,6 +1247,16 @@ quic_derive_handshake_secrets(const quic_cid_t *cid, * server_handshake_secret = * QHKDF-Expand(handshake_secret, "server hs", Hash.length) * + * https://tools.ietf.org/html/draft-ietf-quic-tls-14#section-5.1.1 + * + * initial_salt = 0x9c108f98520a5c5c32968e950e8a2c5fe06d6c38 + * initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id) + * + * client_initial_secret = HKDF-Expand-Label(initial_secret, + * "client in", "", Hash.length) + * server_initial_secret = HKDF-Expand-Label(initial_secret, + * "server in", "", Hash.length) + * * Hash for handshake packets is SHA-256 (output size 32). */ static const guint8 handshake_salt[20] = { @@ -1250,16 +1273,30 @@ quic_derive_handshake_secrets(const quic_cid_t *cid, return FALSE; } - if (qhkdf_expand(GCRY_MD_SHA256, secret, sizeof(secret), "client hs", - client_handshake_secret, HASH_SHA2_256_LENGTH)) { - *error = "Key expansion (client) failed"; - return FALSE; - } + if (is_quic_draft_max(quic_info->version, 12)) { + if (qhkdf_expand(GCRY_MD_SHA256, secret, sizeof(secret), "client hs", + client_handshake_secret, HASH_SHA2_256_LENGTH)) { + *error = "Key expansion (client) failed"; + return FALSE; + } - if (qhkdf_expand(GCRY_MD_SHA256, secret, sizeof(secret), "server hs", - server_handshake_secret, HASH_SHA2_256_LENGTH)) { - *error = "Key expansion (server) failed"; - return FALSE; + if (qhkdf_expand(GCRY_MD_SHA256, secret, sizeof(secret), "server hs", + server_handshake_secret, HASH_SHA2_256_LENGTH)) { + *error = "Key expansion (server) failed"; + return FALSE; + } + } else { + if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "client in", + client_handshake_secret, HASH_SHA2_256_LENGTH)) { + *error = "Key expansion (client) failed"; + return FALSE; + } + + if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "server in", + server_handshake_secret, HASH_SHA2_256_LENGTH)) { + *error = "Key expansion (server) failed"; + return FALSE; + } } *error = NULL; @@ -1294,6 +1331,7 @@ quic_create_handshake_decoders(const quic_cid_t *cid, const gchar **error, quic_ { guint8 client_secret[HASH_SHA2_256_LENGTH]; guint8 server_secret[HASH_SHA2_256_LENGTH]; + guint32 version = quic_info->version; if (!quic_derive_handshake_secrets(cid, client_secret, server_secret, quic_info, error)) { return FALSE; @@ -1322,8 +1360,8 @@ quic_create_handshake_decoders(const quic_cid_t *cid, const gchar **error, quic_ } guint cipher_keylen = (guint8) gcry_cipher_get_algo_keylen(GCRY_CIPHER_AES128); - if (!quic_cipher_init(&quic_info->client_handshake_cipher, GCRY_MD_SHA256, cipher_keylen, client_secret) || - !quic_cipher_init(&quic_info->server_handshake_cipher, GCRY_MD_SHA256, cipher_keylen, server_secret)) { + if (!quic_cipher_init(version, &quic_info->client_handshake_cipher, GCRY_MD_SHA256, cipher_keylen, client_secret) || + !quic_cipher_init(version, &quic_info->server_handshake_cipher, GCRY_MD_SHA256, cipher_keylen, server_secret)) { *error = "Failed to derive key material for cipher"; return FALSE; } @@ -1390,7 +1428,7 @@ quic_get_pp0_secret(packet_info *pinfo, int hash_algo, quic_pp_state_t *pp_state * and initialize cipher with the new key. */ static gboolean -quic_cipher_init(quic_cipher *cipher, int hash_algo, guint8 key_length, guint8 *secret) +quic_cipher_init(guint32 version, quic_cipher *cipher, int hash_algo, guint8 key_length, guint8 *secret) { guchar write_key[256/8]; /* Maximum key size is for AES256 cipher. */ guchar pn_key[256/8]; @@ -1400,10 +1438,18 @@ quic_cipher_init(quic_cipher *cipher, int hash_algo, guint8 key_length, guint8 * return FALSE; } - if (qhkdf_expand(hash_algo, secret, hash_len, "key", write_key, key_length) || - qhkdf_expand(hash_algo, secret, hash_len, "iv", cipher->pp_iv, sizeof(cipher->pp_iv)) || - qhkdf_expand(hash_algo, secret, hash_len, "pn", pn_key, key_length)) { - return FALSE; + if (is_quic_draft_max(version, 12)) { + if (qhkdf_expand(hash_algo, secret, hash_len, "key", write_key, key_length) || + qhkdf_expand(hash_algo, secret, hash_len, "iv", cipher->pp_iv, sizeof(cipher->pp_iv)) || + qhkdf_expand(hash_algo, secret, hash_len, "pn", pn_key, key_length)) { + return FALSE; + } + } else { + if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "key", write_key, key_length) || + !quic_hkdf_expand_label(hash_algo, secret, hash_len, "iv", cipher->pp_iv, sizeof(cipher->pp_iv)) || + !quic_hkdf_expand_label(hash_algo, secret, hash_len, "pn", pn_key, key_length)) { + return FALSE; + } } return gcry_cipher_setkey(cipher->pn_cipher, pn_key, key_length) == 0 && @@ -1430,6 +1476,7 @@ static quic_cipher * quic_get_pp_cipher(packet_info *pinfo, gboolean key_phase, guint64 pkn, quic_info_data_t *quic_info, gboolean from_server) { gboolean needs_key_update = FALSE; + guint32 version = quic_info->version; /* Keys were previously not available. */ if (quic_info->skip_decryption) { @@ -1479,8 +1526,8 @@ quic_get_pp_cipher(packet_info *pinfo, gboolean key_phase, guint64 pkn, quic_inf quic_info->cipher_keylen = (guint8) gcry_cipher_get_algo_keylen(cipher_algo); /* Set key for cipher handles KEY_PHASE 0. */ - if (!quic_cipher_init(&client_pp->cipher[0], quic_info->hash_algo, quic_info->cipher_keylen, client_pp->secret) || - !quic_cipher_init(&server_pp->cipher[0], quic_info->hash_algo, quic_info->cipher_keylen, server_pp->secret)) { + if (!quic_cipher_init(version, &client_pp->cipher[0], quic_info->hash_algo, quic_info->cipher_keylen, client_pp->secret) || + !quic_cipher_init(version, &server_pp->cipher[0], quic_info->hash_algo, quic_info->cipher_keylen, server_pp->secret)) { quic_info->skip_decryption = TRUE; return NULL; } @@ -1507,7 +1554,7 @@ quic_get_pp_cipher(packet_info *pinfo, gboolean key_phase, guint64 pkn, quic_inf } else { /* Key update requested, update key. */ quic_update_key(quic_info->hash_algo, pp_state, !from_server); - quic_cipher_init(&pp_state->cipher[key_phase], quic_info->hash_algo, quic_info->cipher_keylen, pp_state->secret); + quic_cipher_init(version, &pp_state->cipher[key_phase], quic_info->hash_algo, quic_info->cipher_keylen, pp_state->secret); pp_state->key_phase = key_phase; pp_state->changed_in_pkn = pkn; } |