[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Fri Feb 17 10:32:51 UTC 2017


The branch master has been updated
       via  a9998e2f67046d3f778d3c9d578ea56e183a638b (commit)
       via  4fbfe86ae3c5a829ea1a259330921bd5549223a5 (commit)
       via  9b92f161708e31de87cf8df0d58e3f99bd7d1724 (commit)
       via  398206375688f053774ab0622a59db69fb2e2b99 (commit)
       via  c2fd15f6a7fb0d7a5944c5af0c01de678b271c90 (commit)
       via  f14afcaa4227df12bc11a426a60f41005a71e95f (commit)
       via  82f992cbe0db628879aae4bf3ddd95cfcb1098a5 (commit)
       via  57389a3261075cc1266218742434aa749cf3733e (commit)
       via  5bf47933783d032fb58f438318fabdb9b9a164b4 (commit)
       via  8cdc8c5105dfa94f1b62265900a1e238f17ac562 (commit)
       via  b07b2a1b44a26909c9e89435ae417e0d30ca6951 (commit)
       via  53d1d07d303f8f482600aafe7bd983a9ee70936d (commit)
       via  9412b3ad3411a2106e87f0570e5f021af071ab8b (commit)
       via  e1c3de4450ccac6c981d0cab3c78f87220ac79fa (commit)
       via  7d8c2dfa64f1f717581a67d078d98e4c331e6d14 (commit)
       via  34df45b531c58f94c921992c5114c2d9475f73e9 (commit)
       via  44c04a2e063715abdf2db095827261456fada74a (commit)
      from  25b802bb855285bb3a799014f3669f73f4d11871 (commit)


- Log -----------------------------------------------------------------
commit a9998e2f67046d3f778d3c9d578ea56e183a638b
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 16 17:09:28 2017 +0000

    Updates following review feedback
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 4fbfe86ae3c5a829ea1a259330921bd5549223a5
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 16 17:04:40 2017 +0000

    Don't use an enum in the return type for a public API function
    
    We use an int instead. That means SSL_key_update() also should use an int.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 9b92f161708e31de87cf8df0d58e3f99bd7d1724
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 15 09:25:52 2017 +0000

    Add some KeyUpdate tests
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 398206375688f053774ab0622a59db69fb2e2b99
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Feb 14 15:25:22 2017 +0000

    Add documentation for SSL_key_update() and SSL_get_key_update_type()
    
    This also adds documentation for the pre-existing and related
    SSL_renegotiate*() functions.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit c2fd15f6a7fb0d7a5944c5af0c01de678b271c90
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Feb 14 11:48:24 2017 +0000

    Fix a shadowed global variable warning
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit f14afcaa4227df12bc11a426a60f41005a71e95f
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Feb 14 11:20:44 2017 +0000

    Updates following review feedback
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 82f992cbe0db628879aae4bf3ddd95cfcb1098a5
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Feb 13 11:55:38 2017 +0000

    Limit the number of KeyUpdate messages we can process
    
    Too many KeyUpdate message could be inicative of a problem (e.g. an
    infinite KeyUpdate loop if the peer always responds to a KeyUpdate message
    with an "update_requested" KeyUpdate response), or (conceivably) an attack.
    Either way we limit the number of KeyUpdate messages we are prepared to
    handle.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 57389a3261075cc1266218742434aa749cf3733e
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Feb 10 17:43:09 2017 +0000

    Actually update the keys when a KeyUpdate message is sent or received
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 5bf47933783d032fb58f438318fabdb9b9a164b4
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 16:00:12 2017 +0000

    If we receive an "update_requested" KeyUpdate then respond with a KeyUpdate
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 8cdc8c5105dfa94f1b62265900a1e238f17ac562
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 15:29:45 2017 +0000

    Add the ability for a server to receive a KeyUpdate message
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit b07b2a1b44a26909c9e89435ae417e0d30ca6951
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 13:33:09 2017 +0000

    Add the ability for s_client to send a KeyUpdate message
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 53d1d07d303f8f482600aafe7bd983a9ee70936d
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 15:05:46 2017 +0000

    Add a SSL_get_key_update_type() function
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 9412b3ad3411a2106e87f0570e5f021af071ab8b
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 13:12:00 2017 +0000

    Add the ability for a client to send a KeyUpdate message
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit e1c3de4450ccac6c981d0cab3c78f87220ac79fa
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 9 12:07:31 2017 +0000

    Add the ability for a client to receive a KeyUpdate message
    
    This just receives the message. It doesn't actually update any keys yet.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 7d8c2dfa64f1f717581a67d078d98e4c331e6d14
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 8 17:08:48 2017 +0000

    Add SSL_trace() support for KeyUpdate messages
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 34df45b531c58f94c921992c5114c2d9475f73e9
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 8 16:52:23 2017 +0000

    Add a capability to s_server to send KeyUpdate messages
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

commit 44c04a2e063715abdf2db095827261456fada74a
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 8 09:15:22 2017 +0000

    Provide a function to send a KeyUpdate message
    
    This implements the server side KeyUpdate sending capability as well.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2609)

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

Summary of changes:
 apps/s_client.c                      |  12 ++-
 apps/s_server.c                      |  14 +++
 doc/man3/SSL_key_update.pod          | 110 ++++++++++++++++++++++++
 doc/man7/ssl.pod                     |   4 +
 include/openssl/ssl.h                |  23 ++++-
 include/openssl/ssl3.h               |   1 +
 ssl/ssl_err.c                        |   9 ++
 ssl/ssl_lib.c                        |  47 ++++++++--
 ssl/ssl_locl.h                       |   7 ++
 ssl/statem/statem_clnt.c             |  42 +++++++--
 ssl/statem/statem_lib.c              |  57 ++++++++++++
 ssl/statem/statem_locl.h             |   6 ++
 ssl/statem/statem_srvr.c             |  47 ++++++++--
 ssl/t1_trce.c                        |  18 +++-
 ssl/tls13_enc.c                      | 162 ++++++++++++++++++++++++-----------
 test/handshake_helper.c              |  44 +++++++++-
 test/recipes/80-test_ssl_new.t       |   3 +-
 test/ssl-tests/21-key-update.conf    | 112 ++++++++++++++++++++++++
 test/ssl-tests/21-key-update.conf.in |  62 ++++++++++++++
 test/ssl_test_ctx.c                  |  21 +++++
 test/ssl_test_ctx.h                  |   6 +-
 util/libssl.num                      |   2 +
 22 files changed, 734 insertions(+), 75 deletions(-)
 create mode 100644 doc/man3/SSL_key_update.pod
 create mode 100644 test/ssl-tests/21-key-update.conf
 create mode 100644 test/ssl-tests/21-key-update.conf.in

diff --git a/apps/s_client.c b/apps/s_client.c
index 2db985d..7d6eb02 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -2291,7 +2291,8 @@ int s_client_main(int argc, char **argv)
         else
             timeoutp = NULL;
 
-        if (SSL_in_init(con) && !SSL_total_renegotiations(con)) {
+        if (SSL_in_init(con) && !SSL_total_renegotiations(con)
+                && SSL_get_key_update_type(con) == SSL_KEY_UPDATE_NONE) {
             in_init = 1;
             tty_on = 0;
         } else {
@@ -2606,6 +2607,15 @@ int s_client_main(int argc, char **argv)
                 SSL_renegotiate(con);
                 cbuf_len = 0;
             }
+
+            if (!c_ign_eof && (cbuf[0] == 'K' || cbuf[0] == 'k' )
+                    && cmdletters) {
+                BIO_printf(bio_err, "KEYUPDATE\n");
+                SSL_key_update(con,
+                               cbuf[0] == 'K' ? SSL_KEY_UPDATE_REQUESTED
+                                              : SSL_KEY_UPDATE_NOT_REQUESTED);
+                cbuf_len = 0;
+            }
 #ifndef OPENSSL_NO_HEARTBEATS
             else if ((!c_ign_eof) && (cbuf[0] == 'B' && cmdletters)) {
                 BIO_printf(bio_err, "HEARTBEATING\n");
diff --git a/apps/s_server.c b/apps/s_server.c
index e064290..dba7b67 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -2320,6 +2320,20 @@ static int sv_body(int s, int stype, unsigned char *context)
                      * cert\n");
                      */
                 }
+                if ((buf[0] == 'K' || buf[0] == 'k')
+                        && ((buf[1] == '\n') || (buf[1] == '\r'))) {
+                    SSL_key_update(con, buf[0] == 'K' ?
+                                        SSL_KEY_UPDATE_REQUESTED
+                                        : SSL_KEY_UPDATE_NOT_REQUESTED);
+                    i = SSL_do_handshake(con);
+                    printf("SSL_do_handshake -> %d\n", i);
+                    i = 0;
+                    continue;
+                    /*
+                     * strcpy(buf,"server side RE-NEGOTIATE asking for client
+                     * cert\n");
+                     */
+                }
                 if (buf[0] == 'P') {
                     static const char *str = "Lets print some clear text\n";
                     BIO_write(SSL_get_wbio(con), str, strlen(str));
diff --git a/doc/man3/SSL_key_update.pod b/doc/man3/SSL_key_update.pod
new file mode 100644
index 0000000..5b62234
--- /dev/null
+++ b/doc/man3/SSL_key_update.pod
@@ -0,0 +1,110 @@
+=pod
+
+=head1 NAME
+
+SSL_key_update,
+SSL_get_key_update_type,
+SSL_renegotiate,
+SSL_renegotiate_abbreviated,
+SSL_renegotiate_pending
+- initiate and obtain information about updating connection keys
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_key_update(SSL *s, int updatetype);
+ int SSL_get_key_update_type(SSL *s);
+
+ int SSL_renegotiate(SSL *s);
+ int SSL_renegotiate_abbreviated(SSL *s);
+ int SSL_renegotiate_pending(SSL *s);
+
+=head1 DESCRIPTION
+
+SSL_key_update() schedules an update of the keys for the current TLS connection.
+If the B<updatetype> parameter is set to B<SSL_KEY_UPDATE_NOT_REQUESTED> then
+the sending keys for this connection will be updated and the peer will be
+informed of the change. If the B<updatetype> parameter is set to
+B<SSL_KEY_UPDATE_REQUESTED> then the sending keys for this connection will be
+updated and the peer will be informed of the change along with a request for the
+peer to additionally update its sending keys. It is an error if B<updatetype> is
+set to B<SSL_KEY_UPDATE_NONE>.
+
+SSL_key_update() must only be called after the initial handshake has been
+completed and TLSv1.3 has been negotiated. The key update will not take place
+until the next time an IO operation such as SSL_read_ex() or SSL_write_ex()
+takes place on the connection. Alternatively SSL_do_handshake() can be called to
+force the update to take place immediately.
+
+SSL_get_key_update_type() can be used to determine whether a key update
+operation has been scheduled but not yet performed. The type of the pending key
+update operation will be returned if there is one, or SSL_KEY_UPDATE_NONE
+otherwise.
+
+SSL_renegotiate() and SSL_renegotiate_abbreviated() should only be called for
+connections that have negotiated TLSv1.2 or less. Calling them on any other
+connection will result in an error.
+
+When called from the client side, SSL_renegotiate() schedules a completely new
+handshake over an existing SSL/TLS connection. The next time an IO operation
+such as SSL_read_ex() or SSL_write_ex() takes place on the connection a check
+will be performed to confirm that it is a suitable time to start a
+renegotiation. If so, then it will be initiated immediately. OpenSSL will not
+attempt to resume any session associated with the connection in the new
+handshake.
+
+When called from the client side, SSL_renegotiate_abbreviated() works in the
+same was as SSL_renegotiate() except that OpenSSL will attempt to resume the
+session associated with the current connection in the new handshake.
+
+When called from the server side, SSL_renegotiate() and
+SSL_renegotiate_abbreviated() behave identically. They both schedule a request
+for a new handshake to be sent to the client. The next time an IO operation is
+performed then the same checks as on the client side are performed and then, if
+appropriate, the request is sent. The client may or may not respond with a new
+handshake and it may or may not attempt to resume an existing session. If
+a new handshake is started then this will be handled transparently by calling
+any OpenSSL IO function.
+
+If an OpenSSL client receives a renegotiation request from a server then again
+this will be handled transparently through calling any OpenSSL IO function. For
+a TLS connection the client will attempt to resume the current session in the
+new handshake. For historical reasons, DTLS clients will not attempt to resume
+the session in the new handshake.
+
+The SSL_renegotiate_pending() function returns 1 if a renegotiation or
+rengotiation request has been scheduled but not yet acted on, or 0 otherwise.
+
+=head1 RETURN VALUES
+
+SSL_key_update(), SSL_renegotiate() and SSL_renegotiate_abbreviated() return 1
+on success or 0 on error.
+
+SSL_get_key_update_type() returns the update type of the pending key update
+operation or SSL_KEY_UPDATE_NONE if there is none.
+
+SSL_renegotiate_pending() returns 1 if a renegotiation or rengotiation request
+has been scheduled but not yet acted on, or 0 otherwise.
+
+=head1 SEE ALSO
+
+L<ssl(7)>, L<SSL_read_ex(3)>,
+L<SSL_write_ex(3)>,
+L<SSL_do_handshake(3)>
+
+=head1 HISTORY
+
+The SSL_key_update() and SSL_get_key_update_type() functions were added in
+OpenSSL 1.1.1.
+
+=head1 COPYRIGHT
+
+Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/ssl.pod b/doc/man7/ssl.pod
index 3b1b581..5812bc8 100644
--- a/doc/man7/ssl.pod
+++ b/doc/man7/ssl.pod
@@ -552,6 +552,8 @@ fresh handle for each connection.
 
 =item void (*B<SSL_get_info_callback>(const SSL *ssl);)()
 
+=item int B<SSL_get_key_update_type>(SSL *s);
+
 =item STACK *B<SSL_get_peer_cert_chain>(const SSL *ssl);
 
 =item X509 *B<SSL_get_peer_certificate>(const SSL *ssl);
@@ -600,6 +602,8 @@ fresh handle for each connection.
 
 =item int B<SSL_is_init_finished>(SSL *ssl);
 
+=item int B<SSL_key_update>(SSL *s, int updatetype);
+
 =item STACK *B<SSL_load_client_CA_file>(const char *file);
 
 =item SSL *B<SSL_new>(SSL_CTX *ctx);
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 89b4514..5b8a0bb 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -825,6 +825,12 @@ DEFINE_STACK_OF(SSL_COMP)
 # define SSL_CTX_set_app_data(ctx,arg)   (SSL_CTX_set_ex_data(ctx,0,(char *)arg))
 DEPRECATEDIN_1_1_0(void SSL_set_debug(SSL *s, int debug))
 
+/* TLSv1.3 KeyUpdate message types */
+/* -1 used so that this is an invalid value for the on-the-wire protocol */
+#define SSL_KEY_UPDATE_NONE             -1
+/* Values as defined for the on-the-wire protocol */
+#define SSL_KEY_UPDATE_NOT_REQUESTED     0
+#define SSL_KEY_UPDATE_REQUESTED         1
 
 /*
  * The valid handshake states (one for each type message sent and one for each
@@ -882,7 +888,11 @@ typedef enum {
     TLS_ST_SW_CERT_VRFY,
     TLS_ST_CR_HELLO_REQ,
     TLS_ST_SW_HELLO_RETRY_REQUEST,
-    TLS_ST_CR_HELLO_RETRY_REQUEST
+    TLS_ST_CR_HELLO_RETRY_REQUEST,
+    TLS_ST_SW_KEY_UPDATE,
+    TLS_ST_CW_KEY_UPDATE,
+    TLS_ST_SR_KEY_UPDATE,
+    TLS_ST_CR_KEY_UPDATE
 } OSSL_HANDSHAKE_STATE;
 
 /*
@@ -1650,6 +1660,8 @@ __owur STACK_OF(SSL_CIPHER) *SSL_get_client_ciphers(const SSL *s);
 __owur STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s);
 
 __owur int SSL_do_handshake(SSL *s);
+int SSL_key_update(SSL *s, int updatetype);
+int SSL_get_key_update_type(SSL *s);
 int SSL_renegotiate(SSL *s);
 int SSL_renegotiate_abbreviated(SSL *s);
 __owur int SSL_renegotiate_pending(SSL *s);
@@ -2082,6 +2094,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_DANE_CTX_ENABLE                            347
 # define SSL_F_DANE_MTYPE_SET                             393
 # define SSL_F_DANE_TLSA_ADD                              394
+# define SSL_F_DERIVE_SECRET_KEY_AND_IV                   514
 # define SSL_F_DO_DTLS1_WRITE                             245
 # define SSL_F_DO_SSL3_WRITE                              104
 # define SSL_F_DTLS1_BUFFER_RECORD                        247
@@ -2195,6 +2208,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_GET_SERVER_CERT_INDEX                  322
 # define SSL_F_SSL_GET_SIGN_PKEY                          183
 # define SSL_F_SSL_INIT_WBIO_BUFFER                       184
+# define SSL_F_SSL_KEY_UPDATE                             515
 # define SSL_F_SSL_LOAD_CLIENT_CA_FILE                    185
 # define SSL_F_SSL_LOG_MASTER_SECRET                      498
 # define SSL_F_SSL_LOG_RSA_CLIENT_KEY_EXCHANGE            499
@@ -2210,6 +2224,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_PEEK_EX                                432
 # define SSL_F_SSL_READ                                   223
 # define SSL_F_SSL_READ_EX                                434
+# define SSL_F_SSL_RENEGOTIATE                            516
 # define SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT                320
 # define SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT                321
 # define SSL_F_SSL_SESSION_DUP                            348
@@ -2305,6 +2320,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_CONSTRUCT_FINISHED                     359
 # define SSL_F_TLS_CONSTRUCT_HELLO_REQUEST                373
 # define SSL_F_TLS_CONSTRUCT_HELLO_RETRY_REQUEST          510
+# define SSL_F_TLS_CONSTRUCT_KEY_UPDATE                   517
 # define SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET           428
 # define SSL_F_TLS_CONSTRUCT_NEXT_PROTO                   426
 # define SSL_F_TLS_CONSTRUCT_SERVER_CERTIFICATE           490
@@ -2361,6 +2377,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST            511
 # define SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT          442
 # define SSL_F_TLS_PROCESS_KEY_EXCHANGE                   365
+# define SSL_F_TLS_PROCESS_KEY_UPDATE                     513
 # define SSL_F_TLS_PROCESS_NEW_SESSION_TICKET             366
 # define SSL_F_TLS_PROCESS_NEXT_PROTO                     383
 # define SSL_F_TLS_PROCESS_SERVER_CERTIFICATE             367
@@ -2392,6 +2409,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_BAD_HANDSHAKE_LENGTH                       332
 # define SSL_R_BAD_HELLO_REQUEST                          105
 # define SSL_R_BAD_KEY_SHARE                              108
+# define SSL_R_BAD_KEY_UPDATE                             122
 # define SSL_R_BAD_LENGTH                                 271
 # define SSL_R_BAD_PACKET_LENGTH                          115
 # define SSL_R_BAD_PROTOCOL_VERSION_NUMBER                116
@@ -2472,6 +2490,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_INVALID_COMPRESSION_ALGORITHM              341
 # define SSL_R_INVALID_CONFIGURATION_NAME                 113
 # define SSL_R_INVALID_CT_VALIDATION_TYPE                 212
+# define SSL_R_INVALID_KEY_UPDATE_TYPE                    120
 # define SSL_R_INVALID_NULL_CMD_NAME                      385
 # define SSL_R_INVALID_SEQUENCE_NUMBER                    402
 # define SSL_R_INVALID_SERVERINFO_DATA                    388
@@ -2578,6 +2597,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH              303
 # define SSL_R_SSL_SESSION_ID_TOO_LONG                    408
 # define SSL_R_SSL_SESSION_VERSION_MISMATCH               210
+# define SSL_R_STILL_IN_INIT                              121
 # define SSL_R_TLSV1_ALERT_ACCESS_DENIED                  1049
 # define SSL_R_TLSV1_ALERT_DECODE_ERROR                   1050
 # define SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              1021
@@ -2600,6 +2620,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_TLS_HEARTBEAT_PENDING                      366
 # define SSL_R_TLS_ILLEGAL_EXPORTER_LABEL                 367
 # define SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST             157
+# define SSL_R_TOO_MANY_KEY_UPDATES                       132
 # define SSL_R_TOO_MANY_WARN_ALERTS                       409
 # define SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS             314
 # define SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS       239
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index f2f62b4..5948dfb 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -287,6 +287,7 @@ extern "C" {
 # define SSL3_MT_CLIENT_KEY_EXCHANGE             16
 # define SSL3_MT_FINISHED                        20
 # define SSL3_MT_CERTIFICATE_STATUS              22
+# define SSL3_MT_KEY_UPDATE                      24
 # ifndef OPENSSL_NO_NEXTPROTONEG
 #  define SSL3_MT_NEXT_PROTO                      67
 # endif
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index cea6040..341712c 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -28,6 +28,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_DANE_CTX_ENABLE), "dane_ctx_enable"},
     {ERR_FUNC(SSL_F_DANE_MTYPE_SET), "dane_mtype_set"},
     {ERR_FUNC(SSL_F_DANE_TLSA_ADD), "dane_tlsa_add"},
+    {ERR_FUNC(SSL_F_DERIVE_SECRET_KEY_AND_IV), "derive_secret_key_and_iv"},
     {ERR_FUNC(SSL_F_DO_DTLS1_WRITE), "do_dtls1_write"},
     {ERR_FUNC(SSL_F_DO_SSL3_WRITE), "do_ssl3_write"},
     {ERR_FUNC(SSL_F_DTLS1_BUFFER_RECORD), "dtls1_buffer_record"},
@@ -179,6 +180,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_GET_SERVER_CERT_INDEX), "ssl_get_server_cert_index"},
     {ERR_FUNC(SSL_F_SSL_GET_SIGN_PKEY), "ssl_get_sign_pkey"},
     {ERR_FUNC(SSL_F_SSL_INIT_WBIO_BUFFER), "ssl_init_wbio_buffer"},
+    {ERR_FUNC(SSL_F_SSL_KEY_UPDATE), "SSL_key_update"},
     {ERR_FUNC(SSL_F_SSL_LOAD_CLIENT_CA_FILE), "SSL_load_client_CA_file"},
     {ERR_FUNC(SSL_F_SSL_LOG_MASTER_SECRET), "ssl_log_master_secret"},
     {ERR_FUNC(SSL_F_SSL_LOG_RSA_CLIENT_KEY_EXCHANGE),
@@ -201,6 +203,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_PEEK_EX), "SSL_peek_ex"},
     {ERR_FUNC(SSL_F_SSL_READ), "SSL_read"},
     {ERR_FUNC(SSL_F_SSL_READ_EX), "SSL_read_ex"},
+    {ERR_FUNC(SSL_F_SSL_RENEGOTIATE), "SSL_renegotiate"},
     {ERR_FUNC(SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT),
      "ssl_scan_clienthello_tlsext"},
     {ERR_FUNC(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT),
@@ -333,6 +336,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
      "tls_construct_hello_request"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_HELLO_RETRY_REQUEST),
      "tls_construct_hello_retry_request"},
+    {ERR_FUNC(SSL_F_TLS_CONSTRUCT_KEY_UPDATE), "tls_construct_key_update"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET),
      "tls_construct_new_session_ticket"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_NEXT_PROTO), "tls_construct_next_proto"},
@@ -419,6 +423,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT),
      "tls_process_initial_server_flight"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_KEY_EXCHANGE), "tls_process_key_exchange"},
+    {ERR_FUNC(SSL_F_TLS_PROCESS_KEY_UPDATE), "tls_process_key_update"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_NEW_SESSION_TICKET),
      "tls_process_new_session_ticket"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_NEXT_PROTO), "tls_process_next_proto"},
@@ -461,6 +466,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_REASON(SSL_R_BAD_HANDSHAKE_LENGTH), "bad handshake length"},
     {ERR_REASON(SSL_R_BAD_HELLO_REQUEST), "bad hello request"},
     {ERR_REASON(SSL_R_BAD_KEY_SHARE), "bad key share"},
+    {ERR_REASON(SSL_R_BAD_KEY_UPDATE), "bad key update"},
     {ERR_REASON(SSL_R_BAD_LENGTH), "bad length"},
     {ERR_REASON(SSL_R_BAD_PACKET_LENGTH), "bad packet length"},
     {ERR_REASON(SSL_R_BAD_PROTOCOL_VERSION_NUMBER),
@@ -568,6 +574,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "invalid configuration name"},
     {ERR_REASON(SSL_R_INVALID_CT_VALIDATION_TYPE),
      "invalid ct validation type"},
+    {ERR_REASON(SSL_R_INVALID_KEY_UPDATE_TYPE), "invalid key update type"},
     {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME), "invalid null cmd name"},
     {ERR_REASON(SSL_R_INVALID_SEQUENCE_NUMBER), "invalid sequence number"},
     {ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA), "invalid serverinfo data"},
@@ -715,6 +722,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_REASON(SSL_R_SSL_SESSION_ID_TOO_LONG), "ssl session id too long"},
     {ERR_REASON(SSL_R_SSL_SESSION_VERSION_MISMATCH),
      "ssl session version mismatch"},
+    {ERR_REASON(SSL_R_STILL_IN_INIT), "still in init"},
     {ERR_REASON(SSL_R_TLSV1_ALERT_ACCESS_DENIED),
      "tlsv1 alert access denied"},
     {ERR_REASON(SSL_R_TLSV1_ALERT_DECODE_ERROR), "tlsv1 alert decode error"},
@@ -756,6 +764,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "tls illegal exporter label"},
     {ERR_REASON(SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST),
      "tls invalid ecpointformat list"},
+    {ERR_REASON(SSL_R_TOO_MANY_KEY_UPDATES), "too many key updates"},
     {ERR_REASON(SSL_R_TOO_MANY_WARN_ALERTS), "too many warn alerts"},
     {ERR_REASON(SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS),
      "unable to find ecdh parameters"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 1642215..48c37b8 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -471,6 +471,8 @@ int SSL_clear(SSL *s)
     clear_ciphers(s);
     s->first_packet = 0;
 
+    s->key_update = SSL_KEY_UPDATE_NONE;
+
     /* Reset DANE verification result state */
     s->dane.mdpth = -1;
     s->dane.pdpth = -1;
@@ -639,6 +641,8 @@ SSL *SSL_new(SSL_CTX *ctx)
 
     s->method = ctx->method;
 
+    s->key_update = SSL_KEY_UPDATE_NONE;
+
     if (!s->method->ssl_new(s))
         goto err;
 
@@ -1714,14 +1718,45 @@ int SSL_shutdown(SSL *s)
     }
 }
 
-int SSL_renegotiate(SSL *s)
+int SSL_key_update(SSL *s, int updatetype)
 {
     /*
-     * TODO(TLS1.3): Return an error for now. Perhaps we should do a KeyUpdate
-     * instead when we support that?
+     * TODO(TLS1.3): How will applications know whether TLSv1.3 has been
+     * negotiated, and that it is appropriate to call SSL_key_update() instead
+     * of SSL_renegotiate().
      */
-    if (SSL_IS_TLS13(s))
+    if (!SSL_IS_TLS13(s)) {
+        SSLerr(SSL_F_SSL_KEY_UPDATE, SSL_R_WRONG_SSL_VERSION);
+        return 0;
+    }
+
+    if (updatetype != SSL_KEY_UPDATE_NOT_REQUESTED
+            && updatetype != SSL_KEY_UPDATE_REQUESTED) {
+        SSLerr(SSL_F_SSL_KEY_UPDATE, SSL_R_INVALID_KEY_UPDATE_TYPE);
+        return 0;
+    }
+
+    if (!SSL_is_init_finished(s)) {
+        SSLerr(SSL_F_SSL_KEY_UPDATE, SSL_R_STILL_IN_INIT);
+        return 0;
+    }
+
+    ossl_statem_set_in_init(s, 1);
+    s->key_update = updatetype;
+    return 1;
+}
+
+int SSL_get_key_update_type(SSL *s)
+{
+    return s->key_update;
+}
+
+int SSL_renegotiate(SSL *s)
+{
+    if (SSL_IS_TLS13(s)) {
+        SSLerr(SSL_F_SSL_RENEGOTIATE, SSL_R_WRONG_SSL_VERSION);
         return 0;
+    }
 
     if (s->renegotiate == 0)
         s->renegotiate = 1;
@@ -1733,10 +1768,6 @@ int SSL_renegotiate(SSL *s)
 
 int SSL_renegotiate_abbreviated(SSL *s)
 {
-    /*
-     * TODO(TLS1.3): Return an error for now. Perhaps we should do a KeyUpdate
-     * instead when we support that?
-     */
     if (SSL_IS_TLS13(s))
         return 0;
 
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 6f83877..70a47a8 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -986,6 +986,8 @@ struct ssl_st {
     unsigned char client_finished_secret[EVP_MAX_MD_SIZE];
     unsigned char server_finished_secret[EVP_MAX_MD_SIZE];
     unsigned char server_finished_hash[EVP_MAX_MD_SIZE];
+    unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
+    unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
     EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
     unsigned char read_iv[EVP_MAX_IV_LENGTH]; /* TLSv1.3 static read IV */
     EVP_MD_CTX *read_hash;      /* used for mac generation */
@@ -994,6 +996,8 @@ struct ssl_st {
     EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
     unsigned char write_iv[EVP_MAX_IV_LENGTH]; /* TLSv1.3 static write IV */
     EVP_MD_CTX *write_hash;     /* used for mac generation */
+    /* Count of how many KeyUpdate messages we have received */
+    unsigned int key_update_count;
     /* session info */
     /* client cert? */
     /* This is used to hold the server certificate used */
@@ -1172,6 +1176,8 @@ struct ssl_st {
      * (i.e. not just sending a HelloRequest)
      */
     int renegotiate;
+    /* If sending a KeyUpdate is pending */
+    int key_update;
 # ifndef OPENSSL_NO_SRP
     /* ctx for SRP authentication */
     SRP_CTX srp_ctx;
@@ -2161,6 +2167,7 @@ __owur int tls13_setup_key_block(SSL *s);
 __owur size_t tls13_final_finish_mac(SSL *s, const char *str, size_t slen,
                                      unsigned char *p);
 __owur int tls13_change_cipher_state(SSL *s, int which);
+__owur int tls13_update_key(SSL *s, int send);
 __owur int tls13_hkdf_expand(SSL *s, const EVP_MD *md,
                              const unsigned char *secret,
                              const unsigned char *label, size_t labellen,
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 4923e24..9f2e7af 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -200,6 +200,10 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
             st->hand_state = TLS_ST_CR_SESSION_TICKET;
             return 1;
         }
+        if (mt == SSL3_MT_KEY_UPDATE) {
+            st->hand_state = TLS_ST_CR_KEY_UPDATE;
+            return 1;
+        }
         break;
     }
 
@@ -402,11 +406,6 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
     OSSL_STATEM *st = &s->statem;
 
     /*
-     * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time
-     * we will update this to look more like real TLSv1.3
-     */
-
-    /*
      * Note: There are no cases for TLS_ST_BEFORE because we haven't negotiated
      * TLSv1.3 yet at that point. They are handled by
      * ossl_statem_client_write_transition().
@@ -439,6 +438,14 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
         st->hand_state = TLS_ST_CW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
+    case TLS_ST_CR_KEY_UPDATE:
+        if (s->key_update != SSL_KEY_UPDATE_NONE) {
+            st->hand_state = TLS_ST_CW_KEY_UPDATE;
+            return WRITE_TRAN_CONTINUE;
+        }
+        /* Fall through */
+
+    case TLS_ST_CW_KEY_UPDATE:
     case TLS_ST_CR_SESSION_TICKET:
     case TLS_ST_CW_FINISHED:
         st->hand_state = TLS_ST_OK;
@@ -446,7 +453,12 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_OK:
-        /* Just go straight to trying to read from the server */
+        if (s->key_update != SSL_KEY_UPDATE_NONE) {
+            st->hand_state = TLS_ST_CW_KEY_UPDATE;
+            return WRITE_TRAN_CONTINUE;
+        }
+
+        /* Try to read from the server instead */
         return WRITE_TRAN_FINISHED;
     }
 }
@@ -719,6 +731,13 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
             return WORK_ERROR;
         }
         break;
+
+    case TLS_ST_CW_KEY_UPDATE:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        if (!tls13_update_key(s, 1))
+            return WORK_ERROR;
+        break;
     }
 
     return WORK_FINISHED_CONTINUE;
@@ -780,6 +799,11 @@ int ossl_statem_client_construct_message(SSL *s, WPACKET *pkt,
         *confunc = tls_construct_finished;
         *mt = SSL3_MT_FINISHED;
         break;
+
+    case TLS_ST_CW_KEY_UPDATE:
+        *confunc = tls_construct_key_update;
+        *mt = SSL3_MT_KEY_UPDATE;
+        break;
     }
 
     return 1;
@@ -843,6 +867,9 @@ size_t ossl_statem_client_max_message_size(SSL *s)
 
     case TLS_ST_CR_ENCRYPTED_EXTENSIONS:
         return ENCRYPTED_EXTENSIONS_MAX_LENGTH;
+
+    case TLS_ST_CR_KEY_UPDATE:
+        return KEY_UPDATE_MAX_LENGTH;
     }
 }
 
@@ -899,6 +926,9 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL *s, PACKET *pkt)
 
     case TLS_ST_CR_ENCRYPTED_EXTENSIONS:
         return tls_process_encrypted_extensions(s, pkt);
+
+    case TLS_ST_CR_KEY_UPDATE:
+        return tls_process_key_update(s, pkt);
     }
 }
 
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index ed07266..c871c00 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -495,6 +495,63 @@ int tls_construct_finished(SSL *s, WPACKET *pkt)
     return 0;
 }
 
+int tls_construct_key_update(SSL *s, WPACKET *pkt)
+{
+    if (!WPACKET_put_bytes_u8(pkt, s->key_update)) {
+        SSLerr(SSL_F_TLS_CONSTRUCT_KEY_UPDATE, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    s->key_update = SSL_KEY_UPDATE_NONE;
+    return 1;
+
+ err:
+    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return 0;
+}
+
+MSG_PROCESS_RETURN tls_process_key_update(SSL *s, PACKET *pkt)
+{
+    int al;
+    unsigned int updatetype;
+
+    s->key_update_count++;
+    if (s->key_update_count > MAX_KEY_UPDATE_MESSAGES) {
+        al = SSL_AD_ILLEGAL_PARAMETER;
+        SSLerr(SSL_F_TLS_PROCESS_KEY_UPDATE, SSL_R_TOO_MANY_KEY_UPDATES);
+        goto err;
+    }
+
+    if (!PACKET_get_1(pkt, &updatetype)
+            || PACKET_remaining(pkt) != 0
+            || (updatetype != SSL_KEY_UPDATE_NOT_REQUESTED
+                && updatetype != SSL_KEY_UPDATE_REQUESTED)) {
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_KEY_UPDATE, SSL_R_BAD_KEY_UPDATE);
+        goto err;
+    }
+
+    /*
+     * If we get a request for us to update our sending keys too then, we need
+     * to additionally send a KeyUpdate message. However that message should
+     * not also request an update (otherwise we get into an infinite loop).
+     */
+    if (updatetype == SSL_KEY_UPDATE_REQUESTED)
+        s->key_update = SSL_KEY_UPDATE_NOT_REQUESTED;
+
+    if (!tls13_update_key(s, 0)) {
+        al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_KEY_UPDATE, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    return MSG_PROCESS_FINISHED_READING;
+ err:
+    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ossl_statem_set_error(s);
+    return MSG_PROCESS_ERROR;
+}
+
 #ifndef OPENSSL_NO_NEXTPROTONEG
 /*
  * ssl3_take_mac calculates the Finished MAC for the handshakes messages seen
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index fa13a26..595a803 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -23,10 +23,14 @@
 #define ENCRYPTED_EXTENSIONS_MAX_LENGTH 20000
 #define SERVER_KEY_EXCH_MAX_LENGTH      102400
 #define SERVER_HELLO_DONE_MAX_LENGTH    0
+#define KEY_UPDATE_MAX_LENGTH           1
 #define CCS_MAX_LENGTH                  1
 /* Max should actually be 36 but we are generous */
 #define FINISHED_MAX_LENGTH             64
 
+/* The maximum number of incoming KeyUpdate messages we will accept */
+#define MAX_KEY_UPDATE_MESSAGES     32
+
 /* Extension context codes */
 /* This extension is only allowed in TLS */
 #define EXT_TLS_ONLY                        0x0001
@@ -111,6 +115,8 @@ __owur int tls_construct_change_cipher_spec(SSL *s, WPACKET *pkt);
 __owur int dtls_construct_change_cipher_spec(SSL *s, WPACKET *pkt);
 
 __owur int tls_construct_finished(SSL *s, WPACKET *pkt);
+__owur int tls_construct_key_update(SSL *s, WPACKET *pkt);
+__owur MSG_PROCESS_RETURN tls_process_key_update(SSL *s, PACKET *pkt);
 __owur WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst, int clearbufs);
 __owur WORK_STATE dtls_wait_for_dry(SSL *s);
 
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 2330bde..0037e79 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -132,6 +132,13 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
             return 1;
         }
         break;
+
+    case TLS_ST_OK:
+        if (mt == SSL3_MT_KEY_UPDATE) {
+            st->hand_state = TLS_ST_SR_KEY_UPDATE;
+            return 1;
+        }
+        break;
     }
 
     /* No valid transition found */
@@ -394,11 +401,6 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
     OSSL_STATEM *st = &s->statem;
 
     /*
-     * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time
-     * we will update this to look more like real TLSv1.3
-     */
-
-    /*
      * No case for TLS_ST_BEFORE, because at that stage we have not negotiated
      * TLSv1.3 yet, so that is handled by ossl_statem_server_write_transition()
      */
@@ -408,6 +410,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         /* Shouldn't happen */
         return WRITE_TRAN_ERROR;
 
+    case TLS_ST_OK:
+        if (s->key_update != SSL_KEY_UPDATE_NONE) {
+            st->hand_state = TLS_ST_SW_KEY_UPDATE;
+            return WRITE_TRAN_CONTINUE;
+        }
+        /* Try to read from the client instead */
+        return WRITE_TRAN_FINISHED;
+
     case TLS_ST_SR_CLNT_HELLO:
         if (s->hello_retry_request)
             st->hand_state = TLS_ST_SW_HELLO_RETRY_REQUEST;
@@ -459,6 +469,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         st->hand_state = TLS_ST_SW_SESSION_TICKET;
         return WRITE_TRAN_CONTINUE;
 
+    case TLS_ST_SR_KEY_UPDATE:
+        if (s->key_update != SSL_KEY_UPDATE_NONE) {
+            st->hand_state = TLS_ST_SW_KEY_UPDATE;
+            return WRITE_TRAN_CONTINUE;
+        }
+        /* Fall through */
+
+    case TLS_ST_SW_KEY_UPDATE:
     case TLS_ST_SW_SESSION_TICKET:
         st->hand_state = TLS_ST_OK;
         ossl_statem_set_in_init(s, 0);
@@ -822,6 +840,13 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
         }
         break;
 
+    case TLS_ST_SW_KEY_UPDATE:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        if (!tls13_update_key(s, 1))
+            return WORK_ERROR;
+        break;
+
     case TLS_ST_SW_SESSION_TICKET:
         if (SSL_IS_TLS13(s) && statem_flush(s) != 1)
             return WORK_MORE_A;
@@ -923,6 +948,11 @@ int ossl_statem_server_construct_message(SSL *s, WPACKET *pkt,
         *confunc = tls_construct_hello_retry_request;
         *mt = SSL3_MT_HELLO_RETRY_REQUEST;
         break;
+
+    case TLS_ST_SW_KEY_UPDATE:
+        *confunc = tls_construct_key_update;
+        *mt = SSL3_MT_KEY_UPDATE;
+        break;
     }
 
     return 1;
@@ -983,6 +1013,9 @@ size_t ossl_statem_server_max_message_size(SSL *s)
 
     case TLS_ST_SR_FINISHED:
         return FINISHED_MAX_LENGTH;
+
+    case TLS_ST_SR_KEY_UPDATE:
+        return KEY_UPDATE_MAX_LENGTH;
     }
 }
 
@@ -1020,6 +1053,10 @@ MSG_PROCESS_RETURN ossl_statem_server_process_message(SSL *s, PACKET *pkt)
 
     case TLS_ST_SR_FINISHED:
         return tls_process_finished(s, pkt);
+
+    case TLS_ST_SR_KEY_UPDATE:
+        return tls_process_key_update(s, pkt);
+
     }
 }
 
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index 5561e8f..6f340c9 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -94,7 +94,8 @@ static ssl_trace_tbl ssl_handshake_tbl[] = {
     {SSL3_MT_CERTIFICATE_STATUS, "CertificateStatus"},
     {SSL3_MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange"},
     {SSL3_MT_FINISHED, "Finished"},
-    {SSL3_MT_CERTIFICATE_STATUS, "CertificateStatus"}
+    {SSL3_MT_CERTIFICATE_STATUS, "CertificateStatus"},
+    {SSL3_MT_KEY_UPDATE, "KeyUpdate"}
 };
 
 /* Cipher suites */
@@ -562,6 +563,11 @@ static ssl_trace_tbl ssl_crypto_tbl[] = {
     {TLS1_RT_CRYPTO_FIXED_IV | TLS1_RT_CRYPTO_READ, "Read IV (fixed part)"}
 };
 
+static ssl_trace_tbl ssl_key_update_tbl[] = {
+    {SSL_KEY_UPDATE_NOT_REQUESTED, "update_not_requested"},
+    {SSL_KEY_UPDATE_REQUESTED, "update_requested"}
+};
+
 static void ssl_print_hex(BIO *bio, int indent, const char *name,
                           const unsigned char *msg, size_t msglen)
 {
@@ -1351,6 +1357,16 @@ static int ssl_print_handshake(BIO *bio, SSL *ssl, int server,
             return 0;
         break;
 
+    case SSL3_MT_KEY_UPDATE:
+        if (msglen != 1) {
+            ssl_print_hex(bio, indent + 2, "unexpected value", msg, msglen);
+            return 0;
+        }
+        if (!ssl_trace_list(bio, indent + 2, msg, msglen, 1,
+                            ssl_key_update_tbl))
+            return 0;
+        break;
+
     default:
         BIO_indent(bio, indent + 2, 80);
         BIO_puts(bio, "Unsupported, hex dump follows:\n");
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index ebfeecd..6faa4e0 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -242,6 +242,74 @@ int tls13_setup_key_block(SSL *s)
     return 1;
 }
 
+static int derive_secret_key_and_iv(SSL *s, int send,
+                                    const unsigned char *insecret,
+                                    const unsigned char *hash,
+                                    const unsigned char *label,
+                                    size_t labellen, unsigned char *secret,
+                                    unsigned char *iv, EVP_CIPHER_CTX *ciph_ctx)
+{
+    unsigned char key[EVP_MAX_KEY_LENGTH];
+    size_t ivlen, keylen, taglen;
+    const EVP_MD *md = ssl_handshake_md(s);
+    size_t hashlen = EVP_MD_size(md);
+    const EVP_CIPHER *ciph = s->s3->tmp.new_sym_enc;
+
+    if (!tls13_hkdf_expand(s, md, insecret, label, labellen, hash, secret,
+                           hashlen)) {
+        SSLerr(SSL_F_DERIVE_SECRET_KEY_AND_IV, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    /* TODO(size_t): convert me */
+    keylen = EVP_CIPHER_key_length(ciph);
+    if (EVP_CIPHER_mode(ciph) == EVP_CIPH_CCM_MODE) {
+        ivlen = EVP_CCM_TLS_IV_LEN;
+        if (s->s3->tmp.new_cipher->algorithm_enc
+                & (SSL_AES128CCM8 | SSL_AES256CCM8))
+            taglen = EVP_CCM8_TLS_TAG_LEN;
+         else
+            taglen = EVP_CCM_TLS_TAG_LEN;
+    } else {
+        ivlen = EVP_CIPHER_iv_length(ciph);
+        taglen = 0;
+    }
+
+    if (!tls13_derive_key(s, secret, key, keylen)
+            || !tls13_derive_iv(s, secret, iv, ivlen)) {
+        SSLerr(SSL_F_DERIVE_SECRET_KEY_AND_IV, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (EVP_CipherInit_ex(ciph_ctx, ciph, NULL, NULL, NULL, send) <= 0
+        || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, NULL)
+        || (taglen != 0 && !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_TAG,
+                                                taglen, NULL))
+        || EVP_CipherInit_ex(ciph_ctx, NULL, NULL, key, NULL, -1) <= 0) {
+        SSLerr(SSL_F_DERIVE_SECRET_KEY_AND_IV, ERR_R_EVP_LIB);
+        goto err;
+    }
+
+#ifdef OPENSSL_SSL_TRACE_CRYPTO
+    if (s->msg_callback) {
+        int wh = send ? TLS1_RT_CRYPTO_WRITE : 0;
+
+        if (ciph->key_len)
+            s->msg_callback(2, s->version, wh | TLS1_RT_CRYPTO_KEY,
+                            key, ciph->key_len, s, s->msg_callback_arg);
+
+        wh |= TLS1_RT_CRYPTO_IV;
+        s->msg_callback(2, s->version, wh, iv, ivlen, s,
+                        s->msg_callback_arg);
+    }
+#endif
+
+    return 1;
+ err:
+    OPENSSL_cleanse(key, sizeof(key));
+    return 0;
+}
+
 int tls13_change_cipher_state(SSL *s, int which)
 {
     static const unsigned char client_handshake_traffic[] =
@@ -254,7 +322,6 @@ int tls13_change_cipher_state(SSL *s, int which)
         "server application traffic secret";
     static const unsigned char resumption_master_secret[] =
         "resumption master secret";
-    unsigned char key[EVP_MAX_KEY_LENGTH];
     unsigned char *iv;
     unsigned char secret[EVP_MAX_MD_SIZE];
     unsigned char hashval[EVP_MAX_MD_SIZE];
@@ -263,8 +330,7 @@ int tls13_change_cipher_state(SSL *s, int which)
     unsigned char *finsecret = NULL;
     const char *log_label = NULL;
     EVP_CIPHER_CTX *ciph_ctx;
-    const EVP_CIPHER *ciph = s->s3->tmp.new_sym_enc;
-    size_t ivlen, keylen, taglen, finsecretlen = 0;
+    size_t finsecretlen = 0;
     const unsigned char *label;
     size_t labellen, hashlen = 0;
     int ret = 0;
@@ -350,12 +416,6 @@ int tls13_change_cipher_state(SSL *s, int which)
     if (label == server_application_traffic)
         memcpy(s->server_finished_hash, hashval, hashlen);
 
-    if (!tls13_hkdf_expand(s, ssl_handshake_md(s), insecret, label, labellen,
-                           hash, secret, hashlen)) {
-        SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
-        goto err;
-    }
-
     if (label == client_application_traffic) {
         /*
          * We also create the resumption master secret, but this time use the
@@ -371,64 +431,70 @@ int tls13_change_cipher_state(SSL *s, int which)
         s->session->master_key_length = hashlen;
     }
 
-    /* TODO(size_t): convert me */
-    keylen = EVP_CIPHER_key_length(ciph);
-    if (EVP_CIPHER_mode(ciph) == EVP_CIPH_CCM_MODE) {
-        ivlen = EVP_CCM_TLS_IV_LEN;
-        if (s->s3->tmp.new_cipher->algorithm_enc
-                & (SSL_AES128CCM8 | SSL_AES256CCM8))
-            taglen = EVP_CCM8_TLS_TAG_LEN;
-         else
-            taglen = EVP_CCM_TLS_TAG_LEN;
-    } else {
-        ivlen = EVP_CIPHER_iv_length(ciph);
-        taglen = 0;
+    if (!derive_secret_key_and_iv(s, which & SSL3_CC_WRITE, insecret, hash,
+                                  label, labellen, secret, iv, ciph_ctx)) {
+        goto err;
     }
 
+    if (label == server_application_traffic)
+        memcpy(s->server_app_traffic_secret, secret, hashlen);
+    else if (label == client_application_traffic)
+        memcpy(s->client_app_traffic_secret, secret, hashlen);
+
     if (!ssl_log_secret(s, log_label, secret, hashlen)) {
         SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
-    if (!tls13_derive_key(s, secret, key, keylen)
-            || !tls13_derive_iv(s, secret, iv, ivlen)
-            || (finsecret != NULL && !tls13_derive_finishedkey(s,
-                                                           ssl_handshake_md(s),
-                                                           secret,
-                                                           finsecret,
-                                                           finsecretlen))) {
+    if (finsecret != NULL
+            && !tls13_derive_finishedkey(s, ssl_handshake_md(s), secret,
+                                         finsecret, finsecretlen)) {
         SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
-    if (EVP_CipherInit_ex(ciph_ctx, ciph, NULL, NULL, NULL,
-                          (which & SSL3_CC_WRITE)) <= 0
-        || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, NULL)
-        || (taglen != 0 && !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_TAG,
-                                                taglen, NULL))
-        || EVP_CipherInit_ex(ciph_ctx, NULL, NULL, key, NULL, -1) <= 0) {
-        SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_EVP_LIB);
-        goto err;
-    }
+    ret = 1;
+ err:
+    OPENSSL_cleanse(secret, sizeof(secret));
+    return ret;
+}
 
-#ifdef OPENSSL_SSL_TRACE_CRYPTO
-    if (s->msg_callback) {
-        int wh = which & SSL3_CC_WRITE ? TLS1_RT_CRYPTO_WRITE : 0;
+int tls13_update_key(SSL *s, int send)
+{
+    static const unsigned char application_traffic[] =
+        "application traffic secret";
+    const EVP_MD *md = ssl_handshake_md(s);
+    size_t hashlen = EVP_MD_size(md);
+    unsigned char *insecret, *iv;
+    unsigned char secret[EVP_MAX_MD_SIZE];
+    EVP_CIPHER_CTX *ciph_ctx;
+    int ret = 0;
 
-        if (ciph->key_len)
-            s->msg_callback(2, s->version, wh | TLS1_RT_CRYPTO_KEY,
-                            key, ciph->key_len, s, s->msg_callback_arg);
+    if (s->server == send)
+        insecret = s->server_app_traffic_secret;
+    else
+        insecret = s->client_app_traffic_secret;
 
-        wh |= TLS1_RT_CRYPTO_IV;
-        s->msg_callback(2, s->version, wh, iv, ivlen, s,
-                        s->msg_callback_arg);
+    if (send) {
+        iv = s->write_iv;
+        ciph_ctx = s->enc_write_ctx;
+        RECORD_LAYER_reset_write_sequence(&s->rlayer);
+    } else {
+        iv = s->read_iv;
+        ciph_ctx = s->enc_read_ctx;
+        RECORD_LAYER_reset_read_sequence(&s->rlayer);
     }
-#endif
+
+    if (!derive_secret_key_and_iv(s, send, insecret, NULL, application_traffic,
+                                  sizeof(application_traffic) - 1, secret, iv,
+                                  ciph_ctx))
+        goto err;
+
+    memcpy(insecret, secret, hashlen);
 
     ret = 1;
  err:
     OPENSSL_cleanse(secret, sizeof(secret));
-    OPENSSL_cleanse(key, sizeof(key));
     return ret;
 }
 
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index 2b869a4..c82581c 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -590,7 +590,14 @@ static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
 
     TEST_check(peer->status == PEER_RETRY);
     TEST_check(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_RENEG_CLIENT
+                || test_ctx->handshake_mode
+                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER
+                || test_ctx->handshake_mode
+                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT);
+
+    /* Reset the count of the amount of app data we need to read/write */
+    peer->bytes_to_write = peer->bytes_to_read = test_ctx->app_data_size;
 
     /* Check if we are the peer that is going to initiate */
     if ((test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_SERVER
@@ -642,6 +649,29 @@ static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
                 peer->status = PEER_RETRY;
             return;
         }
+    } else if (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER
+               || test_ctx->handshake_mode
+                  == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT) {
+        if (SSL_is_server(peer->ssl)
+                != (test_ctx->handshake_mode
+                    == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER)) {
+            peer->status = PEER_SUCCESS;
+            return;
+        }
+
+        ret = SSL_key_update(peer->ssl, test_ctx->key_update_type);
+        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;
     }
 
     /*
@@ -663,7 +693,7 @@ static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
             peer->status = PEER_ERROR;
             return;
         }
-        /* If we're no in init yet then we're not done with setup yet */
+        /* If we're not in init yet then we're not done with setup yet */
         if (!SSL_in_init(peer->ssl))
             return;
     }
@@ -720,12 +750,20 @@ static connect_phase_t next_phase(const SSL_TEST_CTX *test_ctx,
     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_RENEG_CLIENT
+                || test_ctx->handshake_mode
+                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT
+                || test_ctx->handshake_mode
+                   == SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER)
             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)
+            return APPLICATION_DATA;
         return RENEG_HANDSHAKE;
     case RENEG_HANDSHAKE:
         return APPLICATION_DATA;
diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t
index 5c512cf..640f314 100644
--- a/test/recipes/80-test_ssl_new.t
+++ b/test/recipes/80-test_ssl_new.t
@@ -29,7 +29,7 @@ map { s/\.in// } @conf_files;
 
 # We hard-code the number of tests to double-check that the globbing above
 # finds all files as expected.
-plan tests => 20;  # = scalar @conf_srcs
+plan tests => 21;  # = scalar @conf_srcs
 
 # Some test results depend on the configuration of enabled protocols. We only
 # verify generated sources in the default configuration.
@@ -82,6 +82,7 @@ my %skip = (
   "18-dtls-renegotiate.conf" => $no_dtls,
   "19-mac-then-encrypt.conf" => $no_pre_tls1_3,
   "20-cert-select.conf" => disabled("tls1_2") || $no_ec,
+  "21-key-update.conf" => disabled("tls1_3"),
 );
 
 foreach my $conf (@conf_files) {
diff --git a/test/ssl-tests/21-key-update.conf b/test/ssl-tests/21-key-update.conf
new file mode 100644
index 0000000..b79eb44
--- /dev/null
+++ b/test/ssl-tests/21-key-update.conf
@@ -0,0 +1,112 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 4
+
+test-0 = 0-update-key-client-update-not-requested
+test-1 = 1-update-key-server-update-not-requested
+test-2 = 2-update-key-client-update-requested
+test-3 = 3-update-key-server-update-requested
+# ===========================================================
+
+[0-update-key-client-update-not-requested]
+ssl_conf = 0-update-key-client-update-not-requested-ssl
+
+[0-update-key-client-update-not-requested-ssl]
+server = 0-update-key-client-update-not-requested-server
+client = 0-update-key-client-update-not-requested-client
+
+[0-update-key-client-update-not-requested-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-update-key-client-update-not-requested-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedResult = Success
+HandshakeMode = KeyUpdateClient
+KeyUpdateType = KeyUpdateNotRequested
+ResumptionExpected = No
+
+
+# ===========================================================
+
+[1-update-key-server-update-not-requested]
+ssl_conf = 1-update-key-server-update-not-requested-ssl
+
+[1-update-key-server-update-not-requested-ssl]
+server = 1-update-key-server-update-not-requested-server
+client = 1-update-key-server-update-not-requested-client
+
+[1-update-key-server-update-not-requested-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-update-key-server-update-not-requested-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedResult = Success
+HandshakeMode = KeyUpdateServer
+KeyUpdateType = KeyUpdateNotRequested
+ResumptionExpected = No
+
+
+# ===========================================================
+
+[2-update-key-client-update-requested]
+ssl_conf = 2-update-key-client-update-requested-ssl
+
+[2-update-key-client-update-requested-ssl]
+server = 2-update-key-client-update-requested-server
+client = 2-update-key-client-update-requested-client
+
+[2-update-key-client-update-requested-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[2-update-key-client-update-requested-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-2]
+ExpectedResult = Success
+HandshakeMode = KeyUpdateClient
+KeyUpdateType = KeyUpdateRequested
+ResumptionExpected = No
+
+
+# ===========================================================
+
+[3-update-key-server-update-requested]
+ssl_conf = 3-update-key-server-update-requested-ssl
+
+[3-update-key-server-update-requested-ssl]
+server = 3-update-key-server-update-requested-server
+client = 3-update-key-server-update-requested-client
+
+[3-update-key-server-update-requested-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[3-update-key-server-update-requested-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-3]
+ExpectedResult = Success
+HandshakeMode = KeyUpdateServer
+KeyUpdateType = KeyUpdateRequested
+ResumptionExpected = No
+
+
diff --git a/test/ssl-tests/21-key-update.conf.in b/test/ssl-tests/21-key-update.conf.in
new file mode 100644
index 0000000..4bebb48
--- /dev/null
+++ b/test/ssl-tests/21-key-update.conf.in
@@ -0,0 +1,62 @@
+# -*- mode: perl; -*-
+# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+## Test KeyUpdate
+
+use strict;
+use warnings;
+
+package ssltests;
+
+our @tests = (
+    {
+        name => "update-key-client-update-not-requested",
+        server => {},
+        client => {},
+        test => {
+            "HandshakeMode" => "KeyUpdateClient",
+            "KeyUpdateType" => "KeyUpdateNotRequested",
+            "ResumptionExpected" => "No",
+            "ExpectedResult" => "Success"
+        }
+    },
+    {
+        name => "update-key-server-update-not-requested",
+        server => {},
+        client => {},
+        test => {
+            "HandshakeMode" => "KeyUpdateServer",
+            "KeyUpdateType" => "KeyUpdateNotRequested",
+            "ResumptionExpected" => "No",
+            "ExpectedResult" => "Success"
+        }
+    },
+    {
+        name => "update-key-client-update-requested",
+        server => {},
+        client => {},
+        test => {
+            "HandshakeMode" => "KeyUpdateClient",
+            "KeyUpdateType" => "KeyUpdateRequested",
+            "ResumptionExpected" => "No",
+            "ExpectedResult" => "Success"
+        }
+    },
+    {
+        name => "update-key-server-update-requested",
+        server => {},
+        client => {},
+        test => {
+            "HandshakeMode" => "KeyUpdateServer",
+            "KeyUpdateType" => "KeyUpdateRequested",
+            "ResumptionExpected" => "No",
+            "ExpectedResult" => "Success"
+        }
+    }
+);
diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c
index c5b9a3e..66fb31c 100644
--- a/test/ssl_test_ctx.c
+++ b/test/ssl_test_ctx.c
@@ -322,6 +322,8 @@ static const test_enum ssl_handshake_modes[] = {
     {"Resume", SSL_TEST_HANDSHAKE_RESUME},
     {"RenegotiateServer", SSL_TEST_HANDSHAKE_RENEG_SERVER},
     {"RenegotiateClient", SSL_TEST_HANDSHAKE_RENEG_CLIENT},
+    {"KeyUpdateServer", SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER},
+    {"KeyUpdateClient", SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT},
 };
 
 __owur static int parse_handshake_mode(SSL_TEST_CTX *test_ctx, const char *value)
@@ -345,6 +347,24 @@ const char *ssl_handshake_mode_name(ssl_handshake_mode_t mode)
 
 IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CLIENT_CONF, client, reneg_ciphers)
 
+/* KeyUpdateType */
+
+static const test_enum ssl_key_update_types[] = {
+    {"KeyUpdateRequested", SSL_KEY_UPDATE_REQUESTED},
+    {"KeyUpdateNotRequested", SSL_KEY_UPDATE_NOT_REQUESTED},
+};
+
+__owur static int parse_key_update_type(SSL_TEST_CTX *test_ctx, const char *value)
+{
+    int ret_value;
+    if (!parse_enum(ssl_key_update_types, OSSL_NELEM(ssl_key_update_types),
+                    &ret_value, value)) {
+        return 0;
+    }
+    test_ctx->key_update_type = ret_value;
+    return 1;
+}
+
 /* CT Validation */
 
 static const test_enum ssl_ct_validation_modes[] = {
@@ -522,6 +542,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = {
     { "ExpectedNPNProtocol", &parse_test_expected_npn_protocol },
     { "ExpectedALPNProtocol", &parse_test_expected_alpn_protocol },
     { "HandshakeMode", &parse_handshake_mode },
+    { "KeyUpdateType", &parse_key_update_type },
     { "ResumptionExpected", &parse_test_resumption_expected },
     { "ApplicationData", &parse_test_app_data_size },
     { "MaxFragmentSize", &parse_test_max_fragment_size },
diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h
index 6036a02..1c66740 100644
--- a/test/ssl_test_ctx.h
+++ b/test/ssl_test_ctx.h
@@ -57,7 +57,9 @@ typedef enum {
     SSL_TEST_HANDSHAKE_SIMPLE = 0, /* Default */
     SSL_TEST_HANDSHAKE_RESUME,
     SSL_TEST_HANDSHAKE_RENEG_SERVER,
-    SSL_TEST_HANDSHAKE_RENEG_CLIENT
+    SSL_TEST_HANDSHAKE_RENEG_CLIENT,
+    SSL_TEST_HANDSHAKE_KEY_UPDATE_SERVER,
+    SSL_TEST_HANDSHAKE_KEY_UPDATE_CLIENT
 } ssl_handshake_mode_t;
 
 typedef enum {
@@ -121,6 +123,8 @@ typedef struct {
     int app_data_size;
     /* Maximum send fragment size. */
     int max_fragment_size;
+    /* KeyUpdate type */
+    int key_update_type;
 
     /*
      * Extra server/client configurations. Per-handshake.
diff --git a/util/libssl.num b/util/libssl.num
index 199c17a..32b5f76 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -413,3 +413,5 @@ SSL_COMP_get0_name                      413	1_1_0d	EXIST::FUNCTION:
 SSL_CTX_set_keylog_callback             414	1_1_1	EXIST::FUNCTION:
 SSL_CTX_get_keylog_callback             415	1_1_1	EXIST::FUNCTION:
 SSL_get_peer_signature_type_nid         416	1_1_1	EXIST::FUNCTION:
+SSL_key_update                          417	1_1_1	EXIST::FUNCTION:
+SSL_get_key_update_type                 418	1_1_1	EXIST::FUNCTION:


More information about the openssl-commits mailing list