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@mit.edu> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/4964)
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 @@ {"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 @@ 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 @@ #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 isdtls = 0; #endif char *psksessf = NULL; + int force_pha = 0; FD_ZERO(&readfds); FD_ZERO(&writefds); @@ -1469,6 +1472,9 @@ 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 @@ 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 d57eefb..a0e72b3 100644 --- a/apps/s_server.c +++ b/apps/s_server.c
@@ -2501,6 +2501,19 @@ i = 0; continue; } + 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"; BIO_write(SSL_get_wbio(con), str, strlen(str));
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_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_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_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_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_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_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_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_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_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<-ctlogfile>] [B<-keylogfile file>] [B<-early_data file>] +[B<-force_pha>] [B<target>] =head1 DESCRIPTION @@ -621,6 +622,11 @@ 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 @@ 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..c9b4daf 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 @@ 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 @@ 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 Certificate Request 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 @@ 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 @@ =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 @@ 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 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 B<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 @@ 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_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_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_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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ # 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 @@ 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 @@ {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 @@ {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 @@ "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 @@ "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 @@ "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 @@ {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 @@ "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 @@ "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 @@ "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..5a5fbad 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c
@@ -1186,6 +1186,8 @@ 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 +5320,55 @@ 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 @@ 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 @@ 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_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..2faba13 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c
@@ -56,6 +56,8 @@ 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 { /* The defined type for the extension */ @@ -290,6 +292,14 @@ 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 +1663,10 @@ 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 @@ #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 @@ 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 @@ 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..5050233 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 @@ 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; } @@ -375,6 +395,13 @@ * ossl_statem_client_write_transition(). */ switch (st->hand_state) { + 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; + } + /* Fall through */ + default: /* Shouldn't happen */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, @@ -798,11 +825,17 @@ 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 +2432,11 @@ 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); @@ -3332,6 +3367,7 @@ if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT && !tls1_check_chain(s, NULL, NULL, NULL, -2)) return 0; + return 1; } @@ -3357,8 +3393,12 @@ } 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 +3443,8 @@ } } + if (s->post_handshake_auth == SSL_PHA_REQUESTED) + return WORK_FINISHED_STOP; return WORK_FINISHED_CONTINUE; } @@ -3414,14 +3456,19 @@ 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 @@ /* * 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 @@ 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 @@ /* 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 @@ */ 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 @@ 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 @@ /* * 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 @@ *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 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ * 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 /* - * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert - * during re-negotiation: + * don't request if post-handshake-only unless doing + * post-handshake in TLSv1.3: */ - && (s->s3->tmp.finish_md_len == 0 || + && (!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 + * a second time: + */ + && (s->certreqs_sent < 1 || !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) /* * never request cert in anonymous ciphersuites (see @@ -388,6 +401,10 @@ 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 @@ 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 @@ * 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 @@ } 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_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 @@ } done: + s->certreqs_sent++; s->s3->tmp.cert_request = 1; return 1; } @@ -3396,11 +3446,12 @@ 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 @@ 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 @@ } } + /* + * 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 @@ 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 @@ {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 @@ 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 @@ 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 @@ || 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 @@ 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 @@ 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 @@ 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 @@ 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->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 @@ # 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 @@ && 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 @@ {"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 @@ 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 @@ { "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 @@ { "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 @@ 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 @@ char *reneg_ciphers; char *srp_user; char *srp_password; + /* Forced PHA */ + int force_pha; } SSL_TEST_CLIENT_CONF; typedef struct { @@ -122,6 +125,8 @@ /* 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 @@ 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 @@ 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_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 @@ 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 @@ 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 = ();