Add support for key logging callbacks.

Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/1646)
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 2f6d59a..86ffcb9 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -775,6 +775,25 @@
 # define SSL_MAC_FLAG_READ_MAC_STREAM 1
 # define SSL_MAC_FLAG_WRITE_MAC_STREAM 2
 
+/*
+ * A callback for logging out TLS key material. This callback should log out
+ * |line| followed by a newline.
+ */
+typedef void (*SSL_CTX_keylog_cb_func)(const SSL *ssl, const char *line);
+
+/*
+ * SSL_CTX_set_keylog_callback configures a callback to log key material. This
+ * is intended for debugging use with tools like Wireshark. The cb function
+ * should log line followed by a newline.
+ */
+void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb);
+
+/*
+ * SSL_CTX_get_keylog_callback returns the callback configured by
+ * SSL_CTX_set_keylog_callback.
+ */
+SSL_CTX_keylog_cb_func SSL_CTX_get_keylog_callback(const SSL_CTX *ctx);
+
 #ifdef __cplusplus
 }
 #endif
@@ -2079,6 +2098,7 @@
 # define SSL_F_FINAL_EMS                                  486
 # define SSL_F_FINAL_RENEGOTIATE                          483
 # define SSL_F_FINAL_SIG_ALGS                             497
+# define SSL_F_NSS_KEYLOG_INT                             500
 # define SSL_F_OPENSSL_INIT_SSL                           342
 # define SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION       436
 # define SSL_F_OSSL_STATEM_CLIENT_CONSTRUCT_MESSAGE       430
@@ -2170,6 +2190,8 @@
 # define SSL_F_SSL_GET_SIGN_PKEY                          183
 # define SSL_F_SSL_INIT_WBIO_BUFFER                       184
 # define SSL_F_SSL_LOAD_CLIENT_CA_FILE                    185
+# define SSL_F_SSL_LOG_MASTER_SECRET                      498
+# define SSL_F_SSL_LOG_RSA_CLIENT_KEY_EXCHANGE            499
 # define SSL_F_SSL_MODULE_INIT                            392
 # define SSL_F_SSL_NEW                                    186
 # define SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT      300
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 79cbf2e..d380c86 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the OpenSSL license (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -53,6 +53,7 @@
     {ERR_FUNC(SSL_F_FINAL_EMS), "final_ems"},
     {ERR_FUNC(SSL_F_FINAL_RENEGOTIATE), "final_renegotiate"},
     {ERR_FUNC(SSL_F_FINAL_SIG_ALGS), "final_sig_algs"},
+    {ERR_FUNC(SSL_F_NSS_KEYLOG_INT), "nss_keylog_int"},
     {ERR_FUNC(SSL_F_OPENSSL_INIT_SSL), "OPENSSL_init_ssl"},
     {ERR_FUNC(SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION),
      "ossl_statem_client13_read_transition"},
@@ -177,6 +178,9 @@
     {ERR_FUNC(SSL_F_SSL_GET_SIGN_PKEY), "ssl_get_sign_pkey"},
     {ERR_FUNC(SSL_F_SSL_INIT_WBIO_BUFFER), "ssl_init_wbio_buffer"},
     {ERR_FUNC(SSL_F_SSL_LOAD_CLIENT_CA_FILE), "SSL_load_client_CA_file"},
+    {ERR_FUNC(SSL_F_SSL_LOG_MASTER_SECRET), "ssl_log_master_secret"},
+    {ERR_FUNC(SSL_F_SSL_LOG_RSA_CLIENT_KEY_EXCHANGE),
+     "ssl_log_rsa_client_key_exchange"},
     {ERR_FUNC(SSL_F_SSL_MODULE_INIT), "ssl_module_init"},
     {ERR_FUNC(SSL_F_SSL_NEW), "SSL_new"},
     {ERR_FUNC(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT),
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 21ea284..fe17f3d 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -4344,3 +4344,114 @@
 }
 
 #endif
+
+void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb)
+{
+    ctx->keylog_callback = cb;
+}
+
+SSL_CTX_keylog_cb_func SSL_CTX_get_keylog_callback(const SSL_CTX *ctx)
+{
+    return ctx->keylog_callback;
+}
+
+static int nss_keylog_int(const char *prefix,
+                          SSL *ssl,
+                          const uint8_t *parameter_1,
+                          size_t parameter_1_len,
+                          const uint8_t *parameter_2,
+                          size_t parameter_2_len)
+{
+    char *out = NULL;
+    char *cursor = NULL;
+    size_t out_len = 0;
+    size_t i;
+    size_t prefix_len;
+
+    if (ssl->ctx->keylog_callback == NULL) return 1;
+
+    /*
+     * Our output buffer will contain the following strings, rendered with
+     * space characters in between, terminated by a NULL character: first the
+     * prefix, then the first parameter, then the second parameter. The
+     * meaning of each parameter depends on the specific key material being
+     * logged. Note that the first and second parameters are encoded in
+     * hexadecimal, so we need a buffer that is twice their lengths.
+     */
+    prefix_len = strlen(prefix);
+    out_len = prefix_len + (2*parameter_1_len) + (2*parameter_2_len) + 3;
+    if ((out = cursor = OPENSSL_malloc(out_len)) == NULL) {
+        SSLerr(SSL_F_NSS_KEYLOG_INT, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    strcpy(cursor, prefix);
+    cursor += prefix_len;
+    *cursor++ = ' ';
+
+    for (i = 0; i < parameter_1_len; i++) {
+        sprintf(cursor, "%02x", parameter_1[i]);
+        cursor += 2;
+    }
+    *cursor++ = ' ';
+
+    for (i = 0; i < parameter_2_len; i++) {
+        sprintf(cursor, "%02x", parameter_2[i]);
+        cursor += 2;
+    }
+    *cursor = '\0';
+
+    ssl->ctx->keylog_callback(ssl, (const char *)out);
+    OPENSSL_free(out);
+    return 1;
+
+}
+
+int ssl_log_rsa_client_key_exchange(SSL *ssl,
+                                    const uint8_t *encrypted_premaster,
+                                    size_t encrypted_premaster_len,
+                                    const uint8_t *premaster,
+                                    size_t premaster_len)
+{
+    if (encrypted_premaster_len < 8) {
+        SSLerr(SSL_F_SSL_LOG_RSA_CLIENT_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return nss_keylog_int("RSA",
+                          ssl,
+                          encrypted_premaster,
+                          encrypted_premaster_len,
+                          premaster,
+                          premaster_len);
+}
+
+int ssl_log_master_secret(SSL *ssl,
+                          const uint8_t *client_random,
+                          size_t client_random_len,
+                          const uint8_t *master,
+                          size_t master_len)
+{
+    /*
+     * TLSv1.3 changes the derivation of the master secret compared to earlier
+     * TLS versions, meaning that logging it out is less useful. Instead we
+     * want to log out other secrets: specifically, the handshake and
+     * application traffic secrets. For this reason, if this function is called
+     * for TLSv1.3 we don't bother logging, and just return success
+     * immediately.
+     */
+    if (SSL_IS_TLS13(ssl)) return 1;
+
+    if (client_random_len != 32) {
+        SSLerr(SSL_F_SSL_LOG_MASTER_SECRET, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return nss_keylog_int("CLIENT_RANDOM",
+                          ssl,
+                          client_random,
+                          client_random_len,
+                          master,
+                          master_len);
+}
+
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 10ae54c..27bfd9e 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -875,6 +875,12 @@
     int (*not_resumable_session_cb) (SSL *ssl, int is_forward_secure);
 
     CRYPTO_RWLOCK *lock;
+
+    /*
+     * Callback for logging key material for use with debugging tools like
+     * Wireshark. The callback should log `line` followed by a newline.
+     */
+    SSL_CTX_keylog_cb_func keylog_callback;
 };
 
 struct ssl_st {
@@ -2194,6 +2200,26 @@
 __owur const EVP_MD *ssl_handshake_md(SSL *s);
 __owur const EVP_MD *ssl_prf_md(SSL *s);
 
+/*
+ * ssl_log_rsa_client_key_exchange logs |premaster| to the SSL_CTX associated
+ * with |ssl|, if logging is enabled. It returns one on success and zero on
+ * failure. The entry is identified by the first 8 bytes of
+ * |encrypted_premaster|.
+ */
+__owur int ssl_log_rsa_client_key_exchange(SSL *ssl,
+                                           const uint8_t *encrypted_premaster,
+                                           size_t encrypted_premaster_len,
+                                           const uint8_t *premaster,
+                                           size_t premaster_len);
+
+/* ssl_log_master_secret logs |master| to the SSL_CTX associated with |ssl|, if
+ * logging is enabled. It returns one on success and zero on failure. The entry
+ * is identified by |client_random|.
+ */
+__owur int ssl_log_master_secret(SSL *ssl, const uint8_t *client_random,
+                                 size_t client_random_len,
+                                 const uint8_t *master, size_t master_len);
+
 /* s3_cbc.c */
 __owur char ssl3_cbc_record_digest_supported(const EVP_MD_CTX *ctx);
 __owur int ssl3_cbc_digest_record(const EVP_MD_CTX *ctx,
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 5eec0d1..80ae480 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -2258,7 +2258,7 @@
 
     return 1;
 }
-    
+
 
 MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt)
 {
@@ -2522,6 +2522,10 @@
     s->s3->tmp.pms = pms;
     s->s3->tmp.pmslen = pmslen;
 
+    /* Log the premaster secret, if logging is enabled. */
+    if (!ssl_log_rsa_client_key_exchange(s, encdata, enclen, pms, pmslen))
+        goto err;
+
     return 1;
  err:
     OPENSSL_clear_free(pms, pmslen);
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 03efdec..905a2cc 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -427,6 +427,12 @@
         goto err;
     }
 
+    /* Log the master secret, if logging is enabled. */
+    if (!ssl_log_master_secret(s, s->s3->client_random, SSL3_RANDOM_SIZE,
+                               s->session->master_key,
+                               s->session->master_key_length))
+        return 0;
+
     /*
      * Copy the finished so we can use it for renegotiation checks
      */
diff --git a/util/libssl.num b/util/libssl.num
index 730cff1..94def68 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -410,3 +410,5 @@
 SSL_write_ex                            411	1_1_1	EXIST::FUNCTION:
 SSL_COMP_get_id                         412	1_1_0d	EXIST::FUNCTION:
 SSL_COMP_get0_name                      413	1_1_0d	EXIST::FUNCTION:
+SSL_CTX_set_keylog_callback             414	1_1_1	EXIST::FUNCTION:
+SSL_CTX_get_keylog_callback             415	1_1_1	EXIST::FUNCTION: