From 904c263dba50ab73290eca81c8623f4d3691011e Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 24 Nov 2017 11:07:20 +0100 Subject: gnutls_prf_rfc5705: calculate exporter using HKDF if TLS 1.3 Signed-off-by: Daiki Ueno --- lib/prf.c | 80 +++++++++++--- tests/Makefile.am | 3 +- tests/tls13/prf.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 387 insertions(+), 19 deletions(-) create mode 100644 tests/tls13/prf.c diff --git a/lib/prf.c b/lib/prf.c index 7ac1090d92..b5dd8888fa 100644 --- a/lib/prf.c +++ b/lib/prf.c @@ -27,10 +27,13 @@ #include "gnutls_int.h" #include "errors.h" +#include "secrets.h" #include #include #include +#define EXPORTER_LABEL "exporter" + /** * gnutls_prf_raw: * @session: is a #gnutls_session_t type. @@ -44,6 +47,10 @@ * Apply the TLS Pseudo-Random-Function (PRF) on the master secret * and the provided data. * + * This function only works with the TLS versions prior to 1.3. In + * TLS 1.3, the use of PRF is replaced with HKDF (HMAC-based Key + * Derivation Function) based on the multi-stage key scheduling. + * * The @label variable usually contains a string denoting the purpose * for the generated data. The @seed usually contains data such as the * client and server random, perhaps together with some additional @@ -88,15 +95,18 @@ gnutls_prf_raw(gnutls_session_t session, * @outsize: size of pre-allocated output buffer to hold the output. * @out: pre-allocated buffer to hold the generated data. * - * Applies the TLS Pseudo-Random-Function (PRF) on the master secret - * and the provided data, seeded with the client and server random fields, + * Exports keyring material from TLS/DTLS session to an application, * as specified in RFC5705. * + * In the TLS versions prior to 1.3, it applies the TLS + * Pseudo-Random-Function (PRF) on the master secret and the provided + * data, seeded with the client and server random fields. + * + * In TLS 1.3, it applies HKDF on the exporter master secret derived + * from the master secret. + * * The @label variable usually contains a string denoting the purpose - * for the generated data. The @server_random_first indicates whether - * the client random field or the server random field should be first - * in the seed. Non-zero indicates that the server random field is first, - * 0 that the client random field is first. + * for the generated data. * * The @context variable can be used to add more data to the seed, after * the random variables. It can be used to make sure the @@ -118,7 +128,7 @@ gnutls_prf_rfc5705(gnutls_session_t session, size_t context_size, const char *context, size_t outsize, char *out) { - char *pctx = NULL; + const version_entry_st *vers = get_version(session); int ret; if (context != NULL && context_size > 65535) { @@ -126,22 +136,52 @@ gnutls_prf_rfc5705(gnutls_session_t session, return GNUTLS_E_INVALID_REQUEST; } - if (context != NULL) { - pctx = gnutls_malloc(context_size+2); - if (!pctx) { + if (vers && vers->tls13_sem) { + uint8_t secret[MAX_HASH_SIZE]; + uint8_t digest[MAX_HASH_SIZE]; + unsigned digest_size = session->security_parameters.prf->output_size; + + /* exporter_master_secret might not be set, when + * handshake is in progress */ + if (session->internals.handshake_in_progress) { gnutls_assert(); - return GNUTLS_E_MEMORY_ERROR; + return GNUTLS_E_INVALID_REQUEST; } - memcpy(pctx+2, context, context_size); - _gnutls_write_uint16(context_size, (void*)pctx); - context_size += 2; - } + ret = _tls13_derive_secret(session, label, label_size, NULL, 0, + session->key.ap_expkey, secret); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_hash_fast((gnutls_digest_algorithm_t)session->security_parameters.prf->id, + context, context_size, digest); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _tls13_expand_secret(session, EXPORTER_LABEL, sizeof(EXPORTER_LABEL)-1, + digest, digest_size, + secret, outsize, out); + } else { + char *pctx = NULL; - ret = gnutls_prf(session, label_size, label, 0, - context_size, pctx, outsize, out); + if (context != NULL) { + pctx = gnutls_malloc(context_size+2); + if (!pctx) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy(pctx+2, context, context_size); + _gnutls_write_uint16(context_size, (void*)pctx); + context_size += 2; + } + + ret = gnutls_prf(session, label_size, label, 0, + context_size, pctx, outsize, out); + + gnutls_free(pctx); + } - gnutls_free(pctx); return ret; } @@ -160,6 +200,10 @@ gnutls_prf_rfc5705(gnutls_session_t session, * and the provided data, seeded with the client and server random fields. * For the key expansion specified in RFC5705 see gnutls_prf_rfc5705(). * + * This function only works with the TLS versions prior to 1.3. In + * TLS 1.3, the use of PRF is replaced with HKDF (HMAC-based Key + * Derivation Function) based on the multi-stage key scheduling. + * * The @label variable usually contains a string denoting the purpose * for the generated data. The @server_random_first indicates whether * the client random field or the server random field should be first diff --git a/tests/Makefile.am b/tests/Makefile.am index e7c5a12340..476c532cef 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -93,7 +93,8 @@ indirect_tests = ctests = tls13/supported_versions tls13/tls12-no-tls13-exts \ tls13/post-handshake-with-cert tls13/post-handshake-without-cert \ - tls13/cookie tls13/key_share tls12-rollback-detection tls11-rollback-detection \ + tls13/cookie tls13/key_share tls13/prf \ + tls12-rollback-detection tls11-rollback-detection \ tls12-check-rollback-val tls11-check-rollback-val tls13/hello_random_value ctests += mini-record-2 simple gc set_pkcs12_cred cert certuniqueid tls-neg-ext-key \ diff --git a/tests/tls13/prf.c b/tests/tls13/prf.c new file mode 100644 index 0000000000..16cca4c5e9 --- /dev/null +++ b/tests/tls13/prf.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2015-2017 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#if !defined(__linux__) || !defined(__GNUC__) + +int main(int argc, char **argv) +{ + exit(77); +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cert-common.h" +#include "utils.h" + +static void terminate(void); + +/* This program tests whether the gnutls_prf() works as + * expected. + */ + +static void server_log_func(int level, const char *str) +{ + fprintf(stderr, "server|<%d>| %s", level, str); +} + +static void client_log_func(int level, const char *str) +{ + fprintf(stderr, "client|<%d>| %s", level, str); +} + +/* These are global */ +static pid_t child; + +static const +gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32}; +static const +gnutls_datum_t hsrnd = {(void*)"\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32}; + +int __attribute__ ((visibility ("protected"))) +gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len) +{ + memset(data, 0xff, len); + + /* Flip the first byte to avoid infinite loop in the RSA + * blinding code of Nettle */ + if (len > 0) + memset(data, 0x0, 1); + return 0; +} + +static void dump(const char *name, const uint8_t *data, unsigned data_size) +{ + unsigned i; + + fprintf(stderr, "%s", name); + for (i=0;i