aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2018-09-12 23:49:15 +0200
committerAnders Broman <a.broman58@gmail.com>2018-09-13 21:26:24 +0000
commitaedf4a7dd7755eb825409bb506084a5595fb05b6 (patch)
tree5a44e04b34b63f5732f844d0ea12d15982beb528
parent81710c7d3cb6eb8ec1a38e42d8ea1aff19f732b5 (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.c89
1 files changed, 68 insertions, 21 deletions
diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c
index 3ab6ab3..5262c85 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;
}