From a93fd4e74236a7f2e2ca43a3c9dfbbe4916f092f Mon Sep 17 00:00:00 2001 From: Todd Short Date: Fri, 12 Apr 2019 11:13:25 -0400 Subject: [PATCH 01/28] QUIC: Add support for BoringSSL QUIC APIs This is a cherry-pick of 2a4b03a306439307e0b822b17eda3bdabddfbb68 on the master-quic-support2 branch (2019-10-07) Which was a rebase/squash of master-quic-support: * 5aa62ce Add support for more secrets - Todd Short/Todd Short (master-quic-support) * 58e0643 Tweeks to quic_change_cipher_state() - Todd Short/Todd Short * 8169702 Move QUIC code out of tls13_change_cipher_state() - Todd Short/Todd Short * a08cfe6 Correctly disable middlebox compat - Todd Short/Todd Short * 3a9eabf Add OPENSSL_NO_QUIC wrapper - Todd Short/Todd Short * f550eca Add client early traffic secret storage - Todd Short/Todd Short * 1b787ae Quick fix: s2c to c2s for early secret - Todd Short/Todd Short * f97e6a9 Don't process an incomplete message - Todd Short/Todd Short * 81f0ce2 Reset init state in SSL_process_quic_post_handshake() - Todd Short/Todd Short * 5d59cf9 Fix quic_transport constructors/parsers - Todd Short/Todd Short * 5e5f91c Fix INSTALL nit. - Todd Short/Todd Short * bd290ab Fix duplicate word in docs - Todd Short/Todd Short * 699590b fixup! Handle partial handshake messages - Todd Short/Todd Short * a472a8d Handle partial handshake messages - Todd Short/Todd Short * 363cf3d fixup! Use proper secrets for handshake - Todd Short/Todd Short * b03fee6 Use proper secrets for handshake - Todd Short/Todd Short * 2ab1aa0 Move QUIC transport params to encrypted extensions - Todd Short/Todd Short * 0d16af9 Make temp secret names less confusing - Todd Short/Todd Short * abb6f39 New method to get QUIC secret length - Todd Short/Todd Short * 05fdae9 Add support for BoringSSL QUIC APIs - Todd Short/Todd Short This adds a compatible API for BoringSSL's QUIC support, based on the current |draft-ietf-quic-tls|. Based on BoringSSL commit 3c034b2cf386b3131f75520705491871a2e0cafe Based on BoringSSL commit c8e0f90f83b9ec38ea833deb86b5a41360b62b6a Based on BoringSSL commit 3cbb0299a28a8bd0136257251a78b91a96c5eec8 Based on BoringSSL commit cc9d935256539af2d3b7f831abf57c0d685ffd81 Based on BoringSSL commit e6eef1ca16a022e476bbaedffef044597cfc8f4b Based on BoringSSL commit 6f733791148cf8a076bf0e95498235aadbe5926d Based on BoringSSL commit 384d0eaf1930af1ebc47eda751f0c78dfcba1c03 Based on BoringSSL commit a0373182eb5cc7b81d49f434596b473c7801c942 Based on BoringSSL commit b1b76aee3cb43ce11889403c5334283d951ebd37 New method to get QUIC secret length Make temp secret names less confusing Move QUIC transport params to encrypted extensions Use proper secrets for handshake fixup! Use proper secrets for handshake Handle partial handshake messages fixup! Handle partial handshake messages Fix duplicate word in docs Fix INSTALL nit. Fix quic_transport constructors/parsers Reset init state in SSL_process_quic_post_handshake() Don't process an incomplete message Quick fix: s2c to c2s for early secret Add client early traffic secret storage Add OPENSSL_NO_QUIC wrapper Correctly disable middlebox compat Move QUIC code out of tls13_change_cipher_state() Create quic_change_cipher_state() that does the minimal required to generate the QUIC secrets. (e.g. encryption contexts are not initialized). Tweeks to quic_change_cipher_state() Add support for more secrets (cherry picked from commit 3b0bdf80dabddfe37d8f8f07e82a2ba85f5a93ad) --- CHANGES | 3 + Configure | 3 + INSTALL | 3 + crypto/err/openssl.txt | 19 +- doc/man3/SSL_CIPHER_get_name.pod | 13 ++ doc/man3/SSL_CTX_set_quic_method.pod | 232 ++++++++++++++++++++++ include/openssl/evp.h | 4 + include/openssl/ossl_typ.h | 2 + include/openssl/ssl.h | 45 +++++ include/openssl/sslerr.h | 17 +- include/openssl/tls1.h | 3 + ssl/build.info | 3 +- ssl/s3_msg.c | 10 + ssl/ssl_ciph.c | 32 +++ ssl/ssl_err.c | 25 +++ ssl/ssl_lib.c | 41 +++- ssl/ssl_local.h | 44 +++++ ssl/ssl_quic.c | 285 +++++++++++++++++++++++++++ ssl/statem/extensions.c | 29 +++ ssl/statem/extensions_clnt.c | 40 ++++ ssl/statem/extensions_srvr.c | 43 ++++ ssl/statem/statem.c | 21 +- ssl/statem/statem_lib.c | 19 +- ssl/statem/statem_local.h | 19 ++ ssl/statem/statem_quic.c | 109 ++++++++++ ssl/tls13_enc.c | 146 ++++++++++++-- test/sslapitest.c | 132 +++++++++++++ test/ssltestlib.c | 5 + test/tls13secretstest.c | 7 + util/libssl.num | 11 ++ util/private.num | 2 + 31 files changed, 1337 insertions(+), 30 deletions(-) create mode 100644 doc/man3/SSL_CTX_set_quic_method.pod create mode 100644 ssl/ssl_quic.c create mode 100644 ssl/statem/statem_quic.c diff --git a/CHANGES b/CHANGES index 824f421b8d331..7401b52d3a386 100644 --- a/CHANGES +++ b/CHANGES @@ -324,6 +324,9 @@ Changes between 1.1.1c and 1.1.1d [10 Sep 2019] + *) Implement BoringSSL's QUIC API + [Todd Short] + *) Fixed a fork protection issue. OpenSSL 1.1.1 introduced a rewritten random number generator (RNG). This was intended to include protection in the event of a fork() system call in order to ensure that the parent and child diff --git a/Configure b/Configure index b286dd0678bbb..8288c9d51e38e 100755 --- a/Configure +++ b/Configure @@ -401,6 +401,7 @@ my @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -514,6 +515,8 @@ my @disable_cascades = ( "comp" => [ "zlib" ], "ec" => [ "tls1_3", "sm2" ], "sm3" => [ "sm2" ], + "tls1_3" => [ "quic" ], + sub { !$disabled{"unit-test"} } => [ "heartbeats" ], sub { !$disabled{"msan"} } => [ "asm" ], diff --git a/INSTALL b/INSTALL index f3ac727183f0d..cd2e6f2f8fbe3 100644 --- a/INSTALL +++ b/INSTALL @@ -457,6 +457,9 @@ no-psk Don't build support for Pre-Shared Key based ciphersuites. + no-quic + Don't build with support for QUIC. + no-rdrand Don't use hardware RDRAND capabilities. diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 902e97b843551..65d88597b7be2 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1184,7 +1184,7 @@ SSL_F_OSSL_STATEM_SERVER_CONSTRUCT_MESSAGE:431:* SSL_F_OSSL_STATEM_SERVER_POST_PROCESS_MESSAGE:601:\ ossl_statem_server_post_process_message SSL_F_OSSL_STATEM_SERVER_POST_WORK:602:ossl_statem_server_post_work -SSL_F_OSSL_STATEM_SERVER_PRE_WORK:640: +SSL_F_OSSL_STATEM_SERVER_PRE_WORK:640:ossl_statem_server_pre_work SSL_F_OSSL_STATEM_SERVER_PROCESS_MESSAGE:603:ossl_statem_server_process_message SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION:418:ossl_statem_server_read_transition SSL_F_OSSL_STATEM_SERVER_WRITE_TRANSITION:604:\ @@ -1193,6 +1193,9 @@ SSL_F_PARSE_CA_NAMES:541:parse_ca_names SSL_F_PITEM_NEW:624:pitem_new SSL_F_PQUEUE_NEW:625:pqueue_new SSL_F_PROCESS_KEY_SHARE_EXT:439:* +SSL_F_QUIC_CHANGE_CIPHER_STATE:3000:quic_change_cipher_state +SSL_F_QUIC_GET_MESSAGE:3001:quic_get_message +SSL_F_QUIC_SET_ENCRYPTION_SECRETS:3002:quic_set_encryption_secrets SSL_F_READ_STATE_MACHINE:352:read_state_machine SSL_F_SET_CLIENT_CIPHERSUITE:540:set_client_ciphersuite SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET:595:srp_generate_client_master_secret @@ -1203,7 +1206,9 @@ SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM:130:ssl3_check_cert_and_algorithm SSL_F_SSL3_CTRL:213:ssl3_ctrl SSL_F_SSL3_CTX_CTRL:133:ssl3_ctx_ctrl SSL_F_SSL3_DIGEST_CACHED_RECORDS:293:ssl3_digest_cached_records +SSL_F_SSL3_DISPATCH_ALERT:3003:ssl3_dispatch_alert SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC:292:ssl3_do_change_cipher_spec +SSL_F_SSL3_DO_WRITE:3004:ssl3_do_write SSL_F_SSL3_ENC:608:ssl3_enc SSL_F_SSL3_FINAL_FINISH_MAC:285:ssl3_final_finish_mac SSL_F_SSL3_FINISH_MAC:587:ssl3_finish_mac @@ -1311,6 +1316,8 @@ SSL_F_SSL_PARSE_SERVERHELLO_USE_SRTP_EXT:311:* SSL_F_SSL_PEEK:270:SSL_peek SSL_F_SSL_PEEK_EX:432:SSL_peek_ex SSL_F_SSL_PEEK_INTERNAL:522:ssl_peek_internal +SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE:3005:SSL_process_quic_post_handshake +SSL_F_SSL_PROVIDE_QUIC_DATA:3006:SSL_provide_quic_data SSL_F_SSL_READ:223:SSL_read SSL_F_SSL_READ_EARLY_DATA:529:SSL_read_early_data SSL_F_SSL_READ_EX:434:SSL_read_ex @@ -1360,6 +1367,7 @@ SSL_F_SSL_WRITE_EARLY_DATA:526:SSL_write_early_data SSL_F_SSL_WRITE_EARLY_FINISH:527:* SSL_F_SSL_WRITE_EX:433:SSL_write_ex SSL_F_SSL_WRITE_INTERNAL:524:ssl_write_internal +SSL_F_STATEM_FLUSH:3007:statem_flush SSL_F_STATE_MACHINE:353:state_machine SSL_F_TLS12_CHECK_PEER_SIGALG:333:tls12_check_peer_sigalg SSL_F_TLS12_COPY_SIGALGS:533:tls12_copy_sigalgs @@ -1423,6 +1431,8 @@ 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_QUIC_TRANSPORT_PARAMS:3008:\ + tls_construct_ctos_quic_transport_params SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE:473:tls_construct_ctos_renegotiate SSL_F_TLS_CONSTRUCT_CTOS_SCT:474:tls_construct_ctos_sct SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME:475:tls_construct_ctos_server_name @@ -1464,6 +1474,8 @@ SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE:456:tls_construct_stoc_key_share SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN:548:tls_construct_stoc_maxfragmentlen SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG:457:tls_construct_stoc_next_proto_neg SSL_F_TLS_CONSTRUCT_STOC_PSK:504:tls_construct_stoc_psk +SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS:3009:\ + tls_construct_stoc_quic_transport_params SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE:458:tls_construct_stoc_renegotiate SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME:459:tls_construct_stoc_server_name SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET:460:tls_construct_stoc_session_ticket @@ -1492,6 +1504,8 @@ 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_QUIC_TRANSPORT_PARAMS:3010:\ + tls_parse_ctos_quic_transport_params SSL_F_TLS_PARSE_CTOS_RENEGOTIATE:464:tls_parse_ctos_renegotiate SSL_F_TLS_PARSE_CTOS_SERVER_NAME:573:tls_parse_ctos_server_name SSL_F_TLS_PARSE_CTOS_SESSION_TICKET:574:tls_parse_ctos_session_ticket @@ -1510,6 +1524,8 @@ SSL_F_TLS_PARSE_STOC_KEY_SHARE:445:tls_parse_stoc_key_share SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN:581:tls_parse_stoc_maxfragmentlen SSL_F_TLS_PARSE_STOC_NPN:582:tls_parse_stoc_npn SSL_F_TLS_PARSE_STOC_PSK:502:tls_parse_stoc_psk +SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS:3011:\ + tls_parse_stoc_quic_transport_params SSL_F_TLS_PARSE_STOC_RENEGOTIATE:448:tls_parse_stoc_renegotiate SSL_F_TLS_PARSE_STOC_SCT:564:tls_parse_stoc_sct SSL_F_TLS_PARSE_STOC_SERVER_NAME:583:tls_parse_stoc_server_name @@ -2894,6 +2910,7 @@ SSL_R_VERSION_TOO_LOW:396:version too low SSL_R_WRONG_CERTIFICATE_TYPE:383:wrong certificate type SSL_R_WRONG_CIPHER_RETURNED:261:wrong cipher returned SSL_R_WRONG_CURVE:378:wrong curve +SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED:800:wrong encryption level received SSL_R_WRONG_SIGNATURE_LENGTH:264:wrong signature length SSL_R_WRONG_SIGNATURE_SIZE:265:wrong signature size SSL_R_WRONG_SIGNATURE_TYPE:370:wrong signature type diff --git a/doc/man3/SSL_CIPHER_get_name.pod b/doc/man3/SSL_CIPHER_get_name.pod index 26edae3d80be9..20437b76e8479 100644 --- a/doc/man3/SSL_CIPHER_get_name.pod +++ b/doc/man3/SSL_CIPHER_get_name.pod @@ -13,6 +13,7 @@ SSL_CIPHER_get_digest_nid, SSL_CIPHER_get_handshake_digest, SSL_CIPHER_get_kx_nid, SSL_CIPHER_get_auth_nid, +SSL_CIPHER_get_prf_nid, SSL_CIPHER_is_aead, SSL_CIPHER_find, SSL_CIPHER_get_id, @@ -34,6 +35,7 @@ SSL_CIPHER_get_protocol_id const EVP_MD *SSL_CIPHER_get_handshake_digest(const SSL_CIPHER *c); int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c); int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c); + int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); int SSL_CIPHER_is_aead(const SSL_CIPHER *c); const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr); uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *c); @@ -91,6 +93,15 @@ TLS 1.3 cipher suites) B is returned. Examples (not comprehensive) NID_auth_ecdsa NID_auth_psk +SSL_CIPHER_get_prf_nid() retuns the pseudo-random function NID for B. If B is +a pre-TLS-1.2 cipher, it returns B but note these ciphers use +SHA-256 in TLS 1.2. Other return values may be treated uniformly in all +applicable versions. Examples (not comprehensive): + + NID_md5_sha1 + NID_sha256 + NID_sha384 + SSL_CIPHER_is_aead() returns 1 if the cipher B is AEAD (e.g. GCM or ChaCha20/Poly1305), and 0 if it is not AEAD. @@ -201,6 +212,8 @@ required to enable this function. The OPENSSL_cipher_name() function was added in OpenSSL 1.1.1. +The SSL_CIPHER_get_prf_nid() function was added in OpenSSL 3.0.0. + =head1 COPYRIGHT Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod new file mode 100644 index 0000000000000..60bf704944b2e --- /dev/null +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -0,0 +1,232 @@ +=pod + +=head1 NAME + +SSL_QUIC_METHOD, +OSSL_ENCRYPTION_LEVEL, +SSL_CTX_set_quic_method, +SSL_set_quic_method, +SSL_set_quic_transport_params, +SSL_get_peer_quic_transport_params, +SSL_quic_max_handshake_flight_len, +SSL_quic_read_level, +SSL_quic_write_level, +SSL_provide_quic_data, +SSL_process_quic_post_handshake, +SSL_is_quic +- QUIC support + +=head1 SYNOPSIS + + #include + + typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + typedef enum ssl_encryption_level_t OSSL_ENCRYPTION_LEVEL; + + int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); + int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); + int SSL_set_quic_transport_params(SSL *ssl, + const uint8_t *params, + size_t params_len); + void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len); + size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); + OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); + OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); + int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int SSL_process_quic_post_handshake(SSL *ssl); + int SSL_is_quic(SSL *ssl); + +=head1 DESCRIPTION + +SSL_CTX_set_quic_method() and SSL_set_quic_method() configures the QUIC methods. +This should only be configured with a minimum version of TLS 1.3. B +must remain valid for the lifetime of B or B. Calling this disables +the SSL_OP_ENABLE_MIDDLEBOX_COMPAT option, which is not required for QUIC. + +SSL_set_quic_transport_params() configures B to send B (of length +B) in the quic_transport_parameters extension in either the +ClientHello or EncryptedExtensions handshake message. This extension will +only be sent if the TLS version is at least 1.3, and for a server, only if +the client sent the extension. The buffer pointed to by B only need be +valid for the duration of the call to this function. + +SSL_get_peer_quic_transport_params() provides the caller with the value of the +quic_transport_parameters extension sent by the peer. A pointer to the buffer +containing the TransportParameters will be put in B<*out_params>, and its +length in B<*out_params_len>. This buffer will be valid for the lifetime of the +B. If no params were received from the peer, B<*out_params_len> will be 0. + +SSL_quic_max_handshake_flight_len() returns the maximum number of bytes +that may be received at the given encryption level. This function should be +used to limit buffering in the QUIC implementation. + +See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4. + +SSL_quic_read_level() returns the current read encryption level. + +SSL_quic_write_level() returns the current write encryption level. + +SSL_provide_quic_data() provides data from QUIC at a particular encryption +level B. It is an error to call this function outside of the handshake +or with an encryption level other than the current read level. It returns one +on success and zero on error. + +SSL_process_quic_post_handshake() processes any data that QUIC has provided +after the handshake has completed. This includes NewSessionTicket messages +sent by the server. + +SSL_is_quic() indicates whether a connection uses QUIC. + +=head1 NOTES + +These APIs are implementations of BoringSSL's QUIC APIs. + +QUIC acts as an underlying transport for the TLS 1.3 handshake. The following +functions allow a QUIC implementation to serve as the underlying transport as +described in draft-ietf-quic-tls. + +When configured for QUIC, SSL_do_handshake() will drive the handshake as +before, but it will not use the configured B. It will call functions on +B to configure secrets and send data. If data is needed from +the peer, it will return B. When received, the caller +should call SSL_provide_quic_data() and then SSL_do_handshake() to continue +the handshake. After the handshake is complete, the caller should call +SSL_provide_quic_data() for any post-handshake data, followed by +SSL_process_quic_post_handshake() to process it. It is an error to call +SSL_read()/SSL_read_ex() and SSL_write()/SSL_write_ex() in QUIC. + +Note that secrets for an encryption level may be available to QUIC before the +level is active in TLS. Callers should use SSL_quic_read_level() to determine +the active read level for SSL_provide_quic_data(). SSL_do_handshake() will +pass the active write level to add_handshake_data() when writing data. Callers +can use SSL_quic_write_level() to query the active write level when +generating their own errors. + +See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more +details. + +To avoid DoS attacks, the QUIC implementation must limit the amount of data +being queued up. The implementation can call +SSL_quic_max_handshake_flight_len() to get the maximum buffer length at each +encryption level. + +draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters +used by QUIC for each endpoint to unilaterally declare its supported +transport parameters. draft-ietf-quic-transport (section 7.4) defines the +contents of that extension (a TransportParameters struct) and describes how +to handle it and its semantic meaning. + +OpenSSL handles this extension as an opaque byte string. The caller is +responsible for serializing and parsing it. + +=head2 OSSL_ENCRYPTION_LEVEL + +B (B) represents the +encryption levels: + +=over 4 + +=item ssl_encryption_initial + +The initial encryption level that is used for client and server hellos. + +=item ssl_encryption_early_data + +The encryption level for early data. This is a write-level for the client +and a read-level for the server. + +=item ssl_encryption_handshake + +The encryption level for the remainder of the handshake. + +=item ssl_encryption_application + +The encryption level for the application data. + +=back + +=head2 SSL_QUIC_METHOD + +The B (B) describes the +QUIC methods. + + struct ssl_quic_method_st { + int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len); + int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int (*flush_flight)(SSL *ssl); + int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); + }; + typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + +set_encryption_secrets() configures the read and write secrets for the given +encryption level. This function will always be called before an encryption +level other than B is used. Note, however, that +secrets for a level may be configured before TLS is ready to send or accept +data at that level. + +When reading packets at a given level, the QUIC implementation must send +ACKs at the same level, so this function provides read and write secrets +together. The exception is B, where secrets are +only available in the client to server direction. The other secret will be +NULL. The server acknowledges such data at B, +which will be configured in the same SSL_do_handshake() call. + +This function should use SSL_get_current_cipher() to determine the TLS +cipher suite. + +add_handshake_data() adds handshake data to the current flight at the given +encryption level. It returns one on success and zero on error. + +OpenSSL will pack data from a single encryption level together, but a +single handshake flight may include multiple encryption levels. Callers +should defer writing data to the network until flush_flight() to better +pack QUIC packets into transport datagrams. + +flush_flight() is called when the current flight is complete and should be +written to the transport. Note a flight may contain data at several +encryption levels. + +send_alert() sends a fatal alert at the specified encryption level. + +All QUIC methods return 1 on success and 0 on error. + +=head1 RETURN VALUES + +SSL_CTX_set_quic_method(), +SSL_set_quic_method(), +SSL_set_quic_transport_params(), and +SSL_process_quic_post_handshake() +return 1 on success, and 0 on error. + +SSL_quic_read_level() and SSL_quic_write_level() return the current +encryption level as B (B). + +SSL_quic_max_handshake_flight_len() returns the maximum length of a flight +for a given encryption level. + +SSL_is_quic() returns 1 if QUIC is being used, 0 if not. + +=head1 SEE ALSO + +L, L, L + +=head1 HISTORY + +These functions were added in OpenSSL 3.0.0. + +=head1 COPYRIGHT + +Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (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. + +=cut diff --git a/include/openssl/evp.h b/include/openssl/evp.h index a411f3f2f9491..275b7a4acca55 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1324,6 +1324,10 @@ void EVP_PKEY_asn1_set_security_bits(EVP_PKEY_ASN1_METHOD *ameth, */ # define EVP_PKEY_FLAG_SIGCTX_CUSTOM 4 +/* Used by Chromium/QUIC */ +# define X25519_PRIVATE_KEY_LEN 32 +# define X25519_PUBLIC_VALUE_LEN 32 + const EVP_PKEY_METHOD *EVP_PKEY_meth_find(int type); EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags); void EVP_PKEY_meth_get0_info(int *ppkey_id, int *pflags, diff --git a/include/openssl/ossl_typ.h b/include/openssl/ossl_typ.h index e0edfaaf47605..d2fdce8fdf6fd 100644 --- a/include/openssl/ossl_typ.h +++ b/include/openssl/ossl_typ.h @@ -176,6 +176,8 @@ typedef struct ct_policy_eval_ctx_st CT_POLICY_EVAL_CTX; typedef struct ossl_store_info_st OSSL_STORE_INFO; typedef struct ossl_store_search_st OSSL_STORE_SEARCH; +typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ defined(INTMAX_MAX) && defined(UINTMAX_MAX) typedef intmax_t ossl_intmax_t; diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index fd0c5a99967ff..8d9b9fb0a36a6 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2432,6 +2432,51 @@ void SSL_set_allow_early_data_cb(SSL *s, SSL_allow_early_data_cb_fn cb, void *arg); +# ifndef OPENSSL_NO_QUIC +/* + * QUIC integration - The QUIC interface matches BoringSSL + * + * ssl_encryption_level_t represents a specific QUIC encryption level used to + * transmit handshake messages. BoringSSL has this as an 'enum'. + */ +typedef enum ssl_encryption_level_t { + ssl_encryption_initial = 0, + ssl_encryption_early_data, + ssl_encryption_handshake, + ssl_encryption_application +} OSSL_ENCRYPTION_LEVEL; + +struct ssl_quic_method_st { + int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len); + int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int (*flush_flight)(SSL *ssl); + int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); +}; + +__owur int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); +__owur int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); +__owur int SSL_set_quic_transport_params(SSL *ssl, + const uint8_t *params, + size_t params_len); +void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len); +__owur size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); +__owur OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); +__owur OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); +__owur int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); +__owur int SSL_process_quic_post_handshake(SSL *ssl); + +__owur int SSL_is_quic(SSL *ssl); + +# endif + +int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); + # ifdef __cplusplus } # endif diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index 701d61c6e9cb5..a303500cd7123 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -11,9 +11,7 @@ #ifndef HEADER_SSLERR_H # define HEADER_SSLERR_H -# ifndef HEADER_SYMHACKS_H -# include -# endif +# include # ifdef __cplusplus extern "C" @@ -97,6 +95,9 @@ int ERR_load_SSL_strings(void); # define SSL_F_PITEM_NEW 624 # define SSL_F_PQUEUE_NEW 625 # define SSL_F_PROCESS_KEY_SHARE_EXT 439 +# define SSL_F_QUIC_CHANGE_CIPHER_STATE 3000 +# define SSL_F_QUIC_GET_MESSAGE 3001 +# define SSL_F_QUIC_SET_ENCRYPTION_SECRETS 3002 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SET_CLIENT_CIPHERSUITE 540 # define SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET 595 @@ -107,7 +108,9 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL3_CTRL 213 # define SSL_F_SSL3_CTX_CTRL 133 # define SSL_F_SSL3_DIGEST_CACHED_RECORDS 293 +# define SSL_F_SSL3_DISPATCH_ALERT 3003 # define SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC 292 +# define SSL_F_SSL3_DO_WRITE 3004 # define SSL_F_SSL3_ENC 608 # define SSL_F_SSL3_FINAL_FINISH_MAC 285 # define SSL_F_SSL3_FINISH_MAC 587 @@ -212,6 +215,8 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_PEEK 270 # define SSL_F_SSL_PEEK_EX 432 # define SSL_F_SSL_PEEK_INTERNAL 522 +# define SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE 3005 +# define SSL_F_SSL_PROVIDE_QUIC_DATA 3006 # define SSL_F_SSL_READ 223 # define SSL_F_SSL_READ_EARLY_DATA 529 # define SSL_F_SSL_READ_EX 434 @@ -261,6 +266,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_WRITE_EARLY_FINISH 527 # define SSL_F_SSL_WRITE_EX 433 # define SSL_F_SSL_WRITE_INTERNAL 524 +# define SSL_F_STATEM_FLUSH 3007 # define SSL_F_STATE_MACHINE 353 # define SSL_F_TLS12_CHECK_PEER_SIGALG 333 # define SSL_F_TLS12_COPY_SIGALGS 533 @@ -320,6 +326,7 @@ int ERR_load_SSL_strings(void); # 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_QUIC_TRANSPORT_PARAMS 3008 # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE 473 # define SSL_F_TLS_CONSTRUCT_CTOS_SCT 474 # define SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME 475 @@ -359,6 +366,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN 548 # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457 # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504 +# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS 3009 # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458 # define SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME 459 # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET 460 @@ -384,6 +392,7 @@ int ERR_load_SSL_strings(void); # 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_QUIC_TRANSPORT_PARAMS 3010 # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464 # define SSL_F_TLS_PARSE_CTOS_SERVER_NAME 573 # define SSL_F_TLS_PARSE_CTOS_SESSION_TICKET 574 @@ -402,6 +411,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN 581 # define SSL_F_TLS_PARSE_STOC_NPN 582 # define SSL_F_TLS_PARSE_STOC_PSK 502 +# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS 3011 # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448 # define SSL_F_TLS_PARSE_STOC_SCT 564 # define SSL_F_TLS_PARSE_STOC_SERVER_NAME 583 @@ -765,6 +775,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_WRONG_CERTIFICATE_TYPE 383 # define SSL_R_WRONG_CIPHER_RETURNED 261 # define SSL_R_WRONG_CURVE 378 +# define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 800 # define SSL_R_WRONG_SIGNATURE_LENGTH 264 # define SSL_R_WRONG_SIGNATURE_SIZE 265 # define SSL_R_WRONG_SIGNATURE_TYPE 370 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 76d9fda46e207..6e16c97316ddc 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -148,6 +148,9 @@ extern "C" { /* Temporary extension type */ # define TLSEXT_TYPE_renegotiate 0xff01 +/* ExtensionType value from draft-ietf-quic-tls-13 */ +# define TLSEXT_TYPE_quic_transport_parameters 0xffa5 + # ifndef OPENSSL_NO_NEXTPROTONEG /* This is not an IANA defined extension number */ # define TLSEXT_TYPE_next_proto_neg 13172 diff --git a/ssl/build.info b/ssl/build.info index bb2f1deb53006..eec0d14f2c563 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -12,4 +12,5 @@ SOURCE[../libssl]=\ ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \ bio_ssl.c ssl_err.c tls_srp.c t1_trce.c ssl_utst.c \ record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \ - statem/statem.c record/ssl3_record_tls13.c + statem/statem.c record/ssl3_record_tls13.c \ + ssl_quic.c statem/statem_quic.c diff --git a/ssl/s3_msg.c b/ssl/s3_msg.c index 707e962d73d41..1adf564e95089 100644 --- a/ssl/s3_msg.c +++ b/ssl/s3_msg.c @@ -77,6 +77,16 @@ int ssl3_dispatch_alert(SSL *s) s->s3->alert_dispatch = 0; alertlen = 2; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + if (!s->quic_method->send_alert(s, s->quic_write_level, + s->s3->send_alert[1])) { + SSLerr(SSL_F_SSL3_DISPATCH_ALERT, ERR_R_INTERNAL_ERROR); + return 0; + } + i = 1; + } else +#endif i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], &alertlen, 1, 0, &written); if (i <= 0) { diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 735a483c64486..a3fe97597b592 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -2162,3 +2162,35 @@ int ssl_cert_is_disabled(size_t idx) return 1; return 0; } + +int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c) +{ + switch (c->algorithm2 & (0xFF << TLS1_PRF_DGST_SHIFT)) { + default: + break; + case TLS1_PRF_SHA1_MD5: /* TLS1_PRF */ + return NID_md5_sha1; + case TLS1_PRF_SHA256: + return NID_sha256; + case TLS1_PRF_SHA384: + return NID_sha384; + case TLS1_PRF_GOST94: + return NID_id_GostR3411_94_prf; + case TLS1_PRF_GOST12_256: + return NID_id_GostR3411_2012_256; + case TLS1_PRF_GOST12_512: + return NID_id_GostR3411_2012_512; + } + /* TLSv1.3 ciphers don't specify separate PRF */ + switch (c->algorithm2 & SSL_HANDSHAKE_MAC_MASK) { + default: + break; + case SSL_HANDSHAKE_MAC_MD5_SHA1: /* SSL_HANDSHAKE_MAC_DEFAULT */ + return NID_md5_sha1; + case SSL_HANDSHAKE_MAC_SHA256: + return NID_sha256; + case SSL_HANDSHAKE_MAC_SHA384: + return NID_sha384; + } + return NID_undef; +} diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 324f2ccbb0de0..094c5751006d2 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -113,6 +113,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "ossl_statem_server_post_process_message"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_POST_WORK, 0), "ossl_statem_server_post_work"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PRE_WORK, 0), + "ossl_statem_server_pre_work"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_PROCESS_MESSAGE, 0), "ossl_statem_server_process_message"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION, 0), @@ -123,6 +125,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_PITEM_NEW, 0), "pitem_new"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_PQUEUE_NEW, 0), "pqueue_new"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_PROCESS_KEY_SHARE_EXT, 0), ""}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_CHANGE_CIPHER_STATE, 0), + "quic_change_cipher_state"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_GET_MESSAGE, 0), "quic_get_message"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, 0), + "quic_set_encryption_secrets"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_READ_STATE_MACHINE, 0), "read_state_machine"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SET_CLIENT_CIPHERSUITE, 0), "set_client_ciphersuite"}, @@ -140,8 +147,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_CTX_CTRL, 0), "ssl3_ctx_ctrl"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DIGEST_CACHED_RECORDS, 0), "ssl3_digest_cached_records"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DISPATCH_ALERT, 0), + "ssl3_dispatch_alert"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC, 0), "ssl3_do_change_cipher_spec"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_WRITE, 0), "ssl3_do_write"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_ENC, 0), "ssl3_enc"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_FINAL_FINISH_MAC, 0), "ssl3_final_finish_mac"}, @@ -303,6 +313,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK, 0), "SSL_peek"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_EX, 0), "SSL_peek_ex"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_INTERNAL, 0), "ssl_peek_internal"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, 0), + "SSL_process_quic_post_handshake"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROVIDE_QUIC_DATA, 0), + "SSL_provide_quic_data"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ, 0), "SSL_read"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ_EARLY_DATA, 0), "SSL_read_early_data"}, @@ -379,6 +393,7 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_FINISH, 0), ""}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EX, 0), "SSL_write_ex"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_INTERNAL, 0), "ssl_write_internal"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_STATEM_FLUSH, 0), "statem_flush"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_STATE_MACHINE, 0), "state_machine"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS12_CHECK_PEER_SIGALG, 0), "tls12_check_peer_sigalg"}, @@ -480,6 +495,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_ctos_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0), "tls_construct_ctos_psk_kex_modes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, 0), + "tls_construct_ctos_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE, 0), "tls_construct_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_SCT, 0), @@ -551,6 +568,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_stoc_next_proto_neg"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_PSK, 0), "tls_construct_stoc_psk"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, 0), + "tls_construct_stoc_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE, 0), "tls_construct_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME, 0), @@ -597,6 +616,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {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"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, 0), + "tls_parse_ctos_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_RENEGOTIATE, 0), "tls_parse_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_SERVER_NAME, 0), @@ -629,6 +650,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_parse_stoc_maxfragmentlen"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_NPN, 0), "tls_parse_stoc_npn"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_PSK, 0), "tls_parse_stoc_psk"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, 0), + "tls_parse_stoc_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_RENEGOTIATE, 0), "tls_parse_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SCT, 0), "tls_parse_stoc_sct"}, @@ -1253,6 +1276,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED), "wrong cipher returned"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "wrong curve"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED), + "wrong encryption level received"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH), "wrong signature length"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index ffd0a0bc6d176..7b02752f2690f 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -845,6 +845,10 @@ SSL *SSL_new(SSL_CTX *ctx) s->job = NULL; +#ifndef OPENSSL_NO_QUIC + s->quic_method = ctx->quic_method; +#endif + #ifndef OPENSSL_NO_CT if (!SSL_set_ct_validation_callback(s, ctx->ct_validation_callback, ctx->ct_validation_callback_arg)) @@ -1212,6 +1216,18 @@ void SSL_free(SSL *s) OPENSSL_free(s->pha_context); EVP_MD_CTX_free(s->pha_dgst); +#ifndef OPENSSL_NO_QUIC + OPENSSL_free(s->ext.quic_transport_params); + OPENSSL_free(s->ext.peer_quic_transport_params); + while (s->quic_input_data_head != NULL) { + QUIC_DATA *qd; + + qd = s->quic_input_data_head; + s->quic_input_data_head = qd->next; + OPENSSL_free(qd); + } +#endif + sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free); sk_X509_NAME_pop_free(s->client_ca_names, X509_NAME_free); @@ -1731,6 +1747,12 @@ static int ssl_io_intern(void *vargs) int ssl_read_internal(SSL *s, void *buf, size_t num, size_t *readbytes) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_READ_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_READ_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -1863,6 +1885,12 @@ int SSL_get_early_data_status(const SSL *s) static int ssl_peek_internal(SSL *s, void *buf, size_t num, size_t *readbytes) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_PEEK_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_PEEK_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -1923,6 +1951,12 @@ int SSL_peek_ex(SSL *s, void *buf, size_t num, size_t *readbytes) int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_WRITE_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_WRITE_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -3618,6 +3652,11 @@ int SSL_get_error(const SSL *s, int i) } if (SSL_want_read(s)) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + return SSL_ERROR_WANT_READ; + } +#endif bio = SSL_get_rbio(s); if (BIO_should_read(bio)) return SSL_ERROR_WANT_READ; @@ -3983,7 +4022,7 @@ EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) { - if ((s->session != NULL) && (s->session->cipher != NULL)) + if (s->session != NULL) return s->session->cipher; return NULL; } diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index f92472117a1b7..0399fa6004a7e 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -315,6 +315,13 @@ /* Flag used on OpenSSL ciphersuite ids to indicate they are for SSLv3+ */ # define SSL3_CK_CIPHERSUITE_FLAG 0x03000000 +/* Check if an SSL structure is using QUIC (which uses TLSv1.3) */ +# ifndef OPENSSL_NO_QUIC +# define SSL_IS_QUIC(s) (s->quic_method != NULL) +# else +# define SSL_IS_QUIC(s) 0 +# endif + /* Check if an SSL structure is using DTLS */ # define SSL_IS_DTLS(s) (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS) @@ -714,6 +721,7 @@ typedef enum tlsext_index_en { TLSEXT_IDX_cryptopro_bug, TLSEXT_IDX_early_data, TLSEXT_IDX_certificate_authorities, + TLSEXT_IDX_quic_transport_params, TLSEXT_IDX_padding, TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ @@ -1063,7 +1071,24 @@ struct ssl_ctx_st { /* Do we advertise Post-handshake auth support? */ int pha_enabled; + +#ifndef OPENSSL_NO_QUIC + const SSL_QUIC_METHOD *quic_method; +#endif +}; + +typedef struct cert_pkey_st CERT_PKEY; + +#ifndef OPENSSL_NO_QUIC +struct quic_data_st { + struct quic_data_st *next; + OSSL_ENCRYPTION_LEVEL level; + size_t offset; + size_t length; }; +typedef struct quic_data_st QUIC_DATA; +int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); +#endif struct ssl_st { /* @@ -1152,6 +1177,11 @@ struct ssl_st { unsigned char handshake_traffic_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]; +# ifndef OPENSSL_NO_QUIC + unsigned char client_hand_traffic_secret[EVP_MAX_MD_SIZE]; + unsigned char server_hand_traffic_secret[EVP_MAX_MD_SIZE]; + unsigned char client_early_traffic_secret[EVP_MAX_MD_SIZE]; +# endif unsigned char exporter_master_secret[EVP_MAX_MD_SIZE]; unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE]; EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */ @@ -1364,8 +1394,22 @@ struct ssl_st { * selected. */ int tick_identity; + +#ifndef OPENSSL_NO_QUIC + uint8_t *quic_transport_params; + size_t quic_transport_params_len; + uint8_t *peer_quic_transport_params; + size_t peer_quic_transport_params_len; +#endif } ext; +#ifndef OPENSSL_NO_QUIC + OSSL_ENCRYPTION_LEVEL quic_read_level; + OSSL_ENCRYPTION_LEVEL quic_write_level; + QUIC_DATA *quic_input_data_head; + QUIC_DATA *quic_input_data_tail; + const SSL_QUIC_METHOD *quic_method; +#endif /* * Parsed form of the ClientHello, kept around across client_hello_cb * calls. diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c new file mode 100644 index 0000000000000..62de36c990828 --- /dev/null +++ b/ssl/ssl_quic.c @@ -0,0 +1,285 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (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 + */ + +#include "ssl_local.h" +#include "internal/cryptlib.h" +#include "internal/refcount.h" + +#ifdef OPENSSL_NO_QUIC +NON_EMPTY_TRANSLATION_UNIT +#else + +int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, + size_t params_len) +{ + uint8_t *tmp; + + if (params == NULL || params_len == 0) { + tmp = NULL; + params_len = 0; + } else { + tmp = OPENSSL_memdup(params, params_len); + if (tmp == NULL) + return 0; + } + + OPENSSL_free(ssl->ext.quic_transport_params); + ssl->ext.quic_transport_params = tmp; + ssl->ext.quic_transport_params_len = params_len; + return 1; +} + +void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len) +{ + *out_params = ssl->ext.peer_quic_transport_params; + *out_params_len = ssl->ext.peer_quic_transport_params_len; +} + +size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level) +{ + /* + * Limits flights to 16K by default when there are no large + * (certificate-carrying) messages. + */ + static const size_t DEFAULT_FLIGHT_LIMIT = 16384; + + switch (level) { + case ssl_encryption_initial: + return DEFAULT_FLIGHT_LIMIT; + case ssl_encryption_early_data: + /* QUIC does not send EndOfEarlyData. */ + return 0; + case ssl_encryption_handshake: + if (ssl->server) { + /* + * Servers may receive Certificate message if configured to request + * client certificates. + */ + if ((ssl->verify_mode & SSL_VERIFY_PEER) + && ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) + return ssl->max_cert_list; + } else { + /* + * Clients may receive both Certificate message and a CertificateRequest + * message. + */ + if (2*ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) + return 2 * ssl->max_cert_list; + } + return DEFAULT_FLIGHT_LIMIT; + case ssl_encryption_application: + return DEFAULT_FLIGHT_LIMIT; + } + + return 0; +} + +OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl) +{ + return ssl->quic_read_level; +} + +OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl) +{ + return ssl->quic_write_level; +} + +int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len) +{ + size_t l; + + if (!SSL_IS_QUIC(ssl)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + /* Level can be different than the current read, but not less */ + if (level < ssl->quic_read_level + || (ssl->quic_input_data_tail != NULL && level < ssl->quic_input_data_tail->level)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + return 0; + } + + /* Split the QUIC messages up, if necessary */ + while (len > 0) { + QUIC_DATA *qd; + const uint8_t *p = data + 1; + + /* Check for an incomplete block */ + qd = ssl->quic_input_data_tail; + if (qd != NULL) { + l = qd->length - qd->offset; + if (l != 0) { + /* we still need to copy `l` bytes into the last data block */ + if (l > len) + l = len; + memcpy((char*)(qd+1) + qd->offset, data, l); + qd->offset += l; + len -= l; + data += l; + continue; + } + } + + n2l3(p, l); + l += SSL3_HM_HEADER_LENGTH; + + qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); + if (qd == NULL) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; + } + + qd->next = NULL; + qd->length = l; + qd->level = level; + /* partial data received? */ + if (l > len) + l = len; + qd->offset = l; + + memcpy((void*)(qd + 1), data, l); + if (ssl->quic_input_data_tail != NULL) + ssl->quic_input_data_tail->next = qd; + else + ssl->quic_input_data_head = qd; + ssl->quic_input_data_tail = qd; + + data += l; + len -= l; + } + + return 1; +} + +int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) +{ + switch (ctx->method->version) { + case DTLS1_VERSION: + case DTLS1_2_VERSION: + case DTLS_ANY_VERSION: + case DTLS1_BAD_VER: + return 0; + default: + break; + } + ctx->quic_method = quic_method; + ctx->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; + return 1; +} + +int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method) +{ + switch (ssl->method->version) { + case DTLS1_VERSION: + case DTLS1_2_VERSION: + case DTLS_ANY_VERSION: + case DTLS1_BAD_VER: + return 0; + default: + break; + } + ssl->quic_method = quic_method; + ssl->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; + return 1; +} + +int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) +{ + uint8_t *c2s_secret = NULL; + uint8_t *s2c_secret = NULL; + size_t len; + const EVP_MD *md; + + if (!SSL_IS_QUIC(ssl)) + return 1; + + /* secrets from the POV of the client */ + switch (level) { + case ssl_encryption_early_data: + c2s_secret = ssl->client_early_traffic_secret; + break; + case ssl_encryption_handshake: + c2s_secret = ssl->client_hand_traffic_secret; + s2c_secret = ssl->server_hand_traffic_secret; + break; + case ssl_encryption_application: + c2s_secret = ssl->client_app_traffic_secret; + s2c_secret = ssl->server_app_traffic_secret; + break; + default: + return 1; + } + + md = ssl_handshake_md(ssl); + if (md == NULL) { + /* May not have selected cipher, yet */ + const SSL_CIPHER *c = NULL; + + if (ssl->session != NULL) + c = SSL_SESSION_get0_cipher(ssl->session); + else if (ssl->psksession != NULL) + c = SSL_SESSION_get0_cipher(ssl->psksession); + + if (c != NULL) + md = SSL_CIPHER_get_handshake_digest(c); + } + + if ((len = EVP_MD_size(md)) <= 0) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + + if (ssl->server) { + if (!ssl->quic_method->set_encryption_secrets(ssl, level, c2s_secret, + s2c_secret, len)) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + if (!ssl->quic_method->set_encryption_secrets(ssl, level, s2c_secret, + c2s_secret, len)) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + } + + return 1; +} + +int SSL_process_quic_post_handshake(SSL *ssl) +{ + int ret; + + if (SSL_in_init(ssl) || !SSL_IS_QUIC(ssl)) { + SSLerr(SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + ossl_statem_set_in_init(ssl, 1); + ret = ssl->handshake_func(ssl); + ossl_statem_set_in_init(ssl, 0); + + if (ret <= 0) + return 0; + return 1; +} + +int SSL_is_quic(SSL* ssl) +{ + return SSL_IS_QUIC(ssl); +} + +#endif diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 0f39275baa015..f7a395f4bbd57 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -58,6 +58,10 @@ 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); static int final_psk(SSL *s, unsigned int context, int sent); +#ifndef OPENSSL_NO_QUIC +static int init_quic_transport_params(SSL *s, unsigned int context); +static int final_quic_transport_params(SSL *s, unsigned int context, int sent); +#endif /* Structure to define a built-in extension */ typedef struct extensions_definition_st { @@ -377,6 +381,19 @@ static const EXTENSION_DEFINITION ext_defs[] = { tls_construct_certificate_authorities, tls_construct_certificate_authorities, NULL, }, +#ifndef OPENSSL_NO_QUIC + { + TLSEXT_TYPE_quic_transport_parameters, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS + | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, + init_quic_transport_params, + tls_parse_ctos_quic_transport_params, tls_parse_stoc_quic_transport_params, + tls_construct_stoc_quic_transport_params, tls_construct_ctos_quic_transport_params, + final_quic_transport_params, + }, +#else + INVALID_EXTENSION, +#endif { /* Must be immediately before pre_shared_key */ TLSEXT_TYPE_padding, @@ -1745,3 +1762,15 @@ static int final_psk(SSL *s, unsigned int context, int sent) return 1; } + +#ifndef OPENSSL_NO_QUIC +static int init_quic_transport_params(SSL *s, unsigned int context) +{ + return 1; +} + +static int final_quic_transport_params(SSL *s, unsigned int context, int sent) +{ + return 1; +} +#endif diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index ce8a75794c3ad..96d8e85502f03 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1215,7 +1215,28 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, #endif } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_construct_stoc_quic_transport_params() */ +EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif /* * Parse the server's renegotiation binding and abort if it's not right */ @@ -2009,3 +2030,22 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 1; } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_parse_ctos_quic_transport_params() */ +int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params, + &s->ext.peer_quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} +#endif diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 04f64f8106404..2dab8ca6871dc 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1303,6 +1303,26 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context return 1; } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_parse_stoc_quic_transport_params() */ +int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params, + &s->ext.peer_quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} +#endif + /* * Add the server's renegotiation binding */ @@ -1979,3 +1999,26 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, return EXT_RETURN_SENT; } + +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_construct_ctos_quic_transport_params() */ +EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index 20f5bd584e6c5..0a8acedebfefe 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -575,6 +575,10 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) * In DTLS we get the whole message in one go - header and body */ ret = dtls_get_message(s, &mt, &len); +#ifndef OPENSSL_NO_QUIC + } else if (SSL_IS_QUIC(s)) { + ret = quic_get_message(s, &mt, &len); +#endif } else { ret = tls_get_message_header(s, &mt); } @@ -604,8 +608,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) return SUB_STATE_ERROR; } - /* dtls_get_message already did this */ - if (!SSL_IS_DTLS(s) + /* dtls_get_message/quic_get_message already did this */ + if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s) && s->s3->tmp.message_size > 0 && !grow_init_buf(s, s->s3->tmp.message_size + SSL3_HM_HEADER_LENGTH)) { @@ -618,8 +622,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) /* Fall through */ case READ_STATE_BODY: - if (!SSL_IS_DTLS(s)) { - /* We already got this above for DTLS */ + if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s)) { + /* We already got this above for DTLS & QUIC */ ret = tls_get_message_body(s, &len); if (ret == 0) { /* Could be non-blocking IO */ @@ -900,6 +904,15 @@ static SUB_STATE_RETURN write_state_machine(SSL *s) int statem_flush(SSL *s) { s->rwstate = SSL_WRITING; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + if (!s->quic_method->flush_flight(s)) { + /* NOTE: BIO_flush() does not generate an error */ + SSLerr(SSL_F_STATEM_FLUSH, ERR_R_INTERNAL_ERROR); + return 0; + } + } else +#endif if (BIO_flush(s->wbio) <= 0) { return 0; } diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index c3b6f8f4569a6..ce992626042ff 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -42,9 +42,23 @@ int ssl3_do_write(SSL *s, int type) { int ret; size_t written = 0; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s) && type == SSL3_RT_HANDSHAKE) { + ret = s->quic_method->add_handshake_data(s, s->quic_write_level, + (const uint8_t*)&s->init_buf->data[s->init_off], + s->init_num); + if (!ret) { + ret = -1; + /* QUIC can't sent anything out sice the above failed */ + SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_INTERNAL_ERROR); + } else { + written = s->init_num; + } + } else +#endif + ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], + s->init_num, &written); - ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], - s->init_num, &written); if (ret < 0) return -1; if (type == SSL3_RT_HANDSHAKE) @@ -1157,6 +1171,7 @@ int tls_get_message_header(SSL *s, int *mt) do { while (s->init_num < SSL3_HM_HEADER_LENGTH) { + /* QUIC: either create a special ssl_read_bytes... or if/else this */ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &recvd_type, &p[s->init_num], SSL3_HM_HEADER_LENGTH - s->init_num, diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index eae88053dcd75..d26f243a7c775 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -95,6 +95,7 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst); __owur int tls_get_message_header(SSL *s, int *mt); __owur int tls_get_message_body(SSL *s, size_t *len); __owur int dtls_get_message(SSL *s, int *mt, size_t *len); +__owur int quic_get_message(SSL *s, int *mt, size_t *len); /* Message construction and processing functions */ __owur int tls_process_initial_server_flight(SSL *s); @@ -238,6 +239,10 @@ 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); +#ifndef OPENSSL_NO_QUIC +int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); +#endif EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, @@ -300,6 +305,11 @@ EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, size_t chainidx); EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); +#endif /* Client Extension processing */ EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, @@ -370,6 +380,11 @@ 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); +#ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); +#endif int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); @@ -415,6 +430,10 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); +#endif int tls_handle_alpn(SSL *s); diff --git a/ssl/statem/statem_quic.c b/ssl/statem/statem_quic.c new file mode 100644 index 0000000000000..eb1a76ec9d814 --- /dev/null +++ b/ssl/statem/statem_quic.c @@ -0,0 +1,109 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (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 + */ + +#include "../ssl_local.h" +#include "statem_local.h" +#include "internal/cryptlib.h" + +#ifdef OPENSSL_NO_QUIC +NON_EMPTY_TRANSLATION_UNIT +#else + +int quic_get_message(SSL *s, int *mt, size_t *len) +{ + size_t l; + QUIC_DATA *qd = s->quic_input_data_head; + uint8_t *p; + + if (qd == NULL || (qd->length - qd->offset) != 0) { + s->rwstate = SSL_READING; + *len = 0; + return 0; + } + + /* This is where we check for the proper level, not when data is given */ + if (qd->level != s->quic_read_level) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, + SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + *len = 0; + return 0; + } + + if (!BUF_MEM_grow_clean(s->init_buf, (int)qd->length)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, + ERR_R_BUF_LIB); + *len = 0; + return 0; + } + + /* Copy buffered data */ + memcpy(s->init_buf->data, (void*)(qd + 1), qd->length); + s->init_buf->length = qd->length; + s->quic_input_data_head = qd->next; + if (s->quic_input_data_head == NULL) + s->quic_input_data_tail = NULL; + OPENSSL_free(qd); + + s->s3->tmp.message_type = *mt = *(s->init_buf->data); + p = (uint8_t*)s->init_buf->data + 1; + n2l3(p, l); + s->init_num = s->s3->tmp.message_size = *len = l; + s->init_msg = s->init_buf->data + SSL3_HM_HEADER_LENGTH; + + /* No CCS in QUIC/TLSv1.3? */ + if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, + SSL_F_QUIC_GET_MESSAGE, + SSL_R_CCS_RECEIVED_EARLY); + *len = 0; + return 0; + } + + /* + * If receiving Finished, record MAC of prior handshake messages for + * Finished verification. + */ + if (*mt == SSL3_MT_FINISHED && !ssl3_take_mac(s)) { + /* SSLfatal() already called */ + *len = 0; + return 0; + } + + /* + * 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) + /* 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) + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, + (size_t)s->init_num + SSL3_HM_HEADER_LENGTH, s, + s->msg_callback_arg); + + return 1; +} + +#endif diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index b8fb07f210ef2..77f9576761653 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -434,27 +434,131 @@ static int derive_secret_key_and_iv(SSL *s, int sending, const EVP_MD *md, return 0; } -int tls13_change_cipher_state(SSL *s, int which) -{ #ifdef CHARSET_EBCDIC - static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; - static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; - static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; #else - static const unsigned char client_early_traffic[] = "c e traffic"; - static const unsigned char client_handshake_traffic[] = "c hs traffic"; - static const unsigned char client_application_traffic[] = "c ap traffic"; - static const unsigned char server_handshake_traffic[] = "s hs traffic"; - static const unsigned char server_application_traffic[] = "s ap traffic"; - static const unsigned char exporter_master_secret[] = "exp master"; - static const unsigned char resumption_master_secret[] = "res master"; - static const unsigned char early_exporter_master_secret[] = "e exp master"; +static const unsigned char client_early_traffic[] = "c e traffic"; +static const unsigned char client_handshake_traffic[] = "c hs traffic"; +static const unsigned char client_application_traffic[] = "c ap traffic"; +static const unsigned char server_handshake_traffic[] = "s hs traffic"; +static const unsigned char server_application_traffic[] = "s ap traffic"; +static const unsigned char exporter_master_secret[] = "exp master"; +static const unsigned char resumption_master_secret[] = "res master"; +static const unsigned char early_exporter_master_secret[] = "e exp master"; #endif +#ifndef OPENSSL_NO_QUIC +static int quic_change_cipher_state(SSL *s, int which) +{ + unsigned char hash[EVP_MAX_MD_SIZE]; + size_t hashlen = 0; + int hashleni; + int ret = 0; + const EVP_MD *md = NULL; + OSSL_ENCRYPTION_LEVEL level = ssl_encryption_initial; + int is_handshake = ((which & SSL3_CC_HANDSHAKE) == SSL3_CC_HANDSHAKE); + int is_client_read = ((which & SSL3_CHANGE_CIPHER_CLIENT_READ) == SSL3_CHANGE_CIPHER_CLIENT_READ); + int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); + int is_early = (which & SSL3_CC_EARLY); + + md = ssl_handshake_md(s); + if (!ssl3_digest_cached_records(s, 1) + || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { + /* SSLfatal() already called */; + goto err; + } + + /* Ensure cast to size_t is safe */ + hashleni = EVP_MD_size(md); + if (!ossl_assert(hashleni >= 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + ERR_R_EVP_LIB); + goto err; + } + hashlen = (size_t)hashleni; + + if (is_client_read || is_server_write) { + if (is_handshake) { + level = ssl_encryption_handshake; + + if (!tls13_hkdf_expand(s, md, s->handshake_secret, client_handshake_traffic, + sizeof(client_handshake_traffic)-1, hash, hashlen, + s->client_hand_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL, s->client_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, s->client_hand_traffic_secret, + s->client_finished_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->handshake_secret, server_handshake_traffic, + sizeof(server_handshake_traffic)-1, hash, hashlen, + s->server_hand_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL, s->server_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, s->server_hand_traffic_secret, + s->server_finished_secret, hashlen)) { + /* SSLfatal() already called */ + goto err; + } + } else { + level = ssl_encryption_application; + + if (!tls13_hkdf_expand(s, md, s->master_secret, client_application_traffic, + sizeof(client_application_traffic)-1, hash, hashlen, + s->client_app_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL, s->client_app_traffic_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->master_secret, server_application_traffic, + sizeof(server_application_traffic)-1, hash, hashlen, + s->server_app_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, + sizeof(resumption_master_secret)-1, hash, hashlen, + s->resumption_master_secret, hashlen, 1)) { + /* SSLfatal() already called */ + goto err; + } + } + if (!quic_set_encryption_secrets(s, level)) { + /* SSLfatal() already called */ + goto err; + } + if (s->server) + s->quic_write_level = level; + else + s->quic_read_level = level; + } else { + if (is_early) { + level = ssl_encryption_early_data; + + if (!tls13_hkdf_expand(s, md, s->early_secret, client_early_traffic, + sizeof(client_early_traffic)-1, hash, hashlen, + s->client_early_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_EARLY_LABEL, s->client_early_traffic_secret, hashlen) + || !quic_set_encryption_secrets(s, level)) { + /* SSLfatal() already called */ + goto err; + } + } else if (is_handshake) { + level = ssl_encryption_handshake; + } else { + level = ssl_encryption_application; + } + + if (s->server) + s->quic_read_level = level; + else + s->quic_write_level = level; + } + + ret = 1; + err: + return ret; +} +#endif /* OPENSSL_NO_QUIC */ +int tls13_change_cipher_state(SSL *s, int which) +{ unsigned char *iv; unsigned char secret[EVP_MAX_MD_SIZE]; unsigned char hashval[EVP_MAX_MD_SIZE]; @@ -470,6 +574,11 @@ int tls13_change_cipher_state(SSL *s, int which) const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) + return quic_change_cipher_state(s, which); +#endif + if (which & SSL3_CC_READ) { if (s->enc_read_ctx != NULL) { EVP_CIPHER_CTX_reset(s->enc_read_ctx); @@ -714,6 +823,7 @@ int tls13_change_cipher_state(SSL *s, int which) s->statem.enc_write_state = ENC_WRITE_STATE_WRITE_PLAIN_ALERTS; else s->statem.enc_write_state = ENC_WRITE_STATE_VALID; + ret = 1; err: OPENSSL_cleanse(secret, sizeof(secret)); diff --git a/test/sslapitest.c b/test/sslapitest.c index d311bb2ad3af3..e3bf6f6503468 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6829,6 +6829,135 @@ static int test_inherit_verify_param(void) return testresult; } +#ifndef OPENSSL_NO_QUIC + +static int test_quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len) +{ + test_printf_stderr("quic_set_encryption_secrets() %s, lvl=%d, len=%zd\n", + ssl->server ? "server" : "client", level, secret_len); + return 1; +} +static int test_quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len) +{ + SSL *peer = (SSL*)SSL_get_app_data(ssl); + + test_printf_stderr("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", + ssl->server ? "server" : "client", level, (int)*data, len); + if (!TEST_ptr(peer)) + return 0; + + if (!TEST_true(SSL_provide_quic_data(peer, level, data, len))) { + ERR_print_errors_fp(stderr); + return 0; + } + + return 1; +} +static int test_quic_flush_flight(SSL *ssl) +{ + test_printf_stderr("quic_flush_flight() %s\n", ssl->server ? "server" : "client"); + return 1; +} +static int test_quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) +{ + test_printf_stderr("quic_send_alert() %s, lvl=%d, alert=%d\n", + ssl->server ? "server" : "client", level, alert); + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + test_quic_set_encryption_secrets, + test_quic_add_handshake_data, + test_quic_flush_flight, + test_quic_send_alert, +}; +static int test_quic_api(void) +{ + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL; + int testresult = 0; + static const char *server_str = "SERVER"; + static const char *client_str = "CLIENT"; + const uint8_t *peer_str; + size_t peer_str_len; + + /* Clean up logging space */ + memset(client_log_buffer, 0, sizeof(client_log_buffer)); + memset(server_log_buffer, 0, sizeof(server_log_buffer)); + client_log_buffer_index = 0; + server_log_buffer_index = 0; + error_writing_log = 0; + + + if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) + || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) + || !TEST_ptr(sctx->quic_method) + || !TEST_ptr(serverssl = SSL_new(sctx)) + || !TEST_true(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, NULL)) + || !TEST_false(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) + || !TEST_true(SSL_IS_QUIC(serverssl))) + goto end; + + SSL_CTX_free(sctx); + sctx = NULL; + SSL_free(serverssl); + serverssl = NULL; + + if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), + TLS_client_method(), + TLS1_3_VERSION, 0, + &sctx, &cctx, cert, privkey)) + || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) + || !TEST_true(SSL_CTX_set_quic_method(cctx, &quic_method)) + || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, + &clientssl, NULL, NULL)) + || !TEST_true(SSL_set_quic_transport_params(serverssl, + (unsigned char*)server_str, + sizeof(server_str))) + || !TEST_true(SSL_set_quic_transport_params(clientssl, + (unsigned char*)client_str, + sizeof(client_str))) + || !TEST_true(SSL_set_app_data(serverssl, clientssl)) + || !TEST_true(SSL_set_app_data(clientssl, serverssl)) + || !TEST_true(create_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE)) + || !TEST_true(SSL_version(serverssl) == TLS1_3_VERSION) + || !TEST_true(SSL_version(clientssl) == TLS1_3_VERSION) + || !(TEST_int_eq(SSL_quic_read_level(clientssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_read_level(serverssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_write_level(clientssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_write_level(serverssl), ssl_encryption_application))) + goto end; + + SSL_get_peer_quic_transport_params(serverssl, &peer_str, &peer_str_len); + if (!TEST_mem_eq(peer_str, peer_str_len, client_str, sizeof(client_str))) + goto end; + SSL_get_peer_quic_transport_params(clientssl, &peer_str, &peer_str_len); + if (!TEST_mem_eq(peer_str, peer_str_len, server_str, sizeof(server_str))) + goto end; + + /* Deal with two NewSessionTickets */ + if (!TEST_true(SSL_process_quic_post_handshake(clientssl)) + || !TEST_true(SSL_process_quic_post_handshake(clientssl))) + goto end; + + 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(certsdir = test_get_argument(0)) @@ -6957,6 +7086,9 @@ int setup_tests(void) #endif ADD_TEST(test_set_alpn); ADD_TEST(test_inherit_verify_param); +#ifndef OPENSSL_NO_QUIC + ADD_TEST(test_quic_api); +#endif return 1; } diff --git a/test/ssltestlib.c b/test/ssltestlib.c index 456afdf4716e0..5f61e6339020b 100644 --- a/test/ssltestlib.c +++ b/test/ssltestlib.c @@ -917,6 +917,11 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want) if (!create_bare_ssl_connection(serverssl, clientssl, want, 1)) return 0; +#ifndef OPENSSL_NO_QUIC + /* QUIC does not support SSL_read_ex */ + if (SSL_is_quic(clientssl)) + return 1; +#endif /* * We attempt to read some data on the client side which we expect to fail. * This will ensure we have received the NewSessionTicket in TLSv1.3 where diff --git a/test/tls13secretstest.c b/test/tls13secretstest.c index 52fc2b667361e..970c2f4aae1bd 100644 --- a/test/tls13secretstest.c +++ b/test/tls13secretstest.c @@ -216,6 +216,13 @@ int ossl_statem_export_early_allowed(SSL *s) return 1; } +#ifndef OPENSSL_NO_QUIC +int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) +{ + return 1; +} +#endif + /* End of mocked out code */ static int test_secret(SSL *s, unsigned char *prk, diff --git a/util/libssl.num b/util/libssl.num index 297522c36391f..a0a8dfa185d29 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -498,3 +498,14 @@ SSL_CTX_get_recv_max_early_data 498 1_1_1 EXIST::FUNCTION: SSL_CTX_set_recv_max_early_data 499 1_1_1 EXIST::FUNCTION: SSL_CTX_set_post_handshake_auth 500 1_1_1 EXIST::FUNCTION: SSL_get_signature_type_nid 501 1_1_1a EXIST::FUNCTION: +SSL_quic_read_level 20000 1_1_1i EXIST::FUNCTION:QUIC +SSL_set_quic_transport_params 20001 1_1_1i EXIST::FUNCTION:QUIC +SSL_CIPHER_get_prf_nid 20002 1_1_1i EXIST::FUNCTION: +SSL_is_quic 20003 1_1_1i EXIST::FUNCTION:QUIC +SSL_get_peer_quic_transport_params 20004 1_1_1i EXIST::FUNCTION:QUIC +SSL_quic_write_level 20005 1_1_1i EXIST::FUNCTION:QUIC +SSL_CTX_set_quic_method 20006 1_1_1i EXIST::FUNCTION:QUIC +SSL_set_quic_method 20007 1_1_1i EXIST::FUNCTION:QUIC +SSL_quic_max_handshake_flight_len 20008 1_1_1i EXIST::FUNCTION:QUIC +SSL_process_quic_post_handshake 20009 1_1_1i EXIST::FUNCTION:QUIC +SSL_provide_quic_data 20010 1_1_1i EXIST::FUNCTION:QUIC diff --git a/util/private.num b/util/private.num index bc7d967b5d144..eb3d409f6e90f 100644 --- a/util/private.num +++ b/util/private.num @@ -91,6 +91,8 @@ custom_ext_free_cb datatype custom_ext_parse_cb datatype pem_password_cb datatype ssl_ct_validation_cb datatype +OSSL_ENCRYPTION_LEVEL datatype +SSL_QUIC_METHOD datatype # BIO_append_filename define BIO_destroy_bio_pair define From d31feb510a0fcacdc2ef7d58061203a1733f55c5 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 12 Nov 2019 13:52:35 -0500 Subject: [PATCH 02/28] QUIC: Fix resumption secret (cherry picked from commit 16fafdf4e0ec6cddd5705f407e5dca26cb30914d) (cherry picked from commit b97af137adf08bdd75c0c5843b2eb4a1483c2293) --- ssl/tls13_enc.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index 77f9576761653..89678d6c22167 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -512,10 +512,7 @@ static int quic_change_cipher_state(SSL *s, int which) || !tls13_hkdf_expand(s, md, s->master_secret, server_application_traffic, sizeof(server_application_traffic)-1, hash, hashlen, s->server_app_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen) - || !tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, - sizeof(resumption_master_secret)-1, hash, hashlen, - s->resumption_master_secret, hashlen, 1)) { + || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen)) { /* SSLfatal() already called */ goto err; } @@ -529,6 +526,8 @@ static int quic_change_cipher_state(SSL *s, int which) else s->quic_read_level = level; } else { + /* is_client_write || is_server_read */ + if (is_early) { level = ssl_encryption_early_data; @@ -544,6 +543,16 @@ static int quic_change_cipher_state(SSL *s, int which) level = ssl_encryption_handshake; } else { level = ssl_encryption_application; + /* + * We also create the resumption master secret, but this time use the + * hash for the whole handshake including the Client Finished + */ + if (!tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, + sizeof(resumption_master_secret)-1, hash, hashlen, + s->resumption_master_secret, hashlen, 1)) { + /* SSLfatal() already called */ + goto err; + } } if (s->server) From 841fb37c30f4cac9a2216b275600788245cf3931 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 13 Nov 2019 12:11:00 -0500 Subject: [PATCH 03/28] QUIC: Handle EndOfEarlyData and MaxEarlyData (cherry picked from commit 946e0c928b14ce22eb6a4a5b3d360853e33eb98b) --- ssl/statem/extensions_clnt.c | 12 ++++++++++++ ssl/statem/extensions_srvr.c | 12 ++++++++++-- ssl/statem/statem_clnt.c | 8 ++++++++ ssl/statem/statem_srvr.c | 4 ++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index 96d8e85502f03..d2fa56dedc0b4 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1943,6 +1943,18 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, return 0; } +#ifndef OPENSSL_NO_QUIC + /* + * QUIC server must send 0xFFFFFFFF or it's a PROTOCOL_VIOLATION + * per draft-ietf-quic-tls-24 S4.5 + */ + if (s->quic_method != NULL && max_early_data != 0xFFFFFFFF) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_EARLY_DATA, + SSL_R_INVALID_MAX_EARLY_DATA); + return 0; + } +#endif + s->session->ext.max_early_data = max_early_data; return 1; diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 2dab8ca6871dc..d78f397ff1399 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1953,12 +1953,20 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, size_t chainidx) { if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) { - if (s->max_early_data == 0) + uint32_t max_early_data = s->max_early_data; + + if (max_early_data == 0) return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_QUIC + /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-24 S4.5 */ + if (s->quic_method != NULL) + max_early_data = 0xFFFFFFFF; +#endif + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data) || !WPACKET_start_sub_packet_u16(pkt) - || !WPACKET_put_bytes_u32(pkt, s->max_early_data) + || !WPACKET_put_bytes_u32(pkt, max_early_data) || !WPACKET_close(pkt)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA, ERR_R_INTERNAL_ERROR); diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index d1a39698124b0..8c0b5b5c71a82 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -909,6 +909,14 @@ int ossl_statem_client_construct_message(SSL *s, WPACKET *pkt, break; case TLS_ST_CW_END_OF_EARLY_DATA: +#ifndef OPENSSL_NO_QUIC + /* QUIC does not send EndOfEarlyData, draft-ietf-quic-tls-24 S8.3 */ + if (s->quic_method != NULL) { + *confunc = NULL; + *mt = SSL3_MT_DUMMY; + break; + } +#endif *confunc = tls_construct_end_of_early_data; *mt = SSL3_MT_END_OF_EARLY_DATA; break; diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index d701c46b43b5a..2a8b18476a109 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -74,6 +74,10 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt) break; } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { if (mt == SSL3_MT_END_OF_EARLY_DATA) { +#ifndef OPENSSL_NO_QUIC + if (s->quic_method != NULL) + return 0; +#endif st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA; return 1; } From 19e450cbab1d5e7e4aea78d2174237a5e54e306e Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 7 Jan 2020 10:01:23 -0500 Subject: [PATCH 04/28] QUIC: Increase HKDF_MAXBUF to 2048 (cherry picked from commit 1fe3e022d092034fac8891cd629f7b80a1c31d61) --- crypto/kdf/hkdf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/kdf/hkdf.c b/crypto/kdf/hkdf.c index 25bf4b729f64c..6d1a32c885ecf 100644 --- a/crypto/kdf/hkdf.c +++ b/crypto/kdf/hkdf.c @@ -15,7 +15,7 @@ #include "internal/cryptlib.h" #include "crypto/evp.h" -#define HKDF_MAXBUF 1024 +#define HKDF_MAXBUF 2048 static unsigned char *HKDF(const EVP_MD *evp_md, const unsigned char *salt, size_t salt_len, From 21949c43f49cc6ec2cc3575ab0543ad6e9fa151e Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 7 Jan 2020 10:59:08 -0500 Subject: [PATCH 05/28] QUIC: Fall-through for 0RTT (cherry picked from commit aa4d9c6f4a1d85022f593cc72d89f6c60ed6f767) --- ssl/statem/statem_srvr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 2a8b18476a109..42f02b06013f1 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -72,12 +72,9 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt) return 1; } break; - } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { + } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED + && !SSL_IS_QUIC(s)) { if (mt == SSL3_MT_END_OF_EARLY_DATA) { -#ifndef OPENSSL_NO_QUIC - if (s->quic_method != NULL) - return 0; -#endif st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA; return 1; } From 98ffed13f82d31416bcb64bc3b2ef65656a84861 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Wed, 22 Apr 2020 09:12:36 -0700 Subject: [PATCH 06/28] QUIC: Some cleanup for the main QUIC changes Try to reduce unneeded whitespace changes and wrap new code to 80 columns. Reword documentation to attempt to improve clarity. Add some more sanity checks and clarifying comments to the code. Update referenced I-D versions. (cherry picked from commit 4a03a80c1d989658282db830c8b35b5351c280aa) --- doc/man3/SSL_CTX_set_quic_method.pod | 43 +++++++------- include/openssl/ssl.h | 4 +- include/openssl/sslerr.h | 4 +- include/openssl/tls1.h | 2 +- ssl/build.info | 7 ++- ssl/ssl_ciph.c | 2 + ssl/ssl_lib.c | 2 +- ssl/ssl_local.h | 3 +- ssl/ssl_quic.c | 45 +++++++-------- ssl/statem/extensions_clnt.c | 5 +- ssl/statem/extensions_srvr.c | 8 ++- ssl/statem/statem.c | 2 +- ssl/statem/statem_lib.c | 27 +++++---- ssl/statem/statem_local.h | 2 + ssl/statem/statem_quic.c | 23 ++++---- ssl/tls13_enc.c | 85 +++++++++++++++++++--------- test/sslapitest.c | 11 ++-- util/libssl.num | 2 +- 18 files changed, 166 insertions(+), 111 deletions(-) diff --git a/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod index 60bf704944b2e..3d7bf7e682137 100644 --- a/doc/man3/SSL_CTX_set_quic_method.pod +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -63,22 +63,25 @@ SSL_quic_max_handshake_flight_len() returns the maximum number of bytes that may be received at the given encryption level. This function should be used to limit buffering in the QUIC implementation. -See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4. +See https://tools.ietf.org/html/draft-ietf-quic-transport-27#section-4. SSL_quic_read_level() returns the current read encryption level. SSL_quic_write_level() returns the current write encryption level. -SSL_provide_quic_data() provides data from QUIC at a particular encryption -level B. It is an error to call this function outside of the handshake -or with an encryption level other than the current read level. It returns one -on success and zero on error. +SSL_provide_quic_data() is used to provide data from QUIC CRYPTO frames to the +state machine, at a particular encryption level B. It is an error to +call this function outside of the handshake or with an encryption level other +than the current read level. The application must buffer and consolidate any +frames with less than four bytes of content. It returns one on success and +zero on error. SSL_process_quic_post_handshake() processes any data that QUIC has provided after the handshake has completed. This includes NewSessionTicket messages sent by the server. -SSL_is_quic() indicates whether a connection uses QUIC. +SSL_is_quic() indicates whether a connection uses QUIC. A given B +or B can only be used with QUIC or TLS, but not both. =head1 NOTES @@ -89,11 +92,11 @@ functions allow a QUIC implementation to serve as the underlying transport as described in draft-ietf-quic-tls. When configured for QUIC, SSL_do_handshake() will drive the handshake as -before, but it will not use the configured B. It will call functions on -B to configure secrets and send data. If data is needed from -the peer, it will return B. When received, the caller -should call SSL_provide_quic_data() and then SSL_do_handshake() to continue -the handshake. After the handshake is complete, the caller should call +before, but it will not use the configured B. It will call functions from +the configured B to configure secrets and send data. If data +is needed from the peer, it will return B. When received, +the caller should call SSL_provide_quic_data() and then SSL_do_handshake() to +continue the handshake. After the handshake is complete, the caller should call SSL_provide_quic_data() for any post-handshake data, followed by SSL_process_quic_post_handshake() to process it. It is an error to call SSL_read()/SSL_read_ex() and SSL_write()/SSL_write_ex() in QUIC. @@ -105,7 +108,7 @@ pass the active write level to add_handshake_data() when writing data. Callers can use SSL_quic_write_level() to query the active write level when generating their own errors. -See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more +See https://tools.ietf.org/html/draft-ietf-quic-tls-27#section-4.1 for more details. To avoid DoS attacks, the QUIC implementation must limit the amount of data @@ -113,11 +116,12 @@ being queued up. The implementation can call SSL_quic_max_handshake_flight_len() to get the maximum buffer length at each encryption level. -draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters +draft-ietf-quic-tls defines a new TLS extension "quic_transport_parameters" used by QUIC for each endpoint to unilaterally declare its supported -transport parameters. draft-ietf-quic-transport (section 7.4) defines the -contents of that extension (a TransportParameters struct) and describes how -to handle it and its semantic meaning. +transport parameters. The contents of the extension are specified in +https://tools.ietf.org/html/draft-ietf-quic-transport-27#section-18 (as +a sequence of tag/length/value parameters) along with the interpretation of the +various parameters and the rules for their processing. OpenSSL handles this extension as an opaque byte string. The caller is responsible for serializing and parsing it. @@ -205,10 +209,11 @@ SSL_process_quic_post_handshake() return 1 on success, and 0 on error. SSL_quic_read_level() and SSL_quic_write_level() return the current -encryption level as B (B). +encryption level as an B +(B). -SSL_quic_max_handshake_flight_len() returns the maximum length of a flight -for a given encryption level. +SSL_quic_max_handshake_flight_len() returns the maximum length in bytes of a +flight for a given encryption level. SSL_is_quic() returns 1 if QUIC is being used, 0 if not. diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 8d9b9fb0a36a6..08d4c74b237cc 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2473,10 +2473,10 @@ __owur int SSL_process_quic_post_handshake(SSL *ssl); __owur int SSL_is_quic(SSL *ssl); -# endif - int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); +# endif + # ifdef __cplusplus } # endif diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index a303500cd7123..e31bf3ccf61a6 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -11,7 +11,9 @@ #ifndef HEADER_SSLERR_H # define HEADER_SSLERR_H -# include +# ifndef HEADER_SYMHACKS_H +# include +# endif # ifdef __cplusplus extern "C" diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 6e16c97316ddc..c11ca1efa30c9 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -148,7 +148,7 @@ extern "C" { /* Temporary extension type */ # define TLSEXT_TYPE_renegotiate 0xff01 -/* ExtensionType value from draft-ietf-quic-tls-13 */ +/* ExtensionType value from draft-ietf-quic-tls-27 */ # define TLSEXT_TYPE_quic_transport_parameters 0xffa5 # ifndef OPENSSL_NO_NEXTPROTONEG diff --git a/ssl/build.info b/ssl/build.info index eec0d14f2c563..497d943900d64 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -12,5 +12,8 @@ SOURCE[../libssl]=\ ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \ bio_ssl.c ssl_err.c tls_srp.c t1_trce.c ssl_utst.c \ record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \ - statem/statem.c record/ssl3_record_tls13.c \ - ssl_quic.c statem/statem_quic.c + statem/statem.c record/ssl3_record_tls13.c + +IF[{- !$disabled{quic} -}] + SOURCE[../libssl]=ssl_quic.c statem/statem_quic.c +ENDIF diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index a3fe97597b592..67514f40b8dd3 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -2163,6 +2163,7 @@ int ssl_cert_is_disabled(size_t idx) return 0; } +#ifndef OPENSSL_NO_QUIC int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c) { switch (c->algorithm2 & (0xFF << TLS1_PRF_DGST_SHIFT)) { @@ -2194,3 +2195,4 @@ int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c) } return NID_undef; } +#endif diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 7b02752f2690f..c5c5614ee1cf0 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -4022,7 +4022,7 @@ EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) { - if (s->session != NULL) + if ((s->session != NULL) && (s->session->cipher != NULL)) return s->session->cipher; return NULL; } diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 0399fa6004a7e..6dc603c450dc0 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1085,6 +1085,7 @@ struct quic_data_st { OSSL_ENCRYPTION_LEVEL level; size_t offset; size_t length; + /* char data[]; should be here but C90 VLAs not allowed here */ }; typedef struct quic_data_st QUIC_DATA; int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); @@ -1560,8 +1561,6 @@ typedef struct tls_group_info_st { # define TLS_CURVE_CHAR2 0x1 # define TLS_CURVE_CUSTOM 0x2 -typedef struct cert_pkey_st CERT_PKEY; - /* * Structure containing table entry of certificate info corresponding to * CERT_PKEY entries diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index 62de36c990828..695238016d5a4 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -11,10 +11,6 @@ #include "internal/cryptlib.h" #include "internal/refcount.h" -#ifdef OPENSSL_NO_QUIC -NON_EMPTY_TRANSLATION_UNIT -#else - int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, size_t params_len) { @@ -109,10 +105,10 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, return 0; } - /* Split the QUIC messages up, if necessary */ + /* Split on handshake message boundaries, if necessary */ while (len > 0) { QUIC_DATA *qd; - const uint8_t *p = data + 1; + const uint8_t *p; /* Check for an incomplete block */ qd = ssl->quic_input_data_tail; @@ -130,6 +126,12 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, } } + if (len < SSL3_HM_HEADER_LENGTH) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_BAD_LENGTH); + return 0; + } + /* TLS Handshake message header has 1-byte type and 3-byte length */ + p = data + 1; n2l3(p, l); l += SSL3_HM_HEADER_LENGTH; @@ -163,15 +165,8 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) { - switch (ctx->method->version) { - case DTLS1_VERSION: - case DTLS1_2_VERSION: - case DTLS_ANY_VERSION: - case DTLS1_BAD_VER: + if (ctx->method->version != TLS_ANY_VERSION) return 0; - default: - break; - } ctx->quic_method = quic_method; ctx->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; return 1; @@ -179,15 +174,8 @@ int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method) { - switch (ssl->method->version) { - case DTLS1_VERSION: - case DTLS1_2_VERSION: - case DTLS_ANY_VERSION: - case DTLS1_BAD_VER: + if (ssl->method->version != TLS_ANY_VERSION) return 0; - default: - break; - } ssl->quic_method = quic_method; ssl->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; return 1; @@ -225,6 +213,12 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) /* May not have selected cipher, yet */ const SSL_CIPHER *c = NULL; + /* + * It probably doesn't make sense to use an (external) PSK session, + * but in theory some kinds of external session caches could be + * implemented using it, so allow psksession to be used as well as + * the regular session. + */ if (ssl->session != NULL) c = SSL_SESSION_get0_cipher(ssl->session); else if (ssl->psksession != NULL) @@ -268,6 +262,11 @@ int SSL_process_quic_post_handshake(SSL *ssl) return 0; } + /* + * This is always safe (we are sure to be at a record boundary) because + * SSL_read()/SSL_write() are never used for QUIC connections -- the + * application data is handled at the QUIC layer instead. + */ ossl_statem_set_in_init(ssl, 1); ret = ssl->handshake_func(ssl); ossl_statem_set_in_init(ssl, 0); @@ -281,5 +280,3 @@ int SSL_is_quic(SSL* ssl) { return SSL_IS_QUIC(ssl); } - -#endif diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index d2fa56dedc0b4..9e48458679bb7 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1946,7 +1946,7 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, #ifndef OPENSSL_NO_QUIC /* * QUIC server must send 0xFFFFFFFF or it's a PROTOCOL_VIOLATION - * per draft-ietf-quic-tls-24 S4.5 + * per draft-ietf-quic-tls-27 S4.5 */ if (s->quic_method != NULL && max_early_data != 0xFFFFFFFF) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_EARLY_DATA, @@ -2055,7 +2055,8 @@ int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int conte &s->ext.peer_quic_transport_params, &s->ext.peer_quic_transport_params_len)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, + ERR_R_INTERNAL_ERROR); return 0; } return 1; diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index d78f397ff1399..ff782bd53b76e 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1316,7 +1316,8 @@ int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int conte &s->ext.peer_quic_transport_params, &s->ext.peer_quic_transport_params_len)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, + ERR_R_INTERNAL_ERROR); return 0; } return 1; @@ -1959,7 +1960,7 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, return EXT_RETURN_NOT_SENT; #ifndef OPENSSL_NO_QUIC - /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-24 S4.5 */ + /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-27 S4.5 */ if (s->quic_method != NULL) max_early_data = 0xFFFFFFFF; #endif @@ -2023,7 +2024,8 @@ EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, s->ext.quic_transport_params_len)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, + ERR_R_INTERNAL_ERROR); return EXT_RETURN_FAIL; } diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index 0a8acedebfefe..d76924da85c48 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -577,6 +577,7 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) ret = dtls_get_message(s, &mt, &len); #ifndef OPENSSL_NO_QUIC } else if (SSL_IS_QUIC(s)) { + /* QUIC behaves like DTLS -- all in one go. */ ret = quic_get_message(s, &mt, &len); #endif } else { @@ -907,7 +908,6 @@ int statem_flush(SSL *s) #ifndef OPENSSL_NO_QUIC if (SSL_IS_QUIC(s)) { if (!s->quic_method->flush_flight(s)) { - /* NOTE: BIO_flush() does not generate an error */ SSLerr(SSL_F_STATEM_FLUSH, ERR_R_INTERNAL_ERROR); return 0; } diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index ce992626042ff..f84d4ebca624e 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -42,23 +42,29 @@ int ssl3_do_write(SSL *s, int type) { int ret; size_t written = 0; + #ifndef OPENSSL_NO_QUIC - if (SSL_IS_QUIC(s) && type == SSL3_RT_HANDSHAKE) { - ret = s->quic_method->add_handshake_data(s, s->quic_write_level, - (const uint8_t*)&s->init_buf->data[s->init_off], - s->init_num); - if (!ret) { - ret = -1; - /* QUIC can't sent anything out sice the above failed */ - SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_INTERNAL_ERROR); + if (SSL_IS_QUIC(s)) { + if (type == SSL3_RT_HANDSHAKE) { + ret = s->quic_method->add_handshake_data(s, s->quic_write_level, + (const uint8_t*)&s->init_buf->data[s->init_off], + s->init_num); + if (!ret) { + ret = -1; + /* QUIC can't sent anything out sice the above failed */ + SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_INTERNAL_ERROR); + } else { + written = s->init_num; + } } else { - written = s->init_num; + /* QUIC doesn't use ChangeCipherSpec */ + ret = -1; + SSLerr(SSL_F_SSL3_DO_WRITE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); } } else #endif ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], s->init_num, &written); - if (ret < 0) return -1; if (type == SSL3_RT_HANDSHAKE) @@ -1171,7 +1177,6 @@ int tls_get_message_header(SSL *s, int *mt) do { while (s->init_num < SSL3_HM_HEADER_LENGTH) { - /* QUIC: either create a special ssl_read_bytes... or if/else this */ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &recvd_type, &p[s->init_num], SSL3_HM_HEADER_LENGTH - s->init_num, diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index d26f243a7c775..cefbebdd92852 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -95,7 +95,9 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst); __owur int tls_get_message_header(SSL *s, int *mt); __owur int tls_get_message_body(SSL *s, size_t *len); __owur int dtls_get_message(SSL *s, int *mt, size_t *len); +#ifndef OPENSSL_NO_QUIC __owur int quic_get_message(SSL *s, int *mt, size_t *len); +#endif /* Message construction and processing functions */ __owur int tls_process_initial_server_flight(SSL *s); diff --git a/ssl/statem/statem_quic.c b/ssl/statem/statem_quic.c index eb1a76ec9d814..a2ba29337c049 100644 --- a/ssl/statem/statem_quic.c +++ b/ssl/statem/statem_quic.c @@ -11,10 +11,6 @@ #include "statem_local.h" #include "internal/cryptlib.h" -#ifdef OPENSSL_NO_QUIC -NON_EMPTY_TRANSLATION_UNIT -#else - int quic_get_message(SSL *s, int *mt, size_t *len) { size_t l; @@ -23,7 +19,14 @@ int quic_get_message(SSL *s, int *mt, size_t *len) if (qd == NULL || (qd->length - qd->offset) != 0) { s->rwstate = SSL_READING; - *len = 0; + *mt = *len = 0; + return 0; + } + + if (!ossl_assert(qd->length >= SSL3_HM_HEADER_LENGTH)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, + SSL_R_BAD_LENGTH); + *mt = *len = 0; return 0; } @@ -31,14 +34,14 @@ int quic_get_message(SSL *s, int *mt, size_t *len) if (qd->level != s->quic_read_level) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); - *len = 0; + *mt = *len = 0; return 0; } if (!BUF_MEM_grow_clean(s->init_buf, (int)qd->length)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, ERR_R_BUF_LIB); - *len = 0; + *mt = *len = 0; return 0; } @@ -83,8 +86,8 @@ int quic_get_message(SSL *s, int *mt, size_t *len) */ #define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2) /* 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_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, @@ -105,5 +108,3 @@ int quic_get_message(SSL *s, int *mt, size_t *len) return 1; } - -#endif diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index 89678d6c22167..f73b97fbe496b 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -453,6 +453,7 @@ static const unsigned char exporter_master_secret[] = "exp master"; static const unsigned char resumption_master_secret[] = "res master"; static const unsigned char early_exporter_master_secret[] = "e exp master"; #endif + #ifndef OPENSSL_NO_QUIC static int quic_change_cipher_state(SSL *s, int which) { @@ -461,7 +462,7 @@ static int quic_change_cipher_state(SSL *s, int which) int hashleni; int ret = 0; const EVP_MD *md = NULL; - OSSL_ENCRYPTION_LEVEL level = ssl_encryption_initial; + OSSL_ENCRYPTION_LEVEL level; int is_handshake = ((which & SSL3_CC_HANDSHAKE) == SSL3_CC_HANDSHAKE); int is_client_read = ((which & SSL3_CHANGE_CIPHER_CLIENT_READ) == SSL3_CHANGE_CIPHER_CLIENT_READ); int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); @@ -485,34 +486,62 @@ static int quic_change_cipher_state(SSL *s, int which) if (is_client_read || is_server_write) { if (is_handshake) { + /* + * This looks a bit weird, since the condition is basically "the + * server is writing" but we set both the server *and* client + * handshake traffic keys here. That's because there's only a fixed + * number of change-cipher-state events in the TLS 1.3 handshake, + * and in particular there's not an event in between when the server + * writes encrypted handshake messages and when the client writes + * encrypted handshake messages, so we generate both here. + */ level = ssl_encryption_handshake; - if (!tls13_hkdf_expand(s, md, s->handshake_secret, client_handshake_traffic, - sizeof(client_handshake_traffic)-1, hash, hashlen, - s->client_hand_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL, s->client_hand_traffic_secret, hashlen) - || !tls13_derive_finishedkey(s, md, s->client_hand_traffic_secret, + if (!tls13_hkdf_expand(s, md, s->handshake_secret, + client_handshake_traffic, + sizeof(client_handshake_traffic)-1, hash, + hashlen, s->client_hand_traffic_secret, + hashlen, 1) + || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL, + s->client_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, + s->client_hand_traffic_secret, s->client_finished_secret, hashlen) - || !tls13_hkdf_expand(s, md, s->handshake_secret, server_handshake_traffic, - sizeof(server_handshake_traffic)-1, hash, hashlen, - s->server_hand_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL, s->server_hand_traffic_secret, hashlen) - || !tls13_derive_finishedkey(s, md, s->server_hand_traffic_secret, - s->server_finished_secret, hashlen)) { + || !tls13_hkdf_expand(s, md, s->handshake_secret, + server_handshake_traffic, + sizeof(server_handshake_traffic)-1, hash, + hashlen, s->server_hand_traffic_secret, + hashlen, 1) + || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL, + s->server_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, + s->server_hand_traffic_secret, + s->server_finished_secret, + hashlen)) { /* SSLfatal() already called */ goto err; } } else { + /* + * As above, we generate both sets of application traffic keys at + * the same time. + */ level = ssl_encryption_application; - if (!tls13_hkdf_expand(s, md, s->master_secret, client_application_traffic, - sizeof(client_application_traffic)-1, hash, hashlen, - s->client_app_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL, s->client_app_traffic_secret, hashlen) - || !tls13_hkdf_expand(s, md, s->master_secret, server_application_traffic, - sizeof(server_application_traffic)-1, hash, hashlen, + if (!tls13_hkdf_expand(s, md, s->master_secret, + client_application_traffic, + sizeof(client_application_traffic)-1, hash, + hashlen, s->client_app_traffic_secret, + hashlen, 1) + || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL, + s->client_app_traffic_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->master_secret, + server_application_traffic, + sizeof(server_application_traffic)-1, + hash, hashlen, s->server_app_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen)) { + || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, + s->server_app_traffic_secret, hashlen)) { /* SSLfatal() already called */ goto err; } @@ -532,9 +561,11 @@ static int quic_change_cipher_state(SSL *s, int which) level = ssl_encryption_early_data; if (!tls13_hkdf_expand(s, md, s->early_secret, client_early_traffic, - sizeof(client_early_traffic)-1, hash, hashlen, - s->client_early_traffic_secret, hashlen, 1) - || !ssl_log_secret(s, CLIENT_EARLY_LABEL, s->client_early_traffic_secret, hashlen) + sizeof(client_early_traffic)-1, hash, + hashlen, s->client_early_traffic_secret, + hashlen, 1) + || !ssl_log_secret(s, CLIENT_EARLY_LABEL, + s->client_early_traffic_secret, hashlen) || !quic_set_encryption_secrets(s, level)) { /* SSLfatal() already called */ goto err; @@ -547,9 +578,11 @@ static int quic_change_cipher_state(SSL *s, int which) * We also create the resumption master secret, but this time use the * hash for the whole handshake including the Client Finished */ - if (!tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, - sizeof(resumption_master_secret)-1, hash, hashlen, - s->resumption_master_secret, hashlen, 1)) { + if (!tls13_hkdf_expand(s, md, s->master_secret, + resumption_master_secret, + sizeof(resumption_master_secret)-1, hash, + hashlen, s->resumption_master_secret, + hashlen, 1)) { /* SSLfatal() already called */ goto err; } @@ -566,6 +599,7 @@ static int quic_change_cipher_state(SSL *s, int which) return ret; } #endif /* OPENSSL_NO_QUIC */ + int tls13_change_cipher_state(SSL *s, int which) { unsigned char *iv; @@ -832,7 +866,6 @@ int tls13_change_cipher_state(SSL *s, int which) s->statem.enc_write_state = ENC_WRITE_STATE_WRITE_PLAIN_ALERTS; else s->statem.enc_write_state = ENC_WRITE_STATE_VALID; - ret = 1; err: OPENSSL_cleanse(secret, sizeof(secret)); diff --git a/test/sslapitest.c b/test/sslapitest.c index e3bf6f6503468..925f2753e3c59 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6831,9 +6831,11 @@ static int test_inherit_verify_param(void) #ifndef OPENSSL_NO_QUIC -static int test_quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, +static int test_quic_set_encryption_secrets(SSL *ssl, + OSSL_ENCRYPTION_LEVEL level, const uint8_t *read_secret, - const uint8_t *write_secret, size_t secret_len) + const uint8_t *write_secret, + size_t secret_len) { test_printf_stderr("quic_set_encryption_secrets() %s, lvl=%d, len=%zd\n", ssl->server ? "server" : "client", level, secret_len); @@ -6844,11 +6846,12 @@ static int test_quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, { SSL *peer = (SSL*)SSL_get_app_data(ssl); - test_printf_stderr("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", - ssl->server ? "server" : "client", level, (int)*data, len); + TEST_info("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", + ssl->server ? "server" : "client", level, (int)*data, len); if (!TEST_ptr(peer)) return 0; + /* We're called with what is locally written; this gives it to the peer */ if (!TEST_true(SSL_provide_quic_data(peer, level, data, len))) { ERR_print_errors_fp(stderr); return 0; diff --git a/util/libssl.num b/util/libssl.num index a0a8dfa185d29..9a19d1757d292 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -500,7 +500,7 @@ SSL_CTX_set_post_handshake_auth 500 1_1_1 EXIST::FUNCTION: SSL_get_signature_type_nid 501 1_1_1a EXIST::FUNCTION: SSL_quic_read_level 20000 1_1_1i EXIST::FUNCTION:QUIC SSL_set_quic_transport_params 20001 1_1_1i EXIST::FUNCTION:QUIC -SSL_CIPHER_get_prf_nid 20002 1_1_1i EXIST::FUNCTION: +SSL_CIPHER_get_prf_nid 20002 1_1_1i EXIST::FUNCTION:QUIC SSL_is_quic 20003 1_1_1i EXIST::FUNCTION:QUIC SSL_get_peer_quic_transport_params 20004 1_1_1i EXIST::FUNCTION:QUIC SSL_quic_write_level 20005 1_1_1i EXIST::FUNCTION:QUIC From 9e4613893c3ea3336255c7398a39ebcad7477022 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 11 May 2020 13:13:01 -0700 Subject: [PATCH 07/28] QUIC: Prevent KeyUpdate for QUIC QUIC does not use the TLS KeyUpdate message/mechanism, and indeed it is an error to generate or receive such a message. Add the necessary checks (noting that the check for receipt should be redundant since SSL_provide_quic_data() is the only way to provide input to the TLS layer for a QUIC connection). (cherry picked from commit fea9d3bdf12ccb69eaf203799134abec02554839) --- ssl/ssl_quic.c | 6 ++++++ ssl/statem/statem_lib.c | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index 695238016d5a4..c7b6f1f5c1b81 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -92,6 +92,7 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const uint8_t *data, size_t len) { size_t l; + uint8_t mt; if (!SSL_IS_QUIC(ssl)) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -131,9 +132,14 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, return 0; } /* TLS Handshake message header has 1-byte type and 3-byte length */ + mt = *data; p = data + 1; n2l3(p, l); l += SSL3_HM_HEADER_LENGTH; + if (mt == SSL3_MT_KEY_UPDATE) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_UNEXPECTED_MESSAGE); + return 0; + } qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); if (qd == NULL) { diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index f84d4ebca624e..a02852be1a7bd 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -630,6 +630,14 @@ int tls_construct_finished(SSL *s, WPACKET *pkt) int tls_construct_key_update(SSL *s, WPACKET *pkt) { +#ifndef OPENSSL_NO_QUIC + if (SSL_is_quic(s)) { + /* TLS KeyUpdate is not used for QUIC, so this is an error. */ + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_KEY_UPDATE, + ERR_R_INTERNAL_ERROR); + return 0; + } +#endif if (!WPACKET_put_bytes_u8(pkt, s->key_update)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_KEY_UPDATE, ERR_R_INTERNAL_ERROR); @@ -654,6 +662,14 @@ MSG_PROCESS_RETURN tls_process_key_update(SSL *s, PACKET *pkt) return MSG_PROCESS_ERROR; } +#ifndef OPENSSL_NO_QUIC + if (SSL_is_quic(s)) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_KEY_UPDATE, + SSL_R_UNEXPECTED_MESSAGE); + return MSG_PROCESS_ERROR; + } +#endif + if (!PACKET_get_1(pkt, &updatetype) || PACKET_remaining(pkt) != 0) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_KEY_UPDATE, From 3497e1d72ded49f753e815e0c5f6ca6352d829ed Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 11 May 2020 13:26:07 -0700 Subject: [PATCH 08/28] QUIC: Test KeyUpdate rejection For now, just test that we don't generate any, since we don't really expose the mechanics for encrypting one and the QUIC API is not integrated into the TLSProxy setup. (cherry picked from commit 67ac3a279919142b198f7bd4c69963fd60de6ecf) --- test/sslapitest.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/sslapitest.c b/test/sslapitest.c index 925f2753e3c59..79ae6618bc0db 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6949,6 +6949,17 @@ static int test_quic_api(void) || !TEST_true(SSL_process_quic_post_handshake(clientssl))) goto end; + /* Dummy handshake call should succeed */ + if (!TEST_true(SSL_do_handshake(clientssl))) + goto end; + /* Test that we (correctly) fail to send KeyUpdate */ + if (!TEST_true(SSL_key_update(clientssl, SSL_KEY_UPDATE_NOT_REQUESTED)) + || !TEST_int_le(SSL_do_handshake(clientssl), 0)) + goto end; + if (!TEST_true(SSL_key_update(serverssl, SSL_KEY_UPDATE_NOT_REQUESTED)) + || !TEST_int_le(SSL_do_handshake(serverssl), 0)) + goto end; + testresult = 1; end: From f6628e153e985e7beeafb8c8cc9137b399e591fa Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Thu, 21 May 2020 12:53:59 -0700 Subject: [PATCH 09/28] QUIC: Test HKDF with empty IKM Add an extra EVP test that provides empty input key material. It currently fails, since attempting to set a zero-length key on an EVP_PKEY_CTX results in a call to OPENSSL_memdup() with length zero, which returns NULL and is detected as failure. (cherry picked from commit 232c9a188cad4bd262af13d66b56fb3c344cd6b7) --- test/evp_extra_test.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 3eea4b0fba981..2a5eefb1044db 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -1626,6 +1626,47 @@ static int test_HKDF(void) return ret; } +static int test_emptyikm_HKDF(void) +{ + EVP_PKEY_CTX *pctx; + unsigned char out[20]; + size_t outlen; + int ret = 0; + unsigned char salt[] = "9876543210"; + unsigned char key[] = ""; + unsigned char info[] = "stringinfo"; + const unsigned char expected[] = { + 0x68, 0x81, 0xa5, 0x3e, 0x5b, 0x9c, 0x7b, 0x6f, 0x2e, 0xec, 0xc8, 0x47, + 0x7c, 0xfa, 0x47, 0x35, 0x66, 0x82, 0x15, 0x30 + }; + size_t expectedlen = sizeof(expected); + + if (!TEST_ptr(pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL))) + goto done; + + outlen = sizeof(out); + memset(out, 0, outlen); + + if (!TEST_int_gt(EVP_PKEY_derive_init(pctx), 0) + || !TEST_int_gt(EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()), 0) + || !TEST_int_gt(EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, + sizeof(salt) - 1), 0) + || !TEST_int_gt(EVP_PKEY_CTX_set1_hkdf_key(pctx, key, + sizeof(key) - 1), 0) + || !TEST_int_gt(EVP_PKEY_CTX_add1_hkdf_info(pctx, info, + sizeof(info) - 1), 0) + || !TEST_int_gt(EVP_PKEY_derive(pctx, out, &outlen), 0) + || !TEST_mem_eq(out, outlen, expected, expectedlen)) + goto done; + + ret = 1; + + done: + EVP_PKEY_CTX_free(pctx); + + return ret; +} + #ifndef OPENSSL_NO_EC static int test_X509_PUBKEY_inplace(void) { @@ -1784,6 +1825,7 @@ int setup_tests(void) return 0; ADD_ALL_TESTS(test_EVP_PKEY_check, OSSL_NELEM(keycheckdata)); ADD_TEST(test_HKDF); + ADD_TEST(test_emptyikm_HKDF); #ifndef OPENSSL_NO_EC ADD_TEST(test_X509_PUBKEY_inplace); ADD_ALL_TESTS(test_invalide_ec_char2_pub_range_decode, From 98d9fa59c91c6bfaf14ac8efc7b78026d522386c Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Fri, 22 May 2020 13:23:49 -0700 Subject: [PATCH 10/28] QUIC: Allow zero-length HKDF keys When making a copy to keep in the EVP_PKEY_CTX, allocate a single byte for the cached key instead of letting memdup return NULL and cause the call to fail. The length still gets set to zero properly, so we don't end up inspecting the allocated byte, but it's important to have a non-NULL pointer set. (cherry picked from commit 93f2e101bd73ee6c215b75b50ef96a74372dd44e) --- crypto/kdf/hkdf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/kdf/hkdf.c b/crypto/kdf/hkdf.c index 6d1a32c885ecf..cab5e231fbe63 100644 --- a/crypto/kdf/hkdf.c +++ b/crypto/kdf/hkdf.c @@ -107,7 +107,10 @@ static int pkey_hkdf_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) if (kctx->key != NULL) OPENSSL_clear_free(kctx->key, kctx->key_len); - kctx->key = OPENSSL_memdup(p2, p1); + if (p1 == 0) + kctx->key = OPENSSL_zalloc(1); + else + kctx->key = OPENSSL_memdup(p2, p1); if (kctx->key == NULL) return 0; From 0740c85c5c9a574753fa2149920de11c4f259ed9 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 31 Aug 2020 12:27:33 -0700 Subject: [PATCH 11/28] QUIC: Buffer all provided quic data Make all data supplied via SSL_provide_quic_data() pass through an internal buffer, so that we can handle data supplied with arbitrary framing and only parse complete TLS records onto the list of QUIC_DATA managed by quic_input_data_head/quic_input_data_tail. This lets us remove the concept of "incomplete" QUIC_DATA structures, and the 'offset' field needed to support them. However, we've already moved the provided data onto the buffer by the time we can check for KeyUpdate messages, so defer that check to quic_get_message() (where it is adjacent to the preexisting ChangeCipherSpec check). To avoid extra memory copies, we also make the QUIC_DATA structures just store offsets into the consolidated buffer instead of having copies of the TLS handshake messages themselves. (cherry picked from commit 0bbcd60a1e7ff8b88c7cccda23d5ab0a0e423170) --- ssl/ssl_lib.c | 1 + ssl/ssl_local.h | 5 +-- ssl/ssl_quic.c | 75 +++++++++++++++++++--------------------- ssl/statem/statem_quic.c | 12 +++++-- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index c5c5614ee1cf0..ff05535f3d589 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1219,6 +1219,7 @@ void SSL_free(SSL *s) #ifndef OPENSSL_NO_QUIC OPENSSL_free(s->ext.quic_transport_params); OPENSSL_free(s->ext.peer_quic_transport_params); + BUF_MEM_free(s->quic_buf); while (s->quic_input_data_head != NULL) { QUIC_DATA *qd; diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 6dc603c450dc0..59a49bbee63f8 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1083,9 +1083,8 @@ typedef struct cert_pkey_st CERT_PKEY; struct quic_data_st { struct quic_data_st *next; OSSL_ENCRYPTION_LEVEL level; - size_t offset; + size_t start; /* offset into quic_buf->data */ size_t length; - /* char data[]; should be here but C90 VLAs not allowed here */ }; typedef struct quic_data_st QUIC_DATA; int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); @@ -1407,8 +1406,10 @@ struct ssl_st { #ifndef OPENSSL_NO_QUIC OSSL_ENCRYPTION_LEVEL quic_read_level; OSSL_ENCRYPTION_LEVEL quic_write_level; + BUF_MEM *quic_buf; /* buffer incoming handshake messages */ QUIC_DATA *quic_input_data_head; QUIC_DATA *quic_input_data_tail; + size_t quic_next_record_start; const SSL_QUIC_METHOD *quic_method; #endif /* diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index c7b6f1f5c1b81..bc3c220067516 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -91,8 +91,7 @@ OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl) int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const uint8_t *data, size_t len) { - size_t l; - uint8_t mt; + size_t l, offset; if (!SSL_IS_QUIC(ssl)) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -106,42 +105,46 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, return 0; } - /* Split on handshake message boundaries, if necessary */ - while (len > 0) { - QUIC_DATA *qd; - const uint8_t *p; - - /* Check for an incomplete block */ - qd = ssl->quic_input_data_tail; - if (qd != NULL) { - l = qd->length - qd->offset; - if (l != 0) { - /* we still need to copy `l` bytes into the last data block */ - if (l > len) - l = len; - memcpy((char*)(qd+1) + qd->offset, data, l); - qd->offset += l; - len -= l; - data += l; - continue; - } + if (ssl->quic_buf == NULL) { + BUF_MEM *buf; + if ((buf = BUF_MEM_new()) == NULL) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; } - - if (len < SSL3_HM_HEADER_LENGTH) { - SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_BAD_LENGTH); + if (!BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + BUF_MEM_free(buf); return 0; } + ssl->quic_buf = buf; + /* We preallocated storage, but there's still no *data*. */ + ssl->quic_buf->length = 0; + buf = NULL; + } + + offset = ssl->quic_buf->length; + if (!BUF_MEM_grow(ssl->quic_buf, offset + len)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; + } + memcpy(ssl->quic_buf->data + offset, data, len); + + /* Split on handshake message boundaries */ + while (ssl->quic_buf->length > ssl->quic_next_record_start + + SSL3_HM_HEADER_LENGTH) { + QUIC_DATA *qd; + const uint8_t *p; + /* TLS Handshake message header has 1-byte type and 3-byte length */ - mt = *data; - p = data + 1; + p = (const uint8_t *)ssl->quic_buf->data + + ssl->quic_next_record_start + 1; n2l3(p, l); l += SSL3_HM_HEADER_LENGTH; - if (mt == SSL3_MT_KEY_UPDATE) { - SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_UNEXPECTED_MESSAGE); - return 0; - } + /* Don't allocate a QUIC_DATA if we don't have a full record */ + if (l > ssl->quic_buf->length - ssl->quic_next_record_start) + break; - qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); + qd = OPENSSL_zalloc(sizeof(*qd)); if (qd == NULL) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); return 0; @@ -149,21 +152,15 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, qd->next = NULL; qd->length = l; + qd->start = ssl->quic_next_record_start; qd->level = level; - /* partial data received? */ - if (l > len) - l = len; - qd->offset = l; - memcpy((void*)(qd + 1), data, l); if (ssl->quic_input_data_tail != NULL) ssl->quic_input_data_tail->next = qd; else ssl->quic_input_data_head = qd; ssl->quic_input_data_tail = qd; - - data += l; - len -= l; + ssl->quic_next_record_start += l; } return 1; diff --git a/ssl/statem/statem_quic.c b/ssl/statem/statem_quic.c index a2ba29337c049..2843938ce2ef4 100644 --- a/ssl/statem/statem_quic.c +++ b/ssl/statem/statem_quic.c @@ -17,7 +17,7 @@ int quic_get_message(SSL *s, int *mt, size_t *len) QUIC_DATA *qd = s->quic_input_data_head; uint8_t *p; - if (qd == NULL || (qd->length - qd->offset) != 0) { + if (qd == NULL) { s->rwstate = SSL_READING; *mt = *len = 0; return 0; @@ -46,7 +46,7 @@ int quic_get_message(SSL *s, int *mt, size_t *len) } /* Copy buffered data */ - memcpy(s->init_buf->data, (void*)(qd + 1), qd->length); + memcpy(s->init_buf->data, s->quic_buf->data + qd->start, qd->length); s->init_buf->length = qd->length; s->quic_input_data_head = qd->next; if (s->quic_input_data_head == NULL) @@ -67,6 +67,14 @@ int quic_get_message(SSL *s, int *mt, size_t *len) *len = 0; return 0; } + /* No KeyUpdate in QUIC */ + if (*mt == SSL3_MT_KEY_UPDATE) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_QUIC_GET_MESSAGE, + SSL_R_UNEXPECTED_MESSAGE); + *len = 0; + return 0; + } + /* * If receiving Finished, record MAC of prior handshake messages for From 047a5a26fb6995ca1f4ff2115d1838ca1f61ca58 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Tue, 1 Sep 2020 15:10:41 -0700 Subject: [PATCH 12/28] QUIC: enforce consistent encryption level for handshake messages The QUIC-TLS spec requires that TLS handshake messages do not cross encryption level boundaries, but we were not previously enforcing this. (cherry picked from commit 5b76e4fb7bdc2165edcbd335599b79306d8402aa) --- ssl/ssl_local.h | 1 + ssl/ssl_quic.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 59a49bbee63f8..f53608c373538 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1406,6 +1406,7 @@ struct ssl_st { #ifndef OPENSSL_NO_QUIC OSSL_ENCRYPTION_LEVEL quic_read_level; OSSL_ENCRYPTION_LEVEL quic_write_level; + OSSL_ENCRYPTION_LEVEL quic_latest_level_received; BUF_MEM *quic_buf; /* buffer incoming handshake messages */ QUIC_DATA *quic_input_data_head; QUIC_DATA *quic_input_data_tail; diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index bc3c220067516..b79a40a8b629f 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -100,7 +100,8 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, /* Level can be different than the current read, but not less */ if (level < ssl->quic_read_level - || (ssl->quic_input_data_tail != NULL && level < ssl->quic_input_data_tail->level)) { + || (ssl->quic_input_data_tail != NULL && level < ssl->quic_input_data_tail->level) + || level < ssl->quic_latest_level_received) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); return 0; } @@ -122,6 +123,15 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, buf = NULL; } + /* A TLS message must not cross an encryption level boundary */ + if (ssl->quic_buf->length != ssl->quic_next_record_start + && level != ssl->quic_latest_level_received) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, + SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + return 0; + } + ssl->quic_latest_level_received = level; + offset = ssl->quic_buf->length; if (!BUF_MEM_grow(ssl->quic_buf, offset + len)) { SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); From f156af6b949e7af5e8e62f8b887689c11679c755 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Sat, 12 Dec 2020 17:27:46 +0900 Subject: [PATCH 13/28] QUIC: add v1 quic_transport_parameters (cherry picked from commit 8f4f7f2b3adebe5f6e23a8c0f5684feae5574ae2) --- crypto/err/openssl.txt | 11 +++ doc/man3/SSL_CTX_set_quic_method.pod | 25 +++++- include/openssl/ssl.h | 13 ++++ include/openssl/sslerr.h | 6 ++ include/openssl/tls1.h | 3 +- ssl/ssl_err.c | 12 +++ ssl/ssl_lib.c | 1 + ssl/ssl_local.h | 11 +++ ssl/ssl_quic.c | 41 +++++++++- ssl/statem/extensions.c | 40 ++++++++++ ssl/statem/extensions_clnt.c | 46 ++++++++++- ssl/statem/extensions_srvr.c | 48 +++++++++++- ssl/statem/statem_local.h | 17 ++++ test/sslapitest.c | 111 +++++++++++++++++++++------ util/libssl.num | 4 + 15 files changed, 352 insertions(+), 37 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 65d88597b7be2..66cc25ab04db0 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1161,6 +1161,7 @@ SSL_F_FINAL_EMS:486:final_ems SSL_F_FINAL_KEY_SHARE:503:final_key_share SSL_F_FINAL_MAXFRAGMENTLEN:557:final_maxfragmentlen SSL_F_FINAL_PSK:639:final_psk +SSL_F_FINAL_QUIC_TRANSPORT_PARAMS:3012:final_quic_transport_params SSL_F_FINAL_RENEGOTIATE:483:final_renegotiate SSL_F_FINAL_SERVER_NAME:558:final_server_name SSL_F_FINAL_SIG_ALGS:497:final_sig_algs @@ -1433,6 +1434,8 @@ 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_QUIC_TRANSPORT_PARAMS:3008:\ tls_construct_ctos_quic_transport_params +SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT:3013:\ + tls_construct_ctos_quic_transport_params_draft SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE:473:tls_construct_ctos_renegotiate SSL_F_TLS_CONSTRUCT_CTOS_SCT:474:tls_construct_ctos_sct SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME:475:tls_construct_ctos_server_name @@ -1476,6 +1479,8 @@ SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG:457:tls_construct_stoc_next_proto_neg SSL_F_TLS_CONSTRUCT_STOC_PSK:504:tls_construct_stoc_psk SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS:3009:\ tls_construct_stoc_quic_transport_params +SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT:3014:\ + tls_construct_stoc_quic_transport_params_draft SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE:458:tls_construct_stoc_renegotiate SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME:459:tls_construct_stoc_server_name SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET:460:tls_construct_stoc_session_ticket @@ -1506,6 +1511,8 @@ 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_QUIC_TRANSPORT_PARAMS:3010:\ tls_parse_ctos_quic_transport_params +SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT:3015:\ + tls_parse_ctos_quic_transport_params_draft SSL_F_TLS_PARSE_CTOS_RENEGOTIATE:464:tls_parse_ctos_renegotiate SSL_F_TLS_PARSE_CTOS_SERVER_NAME:573:tls_parse_ctos_server_name SSL_F_TLS_PARSE_CTOS_SESSION_TICKET:574:tls_parse_ctos_session_ticket @@ -1526,6 +1533,8 @@ SSL_F_TLS_PARSE_STOC_NPN:582:tls_parse_stoc_npn SSL_F_TLS_PARSE_STOC_PSK:502:tls_parse_stoc_psk SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS:3011:\ tls_parse_stoc_quic_transport_params +SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT:3016:\ + tls_parse_stoc_quic_transport_params_draft SSL_F_TLS_PARSE_STOC_RENEGOTIATE:448:tls_parse_stoc_renegotiate SSL_F_TLS_PARSE_STOC_SCT:564:tls_parse_stoc_sct SSL_F_TLS_PARSE_STOC_SERVER_NAME:583:tls_parse_stoc_server_name @@ -2761,6 +2770,8 @@ SSL_R_MISSING_ECDSA_SIGNING_CERT:381:missing ecdsa signing cert SSL_R_MISSING_FATAL:256:missing fatal SSL_R_MISSING_PARAMETERS:290:missing parameters SSL_R_MISSING_PSK_KEX_MODES_EXTENSION:310:missing psk kex modes extension +SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION:801:\ + missing quic transport parameters extension SSL_R_MISSING_RSA_CERTIFICATE:168:missing rsa certificate SSL_R_MISSING_RSA_ENCRYPTING_CERT:169:missing rsa encrypting cert SSL_R_MISSING_RSA_SIGNING_CERT:170:missing rsa signing cert diff --git a/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod index 3d7bf7e682137..39ff3a8da4517 100644 --- a/doc/man3/SSL_CTX_set_quic_method.pod +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -13,7 +13,11 @@ SSL_quic_read_level, SSL_quic_write_level, SSL_provide_quic_data, SSL_process_quic_post_handshake, -SSL_is_quic +SSL_is_quic, +SSL_get_peer_quic_transport_version, +SSL_get_quic_transport_version, +SSL_set_quic_transport_version, +SSL_set_quic_use_legacy_codepoint - QUIC support =head1 SYNOPSIS @@ -39,6 +43,11 @@ SSL_is_quic int SSL_process_quic_post_handshake(SSL *ssl); int SSL_is_quic(SSL *ssl); + void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy); + void SSL_set_quic_transport_version(SSL *ssl, int version); + int SSL_get_quic_transport_version(const SSL *ssl); + int SSL_get_peer_quic_transport_version(const SSL *ssl); + =head1 DESCRIPTION SSL_CTX_set_quic_method() and SSL_set_quic_method() configures the QUIC methods. @@ -83,6 +92,20 @@ sent by the server. SSL_is_quic() indicates whether a connection uses QUIC. A given B or B can only be used with QUIC or TLS, but not both. +SSL_set_quic_use_legacy_codepoint() specifies the legacy extension codepoint +in manner compatible with some versions of BoringSSL. + +SSL_set_quic_transport_version() specifies the quic transport version that +allows for backwards and forwards compatibility. If set to 0 (default) the +server will use the highest version the client sent. If set to 0 (default) +the client will send both extensions. + +SSL_get_quic_transport_version() returns the value set by +SSL_set_quic_transport_version(). + +SSL_get_peer_quic_transport_version() returns the version the that was +negotiated. + =head1 NOTES These APIs are implementations of BoringSSL's QUIC APIs. diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 08d4c74b237cc..21d77d9ea8ad8 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2473,6 +2473,19 @@ __owur int SSL_process_quic_post_handshake(SSL *ssl); __owur int SSL_is_quic(SSL *ssl); +/* BoringSSL API */ +void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy); + +/* + * Set an explicit value that you want to use + * If 0 (default) the server will use the highest extenstion the client sent + * If 0 (default) the client will send both extensions + */ +void SSL_set_quic_transport_version(SSL *ssl, int version); +__owur int SSL_get_quic_transport_version(const SSL *ssl); +/* Returns the negotiated version, or -1 on error */ +__owur int SSL_get_peer_quic_transport_version(const SSL *ssl); + int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); # endif diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index e31bf3ccf61a6..64e152cc517d5 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -71,6 +71,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_FINAL_KEY_SHARE 503 # define SSL_F_FINAL_MAXFRAGMENTLEN 557 # define SSL_F_FINAL_PSK 639 +# define SSL_F_FINAL_QUIC_TRANSPORT_PARAMS 3012 # define SSL_F_FINAL_RENEGOTIATE 483 # define SSL_F_FINAL_SERVER_NAME 558 # define SSL_F_FINAL_SIG_ALGS 497 @@ -329,6 +330,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_CTOS_PSK 501 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES 509 # define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS 3008 +# define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT 3013 # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE 473 # define SSL_F_TLS_CONSTRUCT_CTOS_SCT 474 # define SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME 475 @@ -369,6 +371,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457 # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504 # define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS 3009 +# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT 3014 # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458 # define SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME 459 # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET 460 @@ -395,6 +398,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_CTOS_PSK 505 # define SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES 572 # define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS 3010 +# define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT 3015 # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464 # define SSL_F_TLS_PARSE_CTOS_SERVER_NAME 573 # define SSL_F_TLS_PARSE_CTOS_SESSION_TICKET 574 @@ -414,6 +418,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_STOC_NPN 582 # define SSL_F_TLS_PARSE_STOC_PSK 502 # define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS 3011 +# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT 3016 # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448 # define SSL_F_TLS_PARSE_STOC_SCT 564 # define SSL_F_TLS_PARSE_STOC_SERVER_NAME 583 @@ -606,6 +611,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_MISSING_FATAL 256 # define SSL_R_MISSING_PARAMETERS 290 # define SSL_R_MISSING_PSK_KEX_MODES_EXTENSION 310 +# define SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION 801 # define SSL_R_MISSING_RSA_CERTIFICATE 168 # define SSL_R_MISSING_RSA_ENCRYPTING_CERT 169 # define SSL_R_MISSING_RSA_SIGNING_CERT 170 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index c11ca1efa30c9..2cbf53265f871 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -149,7 +149,8 @@ extern "C" { # define TLSEXT_TYPE_renegotiate 0xff01 /* ExtensionType value from draft-ietf-quic-tls-27 */ -# define TLSEXT_TYPE_quic_transport_parameters 0xffa5 +# define TLSEXT_TYPE_quic_transport_parameters_draft 0xffa5 +# define TLSEXT_TYPE_quic_transport_parameters 0x0039 # ifndef OPENSSL_NO_NEXTPROTONEG /* This is not an IANA defined extension number */ diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 094c5751006d2..56adfdca42a55 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -86,6 +86,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_MAXFRAGMENTLEN, 0), "final_maxfragmentlen"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_PSK, 0), "final_psk"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_QUIC_TRANSPORT_PARAMS, 0), + "final_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_RENEGOTIATE, 0), "final_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_SERVER_NAME, 0), "final_server_name"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_FINAL_SIG_ALGS, 0), "final_sig_algs"}, @@ -497,6 +499,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_ctos_psk_kex_modes"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, 0), "tls_construct_ctos_quic_transport_params"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, 0), + "tls_construct_ctos_quic_transport_params_draft"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE, 0), "tls_construct_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_SCT, 0), @@ -570,6 +574,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_stoc_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, 0), "tls_construct_stoc_quic_transport_params"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, 0), + "tls_construct_stoc_quic_transport_params_draft"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE, 0), "tls_construct_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME, 0), @@ -618,6 +624,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_parse_ctos_psk_kex_modes"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, 0), "tls_parse_ctos_quic_transport_params"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, 0), + "tls_parse_ctos_quic_transport_params_draft"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_RENEGOTIATE, 0), "tls_parse_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_SERVER_NAME, 0), @@ -652,6 +660,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_PSK, 0), "tls_parse_stoc_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, 0), "tls_parse_stoc_quic_transport_params"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, 0), + "tls_parse_stoc_quic_transport_params_draft"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_RENEGOTIATE, 0), "tls_parse_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SCT, 0), "tls_parse_stoc_sct"}, @@ -974,6 +984,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PARAMETERS), "missing parameters"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION), "missing psk kex modes extension"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION), + "missing quic transport parameters extension"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE), "missing rsa certificate"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index ff05535f3d589..65089bd78df43 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1218,6 +1218,7 @@ void SSL_free(SSL *s) #ifndef OPENSSL_NO_QUIC OPENSSL_free(s->ext.quic_transport_params); + OPENSSL_free(s->ext.peer_quic_transport_params_draft); OPENSSL_free(s->ext.peer_quic_transport_params); BUF_MEM_free(s->quic_buf); while (s->quic_input_data_head != NULL) { diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index f53608c373538..55632593916bf 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -721,6 +721,7 @@ typedef enum tlsext_index_en { TLSEXT_IDX_cryptopro_bug, TLSEXT_IDX_early_data, TLSEXT_IDX_certificate_authorities, + TLSEXT_IDX_quic_transport_params_draft, TLSEXT_IDX_quic_transport_params, TLSEXT_IDX_padding, TLSEXT_IDX_psk, @@ -1398,6 +1399,8 @@ struct ssl_st { #ifndef OPENSSL_NO_QUIC uint8_t *quic_transport_params; size_t quic_transport_params_len; + uint8_t *peer_quic_transport_params_draft; + size_t peer_quic_transport_params_draft_len; uint8_t *peer_quic_transport_params; size_t peer_quic_transport_params_len; #endif @@ -1407,6 +1410,14 @@ struct ssl_st { OSSL_ENCRYPTION_LEVEL quic_read_level; OSSL_ENCRYPTION_LEVEL quic_write_level; OSSL_ENCRYPTION_LEVEL quic_latest_level_received; + /* + * defaults to 0, but can be set to: + * - TLSEXT_TYPE_quic_transport_parameters_draft + * - TLSEXT_TYPE_quic_transport_parameters + * Client: if 0, send both + * Server: if 0, use same version as client sent + */ + int quic_transport_version; BUF_MEM *quic_buf; /* buffer incoming handshake messages */ QUIC_DATA *quic_input_data_head; QUIC_DATA *quic_input_data_tail; diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index b79a40a8b629f..f7171be140225 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -35,8 +35,45 @@ void SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params, size_t *out_params_len) { - *out_params = ssl->ext.peer_quic_transport_params; - *out_params_len = ssl->ext.peer_quic_transport_params_len; + if (ssl->ext.peer_quic_transport_params_len) { + *out_params = ssl->ext.peer_quic_transport_params; + *out_params_len = ssl->ext.peer_quic_transport_params_len; + } else { + *out_params = ssl->ext.peer_quic_transport_params_draft; + *out_params_len = ssl->ext.peer_quic_transport_params_draft_len; + } +} + +/* Returns the negotiated version, or -1 on error */ +int SSL_get_peer_quic_transport_version(const SSL *ssl) +{ + if (ssl->ext.peer_quic_transport_params_len != 0 + && ssl->ext.peer_quic_transport_params_draft_len != 0) + return -1; + if (ssl->ext.peer_quic_transport_params_len != 0) + return TLSEXT_TYPE_quic_transport_parameters; + if (ssl->ext.peer_quic_transport_params_draft_len != 0) + return TLSEXT_TYPE_quic_transport_parameters_draft; + + return -1; +} + +void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy) +{ + if (use_legacy) + ssl->quic_transport_version = TLSEXT_TYPE_quic_transport_parameters_draft; + else + ssl->quic_transport_version = TLSEXT_TYPE_quic_transport_parameters; +} + +void SSL_set_quic_transport_version(SSL *ssl, int version) +{ + ssl->quic_transport_version = version; +} + +int SSL_get_quic_transport_version(const SSL *ssl) +{ + return ssl->quic_transport_version; } size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level) diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index f7a395f4bbd57..3a6702cd6aad7 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -60,6 +60,7 @@ static int init_post_handshake_auth(SSL *s, unsigned int context); static int final_psk(SSL *s, unsigned int context, int sent); #ifndef OPENSSL_NO_QUIC static int init_quic_transport_params(SSL *s, unsigned int context); +static int final_quic_transport_params_draft(SSL *s, unsigned int context, int sent); static int final_quic_transport_params(SSL *s, unsigned int context, int sent); #endif @@ -382,6 +383,15 @@ static const EXTENSION_DEFINITION ext_defs[] = { tls_construct_certificate_authorities, NULL, }, #ifndef OPENSSL_NO_QUIC + { + TLSEXT_TYPE_quic_transport_parameters_draft, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS + | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, + init_quic_transport_params, + tls_parse_ctos_quic_transport_params_draft, tls_parse_stoc_quic_transport_params_draft, + tls_construct_stoc_quic_transport_params_draft, tls_construct_ctos_quic_transport_params_draft, + final_quic_transport_params_draft, + }, { TLSEXT_TYPE_quic_transport_parameters, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS @@ -1769,8 +1779,38 @@ static int init_quic_transport_params(SSL *s, unsigned int context) return 1; } +static int final_quic_transport_params_draft(SSL *s, unsigned int context, + int sent) +{ + return 1; +} + static int final_quic_transport_params(SSL *s, unsigned int context, int sent) { + /* called after final_quic_transport_params_draft */ + if (SSL_IS_QUIC(s)) { + if (s->ext.peer_quic_transport_params_len == 0 + && s->ext.peer_quic_transport_params_draft_len == 0) { + SSLfatal(s, SSL_AD_MISSING_EXTENSION, + SSL_F_FINAL_QUIC_TRANSPORT_PARAMS, + SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION); + return 0; + } + /* if we got both, discard the one we can't use */ + if (s->ext.peer_quic_transport_params_len != 0 + && s->ext.peer_quic_transport_params_draft_len != 0) { + if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft) { + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + } else { + OPENSSL_free(s->ext.peer_quic_transport_params_draft); + s->ext.peer_quic_transport_params_draft = NULL; + s->ext.peer_quic_transport_params_draft_len = 0; + } + } + } + return 1; } #endif diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index 9e48458679bb7..2b403dda50090 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1216,13 +1216,34 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, } #ifndef OPENSSL_NO_QUIC -/* SAME AS tls_construct_stoc_quic_transport_params() */ +EXT_RETURN tls_construct_ctos_quic_transport_params_draft(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters + || s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters_draft) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { - if (s->ext.quic_transport_params == NULL - || s->ext.quic_transport_params_len == 0) { + if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft + || s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { return EXT_RETURN_NOT_SENT; } @@ -2043,7 +2064,24 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 1; } #ifndef OPENSSL_NO_QUIC -/* SAME AS tls_parse_ctos_quic_transport_params() */ +int tls_parse_stoc_quic_transport_params_draft(SSL *s, PACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params_draft); + s->ext.peer_quic_transport_params_draft = NULL; + s->ext.peer_quic_transport_params_draft_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params_draft, + &s->ext.peer_quic_transport_params_draft_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} + int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index ff782bd53b76e..f47a09ed94287 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1304,7 +1304,23 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context } #ifndef OPENSSL_NO_QUIC -/* SAME AS tls_parse_stoc_quic_transport_params() */ +int tls_parse_ctos_quic_transport_params_draft(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params_draft); + s->ext.peer_quic_transport_params_draft = NULL; + s->ext.peer_quic_transport_params_draft_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params_draft, + &s->ext.peer_quic_transport_params_draft_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} + int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { @@ -2010,13 +2026,37 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, } #ifndef OPENSSL_NO_QUIC -/* SAME AS tls_construct_ctos_quic_transport_params() */ +EXT_RETURN tls_construct_stoc_quic_transport_params_draft(SSL *s, WPACKET *pkt, + unsigned int context, + X509 *x, + size_t chainidx) +{ + if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters + || s->ext.peer_quic_transport_params_draft_len == 0 + || s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters_draft) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS_DRAFT, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { - if (s->ext.quic_transport_params == NULL - || s->ext.quic_transport_params_len == 0) { + if (s->quic_transport_version == TLSEXT_TYPE_quic_transport_parameters_draft + || s->ext.peer_quic_transport_params_len == 0 + || s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { return EXT_RETURN_NOT_SENT; } diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index cefbebdd92852..7bf905f3dfe93 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -242,6 +242,10 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #ifndef OPENSSL_NO_QUIC +int tls_parse_ctos_quic_transport_params_draft(SSL *s, PACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); + int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #endif @@ -308,6 +312,11 @@ EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_stoc_quic_transport_params_draft(SSL *s, WPACKET *pkt, + unsigned int context, + X509 *x, + size_t chainidx); + EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); @@ -383,6 +392,10 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_ctos_quic_transport_params_draft(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); + EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); @@ -433,6 +446,10 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #ifndef OPENSSL_NO_QUIC +int tls_parse_stoc_quic_transport_params_draft(SSL *s, PACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); + int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); #endif diff --git a/test/sslapitest.c b/test/sslapitest.c index 79ae6618bc0db..167f4d3a83e0f 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6877,7 +6877,14 @@ static SSL_QUIC_METHOD quic_method = { test_quic_flush_flight, test_quic_send_alert, }; -static int test_quic_api(void) + +static int test_quic_api_set_versions(SSL *ssl, int ver) +{ + SSL_set_quic_transport_version(ssl, ver); + return 1; +} + +static int test_quic_api_version(int clnt, int srvr) { SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; @@ -6887,29 +6894,7 @@ static int test_quic_api(void) const uint8_t *peer_str; size_t peer_str_len; - /* Clean up logging space */ - memset(client_log_buffer, 0, sizeof(client_log_buffer)); - memset(server_log_buffer, 0, sizeof(server_log_buffer)); - client_log_buffer_index = 0; - server_log_buffer_index = 0; - error_writing_log = 0; - - - if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) - || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) - || !TEST_ptr(sctx->quic_method) - || !TEST_ptr(serverssl = SSL_new(sctx)) - || !TEST_true(SSL_IS_QUIC(serverssl)) - || !TEST_true(SSL_set_quic_method(serverssl, NULL)) - || !TEST_false(SSL_IS_QUIC(serverssl)) - || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) - || !TEST_true(SSL_IS_QUIC(serverssl))) - goto end; - - SSL_CTX_free(sctx); - sctx = NULL; - SSL_free(serverssl); - serverssl = NULL; + TEST_info("original clnt=0x%X, srvr=0x%X\n", clnt, srvr); if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), @@ -6927,6 +6912,8 @@ static int test_quic_api(void) sizeof(client_str))) || !TEST_true(SSL_set_app_data(serverssl, clientssl)) || !TEST_true(SSL_set_app_data(clientssl, serverssl)) + || !TEST_true(test_quic_api_set_versions(clientssl, clnt)) + || !TEST_true(test_quic_api_set_versions(serverssl, srvr)) || !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) || !TEST_true(SSL_version(serverssl) == TLS1_3_VERSION) @@ -6960,6 +6947,19 @@ static int test_quic_api(void) || !TEST_int_le(SSL_do_handshake(serverssl), 0)) goto end; + TEST_info("original clnt=0x%X, srvr=0x%X\n", clnt, srvr); + if (srvr == 0 && clnt == 0) + srvr = clnt = TLSEXT_TYPE_quic_transport_parameters; + else if (srvr == 0) + srvr = clnt; + else if (clnt == 0) + clnt = srvr; + TEST_info("expected clnt=0x%X, srvr=0x%X\n", clnt, srvr); + if (!TEST_int_eq(SSL_get_peer_quic_transport_version(serverssl), clnt)) + goto end; + if (!TEST_int_eq(SSL_get_peer_quic_transport_version(clientssl), srvr)) + goto end; + testresult = 1; end: @@ -6970,6 +6970,67 @@ static int test_quic_api(void) return testresult; } + +static int test_quic_api(int tst) +{ + SSL_CTX *sctx = NULL; + SSL *serverssl = NULL; + int testresult = 0; + static int clnt_params[] = { 0, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters, + 0, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters, + 0, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters }; + static int srvr_params[] = { 0, + 0, + 0, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters_draft, + TLSEXT_TYPE_quic_transport_parameters, + TLSEXT_TYPE_quic_transport_parameters, + TLSEXT_TYPE_quic_transport_parameters }; + static int results[] = { 1, 1, 1, 1, 1, 0, 1, 0, 1 }; + + /* Failure cases: + * test 6/[5] clnt = parameters, srvr = draft + * test 8/[7] clnt = draft, srvr = parameters + */ + + /* Clean up logging space */ + memset(client_log_buffer, 0, sizeof(client_log_buffer)); + memset(server_log_buffer, 0, sizeof(server_log_buffer)); + client_log_buffer_index = 0; + server_log_buffer_index = 0; + error_writing_log = 0; + + if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) + || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) + || !TEST_ptr(sctx->quic_method) + || !TEST_ptr(serverssl = SSL_new(sctx)) + || !TEST_true(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, NULL)) + || !TEST_false(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) + || !TEST_true(SSL_IS_QUIC(serverssl))) + goto end; + + if (!TEST_int_eq(test_quic_api_version(clnt_params[tst], srvr_params[tst]), results[tst])) + goto end; + + testresult = 1; + +end: + SSL_CTX_free(sctx); + sctx = NULL; + SSL_free(serverssl); + serverssl = NULL; + return testresult; +} #endif int setup_tests(void) @@ -7101,7 +7162,7 @@ int setup_tests(void) ADD_TEST(test_set_alpn); ADD_TEST(test_inherit_verify_param); #ifndef OPENSSL_NO_QUIC - ADD_TEST(test_quic_api); + ADD_ALL_TESTS(test_quic_api, 9); #endif return 1; } diff --git a/util/libssl.num b/util/libssl.num index 9a19d1757d292..d27011d83fccc 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -509,3 +509,7 @@ SSL_set_quic_method 20007 1_1_1i EXIST::FUNCTION:QUIC SSL_quic_max_handshake_flight_len 20008 1_1_1i EXIST::FUNCTION:QUIC SSL_process_quic_post_handshake 20009 1_1_1i EXIST::FUNCTION:QUIC SSL_provide_quic_data 20010 1_1_1i EXIST::FUNCTION:QUIC +SSL_set_quic_use_legacy_codepoint 20011 1_1_1i EXIST::FUNCTION:QUIC +SSL_set_quic_transport_version 20012 1_1_1i EXIST::FUNCTION:QUIC +SSL_get_peer_quic_transport_version 20013 1_1_1i EXIST::FUNCTION:QUIC +SSL_get_quic_transport_version 20014 1_1_1i EXIST::FUNCTION:QUIC From 152e1acf0936603fc6d6841d5da2fb831459aca2 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 5 Jan 2021 13:50:21 -0500 Subject: [PATCH 14/28] QUIC: return success when no post-handshake data (cherry picked from commit 31c23afe978ed2a046268bd43d589047a1316976) --- ssl/ssl_quic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index f7171be140225..fc6ac52102940 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -312,6 +312,10 @@ int SSL_process_quic_post_handshake(SSL *ssl) return 0; } + /* if there is no data, return success as BoringSSL */ + if (ssl->quic_input_data_head == NULL) + return 1; + /* * This is always safe (we are sure to be at a record boundary) because * SSL_read()/SSL_write() are never used for QUIC connections -- the From e57c06fe2e092942d6addec488260bf13149554d Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 3 Mar 2021 11:30:50 -0500 Subject: [PATCH 15/28] QUIC: Update shared library version Prefix the shared library version with 17 (for 'Q'), to allow this version to be used alongside a standard OpenSSL distribution Add +quic to the version (i.e. OPENSSL_VERSION_TEXT) --- include/openssl/opensslv.h | 4 ++-- util/mkdef.pl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/openssl/opensslv.h b/include/openssl/opensslv.h index cbbfab12b3f3d..a8b71e511418d 100644 --- a/include/openssl/opensslv.h +++ b/include/openssl/opensslv.h @@ -40,7 +40,7 @@ extern "C" { * major minor fix final patch/beta) */ # define OPENSSL_VERSION_NUMBER 0x101010cfL -# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.1l 24 Aug 2021" +# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.1l+quic 24 Aug 2021" /*- * The macros below are to be used for shared library (.so, .dll, ...) @@ -92,7 +92,7 @@ extern "C" { * should only keep the versions that are binary compatible with the current. */ # define SHLIB_VERSION_HISTORY "" -# define SHLIB_VERSION_NUMBER "1.1" +# define SHLIB_VERSION_NUMBER "81.1.1" #ifdef __cplusplus diff --git a/util/mkdef.pl b/util/mkdef.pl index 3ac79821b020d..7af70f9654cca 100755 --- a/util/mkdef.pl +++ b/util/mkdef.pl @@ -1497,7 +1497,7 @@ () open (IN, "$fn") || die "Can't open opensslv.h"; while() { - if (/OPENSSL_VERSION_TEXT\s+"OpenSSL (\d\.\d\.)(\d[a-z]*)(-| )/) { + if (/OPENSSL_VERSION_TEXT\s+"OpenSSL (\d\.\d\.)(\d[a-z]*)(-| |\+)/) { my $suffix = $2; (my $baseversion = $1) =~ s/\./_/g; close IN; From 049930527c2362f2de696053081640780aa12f86 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 3 Mar 2021 15:46:04 -0500 Subject: [PATCH 16/28] QUIC: Swap around README files --- README => README-OpenSSL.md | 0 README.md | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) rename README => README-OpenSSL.md (100%) create mode 100644 README.md diff --git a/README b/README-OpenSSL.md similarity index 100% rename from README rename to README-OpenSSL.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..ef46c4b393a6c --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +What This Is +============ + +This is a fork of [OpenSSL](https://www.openssl.org) to enable QUIC. In addition to the +website, the official source distribution is at https://github.com/openssl/openssl. +The OpenSSL `README` can be found at [README-OpenSSL.md](README-OpenSSL.md). + +This fork adds API that can be used by QUIC implementations for connection +handshakes. Quoting the IETF Working group +[charter](https://datatracker.ietf.org/wg/quic/about/), QUIC is a "UDP-based, +stream-multiplexing, encrypted transport protocol." If you don't need QUIC, you +should use the official OpenSSL distributions. + +This API's here are used by Microsoft's +[MsQuic](https://github.com/microsoft/msquic) and Google's +[Chromium QUIC](https://chromium.googlesource.com/chromium/src/+/master/net/quic/) + +We are not in competition with OpenSSL project. We informed them of +our plans to fork the code before we went public. We do not speak for the +OpenSSL project, and can only point to a +[blog post](https://www.openssl.org/blog/blog/2020/02/17/QUIC-and-OpenSSL/) that +provides their view of QUIC support. + +As stated in their blog post, the OpenSSL team is focused on their 3.0 release +which is still in alpha, and does not intend to add QUIC functionality to 1.1.x. +There is a community need for a QUIC capable TLS library. This fork is intended +as stopgap solution to enable higher level frameworks and runtimes to use QUIC +with the proven and reliable TLS functionality from OpenSSL. This fork will be +maintained until OpenSSL officially provides reasonable support for QUIC +implementations. + +This fork can be considered a supported version of +[OpenSSL PR 8797](https://github.com/openssl/openssl/pull/8797). +We will endeavor to track OpenSSL releases within a day or so, and there is an +item below about how we'll follow their tagging. + +On to the questions and answers. + +What about branches? +-------------------- +We don't want to conflict with OpenSSL branch names. Our current plan is to append +`+quic`. Release tags are likely to be the QUIC branch with `-releaseX` appended. +For example, the OpenSSL tag `openssl-3.0.0-alpha12` would have a branch named +`openssl-3.0.0-alpha12+quic` and a release tag of `openssl-3.0.0-alpha12+quic-release1` + +How are you keeping current with OpenSSL? +----------------------------------------- +(In other words, "What about rebasing?") + +Our plan it to always rebase on top of an upstream release tag. In particular: +- The changes for QUIC will always be at the tip of the branch -- you will know what +is from the original OpenSSL and what is for QUIC. +- New versions are quickly created once upstream creates a new tag. +- The use of git commands (such as "cherry") can be used to ensure that all changes +have moved forward with minimal or no changes. You will be able to see "QUIC: Add X" +on all branches and the commit itself will be nearly identical on all branches, and +any changes to that can be easily identified. + +What about library names? +------------------------- +Library names will be the same, but will use a different version number. The version +numbers for the current OpenSSL libraries are `1.1` (for the 1.1.0 and 1.1.1 branches) +and `3` (for the to-be-3.0 branch). We will be prefixing 81 (ASCII for 'Q') to +the version numbers to generate a unique version number. + +``` +libcrypto.so.81.3 libcrypto.so.81.1.1 libcrypto.so.1.1 libcrypto.so.3 +libssl.so.81.3 libssl.so.81.1.1 libsslo.so.1.1 libssl.so.3 +``` +The SONAME of these libraries are all different, guaranteeing the correct library +will be used. + +...and the executable? +---------------------- +We currently do not have any plans to change the name, mainly because we +haven't made any changes there. If you see a need, please open an issue. + +The `openssl version` command will report that it is `+quic` enabled. + +...and FIPS? +------------ +We are not doing anything with FIPS. This is actually good news: you should +be able to load the OpenSSL 3.0 FIPS module into an application built against +this fork and everything should Just Work™. + +How can I contribute? +--------------------- +We want any code here to be acceptable to OpenSSL. This means that all contributors +must have signed the appropriate +[contributor license agreements](https://www.openssl.org/policies/cla.html). We +will not ask for copies of any paperwork, you just need to tell us that you've +done so (and we might verify with OpenSSL). We are only interested in making it +easier and better for at least the mentioned QUIC implementations to use a variant +of OpenSSL. If you have a pull request that changes the TLS protocol, or adds +assembly support for a new CPU, or otherwise is not specific to enabling QUIC, +please contribute that to OpenSSL. This fork is intended to be a clean extension +to OpenSSL, with the deltas being specific to QUIC. + +Who are you? +------------ +This is a collaborative effort between [Akamai](https://www.akamai.com) and +[Microsoft](https://www.microsoft.com). We welcome anyone to contribute! From 414fbe630bc48934cc7694367441354fcd2ce817 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 3 Mar 2021 17:32:31 -0500 Subject: [PATCH 17/28] QUIC: Fix 1.1.1 GitHub CI --- test/sslapitest.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/sslapitest.c b/test/sslapitest.c index 167f4d3a83e0f..d8135375f7433 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6906,10 +6906,10 @@ static int test_quic_api_version(int clnt, int srvr) &clientssl, NULL, NULL)) || !TEST_true(SSL_set_quic_transport_params(serverssl, (unsigned char*)server_str, - sizeof(server_str))) + strlen(server_str)+1)) || !TEST_true(SSL_set_quic_transport_params(clientssl, (unsigned char*)client_str, - sizeof(client_str))) + strlen(client_str)+1)) || !TEST_true(SSL_set_app_data(serverssl, clientssl)) || !TEST_true(SSL_set_app_data(clientssl, serverssl)) || !TEST_true(test_quic_api_set_versions(clientssl, clnt)) @@ -6925,10 +6925,10 @@ static int test_quic_api_version(int clnt, int srvr) goto end; SSL_get_peer_quic_transport_params(serverssl, &peer_str, &peer_str_len); - if (!TEST_mem_eq(peer_str, peer_str_len, client_str, sizeof(client_str))) + if (!TEST_mem_eq(peer_str, peer_str_len, client_str, strlen(client_str)+1)) goto end; SSL_get_peer_quic_transport_params(clientssl, &peer_str, &peer_str_len); - if (!TEST_mem_eq(peer_str, peer_str_len, server_str, sizeof(server_str))) + if (!TEST_mem_eq(peer_str, peer_str_len, server_str, strlen(server_str)+1)) goto end; /* Deal with two NewSessionTickets */ From d5858cb74e2fe577ebd6302255d8ec4d9a9d7515 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Fri, 5 Mar 2021 15:50:12 -0500 Subject: [PATCH 18/28] QUIC: Add compile/run-time checking for QUIC Different from 3.0.0 as there's no OpenSSL_info() --- crypto/cversion.c | 4 ++++ include/openssl/crypto.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/crypto/cversion.c b/crypto/cversion.c index 534e7eba55b36..cd7fa0c63e2c7 100644 --- a/crypto/cversion.c +++ b/crypto/cversion.c @@ -38,6 +38,10 @@ const char *OpenSSL_version(int t) return "ENGINESDIR: \"" ENGINESDIR "\""; #else return "ENGINESDIR: N/A"; +#endif +#ifndef OPENSSL_NO_QUIC + case OPENSSL_INFO_QUIC: + return "QUIC"; #endif } return "not available"; diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h index 7d0b526236f8e..5fdd7c3d04d34 100644 --- a/include/openssl/crypto.h +++ b/include/openssl/crypto.h @@ -161,6 +161,10 @@ const char *OpenSSL_version(int type); # define OPENSSL_DIR 4 # define OPENSSL_ENGINES_DIR 5 +# ifndef OPENSSL_NO_QUIC +# define OPENSSL_INFO_QUIC 2000 +# endif + int OPENSSL_issetugid(void); typedef void CRYPTO_EX_new (void *parent, void *ptr, CRYPTO_EX_DATA *ad, From a79840d0b279c58732616462e8aad12e0709044b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> Date: Wed, 10 Mar 2021 22:28:13 +0900 Subject: [PATCH 19/28] QUIC: Add early data support (#8) * QUIC: Add early data support This commit adds SSL_set_quic_early_data_enabled to add early data support to QUIC. * fixup! QUIC: Add early data support * fixup! QUIC: Add early data support --- doc/man3/SSL_CTX_set_quic_method.pod | 10 +- include/openssl/ssl.h | 2 + ssl/ssl_lib.c | 15 +++ ssl/ssl_quic.c | 79 +++++++++++--- ssl/statem/statem_srvr.c | 10 ++ ssl/tls13_enc.c | 96 ++++++++++++++--- test/sslapitest.c | 152 +++++++++++++++++++++++++++ util/libssl.num | 1 + 8 files changed, 331 insertions(+), 34 deletions(-) diff --git a/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod index 39ff3a8da4517..86eb257e91d40 100644 --- a/doc/man3/SSL_CTX_set_quic_method.pod +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -17,7 +17,8 @@ SSL_is_quic, SSL_get_peer_quic_transport_version, SSL_get_quic_transport_version, SSL_set_quic_transport_version, -SSL_set_quic_use_legacy_codepoint +SSL_set_quic_use_legacy_codepoint, +SSL_set_quic_early_data_enabled - QUIC support =head1 SYNOPSIS @@ -47,6 +48,7 @@ SSL_set_quic_use_legacy_codepoint void SSL_set_quic_transport_version(SSL *ssl, int version); int SSL_get_quic_transport_version(const SSL *ssl); int SSL_get_peer_quic_transport_version(const SSL *ssl); + void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled); =head1 DESCRIPTION @@ -106,6 +108,12 @@ SSL_set_quic_transport_version(). SSL_get_peer_quic_transport_version() returns the version the that was negotiated. +SSL_set_quic_early_data_enabled() enables QUIC early data if a nonzero +value is passed. Client must set a resumed session before calling +this function. Server must set 0xffffffffu to +SSL_CTX_set_max_early_data() or SSL_set_max_early_data() so that a +session ticket indicates that server is able to accept early data. + =head1 NOTES These APIs are implementations of BoringSSL's QUIC APIs. diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 21d77d9ea8ad8..c6f8e4d180106 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2488,6 +2488,8 @@ __owur int SSL_get_peer_quic_transport_version(const SSL *ssl); int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); +void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled); + # endif # ifdef __cplusplus diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 65089bd78df43..f398a5f68a5f7 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -3755,6 +3755,21 @@ int SSL_do_handshake(SSL *s) ret = s->handshake_func(s); } } +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s) && ret == 1) { + if (s->server) { + if (s->early_data_state == SSL_EARLY_DATA_ACCEPTING) { + s->early_data_state = SSL_EARLY_DATA_FINISHED_READING; + s->rwstate = SSL_READING; + ret = 0; + } + } else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) { + s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY; + s->rwstate = SSL_READING; + ret = 0; + } + } +#endif return ret; } diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index fc6ac52102940..2d0d94dd64e61 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -258,24 +258,49 @@ int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) return 1; } - md = ssl_handshake_md(ssl); - if (md == NULL) { - /* May not have selected cipher, yet */ - const SSL_CIPHER *c = NULL; - - /* - * It probably doesn't make sense to use an (external) PSK session, - * but in theory some kinds of external session caches could be - * implemented using it, so allow psksession to be used as well as - * the regular session. - */ - if (ssl->session != NULL) - c = SSL_SESSION_get0_cipher(ssl->session); - else if (ssl->psksession != NULL) + if (level == ssl_encryption_early_data) { + const SSL_CIPHER *c = SSL_SESSION_get0_cipher(ssl->session); + if (ssl->early_data_state == SSL_EARLY_DATA_CONNECTING + && ssl->max_early_data > 0 + && ssl->session->ext.max_early_data == 0) { + if (!ossl_assert(ssl->psksession != NULL + && ssl->max_early_data + == ssl->psksession->ext.max_early_data)) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, + SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } c = SSL_SESSION_get0_cipher(ssl->psksession); + } + + if (c == NULL) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, + SSL_F_QUIC_SET_ENCRYPTION_SECRETS, ERR_R_INTERNAL_ERROR); + return 0; + } + + md = ssl_md(c->algorithm2); + } else { + md = ssl_handshake_md(ssl); + if (md == NULL) { + /* May not have selected cipher, yet */ + const SSL_CIPHER *c = NULL; - if (c != NULL) - md = SSL_CIPHER_get_handshake_digest(c); + /* + * It probably doesn't make sense to use an (external) PSK session, + * but in theory some kinds of external session caches could be + * implemented using it, so allow psksession to be used as well as + * the regular session. + */ + if (ssl->session != NULL) + c = SSL_SESSION_get0_cipher(ssl->session); + else if (ssl->psksession != NULL) + c = SSL_SESSION_get0_cipher(ssl->psksession); + + if (c != NULL) + md = SSL_CIPHER_get_handshake_digest(c); + } } if ((len = EVP_MD_size(md)) <= 0) { @@ -334,3 +359,25 @@ int SSL_is_quic(SSL* ssl) { return SSL_IS_QUIC(ssl); } + +void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled) +{ + if (!SSL_is_quic(ssl) || !SSL_in_before(ssl)) + return; + + if (!enabled) { + ssl->early_data_state = SSL_EARLY_DATA_NONE; + return; + } + + if (ssl->server) { + ssl->early_data_state = SSL_EARLY_DATA_ACCEPTING; + return; + } + + if ((ssl->session == NULL || ssl->session->ext.max_early_data == 0) + && ssl->psk_use_session_cb == NULL) + return; + + ssl->early_data_state = SSL_EARLY_DATA_CONNECTING; +} diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 42f02b06013f1..e2f7b2644165c 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -965,6 +965,16 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst) SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_WRITE)) /* SSLfatal() already called */ return WORK_ERROR; + +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s) && s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { + s->early_data_state = SSL_EARLY_DATA_FINISHED_READING; + if (!s->method->ssl3_enc->change_cipher_state( + s, SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_READ)) + /* SSLfatal() already called */ + return WORK_ERROR; + } +#endif } break; diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index f73b97fbe496b..e4c72cdb28474 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -468,21 +468,81 @@ static int quic_change_cipher_state(SSL *s, int which) int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); int is_early = (which & SSL3_CC_EARLY); - md = ssl_handshake_md(s); - if (!ssl3_digest_cached_records(s, 1) - || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { - /* SSLfatal() already called */; - goto err; - } + if (is_early) { + EVP_MD_CTX *mdctx = NULL; + long handlen; + void *hdata; + unsigned int hashlenui; + const SSL_CIPHER *sslcipher = SSL_SESSION_get0_cipher(s->session); + + handlen = BIO_get_mem_data(s->s3->handshake_buffer, &hdata); + if (handlen <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + SSL_R_BAD_HANDSHAKE_LENGTH); + goto err; + } - /* Ensure cast to size_t is safe */ - hashleni = EVP_MD_size(md); - if (!ossl_assert(hashleni >= 0)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, - ERR_R_EVP_LIB); - goto err; + if (s->early_data_state == SSL_EARLY_DATA_CONNECTING + && s->max_early_data > 0 && s->session->ext.max_early_data == 0) { + /* + * If we are attempting to send early data, and we've decided to + * actually do it but max_early_data in s->session is 0 then we + * must be using an external PSK. + */ + if (!ossl_assert(s->psksession != NULL + && s->max_early_data + == s->psksession->ext.max_early_data)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_QUIC_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR); + goto err; + } + sslcipher = SSL_SESSION_get0_cipher(s->psksession); + } + if (sslcipher == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + SSL_R_BAD_PSK); + goto err; + } + + /* + * We need to calculate the handshake digest using the digest from + * the session. We haven't yet selected our ciphersuite so we can't + * use ssl_handshake_md(). + */ + mdctx = EVP_MD_CTX_new(); + if (mdctx == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + ERR_R_MALLOC_FAILURE); + goto err; + } + md = ssl_md(sslcipher->algorithm2); + if (md == NULL || !EVP_DigestInit_ex(mdctx, md, NULL) + || !EVP_DigestUpdate(mdctx, hdata, handlen) + || !EVP_DigestFinal_ex(mdctx, hash, &hashlenui)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + ERR_R_INTERNAL_ERROR); + EVP_MD_CTX_free(mdctx); + goto err; + } + hashlen = hashlenui; + EVP_MD_CTX_free(mdctx); + } else { + md = ssl_handshake_md(s); + if (!ssl3_digest_cached_records(s, 1) + || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { + /* SSLfatal() already called */; + goto err; + } + + /* Ensure cast to size_t is safe */ + hashleni = EVP_MD_size(md); + if (!ossl_assert(hashleni >= 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + ERR_R_EVP_LIB); + goto err; + } + hashlen = (size_t)hashleni; } - hashlen = (size_t)hashleni; if (is_client_read || is_server_write) { if (is_handshake) { @@ -588,10 +648,12 @@ static int quic_change_cipher_state(SSL *s, int which) } } - if (s->server) - s->quic_read_level = level; - else - s->quic_write_level = level; + if (level != ssl_encryption_early_data) { + if (s->server) + s->quic_read_level = level; + else + s->quic_write_level = level; + } } ret = 1; diff --git a/test/sslapitest.c b/test/sslapitest.c index d8135375f7433..8681aa47cc8eb 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -7031,6 +7031,157 @@ static int test_quic_api(int tst) serverssl = NULL; return testresult; } + +/* + * Helper method to setup objects for QUIC early data test. Caller + * frees objects on error. + */ +static int quic_setupearly_data_test(SSL_CTX **cctx, SSL_CTX **sctx, + SSL **clientssl, SSL **serverssl, + SSL_SESSION **sess, int idx) +{ + static const char *server_str = "SERVER"; + static const char *client_str = "CLIENT"; + + if (*sctx == NULL + && (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), + TLS_client_method(), + TLS1_3_VERSION, 0, + sctx, cctx, cert, privkey)) + || !TEST_true(SSL_CTX_set_quic_method(*sctx, &quic_method)) + || !TEST_true(SSL_CTX_set_quic_method(*cctx, &quic_method)) + || !TEST_true(SSL_CTX_set_max_early_data(*sctx, 0xffffffffu)))) + return 0; + + if (idx == 1) { + /* When idx == 1 we repeat the tests with read_ahead set */ + SSL_CTX_set_read_ahead(*cctx, 1); + SSL_CTX_set_read_ahead(*sctx, 1); + } else if (idx == 2) { + /* When idx == 2 we are doing early_data with a PSK. Set up callbacks */ + SSL_CTX_set_psk_use_session_callback(*cctx, use_session_cb); + SSL_CTX_set_psk_find_session_callback(*sctx, find_session_cb); + use_session_cb_cnt = 0; + find_session_cb_cnt = 0; + srvid = pskid; + } + + if (!TEST_true(create_ssl_objects(*sctx, *cctx, serverssl, clientssl, + NULL, NULL)) + || !TEST_true(SSL_set_quic_transport_params(*serverssl, + (unsigned char*)server_str, + strlen(server_str)+1)) + || !TEST_true(SSL_set_quic_transport_params(*clientssl, + (unsigned char*)client_str, + strlen(client_str)+1)) + || !TEST_true(SSL_set_app_data(*serverssl, *clientssl)) + || !TEST_true(SSL_set_app_data(*clientssl, *serverssl))) + return 0; + + /* + * For one of the run throughs (doesn't matter which one), we'll try sending + * some SNI data in the initial ClientHello. This will be ignored (because + * there is no SNI cb set up by the server), so it should not impact + * early_data. + */ + if (idx == 1 + && !TEST_true(SSL_set_tlsext_host_name(*clientssl, "localhost"))) + return 0; + + if (idx == 2) { + clientpsk = create_a_psk(*clientssl); + if (!TEST_ptr(clientpsk) + || !TEST_true(SSL_SESSION_set_max_early_data(clientpsk, + 0xffffffffu)) + || !TEST_true(SSL_SESSION_up_ref(clientpsk))) { + SSL_SESSION_free(clientpsk); + clientpsk = NULL; + return 0; + } + serverpsk = clientpsk; + + if (sess != NULL) { + if (!TEST_true(SSL_SESSION_up_ref(clientpsk))) { + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + return 0; + } + *sess = clientpsk; + } + + SSL_set_quic_early_data_enabled(*serverssl, 1); + SSL_set_quic_early_data_enabled(*clientssl, 1); + + return 1; + } + + if (sess == NULL) + return 1; + + if (!TEST_true(create_ssl_connection(*serverssl, *clientssl, + SSL_ERROR_NONE))) + return 0; + + /* Deal with two NewSessionTickets */ + if (!TEST_true(SSL_process_quic_post_handshake(*clientssl)) + || !TEST_true(SSL_process_quic_post_handshake(*clientssl))) + return 0; + + *sess = SSL_get1_session(*clientssl); + SSL_shutdown(*clientssl); + SSL_shutdown(*serverssl); + SSL_free(*serverssl); + SSL_free(*clientssl); + *serverssl = *clientssl = NULL; + + if (!TEST_true(create_ssl_objects(*sctx, *cctx, serverssl, + clientssl, NULL, NULL)) + || !TEST_true(SSL_set_session(*clientssl, *sess)) + || !TEST_true(SSL_set_quic_transport_params(*serverssl, + (unsigned char*)server_str, + strlen(server_str)+1)) + || !TEST_true(SSL_set_quic_transport_params(*clientssl, + (unsigned char*)client_str, + strlen(client_str)+1)) + || !TEST_true(SSL_set_app_data(*serverssl, *clientssl)) + || !TEST_true(SSL_set_app_data(*clientssl, *serverssl))) + return 0; + + SSL_set_quic_early_data_enabled(*serverssl, 1); + SSL_set_quic_early_data_enabled(*clientssl, 1); + + return 1; +} + +static int test_quic_early_data(int tst) +{ + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL; + int testresult = 0; + SSL_SESSION *sess = NULL; + + if (!TEST_true(quic_setupearly_data_test(&cctx, &sctx, &clientssl, + &serverssl, &sess, tst))) + goto end; + + if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) + || !TEST_true(SSL_get_early_data_status(serverssl))) + goto end; + + testresult = 1; + + end: + SSL_SESSION_free(sess); + SSL_SESSION_free(clientpsk); + SSL_SESSION_free(serverpsk); + clientpsk = serverpsk = NULL; + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + return testresult; +} #endif int setup_tests(void) @@ -7163,6 +7314,7 @@ int setup_tests(void) ADD_TEST(test_inherit_verify_param); #ifndef OPENSSL_NO_QUIC ADD_ALL_TESTS(test_quic_api, 9); + ADD_ALL_TESTS(test_quic_early_data, 3); #endif return 1; } diff --git a/util/libssl.num b/util/libssl.num index d27011d83fccc..7c8f9463abff0 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -513,3 +513,4 @@ SSL_set_quic_use_legacy_codepoint 20011 1_1_1i EXIST::FUNCTION:QUIC SSL_set_quic_transport_version 20012 1_1_1i EXIST::FUNCTION:QUIC SSL_get_peer_quic_transport_version 20013 1_1_1i EXIST::FUNCTION:QUIC SSL_get_quic_transport_version 20014 1_1_1i EXIST::FUNCTION:QUIC +SSL_set_quic_early_data_enabled 20015 1_1_1j EXIST::FUNCTION:QUIC From e9726fa9fec2f5ed02188ac6d8965c14ecbc7e32 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> Date: Wed, 10 Mar 2021 22:30:20 +0900 Subject: [PATCH 20/28] QUIC: Make SSL_provide_quic_data accept 0 length data (#10) This commit makes SSL_provide_quic_data accept 0 length data, which matches BoringSSL behavior. Fixes #9 --- ssl/ssl_quic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index 2d0d94dd64e61..0cbb43af39ac9 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -143,6 +143,9 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, return 0; } + if (len == 0) + return 1; + if (ssl->quic_buf == NULL) { BUF_MEM *buf; if ((buf = BUF_MEM_new()) == NULL) { From b7086054b4683abdfb23839d92408b8adf48344f Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com> Date: Fri, 12 Mar 2021 09:55:26 +0900 Subject: [PATCH 21/28] QUIC: Process multiple post-handshake messages in a single call (#14) --- ssl/ssl_quic.c | 27 +++++++++++++-------------- test/sslapitest.c | 6 ++---- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c index 0cbb43af39ac9..c5f20c20af7eb 100644 --- a/ssl/ssl_quic.c +++ b/ssl/ssl_quic.c @@ -341,20 +341,19 @@ int SSL_process_quic_post_handshake(SSL *ssl) } /* if there is no data, return success as BoringSSL */ - if (ssl->quic_input_data_head == NULL) - return 1; - - /* - * This is always safe (we are sure to be at a record boundary) because - * SSL_read()/SSL_write() are never used for QUIC connections -- the - * application data is handled at the QUIC layer instead. - */ - ossl_statem_set_in_init(ssl, 1); - ret = ssl->handshake_func(ssl); - ossl_statem_set_in_init(ssl, 0); - - if (ret <= 0) - return 0; + while (ssl->quic_input_data_head != NULL) { + /* + * This is always safe (we are sure to be at a record boundary) because + * SSL_read()/SSL_write() are never used for QUIC connections -- the + * application data is handled at the QUIC layer instead. + */ + ossl_statem_set_in_init(ssl, 1); + ret = ssl->handshake_func(ssl); + ossl_statem_set_in_init(ssl, 0); + + if (ret <= 0) + return 0; + } return 1; } diff --git a/test/sslapitest.c b/test/sslapitest.c index 8681aa47cc8eb..0be61c328ce4f 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -6932,8 +6932,7 @@ static int test_quic_api_version(int clnt, int srvr) goto end; /* Deal with two NewSessionTickets */ - if (!TEST_true(SSL_process_quic_post_handshake(clientssl)) - || !TEST_true(SSL_process_quic_post_handshake(clientssl))) + if (!TEST_true(SSL_process_quic_post_handshake(clientssl))) goto end; /* Dummy handshake call should succeed */ @@ -7124,8 +7123,7 @@ static int quic_setupearly_data_test(SSL_CTX **cctx, SSL_CTX **sctx, return 0; /* Deal with two NewSessionTickets */ - if (!TEST_true(SSL_process_quic_post_handshake(*clientssl)) - || !TEST_true(SSL_process_quic_post_handshake(*clientssl))) + if (!TEST_true(SSL_process_quic_post_handshake(*clientssl))) return 0; *sess = SSL_get1_session(*clientssl); From 11ad71d310e5deadf89a82ff4a7de9b6beed6dbe Mon Sep 17 00:00:00 2001 From: kaduk Date: Fri, 12 Mar 2021 12:48:01 -0800 Subject: [PATCH 22/28] QUIC: Tighten up some language in SSL_CTX_set_quic_method.pod (#12) --- doc/man3/SSL_CTX_set_quic_method.pod | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod index 86eb257e91d40..906c7591d9e55 100644 --- a/doc/man3/SSL_CTX_set_quic_method.pod +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -109,10 +109,10 @@ SSL_get_peer_quic_transport_version() returns the version the that was negotiated. SSL_set_quic_early_data_enabled() enables QUIC early data if a nonzero -value is passed. Client must set a resumed session before calling -this function. Server must set 0xffffffffu to -SSL_CTX_set_max_early_data() or SSL_set_max_early_data() so that a -session ticket indicates that server is able to accept early data. +value is passed. Clients must set a resumed session before calling this +function. Servers must additionally call SSL_CTX_set_max_early_data() or +SSL_set_max_early_data() with 0xffffffffu as the argument, so that any +issued session tickets indicate that server is able to accept early data. =head1 NOTES From b0334ffc1392b7a1422a81a59c04a3fa777b24dd Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Tue, 16 Mar 2021 08:53:44 +0800 Subject: [PATCH 23/28] QUIC: Fix typo in README.md (#21) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef46c4b393a6c..26defbc8cbf2a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ How are you keeping current with OpenSSL? ----------------------------------------- (In other words, "What about rebasing?") -Our plan it to always rebase on top of an upstream release tag. In particular: +Our plan is to always rebase on top of an upstream release tag. In particular: - The changes for QUIC will always be at the tip of the branch -- you will know what is from the original OpenSSL and what is for QUIC. - New versions are quickly created once upstream creates a new tag. From 6d7e71dd813cda20c3d703d60a469daacf089947 Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 16 Mar 2020 11:25:58 -0700 Subject: [PATCH 24/28] QUIC: Add SSL_new_session_ticket() API This API requests that the TLS stack generate a (TLS 1.3) NewSessionTicket message the next time it is safe to do so (i.e., we do not have other data pending write, which could be mid-record). For efficiency, defer actually generating/writing the ticket until there is other data to write, to avoid producing server-to-client traffic when not needed. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/11416) (cherry picked from commit 3bfacb5fd4679812a7b9ec61d296b1add64669c0) --- doc/man3/SSL_CTX_set_num_tickets.pod | 27 +++++++++++++++++++++++---- include/openssl/ssl.h | 1 + ssl/record/rec_layer_s3.c | 8 +++++--- ssl/ssl_lib.c | 9 +++++++++ ssl/ssl_local.h | 2 ++ ssl/statem/statem_srvr.c | 16 +++++++++++++--- 6 files changed, 53 insertions(+), 10 deletions(-) diff --git a/doc/man3/SSL_CTX_set_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod index ad13ed15f4065..bc72776755f7d 100644 --- a/doc/man3/SSL_CTX_set_num_tickets.pod +++ b/doc/man3/SSL_CTX_set_num_tickets.pod @@ -5,7 +5,8 @@ SSL_set_num_tickets, SSL_get_num_tickets, SSL_CTX_set_num_tickets, -SSL_CTX_get_num_tickets +SSL_CTX_get_num_tickets, +SSL_new_session_ticket - control the number of TLSv1.3 session tickets that are issued =head1 SYNOPSIS @@ -16,6 +17,7 @@ SSL_CTX_get_num_tickets size_t SSL_get_num_tickets(SSL *s); int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets); size_t SSL_CTX_get_num_tickets(SSL_CTX *ctx); + int SSL_new_session_ticket(SSL *s); =head1 DESCRIPTION @@ -40,21 +42,38 @@ handshake then SSL_set_num_tickets() can be called again prior to calling SSL_verify_client_post_handshake() to update the number of tickets that will be sent. +To issue tickets after other events (such as application-layer changes), +SSL_new_session_ticket() is used by a server application to request that a new +ticket be sent when it is safe to do so. New tickets are only allowed to be +sent in this manner after the initial handshake has completed, and only for TLS +1.3 connections. The ticket generation and transmission are delayed until the +server is starting a new write operation, so that it is bundled with other +application data being written and properly aligned to a record boundary. +SSL_new_session_ticket() can be called more than once to request additional +tickets be sent; all such requests are queued and written together when it is +safe to do so. Note that a successful return from SSL_new_session_ticket() +indicates only that the request to send a ticket was processed, not that the +ticket itself was sent. To be notified when the ticket itself is sent, a +new-session callback can be registered with L that +will be invoked as the ticket or tickets are generated. + SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of tickets set by a previous call to SSL_CTX_set_num_tickets() or SSL_set_num_tickets(), or 2 if no such call has been made. =head1 RETURN VALUES -SSL_CTX_set_num_tickets() and SSL_set_num_tickets() return 1 on success or 0 on -failure. +SSL_CTX_set_num_tickets(), SSL_set_num_tickets(), and +SSL_new_session_ticket() return 1 on success or 0 on failure. SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of tickets that have been previously set. =head1 HISTORY -These functions were added in OpenSSL 1.1.1. +SSL_new_session_ticket() was added in OpenSSL 3.0.0. +SSL_set_num_tickets(), SSL_get_num_tickets(), SSL_CTX_set_num_tickets(), and +SSL_CTX_get_num_tickets() were added in OpenSSL 1.1.1. =head1 COPYRIGHT diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index c6f8e4d180106..17bd0bc2c9c9c 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1921,6 +1921,7 @@ int SSL_get_key_update_type(const SSL *s); int SSL_renegotiate(SSL *s); int SSL_renegotiate_abbreviated(SSL *s); __owur int SSL_renegotiate_pending(const SSL *s); +int SSL_new_session_ticket(SSL *s); int SSL_shutdown(SSL *s); __owur int SSL_verify_client_post_handshake(SSL *s); void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val); diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 8249b4ace9cd4..cdc90ed0b3966 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -374,10 +374,12 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, s->rlayer.wnum = 0; /* - * If we are supposed to be sending a KeyUpdate then go into init unless we - * have writes pending - in which case we should finish doing that first. + * If we are supposed to be sending a KeyUpdate or NewSessionTicket then go + * into init unless we have writes pending - in which case we should finish + * doing that first. */ - if (wb->left == 0 && s->key_update != SSL_KEY_UPDATE_NONE) + if (wb->left == 0 && (s->key_update != SSL_KEY_UPDATE_NONE + || s->ext.extra_tickets_expected > 0)) ossl_statem_set_in_init(s, 1); /* diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index f398a5f68a5f7..b94651583cf10 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2215,6 +2215,15 @@ int SSL_renegotiate_pending(const SSL *s) return (s->renegotiate != 0); } +int SSL_new_session_ticket(SSL *s) +{ + if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server + || !SSL_IS_TLS13(s)) + return 0; + s->ext.extra_tickets_expected++; + return 1; +} + long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) { long l; diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 55632593916bf..194f9674ebdf0 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1320,6 +1320,8 @@ struct ssl_st { /* RFC4507 session ticket expected to be received or sent */ int ticket_expected; + /* TLS 1.3 tickets requested by the application. */ + int extra_tickets_expected; # ifndef OPENSSL_NO_EC size_t ecpointformats_len; /* our list */ diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index e2f7b2644165c..4a2f8872acad6 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -437,6 +437,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) st->hand_state = TLS_ST_SW_CERT_REQ; return WRITE_TRAN_CONTINUE; } + if (s->ext.extra_tickets_expected > 0) { + st->hand_state = TLS_ST_SW_SESSION_TICKET; + return WRITE_TRAN_CONTINUE; + } /* Try to read from the client instead */ return WRITE_TRAN_FINISHED; @@ -527,7 +531,9 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) * Following an initial handshake we send the number of tickets we have * been configured for. */ - if (s->hit || s->num_tickets <= s->sent_tickets) { + if (!SSL_IS_FIRST_HANDSHAKE(s) && s->ext.extra_tickets_expected > 0) { + return WRITE_TRAN_CONTINUE; + } else if (s->hit || s->num_tickets <= s->sent_tickets) { /* We've written enough tickets out. */ st->hand_state = TLS_ST_OK; } @@ -723,7 +729,8 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst) return WORK_FINISHED_CONTINUE; case TLS_ST_SW_SESSION_TICKET: - if (SSL_IS_TLS13(s) && s->sent_tickets == 0) { + if (SSL_IS_TLS13(s) && s->sent_tickets == 0 + && s->ext.extra_tickets_expected == 0) { /* * Actually this is the end of the handshake, but we're going * straight into writing the session ticket out. So we finish off @@ -4185,10 +4192,13 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) /* * Increment both |sent_tickets| and |next_ticket_nonce|. |sent_tickets| * gets reset to 0 if we send more tickets following a post-handshake - * auth, but |next_ticket_nonce| does not. + * auth, but |next_ticket_nonce| does not. If we're sending extra + * tickets, decrement the count of pending extra tickets. */ s->sent_tickets++; s->next_ticket_nonce++; + if (s->ext.extra_tickets_expected > 0) + s->ext.extra_tickets_expected--; ssl_update_cache(s, SSL_SESS_CACHE_SERVER); } From 065f641474ac9fae79255d683980fc01922743fa Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Tue, 17 Mar 2020 10:08:11 -0700 Subject: [PATCH 25/28] QUIC: Add test for SSL_new_session_ticket() Run a normal handshake and then request some extra tickets, checking that the new_session_cb is called the expected number of times. Since the tickets are generated in the same way as other tickets, there should not be a need to verify that these specific ones can be used to resume. Run the test with both zero and a non-zero number of tickets issued in the initial handshake. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/11416) (cherry picked from commit f0049b86cc5d745af97c087e54abaeb00de40b6b) --- test/sslapitest.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/test/sslapitest.c b/test/sslapitest.c index 0be61c328ce4f..50d97e8c4ae9d 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -1627,6 +1627,148 @@ static int test_psk_tickets(void) return testresult; } + +static int test_extra_tickets(int idx) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl = NULL, *clientssl = NULL; + BIO *bretry = BIO_new(bio_s_always_retry()); + BIO *tmp = NULL; + int testresult = 0; + int stateful = 0; + size_t nbytes; + unsigned char c, buf[1]; + + new_called = 0; + do_cache = 1; + + if (idx >= 3) { + idx -= 3; + stateful = 1; + } + + if (!TEST_ptr(bretry) || !setup_ticket_test(stateful, idx, &sctx, &cctx)) + goto end; + SSL_CTX_sess_set_new_cb(sctx, new_session_cb); + /* setup_ticket_test() uses new_cachesession_cb which we don't need. */ + SSL_CTX_sess_set_new_cb(cctx, new_session_cb); + + if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, + &clientssl, NULL, NULL))) + goto end; + + /* + * Note that we have new_session_cb on both sctx and cctx, so new_called is + * incremented by both client and server. + */ + if (!TEST_true(create_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE)) + /* Check we got the number of tickets we were expecting */ + || !TEST_int_eq(idx * 2, new_called) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_int_eq(idx * 2, new_called)) + goto end; + + /* Now try a (real) write to actually send the tickets */ + c = '1'; + if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes)) + || !TEST_size_t_eq(1, nbytes) + || !TEST_int_eq(idx * 2 + 2, new_called) + || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(idx * 2 + 4, new_called) + || !TEST_int_eq(sizeof(buf), nbytes) + || !TEST_int_eq(c, buf[0]) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))) + goto end; + + /* Try with only requesting one new ticket, too */ + c = '2'; + new_called = 0; + if (!TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_write_ex(serverssl, &c, sizeof(c), &nbytes)) + || !TEST_size_t_eq(sizeof(c), nbytes) + || !TEST_int_eq(1, new_called) + || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(2, new_called) + || !TEST_size_t_eq(sizeof(buf), nbytes) + || !TEST_int_eq(c, buf[0])) + goto end; + + /* Do it again but use dummy writes to drive the ticket generation */ + c = '3'; + new_called = 0; + if (!TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_write_ex(serverssl, &c, 0, &nbytes)) + || !TEST_size_t_eq(0, nbytes) + || !TEST_int_eq(2, new_called) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(4, new_called)) + goto end; + + /* + * Use the always-retry BIO to exercise the logic that forces ticket + * generation to wait until a record boundary. + */ + c = '4'; + new_called = 0; + tmp = SSL_get_wbio(serverssl); + if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) { + tmp = NULL; + goto end; + } + SSL_set0_wbio(serverssl, bretry); + bretry = NULL; + if (!TEST_false(SSL_write_ex(serverssl, &c, 1, &nbytes)) + || !TEST_int_eq(SSL_get_error(serverssl, 0), SSL_ERROR_WANT_WRITE) + || !TEST_size_t_eq(nbytes, 0)) + goto end; + /* Restore a BIO that will let the write succeed */ + SSL_set0_wbio(serverssl, tmp); + tmp = NULL; + /* These calls should just queue the request and not send anything. */ + if (!TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_int_eq(0, new_called)) + goto end; + /* Re-do the write; still no tickets sent */ + if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes)) + || !TEST_size_t_eq(1, nbytes) + || !TEST_int_eq(0, new_called) + || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(0, new_called) + || !TEST_int_eq(sizeof(buf), nbytes) + || !TEST_int_eq(c, buf[0]) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))) + goto end; + /* Now the *next* write should send the tickets */ + c = '5'; + if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes)) + || !TEST_size_t_eq(1, nbytes) + || !TEST_int_eq(2, new_called) + || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(4, new_called) + || !TEST_int_eq(sizeof(buf), nbytes) + || !TEST_int_eq(c, buf[0]) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))) + goto end; + + SSL_shutdown(clientssl); + SSL_shutdown(serverssl); + testresult = 1; + + end: + BIO_free(bretry); + BIO_free(tmp); + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + clientssl = serverssl = NULL; + sctx = cctx = NULL; + return testresult; +} #endif #define USE_NULL 0 @@ -7230,6 +7372,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_stateful_tickets, 3); ADD_ALL_TESTS(test_stateless_tickets, 3); ADD_TEST(test_psk_tickets); + ADD_ALL_TESTS(test_extra_tickets, 6); #endif ADD_ALL_TESTS(test_ssl_set_bio, TOTAL_SSL_SET_BIO_TESTS); ADD_TEST(test_ssl_bio_pop_next_bio); From 1dd56ebafdb9ba4d595ffe593bd8f731aad6b5da Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Fri, 9 Apr 2021 14:33:01 -0700 Subject: [PATCH 26/28] QUIC: make update for SSL_new_session_ticket() --- util/libssl.num | 1 + 1 file changed, 1 insertion(+) diff --git a/util/libssl.num b/util/libssl.num index 7c8f9463abff0..e4df4b580fabe 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -514,3 +514,4 @@ SSL_set_quic_transport_version 20012 1_1_1i EXIST::FUNCTION:QUIC SSL_get_peer_quic_transport_version 20013 1_1_1i EXIST::FUNCTION:QUIC SSL_get_quic_transport_version 20014 1_1_1i EXIST::FUNCTION:QUIC SSL_set_quic_early_data_enabled 20015 1_1_1j EXIST::FUNCTION:QUIC +SSL_new_session_ticket 20016 1_1_1k EXIST::FUNCTION: From 56883c37ceeea856215c39da1ecde770801a909c Mon Sep 17 00:00:00 2001 From: Benjamin Kaduk Date: Mon, 4 May 2020 11:50:25 -0700 Subject: [PATCH 27/28] QUIC: Fix up whitespace nits introduced by PR #11416 Expand a couple literal tabs, and de-indent the body of a function. Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/11728) (cherry picked from commit 35774d5594af9beeb73792742b7ed901d202be70) --- ssl/ssl_lib.c | 10 +++++----- ssl/ssl_local.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b94651583cf10..4efed83ab980d 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2217,11 +2217,11 @@ int SSL_renegotiate_pending(const SSL *s) int SSL_new_session_ticket(SSL *s) { - if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server - || !SSL_IS_TLS13(s)) - return 0; - s->ext.extra_tickets_expected++; - return 1; + if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server + || !SSL_IS_TLS13(s)) + return 0; + s->ext.extra_tickets_expected++; + return 1; } long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 194f9674ebdf0..9793306e7582e 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1320,8 +1320,8 @@ struct ssl_st { /* RFC4507 session ticket expected to be received or sent */ int ticket_expected; - /* TLS 1.3 tickets requested by the application. */ - int extra_tickets_expected; + /* TLS 1.3 tickets requested by the application. */ + int extra_tickets_expected; # ifndef OPENSSL_NO_EC size_t ecpointformats_len; /* our list */ From 31a3230cc40d07db716045c2da7d9a95529a9e3b Mon Sep 17 00:00:00 2001 From: kaduk Date: Fri, 9 Apr 2021 14:16:29 -0700 Subject: [PATCH 28/28] QUIC: SSL_new_session_ticket() support (#26) * Let SSL_new_session_ticket() work immediately The initial implementation always deferred the generation of the requested ticket(s) until the next application write, but this means that the ticket cannot be written at all until there is application data ready to write. In some scenarios this application data may never arrive or may take a long time to arrive, so (when already at a record boundary) allow the application to explicitly call SSL_do_handshake() after SSL_new_session_ticket() to force an immediate write, even when there is no application data available. The default behavior remains to defer the generation of the ticket and coalesce the network traffic for the ticket and application data. * Test new SSL_new_session_ticket() functionality Now that we can become "in init" directly after the call, test the various scenarios where explicit SSL_do_handshake() calls can come into play. * Update SSL_new_session_ticket() manual for triggered send Document the recently added functionality. (cherry picked from commit 4fb1ff7ee88caed42acdd944347a4ff272b351a3) --- doc/man3/SSL_CTX_set_num_tickets.pod | 27 ++++++++++++++++----------- ssl/ssl_lib.c | 6 +++++- test/sslapitest.c | 26 +++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/doc/man3/SSL_CTX_set_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod index bc72776755f7d..67d9b7b2cab44 100644 --- a/doc/man3/SSL_CTX_set_num_tickets.pod +++ b/doc/man3/SSL_CTX_set_num_tickets.pod @@ -45,17 +45,22 @@ sent. To issue tickets after other events (such as application-layer changes), SSL_new_session_ticket() is used by a server application to request that a new ticket be sent when it is safe to do so. New tickets are only allowed to be -sent in this manner after the initial handshake has completed, and only for TLS -1.3 connections. The ticket generation and transmission are delayed until the -server is starting a new write operation, so that it is bundled with other -application data being written and properly aligned to a record boundary. -SSL_new_session_ticket() can be called more than once to request additional -tickets be sent; all such requests are queued and written together when it is -safe to do so. Note that a successful return from SSL_new_session_ticket() -indicates only that the request to send a ticket was processed, not that the -ticket itself was sent. To be notified when the ticket itself is sent, a -new-session callback can be registered with L that -will be invoked as the ticket or tickets are generated. +sent in this manner after the initial handshake has completed, and only for +TLS 1.3 connections. By default, the ticket generation and transmission are +delayed until the server is starting a new write operation, so that it is +bundled with other application data being written and properly aligned to a +record boundary. If the connection was at a record boundary when +SSL_new_session_ticket() was called, the ticket can be sent immediately +(without waiting for the next application write) by calling +SSL_do_handshake(). SSL_new_session_ticket() can be called more than once to +request additional tickets be sent; all such requests are queued and written +together when it is safe to do so and triggered by SSL_write() or +SSL_do_handshake(). Note that a successful return from +SSL_new_session_ticket() indicates only that the request to send a ticket was +processed, not that the ticket itself was sent. To be notified when the +ticket itself is sent, a new-session callback can be registered with +L that will be invoked as the ticket or tickets +are generated. SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of tickets set by a previous call to SSL_CTX_set_num_tickets() or diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 4efed83ab980d..098a1842778e9 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2217,10 +2217,14 @@ int SSL_renegotiate_pending(const SSL *s) int SSL_new_session_ticket(SSL *s) { - if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server + /* If we are in init because we're sending tickets, okay to send more. */ + if ((SSL_in_init(s) && s->ext.extra_tickets_expected == 0) + || SSL_IS_FIRST_HANDSHAKE(s) || !s->server || !SSL_IS_TLS13(s)) return 0; s->ext.extra_tickets_expected++; + if (s->rlayer.wbuf[0].left == 0 && !SSL_in_init(s)) + ossl_statem_set_in_init(s, 1); return 1; } diff --git a/test/sslapitest.c b/test/sslapitest.c index 50d97e8c4ae9d..64082ed4e539d 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -1707,11 +1707,22 @@ static int test_extra_tickets(int idx) || !TEST_int_eq(4, new_called)) goto end; + /* Once more, but with SSL_do_handshake() to drive the ticket generation */ + c = '4'; + new_called = 0; + if (!TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_do_handshake(serverssl)) + || !TEST_int_eq(2, new_called) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(4, new_called)) + goto end; + /* * Use the always-retry BIO to exercise the logic that forces ticket * generation to wait until a record boundary. */ - c = '4'; + c = '5'; new_called = 0; tmp = SSL_get_wbio(serverssl); if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) { @@ -1727,9 +1738,14 @@ static int test_extra_tickets(int idx) /* Restore a BIO that will let the write succeed */ SSL_set0_wbio(serverssl, tmp); tmp = NULL; - /* These calls should just queue the request and not send anything. */ + /* + * These calls should just queue the request and not send anything + * even if we explicitly try to hit the state machine. + */ if (!TEST_true(SSL_new_session_ticket(serverssl)) || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_int_eq(0, new_called) + || !TEST_true(SSL_do_handshake(serverssl)) || !TEST_int_eq(0, new_called)) goto end; /* Re-do the write; still no tickets sent */ @@ -1742,8 +1758,12 @@ static int test_extra_tickets(int idx) || !TEST_int_eq(c, buf[0]) || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))) goto end; + /* Even trying to hit the state machine now will still not send tickets */ + if (!TEST_true(SSL_do_handshake(serverssl)) + || !TEST_int_eq(0, new_called)) + goto end; /* Now the *next* write should send the tickets */ - c = '5'; + c = '6'; if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes)) || !TEST_size_t_eq(1, nbytes) || !TEST_int_eq(2, new_called)