Rewrite TLSv1.2 key exporter.
authorjsing <jsing@openbsd.org>
Mon, 7 Nov 2022 11:58:45 +0000 (11:58 +0000)
committerjsing <jsing@openbsd.org>
Mon, 7 Nov 2022 11:58:45 +0000 (11:58 +0000)
Replace the grotty TLSv1.2 key exporter with a cleaner version that uses
CBB and CBS.

ok tb@

lib/libssl/ssl_lib.c
lib/libssl/ssl_locl.h
lib/libssl/t1_enc.c
lib/libssl/tls12_internal.h [new file with mode: 0644]
lib/libssl/tls12_key_schedule.c

index 4b5f119..c9c63e9 100644 (file)
@@ -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.
  *
 #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
index 42ae429..64cf6a6 100644 (file)
@@ -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);
index 66a7aea..dc627c5 100644 (file)
@@ -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/lib/libssl/tls12_internal.h b/lib/libssl/tls12_internal.h
new file mode 100644 (file)
index 0000000..d416b2e
--- /dev/null
@@ -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
index c206460..e603e2c 100644 (file)
@@ -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;
+}