The branch master has been updated
       via  b38ede8043439d99a3c6c174f17b91875cce66ac (commit)
      from  e454f3add638fda5c2aa32cd368c8929c0b1eb09 (commit)


- Log -----------------------------------------------------------------
commit b38ede8043439d99a3c6c174f17b91875cce66ac
Author: Tatsuhiro Tsujikawa <tatsuhir...@gmail.com>
Date:   Sun Feb 4 12:20:37 2018 +0900

    Export keying material using early exporter master secret
    
    This commit adds SSL_export_keying_material_early() which exports
    keying material using early exporter master secret.
    
    Reviewed-by: Rich Salz <rs...@openssl.org>
    Reviewed-by: Matt Caswell <m...@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5252)

-----------------------------------------------------------------------

Summary of changes:
 doc/man3/SSL_export_keying_material.pod | 31 +++++++++++--
 include/openssl/tls1.h                  | 13 ++++++
 ssl/ssl_lib.c                           | 12 +++++
 ssl/ssl_locl.h                          |  6 +++
 ssl/statem/statem.c                     | 15 ++++++
 ssl/statem/statem.h                     |  1 +
 ssl/tls13_enc.c                         | 70 ++++++++++++++++++++++++++++
 test/sslapitest.c                       | 82 +++++++++++++++++++++++++++++++++
 test/tls13secretstest.c                 |  5 ++
 util/libssl.num                         |  1 +
 10 files changed, 232 insertions(+), 4 deletions(-)

diff --git a/doc/man3/SSL_export_keying_material.pod 
b/doc/man3/SSL_export_keying_material.pod
index 532b144..586ad66 100644
--- a/doc/man3/SSL_export_keying_material.pod
+++ b/doc/man3/SSL_export_keying_material.pod
@@ -2,7 +2,9 @@
 
 =head1 NAME
 
-SSL_export_keying_material - obtain keying material for application use
+SSL_export_keying_material,
+SSL_export_keying_material_early
+- obtain keying material for application use
 
 =head1 SYNOPSIS
 
@@ -13,14 +15,29 @@ SSL_export_keying_material - obtain keying material for 
application use
                                 const unsigned char *context,
                                 size_t contextlen, int use_context);
 
+ int SSL_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
+                                      const char *label, size_t llen,
+                                      const unsigned char *context,
+                                      size_t contextlen);
+
 =head1 DESCRIPTION
 
 During the creation of a TLS or DTLS connection shared keying material is
-established between the two endpoints. The function 
SSL_export_keying_material()
-enables an application to use some of this keying material for its own purposes
-in accordance with RFC5705 (for TLSv1.2 and below) or RFCXXXX (for TLSv1.3).
+established between the two endpoints. The functions
+SSL_export_keying_material() and SSL_export_keying_material_early() enable an
+application to use some of this keying material for its own purposes in
+accordance with RFC5705 (for TLSv1.2 and below) or RFCXXXX (for TLSv1.3).
 TODO(TLS1.3): Update the RFC number when the RFC is published.
 
+SSL_export_keying_material() derives keying material using
+the F<exporter_master_secret> established in the handshake.
+
+SSL_export_keying_material_early() is only usable with TLSv1.3, and derives
+keying material using the F<early_exporter_master_secret> (as defined in the
+TLS 1.3 RFC). For the client, the F<early_exporter_master_secret> is only
+available when the client attempts to send 0-RTT data. For the server, it is
+only available when the server accepts 0-RTT data.
+
 An application may need to securely establish the context within which this
 keying material will be used. For example this may include identifiers for the
 application session, application algorithms or parameters, or the lifetime of
@@ -52,6 +69,12 @@ above. Attempting to use it in SSLv3 will result in an error.
 
 SSL_export_keying_material() returns 0 or -1 on failure or 1 on success.
 
+SSL_export_keying_material_early() returns 0 on failure or 1 on success.
+
+=head1 HISTORY
+
+SSL_export_keying_material_early() was first added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
 Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index ed0380f..f167856 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -232,6 +232,19 @@ __owur int SSL_export_keying_material(SSL *s, unsigned 
char *out, size_t olen,
                                       const unsigned char *context,
                                       size_t contextlen, int use_context);
 
+/*
+ * SSL_export_keying_material_early exports a value derived from the
+ * early exporter master secret, as specified in
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-23. It writes
+ * |olen| bytes to |out| given a label and optional context. It
+ * returns 1 on success and 0 otherwise.
+ */
+__owur int SSL_export_keying_material_early(SSL *s, unsigned char *out,
+                                            size_t olen, const char *label,
+                                            size_t llen,
+                                            const unsigned char *context,
+                                            size_t contextlen);
+
 int SSL_get_peer_signature_type_nid(const SSL *s, int *pnid);
 
 int SSL_get_sigalgs(SSL *s, int idx,
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 00e02f4..59b507e 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2810,6 +2810,18 @@ int SSL_export_keying_material(SSL *s, unsigned char 
*out, size_t olen,
                                                        contextlen, 
use_context);
 }
 
+int SSL_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
+                                     const char *label, size_t llen,
+                                     const unsigned char *context,
+                                     size_t contextlen)
+{
+    if (s->version != TLS1_3_VERSION)
+        return 0;
+
+    return tls13_export_keying_material_early(s, out, olen, label, llen,
+                                              context, contextlen);
+}
+
 static unsigned long ssl_session_hash(const SSL_SESSION *a)
 {
     const unsigned char *session_id = a->session_id;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index b590b53..0dd2a7b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1111,6 +1111,7 @@ struct ssl_st {
     unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
     unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
     unsigned char exporter_master_secret[EVP_MAX_MD_SIZE];
+    unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE];
     EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
     unsigned char read_iv[EVP_MAX_IV_LENGTH]; /* TLSv1.3 static read IV */
     EVP_MD_CTX *read_hash;      /* used for mac generation */
@@ -2406,6 +2407,11 @@ __owur int tls13_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);
+__owur int tls13_export_keying_material_early(SSL *s, unsigned char *out,
+                                              size_t olen, const char *label,
+                                              size_t llen,
+                                              const unsigned char *context,
+                                              size_t contextlen);
 __owur int tls1_alert_code(int code);
 __owur int tls13_alert_code(int code);
 __owur int ssl3_alert_code(int code);
diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c
index 818e648..a574853 100644
--- a/ssl/statem/statem.c
+++ b/ssl/statem/statem.c
@@ -951,3 +951,18 @@ int ossl_statem_export_allowed(SSL *s)
     return s->s3->previous_server_finished_len != 0
            && s->statem.hand_state != TLS_ST_SW_FINISHED;
 }
+
+/*
+ * Return 1 if early TLS exporter is ready to export keying material,
+ * or 0 if otherwise.
+ */
+int ossl_statem_export_early_allowed(SSL *s)
+{
+    /*
+     * The early exporter secret is only present on the server if we
+     * have accepted early_data. It is present on the client as long
+     * as we have sent early_data.
+     */
+    return s->ext.early_data == SSL_EARLY_DATA_ACCEPTED
+           || (!s->server && s->ext.early_data != SSL_EARLY_DATA_NOT_SENT);
+}
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 58cc4f4..1935718 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -133,6 +133,7 @@ void ossl_statem_check_finish_init(SSL *s, int send);
 void ossl_statem_set_hello_verify_done(SSL *s);
 __owur int ossl_statem_app_data_allowed(SSL *s);
 __owur int ossl_statem_export_allowed(SSL *s);
+__owur int ossl_statem_export_early_allowed(SSL *s);
 
 /* Flush the write BIO */
 int statem_flush(SSL *s);
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 9311866..6332804 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -365,6 +365,7 @@ int tls13_change_cipher_state(SSL *s, int which)
     static const unsigned char server_application_traffic[] = "s ap traffic";
     static const unsigned char exporter_master_secret[] = "exp master";
     static const unsigned char resumption_master_secret[] = "res master";
+    static const unsigned char early_exporter_master_secret[] = "e exp master";
     unsigned char *iv;
     unsigned char secret[EVP_MAX_MD_SIZE];
     unsigned char hashval[EVP_MAX_MD_SIZE];
@@ -481,6 +482,16 @@ int tls13_change_cipher_state(SSL *s, int which)
             }
             hashlen = hashlenui;
             EVP_MD_CTX_free(mdctx);
+
+            if (!tls13_hkdf_expand(s, md, insecret,
+                                   early_exporter_master_secret,
+                                   sizeof(early_exporter_master_secret) - 1,
+                                   hashval, hashlen,
+                                   s->early_exporter_master_secret, hashlen)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS13_CHANGE_CIPHER_STATE, 
ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
         } else if (which & SSL3_CC_HANDSHAKE) {
             insecret = s->handshake_secret;
             finsecret = s->client_finished_secret;
@@ -690,3 +701,62 @@ int tls13_export_keying_material(SSL *s, unsigned char 
*out, size_t olen,
     EVP_MD_CTX_free(ctx);
     return ret;
 }
+
+int tls13_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
+                                       const char *label, size_t llen,
+                                       const unsigned char *context,
+                                       size_t contextlen)
+{
+    static const unsigned char exporterlabel[] = "exporter";
+    unsigned char exportsecret[EVP_MAX_MD_SIZE];
+    unsigned char hash[EVP_MAX_MD_SIZE], data[EVP_MAX_MD_SIZE];
+    const EVP_MD *md;
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    unsigned int hashsize, datalen;
+    int ret = 0;
+    const SSL_CIPHER *sslcipher;
+
+    if (ctx == NULL || !ossl_statem_export_early_allowed(s))
+        goto err;
+
+    if (!s->server && s->max_early_data > 0
+            && s->session->ext.max_early_data == 0)
+        sslcipher = SSL_SESSION_get0_cipher(s->psksession);
+    else
+        sslcipher = SSL_SESSION_get0_cipher(s->session);
+
+    md = ssl_md(sslcipher->algorithm2);
+
+    /*
+     * Calculate the hash value and store it in |data|. The reason why
+     * the empty string is used is that the definition of TLS-Exporter
+     * is like so:
+     *
+     * TLS-Exporter(label, context_value, key_length) =
+     *     HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
+     *                       "exporter", Hash(context_value), key_length)
+     *
+     * Derive-Secret(Secret, Label, Messages) =
+     *       HKDF-Expand-Label(Secret, Label,
+     *                         Transcript-Hash(Messages), Hash.length)
+     *
+     * Here Transcript-Hash is the cipher suite hash algorithm.
+     */
+    if (EVP_DigestInit_ex(ctx, md, NULL) <= 0
+            || EVP_DigestUpdate(ctx, context, contextlen) <= 0
+            || EVP_DigestFinal_ex(ctx, hash, &hashsize) <= 0
+            || EVP_DigestInit_ex(ctx, md, NULL) <= 0
+            || EVP_DigestFinal_ex(ctx, data, &datalen) <= 0
+            || !tls13_hkdf_expand(s, md, s->early_exporter_master_secret,
+                                  (const unsigned char *)label, llen,
+                                  data, datalen, exportsecret, hashsize)
+            || !tls13_hkdf_expand(s, md, exportsecret, exporterlabel,
+                                  sizeof(exporterlabel) - 1, hash, hashsize,
+                                  out, olen))
+        goto err;
+
+    ret = 1;
+ err:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 0b6af66..1cf5c4f 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -3157,6 +3157,85 @@ static int test_export_key_mat(int tst)
     return testresult;
 }
 
+#ifndef OPENSSL_NO_TLS1_3
+/*
+ * Test that SSL_export_keying_material_early() produces expected
+ * results. There are no test vectors so all we do is test that both
+ * sides of the communication produce the same results for different
+ * protocol versions.
+ */
+static int test_export_key_mat_early(int idx)
+{
+    static const char label[] = "test label";
+    static const unsigned char context[] = "context";
+    int testresult = 0;
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    SSL_SESSION *sess = NULL;
+    const unsigned char *emptycontext = NULL;
+    unsigned char ckeymat1[80], ckeymat2[80];
+    unsigned char skeymat1[80], skeymat2[80];
+    unsigned char buf[1];
+    size_t readbytes, written;
+
+    if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl, &serverssl,
+                                        &sess, idx)))
+        goto end;
+
+    /* Here writing 0 length early data is enough. */
+    if (!TEST_true(SSL_write_early_data(clientssl, NULL, 0, &written))
+            || !TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
+                                                &readbytes),
+                            SSL_READ_EARLY_DATA_ERROR)
+            || !TEST_int_eq(SSL_get_early_data_status(serverssl),
+                            SSL_EARLY_DATA_ACCEPTED))
+        goto end;
+
+    if (!TEST_int_eq(SSL_export_keying_material_early(
+                     clientssl, ckeymat1, sizeof(ckeymat1), label,
+                     sizeof(label) - 1, context, sizeof(context) - 1), 1)
+            || !TEST_int_eq(SSL_export_keying_material_early(
+                            clientssl, ckeymat2, sizeof(ckeymat2), label,
+                            sizeof(label) - 1, emptycontext, 0), 1)
+            || !TEST_int_eq(SSL_export_keying_material_early(
+                            serverssl, skeymat1, sizeof(skeymat1), label,
+                            sizeof(label) - 1, context, sizeof(context) - 1), 
1)
+            || !TEST_int_eq(SSL_export_keying_material_early(
+                            serverssl, skeymat2, sizeof(skeymat2), label,
+                            sizeof(label) - 1, emptycontext, 0), 1)
+               /*
+                * Check that both sides created the same key material with the
+                * same context.
+                */
+            || !TEST_mem_eq(ckeymat1, sizeof(ckeymat1), skeymat1,
+                            sizeof(skeymat1))
+               /*
+                * Check that both sides created the same key material with an
+                * empty context.
+                */
+            || !TEST_mem_eq(ckeymat2, sizeof(ckeymat2), skeymat2,
+                            sizeof(skeymat2))
+               /* Different contexts should produce different results */
+            || !TEST_mem_ne(ckeymat1, sizeof(ckeymat1), ckeymat2,
+                            sizeof(ckeymat2)))
+        goto end;
+
+    testresult = 1;
+
+ end:
+    if (sess != clientpsk)
+        SSL_SESSION_free(sess);
+    SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+#endif /* OPENSSL_NO_TLS1_3 */
+
 static int test_ssl_clear(int idx)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
@@ -3431,6 +3510,9 @@ int setup_tests(void)
 #endif
     ADD_ALL_TESTS(test_serverinfo, 8);
     ADD_ALL_TESTS(test_export_key_mat, 4);
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_ALL_TESTS(test_export_key_mat_early, 3);
+#endif
     ADD_ALL_TESTS(test_ssl_clear, 2);
     ADD_ALL_TESTS(test_max_fragment_len_ext, 
OSSL_NELEM(max_fragment_len_test));
     return 1;
diff --git a/test/tls13secretstest.c b/test/tls13secretstest.c
index c19bd2c..8b775b8 100644
--- a/test/tls13secretstest.c
+++ b/test/tls13secretstest.c
@@ -217,6 +217,11 @@ int ossl_statem_export_allowed(SSL *s)
     return 1;
 }
 
+int ossl_statem_export_early_allowed(SSL *s)
+{
+    return 1;
+}
+
 /* End of mocked out code */
 
 static int test_secret(SSL *s, unsigned char *prk,
diff --git a/util/libssl.num b/util/libssl.num
index 866ff53..48c5eca 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -476,3 +476,4 @@ SSL_SESSION_get_max_fragment_length     476 1_1_1   
EXIST::FUNCTION:
 SSL_stateless                           477    1_1_1   EXIST::FUNCTION:
 SSL_verify_client_post_handshake        478    1_1_1   EXIST::FUNCTION:
 SSL_force_post_handshake_auth           479    1_1_1   EXIST::FUNCTION:
+SSL_export_keying_material_early        480    1_1_1   EXIST::FUNCTION:
_____
openssl-commits mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-commits

Reply via email to