diff --git a/CHANGES b/CHANGES index 9d58cb0c58d94..880f9cc23fd6d 100644 --- a/CHANGES +++ b/CHANGES @@ -344,6 +344,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 faf57b155a1cb..22f79638c6850 100755 --- a/Configure +++ b/Configure @@ -401,6 +401,7 @@ my @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -485,7 +486,7 @@ my @disable_cascades = ( "ssl3-method" => [ "ssl3" ], "zlib" => [ "zlib-dynamic" ], "des" => [ "mdc2" ], - "ec" => [ "ecdsa", "ecdh" ], + "ec" => [ "ecdsa", "ecdh", "quic" ], "dgram" => [ "dtls", "sctp" ], "sock" => [ "dgram" ], @@ -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/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..8c8c21a01762b --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +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 +. The OpenSSL `README` can be found at +[README-OpenSSL.md](https://github.com/quictls/openssl/blob/OpenSSL_1_1_1m%2Bquic/README-OpenSSL.md). + +This fork adds APIs 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. + +The APIs 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/) and +[openssl-project email](https://github.com/quictls/openssl/discussions/54) +that provides their view of QUIC support. + +As stated in their blog post, the OpenSSL team is focused on their 3.0 release +(released 2021-09-07), 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` would have a branch named +`openssl-3.0.0+quic` and a release tag of `openssl-3.0.0+quic-release1`. + +How are you keeping current with OpenSSL? +----------------------------------------- +(In other words, "What about rebasing?") + +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. +- 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 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` vs `libcrypto.so.3` +- `libcrypto.so.81.1.1` vs `libcrypto.so.1.1` +- `libssl.so.81.3` vs `libssl.so.3` +- `libssl.so.81.1.1` vs `libssl.so.1.1` + +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! 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/crypto/err/openssl.txt b/crypto/err/openssl.txt index 902e97b843551..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 @@ -1184,7 +1185,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 +1194,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 +1207,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 +1317,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 +1368,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 +1432,10 @@ 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_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 @@ -1464,6 +1477,10 @@ 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_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 @@ -1492,6 +1509,10 @@ 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_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 @@ -1510,6 +1531,10 @@ 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_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 @@ -2745,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 @@ -2894,6 +2921,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/crypto/kdf/hkdf.c b/crypto/kdf/hkdf.c index 25bf4b729f64c..cab5e231fbe63 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, @@ -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; 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_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod index bc031008f2f49..8fafc777eac57 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,43 @@ 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. 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 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/doc/man3/SSL_CTX_set_quic_method.pod b/doc/man3/SSL_CTX_set_quic_method.pod new file mode 100644 index 0000000000000..906c7591d9e55 --- /dev/null +++ b/doc/man3/SSL_CTX_set_quic_method.pod @@ -0,0 +1,268 @@ +=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, +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_early_data_enabled +- 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); + + 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); + void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled); + +=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-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() 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. 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. + +SSL_set_quic_early_data_enabled() enables QUIC early data if a nonzero +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 + +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 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. + +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-27#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. 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. + +=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 an B +(B). + +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. + +=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/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, 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/opensslv.h b/include/openssl/opensslv.h index 261d7cb326cbc..afe0161c9ca2d 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 0x101010dfL -# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.1m 14 Dec 2021" +# define OPENSSL_VERSION_TEXT "OpenSSL 1.1.1m+quic 14 Dec 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/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..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); @@ -2432,6 +2433,66 @@ 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); + +/* 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); + +void SSL_set_quic_early_data_enabled(SSL *ssl, int enabled); + +# endif + # ifdef __cplusplus } # endif diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index 701d61c6e9cb5..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 @@ -97,6 +98,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 +111,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 +218,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 +269,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 +329,8 @@ 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_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 @@ -359,6 +370,8 @@ 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_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 @@ -384,6 +397,8 @@ 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_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 @@ -402,6 +417,8 @@ 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_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 @@ -594,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 @@ -765,6 +783,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..2cbf53265f871 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -148,6 +148,10 @@ extern "C" { /* Temporary extension type */ # define TLSEXT_TYPE_renegotiate 0xff01 +/* ExtensionType value from draft-ietf-quic-tls-27 */ +# 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 */ # define TLSEXT_TYPE_next_proto_neg 13172 diff --git a/ssl/build.info b/ssl/build.info index bb2f1deb53006..497d943900d64 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -13,3 +13,7 @@ SOURCE[../libssl]=\ 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 + +IF[{- !$disabled{quic} -}] + SOURCE[../libssl]=ssl_quic.c statem/statem_quic.c +ENDIF 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/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 55f919fcd58ab..c8fede6014f32 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -2163,3 +2163,37 @@ int ssl_cert_is_disabled(size_t idx) return 1; return 0; } + +#ifndef OPENSSL_NO_QUIC +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; +} +#endif diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 324f2ccbb0de0..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"}, @@ -113,6 +115,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 +127,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 +149,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 +315,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 +395,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 +497,10 @@ 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_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), @@ -551,6 +572,10 @@ 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_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), @@ -597,6 +622,10 @@ 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_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), @@ -629,6 +658,10 @@ 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_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"}, @@ -951,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), @@ -1253,6 +1288,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 9c411a3293962..87a5f32c0104a 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -574,7 +574,56 @@ static void clear_ciphers(SSL *s) ssl_clear_hash_ctx(&s->write_hash); } +#ifndef OPENSSL_NO_QUIC int SSL_clear(SSL *s) +{ + if (!SSL_clear_not_quic(s)) + return 0; + return SSL_clear_quic(s); +} + +int SSL_clear_quic(SSL *s) +{ + 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; + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + s->quic_read_level = ssl_encryption_initial; + s->quic_write_level = ssl_encryption_initial; + s->quic_latest_level_received = ssl_encryption_initial; + 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); + } + s->quic_input_data_tail = NULL; + BUF_MEM_free(s->quic_buf); + s->quic_buf = NULL; + s->quic_next_record_start = 0; + memset(s->client_hand_traffic_secret, 0, EVP_MAX_MD_SIZE); + memset(s->server_hand_traffic_secret, 0, EVP_MAX_MD_SIZE); + memset(s->client_early_traffic_secret, 0, EVP_MAX_MD_SIZE); + /* + * CONFIG - DON'T CLEAR + * s->ext.quic_transport_params + * s->ext.quic_transport_params_len + * s->quic_transport_version + * s->quic_method = NULL; + */ + return 1; +} +#endif + +/* Keep this conditional very local */ +#ifndef OPENSSL_NO_QUIC +int SSL_clear_not_quic(SSL *s) +#else +int SSL_clear(SSL *s) +#endif { if (s->method == NULL) { SSLerr(SSL_F_SSL_CLEAR, SSL_R_NO_METHOD_SPECIFIED); @@ -845,6 +894,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 +1265,20 @@ 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_draft); + OPENSSL_free(s->ext.peer_quic_transport_params); + BUF_MEM_free(s->quic_buf); + 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); @@ -1733,6 +1800,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; @@ -1865,6 +1938,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; @@ -1925,6 +2004,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; @@ -2181,6 +2266,19 @@ int SSL_renegotiate_pending(const SSL *s) return (s->renegotiate != 0); } +int SSL_new_session_ticket(SSL *s) +{ + /* 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; +} + long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) { long l; @@ -3620,6 +3718,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; @@ -3716,6 +3819,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_local.h b/ssl/ssl_local.h index 9f346e30e8f41..5fb569b66ba07 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,8 @@ 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, /* Dummy index - must always be the last entry */ @@ -1063,8 +1072,25 @@ 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 start; /* offset into quic_buf->data */ + 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 { /* * protocol version (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, @@ -1152,6 +1178,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 */ @@ -1289,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 */ @@ -1364,8 +1397,35 @@ 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_draft; + size_t peer_quic_transport_params_draft_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; + 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; + size_t quic_next_record_start; + const SSL_QUIC_METHOD *quic_method; +#endif /* * Parsed form of the ClientHello, kept around across client_hello_cb * calls. @@ -1516,8 +1576,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 @@ -2659,6 +2717,11 @@ void custom_exts_free(custom_ext_methods *exts); void ssl_comp_free_compression_methods_int(void); +#ifndef OPENSSL_NO_QUIC +__owur int SSL_clear_not_quic(SSL *s); +__owur int SSL_clear_quic(SSL *s); +#endif + /* ssl_mcnf.c */ void ssl_ctx_system_config(SSL_CTX *ctx); diff --git a/ssl/ssl_quic.c b/ssl/ssl_quic.c new file mode 100644 index 0000000000000..c5f20c20af7eb --- /dev/null +++ b/ssl/ssl_quic.c @@ -0,0 +1,385 @@ +/* + * 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" + +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) +{ + 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) +{ + /* + * 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, offset; + + 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) + || level < ssl->quic_latest_level_received) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + return 0; + } + + if (len == 0) + return 1; + + 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 (!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; + } + + /* 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); + 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 */ + p = (const uint8_t *)ssl->quic_buf->data + + ssl->quic_next_record_start + 1; + n2l3(p, l); + l += SSL3_HM_HEADER_LENGTH; + /* 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(*qd)); + if (qd == NULL) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_INTERNAL_ERROR); + return 0; + } + + qd->next = NULL; + qd->length = l; + qd->start = ssl->quic_next_record_start; + qd->level = level; + + 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; + ssl->quic_next_record_start += l; + } + + return 1; +} + +int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) +{ + if (ctx->method->version != TLS_ANY_VERSION) + return 0; + 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) +{ + if (ssl->method->version != TLS_ANY_VERSION) + return 0; + 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; + } + + 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; + + /* + * 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) { + 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; + } + + /* if there is no data, return success as BoringSSL */ + 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; +} + +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/extensions.c b/ssl/statem/extensions.c index 0f39275baa015..cc9233b772a8c 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -58,6 +58,11 @@ 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_draft(SSL *s, unsigned int context, int sent); +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 +382,29 @@ static const EXTENSION_DEFINITION ext_defs[] = { tls_construct_certificate_authorities, 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 + | 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, + INVALID_EXTENSION, +#endif { /* Must be immediately before pre_shared_key */ TLSEXT_TYPE_padding, @@ -1745,3 +1773,45 @@ 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_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 9d38ac23b5f06..ae0d074b47409 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1215,7 +1215,49 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, #endif } +#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) +{ + 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->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; + } + + 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 */ @@ -1925,6 +1967,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-27 S4.5 + */ + if (SSL_IS_QUIC(s) && 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; @@ -2012,3 +2066,40 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 1; } +#ifndef OPENSSL_NO_QUIC +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) +{ + 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..1a70f3c6506f9 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1303,6 +1303,43 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context return 1; } +#ifndef OPENSSL_NO_QUIC +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) +{ + 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 */ @@ -1933,12 +1970,20 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, size_t chainidx) { if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) { + uint32_t max_early_data = s->max_early_data; + if (s->max_early_data == 0) return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_QUIC + /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-27 S4.5 */ + if (SSL_IS_QUIC(s)) + 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); @@ -1979,3 +2024,51 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, return EXT_RETURN_SENT; } + +#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) +{ + 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->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; + } + + 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..d984fba93ef7f 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -319,8 +319,13 @@ static int state_machine(SSL *s, int server) * If we are stateless then we already called SSL_clear() - don't do * it again and clear the STATELESS flag itself. */ +#ifndef OPENSSL_NO_QUIC + if ((s->s3->flags & TLS1_FLAGS_STATELESS) == 0 && !SSL_clear_not_quic(s)) + return -1; +#else if ((s->s3->flags & TLS1_FLAGS_STATELESS) == 0 && !SSL_clear(s)) return -1; +#endif } #ifndef OPENSSL_NO_SCTP if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))) { @@ -575,6 +580,11 @@ 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)) { + /* QUIC behaves like DTLS -- all in one go. */ + ret = quic_get_message(s, &mt, &len); +#endif } else { ret = tls_get_message_header(s, &mt); } @@ -604,8 +614,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 +628,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 +910,14 @@ 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)) { + 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_clnt.c b/ssl/statem/statem_clnt.c index d1a39698124b0..a3526d241b97e 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 (SSL_IS_QUIC(s)) { + *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_lib.c b/ssl/statem/statem_lib.c index 695caab3d6282..54aa39ec3c69b 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -43,8 +43,28 @@ int ssl3_do_write(SSL *s, int type) int ret; size_t written = 0; - ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], - s->init_num, &written); +#ifndef OPENSSL_NO_QUIC + 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 { + /* 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) @@ -610,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); @@ -634,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, diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index eae88053dcd75..7bf905f3dfe93 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -95,6 +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); @@ -238,6 +241,14 @@ 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_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 EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, @@ -300,6 +311,16 @@ 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_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); +#endif /* Client Extension processing */ EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, @@ -370,6 +391,15 @@ 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_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); +#endif int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); @@ -415,6 +445,14 @@ 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_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 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..2843938ce2ef4 --- /dev/null +++ b/ssl/statem/statem_quic.c @@ -0,0 +1,118 @@ +/* + * 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" + +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) { + s->rwstate = SSL_READING; + *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; + } + + /* 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); + *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); + *mt = *len = 0; + return 0; + } + + /* Copy buffered data */ + 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) + 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; + } + /* 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 + * 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 (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; +} diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index d701c46b43b5a..2ed41c72a9059 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -72,7 +72,8 @@ 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) { st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA; return 1; @@ -436,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; @@ -526,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; } @@ -722,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 @@ -964,6 +972,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; @@ -1578,6 +1596,16 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) goto err; } } +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + /* Any other QUIC checks on ClientHello here */ + if (clienthello->session_id_len > 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PROCESS_CLIENT_HELLO, + SSL_R_LENGTH_MISMATCH); + goto err; + } + } +#endif } if (!PACKET_copy_all(&compression, clienthello->compressions, @@ -4174,10 +4202,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); } diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index b8fb07f210ef2..e4c72cdb28474 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -434,27 +434,236 @@ 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; + 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); + + 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; + } + + 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; + } + + 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, + 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 { + /* + * 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, + s->server_app_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, + s->server_app_traffic_secret, hashlen)) { + /* 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 { + /* is_client_write || is_server_read */ + + 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; + /* + * 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 (level != ssl_encryption_early_data) { + 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 +679,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); diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index e4a0b180d7be3..b63e8504c2d58 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -1628,6 +1628,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) { @@ -1948,6 +1989,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, diff --git a/test/sslapitest.c b/test/sslapitest.c index 21322ceec5e4e..91ac9c22eec0e 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -1627,6 +1627,168 @@ 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; + + /* 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 = '5'; + 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 + * 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 */ + 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; + /* 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 = '6'; + 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 @@ -6848,6 +7010,359 @@ 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_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; + } + + 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_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; + 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; + + 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(), + 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, + 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)) + || !TEST_true(test_quic_api_set_versions(clientssl, clnt)) + || !TEST_true(test_quic_api_set_versions(serverssl, srvr)) + || !TEST_true(create_bare_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE, 0)) + || !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, 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, strlen(server_str)+1)) + goto end; + + /* Deal with two NewSessionTickets */ + if (!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; + + 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: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + 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; +} + +/* + * 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_bare_ssl_connection(*serverssl, *clientssl, + SSL_ERROR_NONE, 0))) + return 0; + + /* Deal with two NewSessionTickets */ + if (!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_bare_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE, 0)) + || !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) { if (!TEST_ptr(certsdir = test_get_argument(0)) @@ -6896,6 +7411,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); @@ -6976,6 +7492,10 @@ int setup_tests(void) #endif ADD_TEST(test_set_alpn); 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/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..e4df4b580fabe 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -498,3 +498,20 @@ 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: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 +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 +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 +SSL_new_session_ticket 20016 1_1_1k EXIST::FUNCTION: 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; 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