From 7edbb85fb63bc248e3633a6d70bd4e49c811e451 Mon Sep 17 00:00:00 2001
From: jsing <>
Date: Mon, 16 Nov 2020 18:55:15 +0000
Subject: Implement exporter for TLSv1.3.

This implements the key material exporter for TLSv1.3, as defined in
RFC8446 section 7.5.

Issue reported by nmathewson on github.

ok inoguchi@ tb@
---
 src/lib/libssl/ssl_lib.c            | 15 ++++++--
 src/lib/libssl/tls13_internal.h     | 16 +++++++-
 src/lib/libssl/tls13_key_schedule.c | 24 ++++++++++--
 src/lib/libssl/tls13_lib.c          | 74 ++++++++++++++++++++++++++++++++++++-
 4 files changed, 121 insertions(+), 8 deletions(-)

(limited to 'src')

diff --git a/src/lib/libssl/ssl_lib.c b/src/lib/libssl/ssl_lib.c
index d92ccd8029..58b9dae910 100644
--- a/src/lib/libssl/ssl_lib.c
+++ b/src/lib/libssl/ssl_lib.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_lib.c,v 1.237 2020/10/14 16:57:33 jsing Exp $ */
+/* $OpenBSD: ssl_lib.c,v 1.238 2020/11/16 18:55:15 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -1716,8 +1716,17 @@ SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
     const char *label, size_t llen, const unsigned char *p, size_t plen,
     int use_context)
 {
-	return (tls1_export_keying_material(s, out, olen,
-	    label, llen, p, plen, use_context));
+	if (s->internal->tls13 != NULL && s->version == TLS1_3_VERSION) {
+		if (!use_context) {
+			p = NULL;
+			plen = 0;
+		}
+		return tls13_exporter(s->internal->tls13, label, llen, p, plen,
+		    out, olen);
+	}
+
+	return (tls1_export_keying_material(s, out, olen, label, llen, p, plen,
+	    use_context));
 }
 
 static unsigned long
diff --git a/src/lib/libssl/tls13_internal.h b/src/lib/libssl/tls13_internal.h
index 03a1a6b4b1..ea5f9a1473 100644
--- a/src/lib/libssl/tls13_internal.h
+++ b/src/lib/libssl/tls13_internal.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_internal.h,v 1.86 2020/07/30 16:23:17 tb Exp $ */
+/* $OpenBSD: tls13_internal.h,v 1.87 2020/11/16 18:55:15 jsing Exp $ */
 /*
  * Copyright (c) 2018 Bob Beck <beck@openbsd.org>
  * Copyright (c) 2018 Theo Buehler <tb@openbsd.org>
@@ -148,6 +148,16 @@ void tls13_secrets_destroy(struct tls13_secrets *secrets);
 int tls13_hkdf_expand_label(struct tls13_secret *out, const EVP_MD *digest,
     const struct tls13_secret *secret, const char *label,
     const struct tls13_secret *context);
+int tls13_hkdf_expand_label_with_length(struct tls13_secret *out,
+    const EVP_MD *digest, const struct tls13_secret *secret,
+    const uint8_t *label, size_t label_len, const struct tls13_secret *context);
+
+int tls13_derive_secret(struct tls13_secret *out, const EVP_MD *digest,
+    const struct tls13_secret *secret, const char *label,   
+    const struct tls13_secret *context);
+int tls13_derive_secret_with_label_length(struct tls13_secret *out,
+    const EVP_MD *digest, const struct tls13_secret *secret,
+    const uint8_t *label, size_t label_len, const struct tls13_secret *context);
 
 int tls13_derive_early_secrets(struct tls13_secrets *secrets, uint8_t *psk,
     size_t psk_len, const struct tls13_secret *context);
@@ -412,6 +422,10 @@ int tls13_error_setx(struct tls13_error *error, int code, int subcode,
 	tls13_error_setx(&(ctx)->error, (code), (subcode), __FILE__, __LINE__, \
 	    (fmt), __VA_ARGS__)
 
+int tls13_exporter(struct tls13_ctx *ctx, const uint8_t *label, size_t label_len,
+    const uint8_t *context_value, size_t context_value_len, uint8_t *out,
+    size_t out_len);
+
 extern const uint8_t tls13_downgrade_12[8];
 extern const uint8_t tls13_downgrade_11[8];
 extern const uint8_t tls13_hello_retry_request_hash[32];
diff --git a/src/lib/libssl/tls13_key_schedule.c b/src/lib/libssl/tls13_key_schedule.c
index 91f59e46f9..35180cfe5c 100644
--- a/src/lib/libssl/tls13_key_schedule.c
+++ b/src/lib/libssl/tls13_key_schedule.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_key_schedule.c,v 1.8 2019/11/17 21:01:08 beck Exp $ */
+/* $OpenBSD: tls13_key_schedule.c,v 1.9 2020/11/16 18:55:15 jsing Exp $ */
 /* Copyright (c) 2018, Bob Beck <beck@openbsd.org>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
@@ -173,6 +173,15 @@ int
 tls13_hkdf_expand_label(struct tls13_secret *out, const EVP_MD *digest,
     const struct tls13_secret *secret, const char *label,
     const struct tls13_secret *context)
+{
+	return tls13_hkdf_expand_label_with_length(out, digest, secret, label,
+	    strlen(label), context);
+}
+
+int
+tls13_hkdf_expand_label_with_length(struct tls13_secret *out,
+    const EVP_MD *digest, const struct tls13_secret *secret,
+    const uint8_t *label, size_t label_len, const struct tls13_secret *context)
 {
 	const char tls13_plabel[] = "tls13 ";
 	uint8_t *hkdf_label;
@@ -188,7 +197,7 @@ tls13_hkdf_expand_label(struct tls13_secret *out, const EVP_MD *digest,
 		goto err;
 	if (!CBB_add_bytes(&child, tls13_plabel, strlen(tls13_plabel)))
 		goto err;
-	if (!CBB_add_bytes(&child, label, strlen(label)))
+	if (!CBB_add_bytes(&child, label, label_len))
 		goto err;
 	if (!CBB_add_u8_length_prefixed(&cbb, &child))
 		goto err;
@@ -207,7 +216,7 @@ tls13_hkdf_expand_label(struct tls13_secret *out, const EVP_MD *digest,
 	return(0);
 }
 
-static int
+int
 tls13_derive_secret(struct tls13_secret *out, const EVP_MD *digest,
     const struct tls13_secret *secret, const char *label,
     const struct tls13_secret *context)
@@ -215,6 +224,15 @@ tls13_derive_secret(struct tls13_secret *out, const EVP_MD *digest,
 	return tls13_hkdf_expand_label(out, digest, secret, label, context);
 }
 
+int
+tls13_derive_secret_with_label_length(struct tls13_secret *out,
+    const EVP_MD *digest, const struct tls13_secret *secret, const uint8_t *label,
+    size_t label_len, const struct tls13_secret *context)
+{
+	return tls13_hkdf_expand_label_with_length(out, digest, secret, label,
+	    label_len, context);
+}
+
 int
 tls13_derive_early_secrets(struct tls13_secrets *secrets,
     uint8_t *psk, size_t psk_len, const struct tls13_secret *context)
diff --git a/src/lib/libssl/tls13_lib.c b/src/lib/libssl/tls13_lib.c
index 590426ad8a..6b6ddce4d6 100644
--- a/src/lib/libssl/tls13_lib.c
+++ b/src/lib/libssl/tls13_lib.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: tls13_lib.c,v 1.54 2020/09/11 15:03:36 jsing Exp $ */
+/*	$OpenBSD: tls13_lib.c,v 1.55 2020/11/16 18:55:15 jsing Exp $ */
 /*
  * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
  * Copyright (c) 2019 Bob Beck <beck@openbsd.org>
@@ -579,3 +579,75 @@ tls13_clienthello_hash_validate(struct tls13_ctx *ctx)
 	return 1;
 }
 
+int
+tls13_exporter(struct tls13_ctx *ctx, const uint8_t *label, size_t label_len,
+    const uint8_t *context_value, size_t context_value_len, uint8_t *out,
+    size_t out_len)
+{
+	struct tls13_secret context, export_out, export_secret;
+	struct tls13_secrets *secrets = ctx->hs->secrets;
+	EVP_MD_CTX *md_ctx = NULL;
+	unsigned int md_out_len;
+	int md_len;
+	int ret = 0;
+
+	/*
+	 * RFC 8446 Section 7.5.
+	 */
+
+	memset(&context, 0, sizeof(context));
+	memset(&export_secret, 0, sizeof(export_secret));
+
+	export_out.data = out;
+	export_out.len = out_len;
+
+	if (!ctx->handshake_completed)
+		return 0;
+
+	md_len = EVP_MD_size(secrets->digest);
+	if (md_len <= 0 || md_len > EVP_MAX_MD_SIZE)
+		goto err;
+
+	if ((export_secret.data = calloc(1, md_len)) == NULL)
+		goto err;
+	export_secret.len = md_len;
+
+	if ((context.data = calloc(1, md_len)) == NULL)
+		goto err;
+	context.len = md_len;
+
+	/* In TLSv1.3 no context is equivalent to an empty context. */
+	if (context_value == NULL) {
+		context_value = "";
+		context_value_len = 0;
+	}
+
+	if ((md_ctx = EVP_MD_CTX_new()) == NULL)
+		goto err;
+	if (!EVP_DigestInit_ex(md_ctx, secrets->digest, NULL))
+		goto err;
+	if (!EVP_DigestUpdate(md_ctx, context_value, context_value_len))
+		goto err;
+	if (!EVP_DigestFinal_ex(md_ctx, context.data, &md_out_len))
+		goto err;
+	if (md_len != md_out_len)
+		goto err;
+
+	if (!tls13_derive_secret_with_label_length(&export_secret,
+	    secrets->digest, &secrets->exporter_master, label, label_len,
+	    &secrets->empty_hash))
+		goto err;
+
+	if (!tls13_hkdf_expand_label(&export_out, secrets->digest,
+	    &export_secret, "exporter", &context))
+		goto err;
+
+	ret = 1;
+
+ err:
+	EVP_MD_CTX_free(md_ctx);
+	freezero(context.data, context.len);
+	freezero(export_secret.data, export_secret.len);
+
+	return ret;
+}
-- 
cgit v1.2.3-55-g6feb