From 9b1a5502ac166e7ddd3da7c143bb65686a035ac4 Mon Sep 17 00:00:00 2001
From: jsing <>
Date: Mon, 7 Nov 2022 11:58:45 +0000
Subject: Rewrite TLSv1.2 key exporter.

Replace the grotty TLSv1.2 key exporter with a cleaner version that uses
CBB and CBS.

ok tb@
---
 src/lib/libssl/ssl_lib.c            |  21 ++++---
 src/lib/libssl/ssl_locl.h           |   5 +-
 src/lib/libssl/t1_enc.c             |  82 +-----------------------
 src/lib/libssl/tls12_internal.h     |  29 +++++++++
 src/lib/libssl/tls12_key_schedule.c | 122 +++++++++++++++++++++++++++++++++++-
 5 files changed, 163 insertions(+), 96 deletions(-)
 create mode 100644 src/lib/libssl/tls12_internal.h

diff --git a/src/lib/libssl/ssl_lib.c b/src/lib/libssl/ssl_lib.c
index 4b5f119a88..c9c63e9d3f 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.306 2022/10/02 16:36:41 jsing Exp $ */
+/* $OpenBSD: ssl_lib.c,v 1.307 2022/11/07 11:58:45 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -163,6 +163,7 @@
 #include "ssl_locl.h"
 #include "ssl_sigalgs.h"
 #include "ssl_tlsext.h"
+#include "tls12_internal.h"
 
 const char *SSL_version_str = OPENSSL_VERSION_TEXT;
 
@@ -1867,21 +1868,21 @@ SSL_set_psk_use_session_callback(SSL *s, SSL_psk_use_session_cb_func cb)
 }
 
 int
-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)
+SSL_export_keying_material(SSL *s, unsigned char *out, size_t out_len,
+    const char *label, size_t label_len, const unsigned char *context,
+    size_t context_len, int use_context)
 {
 	if (s->tls13 != NULL && s->version == TLS1_3_VERSION) {
 		if (!use_context) {
-			p = NULL;
-			plen = 0;
+			context = NULL;
+			context_len = 0;
 		}
-		return tls13_exporter(s->tls13, label, llen, p, plen,
-		    out, olen);
+		return tls13_exporter(s->tls13, label, label_len, context,
+		    context_len, out, out_len);
 	}
 
-	return (tls1_export_keying_material(s, out, olen, label, llen, p, plen,
-	    use_context));
+	return tls12_exporter(s, label, label_len, context, context_len,
+	    use_context, out, out_len);
 }
 
 static unsigned long
diff --git a/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h
index 42ae429074..64cf6a60f9 100644
--- a/src/lib/libssl/ssl_locl.h
+++ b/src/lib/libssl/ssl_locl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.429 2022/10/20 15:22:51 tb Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.430 2022/11/07 11:58:45 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -1463,9 +1463,6 @@ int tls1_change_read_cipher_state(SSL *s);
 int tls1_change_write_cipher_state(SSL *s);
 int tls1_setup_key_block(SSL *s);
 int tls1_generate_key_block(SSL *s, uint8_t *key_block, size_t key_block_len);
-int tls1_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);
 int ssl_ok(SSL *s);
 
 int tls12_derive_finished(SSL *s);
diff --git a/src/lib/libssl/t1_enc.c b/src/lib/libssl/t1_enc.c
index 66a7aea2f5..dc627c5a8b 100644
--- a/src/lib/libssl/t1_enc.c
+++ b/src/lib/libssl/t1_enc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: t1_enc.c,v 1.155 2022/10/02 16:36:41 jsing Exp $ */
+/* $OpenBSD: t1_enc.c,v 1.156 2022/11/07 11:58:45 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -413,83 +413,3 @@ tls1_setup_key_block(SSL *s)
 
 	return (ret);
 }
-
-int
-tls1_export_keying_material(SSL *s, unsigned char *out, size_t olen,
-    const char *label, size_t llen, const unsigned char *context,
-    size_t contextlen, int use_context)
-{
-	unsigned char *val = NULL;
-	size_t vallen, currentvalpos;
-	int rv;
-
-	if (!SSL_is_init_finished(s)) {
-		SSLerror(s, SSL_R_BAD_STATE);
-		return 0;
-	}
-
-	/* construct PRF arguments
-	 * we construct the PRF argument ourself rather than passing separate
-	 * values into the TLS PRF to ensure that the concatenation of values
-	 * does not create a prohibited label.
-	 */
-	vallen = llen + SSL3_RANDOM_SIZE * 2;
-	if (use_context) {
-		vallen += 2 + contextlen;
-	}
-
-	val = malloc(vallen);
-	if (val == NULL)
-		goto err2;
-	currentvalpos = 0;
-	memcpy(val + currentvalpos, (unsigned char *) label, llen);
-	currentvalpos += llen;
-	memcpy(val + currentvalpos, s->s3->client_random, SSL3_RANDOM_SIZE);
-	currentvalpos += SSL3_RANDOM_SIZE;
-	memcpy(val + currentvalpos, s->s3->server_random, SSL3_RANDOM_SIZE);
-	currentvalpos += SSL3_RANDOM_SIZE;
-
-	if (use_context) {
-		val[currentvalpos] = (contextlen >> 8) & 0xff;
-		currentvalpos++;
-		val[currentvalpos] = contextlen & 0xff;
-		currentvalpos++;
-		if ((contextlen > 0) || (context != NULL)) {
-			memcpy(val + currentvalpos, context, contextlen);
-		}
-	}
-
-	/* disallow prohibited labels
-	 * note that SSL3_RANDOM_SIZE > max(prohibited label len) =
-	 * 15, so size of val > max(prohibited label len) = 15 and the
-	 * comparisons won't have buffer overflow
-	 */
-	if (memcmp(val, TLS_MD_CLIENT_FINISH_CONST,
-	    TLS_MD_CLIENT_FINISH_CONST_SIZE) == 0)
-		goto err1;
-	if (memcmp(val, TLS_MD_SERVER_FINISH_CONST,
-	    TLS_MD_SERVER_FINISH_CONST_SIZE) == 0)
-		goto err1;
-	if (memcmp(val, TLS_MD_MASTER_SECRET_CONST,
-	    TLS_MD_MASTER_SECRET_CONST_SIZE) == 0)
-		goto err1;
-	if (memcmp(val, TLS_MD_KEY_EXPANSION_CONST,
-	    TLS_MD_KEY_EXPANSION_CONST_SIZE) == 0)
-		goto err1;
-
-	rv = tls1_PRF(s, s->session->master_key, s->session->master_key_length,
-	    val, vallen, NULL, 0, NULL, 0, NULL, 0, NULL, 0, out, olen);
-
-	goto ret;
- err1:
-	SSLerror(s, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL);
-	rv = 0;
-	goto ret;
- err2:
-	SSLerror(s, ERR_R_MALLOC_FAILURE);
-	rv = 0;
- ret:
-	free(val);
-
-	return (rv);
-}
diff --git a/src/lib/libssl/tls12_internal.h b/src/lib/libssl/tls12_internal.h
new file mode 100644
index 0000000000..d416b2e3f1
--- /dev/null
+++ b/src/lib/libssl/tls12_internal.h
@@ -0,0 +1,29 @@
+/* $OpenBSD: tls12_internal.h,v 1.1 2022/11/07 11:58:45 jsing Exp $ */
+/*
+ * Copyright (c) 2022 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HEADER_TLS12_INTERNAL_H
+#define HEADER_TLS12_INTERNAL_H
+
+__BEGIN_HIDDEN_DECLS
+
+int tls12_exporter(SSL *s, const uint8_t *label, size_t label_len,
+    const uint8_t *context_value, size_t context_value_len, int use_context,
+    uint8_t *out, size_t out_len);
+
+__END_HIDDEN_DECLS
+
+#endif
diff --git a/src/lib/libssl/tls12_key_schedule.c b/src/lib/libssl/tls12_key_schedule.c
index c206460d95..e603e2c222 100644
--- a/src/lib/libssl/tls12_key_schedule.c
+++ b/src/lib/libssl/tls12_key_schedule.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls12_key_schedule.c,v 1.1 2021/05/05 10:05:27 jsing Exp $ */
+/* $OpenBSD: tls12_key_schedule.c,v 1.2 2022/11/07 11:58:45 jsing Exp $ */
 /*
  * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
  *
@@ -21,6 +21,7 @@
 
 #include "bytestring.h"
 #include "ssl_locl.h"
+#include "tls12_internal.h"
 
 struct tls12_key_block {
 	CBS client_write_mac_key;
@@ -173,3 +174,122 @@ tls12_key_block_generate(struct tls12_key_block *kb, SSL *s,
 
 	return 0;
 }
+
+struct tls12_reserved_label {
+	const char *label;
+	size_t label_len;
+};
+
+/*
+ * RFC 5705 section 6.
+ */
+static const struct tls12_reserved_label tls12_reserved_labels[] = {
+	{
+		.label = TLS_MD_CLIENT_FINISH_CONST,
+		.label_len = TLS_MD_CLIENT_FINISH_CONST_SIZE,
+	},
+	{
+		.label = TLS_MD_SERVER_FINISH_CONST,
+		.label_len = TLS_MD_SERVER_FINISH_CONST_SIZE,
+	},
+	{
+		.label = TLS_MD_MASTER_SECRET_CONST,
+		.label_len = TLS_MD_MASTER_SECRET_CONST_SIZE,
+	},
+	{
+		.label = TLS_MD_KEY_EXPANSION_CONST,
+		.label_len = TLS_MD_KEY_EXPANSION_CONST_SIZE,
+	},
+	{
+		.label = NULL,
+		.label_len = 0,
+	},
+};
+
+int
+tls12_exporter(SSL *s, const uint8_t *label, size_t label_len,
+    const uint8_t *context_value, size_t context_value_len, int use_context,
+    uint8_t *out, size_t out_len)
+{
+	uint8_t *data = NULL;
+	size_t data_len = 0;
+	CBB cbb, context;
+	CBS seed;
+	size_t i;
+	int ret = 0;
+
+	/*
+	 * RFC 5705 - Key Material Exporters for TLS.
+	 */
+
+	memset(&cbb, 0, sizeof(cbb));
+
+	if (!SSL_is_init_finished(s)) {
+		SSLerror(s, SSL_R_BAD_STATE);
+		goto err;
+	}
+
+	if (s->s3->hs.negotiated_tls_version >= TLS1_3_VERSION)
+		goto err;
+
+	/*
+	 * Due to exceptional design choices, we need to build a concatenation
+	 * of the label and the seed value, before checking for reserved
+	 * labels. This prevents a reserved label from being split across the
+	 * label and the seed (that includes the client random), which are
+	 * concatenated by the PRF.
+	 */
+	if (!CBB_init(&cbb, 0))
+		goto err;
+	if (!CBB_add_bytes(&cbb, label, label_len))
+		goto err;
+	if (!CBB_add_bytes(&cbb, s->s3->client_random, SSL3_RANDOM_SIZE))
+		goto err;
+	if (!CBB_add_bytes(&cbb, s->s3->server_random, SSL3_RANDOM_SIZE))
+		goto err;
+	if (use_context) {
+		if (!CBB_add_u16_length_prefixed(&cbb, &context))
+			goto err;
+		if (context_value_len > 0) {
+			if (!CBB_add_bytes(&context, context_value,
+			    context_value_len))
+				goto err;
+		}
+	}
+	if (!CBB_finish(&cbb, &data, &data_len))
+		goto err;
+
+	/*
+	 * Ensure that the block (label + seed) does not start with a reserved
+	 * label - in an ideal world we would ensure that the label has an
+	 * explicitly permitted prefix instead, but of course this also got
+	 * messed up by the standards.
+	 */
+	for (i = 0; tls12_reserved_labels[i].label != NULL; i++) {
+		/* XXX - consider adding/using CBS_has_prefix(). */
+		if (tls12_reserved_labels[i].label_len > data_len)
+			goto err;
+		if (memcmp(data, tls12_reserved_labels[i].label,
+		    tls12_reserved_labels[i].label_len) == 0) {
+			SSLerror(s, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL);
+			goto err;
+		}
+	}
+
+	CBS_init(&seed, data, data_len);
+	if (!CBS_skip(&seed, label_len))
+		goto err;
+
+	if (!tls1_PRF(s, s->session->master_key, s->session->master_key_length,
+	    label, label_len, CBS_data(&seed), CBS_len(&seed), NULL, 0, NULL, 0,
+	    NULL, 0, out, out_len))
+		goto err;
+
+	ret = 1;
+
+ err:
+	freezero(data, data_len);
+	CBB_cleanup(&cbb);
+
+	return ret;
+}
-- 
cgit v1.2.3-55-g6feb