[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Thu Aug 31 14:39:14 UTC 2017


The branch master has been updated
       via  0ef2802165706016698d6984dfcb2980881f18e5 (commit)
       via  57dee9bb684268aa434a2bfe7ff4743a14a62ff0 (commit)
       via  4be3a7c7aa8bc93ba68253638030d2e5a92bc946 (commit)
       via  fff202e5f7312f60285a61592301189d35b8450e (commit)
       via  976e53232d4ee8174e2e0f5a15ad04a9d7bea351 (commit)
       via  630369d9ce4f6bf67c4a055790362cd27b32229e (commit)
       via  ae8d7d994afb38c8309770f68212bf51380b8941 (commit)
       via  ffc5bbaaee2bfaba8d420e912c4d77b4090b896f (commit)
       via  67738645dc0b044fc7d120a3c67af5635d0d78ec (commit)
       via  dd5b98c55a64f574958a1a8aef2546692ff30ad9 (commit)
       via  db919b1e255fefb7cde711899709ac0e0e8d7734 (commit)
       via  c5de99a2d90b0714eeda4943444e3a6bfbc525ad (commit)
       via  087175449922ddc3063e37f61e2c4330f3cf0468 (commit)
       via  e105ae842f4a1ac7d710baefde34773d1a52af3c (commit)
       via  e17e1df77e651e557ad8fc67e2bea0bbcf98f153 (commit)
       via  02a3ed5a95ca0cb9f5173343dc10739a354713ac (commit)
       via  98e1d93454a5d0b34e929f28528756ba9a636e5c (commit)
       via  add8d0e9e0bb80728f4b89d15573bf2e70596ceb (commit)
      from  177503752b24299cc97ccf07062a3b79c4f28899 (commit)


- Log -----------------------------------------------------------------
commit 0ef2802165706016698d6984dfcb2980881f18e5
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Aug 31 14:32:51 2017 +0100

    Various review fixes for PSK early_data support
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 57dee9bb684268aa434a2bfe7ff4743a14a62ff0
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Aug 17 13:16:19 2017 +0100

    Test for late client side detection of ALPN inconsistenties
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 4be3a7c7aa8bc93ba68253638030d2e5a92bc946
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Aug 16 12:50:32 2017 +0100

    Client side sanity check of ALPN after server has accepted early_data
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit fff202e5f7312f60285a61592301189d35b8450e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Aug 3 16:30:31 2017 +0100

    Add some fixes for Travis failures
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 976e53232d4ee8174e2e0f5a15ad04a9d7bea351
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Aug 3 15:06:57 2017 +0100

    Add PSK early_data tests
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 630369d9ce4f6bf67c4a055790362cd27b32229e
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Aug 1 15:45:29 2017 +0100

    Add server side sanity checks of SNI/ALPN for use with early_data
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit ae8d7d994afb38c8309770f68212bf51380b8941
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jul 31 11:42:48 2017 +0100

    Make sure we save ALPN data in the session
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit ffc5bbaaee2bfaba8d420e912c4d77b4090b896f
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jul 21 11:41:05 2017 +0100

    Complain if we are writing early data but SNI or ALPN is incorrect
    
    SNI and ALPN must be set to be consistent with the PSK. Otherwise this is
    an error.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 67738645dc0b044fc7d120a3c67af5635d0d78ec
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Aug 3 10:13:31 2017 +0100

    Add functions for getting/setting SNI/ALPN info in SSL_SESSION
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit dd5b98c55a64f574958a1a8aef2546692ff30ad9
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jul 21 11:40:28 2017 +0100

    Show the error stack if there was an error writing early data in s_client
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit db919b1e255fefb7cde711899709ac0e0e8d7734
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Aug 1 15:46:29 2017 +0100

    Update the tests for SNI changes
    
    If there is no SNI in the session then s_client no longer sends the SNI
    extension. Update the tests to take account of that
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit c5de99a2d90b0714eeda4943444e3a6bfbc525ad
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jul 21 11:39:01 2017 +0100

    If no SNI has been explicitly set use the one from the session
    
    If we have not decided on an SNI value yet, but we are attempting to reuse
    a session, and SNI is set in that, then we should use that value by
    default.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 087175449922ddc3063e37f61e2c4330f3cf0468
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jul 19 17:26:00 2017 +0100

    Make sure we use the correct cipher when using the early_secret
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit e105ae842f4a1ac7d710baefde34773d1a52af3c
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jul 13 18:02:40 2017 +0100

    Add HISTORY and SEE ALSO sections for the new TLSv1.3 PSK functions
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit e17e1df77e651e557ad8fc67e2bea0bbcf98f153
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jul 13 18:02:18 2017 +0100

    Add documentation for SSL_SESSION_set_max_early_data()
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 02a3ed5a95ca0cb9f5173343dc10739a354713ac
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jul 13 14:07:34 2017 +0100

    Add some PSK early_data tests
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit 98e1d93454a5d0b34e929f28528756ba9a636e5c
Author: Matt Caswell <matt at openssl.org>
Date:   Sat Jul 8 11:42:55 2017 +0100

    Add SSL_SESSION_set_max_early_data()
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

commit add8d0e9e0bb80728f4b89d15573bf2e70596ceb
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jul 5 20:53:03 2017 +0100

    Enable the ability to use an external PSK for sending early_data
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/3926)

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

Summary of changes:
 apps/s_client.c                              |  26 +-
 crypto/err/openssl.txt                       |   2 +
 doc/man3/SSL_CTX_set_psk_client_callback.pod |  14 +
 doc/man3/SSL_CTX_use_psk_identity_hint.pod   |  10 +
 doc/man3/SSL_SESSION_get0_hostname.pod       |  32 +-
 doc/man3/SSL_read_early_data.pod             |  13 +-
 include/openssl/ssl.h                        |   9 +
 include/openssl/sslerr.h                     |   2 +
 ssl/record/ssl3_record.c                     |  19 +-
 ssl/record/ssl3_record_tls13.c               |   9 +-
 ssl/ssl_err.c                                |   4 +
 ssl/ssl_lib.c                                |   8 +-
 ssl/ssl_locl.h                               |   2 +
 ssl/ssl_sess.c                               |  46 +++
 ssl/statem/extensions.c                      |  61 +++-
 ssl/statem/extensions_clnt.c                 | 148 ++++++--
 ssl/statem/extensions_srvr.c                 |   9 +-
 ssl/statem/statem_locl.h                     |   2 +
 ssl/statem/statem_srvr.c                     |  43 ++-
 ssl/tls13_enc.c                              |  21 +-
 test/recipes/70-test_sslmessages.t           |   3 +-
 test/recipes/70-test_tls13kexmodes.t         |   1 +
 test/recipes/70-test_tls13messages.t         |  16 +-
 test/recipes/70-test_tls13psk.t              |   1 +
 test/sslapitest.c                            | 485 +++++++++++++++++++++------
 util/libssl.num                              |   4 +
 26 files changed, 820 insertions(+), 170 deletions(-)

diff --git a/apps/s_client.c b/apps/s_client.c
index 5a4a2f6..4d2fa86 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -1888,6 +1888,25 @@ int s_client_main(int argc, char **argv)
             ERR_print_errors(bio_err);
             goto end;
         }
+        /* By default the SNI should be the same as was set in the session */
+        if (!noservername && servername == NULL) {
+            const char *sni = SSL_SESSION_get0_hostname(sess);
+
+            if (sni != NULL) {
+                servername = OPENSSL_strdup(sni);
+                if (servername == NULL) {
+                    BIO_printf(bio_err, "Can't set server name\n");
+                    ERR_print_errors(bio_err);
+                    goto end;
+                }
+            } else {
+                /*
+                 * Force no SNI to be sent so we are consistent with the
+                 * session.
+                 */
+                noservername = 1;
+            }
+        }
         SSL_SESSION_free(sess);
     }
 
@@ -2600,8 +2619,10 @@ int s_client_main(int argc, char **argv)
     }
 
     if (early_data_file != NULL
-            && SSL_get0_session(con) != NULL
-            && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0) {
+            && ((SSL_get0_session(con) != NULL
+                 && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0)
+                || (psksess != NULL
+                    && SSL_SESSION_get_max_early_data(psksess) > 0))) {
         BIO *edfile = BIO_new_file(early_data_file, "r");
         size_t readbytes, writtenbytes;
         int finish = 0;
@@ -2625,6 +2646,7 @@ int s_client_main(int argc, char **argv)
                 default:
                     BIO_printf(bio_err, "Error writing early data\n");
                     BIO_free(edfile);
+                    ERR_print_errors(bio_err);
                     goto shut;
                 }
             }
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 517c9f4..4b3c55b 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2320,6 +2320,8 @@ SSL_R_ILLEGAL_POINT_COMPRESSION:162:illegal point compression
 SSL_R_ILLEGAL_SUITEB_DIGEST:380:illegal Suite B digest
 SSL_R_INAPPROPRIATE_FALLBACK:373:inappropriate fallback
 SSL_R_INCONSISTENT_COMPRESSION:340:inconsistent compression
+SSL_R_INCONSISTENT_EARLY_DATA_ALPN:222:inconsistent early data alpn
+SSL_R_INCONSISTENT_EARLY_DATA_SNI:231:inconsistent early data sni
 SSL_R_INCONSISTENT_EXTMS:104:inconsistent extms
 SSL_R_INVALID_ALERT:205:invalid alert
 SSL_R_INVALID_COMMAND:280:invalid command
diff --git a/doc/man3/SSL_CTX_set_psk_client_callback.pod b/doc/man3/SSL_CTX_set_psk_client_callback.pod
index 919b6af..e771072 100644
--- a/doc/man3/SSL_CTX_set_psk_client_callback.pod
+++ b/doc/man3/SSL_CTX_set_psk_client_callback.pod
@@ -98,6 +98,10 @@ be TLS1_3_VERSION.
 
 =back
 
+Additionally the maximum early data value should be set via a call to
+L<SSL_SESSION_set_max_early_data(3)> if the PSK will be used for sending early
+data.
+
 Alternatively an SSL_SESSION created from a previous non-PSK handshake may also
 be used as the basis for a PSK.
 
@@ -130,6 +134,16 @@ the connection setup fails.
 The SSL_psk_use_session_cb_func callback should return 1 on success or 0 on
 failure. In the event of failure the connection setup fails.
 
+=head1 SEE ALSO
+
+L<SSL_CTX_set_psk_find_session_callback(3)>,
+L<SSL_set_psk_find_session_callback(3)>
+
+=head1 HISTORY
+
+SSL_CTX_set_psk_use_session_callback() and SSL_set_psk_use_session_callback()
+were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
 Copyright 2006-2017 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/SSL_CTX_use_psk_identity_hint.pod b/doc/man3/SSL_CTX_use_psk_identity_hint.pod
index 4ded544..d41c0cc 100644
--- a/doc/man3/SSL_CTX_use_psk_identity_hint.pod
+++ b/doc/man3/SSL_CTX_use_psk_identity_hint.pod
@@ -115,6 +115,16 @@ completely.
 The B<SSL_psk_find_session_cb_func> callback should return 1 on success or 0 on
 failure. In the event of failure the connection setup fails.
 
+=head1 SEE ALSO
+
+L<SSL_CTX_set_psk_use_session_callback(3)>,
+L<SSL_set_psk_use_session_callback(3)>
+
+=head1 HISTORY
+
+SSL_CTX_set_psk_find_session_callback() and SSL_set_psk_find_session_callback()
+were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
 Copyright 2006-2017 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/SSL_SESSION_get0_hostname.pod b/doc/man3/SSL_SESSION_get0_hostname.pod
index 4ed7e40..f0f02d3 100644
--- a/doc/man3/SSL_SESSION_get0_hostname.pod
+++ b/doc/man3/SSL_SESSION_get0_hostname.pod
@@ -2,13 +2,24 @@
 
 =head1 NAME
 
-SSL_SESSION_get0_hostname - retrieve the SNI hostname associated with a session
+SSL_SESSION_get0_hostname,
+SSL_SESSION_set1_hostname,
+SSL_SESSION_get0_alpn_selected,
+SSL_SESSION_set1_alpn_selected
+- get and set SNI and ALPN data ssociated with a session
 
 =head1 SYNOPSIS
 
  #include <openssl/ssl.h>
 
  const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s);
+ int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname);
+
+ void SSL_SESSION_get0_alpn_selected(const SSL_SESSION *s,
+                                     const unsigned char **alpn,
+                                     size_t *len);
+ int SSL_SESSION_set1_alpn_selected(SSL_SESSION *s, const unsigned char *alpn,
+                                    size_t len);
 
 =head1 DESCRIPTION
 
@@ -18,6 +29,18 @@ client when the session was created, or NULL if no value was sent.
 The value returned is a pointer to memory maintained within B<s> and
 should not be free'd.
 
+SSL_SESSION_set1_hostname() sets the SNI value for the hostname to a copy of
+the string provided in hostname.
+
+SSL_SESSION_get0_alpn_selected() retrieves the selected ALPN protocol for this
+session and its associated length in bytes. The returned value of B<*alpn> is a
+pointer to memory maintained within B<s> and should not be free'd.
+
+SSL_SESSION_set1_alpn_selected() sets the ALPN protocol for this session to the
+value in B<alpn> which should be of length B<len> bytes. A copy of the input
+value is made, and the caller retains ownership of the memory pointed to by
+B<alpn>.
+
 =head1 SEE ALSO
 
 L<ssl(7)>,
@@ -25,9 +48,14 @@ L<d2i_SSL_SESSION(3)>,
 L<SSL_SESSION_get_time(3)>,
 L<SSL_SESSION_free(3)>
 
+=head1 HISTORY
+
+SSL_SESSION_set1_hostname(), SSL_SESSION_get0_alpn_selected() and
+SSL_SESSION_set1_alpn_selected() were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
-Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2016-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
diff --git a/doc/man3/SSL_read_early_data.pod b/doc/man3/SSL_read_early_data.pod
index f0237fa..1073684 100644
--- a/doc/man3/SSL_read_early_data.pod
+++ b/doc/man3/SSL_read_early_data.pod
@@ -7,6 +7,7 @@ SSL_CTX_set_max_early_data,
 SSL_get_max_early_data,
 SSL_CTX_get_max_early_data,
 SSL_SESSION_get_max_early_data,
+SSL_SESSION_set_max_early_data,
 SSL_write_early_data,
 SSL_read_early_data,
 SSL_get_early_data_status
@@ -21,6 +22,7 @@ SSL_get_early_data_status
  int SSL_set_max_early_data(SSL *s, uint32_t max_early_data);
  uint32_t SSL_get_max_early_data(const SSL *s);
  uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s);
+ int SSL_SESSION_set_max_early_data(SSL_SESSION *s, uint32_t max_early_data);
 
  int SSL_write_early_data(SSL *s, const void *buf, size_t num, size_t *written);
 
@@ -59,6 +61,12 @@ determine if a session established with a server can be used to send early data.
 If the session cannot be used then this function will return 0. Otherwise it
 will return the maximum number of early data bytes that can be sent.
 
+The function SSL_SESSION_set_max_early_data() sets the maximum number of early
+data bytes that can be sent for a session. This would typically be used when
+creating a PSK session file (see L<SSL_CTX_set_psk_use_session_callback(3)>). If
+using a ticket based PSK then this is set automatically to the value provided by
+the server.
+
 A client uses the function SSL_write_early_data() to send early data. This
 function is similar to the L<SSL_write_ex(3)> function, but with the following
 differences. See L<SSL_write_ex(3)> for information on how to write bytes to
@@ -207,8 +215,8 @@ SSL_get_max_early_data(), SSL_CTX_get_max_early_data() and
 SSL_SESSION_get_max_early_data() return the maximum number of early data bytes
 that may be sent.
 
-SSL_set_max_early_data() and SSL_CTX_set_max_early_data() return 1 for success
-or 0 for failure.
+SSL_set_max_early_data(), SSL_CTX_set_max_early_data() and
+SSL_SESSION_set_max_early_data() return 1 for success or 0 for failure.
 
 SSL_get_early_data_status() returns SSL_EARLY_DATA_ACCEPTED if early data was
 accepted by the server, SSL_EARLY_DATA_REJECTED if early data was rejected by
@@ -222,6 +230,7 @@ L<SSL_read_ex(3)>,
 L<SSL_connect(3)>,
 L<SSL_accept(3)>,
 L<SSL_do_handshake(3)>,
+L<SSL_CTX_set_psk_use_session_callback(3)>,
 L<ssl(7)>
 
 =head1 HISTORY
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 237c086..248408f 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1535,6 +1535,13 @@ __owur int SSL_SESSION_get_protocol_version(const SSL_SESSION *s);
 __owur int SSL_SESSION_set_protocol_version(SSL_SESSION *s, int version);
 
 __owur const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s);
+__owur int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname);
+void SSL_SESSION_get0_alpn_selected(const SSL_SESSION *s,
+                                    const unsigned char **alpn,
+                                    size_t *len);
+__owur int SSL_SESSION_set1_alpn_selected(SSL_SESSION *s,
+                                          const unsigned char *alpn,
+                                          size_t len);
 __owur const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *s);
 __owur int SSL_SESSION_set_cipher(SSL_SESSION *s, const SSL_CIPHER *cipher);
 __owur int SSL_SESSION_has_ticket(const SSL_SESSION *s);
@@ -1542,6 +1549,8 @@ __owur unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s);
 void SSL_SESSION_get0_ticket(const SSL_SESSION *s, const unsigned char **tick,
                             size_t *len);
 __owur uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s);
+__owur int SSL_SESSION_set_max_early_data(SSL_SESSION *s,
+                                          uint32_t max_early_data);
 __owur int SSL_copy_session_id(SSL *to, const SSL *from);
 __owur X509 *SSL_SESSION_get0_peer(SSL_SESSION *s);
 __owur int SSL_SESSION_set1_id_context(SSL_SESSION *s, const unsigned char *sid_ctx,
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index bc4c17e..91f6d21 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -458,6 +458,8 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_ILLEGAL_SUITEB_DIGEST                      380
 # define SSL_R_INAPPROPRIATE_FALLBACK                     373
 # define SSL_R_INCONSISTENT_COMPRESSION                   340
+# define SSL_R_INCONSISTENT_EARLY_DATA_ALPN               222
+# define SSL_R_INCONSISTENT_EARLY_DATA_SNI                231
 # define SSL_R_INCONSISTENT_EXTMS                         104
 # define SSL_R_INVALID_ALERT                              205
 # define SSL_R_INVALID_COMMAND                            280
diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c
index ae48504..fa7f5d9 100644
--- a/ssl/record/ssl3_record.c
+++ b/ssl/record/ssl3_record.c
@@ -104,15 +104,24 @@ static int ssl3_record_app_data_waiting(SSL *s)
 int early_data_count_ok(SSL *s, size_t length, size_t overhead, int *al)
 {
     uint32_t max_early_data = s->max_early_data;
+    SSL_SESSION *sess = s->session;
 
     /*
      * If we are a client then we always use the max_early_data from the
-     * session. Otherwise we go with the lowest out of the max early data set in
-     * the session and the configured max_early_data.
+     * session/psksession. Otherwise we go with the lowest out of the max early
+     * data set in the session and the configured max_early_data.
      */
-    if (!s->server || (s->hit
-                       && s->session->ext.max_early_data < s->max_early_data))
-        max_early_data = s->session->ext.max_early_data;
+    if (!s->server && sess->ext.max_early_data == 0) {
+        if (!ossl_assert(s->psksession != NULL
+                         && s->psksession->ext.max_early_data > 0)) {
+            SSLerr(SSL_F_EARLY_DATA_COUNT_OK, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        sess = s->psksession;
+    }
+    if (!s->server
+            || (s->hit && sess->ext.max_early_data < s->max_early_data))
+        max_early_data = sess->ext.max_early_data;
 
     if (max_early_data == 0) {
         if (al != NULL)
diff --git a/ssl/record/ssl3_record_tls13.c b/ssl/record/ssl3_record_tls13.c
index ec8f9f9..696cc37 100644
--- a/ssl/record/ssl3_record_tls13.c
+++ b/ssl/record/ssl3_record_tls13.c
@@ -58,7 +58,14 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
 
     if (s->early_data_state == SSL_EARLY_DATA_WRITING
             || s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY) {
-        alg_enc = s->session->cipher->algorithm_enc;
+        if (s->session != NULL && s->session->ext.max_early_data > 0) {
+            alg_enc = s->session->cipher->algorithm_enc;
+        } else {
+            if (!ossl_assert(s->psksession != NULL
+                             && s->psksession->ext.max_early_data > 0))
+                return -1;
+            alg_enc = s->psksession->cipher->algorithm_enc;
+        }
     } else {
         /*
          * To get here we must have selected a ciphersuite - otherwise ctx would
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index dc1d439..0ce7f27 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -724,6 +724,10 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "inappropriate fallback"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_COMPRESSION),
     "inconsistent compression"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EARLY_DATA_ALPN),
+    "inconsistent early data alpn"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EARLY_DATA_SNI),
+    "inconsistent early data sni"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EXTMS), "inconsistent extms"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_ALERT), "invalid alert"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "invalid command"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index cac8820..70f4acf 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -534,6 +534,9 @@ int SSL_clear(SSL *s)
     }
     SSL_SESSION_free(s->psksession);
     s->psksession = NULL;
+    OPENSSL_free(s->psksession_id);
+    s->psksession_id = NULL;
+    s->psksession_id_len = 0;
 
     s->error = 0;
     s->hit = 0;
@@ -1097,6 +1100,7 @@ void SSL_free(SSL *s)
         SSL_SESSION_free(s->session);
     }
     SSL_SESSION_free(s->psksession);
+    OPENSSL_free(s->psksession_id);
 
     clear_ciphers(s);
 
@@ -1910,8 +1914,8 @@ int SSL_write_early_data(SSL *s, const void *buf, size_t num, size_t *written)
     case SSL_EARLY_DATA_NONE:
         if (s->server
                 || !SSL_in_before(s)
-                || s->session == NULL
-                || s->session->ext.max_early_data == 0) {
+                || ((s->session == NULL || s->session->ext.max_early_data == 0)
+                     && (s->psk_use_session_cb == NULL))) {
             SSLerr(SSL_F_SSL_WRITE_EARLY_DATA,
                    ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
             return 0;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 4896c35..7caec67 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1119,6 +1119,8 @@ struct ssl_st {
     SSL_SESSION *session;
     /* TLSv1.3 PSK session */
     SSL_SESSION *psksession;
+    unsigned char *psksession_id;
+    size_t psksession_id_len;
     /* Default generate session ID callback. */
     GEN_SESSION_CB generate_session_id;
     /* Used in SSL3 */
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 575cd70..1482a3e 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -906,6 +906,18 @@ const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s)
     return s->ext.hostname;
 }
 
+int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname)
+{
+    OPENSSL_free(s->ext.hostname);
+    if (hostname == NULL) {
+        s->ext.hostname = NULL;
+        return 1;
+    }
+    s->ext.hostname = OPENSSL_strdup(hostname);
+
+    return s->ext.hostname != NULL;
+}
+
 int SSL_SESSION_has_ticket(const SSL_SESSION *s)
 {
     return (s->ext.ticklen > 0) ? 1 : 0;
@@ -929,6 +941,40 @@ uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s)
     return s->ext.max_early_data;
 }
 
+int SSL_SESSION_set_max_early_data(SSL_SESSION *s, uint32_t max_early_data)
+{
+    s->ext.max_early_data = max_early_data;
+
+    return 1;
+}
+
+void SSL_SESSION_get0_alpn_selected(const SSL_SESSION *s,
+                                    const unsigned char **alpn,
+                                    size_t *len)
+{
+    *alpn = s->ext.alpn_selected;
+    *len = s->ext.alpn_selected_len;
+}
+
+int SSL_SESSION_set1_alpn_selected(SSL_SESSION *s, const unsigned char *alpn,
+                                   size_t len)
+{
+    OPENSSL_free(s->ext.alpn_selected);
+    if (alpn == NULL || len == 0) {
+        s->ext.alpn_selected = NULL;
+        s->ext.alpn_selected_len = 0;
+        return 1;
+    }
+    s->ext.alpn_selected = OPENSSL_memdup(alpn, len);
+    if (s->ext.alpn_selected == NULL) {
+        s->ext.alpn_selected_len = 0;
+        return 0;
+    }
+    s->ext.alpn_selected_len = len;
+
+    return 1;
+}
+
 X509 *SSL_SESSION_get0_peer(SSL_SESSION *s)
 {
     return s->peer;
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index c435405..b8ab5c8 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -29,6 +29,7 @@ static int init_status_request(SSL *s, unsigned int context);
 static int init_npn(SSL *s, unsigned int context);
 #endif
 static int init_alpn(SSL *s, unsigned int context);
+static int final_alpn(SSL *s, unsigned int context, int sent, int *al);
 static int init_sig_algs(SSL *s, unsigned int context);
 static int init_certificate_authorities(SSL *s, unsigned int context);
 static EXT_RETURN tls_construct_certificate_authorities(SSL *s, WPACKET *pkt,
@@ -203,7 +204,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
         SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
         | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
         init_alpn, tls_parse_ctos_alpn, tls_parse_stoc_alpn,
-        tls_construct_stoc_alpn, tls_construct_ctos_alpn, NULL
+        tls_construct_stoc_alpn, tls_construct_ctos_alpn, final_alpn
     },
 #ifndef OPENSSL_NO_SRTP
     {
@@ -843,6 +844,8 @@ static int final_server_name(SSL *s, unsigned int context, int sent,
 
     case SSL_TLSEXT_ERR_NOACK:
         s->servername_done = 0;
+        if (s->server && s->session->ext.hostname != NULL)
+            s->ext.early_data_ok = 0;
         return 1;
 
     default:
@@ -940,6 +943,24 @@ static int init_alpn(SSL *s, unsigned int context)
     return 1;
 }
 
+static int final_alpn(SSL *s, unsigned int context, int sent, int *al)
+{
+    if (!s->server && !sent && s->session->ext.alpn_selected != NULL)
+            s->ext.early_data_ok = 0;
+
+    if (!s->server || !SSL_IS_TLS13(s))
+        return 1;
+
+    /*
+     * Call alpn_select callback if needed.  Has to be done after SNI and
+     * cipher negotiation (HTTP/2 restricts permitted ciphers). In TLSv1.3
+     * we also have to do this before we decide whether to accept early_data.
+     * In TLSv1.3 we've already negotiated our cipher so we do this call now.
+     * For < TLSv1.3 we defer it until after cipher negotiation.
+     */
+    return tls_handle_alpn(s, al);
+}
+
 static int init_sig_algs(SSL *s, unsigned int context)
 {
     /* Clear any signature algorithms extension received */
@@ -1206,6 +1227,13 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
     const char *label;
     size_t bindersize, labelsize, hashsize = EVP_MD_size(md);
     int ret = -1;
+    int usepskfored = 0;
+
+    if (external
+            && s->early_data_state == SSL_EARLY_DATA_CONNECTING
+            && s->session->ext.max_early_data == 0
+            && sess->ext.max_early_data > 0)
+        usepskfored = 1;
 
     if (external) {
         label = external_label;
@@ -1236,11 +1264,12 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
     /*
      * Generate the early_secret. On the server side we've selected a PSK to
      * resume with (internal or external) so we always do this. On the client
-     * side we do this for a non-external (i.e. resumption) PSK so that it
-     * is in place for sending early data. For client side external PSK we
+     * side we do this for a non-external (i.e. resumption) PSK or external PSK
+     * that will be used for early_data so that it is in place for sending early
+     * data. For client side external PSK not being used for early_data we
      * generate it but store it away for later use.
      */
-    if (s->server || !external)
+    if (s->server || !external || usepskfored)
         early_secret = (unsigned char *)s->early_secret;
     else
         early_secret = (unsigned char *)sess->early_secret;
@@ -1361,19 +1390,31 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
 
 static int final_early_data(SSL *s, unsigned int context, int sent, int *al)
 {
-    if (!s->server || !sent)
+    if (!sent)
         return 1;
 
+    if (!s->server) {
+        if (context == SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+                && sent
+                && !s->ext.early_data_ok) {
+            /*
+             * If we get here then the server accepted our early_data but we
+             * later realised that it shouldn't have done (e.g. inconsistent
+             * ALPN)
+             */
+            *al = SSL_AD_ILLEGAL_PARAMETER;
+            return 0;
+        }
+
+        return 1;
+    }
+
     if (s->max_early_data == 0
             || !s->hit
             || s->session->ext.tick_identity != 0
             || s->early_data_state != SSL_EARLY_DATA_ACCEPTING
             || !s->ext.early_data_ok
-            || s->hello_retry_request
-            || s->s3->alpn_selected_len != s->session->ext.alpn_selected_len
-            || (s->s3->alpn_selected_len > 0
-                && memcmp(s->s3->alpn_selected, s->session->ext.alpn_selected,
-                          s->s3->alpn_selected_len) != 0)) {
+            || s->hello_retry_request) {
         s->ext.early_data = SSL_EARLY_DATA_REJECTED;
     } else {
         s->ext.early_data = SSL_EARLY_DATA_ACCEPTED;
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index b1c2eb0..8db895b 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -679,12 +679,85 @@ EXT_RETURN tls_construct_ctos_early_data(SSL *s, WPACKET *pkt,
                                          unsigned int context, X509 *x,
                                          size_t chainidx, int *al)
 {
+    const unsigned char *id;
+    size_t idlen = 0;
+    SSL_SESSION *psksess = NULL;
+    SSL_SESSION *edsess = NULL;
+    const EVP_MD *handmd = NULL;
+
+    if (s->hello_retry_request)
+        handmd = ssl_handshake_md(s);
+
+    if (s->psk_use_session_cb != NULL
+            && (!s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess)
+                || (psksess != NULL
+                    && psksess->ssl_version != TLS1_3_VERSION))) {
+        SSL_SESSION_free(psksess);
+        SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, SSL_R_BAD_PSK);
+        return EXT_RETURN_FAIL;
+    }
+
+    SSL_SESSION_free(s->psksession);
+    s->psksession = psksess;
+    if (psksess != NULL) {
+        OPENSSL_free(s->psksession_id);
+        s->psksession_id = OPENSSL_memdup(id, idlen);
+        if (s->psksession_id == NULL) {
+            SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+            return EXT_RETURN_FAIL;
+        }
+        s->psksession_id_len = idlen;
+    }
+
     if (s->early_data_state != SSL_EARLY_DATA_CONNECTING
-            || s->session->ext.max_early_data == 0) {
+            || (s->session->ext.max_early_data == 0
+                && (psksess == NULL || psksess->ext.max_early_data == 0))) {
         s->max_early_data = 0;
         return EXT_RETURN_NOT_SENT;
     }
-    s->max_early_data = s->session->ext.max_early_data;
+    edsess = s->session->ext.max_early_data != 0 ? s->session : psksess;
+    s->max_early_data = edsess->ext.max_early_data;
+
+    if ((s->ext.hostname == NULL && edsess->ext.hostname != NULL)
+            || (s->ext.hostname != NULL
+                && (edsess->ext.hostname == NULL
+                    || strcmp(s->ext.hostname, edsess->ext.hostname) != 0))) {
+        SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+               SSL_R_INCONSISTENT_EARLY_DATA_SNI);
+        return EXT_RETURN_FAIL;
+    }
+
+    if ((s->ext.alpn == NULL && edsess->ext.alpn_selected != NULL)) {
+        SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+               SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+        return EXT_RETURN_FAIL;
+    }
+
+    /*
+     * Verify that we are offering an ALPN protocol consistent with the early
+     * data.
+     */
+    if (edsess->ext.alpn_selected != NULL) {
+        PACKET prots, alpnpkt;
+        int found = 0;
+
+        if (!PACKET_buf_init(&prots, s->ext.alpn, s->ext.alpn_len)) {
+            SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+            return EXT_RETURN_FAIL;
+        }
+        while (PACKET_get_length_prefixed_1(&prots, &alpnpkt)) {
+            if (PACKET_equal(&alpnpkt, edsess->ext.alpn_selected,
+                             edsess->ext.alpn_selected_len)) {
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+            SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+                   SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+            return EXT_RETURN_FAIL;
+        }
+    }
 
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data)
             || !WPACKET_start_sub_packet_u16(pkt)
@@ -698,6 +771,7 @@ EXT_RETURN tls_construct_ctos_early_data(SSL *s, WPACKET *pkt,
      * extension, we set it to accepted.
      */
     s->ext.early_data = SSL_EARLY_DATA_REJECTED;
+    s->ext.early_data_ok = 1;
 
     return EXT_RETURN_SENT;
 }
@@ -793,12 +867,10 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
 {
 #ifndef OPENSSL_NO_TLS1_3
     uint32_t now, agesec, agems = 0;
-    size_t reshashsize = 0, pskhashsize = 0, binderoffset, msglen, idlen = 0;
+    size_t reshashsize = 0, pskhashsize = 0, binderoffset, msglen;
     unsigned char *resbinder = NULL, *pskbinder = NULL, *msgstart = NULL;
-    const unsigned char *id = 0;
     const EVP_MD *handmd = NULL, *mdres = NULL, *mdpsk = NULL;
     EXT_RETURN ret = EXT_RETURN_FAIL;
-    SSL_SESSION *psksess = NULL;
     int dores = 0;
 
     s->session->ext.tick_identity = TLSEXT_PSK_BAD_IDENTITY;
@@ -814,18 +886,12 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
      * so don't add this extension.
      */
     if (s->session->ssl_version != TLS1_3_VERSION
-            || (s->session->ext.ticklen == 0 && s->psk_use_session_cb == NULL))
+            || (s->session->ext.ticklen == 0 && s->psksession == NULL))
         return EXT_RETURN_NOT_SENT;
 
     if (s->hello_retry_request)
         handmd = ssl_handshake_md(s);
 
-    if (s->psk_use_session_cb != NULL
-            && !s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess)) {
-        SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, SSL_R_BAD_PSK);
-        goto err;
-    }
-
     if (s->session->ext.ticklen != 0) {
         /* Get the digest associated with the ciphersuite in the session */
         if (s->session->cipher == NULL) {
@@ -890,11 +956,11 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
     }
 
  dopsksess:
-    if (!dores && psksess == NULL)
+    if (!dores && s->psksession == NULL)
         return EXT_RETURN_NOT_SENT;
 
-    if (psksess != NULL) {
-        mdpsk = ssl_md(psksess->cipher->algorithm2);
+    if (s->psksession != NULL) {
+        mdpsk = ssl_md(s->psksession->cipher->algorithm2);
         if (mdpsk == NULL) {
             /*
              * Don't recognize this cipher so we can't use the session.
@@ -933,8 +999,9 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
         }
     }
 
-    if (psksess != NULL) {
-        if (!WPACKET_sub_memcpy_u16(pkt, id, idlen)
+    if (s->psksession != NULL) {
+        if (!WPACKET_sub_memcpy_u16(pkt, s->psksession_id,
+                                    s->psksession_id_len)
                 || !WPACKET_put_bytes_u32(pkt, 0)) {
             SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
             goto err;
@@ -946,7 +1013,7 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
             || !WPACKET_start_sub_packet_u16(pkt)
             || (dores
                 && !WPACKET_sub_allocate_bytes_u8(pkt, reshashsize, &resbinder))
-            || (psksess != NULL
+            || (s->psksession != NULL
                 && !WPACKET_sub_allocate_bytes_u8(pkt, pskhashsize, &pskbinder))
             || !WPACKET_close(pkt)
             || !WPACKET_close(pkt)
@@ -969,24 +1036,20 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
         goto err;
     }
 
-    if (psksess != NULL
+    if (s->psksession != NULL
             && tls_psk_do_binder(s, mdpsk, msgstart, binderoffset, NULL,
-                                 pskbinder, psksess, 1, 1) != 1) {
+                                 pskbinder, s->psksession, 1, 1) != 1) {
         SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
     if (dores)
         s->session->ext.tick_identity = 0;
-    SSL_SESSION_free(s->psksession);
-    s->psksession = psksess;
-    if (psksess != NULL)
+    if (s->psksession != NULL)
         s->psksession->ext.tick_identity = (dores ? 1 : 0);
-    psksess = NULL;
 
     ret = EXT_RETURN_SENT;
  err:
-    SSL_SESSION_free(psksess);
     return ret;
 #else
     return 1;
@@ -1338,6 +1401,24 @@ int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
     }
     s->s3->alpn_selected_len = len;
 
+    if (s->session->ext.alpn_selected == NULL
+            || s->session->ext.alpn_selected_len != len
+            || memcmp(s->session->ext.alpn_selected, s->s3->alpn_selected, len)
+               != 0) {
+        /* ALPN not consistent with the old session so cannot use early_data */
+        s->ext.early_data_ok = 0;
+    }
+    if (!s->hit) {
+        /* If a new session then update it with the selected ALPN */
+        s->session->ext.alpn_selected =
+            OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
+        if (s->session->ext.alpn_selected == NULL) {
+            *al = SSL_AD_INTERNAL_ERROR;
+            return 0;
+        }
+        s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
+    }
+
     return 1;
 }
 
@@ -1564,12 +1645,13 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context,
         return 0;
     }
 
-    if (s->ext.early_data != SSL_EARLY_DATA_REJECTED
+    if (!s->ext.early_data_ok
             || !s->hit
             || s->session->ext.tick_identity != 0) {
         /*
          * If we get here then we didn't send early data, or we didn't resume
-         * using the first identity so the server should not be accepting it.
+         * using the first identity, or the SNI/ALPN is not consistent so the
+         * server should not be accepting it.
          */
         *al = SSL_AD_ILLEGAL_PARAMETER;
         return 0;
@@ -1606,10 +1688,20 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
         return 0;
     }
 
+    /*
+     * If we used the external PSK for sending early_data then s->early_secret
+     * is already set up, so don't overwrite it. Otherwise we copy the
+     * early_secret across that we generated earlier.
+     */
+    if ((s->early_data_state != SSL_EARLY_DATA_WRITE_RETRY
+                && s->early_data_state != SSL_EARLY_DATA_FINISHED_WRITING)
+            || s->session->ext.max_early_data > 0
+            || s->psksession->ext.max_early_data == 0)
+        memcpy(s->early_secret, s->psksession->early_secret, EVP_MAX_MD_SIZE);
+
     SSL_SESSION_free(s->session);
     s->session = s->psksession;
     s->psksession = NULL;
-    memcpy(s->early_secret, s->session->early_secret, EVP_MAX_MD_SIZE);
     s->hit = 1;
 #endif
 
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index a70f53b..0dbec91 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -131,6 +131,9 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context,
         s->servername_done = s->session->ext.hostname
             && PACKET_equal(&hostname, s->session->ext.hostname,
                             strlen(s->session->ext.hostname));
+
+        if (!s->servername_done && s->session->ext.hostname != NULL)
+            s->ext.early_data_ok = 0;
     }
 
     return 1;
@@ -745,6 +748,8 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
             memcpy(sess->sid_ctx, s->sid_ctx, s->sid_ctx_length);
             sess->sid_ctx_length = s->sid_ctx_length;
             ext = 1;
+            if (id == 0)
+                s->ext.early_data_ok = 1;
         } else {
             uint32_t ticket_age = 0, now, agesec, agems;
             int ret = tls_decrypt_ticket(s, PACKET_data(&identity),
@@ -773,7 +778,8 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
              * Therefore we add 1000ms to our age calculation to adjust for
              * rounding errors.
              */
-            if (sess->timeout >= (long)agesec
+            if (id == 0
+                    && sess->timeout >= (long)agesec
                     && agems / (uint32_t)1000 == agesec
                     && ticket_age <= agems + 1000
                     && ticket_age + TICKET_AGE_ALLOWANCE >= agems + 1000) {
@@ -790,6 +796,7 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
             /* The ciphersuite is not compatible with this session. */
             SSL_SESSION_free(sess);
             sess = NULL;
+            s->ext.early_data_ok = 0;
             continue;
         }
         break;
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 1f8c22d..ae33fe5 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -390,3 +390,5 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx, int *al);
 int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx, int *al);
+
+int tls_handle_alpn(SSL *s, int *al);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 8c5f77b..a3a6bdf 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -1938,7 +1938,7 @@ static int tls_handle_status_request(SSL *s, int *al)
  * Call the alpn_select callback if needed. Upon success, returns 1.
  * Upon failure, returns 0 and sets |*al| to the appropriate fatal alert.
  */
-static int tls_handle_alpn(SSL *s, int *al)
+int tls_handle_alpn(SSL *s, int *al)
 {
     const unsigned char *selected = NULL;
     unsigned char selected_len = 0;
@@ -1961,13 +1961,42 @@ static int tls_handle_alpn(SSL *s, int *al)
             /* ALPN takes precedence over NPN. */
             s->s3->npn_seen = 0;
 #endif
-        } else if (r == SSL_TLSEXT_ERR_NOACK) {
-            /* Behave as if no callback was present. */
+
+            /* Check ALPN is consistent with session */
+            if (s->session->ext.alpn_selected == NULL
+                        || selected_len != s->session->ext.alpn_selected_len
+                        || memcmp(selected, s->session->ext.alpn_selected,
+                                  selected_len) != 0) {
+                /* Not consistent so can't be used for early_data */
+                s->ext.early_data_ok = 0;
+
+                if (!s->hit) {
+                    /* If a new session update it with the new ALPN value */
+                    s->session->ext.alpn_selected = OPENSSL_memdup(selected,
+                                                                   selected_len);
+                    if (s->session->ext.alpn_selected == NULL) {
+                        *al = SSL_AD_INTERNAL_ERROR;
+                        return 0;
+                    }
+                    s->session->ext.alpn_selected_len = selected_len;
+                }
+            }
+
             return 1;
-        } else {
+        } else if (r != SSL_TLSEXT_ERR_NOACK) {
             *al = SSL_AD_NO_APPLICATION_PROTOCOL;
             return 0;
         }
+        /*
+         * If r == SSL_TLSEXT_ERR_NOACK then behave as if no callback was
+         * present.
+         */
+    }
+
+    /* Check ALPN is consistent with session */
+    if (s->session->ext.alpn_selected != NULL) {
+        /* Not consistent so can't be used for early_data */
+        s->ext.early_data_ok = 0;
     }
 
     return 1;
@@ -2059,9 +2088,11 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
         }
         /*
          * Call alpn_select callback if needed.  Has to be done after SNI and
-         * cipher negotiation (HTTP/2 restricts permitted ciphers).
+         * cipher negotiation (HTTP/2 restricts permitted ciphers). In TLSv1.3
+         * we already did this because cipher negotiation happens earlier, and
+         * we must handle ALPN before we decide whether to accept early_data.
          */
-        if (!tls_handle_alpn(s, &al)) {
+        if (!SSL_IS_TLS13(s) && !tls_handle_alpn(s, &al)) {
             SSLerr(SSL_F_TLS_POST_PROCESS_CLIENT_HELLO,
                    SSL_R_CLIENTHELLO_TLSEXT);
             goto f_err;
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index ac5d06c..98a1d1e 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -9,6 +9,7 @@
 
 #include <stdlib.h>
 #include "ssl_locl.h"
+#include "internal/cryptlib.h"
 #include <openssl/evp.h>
 #include <openssl/kdf.h>
 
@@ -404,8 +405,26 @@ int tls13_change_cipher_state(SSL *s, int which)
                        SSL_R_BAD_HANDSHAKE_LENGTH);
                 goto err;
             }
+
+            if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
+                    && s->max_early_data > 0
+                    && s->session->ext.max_early_data == 0) {
+                /*
+                 * If we are attempting to send early data, and we've decided to
+                 * actually do it but max_early_data in s->session is 0 then we
+                 * must be using an external PSK.
+                 */
+                if (!ossl_assert(s->psksession != NULL
+                        && s->max_early_data ==
+                           s->psksession->ext.max_early_data)) {
+                    SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE,
+                           ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+                sslcipher = SSL_SESSION_get0_cipher(s->psksession);
+            }
             if (sslcipher == NULL) {
-                SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
+                SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, SSL_R_BAD_PSK);
                 goto err;
             }
 
diff --git a/test/recipes/70-test_sslmessages.t b/test/recipes/70-test_sslmessages.t
index a763486..6aab5af 100644
--- a/test/recipes/70-test_sslmessages.t
+++ b/test/recipes/70-test_sslmessages.t
@@ -164,7 +164,8 @@ $proxy->clientflags("-no_tls1_3 -sess_in ".$session);
 $proxy->clientstart();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
-               & ~checkhandshake::SESSION_TICKET_SRV_EXTENSION,
+               & ~checkhandshake::SESSION_TICKET_SRV_EXTENSION
+               & ~checkhandshake::SERVER_NAME_CLI_EXTENSION,
                "Resumption handshake test");
 unlink $session;
 
diff --git a/test/recipes/70-test_tls13kexmodes.t b/test/recipes/70-test_tls13kexmodes.t
index ec23e13..fe7415a 100644
--- a/test/recipes/70-test_tls13kexmodes.t
+++ b/test/recipes/70-test_tls13kexmodes.t
@@ -143,6 +143,7 @@ my $proxy = TLSProxy::Proxy->new(
 #Test 1: First get a session
 (undef, my $session) = tempfile();
 $proxy->clientflags("-sess_out ".$session);
+$proxy->serverflags("-servername localhost");
 $proxy->sessionfile($session);
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 11;
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
index c211851..24ffb80 100644
--- a/test/recipes/70-test_tls13messages.t
+++ b/test/recipes/70-test_tls13messages.t
@@ -157,9 +157,10 @@ $proxy->clearClient();
 $proxy->clientflags("-sess_in ".$session);
 $proxy->clientstart();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
-               checkhandshake::DEFAULT_EXTENSIONS
-               | checkhandshake::PSK_CLI_EXTENSION
-               | checkhandshake::PSK_SRV_EXTENSION,
+               (checkhandshake::DEFAULT_EXTENSIONS
+                | checkhandshake::PSK_CLI_EXTENSION
+                | checkhandshake::PSK_SRV_EXTENSION)
+               & ~checkhandshake::SERVER_NAME_CLI_EXTENSION,
                "Resumption handshake test");
 
 #Test 3: A status_request handshake (client request only)
@@ -300,10 +301,11 @@ $proxy->clientflags("-sess_in ".$session);
 $proxy->serverflags("-curves P-256");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
-               checkhandshake::DEFAULT_EXTENSIONS
-               | checkhandshake::KEY_SHARE_HRR_EXTENSION
-               | checkhandshake::PSK_CLI_EXTENSION
-               | checkhandshake::PSK_SRV_EXTENSION,
+               (checkhandshake::DEFAULT_EXTENSIONS
+                | checkhandshake::KEY_SHARE_HRR_EXTENSION
+                | checkhandshake::PSK_CLI_EXTENSION
+                | checkhandshake::PSK_SRV_EXTENSION)
+               & ~checkhandshake::SERVER_NAME_CLI_EXTENSION,
                "Resumption handshake with HRR test");
 
 #Test 16: Acceptable but non preferred key_share
diff --git a/test/recipes/70-test_tls13psk.t b/test/recipes/70-test_tls13psk.t
index 23767f9..e344b75 100644
--- a/test/recipes/70-test_tls13psk.t
+++ b/test/recipes/70-test_tls13psk.t
@@ -48,6 +48,7 @@ use constant {
 #Test 1: First get a session
 (undef, my $session) = tempfile();
 $proxy->clientflags("-sess_out ".$session);
+$proxy->serverflags("-servername localhost");
 $proxy->sessionfile($session);
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 5;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 70fbf80..858f28b 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -1401,6 +1401,71 @@ static int test_set_sigalgs(int idx)
 
 #ifndef OPENSSL_NO_TLS1_3
 
+static SSL_SESSION *clientpsk = NULL;
+static SSL_SESSION *serverpsk = NULL;
+static const char *pskid = "Identity";
+static const char *srvid;
+
+static int use_session_cb_cnt = 0;
+static int find_session_cb_cnt = 0;
+
+static int use_session_cb(SSL *ssl, const EVP_MD *md, const unsigned char **id,
+                          size_t *idlen, SSL_SESSION **sess)
+{
+    switch (++use_session_cb_cnt) {
+    case 1:
+        /* The first call should always have a NULL md */
+        if (md != NULL)
+            return 0;
+        break;
+
+    case 2:
+        /* The second call should always have an md */
+        if (md == NULL)
+            return 0;
+        break;
+
+    default:
+        /* We should only be called a maximum of twice */
+        return 0;
+    }
+
+    if (clientpsk != NULL)
+        SSL_SESSION_up_ref(clientpsk);
+
+    *sess = clientpsk;
+    *id = (const unsigned char *)pskid;
+    *idlen = strlen(pskid);
+
+    return 1;
+}
+
+static int find_session_cb(SSL *ssl, const unsigned char *identity,
+                           size_t identity_len, SSL_SESSION **sess)
+{
+    find_session_cb_cnt++;
+
+    /* We should only ever be called a maximum of twice per connection */
+    if (find_session_cb_cnt > 2)
+        return 0;
+
+    if (serverpsk == NULL)
+        return 0;
+
+    /* Identity should match that set by the client */
+    if (strlen(srvid) != identity_len
+            || strncmp(srvid, (const char *)identity, identity_len) != 0) {
+        /* No PSK found, continue but without a PSK */
+        *sess = NULL;
+        return 1;
+    }
+
+    SSL_SESSION_up_ref(serverpsk);
+    *sess = serverpsk;
+
+    return 1;
+}
+
 #define MSG1    "Hello"
 #define MSG2    "World."
 #define MSG3    "This"
@@ -1409,6 +1474,8 @@ static int test_set_sigalgs(int idx)
 #define MSG6    "test"
 #define MSG7    "message."
 
+#define TLS13_AES_256_GCM_SHA384_BYTES  ((const unsigned char *)"\x13\x02")
+
 /*
  * Helper method to setup objects for early data test. Caller frees objects on
  * error.
@@ -1421,16 +1488,67 @@ static int setupearly_data_test(SSL_CTX **cctx, SSL_CTX **sctx, SSL **clientssl,
                                        cctx, cert, privkey)))
         return 0;
 
-    /* When idx == 1 we repeat the tests with read_ahead set */
-    if (idx > 0) {
+    if (idx == 1) {
+        /* When idx == 1 we repeat the tests with read_ahead set */
         SSL_CTX_set_read_ahead(*cctx, 1);
         SSL_CTX_set_read_ahead(*sctx, 1);
+    } else if (idx == 2) {
+        /* When idx == 2 we are doing early_data with a PSK. Set up callbacks */
+        SSL_CTX_set_psk_use_session_callback(*cctx, use_session_cb);
+        SSL_CTX_set_psk_find_session_callback(*sctx, find_session_cb);
+        use_session_cb_cnt = 0;
+        find_session_cb_cnt = 0;
+        srvid = pskid;
     }
 
     if (!TEST_true(create_ssl_objects(*sctx, *cctx, serverssl, clientssl,
-                                      NULL, NULL))
-            || !TEST_true(create_ssl_connection(*serverssl, *clientssl,
-                                                SSL_ERROR_NONE)))
+                                      NULL, NULL)))
+        return 0;
+
+    if (idx == 2) {
+        /* Create the PSK */
+        const SSL_CIPHER *cipher = NULL;
+        const unsigned char key[] = {
+            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+            0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+            0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+            0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+            0x2c, 0x2d, 0x2e, 0x2f
+        };
+
+        cipher = SSL_CIPHER_find(*clientssl, TLS13_AES_256_GCM_SHA384_BYTES);
+        clientpsk = SSL_SESSION_new();
+        if (!TEST_ptr(clientpsk)
+                || !TEST_ptr(cipher)
+                || !TEST_true(SSL_SESSION_set1_master_key(clientpsk, key,
+                                                          sizeof(key)))
+                || !TEST_true(SSL_SESSION_set_cipher(clientpsk, cipher))
+                || !TEST_true(
+                        SSL_SESSION_set_protocol_version(clientpsk,
+                                                         TLS1_3_VERSION))
+                   /*
+                    * We just choose an arbitrary value for max_early_data which
+                    * should be big enough for testing purposes.
+                    */
+                || !TEST_true(SSL_SESSION_set_max_early_data(clientpsk,
+                                                             0x100))
+                || !TEST_true(SSL_SESSION_up_ref(clientpsk))) {
+            SSL_SESSION_free(clientpsk);
+            clientpsk = NULL;
+            return 0;
+        }
+        serverpsk = clientpsk;
+
+        if (sess != NULL)
+            *sess = clientpsk;
+        return 1;
+    }
+
+    if (sess == NULL)
+        return 1;
+
+    if (!TEST_true(create_ssl_connection(*serverssl, *clientssl,
+                                         SSL_ERROR_NONE)))
         return 0;
 
     *sess = SSL_get1_session(*clientssl);
@@ -1591,8 +1709,12 @@ static int test_early_data_read_write(int idx)
             || !TEST_mem_eq(buf, readbytes, MSG7, strlen(MSG7)))
         goto end;
 
-    SSL_SESSION_free(sess);
+    /* We keep the PSK session around if using PSK */
+    if (idx != 2)
+        SSL_SESSION_free(sess);
     sess = SSL_get1_session(clientssl);
+    use_session_cb_cnt = 0;
+    find_session_cb_cnt = 0;
 
     SSL_shutdown(clientssl);
     SSL_shutdown(serverssl);
@@ -1639,7 +1761,11 @@ static int test_early_data_read_write(int idx)
     testresult = 1;
 
  end:
-    SSL_SESSION_free(sess);
+    if (sess != clientpsk)
+        SSL_SESSION_free(sess);
+    SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -1668,6 +1794,12 @@ static int early_data_skip_helper(int hrr, int idx)
         /* Force an HRR to occur */
         if (!TEST_true(SSL_set1_groups_list(serverssl, "P-256")))
             goto end;
+    } else if (idx == 2) {
+        /*
+         * We force early_data rejection by ensuring the PSK identity is
+         * unrecognised
+         */
+        srvid = "Dummy Identity";
     } else {
         /*
          * Deliberately corrupt the creation time. We take 20 seconds off the
@@ -1717,6 +1849,10 @@ static int early_data_skip_helper(int hrr, int idx)
     testresult = 1;
 
  end:
+    if (sess != clientpsk)
+        SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_SESSION_free(sess);
     SSL_free(serverssl);
     SSL_free(clientssl);
@@ -1789,7 +1925,7 @@ static int test_early_data_not_sent(int idx)
      * Should block due to the NewSessionTicket arrival unless we're using
      * read_ahead
      */
-    if (idx == 0) {
+    if (idx != 1) {
         if (!TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes)))
             goto end;
     }
@@ -1801,7 +1937,216 @@ static int test_early_data_not_sent(int idx)
     testresult = 1;
 
  end:
+    /* If using PSK then clientpsk and sess are the same */
     SSL_SESSION_free(sess);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    return testresult;
+}
+
+static const char *servhostname;
+
+static int hostname_cb(SSL *s, int *al, void *arg)
+{
+    const char *hostname = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+
+    if (hostname != NULL && strcmp(hostname, servhostname) == 0)
+        return  SSL_TLSEXT_ERR_OK;
+
+    return SSL_TLSEXT_ERR_NOACK;
+}
+
+static const char *servalpn;
+
+static int alpn_select_cb (SSL *ssl, const unsigned char **out, unsigned char *outlen,
+                    const unsigned char *in, unsigned int inlen, void *arg)
+{
+    unsigned int i, protlen = 0;
+    const unsigned char *prot;
+
+    for (i = 0, prot = in; i < inlen; i += protlen, prot += protlen) {
+        protlen = *(prot++);
+        if (inlen - i < protlen)
+            return SSL_TLSEXT_ERR_NOACK;
+
+        if (protlen == strlen(servalpn)
+                && memcmp(prot, "goodalpn", protlen) == 0) {
+            *out = prot;
+            *outlen = protlen;
+            return SSL_TLSEXT_ERR_OK;
+        }
+    }
+
+    return SSL_TLSEXT_ERR_NOACK;
+}
+
+/* Test that a PSK can be used to send early_data */
+static int test_early_data_psk(int idx)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    SSL_SESSION *sess = NULL;
+    unsigned char alpnlist[] = {
+        0x08, 'g', 'o', 'o', 'd', 'a', 'l', 'p', 'n', 0x07, 'b', 'a', 'd', 'a',
+        'l', 'p', 'n'
+    };
+#define GOODALPNLEN     9
+#define BADALPNLEN      8
+#define GOODALPN        (alpnlist)
+#define BADALPN         (alpnlist + GOODALPNLEN)
+    int err = 0;
+    unsigned char buf[20];
+    size_t readbytes, written;
+    int readearlyres = SSL_READ_EARLY_DATA_SUCCESS, connectres = 1;
+    int edstatus = SSL_EARLY_DATA_ACCEPTED;
+
+    /* We always set this up with a final parameter of "2" for PSK */
+    if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl,
+                                        &serverssl, &sess, 2)))
+        goto end;
+
+    servhostname = "goodhost";
+    servalpn = "goodalpn";
+
+    /*
+     * Note: There is no test for inconsistent SNI with late client detection.
+     * This is because servers do not acknowledge SNI even if they are using
+     * it in a resumption handshake - so it is not actually possible for a
+     * client to detect a problem.
+     */
+    switch (idx) {
+    case 0:
+        /* Set inconsistent SNI (early client detection) */
+        err = SSL_R_INCONSISTENT_EARLY_DATA_SNI;
+        if (!TEST_true(SSL_SESSION_set1_hostname(sess, "goodhost"))
+                || !TEST_true(SSL_set_tlsext_host_name(clientssl, "badhost")))
+            goto end;
+        break;
+
+    case 1:
+        /* Set inconsistent ALPN (early client detection) */
+        err = SSL_R_INCONSISTENT_EARLY_DATA_ALPN;
+        /* SSL_set_alpn_protos returns 0 for success and 1 for failure */
+        if (!TEST_true(SSL_SESSION_set1_alpn_selected(sess, GOODALPN,
+                                                      GOODALPNLEN))
+                || !TEST_false(SSL_set_alpn_protos(clientssl, BADALPN,
+                                                   BADALPNLEN)))
+            goto end;
+        break;
+
+    case 2:
+        /*
+         * Set invalid protocol version. Technically this affects PSKs without
+         * early_data too, but we test it here because it is similar to the
+         * SNI/ALPN consistency tests.
+         */
+        err = SSL_R_BAD_PSK;
+        if (!TEST_true(SSL_SESSION_set_protocol_version(sess, TLS1_2_VERSION)))
+            goto end;
+        break;
+
+    case 3:
+        /*
+         * Set inconsistent SNI (server detected). In this case the connection
+         * will succeed but reject early_data.
+         */
+        servhostname = "badhost";
+        edstatus = SSL_EARLY_DATA_REJECTED;
+        readearlyres = SSL_READ_EARLY_DATA_FINISH;
+        /* Fall through */
+    case 4:
+        /* Set consistent SNI */
+        if (!TEST_true(SSL_SESSION_set1_hostname(sess, "goodhost"))
+                || !TEST_true(SSL_set_tlsext_host_name(clientssl, "goodhost"))
+                || !TEST_true(SSL_CTX_set_tlsext_servername_callback(sctx,
+                                hostname_cb)))
+            goto end;
+        break;
+
+    case 5:
+        /*
+         * Set inconsistent ALPN (server detected). In this case the connection
+         * will succeed but reject early_data.
+         */
+        servalpn = "badalpn";
+        edstatus = SSL_EARLY_DATA_REJECTED;
+        readearlyres = SSL_READ_EARLY_DATA_FINISH;
+        /* Fall through */
+    case 6:
+        /*
+         * Set consistent ALPN.
+         * SSL_set_alpn_protos returns 0 for success and 1 for failure. It
+         * accepts a list of protos (each one length prefixed).
+         * SSL_set1_alpn_selected accepts a single protocol (not length
+         * prefixed)
+         */
+        if (!TEST_true(SSL_SESSION_set1_alpn_selected(sess, GOODALPN + 1,
+                                                      GOODALPNLEN - 1))
+                || !TEST_false(SSL_set_alpn_protos(clientssl, GOODALPN,
+                                                   GOODALPNLEN)))
+            goto end;
+
+        SSL_CTX_set_alpn_select_cb(sctx, alpn_select_cb, NULL);
+        break;
+
+    case 7:
+        /* Set inconsistent ALPN (late client detection) */
+        SSL_SESSION_free(serverpsk);
+        serverpsk = SSL_SESSION_dup(clientpsk);
+        if (!TEST_ptr(serverpsk)
+                || !TEST_true(SSL_SESSION_set1_alpn_selected(clientpsk,
+                                                             BADALPN + 1,
+                                                             BADALPNLEN - 1))
+                || !TEST_true(SSL_SESSION_set1_alpn_selected(serverpsk,
+                                                             GOODALPN + 1,
+                                                             GOODALPNLEN - 1))
+                || !TEST_false(SSL_set_alpn_protos(clientssl, alpnlist,
+                                                   sizeof(alpnlist))))
+            goto end;
+        SSL_CTX_set_alpn_select_cb(sctx, alpn_select_cb, NULL);
+        edstatus = SSL_EARLY_DATA_ACCEPTED;
+        readearlyres = SSL_READ_EARLY_DATA_SUCCESS;
+        /* SSL_connect() call should fail */
+        connectres = -1;
+        break;
+
+    default:
+        TEST_error("Bad test index");
+        goto end;
+    }
+
+    SSL_set_connect_state(clientssl);
+    if (err != 0) {
+        if (!TEST_false(SSL_write_early_data(clientssl, MSG1, strlen(MSG1),
+                                            &written))
+                || !TEST_int_eq(SSL_get_error(clientssl, 0), SSL_ERROR_SSL)
+                || !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), err))
+            goto end;
+    } else {
+        if (!TEST_true(SSL_write_early_data(clientssl, MSG1, strlen(MSG1),
+                                            &written)))
+            goto end;
+
+        if (!TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
+                                             &readbytes), readearlyres)
+                || (readearlyres == SSL_READ_EARLY_DATA_SUCCESS
+                    && !TEST_mem_eq(buf, readbytes, MSG1, strlen(MSG1)))
+                || !TEST_int_eq(SSL_get_early_data_status(serverssl), edstatus)
+                || !TEST_int_eq(SSL_connect(clientssl), connectres))
+            goto end;
+    }
+
+    testresult = 1;
+
+ end:
+    SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -1822,7 +2167,6 @@ static int test_early_data_not_expected(int idx)
     unsigned char buf[20];
     size_t readbytes, written;
 
-
     if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl,
                                         &serverssl, &sess, idx)))
         goto end;
@@ -1857,7 +2201,10 @@ static int test_early_data_not_expected(int idx)
     testresult = 1;
 
  end:
+    /* If using PSK then clientpsk and sess are the same */
     SSL_SESSION_free(sess);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -1879,19 +2226,8 @@ static int test_early_data_tls1_2(int idx)
     unsigned char buf[20];
     size_t readbytes, written;
 
-    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(),
-                                       TLS_client_method(), &sctx,
-                                       &cctx, cert, privkey)))
-        goto end;
-
-    /* When idx == 1 we repeat the tests with read_ahead set */
-    if (idx > 0) {
-        SSL_CTX_set_read_ahead(cctx, 1);
-        SSL_CTX_set_read_ahead(sctx, 1);
-    }
-
-    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
-                                      &clientssl, NULL, NULL)))
+    if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl,
+                                        &serverssl, NULL, idx)))
         goto end;
 
     /* Write some data - should block due to handshake with server */
@@ -1939,6 +2275,10 @@ static int test_early_data_tls1_2(int idx)
     testresult = 1;
 
  end:
+    /* If using PSK then clientpsk and sess are the same */
+    SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -2075,73 +2415,6 @@ static int test_ciphersuite_change(void)
     return testresult;
 }
 
-
-static SSL_SESSION *psk = NULL;
-static const char *pskid = "Identity";
-static const char *srvid;
-
-static int use_session_cb_cnt = 0;
-static int find_session_cb_cnt = 0;
-
-static int use_session_cb(SSL *ssl, const EVP_MD *md, const unsigned char **id,
-                          size_t *idlen, SSL_SESSION **sess)
-{
-    switch (++use_session_cb_cnt) {
-    case 1:
-        /* The first call should always have a NULL md */
-        if (md != NULL)
-            return 0;
-        break;
-
-    case 2:
-        /* The second call should always have an md */
-        if (md == NULL)
-            return 0;
-        break;
-
-    default:
-        /* We should only be called a maximum of twice */
-        return 0;
-    }
-
-    if (psk != NULL)
-        SSL_SESSION_up_ref(psk);
-
-    *sess = psk;
-    *id = (const unsigned char *)pskid;
-    *idlen = strlen(pskid);
-
-    return 1;
-}
-
-static int find_session_cb(SSL *ssl, const unsigned char *identity,
-                           size_t identity_len, SSL_SESSION **sess)
-{
-    find_session_cb_cnt++;
-
-    /* We should only ever be called a maximum of twice per connection */
-    if (find_session_cb_cnt > 2)
-        return 0;
-
-    if (psk == NULL)
-        return 0;
-
-    /* Identity should match that set by the client */
-    if (strlen(srvid) != identity_len
-            || strncmp(srvid, (const char *)identity, identity_len) != 0) {
-        /* No PSK found, continue but without a PSK */
-        *sess = NULL;
-        return 1;
-    }
-
-    SSL_SESSION_up_ref(psk);
-    *sess = psk;
-
-    return 1;
-}
-
-#define TLS13_AES_256_GCM_SHA384_BYTES  ((const unsigned char *)"\x13\x02")
-
 static int test_tls13_psk(void)
 {
     SSL_CTX *sctx = NULL, *cctx = NULL;
@@ -2163,6 +2436,8 @@ static int test_tls13_psk(void)
     SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb);
     SSL_CTX_set_psk_find_session_callback(sctx, find_session_cb);
     srvid = pskid;
+    use_session_cb_cnt = 0;
+    find_session_cb_cnt = 0;
 
     /* Check we can create a connection if callback decides not to send a PSK */
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
@@ -2185,14 +2460,17 @@ static int test_tls13_psk(void)
 
     /* Create the PSK */
     cipher = SSL_CIPHER_find(clientssl, TLS13_AES_256_GCM_SHA384_BYTES);
-    psk = SSL_SESSION_new();
-    if (!TEST_ptr(psk)
+    clientpsk = SSL_SESSION_new();
+    if (!TEST_ptr(clientpsk)
             || !TEST_ptr(cipher)
-            || !TEST_true(SSL_SESSION_set1_master_key(psk, key, sizeof(key)))
-            || !TEST_true(SSL_SESSION_set_cipher(psk, cipher))
-            || !TEST_true(SSL_SESSION_set_protocol_version(psk,
-                                                           TLS1_3_VERSION)))
+            || !TEST_true(SSL_SESSION_set1_master_key(clientpsk, key,
+                                                      sizeof(key)))
+            || !TEST_true(SSL_SESSION_set_cipher(clientpsk, cipher))
+            || !TEST_true(SSL_SESSION_set_protocol_version(clientpsk,
+                                                           TLS1_3_VERSION))
+            || !TEST_true(SSL_SESSION_up_ref(clientpsk)))
         goto end;
+    serverpsk = clientpsk;
 
     /* Check we can create a connection and the PSK is used */
     if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
@@ -2249,7 +2527,9 @@ static int test_tls13_psk(void)
     testresult = 1;
 
  end:
-    SSL_SESSION_free(psk);
+    SSL_SESSION_free(clientpsk);
+    SSL_SESSION_free(serverpsk);
+    clientpsk = serverpsk = NULL;
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -2846,13 +3126,14 @@ int setup_tests(void)
     ADD_TEST(test_early_cb);
 #endif
 #ifndef OPENSSL_NO_TLS1_3
-    ADD_ALL_TESTS(test_early_data_read_write, 2);
-    ADD_ALL_TESTS(test_early_data_skip, 2);
-    ADD_ALL_TESTS(test_early_data_skip_hrr, 2);
-    ADD_ALL_TESTS(test_early_data_not_sent, 2);
-    ADD_ALL_TESTS(test_early_data_not_expected, 2);
+    ADD_ALL_TESTS(test_early_data_read_write, 3);
+    ADD_ALL_TESTS(test_early_data_skip, 3);
+    ADD_ALL_TESTS(test_early_data_skip_hrr, 3);
+    ADD_ALL_TESTS(test_early_data_not_sent, 3);
+    ADD_ALL_TESTS(test_early_data_psk, 8);
+    ADD_ALL_TESTS(test_early_data_not_expected, 3);
 # ifndef OPENSSL_NO_TLS1_2
-    ADD_ALL_TESTS(test_early_data_tls1_2, 2);
+    ADD_ALL_TESTS(test_early_data_tls1_2, 3);
 # endif
 #endif
 #ifndef OPENSSL_NO_TLS1_3
diff --git a/util/libssl.num b/util/libssl.num
index d577456..14a7023 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -465,3 +465,7 @@ SSL_free_buffers                        465	1_1_1	EXIST::FUNCTION:
 SSL_SESSION_dup                         466	1_1_1	EXIST::FUNCTION:
 SSL_get_pending_cipher                  467	1_1_1	EXIST::FUNCTION:
 SSL_CIPHER_get_protocol_id              468	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_set_max_early_data          469	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_set1_alpn_selected          470	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_set1_hostname               471	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_get0_alpn_selected          472	1_1_1	EXIST::FUNCTION:


More information about the openssl-commits mailing list