Extract HKDF-Expand from TLS 1.3 dissector

HKDF (RFC 5869) is a standard construct used in TLS 1.3, QUIC and
OSCORE, generalize it for use outside the TLS dissector.

Since none of the users need the "context" (formerly "hash_value")
field, remove the parameter.

Change-Id: Id952de8cb3000f6f6eda844d17c78bbd3906a84d
Reviewed-on: https://code.wireshark.org/review/25723
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2018-02-10 14:06:17 +01:00 committed by Anders Broman
parent d2016c6a1b
commit ae91f43155
5 changed files with 116 additions and 62 deletions

View File

@ -713,7 +713,7 @@ quic_derive_cleartext_secrets(guint64 cid,
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;
const char *label_prefix;
gcry_error_t err;
guint8 secret_bytes[HASH_SHA2_256_LENGTH];
StringInfo secret = { (guchar *) &secret_bytes, HASH_SHA2_256_LENGTH };
@ -729,14 +729,16 @@ quic_derive_cleartext_secrets(guint64 cid,
return FALSE;
}
if (!tls13_hkdf_expand_label(tls13_draft_version, GCRY_MD_SHA256, &secret, client_label,
"", HASH_SHA2_256_LENGTH, client_cleartext_secret)) {
label_prefix = "tls13 ";
if (!tls13_hkdf_expand_label_common(GCRY_MD_SHA256, &secret, label_prefix, client_label,
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, server_label,
"", HASH_SHA2_256_LENGTH, server_cleartext_secret)) {
if (!tls13_hkdf_expand_label_common(GCRY_MD_SHA256, &secret, label_prefix, server_label,
HASH_SHA2_256_LENGTH, server_cleartext_secret)) {
wmem_free(NULL, *client_cleartext_secret);
*client_cleartext_secret = NULL;
*error = "Key expansion (server) failed";

View File

@ -2755,77 +2755,74 @@ static gint tls12_handshake_hash(SslDecryptSession* ssl, gint md, StringInfo* ou
return 0;
}
/*
* Computes HKDF-Expand-Label(Secret, Label, "", Length) with a custom label
* prefix.
*/
gboolean
tls13_hkdf_expand_label(guchar draft_version,
int md, const StringInfo *secret, const char *label, const char *hash_value,
tls13_hkdf_expand_label_common(int md, const StringInfo *secret,
const char *label_prefix, const char *label,
guint16 out_len, guchar **out)
{
/* draft-ietf-tls-tls13-20:
* HKDF-Expand-Label(Secret, Label, HashValue, Length) =
/* draft-ietf-tls-tls13-23:
* HKDF-Expand-Label(Secret, Label, Context, Length) =
* HKDF-Expand(Secret, HkdfLabel, Length)
* struct {
* uint16 length = Length;
* opaque label<7..255> = "tls13 " + Label;
* opaque hash_value<0..255> = HashValue;
* opaque label<7..255> = "tls13 " + Label; // "tls13 " is label prefix.
* opaque context<0..255> = Context;
* } HkdfLabel;
*
* RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF):
* HKDF-Expand(PRK, info, L) -> OKM
*/
guchar lastoutput[DIGEST_MAX_SIZE];
gcry_md_hd_t h;
gcry_error_t err;
const guint label_prefix_length = (guint) strlen(label_prefix);
const guint label_length = (guint) strlen(label);
const guint hash_value_length = (guint) strlen(hash_value);
const guint hash_len = gcry_md_get_algo_dlen(md);
/* Some sanity checks */
DISSECTOR_ASSERT(out_len > 0 && out_len <= 255 * hash_len);
DISSECTOR_ASSERT(label_length > 0 && label_length <= 255 - 6);
DISSECTOR_ASSERT(hash_value_length <= 255);
DISSECTOR_ASSERT(hash_len > 0 && hash_len <= DIGEST_MAX_SIZE);
DISSECTOR_ASSERT(label_length > 0 && label_prefix_length + label_length <= 255);
/* info = HkdfLabel { length, label, context } */
GByteArray *info = g_byte_array_new();
const guint16 length = g_htons(out_len);
g_byte_array_append(info, (const guint8 *)&length, sizeof(length));
const guint8 label_vector_length = label_prefix_length + label_length;
g_byte_array_append(info, &label_vector_length, 1);
g_byte_array_append(info, label_prefix, label_prefix_length);
g_byte_array_append(info, label, label_length);
const guint8 context_length = 0;
g_byte_array_append(info, &context_length, 1);
*out = (guchar *)wmem_alloc(NULL, out_len);
err = hkdf_expand(md, secret->data, secret->data_len, info->data, info->len, *out, out_len);
g_byte_array_free(info, TRUE);
err = gcry_md_open(&h, md, GCRY_MD_FLAG_HMAC);
if (err) {
ssl_debug_printf("%s failed to invoke hash func %d: %s\n", G_STRFUNC, md, gcry_strerror(err));
ssl_debug_printf("%s failed %d: %s\n", G_STRFUNC, md, gcry_strerror(err));
wmem_free(NULL, *out);
*out = NULL;
return FALSE;
}
*out = (guchar *)wmem_alloc(NULL, out_len);
for (guint offset = 0; offset < out_len; offset += hash_len) {
gcry_md_reset(h);
gcry_md_setkey(h, secret->data, secret->data_len); /* Set PRK */
if (offset > 0) {
gcry_md_write(h, lastoutput, hash_len); /* T(1..N) */
}
/* info = HkdfLabel { length, label, hash_value } */
gcry_md_putc(h, out_len >> 8); /* length */
gcry_md_putc(h, (guint8) out_len);
if (draft_version && draft_version < 20) {
/* Draft -19 and before use a different prefix.
* TODO remove this once implementations are updated for D20. */
gcry_md_putc(h, 9 + label_length); /* label */
gcry_md_write(h, "TLS 1.3, ", 9);
} else {
gcry_md_putc(h, 6 + label_length); /* label */
gcry_md_write(h, "tls13 ", 6);
}
gcry_md_write(h, label, label_length);
gcry_md_putc(h, hash_value_length); /* hash_value */
gcry_md_write(h, hash_value, hash_value_length);
gcry_md_putc(h, (guint8) (offset / hash_len + 1)); /* constant 0x01..N */
memcpy(lastoutput, gcry_md_read(h, md), hash_len);
memcpy(*out + offset, lastoutput, MIN(hash_len, out_len - offset));
}
gcry_md_close(h);
return TRUE;
}
static gboolean
tls13_hkdf_expand_label(guchar draft_version,
int md, const StringInfo *secret, const char *label,
guint16 out_len, guchar **out)
{
if (draft_version && draft_version < 20) {
/* Draft -19 and before use a different prefix.
* TODO remove this once implementations are updated for D20. */
return tls13_hkdf_expand_label_common(md, secret, "TLS 1.3, ", label, out_len, out);
} else {
return tls13_hkdf_expand_label_common(md, secret, "tls13 ", label, out_len, out);
}
}
/* HMAC and the Pseudorandom function }}} */
/* Record Decompression (after decryption) {{{ */
@ -3037,11 +3034,11 @@ tls13_cipher_create(guint8 tls13_draft_version, int cipher_algo, int cipher_mode
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)) {
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)) {
if (!tls13_hkdf_expand_label(tls13_draft_version, hash_algo, secret, "iv", iv_length, &write_iv)) {
*error = "Key expansion (IV) failed";
goto end;
}
@ -3592,11 +3589,11 @@ tls13_generate_keys(SslDecryptSession *ssl_session, const StringInfo *secret, gb
iv_length = 12;
ssl_debug_printf("%s key_length %u iv_length %u\n", G_STRFUNC, key_length, iv_length);
if (!tls13_hkdf_expand_label(ssl_session->session.tls13_draft_version, hash_algo, secret, "key", "", key_length, &write_key)) {
if (!tls13_hkdf_expand_label(ssl_session->session.tls13_draft_version, hash_algo, secret, "key", key_length, &write_key)) {
ssl_debug_printf("%s write_key expansion failed\n", G_STRFUNC);
return FALSE;
}
if (!tls13_hkdf_expand_label(ssl_session->session.tls13_draft_version, hash_algo, secret, "iv", "", iv_length, &write_iv)) {
if (!tls13_hkdf_expand_label(ssl_session->session.tls13_draft_version, hash_algo, secret, "iv", iv_length, &write_iv)) {
ssl_debug_printf("%s write_iv expansion failed\n", G_STRFUNC);
goto end;
}
@ -5072,7 +5069,7 @@ tls13_key_update(SslDecryptSession *ssl, gboolean is_from_server)
const guint hash_len = app_secret->data_len;
guchar *new_secret;
if (!tls13_hkdf_expand_label(ssl->session.tls13_draft_version,
hash_algo, app_secret, "application traffic secret", "",
hash_algo, app_secret, "application traffic secret",
hash_len, &new_secret)) {
ssl_debug_printf("%s traffic_secret_N+1 expansion failed\n", G_STRFUNC);
return;

View File

@ -1095,9 +1095,9 @@ tls_dissect_sct_list(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo
guint32 offset, guint32 offset_end, guint16 version);
extern gboolean
tls13_hkdf_expand_label(guchar draft_version,
int md, const StringInfo *secret, const char *label, const char *hash_value,
guint16 out_len, guchar **out);
tls13_hkdf_expand_label_common(int md, const StringInfo *secret,
const char *label_prefix, const char *label,
guint16 out_len, guchar **out);
/* {{{ */
#define SSL_COMMON_LIST_T(name) \

View File

@ -144,6 +144,44 @@ out:
return decr_len;
}
gcry_error_t
hkdf_expand(int hashalgo, const guint8 *prk, guint prk_len, const guint8 *info, guint info_len,
guint8 *out, guint out_len)
{
// Current maximum hash output size: 48 bytes for SHA-384.
guchar lastoutput[48];
gcry_md_hd_t h;
gcry_error_t err;
const guint hash_len = gcry_md_get_algo_dlen(hashalgo);
/* Some sanity checks */
if (!(out_len > 0 && out_len <= 255 * hash_len) ||
!(hash_len > 0 && hash_len <= sizeof(lastoutput))) {
return GPG_ERR_INV_ARG;
}
err = gcry_md_open(&h, hashalgo, GCRY_MD_FLAG_HMAC);
if (err) {
return err;
}
for (guint offset = 0; offset < out_len; offset += hash_len) {
gcry_md_reset(h);
gcry_md_setkey(h, prk, prk_len); /* Set PRK */
if (offset > 0) {
gcry_md_write(h, lastoutput, hash_len); /* T(1..N) */
}
gcry_md_write(h, info, info_len); /* info */
gcry_md_putc(h, (guint8) (offset / hash_len + 1)); /* constant 0x01..N */
memcpy(lastoutput, gcry_md_read(h, hashalgo), hash_len);
memcpy(out + offset, lastoutput, MIN(hash_len, out_len - offset));
}
gcry_md_close(h);
return 0;
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*

View File

@ -46,5 +46,22 @@ WS_DLL_PUBLIC void crypt_des_ecb(guint8 *output, const guint8 *buffer, const gui
/* Convenience function for RSA decryption. Returns decrypted length on success, 0 on failure */
WS_DLL_PUBLIC size_t rsa_decrypt_inplace(const guint len, guchar* data, gcry_sexp_t pk, gboolean pkcs1_padding, char **err);
/**
* RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function (HKDF):
* HKDF-Expand(PRK, info, L) -> OKM
*
* @param hashalgo [in] Libgcrypt hash algorithm identifier.
* @param prk [in] Pseudo-random key.
* @param prk_len [in] Length of prk.
* @param info [in] Optional context (can be NULL if info_len is zero).
* @param info_len [in] Length of info.
* @param out [out] Output keying material.
* @param out_len [in] Size of output keying material.
* @return 0 on success and an error code otherwise.
*/
WS_DLL_PUBLIC gcry_error_t
hkdf_expand(int hashalgo, const guint8 *prk, guint prk_len, const guint8 *info, guint info_len,
guint8 *out, guint out_len);
#endif /* __WSGCRYPT_H__ */