[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Thu Feb 1 17:19:33 UTC 2018


The branch master has been updated
       via  e43e6b1951de931ca500c6964496e76651332f5e (commit)
       via  658e4879c50dff23c040c681656843a50002e403 (commit)
       via  88834998dda00a713abe3a17d6caebba2f70318d (commit)
       via  9d75dce3e1f94be6006500089491cb3284f70d06 (commit)
       via  633a8829ffc01952aed1f5040d481a5eeef1670c (commit)
      from  848a950b715fa73070a9d1638e7630acfb0dfa4d (commit)


- Log -----------------------------------------------------------------
commit e43e6b1951de931ca500c6964496e76651332f5e
Author: Todd Short <tshort at akamai.com>
Date:   Wed Jan 31 16:36:27 2018 -0500

    Fix some minor code nits
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/4964)

commit 658e4879c50dff23c040c681656843a50002e403
Author: Todd Short <tshort at akamai.com>
Date:   Wed Jan 31 16:34:20 2018 -0500

    Fix doc nits
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/4964)

commit 88834998dda00a713abe3a17d6caebba2f70318d
Author: Todd Short <tshort at akamai.com>
Date:   Wed Jan 31 16:20:52 2018 -0500

    Free pha_dgst in SSL_clear()
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/4964)

commit 9d75dce3e1f94be6006500089491cb3284f70d06
Author: Todd Short <tshort at akamai.com>
Date:   Mon Dec 18 16:52:28 2017 -0500

    Add TLSv1.3 post-handshake authentication (PHA)
    
    Add SSL_verify_client_post_handshake() for servers to initiate PHA
    
    Add SSL_force_post_handshake_auth() for clients that don't have certificates
    initially configured, but use a certificate callback.
    
    Update SSL_CTX_set_verify()/SSL_set_verify() mode:
    
    * Add SSL_VERIFY_POST_HANDSHAKE to postpone client authentication until after
    the initial handshake.
    
    * Update SSL_VERIFY_CLIENT_ONCE now only sends out one CertRequest regardless
    of when the certificate authentication takes place; either initial handshake,
    re-negotiation, or post-handshake authentication.
    
    Add 'RequestPostHandshake' and 'RequirePostHandshake' SSL_CONF options that
    add the SSL_VERIFY_POST_HANDSHAKE to the 'Request' and 'Require' options
    
    Add support to s_client:
    * Enabled automatically when cert is configured
    * Can be forced enabled via -force_pha
    
    Add support to s_server:
    * Use 'c' to invoke PHA in s_server
    * Remove some dead code
    
    Update documentation
    
    Update unit tests:
    * Illegal use of PHA extension
    * TLSv1.3 certificate tests
    
    DTLS and TLS behave ever-so-slightly differently. So, when DTLS1.3 is
    implemented, it's PHA support state machine may need to be different.
    Add a TODO and a #error
    
    Update handshake context to deal with PHA.
    
    The handshake context for TLSv1.3 post-handshake auth is up through the
    ClientFinish message, plus the CertificateRequest message. Subsequent
    Certificate, CertificateVerify, and Finish messages are based on this
    handshake context (not the Certificate message per se, but it's included
    after the hash). KeyUpdate, NewSessionTicket, and prior Certificate
    Request messages are not included in post-handshake authentication.
    
    After the ClientFinished message is processed, save off the digest state
    for future post-handshake authentication. When post-handshake auth occurs,
    copy over the saved handshake context into the "main" handshake digest.
    This effectively discards the any KeyUpdate or NewSessionTicket messages
    and any prior post-handshake authentication.
    
    This, of course, assumes that the ID-22 did not mean to include any
    previous post-handshake authentication into the new handshake transcript.
    This is implied by section 4.4.1 that lists messages only up to the
    first ClientFinished.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/4964)

commit 633a8829ffc01952aed1f5040d481a5eeef1670c
Author: Todd Short <tshort at akamai.com>
Date:   Fri Jan 19 10:43:19 2018 -0500

    Remove bad comments
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/4964)

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

Summary of changes:
 apps/s_cb.c                                 |   1 +
 apps/s_client.c                             |   9 +
 apps/s_server.c                             |  24 +-
 crypto/err/openssl.txt                      |  17 +-
 doc/man1/s_client.pod                       |   6 +
 doc/man3/SSL_CONF_cmd.pod                   |  12 +
 doc/man3/SSL_CTX_set_verify.pod             |  67 +++-
 include/openssl/ssl.h                       |   5 +-
 include/openssl/sslerr.h                    |  12 +
 include/openssl/tls1.h                      |   1 +
 ssl/ssl_conf.c                              |   7 +-
 ssl/ssl_err.c                               |  19 ++
 ssl/ssl_lib.c                               |  57 ++++
 ssl/ssl_locl.h                              |  22 ++
 ssl/statem/extensions.c                     |  16 +
 ssl/statem/extensions_clnt.c                |  42 +++
 ssl/statem/extensions_cust.c                |   1 +
 ssl/statem/extensions_srvr.c                |  14 +
 ssl/statem/statem_clnt.c                    |  78 ++++-
 ssl/statem/statem_lib.c                     | 111 +++++--
 ssl/statem/statem_locl.h                    |  10 +-
 ssl/statem/statem_srvr.c                    | 121 ++++++-
 ssl/t1_trce.c                               |   3 +-
 test/build.info                             |   2 +-
 test/handshake_helper.c                     |  60 +++-
 test/recipes/70-test_tls13messages.t        |   7 +-
 test/recipes/80-test_ssl_new.t              |   3 +-
 test/ssl-tests/26-tls13_client_auth.conf    | 476 ++++++++++++++++++++++++++++
 test/ssl-tests/26-tls13_client_auth.conf.in | 293 +++++++++++++++++
 test/ssl_test_ctx.c                         |   8 +
 test/ssl_test_ctx.h                         |   7 +-
 test/sslapitest.c                           |  60 ++++
 util/libssl.num                             |   2 +
 util/perl/TLSProxy/Message.pm               |   1 +
 util/perl/checkhandshake.pm                 |   3 +-
 35 files changed, 1489 insertions(+), 88 deletions(-)
 create mode 100644 test/ssl-tests/26-tls13_client_auth.conf
 create mode 100644 test/ssl-tests/26-tls13_client_auth.conf.in

diff --git a/apps/s_cb.c b/apps/s_cb.c
index 575fb04..8d51d74 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -666,6 +666,7 @@ static STRINT_PAIR tlsext_types[] = {
     {"psk", TLSEXT_TYPE_psk},
     {"psk kex modes", TLSEXT_TYPE_psk_kex_modes},
     {"certificate authorities", TLSEXT_TYPE_certificate_authorities},
+    {"post handshake auth", TLSEXT_TYPE_post_handshake_auth},
     {NULL}
 };
 
diff --git a/apps/s_client.c b/apps/s_client.c
index 774345e..ca2bd0d 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -602,6 +602,7 @@ typedef enum OPTION_choice {
     OPT_CT, OPT_NOCT, OPT_CTLOG_FILE,
 #endif
     OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME,
+    OPT_FORCE_PHA,
     OPT_R_ENUM
 } OPTION_CHOICE;
 
@@ -788,6 +789,7 @@ const OPTIONS s_client_options[] = {
 #endif
     {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"},
     {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"},
+    {"force_pha", OPT_FORCE_PHA, '-', "Force-enable post-handshake-authentication"},
     {NULL, OPT_EOF, 0x00, NULL}
 };
 
@@ -958,6 +960,7 @@ int s_client_main(int argc, char **argv)
     int isdtls = 0;
 #endif
     char *psksessf = NULL;
+    int force_pha = 0;
 
     FD_ZERO(&readfds);
     FD_ZERO(&writefds);
@@ -1469,6 +1472,9 @@ int s_client_main(int argc, char **argv)
         case OPT_EARLY_DATA:
             early_data_file = opt_arg();
             break;
+        case OPT_FORCE_PHA:
+            force_pha = 1;
+            break;
         }
     }
     if (count4or6 >= 2) {
@@ -1904,6 +1910,9 @@ int s_client_main(int argc, char **argv)
     if (con == NULL)
         goto end;
 
+    if (force_pha)
+        SSL_force_post_handshake_auth(con);
+
     if (sess_in != NULL) {
         SSL_SESSION *sess;
         BIO *stmp = BIO_new_file(sess_in, "r");
diff --git a/apps/s_server.c b/apps/s_server.c
index f124b12..a0e72b3 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -2480,9 +2480,6 @@ static int sv_body(int s, int stype, int prot, unsigned char *context)
                     printf("SSL_do_handshake -> %d\n", i);
                     i = 0;      /* 13; */
                     continue;
-                    /*
-                     * strcpy(buf,"server side RE-NEGOTIATE\n");
-                     */
                 }
                 if ((buf[0] == 'R') && ((buf[1] == '\n') || (buf[1] == '\r'))) {
                     SSL_set_verify(con,
@@ -2493,10 +2490,6 @@ static int sv_body(int s, int stype, int prot, unsigned char *context)
                     printf("SSL_do_handshake -> %d\n", i);
                     i = 0;      /* 13; */
                     continue;
-                    /*
-                     * strcpy(buf,"server side RE-NEGOTIATE asking for client
-                     * cert\n");
-                     */
                 }
                 if ((buf[0] == 'K' || buf[0] == 'k')
                         && ((buf[1] == '\n') || (buf[1] == '\r'))) {
@@ -2507,10 +2500,19 @@ static int sv_body(int s, int stype, int prot, unsigned char *context)
                     printf("SSL_do_handshake -> %d\n", i);
                     i = 0;
                     continue;
-                    /*
-                     * strcpy(buf,"server side RE-NEGOTIATE asking for client
-                     * cert\n");
-                     */
+                }
+                if (buf[0] == 'c' && ((buf[1] == '\n') || (buf[1] == '\r'))) {
+                    SSL_set_verify(con, SSL_VERIFY_PEER, NULL);
+                    i = SSL_verify_client_post_handshake(con);
+                    if (i == 0) {
+                        printf("Failed to initiate request\n");
+                        ERR_print_errors(bio_err);
+                    } else {
+                        i = SSL_do_handshake(con);
+                        printf("SSL_do_handshake -> %d\n", i);
+                        i = 0;
+                    }
+                    continue;
                 }
                 if (buf[0] == 'P') {
                     static const char *str = "Lets print some clear text\n";
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index ed706da..fb3be57 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1,4 +1,4 @@
-# Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 1999-2018 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
@@ -1192,6 +1192,7 @@ SSL_F_SSL_USE_RSAPRIVATEKEY_ASN1:205:SSL_use_RSAPrivateKey_ASN1
 SSL_F_SSL_USE_RSAPRIVATEKEY_FILE:206:SSL_use_RSAPrivateKey_file
 SSL_F_SSL_VALIDATE_CT:400:ssl_validate_ct
 SSL_F_SSL_VERIFY_CERT_CHAIN:207:ssl_verify_cert_chain
+SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE:616:SSL_verify_client_post_handshake
 SSL_F_SSL_WRITE:208:SSL_write
 SSL_F_SSL_WRITE_EARLY_DATA:526:SSL_write_early_data
 SSL_F_SSL_WRITE_EARLY_FINISH:527:*
@@ -1205,6 +1206,10 @@ SSL_F_TLS13_ENC:609:tls13_enc
 SSL_F_TLS13_FINAL_FINISH_MAC:605:tls13_final_finish_mac
 SSL_F_TLS13_GENERATE_SECRET:591:tls13_generate_secret
 SSL_F_TLS13_HKDF_EXPAND:561:tls13_hkdf_expand
+SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA:617:\
+	tls13_restore_handshake_digest_for_pha
+SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA:618:\
+	tls13_save_handshake_digest_for_pha
 SSL_F_TLS13_SETUP_KEY_BLOCK:441:tls13_setup_key_block
 SSL_F_TLS1_CHANGE_CIPHER_STATE:209:tls1_change_cipher_state
 SSL_F_TLS1_CHECK_DUPLICATE_EXTENSIONS:341:*
@@ -1247,6 +1252,8 @@ SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE:470:tls_construct_ctos_key_share
 SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN:549:tls_construct_ctos_maxfragmentlen
 SSL_F_TLS_CONSTRUCT_CTOS_NPN:471:tls_construct_ctos_npn
 SSL_F_TLS_CONSTRUCT_CTOS_PADDING:472:tls_construct_ctos_padding
+SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH:619:\
+	tls_construct_ctos_post_handshake_auth
 SSL_F_TLS_CONSTRUCT_CTOS_PSK:501:tls_construct_ctos_psk
 SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES:509:tls_construct_ctos_psk_kex_modes
 SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE:473:tls_construct_ctos_renegotiate
@@ -1315,6 +1322,7 @@ SSL_F_TLS_PARSE_CTOS_EC_PT_FORMATS:569:tls_parse_ctos_ec_pt_formats
 SSL_F_TLS_PARSE_CTOS_EMS:570:tls_parse_ctos_ems
 SSL_F_TLS_PARSE_CTOS_KEY_SHARE:463:tls_parse_ctos_key_share
 SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN:571:tls_parse_ctos_maxfragmentlen
+SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH:620:tls_parse_ctos_post_handshake_auth
 SSL_F_TLS_PARSE_CTOS_PSK:505:tls_parse_ctos_psk
 SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES:572:tls_parse_ctos_psk_kex_modes
 SSL_F_TLS_PARSE_CTOS_RENEGOTIATE:464:tls_parse_ctos_renegotiate
@@ -2446,6 +2454,7 @@ SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:151:error in received cipher list
 SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN:204:error setting tlsa base domain
 SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE:194:exceeds max fragment size
 SSL_R_EXCESSIVE_MESSAGE_SIZE:152:excessive message size
+SSL_R_EXTENSION_NOT_RECEIVED:279:extension not received
 SSL_R_EXTRA_DATA_IN_MESSAGE:153:extra data in message
 SSL_R_EXT_LENGTH_MISMATCH:163:ext length mismatch
 SSL_R_FAILED_TO_INIT_ASYNC:405:failed to init async
@@ -2466,7 +2475,9 @@ SSL_R_INVALID_CCS_MESSAGE:260:invalid ccs message
 SSL_R_INVALID_CERTIFICATE_OR_ALG:238:invalid certificate or alg
 SSL_R_INVALID_COMMAND:280:invalid command
 SSL_R_INVALID_COMPRESSION_ALGORITHM:341:invalid compression algorithm
+SSL_R_INVALID_CONFIG:283:invalid config
 SSL_R_INVALID_CONFIGURATION_NAME:113:invalid configuration name
+SSL_R_INVALID_CONTEXT:282:invalid context
 SSL_R_INVALID_CT_VALIDATION_TYPE:212:invalid ct validation type
 SSL_R_INVALID_KEY_UPDATE_TYPE:120:invalid key update type
 SSL_R_INVALID_MAX_EARLY_DATA:174:invalid max early data
@@ -2495,6 +2506,7 @@ SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION:209:missing supported groups extension
 SSL_R_MISSING_TMP_DH_KEY:171:missing tmp dh key
 SSL_R_MISSING_TMP_ECDH_KEY:311:missing tmp ecdh key
 SSL_R_NOT_ON_RECORD_BOUNDARY:182:not on record boundary
+SSL_R_NOT_SERVER:284:not server
 SSL_R_NO_APPLICATION_PROTOCOL:235:no application protocol
 SSL_R_NO_CERTIFICATES_RETURNED:176:no certificates returned
 SSL_R_NO_CERTIFICATE_ASSIGNED:177:no certificate assigned
@@ -2534,6 +2546,7 @@ SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE:199:peer did not return a certificate
 SSL_R_PEM_NAME_BAD_PREFIX:391:pem name bad prefix
 SSL_R_PEM_NAME_TOO_SHORT:392:pem name too short
 SSL_R_PIPELINE_FAILURE:406:pipeline failure
+SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR:278:post handshake auth encoding err
 SSL_R_PROTOCOL_IS_SHUTDOWN:207:protocol is shutdown
 SSL_R_PSK_IDENTITY_NOT_FOUND:223:psk identity not found
 SSL_R_PSK_NO_CLIENT_CB:224:psk no client cb
@@ -2545,6 +2558,8 @@ SSL_R_RECORD_TOO_SMALL:298:record too small
 SSL_R_RENEGOTIATE_EXT_TOO_LONG:335:renegotiate ext too long
 SSL_R_RENEGOTIATION_ENCODING_ERR:336:renegotiation encoding err
 SSL_R_RENEGOTIATION_MISMATCH:337:renegotiation mismatch
+SSL_R_REQUEST_PENDING:285:request pending
+SSL_R_REQUEST_SENT:286:request sent
 SSL_R_REQUIRED_CIPHER_MISSING:215:required cipher missing
 SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING:342:\
 	required compression algorithm missing
diff --git a/doc/man1/s_client.pod b/doc/man1/s_client.pod
index 1d9dd39..4f0f01c 100644
--- a/doc/man1/s_client.pod
+++ b/doc/man1/s_client.pod
@@ -118,6 +118,7 @@ B<openssl> B<s_client>
 [B<-ctlogfile>]
 [B<-keylogfile file>]
 [B<-early_data file>]
+[B<-force_pha>]
 [B<target>]
 
 =head1 DESCRIPTION
@@ -621,6 +622,11 @@ Reads the contents of the specified file and attempts to send it as early data
 to the server. This will only work with resumed sessions that support early
 data and when the server accepts the early data.
 
+=item B<-force_pha>
+
+For TLSv1.3 only, always send the Post-Handshake Authentication extension,
+whether or not a certificate has been provided via B<-cert>.
+
 =item B<[target]>
 
 Rather than providing B<-connect>, the target hostname and optional port may
diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod
index 06b98bd..27317e0 100644
--- a/doc/man3/SSL_CONF_cmd.pod
+++ b/doc/man3/SSL_CONF_cmd.pod
@@ -435,6 +435,18 @@ occurs if the client does not present a certificate. Servers only.
 B<Once> requests a certificate from a client only on the initial connection:
 not when renegotiating. Servers only.
 
+B<RequestPostHandshake> configures the connection to support requests but does
+not require a certificate from the client post-handshake. A certificate will
+not be requested during the initial handshake. The server application must
+provide a mechanism to request a certificate post-handshake. Servers only.
+TLSv1.3 only.
+
+B<RequiresPostHandshake> configures the connection to support requests and
+requires a certificate from the client post-handshake: an error occurs if the
+client does not present a certificate. A certificate will not be requested
+during the initial handshake. The server application must provide a mechanism
+to request a certificate post-handshake. Servers only. TLSv1.3 only.
+
 =item B<ClientCAFile>, B<ClientCAPath>
 
 A file or directory of certificates in PEM format whose names are used as the
diff --git a/doc/man3/SSL_CTX_set_verify.pod b/doc/man3/SSL_CTX_set_verify.pod
index 9e634dd..89760f4 100644
--- a/doc/man3/SSL_CTX_set_verify.pod
+++ b/doc/man3/SSL_CTX_set_verify.pod
@@ -5,7 +5,9 @@
 SSL_get_ex_data_X509_STORE_CTX_idx,
 SSL_CTX_set_verify, SSL_set_verify,
 SSL_CTX_set_verify_depth, SSL_set_verify_depth,
-SSL_verify_cb
+SSL_verify_cb,
+SSL_verify_client_post_handshake,
+SSL_force_post_handshake_auth
 - set peer certificate verification parameters
 
 =head1 SYNOPSIS
@@ -15,11 +17,14 @@ SSL_verify_cb
  typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
 
  void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb verify_callback);
- void SSL_set_verify(SSL *s, int mode, SSL_verify_cb verify_callback);
+ void SSL_set_verify(SSL *ssl, int mode, SSL_verify_cb verify_callback);
  SSL_get_ex_data_X509_STORE_CTX_idx(void);
 
  void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth);
- void SSL_set_verify_depth(SSL *s, int depth);
+ void SSL_set_verify_depth(SSL *ssl, int depth);
+
+ int SSL_verify_client_post_handshake(SSL *ssl);
+ void SSL_force_post_handshake_auth(SSL *ssl);
 
 =head1 DESCRIPTION
 
@@ -43,6 +48,16 @@ verification that shall be allowed for B<ctx>.
 SSL_set_verify_depth() sets the maximum B<depth> for the certificate chain
 verification that shall be allowed for B<ssl>.
 
+SSL_force_post_handshake_auth() forces the Post-Handshake Authentication
+extension to be added to the ClientHello regardless of certificate configuration
+at the time of the initial handshake, such that post-handshake authentication
+can be requested by the server. A certificate callback will need to be set via
+SSL_CTX_set_client_cert_cb() if no certificate is provided at initialization.
+
+SSL_verify_client_post_handshake() causes a CertificateRequest message to be
+sent by a server on the given B<ssl> connection. The SSL_VERIFY_PEER flag must
+be set; the SSL_VERIFY_POST_HANDSHAKE flag is optional.
+
 =head1 NOTES
 
 The verification of certificates can be controlled by a set of logically
@@ -69,7 +84,8 @@ fails, the TLS/SSL handshake is
 immediately terminated with an alert message containing the reason for
 the verification failure.
 The behaviour can be controlled by the additional
-SSL_VERIFY_FAIL_IF_NO_PEER_CERT and SSL_VERIFY_CLIENT_ONCE flags.
+SSL_VERIFY_FAIL_IF_NO_PEER_CERT, SSL_VERIFY_CLIENT_ONCE and
+SSL_VERIFY_POST_HANDSHAKE flags.
 
 B<Client mode:> the server certificate is verified. If the verification process
 fails, the TLS/SSL handshake is
@@ -87,9 +103,22 @@ B<Client mode:> ignored
 
 =item SSL_VERIFY_CLIENT_ONCE
 
-B<Server mode:> only request a client certificate on the initial TLS/SSL
-handshake. Do not ask for a client certificate again in case of a
-renegotiation. This flag must be used together with SSL_VERIFY_PEER.
+B<Server mode:> only request a client certificate once during the
+connection. Do not ask for a client certificate again during
+renegotiation or post-authentication if a certificate was requested
+during the initial handshake. This flag must be used together with
+SSL_VERIFY_PEER.
+
+B<Client mode:> ignored
+
+=item SSL_VERIFY_POST_HANDSHAKE
+
+B<Server mode:> the server will not send a client certificate request
+during the initial handshake, but will send the request via
+SSL_verify_client_post_handshake(). This allows the SSL_CTX or SSL
+to be configured for post-handshake peer verification before the
+handshake occurs. This flag must be used together with
+SSL_VERIFY_PEER. TLSv1.3 only; no effect on pre-TLSv1.3 connections.
 
 B<Client mode:> ignored
 
@@ -154,6 +183,20 @@ Its return value is identical to B<preverify_ok>, so that any verification
 failure will lead to a termination of the TLS/SSL handshake with an
 alert message, if SSL_VERIFY_PEER is set.
 
+After calling SSL_force_post_handshake_auth(), the client will need to add a
+certificate or certificate callback to its configuration before it can
+successfully authenticate. This must be called before SSL_connect().
+
+SSL_verify_client_post_handshake() requires that verify flags have been
+previously set, and that a client sent the post-handshake authentication
+extension. When the client returns a certificate the verify callback will be
+invoked. A write operation must take place for the Certificate Request to be
+sent to the client, this can be done with SSL_do_handshake() or SSL_write_ex().
+Only one certificate request may be outstanding at any time.
+
+When post-handshake authentication occurs, a refreshed NewSessionTicket
+message is sent to the client.
+
 =head1 BUGS
 
 In client mode, it is not checked whether the SSL_VERIFY_PEER flag
@@ -165,6 +208,10 @@ required.
 
 The SSL*_set_verify*() functions do not provide diagnostic information.
 
+The SSL_verify_client_post_handshake() function returns 1 if the request
+succeeded, and 0 if the request failed. The error stack can be examined
+to determine the failure reason.
+
 =head1 EXAMPLES
 
 The following code sequence realizes an example B<verify_callback> function
@@ -288,8 +335,14 @@ L<SSL_CTX_load_verify_locations(3)>,
 L<SSL_get_peer_certificate(3)>,
 L<SSL_CTX_set_cert_verify_callback(3)>,
 L<SSL_get_ex_data_X509_STORE_CTX_idx(3)>,
+L<SSL_CTX_set_client_cert_cb(3)>,
 L<CRYPTO_get_ex_new_index(3)>
 
+=head1 HISTORY
+
+The SSL_VERIFY_POST_HANDSHAKE option, and the SSL_verify_client_post_handshake()
+and SSL_force_post_handshake_auth() functions were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
 Copyright 2000-2017 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 97d2e46..9c45b90 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1052,13 +1052,14 @@ size_t SSL_get_finished(const SSL *s, void *buf, size_t count);
 size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count);
 
 /*
- * use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options are
+ * use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 3 options are
  * 'ored' with SSL_VERIFY_PEER if they are desired
  */
 # define SSL_VERIFY_NONE                 0x00
 # define SSL_VERIFY_PEER                 0x01
 # define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
 # define SSL_VERIFY_CLIENT_ONCE          0x04
+# define SSL_VERIFY_POST_HANDSHAKE       0x08
 
 # define OpenSSL_add_ssl_algorithms()    SSL_library_init()
 # if OPENSSL_API_COMPAT < 0x10100000L
@@ -1850,6 +1851,8 @@ int SSL_renegotiate(SSL *s);
 int SSL_renegotiate_abbreviated(SSL *s);
 __owur int SSL_renegotiate_pending(SSL *s);
 int SSL_shutdown(SSL *s);
+__owur int SSL_verify_client_post_handshake(SSL *s);
+void SSL_force_post_handshake_auth(SSL *s);
 
 __owur const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx);
 __owur const SSL_METHOD *SSL_get_ssl_method(SSL *s);
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index ec81ba3..a84a62d 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -241,6 +241,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_USE_RSAPRIVATEKEY_FILE                 206
 # define SSL_F_SSL_VALIDATE_CT                            400
 # define SSL_F_SSL_VERIFY_CERT_CHAIN                      207
+# define SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE           616
 # define SSL_F_SSL_WRITE                                  208
 # define SSL_F_SSL_WRITE_EARLY_DATA                       526
 # define SSL_F_SSL_WRITE_EARLY_FINISH                     527
@@ -254,6 +255,8 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS13_FINAL_FINISH_MAC                     605
 # define SSL_F_TLS13_GENERATE_SECRET                      591
 # define SSL_F_TLS13_HKDF_EXPAND                          561
+# define SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA     617
+# define SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA        618
 # define SSL_F_TLS13_SETUP_KEY_BLOCK                      441
 # define SSL_F_TLS1_CHANGE_CIPHER_STATE                   209
 # define SSL_F_TLS1_CHECK_DUPLICATE_EXTENSIONS            341
@@ -295,6 +298,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN          549
 # define SSL_F_TLS_CONSTRUCT_CTOS_NPN                     471
 # define SSL_F_TLS_CONSTRUCT_CTOS_PADDING                 472
+# define SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH     619
 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK                     501
 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES           509
 # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE             473
@@ -358,6 +362,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_PARSE_CTOS_EMS                         570
 # define SSL_F_TLS_PARSE_CTOS_KEY_SHARE                   463
 # define SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN              571
+# define SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH         620
 # define SSL_F_TLS_PARSE_CTOS_PSK                         505
 # define SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES               572
 # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE                 464
@@ -522,6 +527,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN             204
 # define SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE                  194
 # define SSL_R_EXCESSIVE_MESSAGE_SIZE                     152
+# define SSL_R_EXTENSION_NOT_RECEIVED                     279
 # define SSL_R_EXTRA_DATA_IN_MESSAGE                      153
 # define SSL_R_EXT_LENGTH_MISMATCH                        163
 # define SSL_R_FAILED_TO_INIT_ASYNC                       405
@@ -542,7 +548,9 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_INVALID_CERTIFICATE_OR_ALG                 238
 # define SSL_R_INVALID_COMMAND                            280
 # define SSL_R_INVALID_COMPRESSION_ALGORITHM              341
+# define SSL_R_INVALID_CONFIG                             283
 # define SSL_R_INVALID_CONFIGURATION_NAME                 113
+# define SSL_R_INVALID_CONTEXT                            282
 # define SSL_R_INVALID_CT_VALIDATION_TYPE                 212
 # define SSL_R_INVALID_KEY_UPDATE_TYPE                    120
 # define SSL_R_INVALID_MAX_EARLY_DATA                     174
@@ -571,6 +579,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_MISSING_TMP_DH_KEY                         171
 # define SSL_R_MISSING_TMP_ECDH_KEY                       311
 # define SSL_R_NOT_ON_RECORD_BOUNDARY                     182
+# define SSL_R_NOT_SERVER                                 284
 # define SSL_R_NO_APPLICATION_PROTOCOL                    235
 # define SSL_R_NO_CERTIFICATES_RETURNED                   176
 # define SSL_R_NO_CERTIFICATE_ASSIGNED                    177
@@ -608,6 +617,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_PEM_NAME_BAD_PREFIX                        391
 # define SSL_R_PEM_NAME_TOO_SHORT                         392
 # define SSL_R_PIPELINE_FAILURE                           406
+# define SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR           278
 # define SSL_R_PROTOCOL_IS_SHUTDOWN                       207
 # define SSL_R_PSK_IDENTITY_NOT_FOUND                     223
 # define SSL_R_PSK_NO_CLIENT_CB                           224
@@ -619,6 +629,8 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_RENEGOTIATE_EXT_TOO_LONG                   335
 # define SSL_R_RENEGOTIATION_ENCODING_ERR                 336
 # define SSL_R_RENEGOTIATION_MISMATCH                     337
+# define SSL_R_REQUEST_PENDING                            285
+# define SSL_R_REQUEST_SENT                               286
 # define SSL_R_REQUIRED_CIPHER_MISSING                    215
 # define SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING     342
 # define SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING           345
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 434e327..1253352 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -146,6 +146,7 @@ extern "C" {
 # define TLSEXT_TYPE_cookie                      44
 # define TLSEXT_TYPE_psk_kex_modes               45
 # define TLSEXT_TYPE_certificate_authorities     47
+# define TLSEXT_TYPE_post_handshake_auth         49
 # define TLSEXT_TYPE_signature_algorithms_cert   50
 # define TLSEXT_TYPE_key_share                   51
 
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c
index 0cd8ace..cb4ff8d 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -386,7 +386,12 @@ static int cmd_VerifyMode(SSL_CONF_CTX *cctx, const char *value)
         SSL_FLAG_VFY_SRV("Request", SSL_VERIFY_PEER),
         SSL_FLAG_VFY_SRV("Require",
                          SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
-        SSL_FLAG_VFY_SRV("Once", SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE)
+        SSL_FLAG_VFY_SRV("Once", SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE),
+        SSL_FLAG_VFY_SRV("RequestPostHandshake",
+                         SSL_VERIFY_PEER | SSL_VERIFY_POST_HANDSHAKE),
+        SSL_FLAG_VFY_SRV("RequirePostHandshake",
+                         SSL_VERIFY_PEER | SSL_VERIFY_POST_HANDSHAKE |
+                         SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     };
     if (value == NULL)
         return -3;
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 746678c..f0bde60 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -351,6 +351,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VALIDATE_CT, 0), "ssl_validate_ct"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VERIFY_CERT_CHAIN, 0),
      "ssl_verify_cert_chain"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, 0),
+     "SSL_verify_client_post_handshake"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE, 0), "SSL_write"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_DATA, 0),
      "SSL_write_early_data"},
@@ -369,6 +371,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_GENERATE_SECRET, 0),
      "tls13_generate_secret"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_HKDF_EXPAND, 0), "tls13_hkdf_expand"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA, 0),
+     "tls13_restore_handshake_digest_for_pha"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA, 0),
+     "tls13_save_handshake_digest_for_pha"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_SETUP_KEY_BLOCK, 0),
      "tls13_setup_key_block"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS1_CHANGE_CIPHER_STATE, 0),
@@ -441,6 +447,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "tls_construct_ctos_npn"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PADDING, 0),
      "tls_construct_ctos_padding"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH, 0),
+     "tls_construct_ctos_post_handshake_auth"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK, 0),
      "tls_construct_ctos_psk"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0),
@@ -557,6 +565,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "tls_parse_ctos_key_share"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN, 0),
      "tls_parse_ctos_maxfragmentlen"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH, 0),
+     "tls_parse_ctos_post_handshake_auth"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK, 0), "tls_parse_ctos_psk"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES, 0),
      "tls_parse_ctos_psk_kex_modes"},
@@ -832,6 +842,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "exceeds max fragment size"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCESSIVE_MESSAGE_SIZE),
     "excessive message size"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTENSION_NOT_RECEIVED),
+    "extension not received"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTRA_DATA_IN_MESSAGE),
     "extra data in message"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXT_LENGTH_MISMATCH),
@@ -868,8 +880,10 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "invalid command"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMPRESSION_ALGORITHM),
     "invalid compression algorithm"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIG), "invalid config"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIGURATION_NAME),
     "invalid configuration name"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONTEXT), "invalid context"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CT_VALIDATION_TYPE),
     "invalid ct validation type"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_KEY_UPDATE_TYPE),
@@ -919,6 +933,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "missing tmp ecdh key"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_ON_RECORD_BOUNDARY),
     "not on record boundary"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_SERVER), "not server"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_APPLICATION_PROTOCOL),
     "no application protocol"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED),
@@ -978,6 +993,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "pem name bad prefix"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_TOO_SHORT), "pem name too short"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PIPELINE_FAILURE), "pipeline failure"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR),
+    "post handshake auth encoding err"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN),
     "protocol is shutdown"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND),
@@ -996,6 +1013,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "renegotiation encoding err"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_MISMATCH),
     "renegotiation mismatch"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_PENDING), "request pending"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_SENT), "request sent"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_CIPHER_MISSING),
     "required cipher missing"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING),
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 270d4de..68a9b19 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -614,6 +614,9 @@ int SSL_clear(SSL *s)
 
     s->key_update = SSL_KEY_UPDATE_NONE;
 
+    EVP_MD_CTX_free(s->pha_dgst);
+    s->pha_dgst = NULL;
+
     /* Reset DANE verification result state */
     s->dane.mdpth = -1;
     s->dane.pdpth = -1;
@@ -1186,6 +1189,8 @@ void SSL_free(SSL *s)
     OPENSSL_free(s->ext.alpn);
     OPENSSL_free(s->ext.tls13_cookie);
     OPENSSL_free(s->clienthello);
+    OPENSSL_free(s->pha_context);
+    EVP_MD_CTX_free(s->pha_dgst);
 
     sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free);
 
@@ -5318,3 +5323,55 @@ int SSL_stateless(SSL *s)
 
     return 0;
 }
+
+void SSL_force_post_handshake_auth(SSL *ssl)
+{
+    ssl->pha_forced = 1;
+}
+
+int SSL_verify_client_post_handshake(SSL *ssl)
+{
+    if (!SSL_IS_TLS13(ssl)) {
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_WRONG_SSL_VERSION);
+        return 0;
+    }
+    if (!ssl->server) {
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_NOT_SERVER);
+        return 0;
+    }
+
+    if (!SSL_is_init_finished(ssl)) {
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_STILL_IN_INIT);
+        return 0;
+    }
+
+    switch (ssl->post_handshake_auth) {
+    case SSL_PHA_NONE:
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_EXTENSION_NOT_RECEIVED);
+        return 0;
+    default:
+    case SSL_PHA_EXT_SENT:
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, ERR_R_INTERNAL_ERROR);
+        return 0;
+    case SSL_PHA_EXT_RECEIVED:
+        break;
+    case SSL_PHA_REQUEST_PENDING:
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_REQUEST_PENDING);
+        return 0;
+    case SSL_PHA_REQUESTED:
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_REQUEST_SENT);
+        return 0;
+    }
+
+    ssl->post_handshake_auth = SSL_PHA_REQUEST_PENDING;
+
+    /* checks verify_mode and algorithm_auth */
+    if (!send_certificate_request(ssl)) {
+        ssl->post_handshake_auth = SSL_PHA_EXT_RECEIVED; /* restore on error */
+        SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_INVALID_CONFIG);
+        return 0;
+    }
+
+    ossl_statem_set_in_init(ssl, 1);
+    return 1;
+}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 6afd009..221d5b9 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -402,6 +402,15 @@
 #define CERT_PRIVATE_KEY        2
 */
 
+/* Post-Handshake Authentication state */
+typedef enum {
+    SSL_PHA_NONE = 0,
+    SSL_PHA_EXT_SENT,        /* client-side only: extension sent */
+    SSL_PHA_EXT_RECEIVED,    /* server-side only: extension received */
+    SSL_PHA_REQUEST_PENDING, /* server-side only: request pending */
+    SSL_PHA_REQUESTED        /* request received by client, or sent by server */
+} SSL_PHA_STATE;
+
 /* CipherSuite length. SSLv3 and all TLS versions. */
 # define TLS_CIPHER_LEN 2
 /* used to hold info on the particular ciphers used */
@@ -702,6 +711,7 @@ typedef enum tlsext_index_en {
     TLSEXT_IDX_signed_certificate_timestamp,
     TLSEXT_IDX_extended_master_secret,
     TLSEXT_IDX_signature_algorithms_cert,
+    TLSEXT_IDX_post_handshake_auth,
     TLSEXT_IDX_signature_algorithms,
     TLSEXT_IDX_supported_versions,
     TLSEXT_IDX_psk_kex_modes,
@@ -1334,6 +1344,14 @@ struct ssl_st {
     int renegotiate;
     /* If sending a KeyUpdate is pending */
     int key_update;
+    /* Post-handshake authentication state */
+    SSL_PHA_STATE post_handshake_auth;
+    int pha_forced;
+    uint8_t* pha_context;
+    size_t pha_context_len;
+    int certreqs_sent;
+    EVP_MD_CTX *pha_dgst; /* this is just the digest through ClientFinished */
+
 # ifndef OPENSSL_NO_SRP
     /* ctx for SRP authentication */
     SRP_CTX srp_ctx;
@@ -2535,6 +2553,10 @@ __owur int srp_generate_server_master_secret(SSL *s);
 __owur int srp_generate_client_master_secret(SSL *s);
 __owur int srp_verify_server_param(SSL *s);
 
+/* statem/statem_srvr.c */
+
+__owur int send_certificate_request(SSL *s);
+
 /* statem/extensions_cust.c */
 
 custom_ext_method *custom_ext_find(const custom_ext_methods *exts,
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 5ad86f2..daf43c6 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -55,6 +55,7 @@ static int init_srtp(SSL *s, unsigned int context);
 static int final_sig_algs(SSL *s, unsigned int context, int sent);
 static int final_early_data(SSL *s, unsigned int context, int sent);
 static int final_maxfragmentlen(SSL *s, unsigned int context, int sent);
+static int init_post_handshake_auth(SSL *s, unsigned int context);
 
 /* Structure to define a built-in extension */
 typedef struct extensions_definition_st {
@@ -290,6 +291,14 @@ static const EXTENSION_DEFINITION ext_defs[] = {
         NULL, NULL, NULL
     },
     {
+        TLSEXT_TYPE_post_handshake_auth,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ONLY,
+        init_post_handshake_auth,
+        tls_parse_ctos_post_handshake_auth, NULL,
+        NULL, tls_construct_ctos_post_handshake_auth,
+        NULL,
+    },
+    {
         TLSEXT_TYPE_signature_algorithms,
         SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
         init_sig_algs, tls_parse_ctos_sig_algs,
@@ -1653,3 +1662,10 @@ static int final_maxfragmentlen(SSL *s, unsigned int context, int sent)
 
     return 1;
 }
+
+static int init_post_handshake_auth(SSL *s, unsigned int context)
+{
+    s->post_handshake_auth = SSL_PHA_NONE;
+
+    return 1;
+}
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 5441e98..6286242 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -1133,6 +1133,48 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
 #endif
 }
 
+EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt,
+                                                  unsigned int context,
+                                                  X509 *x, size_t chainidx)
+{
+#ifndef OPENSSL_NO_TLS1_3
+    if (!s->pha_forced) {
+        int i, n = 0;
+
+        /* check for cert, if present, we can do post-handshake auth */
+        if (s->cert == NULL)
+            return EXT_RETURN_NOT_SENT;
+
+        for (i = 0; i < SSL_PKEY_NUM; i++) {
+            if (s->cert->pkeys[i].x509 != NULL
+                    && s->cert->pkeys[i].privatekey != NULL)
+                n++;
+        }
+
+        /* no identity certificates, so no extension */
+        if (n == 0)
+            return EXT_RETURN_NOT_SENT;
+    }
+
+    /* construct extension - 0 length, no contents */
+    if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_post_handshake_auth)
+            || !WPACKET_start_sub_packet_u16(pkt)
+            || !WPACKET_close(pkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH,
+                 ERR_R_INTERNAL_ERROR);
+        return EXT_RETURN_FAIL;
+    }
+
+    s->post_handshake_auth = SSL_PHA_EXT_SENT;
+
+    return EXT_RETURN_SENT;
+#else
+    return EXT_RETURN_NOT_SENT;
+#endif
+}
+
+
 /*
  * Parse the server's renegotiation binding and abort if it's not right
  */
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
index 0a23630..60a7c37 100644
--- a/ssl/statem/extensions_cust.c
+++ b/ssl/statem/extensions_cust.c
@@ -525,6 +525,7 @@ int SSL_extension_supported(unsigned int ext_type)
     case TLSEXT_TYPE_early_data:
     case TLSEXT_TYPE_certificate_authorities:
     case TLSEXT_TYPE_psk:
+    case TLSEXT_TYPE_post_handshake_auth:
         return 1;
     default:
         return 0;
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 0a7bac4..27ff5a5 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1161,6 +1161,20 @@ err:
     return 0;
 }
 
+int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context,
+                                       X509 *x, size_t chainidx)
+{
+    if (PACKET_remaining(pkt) != 0) {
+        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH,
+                 SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR);
+        return 0;
+    }
+
+    s->post_handshake_auth = SSL_PHA_EXT_RECEIVED;
+
+    return 1;
+}
+
 /*
  * Add the server's renegotiation binding
  */
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index e79bd7b..f224da6 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
  * Copyright 2005 Nokia. All rights reserved.
  *
@@ -160,6 +160,26 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
             st->hand_state = TLS_ST_CR_KEY_UPDATE;
             return 1;
         }
+        if (mt == SSL3_MT_CERTIFICATE_REQUEST) {
+#if DTLS_MAX_VERSION != DTLS1_2_VERSION
+# error TODO(DTLS1.3): Restore digest for PHA before adding message.
+#endif
+            if (!SSL_IS_DTLS(s) && s->post_handshake_auth == SSL_PHA_EXT_SENT) {
+                s->post_handshake_auth = SSL_PHA_REQUESTED;
+                /*
+                 * In TLS, this is called before the message is added to the
+                 * digest. In DTLS, this is expected to be called after adding
+                 * to the digest. Either move the digest restore, or add the
+                 * message here after the swap, or do it after the clientFinished?
+                 */
+                if (!tls13_restore_handshake_digest_for_pha(s)) {
+                    /* SSLfatal() already called */
+                    return 0;
+                }
+                st->hand_state = TLS_ST_CR_CERT_REQ;
+                return 1;
+            }
+        }
         break;
     }
 
@@ -382,6 +402,17 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
                  ERR_R_INTERNAL_ERROR);
         return WRITE_TRAN_ERROR;
 
+    case TLS_ST_CR_CERT_REQ:
+        if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+            st->hand_state = TLS_ST_CW_CERT;
+            return WRITE_TRAN_CONTINUE;
+        }
+        /* Shouldn't happen - same as default case */
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_OSSL_STATEM_CLIENT13_WRITE_TRANSITION,
+                 ERR_R_INTERNAL_ERROR);
+        return WRITE_TRAN_ERROR;
+
     case TLS_ST_CR_FINISHED:
         if (s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY
                 || s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING)
@@ -798,11 +829,17 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
             return WORK_MORE_B;
 
         if (SSL_IS_TLS13(s)) {
-            if (!s->method->ssl3_enc->change_cipher_state(s,
-                        SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+            if (!tls13_save_handshake_digest_for_pha(s)) {
                 /* SSLfatal() already called */
                 return WORK_ERROR;
             }
+            if (s->post_handshake_auth != SSL_PHA_REQUESTED) {
+                if (!s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+                    /* SSLfatal() already called */
+                    return WORK_ERROR;
+                }
+            }
         }
         break;
 
@@ -2399,9 +2436,11 @@ MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, PACKET *pkt)
         OPENSSL_free(s->s3->tmp.ctype);
         s->s3->tmp.ctype = NULL;
         s->s3->tmp.ctype_len = 0;
+        OPENSSL_free(s->pha_context);
+        s->pha_context = NULL;
 
-        /* TODO(TLS1.3) need to process request context, for now ignore */
-        if (!PACKET_get_length_prefixed_1(pkt, &reqctx)) {
+        if (!PACKET_get_length_prefixed_1(pkt, &reqctx) ||
+            !PACKET_memdup(&reqctx, &s->pha_context, &s->pha_context_len)) {
             SSLfatal(s, SSL_AD_DECODE_ERROR,
                      SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST,
                      SSL_R_LENGTH_MISMATCH);
@@ -3357,8 +3396,12 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst)
             }
             s->rwstate = SSL_NOTHING;
         }
-        if (ssl3_check_client_certificate(s))
+        if (ssl3_check_client_certificate(s)) {
+            if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+                return WORK_FINISHED_STOP;
+            }
             return WORK_FINISHED_CONTINUE;
+        }
 
         /* Fall through to WORK_MORE_B */
         wst = WORK_MORE_B;
@@ -3403,6 +3446,8 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst)
             }
         }
 
+        if (s->post_handshake_auth == SSL_PHA_REQUESTED)
+            return WORK_FINISHED_STOP;
         return WORK_FINISHED_CONTINUE;
     }
 
@@ -3414,14 +3459,19 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst)
 
 int tls_construct_client_certificate(SSL *s, WPACKET *pkt)
 {
-    /*
-     * TODO(TLS1.3): For now we must put an empty context. Needs to be filled in
-     * later
-     */
-    if (SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR);
-        return 0;
+    if (SSL_IS_TLS13(s)) {
+        if (s->pha_context == NULL) {
+            /* no context available, add 0-length context */
+            if (!WPACKET_put_bytes_u8(pkt, 0)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+        } else if (!WPACKET_sub_memcpy_u8(pkt, s->pha_context, s->pha_context_len)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
     }
     if (!ssl3_output_cert_chain(s, pkt,
                                 (s->s3->tmp.cert_req == 2) ? NULL
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 87ce280..f57f33c 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
  *
  * Licensed under the OpenSSL license (the "License").  You may not use
@@ -43,12 +43,15 @@ int ssl3_do_write(SSL *s, int type)
         /*
          * should not be done for 'Hello Request's, but in that case we'll
          * ignore the result anyway
+         * TLS1.3 KeyUpdate and NewSessionTicket do not need to be added
          */
-        if (!ssl3_finish_mac(s,
-                             (unsigned char *)&s->init_buf->data[s->init_off],
-                             written))
-            return -1;
-
+        if (!SSL_IS_TLS13(s) || (s->statem.hand_state != TLS_ST_SW_SESSION_TICKET
+                                 && s->statem.hand_state != TLS_ST_CW_KEY_UPDATE
+                                 && s->statem.hand_state != TLS_ST_SW_KEY_UPDATE))
+            if (!ssl3_finish_mac(s,
+                                 (unsigned char *)&s->init_buf->data[s->init_off],
+                                 written))
+                return -1;
     if (written == s->init_num) {
         if (s->msg_callback)
             s->msg_callback(1, s->version, type, s->init_buf->data,
@@ -504,7 +507,7 @@ int tls_construct_finished(SSL *s, WPACKET *pkt)
     size_t slen;
 
     /* This is a real handshake so make sure we clean it up at the end */
-    if (!s->server)
+    if (!s->server && s->post_handshake_auth != SSL_PHA_REQUESTED)
         s->statem.cleanuphand = 1;
 
     /*
@@ -741,8 +744,14 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt)
 
 
     /* This is a real handshake so make sure we clean it up at the end */
-    if (s->server)
-        s->statem.cleanuphand = 1;
+    if (s->server) {
+        if (s->post_handshake_auth != SSL_PHA_REQUESTED)
+            s->statem.cleanuphand = 1;
+        if (SSL_IS_TLS13(s) && !tls13_save_handshake_digest_for_pha(s)) {
+                /* SSLfatal() already called */
+                return MSG_PROCESS_ERROR;
+        }
+    }
 
     /*
      * In TLSv1.3 a Finished message signals a key change so the end of the
@@ -801,7 +810,8 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt)
      */
     if (SSL_IS_TLS13(s)) {
         if (s->server) {
-            if (!s->method->ssl3_enc->change_cipher_state(s,
+            if (s->post_handshake_auth != SSL_PHA_REQUESTED &&
+                    !s->method->ssl3_enc->change_cipher_state(s,
                     SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)) {
                 /* SSLfatal() already called */
                 return MSG_PROCESS_ERROR;
@@ -1021,6 +1031,10 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst, int clearbufs, int stop)
         s->init_num = 0;
     }
 
+    if (SSL_IS_TLS13(s) && !s->server
+            && s->post_handshake_auth == SSL_PHA_REQUESTED)
+        s->post_handshake_auth = SSL_PHA_EXT_SENT;
+
     if (s->statem.cleanuphand) {
         /* skipped if we just sent a HelloRequest */
         s->renegotiate = 0;
@@ -1237,18 +1251,24 @@ int tls_get_message_body(SSL *s, size_t *len)
         /*
          * We defer feeding in the HRR until later. We'll do it as part of
          * processing the message
+         * The TLsv1.3 handshake transcript stops at the ClientFinished
+         * message.
          */
 #define SERVER_HELLO_RANDOM_OFFSET  (SSL3_HM_HEADER_LENGTH + 2)
-        if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO
-                || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE
-                || memcmp(hrrrandom,
-                          s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET,
-                          SSL3_RANDOM_SIZE) != 0) {
-            if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data,
-                                 s->init_num + SSL3_HM_HEADER_LENGTH)) {
-                /* SSLfatal() already called */
-                *len = 0;
-                return 0;
+        /* KeyUpdate and NewSessionTicket do not need to be added */
+        if (!SSL_IS_TLS13(s) || (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET
+                                 && s->s3->tmp.message_type != SSL3_MT_KEY_UPDATE)) {
+            if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO
+                    || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE
+                    || memcmp(hrrrandom,
+                              s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET,
+                              SSL3_RANDOM_SIZE) != 0) {
+                if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data,
+                                     s->init_num + SSL3_HM_HEADER_LENGTH)) {
+                    /* SSLfatal() already called */
+                    *len = 0;
+                    return 0;
+                }
             }
         }
         if (s->msg_callback)
@@ -2208,3 +2228,54 @@ size_t construct_key_exchange_tbs(SSL *s, unsigned char **ptbs,
     *ptbs = tbs;
     return tbslen;
 }
+
+/*
+ * Saves the current handshake digest for Post-Handshake Auth,
+ * Done after ClientFinished is processed, done exactly once
+ */
+int tls13_save_handshake_digest_for_pha(SSL *s)
+{
+    if (s->pha_dgst == NULL) {
+        if (!ssl3_digest_cached_records(s, 1))
+            /* SSLfatal() already called */
+            return 0;
+
+        s->pha_dgst = EVP_MD_CTX_new();
+        if (s->pha_dgst == NULL) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA,
+                     ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        if (!EVP_MD_CTX_copy_ex(s->pha_dgst,
+                                s->s3->handshake_dgst)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA,
+                     ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/*
+ * Restores the Post-Handshake Auth handshake digest
+ * Done just before sending/processing the Cert Request
+ */
+int tls13_restore_handshake_digest_for_pha(SSL *s)
+{
+    if (s->pha_dgst == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    if (!EVP_MD_CTX_copy_ex(s->s3->handshake_dgst,
+                            s->pha_dgst)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    return 1;
+}
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index f16d3cb..af081eb 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2015-2018 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
@@ -235,6 +235,8 @@ int tls_parse_ctos_psk_kex_modes(SSL *s, PACKET *pkt, unsigned int context,
                                  X509 *x, size_t chainidx);
 int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx);
+int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context,
+                                       X509 *x, size_t chainidx);
 
 EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt,
                                           unsigned int context, X509 *x,
@@ -365,6 +367,9 @@ EXT_RETURN tls_construct_ctos_padding(SSL *s, WPACKET *pkt,
                                       size_t chainidx);
 EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
                                   X509 *x, size_t chainidx);
+EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context,
+                                                  X509 *x, size_t chainidx);
+
 int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context,
                                X509 *x, size_t chainidx);
 int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, unsigned int context,
@@ -411,3 +416,6 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx);
 
 int tls_handle_alpn(SSL *s);
+
+int tls13_save_handshake_digest_for_pha(SSL *s);
+int tls13_restore_handshake_digest_for_pha(SSL *s);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 51b6ce9..812780a 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
  * Copyright 2005 Nokia. All rights reserved.
  *
@@ -107,6 +107,13 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
          */
         if (s->early_data_state == SSL_EARLY_DATA_READING)
             break;
+
+        if (mt == SSL3_MT_CERTIFICATE
+                && s->post_handshake_auth == SSL_PHA_REQUESTED) {
+            st->hand_state = TLS_ST_SR_CERT;
+            return 1;
+        }
+
         if (mt == SSL3_MT_KEY_UPDATE) {
             st->hand_state = TLS_ST_SR_KEY_UPDATE;
             return 1;
@@ -325,16 +332,22 @@ static int send_server_key_exchange(SSL *s)
  *   1: Yes
  *   0: No
  */
-static int send_certificate_request(SSL *s)
+int send_certificate_request(SSL *s)
 {
     if (
            /* don't request cert unless asked for it: */
            s->verify_mode & SSL_VERIFY_PEER
            /*
+            * don't request if post-handshake-only unless doing
+            * post-handshake in TLSv1.3:
+            */
+           && (!SSL_IS_TLS13(s) || !(s->verify_mode & SSL_VERIFY_POST_HANDSHAKE)
+               || s->post_handshake_auth == SSL_PHA_REQUEST_PENDING)
+           /*
             * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert
-            * during re-negotiation:
+            * a second time:
             */
-           && (s->s3->tmp.finish_md_len == 0 ||
+           && (s->certreqs_sent < 1 ||
                !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE))
            /*
             * never request cert in anonymous ciphersuites (see
@@ -388,6 +401,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
             st->hand_state = TLS_ST_SW_KEY_UPDATE;
             return WRITE_TRAN_CONTINUE;
         }
+        if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+            st->hand_state = TLS_ST_SW_CERT_REQ;
+            return WRITE_TRAN_CONTINUE;
+        }
         /* Try to read from the client instead */
         return WRITE_TRAN_FINISHED;
 
@@ -423,7 +440,12 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_SW_CERT_REQ:
-        st->hand_state = TLS_ST_SW_CERT;
+        if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+            s->post_handshake_auth = SSL_PHA_REQUESTED;
+            st->hand_state = TLS_ST_OK;
+        } else {
+            st->hand_state = TLS_ST_SW_CERT;
+        }
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_SW_CERT:
@@ -450,6 +472,8 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
          * and give the application the opportunity to delay sending the
          * session ticket?
          */
+        if (s->post_handshake_auth == SSL_PHA_REQUESTED)
+            s->post_handshake_auth = SSL_PHA_EXT_RECEIVED;
         st->hand_state = TLS_ST_SW_SESSION_TICKET;
         return WRITE_TRAN_CONTINUE;
 
@@ -863,6 +887,13 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
         }
         break;
 
+    case TLS_ST_SW_CERT_REQ:
+        if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+            if (statem_flush(s) != 1)
+                return WORK_MORE_A;
+        }
+        break;
+
     case TLS_ST_SW_KEY_UPDATE:
         if (statem_flush(s) != 1)
             return WORK_MORE_A;
@@ -2702,12 +2733,30 @@ int tls_construct_server_key_exchange(SSL *s, WPACKET *pkt)
 int tls_construct_certificate_request(SSL *s, WPACKET *pkt)
 {
     if (SSL_IS_TLS13(s)) {
-        /* TODO(TLS1.3) for now send empty request context */
-        if (!WPACKET_put_bytes_u8(pkt, 0)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                     SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
-                     ERR_R_INTERNAL_ERROR);
-            return 0;
+        /* Send random context when doing post-handshake auth */
+        if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+            OPENSSL_free(s->pha_context);
+            s->pha_context_len = 32;
+            if ((s->pha_context = OPENSSL_malloc(s->pha_context_len)) == NULL
+                    || ssl_randbytes(s, s->pha_context, s->pha_context_len) <= 0
+                    || !WPACKET_sub_memcpy_u8(pkt, s->pha_context, s->pha_context_len)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
+                         ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+            /* reset the handshake hash back to just after the ClientFinished */
+            if (!tls13_restore_handshake_digest_for_pha(s)) {
+                /* SSLfatal() already called */
+                return 0;
+            }
+        } else {
+            if (!WPACKET_put_bytes_u8(pkt, 0)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
+                         ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
         }
 
         if (!tls_construct_extensions(s, pkt,
@@ -2748,6 +2797,7 @@ int tls_construct_certificate_request(SSL *s, WPACKET *pkt)
     }
 
  done:
+    s->certreqs_sent++;
     s->s3->tmp.cert_request = 1;
     return 1;
 }
@@ -3396,11 +3446,12 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
     int i;
     MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
     X509 *x = NULL;
-    unsigned long l, llen;
+    unsigned long l;
     const unsigned char *certstart, *certbytes;
     STACK_OF(X509) *sk = NULL;
     PACKET spkt, context;
     size_t chainidx;
+    SSL_SESSION *new_sess = NULL;
 
     if ((sk = sk_X509_new_null()) == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
@@ -3408,10 +3459,16 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
         goto err;
     }
 
-    /* TODO(TLS1.3): For now we ignore the context. We need to verify this */
-    if ((SSL_IS_TLS13(s) && !PACKET_get_length_prefixed_1(pkt, &context))
-            || !PACKET_get_net_3(pkt, &llen)
-            || !PACKET_get_sub_packet(pkt, &spkt, llen)
+    if (SSL_IS_TLS13(s) && (!PACKET_get_length_prefixed_1(pkt, &context)
+                            || (s->pha_context == NULL && PACKET_remaining(&context) != 0)
+                            || (s->pha_context != NULL &&
+                                !PACKET_equal(&context, s->pha_context, s->pha_context_len)))) {
+        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
+                 SSL_R_INVALID_CONTEXT);
+        goto err;
+    }
+
+    if (!PACKET_get_length_prefixed_3(pkt, &spkt)
             || PACKET_remaining(pkt) != 0) {
         SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
                  SSL_R_LENGTH_MISMATCH);
@@ -3516,6 +3573,35 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
         }
     }
 
+    /*
+     * Sessions must be immutable once they go into the session cache. Otherwise
+     * we can get multi-thread problems. Therefore we don't "update" sessions,
+     * we replace them with a duplicate. Here, we need to do this every time
+     * a new certificate is received via post-handshake authentication, as the
+     * session may have already gone into the session cache.
+     */
+
+    if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+        int m = s->session_ctx->session_cache_mode;
+
+        if ((new_sess = ssl_session_dup(s->session, 0)) == 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
+                     ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+
+        if (m & SSL_SESS_CACHE_SERVER) {
+            /*
+             * Remove the old session from the cache. We carry on if this fails
+             */
+            SSL_CTX_remove_session(s->session_ctx, s->session);
+        }
+
+        SSL_SESSION_free(s->session);
+        s->session = new_sess;
+    }
+
     X509_free(s->session->peer);
     s->session->peer = sk_X509_shift(sk);
     s->session->verify_result = s->verify_result;
@@ -3523,6 +3609,9 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
     sk_X509_pop_free(s->session->peer_chain, X509_free);
     s->session->peer_chain = sk;
 
+    if (new_sess != NULL)
+        ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
+
     /*
      * Freeze the handshake buffer. For <TLS1.3 we do this after the CKE
      * message
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index b998765..fa975be 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -481,7 +481,8 @@ static const ssl_trace_tbl ssl_exts_tbl[] = {
     {TLSEXT_TYPE_padding, "padding"},
     {TLSEXT_TYPE_encrypt_then_mac, "encrypt_then_mac"},
     {TLSEXT_TYPE_extended_master_secret, "extended_master_secret"},
-    {TLSEXT_TYPE_early_data, "early_data"}
+    {TLSEXT_TYPE_early_data, "early_data"},
+    {TLSEXT_TYPE_post_handshake_auth, "post_handshake_auth"}
 };
 
 static const ssl_trace_tbl ssl_groups_tbl[] = {
diff --git a/test/build.info b/test/build.info
index c3c7f1e..7811f04 100644
--- a/test/build.info
+++ b/test/build.info
@@ -268,7 +268,7 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
   DEPEND[cipherlist_test]=../libcrypto ../libssl libtestutil.a
 
   INCLUDE[ssl_test_ctx.o]=../include
-  INCLUDE[handshake_helper.o]=../include
+  INCLUDE[handshake_helper.o]=.. ../include
   INCLUDE[ssltestlib.o]=.. ../include
 
   SOURCE[x509aux]=x509aux.c
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index 0add973..48392a4 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -16,6 +16,7 @@
 #include <openssl/srp.h>
 #endif
 
+#include "../ssl/ssl_locl.h"
 #include "internal/sockets.h"
 #include "internal/nelem.h"
 #include "handshake_helper.h"
@@ -674,6 +675,8 @@ static void configure_handshake_ssl(SSL *server, SSL *client,
     if (extra->client.servername != SSL_TEST_SERVERNAME_NONE)
         SSL_set_tlsext_host_name(client,
                                  ssl_servername_name(extra->client.servername));
+    if (extra->client.force_pha)
+        SSL_force_post_handshake_auth(client);
 }
 
 /* The status for each connection phase. */
@@ -848,7 +851,9 @@ static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
                           || test_ctx->handshake_mode
                               == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER
                           || test_ctx->handshake_mode
-                              == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT)) {
+                              == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT
+                          || test_ctx->handshake_mode
+                              == SSL_TEST_HANDSHAKE_POST_HANDSHAKE_AUTH)) {
         peer->status = PEER_TEST_FAILURE;
         return;
     }
@@ -929,6 +934,25 @@ static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
         if (peer->status != PEER_SUCCESS)
             peer->status = PEER_ERROR;
         return;
+    } else if (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_POST_HANDSHAKE_AUTH) {
+        if (SSL_is_server(peer->ssl)) {
+            /* Make the server believe it's received the extension */
+            if (test_ctx->extra.server.force_pha)
+                peer->ssl->post_handshake_auth = SSL_PHA_EXT_RECEIVED;
+            ret = SSL_verify_client_post_handshake(peer->ssl);
+            if (!ret) {
+                peer->status = PEER_ERROR;
+                return;
+            }
+        }
+        do_handshake_step(peer);
+        /*
+         * This is a one step handshake. We shouldn't get anything other than
+         * PEER_SUCCESS
+         */
+        if (peer->status != PEER_SUCCESS)
+            peer->status = PEER_ERROR;
+        return;
     }
 
     /*
@@ -1004,25 +1028,41 @@ typedef enum {
     CONNECTION_DONE
 } connect_phase_t;
 
+
+static int renegotiate_op(const SSL_TEST_CTX *test_ctx)
+{
+    switch (test_ctx->handshake_mode) {
+    case SSL_TEST_HANDSHAKE_RENEG_SERVER:
+    case SSL_TEST_HANDSHAKE_RENEG_CLIENT:
+        return 1;
+    default:
+        return 0;
+    }
+}
+static int post_handshake_op(const SSL_TEST_CTX *test_ctx)
+{
+    switch (test_ctx->handshake_mode) {
+    case SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT:
+    case SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER:
+    case SSL_TEST_HANDSHAKE_POST_HANDSHAKE_AUTH:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
 static connect_phase_t next_phase(const SSL_TEST_CTX *test_ctx,
                                   connect_phase_t phase)
 {
     switch (phase) {
     case HANDSHAKE:
-        if (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_SERVER
-                || test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_CLIENT
-                || test_ctx->handshake_mode
-                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT
-                || test_ctx->handshake_mode
-                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER)
+        if (renegotiate_op(test_ctx) || post_handshake_op(test_ctx))
             return RENEG_APPLICATION_DATA;
         return APPLICATION_DATA;
     case RENEG_APPLICATION_DATA:
         return RENEG_SETUP;
     case RENEG_SETUP:
-        if (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER
-                || test_ctx->handshake_mode
-                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT)
+        if (post_handshake_op(test_ctx))
             return APPLICATION_DATA;
         return RENEG_HANDSHAKE;
     case RENEG_HANDSHAKE:
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
index fc70b4f..4190ab5 100644
--- a/test/recipes/70-test_tls13messages.t
+++ b/test/recipes/70-test_tls13messages.t
@@ -89,6 +89,8 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
         checkhandshake::DEFAULT_EXTENSIONS],
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_POST_HANDSHAKE_AUTH,
+        checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION],
 
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
         checkhandshake::DEFAULT_EXTENSIONS],
@@ -123,6 +125,8 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
         checkhandshake::DEFAULT_EXTENSIONS],
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_POST_HANDSHAKE_AUTH,
+        checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION],
 
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
         checkhandshake::DEFAULT_EXTENSIONS],
@@ -214,7 +218,8 @@ $proxy->clientflags("-cert ".srctop_file("apps", "server.pem"));
 $proxy->serverflags("-Verify 5");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
-               checkhandshake::DEFAULT_EXTENSIONS,
+               checkhandshake::DEFAULT_EXTENSIONS |
+               checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION,
                "Client auth handshake test");
 
 #Test 7: Server name handshake (no client request)
diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t
index d1388d9..be03388 100644
--- a/test/recipes/80-test_ssl_new.t
+++ b/test/recipes/80-test_ssl_new.t
@@ -28,7 +28,7 @@ map { s/\^// } @conf_files if $^O eq "VMS";
 
 # We hard-code the number of tests to double-check that the globbing above
 # finds all files as expected.
-plan tests => 25;  # = scalar @conf_srcs
+plan tests => 26;  # = scalar @conf_srcs
 
 # Some test results depend on the configuration of enabled protocols. We only
 # verify generated sources in the default configuration.
@@ -96,6 +96,7 @@ my %skip = (
                     && disabled("tls1_2")) || disabled("srp"),
   "24-padding.conf" => disabled("tls1_3"),
   "25-cipher.conf" => disabled("ec") || disabled("tls1_2"),
+  "26-tls13_client_auth.conf" => disabled("tls1_3"),
 );
 
 foreach my $conf (@conf_files) {
diff --git a/test/ssl-tests/26-tls13_client_auth.conf b/test/ssl-tests/26-tls13_client_auth.conf
new file mode 100644
index 0000000..f769b12
--- /dev/null
+++ b/test/ssl-tests/26-tls13_client_auth.conf
@@ -0,0 +1,476 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 14
+
+test-0 = 0-server-auth-TLSv1.3
+test-1 = 1-client-auth-TLSv1.3-request
+test-2 = 2-client-auth-TLSv1.3-require-fail
+test-3 = 3-client-auth-TLSv1.3-require
+test-4 = 4-client-auth-TLSv1.3-require-non-empty-names
+test-5 = 5-client-auth-TLSv1.3-noroot
+test-6 = 6-client-auth-TLSv1.3-request-post-handshake
+test-7 = 7-client-auth-TLSv1.3-require-fail-post-handshake
+test-8 = 8-client-auth-TLSv1.3-require-post-handshake
+test-9 = 9-client-auth-TLSv1.3-require-non-empty-names-post-handshake
+test-10 = 10-client-auth-TLSv1.3-noroot-post-handshake
+test-11 = 11-client-auth-TLSv1.3-request-force-client-post-handshake
+test-12 = 12-client-auth-TLSv1.3-request-force-server-post-handshake
+test-13 = 13-client-auth-TLSv1.3-request-force-both-post-handshake
+# ===========================================================
+
+[0-server-auth-TLSv1.3]
+ssl_conf = 0-server-auth-TLSv1.3-ssl
+
+[0-server-auth-TLSv1.3-ssl]
+server = 0-server-auth-TLSv1.3-server
+client = 0-server-auth-TLSv1.3-client
+
+[0-server-auth-TLSv1.3-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-server-auth-TLSv1.3-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[1-client-auth-TLSv1.3-request]
+ssl_conf = 1-client-auth-TLSv1.3-request-ssl
+
+[1-client-auth-TLSv1.3-request-ssl]
+server = 1-client-auth-TLSv1.3-request-server
+client = 1-client-auth-TLSv1.3-request-client
+
+[1-client-auth-TLSv1.3-request-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = Request
+
+[1-client-auth-TLSv1.3-request-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[2-client-auth-TLSv1.3-require-fail]
+ssl_conf = 2-client-auth-TLSv1.3-require-fail-ssl
+
+[2-client-auth-TLSv1.3-require-fail-ssl]
+server = 2-client-auth-TLSv1.3-require-fail-server
+client = 2-client-auth-TLSv1.3-require-fail-client
+
+[2-client-auth-TLSv1.3-require-fail-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Require
+
+[2-client-auth-TLSv1.3-require-fail-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-2]
+ExpectedResult = ServerFail
+ExpectedServerAlert = HandshakeFailure
+
+
+# ===========================================================
+
+[3-client-auth-TLSv1.3-require]
+ssl_conf = 3-client-auth-TLSv1.3-require-ssl
+
+[3-client-auth-TLSv1.3-require-ssl]
+server = 3-client-auth-TLSv1.3-require-server
+client = 3-client-auth-TLSv1.3-require-client
+
+[3-client-auth-TLSv1.3-require-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+ClientSignatureAlgorithms = PSS+SHA256
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[3-client-auth-TLSv1.3-require-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-3]
+ExpectedClientCANames = empty
+ExpectedClientCertType = RSA
+ExpectedClientSignHash = SHA256
+ExpectedClientSignType = RSA-PSS
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[4-client-auth-TLSv1.3-require-non-empty-names]
+ssl_conf = 4-client-auth-TLSv1.3-require-non-empty-names-ssl
+
+[4-client-auth-TLSv1.3-require-non-empty-names-ssl]
+server = 4-client-auth-TLSv1.3-require-non-empty-names-server
+client = 4-client-auth-TLSv1.3-require-non-empty-names-client
+
+[4-client-auth-TLSv1.3-require-non-empty-names-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+ClientCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+ClientSignatureAlgorithms = PSS+SHA256
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[4-client-auth-TLSv1.3-require-non-empty-names-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-4]
+ExpectedClientCANames = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+ExpectedClientCertType = RSA
+ExpectedClientSignHash = SHA256
+ExpectedClientSignType = RSA-PSS
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[5-client-auth-TLSv1.3-noroot]
+ssl_conf = 5-client-auth-TLSv1.3-noroot-ssl
+
+[5-client-auth-TLSv1.3-noroot-ssl]
+server = 5-client-auth-TLSv1.3-noroot-server
+client = 5-client-auth-TLSv1.3-noroot-client
+
+[5-client-auth-TLSv1.3-noroot-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = Require
+
+[5-client-auth-TLSv1.3-noroot-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-5]
+ExpectedResult = ServerFail
+ExpectedServerAlert = UnknownCA
+
+
+# ===========================================================
+
+[6-client-auth-TLSv1.3-request-post-handshake]
+ssl_conf = 6-client-auth-TLSv1.3-request-post-handshake-ssl
+
+[6-client-auth-TLSv1.3-request-post-handshake-ssl]
+server = 6-client-auth-TLSv1.3-request-post-handshake-server
+client = 6-client-auth-TLSv1.3-request-post-handshake-client
+
+[6-client-auth-TLSv1.3-request-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = RequestPostHandshake
+
+[6-client-auth-TLSv1.3-request-post-handshake-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-6]
+ExpectedResult = ServerFail
+HandshakeMode = PostHandshakeAuth
+
+
+# ===========================================================
+
+[7-client-auth-TLSv1.3-require-fail-post-handshake]
+ssl_conf = 7-client-auth-TLSv1.3-require-fail-post-handshake-ssl
+
+[7-client-auth-TLSv1.3-require-fail-post-handshake-ssl]
+server = 7-client-auth-TLSv1.3-require-fail-post-handshake-server
+client = 7-client-auth-TLSv1.3-require-fail-post-handshake-client
+
+[7-client-auth-TLSv1.3-require-fail-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = RequirePostHandshake
+
+[7-client-auth-TLSv1.3-require-fail-post-handshake-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-7]
+ExpectedResult = ServerFail
+HandshakeMode = PostHandshakeAuth
+
+
+# ===========================================================
+
+[8-client-auth-TLSv1.3-require-post-handshake]
+ssl_conf = 8-client-auth-TLSv1.3-require-post-handshake-ssl
+
+[8-client-auth-TLSv1.3-require-post-handshake-ssl]
+server = 8-client-auth-TLSv1.3-require-post-handshake-server
+client = 8-client-auth-TLSv1.3-require-post-handshake-client
+
+[8-client-auth-TLSv1.3-require-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+ClientSignatureAlgorithms = PSS+SHA256
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = RequestPostHandshake
+
+[8-client-auth-TLSv1.3-require-post-handshake-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-8]
+ExpectedClientCANames = empty
+ExpectedClientCertType = RSA
+ExpectedClientSignHash = SHA256
+ExpectedClientSignType = RSA-PSS
+ExpectedResult = Success
+HandshakeMode = PostHandshakeAuth
+
+
+# ===========================================================
+
+[9-client-auth-TLSv1.3-require-non-empty-names-post-handshake]
+ssl_conf = 9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-ssl
+
+[9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-ssl]
+server = 9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-server
+client = 9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-client
+
+[9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+ClientCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+ClientSignatureAlgorithms = PSS+SHA256
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = RequestPostHandshake
+
+[9-client-auth-TLSv1.3-require-non-empty-names-post-handshake-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-9]
+ExpectedClientCANames = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+ExpectedClientCertType = RSA
+ExpectedClientSignHash = SHA256
+ExpectedClientSignType = RSA-PSS
+ExpectedResult = Success
+HandshakeMode = PostHandshakeAuth
+
+
+# ===========================================================
+
+[10-client-auth-TLSv1.3-noroot-post-handshake]
+ssl_conf = 10-client-auth-TLSv1.3-noroot-post-handshake-ssl
+
+[10-client-auth-TLSv1.3-noroot-post-handshake-ssl]
+server = 10-client-auth-TLSv1.3-noroot-post-handshake-server
+client = 10-client-auth-TLSv1.3-noroot-post-handshake-client
+
+[10-client-auth-TLSv1.3-noroot-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = RequirePostHandshake
+
+[10-client-auth-TLSv1.3-noroot-post-handshake-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-10]
+ExpectedResult = ServerFail
+ExpectedServerAlert = UnknownCA
+HandshakeMode = PostHandshakeAuth
+
+
+# ===========================================================
+
+[11-client-auth-TLSv1.3-request-force-client-post-handshake]
+ssl_conf = 11-client-auth-TLSv1.3-request-force-client-post-handshake-ssl
+
+[11-client-auth-TLSv1.3-request-force-client-post-handshake-ssl]
+server = 11-client-auth-TLSv1.3-request-force-client-post-handshake-server
+client = 11-client-auth-TLSv1.3-request-force-client-post-handshake-client
+
+[11-client-auth-TLSv1.3-request-force-client-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = RequestPostHandshake
+
+[11-client-auth-TLSv1.3-request-force-client-post-handshake-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-11]
+ExpectedResult = Success
+HandshakeMode = PostHandshakeAuth
+client = 11-client-auth-TLSv1.3-request-force-client-post-handshake-client-extra
+
+[11-client-auth-TLSv1.3-request-force-client-post-handshake-client-extra]
+ForcePHA = Yes
+
+
+# ===========================================================
+
+[12-client-auth-TLSv1.3-request-force-server-post-handshake]
+ssl_conf = 12-client-auth-TLSv1.3-request-force-server-post-handshake-ssl
+
+[12-client-auth-TLSv1.3-request-force-server-post-handshake-ssl]
+server = 12-client-auth-TLSv1.3-request-force-server-post-handshake-server
+client = 12-client-auth-TLSv1.3-request-force-server-post-handshake-client
+
+[12-client-auth-TLSv1.3-request-force-server-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = RequestPostHandshake
+
+[12-client-auth-TLSv1.3-request-force-server-post-handshake-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-12]
+ExpectedResult = ClientFail
+HandshakeMode = PostHandshakeAuth
+server = 12-client-auth-TLSv1.3-request-force-server-post-handshake-server-extra
+
+[12-client-auth-TLSv1.3-request-force-server-post-handshake-server-extra]
+ForcePHA = Yes
+
+
+# ===========================================================
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake]
+ssl_conf = 13-client-auth-TLSv1.3-request-force-both-post-handshake-ssl
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake-ssl]
+server = 13-client-auth-TLSv1.3-request-force-both-post-handshake-server
+client = 13-client-auth-TLSv1.3-request-force-both-post-handshake-client
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyMode = RequestPostHandshake
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+MinProtocol = TLSv1.3
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-13]
+ExpectedResult = Success
+HandshakeMode = PostHandshakeAuth
+server = 13-client-auth-TLSv1.3-request-force-both-post-handshake-server-extra
+client = 13-client-auth-TLSv1.3-request-force-both-post-handshake-client-extra
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake-server-extra]
+ForcePHA = Yes
+
+[13-client-auth-TLSv1.3-request-force-both-post-handshake-client-extra]
+ForcePHA = Yes
+
+
diff --git a/test/ssl-tests/26-tls13_client_auth.conf.in b/test/ssl-tests/26-tls13_client_auth.conf.in
new file mode 100644
index 0000000..6da4168
--- /dev/null
+++ b/test/ssl-tests/26-tls13_client_auth.conf.in
@@ -0,0 +1,293 @@
+# -*- mode: perl; -*-
+# Copyright 2018 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
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+## Test TLSv1.3 certificate authentication
+## Similar to 04-client_auth.conf.in output, but specific for
+## TLSv1.3 and post-handshake authentication
+
+use strict;
+use warnings;
+
+package ssltests;
+use OpenSSL::Test::Utils;
+
+our @tests = (
+    {
+        name => "server-auth-TLSv1.3",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "Success",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-request",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "Success",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require-fail",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Require",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "ServerFail",
+            "ExpectedServerAlert" => "HandshakeFailure",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "ClientSignatureAlgorithms" => "PSS+SHA256",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "ExpectedClientCertType" => "RSA",
+            "ExpectedClientSignType" => "RSA-PSS",
+            "ExpectedClientSignHash" => "SHA256",
+            "ExpectedClientCANames" => "empty"
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require-non-empty-names",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "ClientSignatureAlgorithms" => "PSS+SHA256",
+            "ClientCAFile" => test_pem("root-cert.pem"),
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "ExpectedClientCertType" => "RSA",
+            "ExpectedClientSignType" => "RSA-PSS",
+            "ExpectedClientSignHash" => "SHA256",
+            "ExpectedClientCANames" => test_pem("root-cert.pem"),
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-noroot",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "Require",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "ServerFail",
+            "ExpectedServerAlert" => "UnknownCA",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-request-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "RequestPostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "ServerFail",
+            "HandshakeMode" => "PostHandshakeAuth",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require-fail-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "RequirePostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "ServerFail",
+            "HandshakeMode" => "PostHandshakeAuth",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "ClientSignatureAlgorithms" => "PSS+SHA256",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "RequestPostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "HandshakeMode" => "PostHandshakeAuth",
+            "ExpectedClientCertType" => "RSA",
+            "ExpectedClientSignType" => "RSA-PSS",
+            "ExpectedClientSignHash" => "SHA256",
+            "ExpectedClientCANames" => "empty"
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-require-non-empty-names-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "ClientSignatureAlgorithms" => "PSS+SHA256",
+            "ClientCAFile" => test_pem("root-cert.pem"),
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "RequestPostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "HandshakeMode" => "PostHandshakeAuth",
+            "ExpectedClientCertType" => "RSA",
+            "ExpectedClientSignType" => "RSA-PSS",
+            "ExpectedClientSignHash" => "SHA256",
+            "ExpectedClientCANames" => test_pem("root-cert.pem"),
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-noroot-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "RequirePostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey" => test_pem("ee-key.pem"),
+        },
+        test => {
+            "ExpectedResult" => "ServerFail",
+            "HandshakeMode" => "PostHandshakeAuth",
+            "ExpectedServerAlert" => "UnknownCA",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-request-force-client-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "RequestPostHandshake",
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+	    extra => {
+		"ForcePHA" => "Yes",
+	    },
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "HandshakeMode" => "PostHandshakeAuth",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-request-force-server-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "RequestPostHandshake",
+	    extra => {
+		"ForcePHA" => "Yes",
+	    },
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+        },
+        test => {
+            "ExpectedResult" => "ClientFail",
+            "HandshakeMode" => "PostHandshakeAuth",
+        },
+    },
+    {
+        name => "client-auth-TLSv1.3-request-force-both-post-handshake",
+        server => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+            "VerifyMode" => "RequestPostHandshake",
+	    extra => {
+		"ForcePHA" => "Yes",
+	    },
+        },
+        client => {
+            "MinProtocol" => "TLSv1.3",
+            "MaxProtocol" => "TLSv1.3",
+	    extra => {
+		"ForcePHA" => "Yes",
+	    },
+        },
+        test => {
+            "ExpectedResult" => "Success",
+            "HandshakeMode" => "PostHandshakeAuth",
+        },
+    },
+);
diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c
index 897b31e..1f7db2e 100644
--- a/test/ssl_test_ctx.c
+++ b/test/ssl_test_ctx.c
@@ -369,6 +369,7 @@ static const test_enum ssl_handshake_modes[] = {
     {"RenegotiateClient", SSL_TEST_HANDSHAKE_RENEG_CLIENT},
     {"KeyUpdateServer", SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER},
     {"KeyUpdateClient", SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT},
+    {"PostHandshakeAuth", SSL_TEST_HANDSHAKE_POST_HANDSHAKE_AUTH},
 };
 
 __owur static int parse_handshake_mode(SSL_TEST_CTX *test_ctx, const char *value)
@@ -622,6 +623,11 @@ __owur static int parse_expected_client_ca_names(SSL_TEST_CTX *test_ctx,
 
 IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CTX, test, expected_cipher)
 
+/* Client and Server ForcePHA */
+
+IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CLIENT_CONF, client, force_pha)
+IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_SERVER_CONF, server, force_pha)
+
 /* Known test options and their corresponding parse methods. */
 
 /* Top-level options. */
@@ -676,6 +682,7 @@ static const ssl_test_client_option ssl_test_client_options[] = {
     { "SRPUser", &parse_client_srp_user },
     { "SRPPassword", &parse_client_srp_password },
     { "MaxFragmentLenExt", &parse_max_fragment_len_mode },
+    { "ForcePHA", &parse_client_force_pha },
 };
 
 /* Nested server options. */
@@ -692,6 +699,7 @@ static const ssl_test_server_option ssl_test_server_options[] = {
     { "CertStatus", &parse_certstatus },
     { "SRPUser", &parse_server_srp_user },
     { "SRPPassword", &parse_server_srp_password },
+    { "ForcePHA", &parse_server_force_pha },
 };
 
 SSL_TEST_CTX *SSL_TEST_CTX_new()
diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h
index 2d7b0c2..0ec87ce 100644
--- a/test/ssl_test_ctx.h
+++ b/test/ssl_test_ctx.h
@@ -73,7 +73,8 @@ typedef enum {
     SSL_TEST_HANDSHAKE_RENEG_SERVER,
     SSL_TEST_HANDSHAKE_RENEG_CLIENT,
     SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER,
-    SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT
+    SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT,
+    SSL_TEST_HANDSHAKE_POST_HANDSHAKE_AUTH
 } ssl_handshake_mode_t;
 
 typedef enum {
@@ -107,6 +108,8 @@ typedef struct {
     char *reneg_ciphers;
     char *srp_user;
     char *srp_password;
+    /* Forced PHA */
+    int force_pha;
 } SSL_TEST_CLIENT_CONF;
 
 typedef struct {
@@ -122,6 +125,8 @@ typedef struct {
     /* An SRP user known to the server. */
     char *srp_user;
     char *srp_password;
+    /* Forced PHA */
+    int force_pha;
 } SSL_TEST_SERVER_CONF;
 
 typedef struct {
diff --git a/test/sslapitest.c b/test/sslapitest.c
index a338578..b655fe7 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -3306,6 +3306,65 @@ end:
     return testresult;
 }
 
+#ifndef OPENSSL_NO_TLS1_3
+static int test_pha_key_update(void)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(),
+                                       TLS_client_method(),
+                                       &sctx, &cctx, cert, privkey)))
+        return 0;
+
+    if (!TEST_true(SSL_CTX_set_min_proto_version(sctx, TLS1_3_VERSION))
+        || !TEST_true(SSL_CTX_set_max_proto_version(sctx, TLS1_3_VERSION))
+        || !TEST_true(SSL_CTX_set_min_proto_version(cctx, TLS1_3_VERSION))
+        || !TEST_true(SSL_CTX_set_max_proto_version(cctx, TLS1_3_VERSION)))
+        goto end;
+
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                      NULL, NULL)))
+        goto end;
+
+    SSL_force_post_handshake_auth(clientssl);
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                                         SSL_ERROR_NONE)))
+        goto end;
+
+    SSL_set_verify(serverssl, SSL_VERIFY_PEER, NULL);
+    if (!TEST_true(SSL_verify_client_post_handshake(serverssl)))
+        goto end;
+
+    if (!TEST_true(SSL_key_update(clientssl, SSL_KEY_UPDATE_NOT_REQUESTED)))
+        goto end;
+
+    /* Start handshake on the server */
+    if (!TEST_int_eq(SSL_do_handshake(serverssl), 1))
+        goto end;
+
+    /* Starts with SSL_connect(), but it's really just SSL_do_handshake() */
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                                         SSL_ERROR_NONE)))
+        goto end;
+
+    SSL_shutdown(clientssl);
+    SSL_shutdown(serverssl);
+
+    testresult = 1;
+
+ end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    return testresult;
+}
+#endif
+
 int setup_tests(void)
 {
     if (!TEST_ptr(cert = test_get_argument(0))
@@ -3352,6 +3411,7 @@ int setup_tests(void)
     ADD_TEST(test_tls13_psk);
     ADD_ALL_TESTS(test_custom_exts, 5);
     ADD_TEST(test_stateless);
+    ADD_TEST(test_pha_key_update);
 #else
     ADD_ALL_TESTS(test_custom_exts, 3);
 #endif
diff --git a/util/libssl.num b/util/libssl.num
index abaa5bf..866ff53 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -474,3 +474,5 @@ SSL_CTX_set_tlsext_max_fragment_length  474	1_1_1	EXIST::FUNCTION:
 SSL_set_tlsext_max_fragment_length      475	1_1_1	EXIST::FUNCTION:
 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:
diff --git a/util/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm
index eea272f..0311490 100644
--- a/util/perl/TLSProxy/Message.pm
+++ b/util/perl/TLSProxy/Message.pm
@@ -79,6 +79,7 @@ use constant {
     EXT_SUPPORTED_VERSIONS => 43,
     EXT_COOKIE => 44,
     EXT_PSK_KEX_MODES => 45,
+    EXT_POST_HANDSHAKE_AUTH => 49,
     EXT_SIG_ALGS_CERT => 50,
     EXT_RENEGOTIATE => 65281,
     EXT_NPN => 13172,
diff --git a/util/perl/checkhandshake.pm b/util/perl/checkhandshake.pm
index e1667d5..2f4a296 100644
--- a/util/perl/checkhandshake.pm
+++ b/util/perl/checkhandshake.pm
@@ -53,7 +53,8 @@ use constant {
     KEY_SHARE_SRV_EXTENSION => 0x00020000,
     PSK_KEX_MODES_EXTENSION => 0x00040000,
     KEY_SHARE_HRR_EXTENSION => 0x00080000,
-    SUPPORTED_GROUPS_SRV_EXTENSION => 0x00100000
+    SUPPORTED_GROUPS_SRV_EXTENSION => 0x00100000,
+    POST_HANDSHAKE_AUTH_CLI_EXTENSION => 0x00200000
 };
 
 our @handmessages = ();


More information about the openssl-commits mailing list