Multi-Stream API
authorHugo Landau <hlandau@openssl.org>
Wed, 29 Mar 2023 06:58:15 +0000 (07:58 +0100)
committerPauli <pauli@openssl.org>
Tue, 27 Jun 2023 22:05:41 +0000 (08:05 +1000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19769)

doc/designs/quic-design/quic-api-ssl-funcs.md
doc/designs/quic-design/quic-api.md

index d43e58684aebd07123dce53b2c63d68021ab7821..c50a6b1cd5b37e41769603724b5cc825b7b8524f 100644 (file)
@@ -87,6 +87,9 @@ Notes:
 - †9: QUIC always uses AES-128-GCM initially. We need to determine when and
   what ciphers we report as being in use.
 - †10: Not supporting async for now.
+- †11: Since these functions only configure cipher suite lists used for TLSv1.2,
+  which is never used for QUIC, they do not require changes, and we can allow
+  applications to configure these lists freely, as they will be ignored.
 
 | API Item | Cat. | Sema. | Appl. | Impl. Req. | Status |
 |----------|----------|-----------|---------------|----------------|--------|
@@ -140,7 +143,7 @@ Notes:
 | `SSL_CTX_up_ref` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_CTX_free` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_new` | Object | 🟩U | 🟩A | 🟧QSI | 🟢Done |
-| `SSL_dup` | Object | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
+| `SSL_dup` | Object | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¥FC | ð\9f\9f¢Done |
 | `SSL_up_ref` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_free` | Object | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_is_dtls` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
@@ -153,28 +156,27 @@ Notes:
 | **⇒ Method Manipulation** | |
 | `SSL_CTX_get_ssl_method` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_get_ssl_method` | Object | 🟩U | 🟩A | 🟩NC | 🟢Done |
-| `SSL_CTX_set_ssl_method` | Object | 🟥TBD | 🟩A | 🟧QSI | 🟠Design TBD |
-| `SSL_set_ssl_method` | Object | 🟥TBD | 🟩A | 🟧QSI | 🟠Design TBD |
+| `SSL_set_ssl_method` | Object | 🟥TBD | 🟩A | 🟧QSI | 🟢Done |
 | **⇒ SRTP** | |
-| `SSL_get_selected_srtp_profile` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* | ð\9f\9f¡TODO |
-| `SSL_get_srtp_profiles` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_tlsext_use_srtp` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* | ð\9f\9f¡TODO |
-| `SSL_set_tlsext_use_srtp` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* | ð\9f\9f¡TODO |
+| `SSL_get_selected_srtp_profile` | HL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_get_srtp_profiles` | HL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_CTX_set_tlsext_use_srtp` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_set_tlsext_use_srtp` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC\* | ð\9f\9f¢Done |
 | **⇒ Ciphersuite Configuration** | |
-| `SSL_CTX_set_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_CTX_set_ciphersuites` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_CTX_get_ciphers` | HL | 🟩U | 🟩A | 🟨C\* †1 | 🟡TODO |
-| `SSL_set_ciphersuites` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_get1_supported_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_bytes_to_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_get_ciphers` | HL | 🟩U | 🟩A | 🟨C\* †1 | 🟡TODO |
-| `SSL_get_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
-| `SSL_set_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¡TODO |
+| `SSL_CTX_set_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 11 | ð\9f\9f¢Done |
+| `SSL_CTX_set_ciphersuites` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¢Done |
+| `SSL_CTX_get_ciphers` | HL | 🟩U | 🟩A |🟩NC\* | 🟢Done |
+| `SSL_set_ciphersuites` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* | ð\9f\9f¢Done |
+| `SSL_get1_supported_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 1 | ð\9f\9f¢Done |
+| `SSL_bytes_to_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* | ð\9f\9f¢Done |
+| `SSL_get_ciphers` | HL | 🟩U | 🟩A |  🟩NC\* | 🟢Done |
+| `SSL_get_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 11 | ð\9f\9f¢Done |
+| `SSL_set_cipher_list` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 11 | ð\9f\9f¢Done |
 | **⇒ Negotiated Ciphersuite Queries** | |
-| `SSL_get_current_cipher` | HL | 🟩U | 🟩A | 🟨C\* †9 | 🟠Design TBD |
-| `SSL_get_pending_cipher` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 9 | ð\9f\9f Design TBD |
-| `SSL_get_shared_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 9 | ð\9f\9f Design TBD |
-| `SSL_get_client_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* â\80 9 | ð\9f\9f Design TBD |
+| `SSL_get_current_cipher` | HL | 🟩U | 🟩A |🟩NC\* †9 | 🟢Done |
+| `SSL_get_pending_cipher` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 9 | ð\9f\9f¢Done |
+| `SSL_get_shared_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 9 | ð\9f\9f¢Done |
+| `SSL_get_client_ciphers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 9 | ð\9f\9f¢Done |
 | `SSL_get_current_compression` | HL | 🟩U | 🟩A | 🟩HLNC | 🟢Done |
 | `SSL_get_current_expansion` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_get_shared_sigalgs` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
@@ -191,9 +193,9 @@ Notes:
 | `SSL_get0_alpn_selected` | HL | 🟩U | 🟩A | 🟨C\* †2 | 🟡TODO |
 | `SSL_CTX_set_alpn_protos` | HL | 🟩U | 🟩A | 🟨C\* †2 | 🟡TODO |
 | **⇒ NPN** | †3 |
-| `SSL_CTX_set_next_proto_select_cb` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* â\80 3 | ð\9f\9f¡TODO |
-| `SSL_CTX_set_next_protos_advertised_cb` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* â\80 3 | ð\9f\9f¡TODO |
-| `SSL_get0_next_proto_negotiated` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* â\80 3 | ð\9f\9f¡TODO |
+| `SSL_CTX_set_next_proto_select_cb` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* â\80 3 | ð\9f\9f¢Done |
+| `SSL_CTX_set_next_protos_advertised_cb` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C\* â\80 3 | ð\9f\9f¢Done |
+| `SSL_get0_next_proto_negotiated` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC\* â\80 3 | ð\9f\9f¢Done |
 | **⇒ Narrow Waist Interface** | †4 |
 | `SSL_CTX_ctrl` | Object | 🟩U | 🟩A | 🟩NC\* †4 | 🟢Done |
 | `SSL_ctrl` | Object | 🟩U | 🟩A | 🟩NC\* †4 | 🟢Done |
@@ -394,11 +396,11 @@ Notes:
 | `SSL_renegotiate_abbreviated` | HL | 🟩U | 🟥FC | 🟩NC\* †5 | 🟢Done |
 | `SSL_renegotiate_pending` | HL | 🟩U | 🟧NO | 🟩NC\* †5 | 🟢Done |
 | **⇒ Options** | |
-| `SSL_CTX_clear_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
-| `SSL_CTX_set_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
+| `SSL_CTX_clear_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_CTX_set_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
 | `SSL_CTX_get_options` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
-| `SSL_clear_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
-| `SSL_set_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
+| `SSL_clear_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_set_options` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
 | `SSL_get_options` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | **⇒ Configuration** | |
 | `SSL_CONF_CTX_new` | Global | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
@@ -519,7 +521,7 @@ Notes:
 | `SSL_set_num_tickets` | HL | 🟩U | 🟩A | 🟩NC\* †7 | 🟢Done |
 | `SSL_CTX_get_num_tickets` | HL | 🟩U | 🟩A | 🟩NC\* †7 | 🟢Done |
 | `SSL_CTX_set_num_tickets` | HL | 🟩U | 🟩A | 🟩NC\* †7 | 🟢Done |
-| `SSL_new_session_ticket` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¡TODO |
+| `SSL_new_session_ticket` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* â\80 7 | ð\9f\9f¢Done |
 | `SSL_set_session_ticket_ext` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_set_session_ticket_ext_cb` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_CTX_set_tlsext_ticket_key_evp_cb` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
@@ -559,9 +561,9 @@ Notes:
 | `SSL_CTX_use_serverinfo_ex` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_CTX_use_serverinfo_file` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | **⇒ Post-Handshake Authentication** | |
-| `SSL_verify_client_post_handshake` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¡TODO |
-| `SSL_CTX_set_post_handshake_auth` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¡TODO |
-| `SSL_set_post_handshake_auth` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¡TODO |
+| `SSL_verify_client_post_handshake` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¢Done |
+| `SSL_CTX_set_post_handshake_auth` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¢Done |
+| `SSL_set_post_handshake_auth` | HL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* â\80 8 | ð\9f\9f¢Done |
 | **⇒ DH Parameters** | |
 | `SSL_CTX_set_dh_auto` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_set_dh_auto` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
@@ -576,23 +578,23 @@ Notes:
 | `SSL_in_before` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_is_init_finished` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_get_state` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
-| `SSL_rstate_string` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_rstate_string_long` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_state_string` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_state_string_long` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
+| `SSL_rstate_string` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_rstate_string_long` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_state_string` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* | ð\9f\9f¢Done |
+| `SSL_state_string_long` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* | ð\9f\9f¢Done |
 | **⇒ Data Path and CSSM** | |
 | `SSL_set_connect_state` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_set_accept_state` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟢Done |
-| `SSL_is_server` | CSSM | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
+| `SSL_is_server` | CSSM | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC\* | ð\9f\9f¢Done |
 | `SSL_peek` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_peek_ex` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_read` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_read_ex` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_write` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_write_ex` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
-| `SSL_sendfile` | ADP | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_pending` | ADP | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_has_pending` | ADP | TBD | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
+| `SSL_sendfile` | ADP | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC\* | ð\9f\9f¢Done |
+| `SSL_pending` | ADP | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_has_pending` | ADP | TBD | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
 | `SSL_accept` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_connect` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_do_handshake` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟢Done |
@@ -605,15 +607,15 @@ Notes:
 | `SSL_get_rfd` | NDP | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_get_wfd` | NDP | 🟩U | 🟩A | 🟩NC | 🟢Done |
 | `SSL_get_fd` | NDP | 🟩U | 🟩A | 🟩NC | 🟢Done |
-| `SSL_set_rfd` | NDP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
-| `SSL_set_wfd` | NDP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
-| `SSL_set_fd` | NDP | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
-| `SSL_key_update` | RL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_get_key_update_type` | RL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f Design TBD |
-| `SSL_clear`  (connection) | CSSM | TBD | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
+| `SSL_set_rfd` | NDP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_set_wfd` | NDP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_set_fd` | NDP | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_key_update` | RL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_get_key_update_type` | RL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_clear`  (connection) | CSSM | TBD | ð\9f\9f©A | ð\9f\9f¥FC | ð\9f\9f¢Done |
 | `SSL_clear`  (stream) | CSSM | TBD | 🟩A | 🟧QSI | 🟠Design TBD |
 | `SSL_shutdown` | CSSM | 🟧C | 🟩A | 🟧QSI | 🟡TODO |
-| `SSL_want` | ADP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
+| `SSL_want` | ADP | ð\9f\9f§C | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
 | `BIO_new_ssl_connect` | Global | 🟩U | 🟩A | 🟧QSI | 🟡TODO |
 | `BIO_new_buffer_ssl_connect` | Global | 🟩U | 🟦U | 🟧QSI | 🟡TODO |
 | `SSL_get_shutdown` | CSSM | 🟩U | 🟩A | 🟧QSI | 🟠Design TBD |
@@ -632,17 +634,31 @@ Notes:
 | `SSL_shutdown_ex` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
 | `SSL_stream_conclude` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
 | `SSL_stream_reset` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
-| `SSL_get_stream_state` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
-| `SSL_get_stream_error_code` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_read_state` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_write_state` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_read_error_code` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_write_error_code` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
 | `SSL_get_conn_close_info` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| **⇒ New APIs for Multi-Stream** | |
+| `SSL_get0_connection` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_is_connection` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_id` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_stream_type` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_new_stream` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_accept_stream` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_get_accept_stream_queue_len` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_set_default_stream_mode` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_set_incoming_stream_reject_policy` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_detach_stream` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
+| `SSL_attach_stream` | CSSM | 🟦N | 🟩A | 🟥QSA | 🟡TODO |
 | **⇒ Currently Not Supported** | |
-| `SSL_copy_session_id` | Special | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `BIO_ssl_copy_session_id` | Special | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_quiet_shutdown` | CSSM | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_get_quiet_shutdown` | CSSM | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_quiet_shutdown` | CSSM | 🟩U | 🟥FC | 🟨C* | 🟡TODO |
-| `SSL_get_quiet_shutdown` | CSSM | 🟩U | 🟧NO | 🟨C* | 🟡TODO |
-| `SSL_CTX_set_ssl_version` | HL | 🟩U | 🟥FC | 🟨C* | 🟡TODO |
+| `SSL_copy_session_id` | Special | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `BIO_ssl_copy_session_id` | Special | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_set_quiet_shutdown` | CSSM | ð\9f\9f©U | ð\9f\9f¦U | ð\9f\9f©NC | ð\9f\9f¢Done |
+| `SSL_CTX_get_quiet_shutdown` | CSSM | ð\9f\9f©U | ð\9f\9f¦U | ð\9f\9f©NC | ð\9f\9f¢Done |
+| `SSL_set_quiet_shutdown` | CSSM | 🟩U | 🟥FC | 🟨C | 🟢Done |
+| `SSL_get_quiet_shutdown` | CSSM | 🟩U | 🟧NO | 🟨C | 🟢Done |
+| `SSL_CTX_set_ssl_version` | HL | 🟩U | 🟥FC | 🟨C | 🟢Done |
 | **⇒ Async** | |
 | `SSL_CTX_set_async_callback` | Async | 🟩U | 🟧NO | 🟩NC* †10 | 🟢Done |
 | `SSL_set_async_callback` | Async | 🟩U | 🟧NO | 🟩NC* †10 | 🟢Done |
@@ -653,67 +669,67 @@ Notes:
 | `SSL_get_all_async_fds` | Async | 🟩U | 🟧NO | 🟩NC* †10 | 🟢Done |
 | `SSL_get_changed_async_fds` | Async | 🟩U | 🟧NO | 🟩NC* †10 | 🟢Done |
 | **⇒ Readahead** | |
-| `SSL_CTX_get_default_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_get_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_read_ahead` | RL | 🟩U | 🟧NO | 🟨C* | 🟡TODO |
-| `SSL_get_read_ahead` | RL | 🟩U | 🟧NO | 🟨C* | 🟡TODO |
-| `SSL_set_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_default_read_buffer_len` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_default_read_buffer_len` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_CTX_get_default_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_CTX_get_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f©NC* |ð\9f\9f¢Done  |
+| `SSL_CTX_set_read_ahead` | RL | 🟩U | 🟧NO | 🟨C* |🟢Done  |
+| `SSL_get_read_ahead` | RL | 🟩U | 🟧NO | 🟨C* |🟢Done |
+| `SSL_set_read_ahead` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_set_default_read_buffer_len` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_default_read_buffer_len` | RL | ð\9f\9f©U | ð\9f\9f§NO | ð\9f\9f¨C* | ð\9f\9f¢Done |
 | **⇒ Record Padding and Fragmentation** | |
-| `SSL_CTX_set_record_padding_callback` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_record_padding_callback` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_get_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_get_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_record_padding_callback_arg` | RL | 🟩U | 🟥FC | 🟨C* | 🟡TODO |
-| `SSL_CTX_set_block_padding` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_block_padding` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_tlsext_max_fragment_length` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_tlsext_max_fragment_length` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_CTX_set_record_padding_callback` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_record_padding_callback` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_get_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_CTX_set_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_get_record_padding_callback_arg` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_record_padding_callback_arg` | RL | 🟩U | 🟥FC |🟩NC* | 🟢Done |
+| `SSL_CTX_set_block_padding` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_block_padding` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_set_tlsext_max_fragment_length` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_tlsext_max_fragment_length` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
 | **⇒ Stateless/HelloRetryRequest** | |
-| `SSL_stateless` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_stateless_cookie_generate_cb` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_stateless_cookie_verify_cb` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_stateless` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_set_stateless_cookie_generate_cb` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_CTX_set_stateless_cookie_verify_cb` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
 | **⇒ Early Data/0-RTT** | |
-| `SSL_CTX_set_allow_early_data_cb` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_allow_early_data_cb` | 0-RTT | 🟩U | 🟥FC | 🟨C* | 🟡TODO |
-| `SSL_CTX_get_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_get_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_get_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTX_set_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_get_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_set_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_read_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_write_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_get_early_data_status` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_CTX_set_allow_early_data_cb` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_allow_early_data_cb` | 0-RTT | 🟩U | 🟥FC | 🟨C* |🟢Done  |
+| `SSL_CTX_get_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_CTX_set_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_get_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_recv_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTX_get_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done  |
+| `SSL_CTX_set_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_get_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
+| `SSL_set_max_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done  |
+| `SSL_read_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_write_early_data` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_get_early_data_status` | 0-RTT | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f©NC* | ð\9f\9f¢Done |
 | **⇒ Miscellaneous** | |
 | `DTLSv1_listen` | RL | 🟩U | 🟦U | 🟩NC | 🟢Done |
 | `DTLS_set_timer_cb` | NDP | 🟩U | 🟦U | 🟩NC | 🟢Done |
 | `DTLS_get_data_mtu` | NDP | 🟩U | 🟦U | 🟩NC | 🟢Done |
 | `SSL_get_ex_data_X509_STORE_CTX_idx` | Global | 🟩U | 🟦U | 🟩NC | 🟢Done |
 | `BIO_ssl_shutdown` | Global | 🟩U | 🟩A | 🟩NC | 🟢Done |
-| `SSL_alloc_buffers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
-| `SSL_free_buffers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f Design TBD |
+| `SSL_alloc_buffers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
+| `SSL_free_buffers` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C\* | ð\9f\9f¢Done |
 | `SSL_trace` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | `SSL_set_debug` | HL | 🟩U | 🟩A | 🟩NC\* | 🟢Done |
 | **⇒ Controls** | |
-| `SSL_CTRL_MODE` | Special | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
-| `SSL_CTRL_CLEAR_MODE` | Special | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¡TODO |
+| `SSL_CTRL_MODE` | Special | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
+| `SSL_CTRL_CLEAR_MODE` | Special | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f§QSI | ð\9f\9f¢Done |
 | `SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS` | HL | 🟩U | 🟧NO | 🟩NC* | 🟢Done |
 | `SSL_CTRL_GET_NUM_RENEGOTIATIONS` | HL | 🟩U | 🟧NO | 🟩NC* | 🟢Done |
 | `SSL_CTRL_GET_TOTAL_RENEGOTIATIONS` | HL | 🟩U | 🟧NO | 🟩NC* | 🟢Done |
 | `SSL_CTRL_GET_RI_SUPPORT` | HL | 🟩U | 🟧NO | 🟩NC* | 🟢Done |
 | `SSL_CTRL_GET_READ_AHEAD` | HL | 🟩U | 🟧NO | 🟩NC* | 🟢Done |
-| `SSL_CTRL_SET_READ_AHEAD` | HL | 🟩U | 🟥FC | 🟨C* | 🟡TODO |
-| `SSL_CTRL_SET_MAX_PIPELINES` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTRL_SET_MAX_SEND_FRAGMENT` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTRL_SET_SPLIT_SEND_FRAGMENT` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_CTRL_SET_READ_AHEAD` | HL | 🟩U | 🟥FC | 🟨C* |🟢Done |
+| `SSL_CTRL_SET_MAX_PIPELINES` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTRL_SET_MAX_SEND_FRAGMENT` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTRL_SET_SPLIT_SEND_FRAGMENT` | RL | ð\9f\9f©U | ð\9f\9f¥FC | ð\9f\9f¨C* | ð\9f\9f¢Done |
 | `SSL_CTRL_SET_MTU` | RL | 🟩U | 🟥FC | 🟩NC* | 🟢Done |
-| `SSL_CTRL_SET_MAX_PROTO_VERSION` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C* | ð\9f\9f¡TODO |
-| `SSL_CTRL_SET_MIN_PROTO_VERSION` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C* | ð\9f\9f¡TODO |
+| `SSL_CTRL_SET_MAX_PROTO_VERSION` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f¨C* | ð\9f\9f¢Done |
+| `SSL_CTRL_SET_MIN_PROTO_VERSION` | HL | ð\9f\9f©U | ð\9f\9f©A | ð\9f\9f©NC* | ð\9f\9f¢Done |
 | `SSL_CTRL_GET_MAX_PROTO_VERSION` | HL | 🟩U | 🟩A | 🟩NC* | 🟢Done |
 | `SSL_CTRL_GET_MIN_PROTO_VERSION` | HL | 🟩U | 🟩A | 🟩NC* | 🟢Done |
 | `SSL_CTRL_BUILD_CERT_CHAIN` | HL | 🟩U | 🟩A | 🟩NC* | 🟢Done |
@@ -810,5 +826,170 @@ Notes:
 | `SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER` | ADP | 🟩U | 🟩A | 🟧QSI | 🟢Done |
 | `SSL_MODE_RELEASE_BUFFERS` | ADP | 🟩U | 🟧NO | 🟩NC | 🟢Done |
 | `SSL_MODE_ASYNC` | ADP | 🟩U | 🟧NO | 🟩NC | 🟢Done |
-| `SSL_MODE_AUTO_RETRY` | ADP | TBD | TBD | TBD | 🔴Pending Triage |
-| `SSL_MODE_SEND_FALLBACK_SCSV` | HL | 🟩U | 🟩A | 🟨C\* | 🟡TODO |
+| `SSL_MODE_AUTO_RETRY` | ADP | 🟩U | 🟧NO | 🟩NC | 🟢Done |
+| `SSL_MODE_SEND_FALLBACK_SCSV` | HL | 🟩U | 🟩U | 🟩NC | 🟢Done |
+
+Q&A For TLS-Related Calls
+-------------------------
+
+### What should `SSL_get_current_cipher`, `SSL_get_pending_cipher`, etc. do?
+
+QUIC always uses AES-128-GCM for Initial packets. At this time the handshake
+layer has not negotiated a ciphersuite so it has no “current” cipher. We could
+return AES-128-GCM here, but it seems reasonable to just return NULL as the
+encryption is mostly for protection against accidential modification and not
+“real” encryption. From the perspective of the Handshake layer encryption is not
+active yet. An application using QUIC can always interpret NULL as meaning
+AES-128-GCM is being used if needed as this is implied by using QUIC.
+
+### What should `SSL_CTX_set_cipher_list` do?
+
+Since this function configures the cipher list for TLSv1.2 and below only, there
+is no need to restrict it as TLSv1.3 is required for QUIC. For the sake of
+application compatibility, applications can still configure the TLSv1.2 cipher
+list; it will always be ignored.
+
+### What should `SSL_get_current_cipher` and similar do?
+
+QUIC always uses AES-128-GCM encryption initially, so we could either return
+AES-128-GCM where the handshake has not yet negotiated another algorithm or
+return NULL here.
+
+A. We return NULL here, because it allows applications to detect if a
+ciphersuite has been negotiated and NULL can be used to infer that Initial
+encryption is still being used. This also minimises the changes needed to the
+implementation.
+
+### What SSL options should be supported?
+
+Options we explicitly want to support:
+
+- `SSL_OP_CIPHER_SERVER_PREFERENCE`
+- `SSL_OP_DISABLE_TLSEXT_CA_NAMES`
+- `SSL_OP_NO_TX_CERTIFICATE_COMPRESSION`
+- `SSL_OP_NO_RX_CERTIFICATE_COMPRESSION`
+- `SSL_OP_PRIORITIZE_CHACHA`
+- `SSL_OP_NO_TICKET`
+
+Options we do not yet support but could support in the future, currently no-ops:
+
+- `SSL_OP_CLEANSE_PLAINTEXT`
+- `SSL_OP_NO_QUERY_MTU`
+- `SSL_OP_NO_ANTI_REPLAY`
+
+The following options must be explicitly forbidden:
+
+- `SSL_OP_NO_TLSv1_3` — TLSv1.3 is required for QUIC
+- `SSL_OP_ENABLE_MIDDLEBOX_COMPAT` — forbidden by QUIC RFCs
+- `SSL_OP_ENABLE_KTLS` — not currently supported for QUIC
+- `SSL_OP_SAFARI_ECDHE_ECDSA_BUG`
+- `SSL_OP_TLSEXT_PADDING`
+- `SSL_OP_TLS_ROLLBACK_BUG`
+- `SSL_OP_IGNORE_UNEXPECTED_EOF`
+- `SSL_OP_ALLOW_NO_DHE_KEX`
+
+The following options are ignored for TLSv1.3 or otherwise not applicable and
+may therefore be settable but ignored. We take this approach on the grounds
+that it is harmless and applications might want to see that options have been
+correctly set for protocols unrelated to QUIC.
+
+- `SSL_OP_CRYPTOPRO_TLSEXT_BUG`
+- `SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS`
+- `SSL_OP_ALLOW_CLIENT_RENEGOTIATION`
+- `SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION`
+- `SSL_OP_CISCO_ANYCONNECT`
+- `SSL_OP_COOKIE_EXCHANGE`
+- `SSL_OP_LEGACY_SERVER_CONNECT`
+- `SSL_OP_NO_COMPRESSION`
+- `SSL_OP_NO_ENCRYPT_THEN_MAC`
+- `SSL_OP_NO_EXTENDED_MASTER_SECRET`
+- `SSL_OP_NO_RENEGOTIATION`
+- `SSL_OP_NO_RESSION_RESUMPTION_ON_NEGOTIATION`
+- `SSL_OP_NO_SSLv3`
+- `SSL_OP_NO_TLSv1`
+- `SSL_OP_NO_TLSv1_1`
+- `SSL_OP_NO_TLSv1_2`
+- `SSL_OP_NO_DTLSv1`
+- `SSL_OP_NO_DTLSv1_2`
+
+### What should `SSL_rstate_string` and `SSL_state_string` do?
+
+SSL_state_string is highly handshake layer specific, so it makes sense to just
+forward to the handshake layer.
+
+SSL_rstate_string is record layer specific. A cursory evaluation of usage via
+GitHub code search did not appear to identify much usage of this function other
+than for debug output; i.e., there seems to be little usage of this in a way
+that depends on the output for the purposes of control flow. Since there is not
+really any direct correspondence to the QUIC record layer, we conservatively
+define the output of this function as "unknown".
+
+TODO: forbid NPN
+TODO: enforce TLSv1.3
+TODO: forbid PHA            - DONE
+TODO: forbid middlebox compat mode in a deeper way?
+TODO: new_session_ticket doesn't need modifying as such, but ticket machinery
+      will
+
+### What should `SSL_pending` and `SSL_has_pending` do?
+
+`SSL_pending` traditionally yields the number of bytes buffered inside a SSL
+object available for immediate reading. For QUIC, we can just make this report
+the current size of the receive stream buffer.
+
+`SSL_has_pending` returns a boolean value indicating whether there is processed
+or unprocessed incoming data pending. There is no direct correspondence to
+QUIC, so there are various implementation options:
+
+- `SSL_pending() > 0`
+- `SSL_pending() > 0 || pending URXEs or RXEs exist`
+
+The latter can probably be viewed as more of a direct correspondence to the
+design intent of the API, so we go with this.
+
+### What should `SSL_alloc_buffers` and `SSL_free_buffers` do?
+
+These do not really correspond to our internal architecture for QUIC. Since
+internal buffers are always available, `SSL_alloc_buffers` can simply always
+return 1. `SSL_free_buffers` can always return 0, as though the buffers are in
+use, which they generally will be.
+
+### What should `SSL_key_update` and `SSL_get_key_update_type`?
+
+`SSL_key_update` can trigger a TX record layer key update, which will cause the
+peer to respond with a key update in turn. The update occurs asynchronously
+at next transmission, not immediately.
+
+`SSL_get_key_update_type` returns an enumerated value which is only relevant to
+the TLSv1.3 protocol; for QUIC, it will always return `SSL_KEY_UPDATE_NONE`.
+
+### What should `SSL_MODE_AUTO_RETRY` do?
+
+The absence of `SSL_MODE_AUTO_RETRY` causes `SSL_read`/`SSL_write` on a normal
+TLS connection to potentially return due to internal handshake message
+processing. This does not really make sense for our QUIC implementation,
+therefore we always act as though `SSL_MODE_AUTO_RETRY` is on, and this mode is
+ignored.
+
+### What should `SSL_MODE_SEND_FALLBACK_SCSV` do?
+
+This is not relevant to QUIC because this functionality relates to protocol
+version downgrade attack protection and QUIC only supports TLSv1.3. Thus,
+it is ignored.
+
+### What should `SSL_CTX_set_ssl_version` do?
+
+This is a deprecated function, so it needn't be supported for QUIC. Fail closed.
+
+### What should `SSL_set_ssl_method` do?
+
+For now we can avoid supporting this for QUIC. Supporting this would be rather
+hairy.
+
+### What should `SSL_set_shutdown` do?
+
+TBD.
+
+### What should `SSL_dup` and `SSL_clear` do?
+
+These may be tricky to support. Currently they are blocked.
index bb7bc28154282791f63831e99aa7cf5c115426b8..b5a81de87a4d802372fa13371409587eae354d0b 100644 (file)
@@ -29,7 +29,7 @@ designs and the relevant design decisions.
       - [`SSL_get_[rw]fd`](#-ssl-get--rw-fd-)
       - [`SSL_CTRL_MODE`, `SSL_CTRL_CLEAR_MODE`](#-ssl-ctrl-mode----ssl-ctrl-clear-mode-)
       - [SSL Modes](#ssl-modes)
-    + [New APIs](#new-apis)
+    + [New APIs for Single-Stream Operation](#new-apis-for-single-stream-operation)
       - [`SSL_tick`](#-ssl-tick-)
       - [`SSL_get_tick_timeout`](#-ssl-get-tick-timeout-)
       - [`SSL_set_blocking_mode`, `SSL_get_blocking_mode`](#-ssl-set-blocking-mode----ssl-get-blocking-mode-)
@@ -41,8 +41,23 @@ designs and the relevant design decisions.
       - [`SSL_stream_conclude`](#-ssl-stream-conclude-)
       - [`SSL_stream_reset`](#-ssl-stream-reset-)
       - [`SSL_get_stream_state`](#-ssl-get-stream-state-)
-      - [`SSL_get_stream_error_code`](#-ssl-get-stream-error-code-)
+      - [`SSL_get_stream_read_error_code`, `SSL_get_stream_write_error_code`](#-ssl-get-stream-read-error-code----ssl-get-stream-write-error-code-)
       - [`SSL_get_conn_close_info`](#-ssl-get-conn-close-info-)
+    + [New APIs for Multi-Stream Operation](#new-apis-for-multi-stream-operation)
+      - [Notes on Multi-Threaded Operation](#notes-on-multi-threaded-operation)
+      - [Notes on Blocking](#notes-on-blocking)
+      - [Notes on Application-Level Polling](#notes-on-application-level-polling)
+      - [`SSL_get0_connection`](#-ssl-get0-connection-)
+      - [`SSL_is_connection`](#-ssl-is-connection-)
+      - [`SSL_get_stream_type`](#-ssl-get-stream-type-)
+      - [`SSL_get_stream_id`](#-ssl-get-stream-id-)
+      - [`SSL_new_stream`](#-ssl-new-stream-)
+      - [`SSL_accept_stream`](#-ssl-accept-stream-)
+      - [`SSL_get_accept_stream_queue_len`](#-ssl-get-accept-stream-queue-len-)
+      - [`SSL_set_incoming_stream_reject_policy`](#-ssl-set-incoming-stream-reject-policy-)
+      - [`SSL_set_default_stream_mode`](#-ssl-set-default-stream-mode-)
+      - [`SSL_detach_stream`](#-ssl-detach-stream-)
+      - [`SSL_attach_stream`](#-ssl-attach-stream-)
     + [Future APIs](#future-apis)
   * [BIO Objects](#bio-objects)
     + [Existing APIs](#existing-apis-1)
@@ -50,7 +65,7 @@ designs and the relevant design decisions.
       - [`BIO_new_bio_pair`](#-bio-new-bio-pair-)
       - [Interactions with `BIO_f_buffer`](#interactions-with--bio-f-buffer-)
       - [MTU Signalling](#mtu-signalling)
-    + [New APIs](#new-apis-1)
+    + [New APIs](#new-apis)
       - [`BIO_sendmmsg` and `BIO_recvmmsg`](#-bio-sendmmsg--and--bio-recvmmsg-)
       - [Truncation Mode](#truncation-mode)
       - [Capability Negotiation](#capability-negotiation)
@@ -60,7 +75,6 @@ designs and the relevant design decisions.
       - [`BIO_s_dgram_mem`](#-bio-s-dgram-mem-)
       - [`BIO_err_is_non_fatal`](#-bio-err-is-non-fatal-)
   * [Q & A](#q---a)
-  * [Implementation Status](#implementation-status)
 
 Overview and Implementation Status
 ----------------------------------
@@ -150,6 +164,9 @@ Each API listed below has an information table with the following fields:
 
     - **CS:** Not handshake-layer related. Can be used on any QUIC SSL object.
 
+    - **S**: Requires a QUIC stream SSL object or a QUIC connection SSL object
+      with a default stream attached.
+
 ### Existing APIs
 
 #### `SSL_set_connect_state`
@@ -411,7 +428,7 @@ Should not require any changes.
 
 - `SSL_MODE_ASYNC`: TBD.
 
-### New APIs
+### New APIs for Single-Stream Operation
 
 TBD: Should any of these be implemented as ctrls rather than actual functions?
 
@@ -721,6 +738,22 @@ no-ops. This is considered a success case.
  * stream.
  */
 #define SSL_STREAM_STATE_NONE                   0
+
+/*
+ * The read or write part of the stream is still available and has not been
+ * terminated in a normal or non-normal manner.
+ */
+#define SSL_STREAM_STATE_OK                     1
+
+/*
+ * The stream is a unidirectional stream and this direction cannot be used; for
+ * example, a remotely initiated unidirectional stream where
+ * SSL_get_stream_write_state is called, or a locally initiated unidirectional
+ * stream where SSL_get_stream_read_state is
+ called.
+ */
+#define SSL_STREAM_STATE_WRONG_DIR              2
+
 /*
  * The read or write part of the stream has been finished in a normal manner.
  *
@@ -732,17 +765,32 @@ no-ops. This is considered a success case.
  * already indicated the end of the stream by calling SSL_stream_conclude,
  * and that future calls to SSL_write will fail.
  */
-#define SSL_STREAM_STATE_FINISHED               1
+#define SSL_STREAM_STATE_FINISHED               3
 
 /*
  * The stream was reset by the local party.
+ *
+ * For SSL_get_stream_read_state, this means that the stream was aborted using a
+ * locally transmitted STOP_SENDING frame. Attempts to read from the stream via
+ * SSL_read will fail, though SSL_read may allow any  residual data waiting to
+ * be  read to be  read first.
+ *
+ * For SSL_get_stream_write_state, this means that the stream was aborted
+ * using a locally transmitted RESET_STREAM frame. Attempts to write to
+ * the stream will fail.
  */
-#define SSL_STREAM_STATE_RESET_LOCAL            2
+#define SSL_STREAM_STATE_RESET_LOCAL            4
 
 /*
  * The stream was reset by the remote party.
+ *
+ * For SSL_get_stream_read_state, this means the peer sent a STREAM_RESET
+ * frame for the stream.
+ *
+ * For SSL_get_stream_write_state, this means the peer sent a STOP_SENDING
+ * frame for the stream.
  */
-#define SSL_STREAM_STATE_RESET_REMOTE           3
+#define SSL_STREAM_STATE_RESET_REMOTE           5
 
 /*
  * The underlying connection supporting the stream has closed or otherwise
@@ -755,7 +803,7 @@ no-ops. This is considered a success case.
  * For SSL_get_stream_write_state, this means that attempts to write to the
  * stream will fail.
  */
-#define SSL_STREAM_STATE_CONN_CLOSED            4
+#define SSL_STREAM_STATE_CONN_CLOSED            6
 
 int SSL_get_stream_read_state(SSL *ssl);
 int SSL_get_stream_write_state(SSL *ssl);
@@ -765,22 +813,31 @@ This API allows the current state of a stream to be queried. This allows an
 application to determine whether a stream is still usable and why a stream has
 reached an error state.
 
-#### `SSL_get_stream_error_code`
+#### `SSL_get_stream_read_error_code`, `SSL_get_stream_write_error_code`
 
 | Semantics | `SSL_get_error` | Can Tick? | CSHL          |
 | --------- | ------------- | --------- | ------------- |
 | New       | Never         | No        | S             |
 
 ```c
-int SSL_get_stream_error_code(SSL *ssl, uint64_t *app_error_code);
+int SSL_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code);
+int SSL_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code);
 ```
 
+`SSL_get_stream_read_error_code` gets the error code for the read part of the
+stream.
+
+`SSL_get_stream_write_error_code` gets the error code for the write part of
+the stream.
+
 If a stream has been terminated normally, returns 0.
 
 If a stream has been terminated non-normally, returns 1 and writes the
 applicable application error code to `*app_error_code`.
 
-If a stream is still healthy, returns -1.
+If a stream is still healthy, or was healthy at the time the connection was
+closed, or the respective part of the stream does not exist (e.g. for a
+unidirectional stream), returns -1.
 
 #### `SSL_get_conn_close_info`
 
@@ -804,7 +861,8 @@ int SSL_get_conn_close_info(SSL *ssl,
 
 If a connection is still healthy, returns 0. Otherwise, fills `*info` with
 information about the error causing connection termination and returns 1.
-`info_len` must be set to `sizeof(*info)`.
+`info_len` must be set to `sizeof(*info)`. Returns -1 if called on a non-QUIC
+SSL object or if the connection status cannot be determined.
 
 `info->reason` is set to point to a buffer containing a reason string. The
 buffer is valid for the lifetime of the SSL object. The reason string will
@@ -818,6 +876,340 @@ reason string in bytes.
 if it was initiated by the application. The namespace of `info->error_code` is
 determined by this parameter.
 
+### New APIs for Multi-Stream Operation
+
+The above new APIs are built on constructively to facilitate multi-stream
+operation.
+
+The concept of a QUIC stream SSL object is introduced. A QUIC SSL object is
+either a QUIC connection SSL object or a QUIC stream SSL object. A QUIC stream
+SSL object belongs to a QUIC connection SSL object. A QUIC connection SSL object
+may or may not have an associated default stream. There may only be at most one
+default stream for a QUIC connection SSL object. Reading or writing application
+data to a QUIC connection SSL object with a default stream is equivalent to
+reading or writing to that stream. It is an error to attempt to read or write
+application data, or perform other stream-specific operations, on a QUIC
+connection SSL object without a default stream associated.
+
+#### Notes on Multi-Threaded Operation
+
+Initially these APIs will not be thread safe over the same connection, but in
+the longer term we intend to support multiple threads using different QUIC
+stream SSL objects on different threads over the same connection without the
+application having to do any locking. This is referred to as multi-stream
+multi-thread (MSMT) operation. Only APIs explicitly denoted below will
+eventually be MSMT-safe.
+
+#### Notes on Blocking
+
+The blocking mode can be configured on each SSL object individually. When a QUIC
+stream SSL object is created it inherits its blocking state from the currently
+configured blocking state of the QUIC connection SSL object at the time the
+stream is created. This can be changed independently. For example, a QUIC
+connection SSL object can be in blocking mode to allow for blocking
+`SSL_accept_stream` calls, yet have some or all QUIC stream SSL objects be in
+non-blocking mode concurrently.
+
+#### Notes on Application-Level Polling
+
+An API may be added in the future to allow applications to poll multiple QUIC
+connection SSL objects efficiently for new stream and stream readability events.
+This is not yet urgent but will be more relevant for concurrent server
+applications.
+
+#### `SSL_get0_connection`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | CS            |
+
+```c
+/*
+ * Get the SSL object representing the connection associated with this object.
+ *
+ * If the SSL object represents a non-QUIC method or a QUIC connection, this
+ * returns the same object passed.
+ *
+ * If the SSL object represents a QUIC stream returns the QUIC connection
+ * object.
+ */
+SSL *SSL_get0_connection(SSL *ssl);
+```
+
+#### `SSL_is_connection`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | CS            |
+
+```c
+/*
+ * Returns 1 if the object represents a connection. This always returns 1 for
+ * non-QUIC methods, but returns 0 for SSL objects for QUIC streams which are
+ * not also the QUIC connection object.
+ *
+ * This is exactly equivalent to (SSL_get0_connection(ssl) == ssl).
+ */
+int SSL_is_connection(SSL *ssl);
+```
+
+#### `SSL_get_stream_type`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | S             |
+
+```c
+/*
+ * If the object represents a stream, returns a SSL_STREAM_TYPE value
+ * designating whether the stream can be used for transmission, reception,
+ * or both.
+ *
+ * This always returns SSL_STREAM_TYPE_BIDI for non-QUIC methods.
+ *
+ * It returns SSL_STREAM_TYPE_NONE for a QUIC connection object if it
+ * does not have a default stream.
+ */
+#define SSL_STREAM_TYPE_NONE    0
+#define SSL_STREAM_TYPE_READ    1
+#define SSL_STREAM_TYPE_WRITE   2
+#define SSL_STREAM_TYPE_BIDI    (SSL_STREAM_TYPE_READ | SSL_STREAM_TYPE_WRITE)
+__owur int SSL_get_stream_type(SSL *ssl);
+```
+
+#### `SSL_get_stream_id`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | S             |
+
+```c
+/*
+ * QUIC: Returns the unique stream ID for the stream, an integer in range [0, 2**62-1],
+ * or UINT64_MAX if the stream ID is not available. If called on a QUIC
+ * connection, returns the unique stream ID for the default stream if there is
+ * one, and otherwise returns UINT64_MAX.
+ *
+ * TLS, DTLS: Returns UINT64_MAX.
+ */
+__owur uint64_t SSL_get_stream_id(SSL *ssl);
+```
+
+#### `SSL_new_stream`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Create a new SSL object representing a single additional stream.
+ *
+ * There is no need to call SSL_connect on the resulting object, and
+ * any such call is a no-op.
+ *
+ * For QUIC:
+ *   Creates a new stream. Must be called only on a QUIC connection SSL object.
+ *   Can be used on client or server. If the SSL_STREAM_FLAG_UNI flag is set,
+ *   the created stream is unidirectional, otherwise it is bidirectional.
+ *
+ *   To be MSMT-safe.
+ *
+ * For TLS and DTLS SSL objects:
+ *   Always fails.
+ */
+#define SSL_STREAM_FLAG_UNI    1
+
+SSL *SSL_new_stream(SSL *ssl, uint64_t flags);
+```
+
+#### `SSL_accept_stream`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | Yes       | C             |
+
+```c
+/*
+ * Create a new SSL object representing an additional stream which was created
+ * by the peer.
+ *
+ * There is no need to call SSL_accept on the resulting object, and
+ * any such call is a no-op.
+ *
+ * For QUIC:
+ *   Must be called only on a QUIC connection SSL object. Fails if called on a
+ *   stream object. Checks if a new stream has been created by the peer. If it
+ *   has, creates a new SSL object to represent it and returns it. Otherwise,
+ *   returns NULL. If multiple streams are available to be accepted, the oldest
+ *   stream (that is, the stream with the lowest stream ID) is accepted.
+ *
+ * For all other methods:
+ *   Returns NULL.
+ *
+ * The flags argument is unused and should be set to zero.
+ *
+ * To be MSMT-safe (i.e., can be called from multiple threads).
+ *
+ * If the QUIC connection SSL object is configured in blocking mode, this
+ * function will block unless the SSL_ACCEPT_STREAM_NO_BLOCK flag is passed.
+ *
+ * This function returns NULL if the effective incoming stream reject policy is
+ * `REJECT`.
+ */
+#define SSL_ACCEPT_STREAM_NO_BLOCK      1
+
+SSL *SSL_accept_stream(SSL *ssl, uint64_t flags);
+```
+
+#### `SSL_get_accept_stream_queue_len`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Determine the number of streams waiting to be returned on a subsequent call
+ * to SSL_accept_stream. If this returns a non-zero value, the next call to
+ * SSL_accept_stream (on any thread) is guaranteed to work. Returns 0 for
+ * non-QUIC objects, or for QUIC stream objects.
+ *
+ * To be MSMT-safe.
+ */
+size_t SSL_get_accept_stream_queue_len(SSL *ssl);
+```
+
+#### `SSL_set_incoming_stream_reject_policy`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Sets the policy for incoming streams. If `policy` is `AUTO` (the default):
+ *
+ *   - if `SSL_detach_stream` has been used, this is equivalent to `ACCEPT`;
+ *
+ *   - otherwise, if the default stream mode is
+ *     `SSL_DEFAULT_STREAM_MODE_AUTO_BIDI` or
+ *     `SSL_DEFAULT_STREAM_MODE_AUTO_UNI`, this is equivalent to `REJECT`;
+ *
+ *   - otherwise, this is equivalent to `ACCEPT`.
+ *
+ * If configured to `ACCEPT`, incoming streams are placed on the accept queue
+ * for application consumption. `aec` is ignored in this case.
+ *
+ * If configured to `REJECT`, incoming streams automatically have both their
+ * receiving and sending parts handled via non-normal termination. `aec` is an
+ * application error code used for the `STOP_SENDING` and `RESET_STREAM` frames
+ * used for the purposes of this termination. The default AEC value used if this
+ * function is never called is 0.
+ */
+#define SSL_INCOMING_STREAM_REJECT_POLICY_AUTO      0
+#define SSL_INCOMING_STREAM_REJECT_POLICY_ACCEPT    1
+#define SSL_INCOMING_STREAM_REJECT_POLICY_REJECT    2
+
+int SSL_set_incoming_stream_reject_policy(SSL *ssl, int policy, uint64_t aec);
+```
+
+#### `SSL_set_default_stream_mode`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Used to control single stream operation. Calling this function determines the
+ * nature of the default stream which will automatically be created on the QUIC
+ * connection SSL object.
+ *
+ * The default mode is `SSL_DEFAULT_STREAM_MODE_AUTO_BIDI`.
+ *
+ * The modes are as follows:
+ *
+ *   - `SSL_DEFAULT_STREAM_MODE_NONE`: No default stream will ever be created.
+ *     The application is assumed to understand multi-stream operation.
+ *     Remotely-initiated streams are placed in the accept queue for application
+ *     consumption. `SSL_read` and `SSL_write` calls must be made on a QUIC
+ *     stream SSL object, not the QUIC connection SSL object, as no default
+ *     stream will be associated with it.
+ *
+ *   - `SSL_DEFAULT_STREAM_MODE_AUTO_BIDI`: "First stream wins" mode of
+ *     operation for single-stream usage. If `SSL_write` is called before the
+ *     peer opens a remotely-initiated stream, a locally-initiated bidirectional
+ *     stream is created and bound as the default stream. If the peer opens a
+ *     remotely-initiated stream before the local application calls `SSL_write`
+ *     (with `len > 0`) for the first time, that stream is bound as the default
+ *     stream, which may be bidirectional or unidirectional; if it is
+ *     unidirectional, calls to `SSL_write` will fail. Attempts to create
+ *     additional streams by the peer are automatically rejected unless
+ *     the application opts in (API TBD).
+ *
+ *   - `SSL_DEFAULT_STREAM_MODE_AUTO_UNI`: "First stream wins" mode of
+ *     operation for single-stream usage, with a unidirectional stream. This
+ *     functions identically to `SSL_DEFAULT_STREAM_MODE_AUTO_BIDI`, but if the
+ *     local application calls `SSL_write` prior to the peer creating a
+ *     remotely-initiated stream, a unidirectional TX-only stream is created and
+ *     bound as the default stream. Thereafter, calls to `SSL_read` will fail.
+ *     If the peer creates a remotely-initiated stream prior to the first call
+ *     to `SSL_write` (with `len > 0`), that stream will be bound as the default
+ *     stream; note that a bidirectional stream may be bound in this case.
+ *     Attempts to create additional streams by the peer are automatically
+ *     rejected unless the application opts in (API TBD).
+ *
+ * This function must be called before a default stream object is created, for
+ * example before initiating a connection. If the function is too late to have
+ * an effect, this function fails and returns 0. To switch to multi-stream
+ * operation after a default stream has been created, use `SSL_detach_stream`.
+ */
+#define SSL_DEFAULT_STREAM_MODE_NONE                0
+#define SSL_DEFAULT_STREAM_MODE_AUTO_BIDI           1
+#define SSL_DEFAULT_STREAM_MODE_AUTO_UNI            2
+
+__owur int SSL_set_default_stream_mode(SSL *ssl, uint32_t mode);
+```
+
+#### `SSL_detach_stream`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Detaches a default stream from a QUIC connection object. If the
+ * QUIC connection object does not contain a default stream, returns NULL.
+ * After calling this, calling SSL_get_stream_type on the connection object
+ * returns SSL_STREAM_TYPE_NONE. Always returns NULL for non-QUIC connections.
+ *
+ * Calling this function automatically inhibits default stream creation;
+ * though, after calling this function, a QUIC connection SSL object will no
+ * longer have a stream attached to it, calling SSL_read or SSL_write on
+ * that QUIC connection SSL object will not automatically create a new
+ * default stream. Default stream creation only occurs at most a single time per
+ * connection.
+ */
+SSL *SSL_detach_stream(SSL *ssl);
+```
+
+#### `SSL_attach_stream`
+
+| Semantics | `SSL_get_error` | Can Tick? | CSHL          |
+| --------- | ------------- | --------- | ------------- |
+| New       | Never         | No        | C             |
+
+```c
+/*
+ * Attaches a default stream to a QUIC connection object. If the conn object is
+ * not a QUIC connection object, or already has a default stream, this function
+ * fails. The stream must belong to the same connection, or this function fails.
+ */
+__owur int SSL_attach_stream(SSL *conn, SSL *stream);
+```
+
 ### Future APIs
 
 A custom poller interface may be provided in the future. For more information,
@@ -1192,3 +1584,119 @@ calls.
 **Q. How should `STOP_SENDING` be supported?**
 
 TODO: Determine how `STOP_SENDING` should be supported.
+
+**Q. Can data be received on a locally initiated bidirectional stream before any
+data is sent on that stream?**
+
+This is an interesting question without a clear answer to be found in the QUIC
+RFCs. A close reading of RFC 9000 suggests that the answer is, in principle,
+yes; however the RFC also grants explicit permission to make design choices in
+implementations which would preclude this:
+
+>An implementation might choose to defer allocating a stream ID to a stream until
+>it sends the first STREAM frame and enters this state, which can allow for
+>better stream prioritization.
+
+If an ID has not been allocated to a stream, obviously incoming data cannot be
+addressed to it. However, supposing that an implementation does not do this,
+RFC 9000 seems basically clear that it is valid for an application to create a
+stream locally, then receive data on it before sending anything:
+
+>The sending part of a stream that the endpoint initiates (types 0 and 2 for
+>clients, 1 and 3 for servers) is opened by the application. The "Ready"
+>state represents a newly created stream that is able to accept data from the
+>application.
+>
+>[...]
+>
+>For a bidirectional stream, the receiving part enters the "Recv" state when
+>the sending part initiated by the endpoint (type 0 for a client, type 1 for
+>a server) enters the "Ready" state.
+
+A peer is not generally notified of the creation of a stream which has not sent
+any data yet, since the creation of a stream is signalled only implicitly via
+the transmission of data in `STREAM` frames. However, a zero-length STREAM frame
+could presumably be used to effect such a notification. RFC 9000 contains no
+specific discussion of this possibility but does not preclude it. As such, in
+order to receive data on a locally-initiated bidirectional stream before sending
+any data on that stream, it would be necessary to either
+
+- Use a QUIC implementation which signals a bidirectional stream which has
+  not yet sent any data via a zero-length stream frame, or
+
+- Use an application protocol which can inform the peer of the stream ID
+  of the created stream in some application protocol-specific way.
+  This is somewhat less plausible because it would require an API between
+  the application and its QUIC library to inform the QUIC library
+  that the peer has in fact created a stream with a given ID and to
+  take its word for it. This is unlikely to be commonly available, especially as
+  application errors in usage of such an API would lead to internal
+  inconsistencies in QUIC connection state.
+
+Of course this discussion is somewhat esoteric as it is unclear why an
+application would want to create a locally-initiated stream and then have the
+peer transmit on it first, rather than simply use a remotely-initiated stream.
+Thus this discussion of this edge case is more of a curiosity, however for
+completeness it needs to be thought about in the API design.
+
+**Q. How should single-stream operation support locally and remotely-initiated
+streams?**
+
+Note that the ID of a stream depends on whether it is bidirectional and whether
+it is initiated by the client or server. Therefore, in single stream operation,
+it is necessary to know whether single-stream QUIC is being used with
+client-initiated or server-initiated stream initiation, and whether a
+bidirectional or unidirectional stream is being used; otherwise, we do not know
+which stream ID to bind to.
+
+The object of single stream operation is to support simple uses cases for simple
+applications. There seems no need to support esoteric usage of streams such as
+receiving first on a locally initiated stream here, thus we avoid supporting
+this to simplify the API.
+
+As such, an application which calls `SSL_write` on a QUIC connection SSL object
+before it calls `SSL_read` by definition is using a locally-initiated stream,
+and an application which does the opposite is using a remotely-initiated stream.
+We can use the ordering of initial calls to `SSL_read` and `SSL_write` to infer
+the desired stream type.
+
+Supporting locally-initiated streams (`SSL_write` called first) is simple;
+we automatically create the stream and queue data for transmission.
+
+Supporting remotely-initiated streams (`SSL_read` called first) is a little
+stranger. We could create the stream with the correct ID when cued to by the
+initial call to `SSL_read` implying use of a remotely-initiated stream. However,
+this would mean we are creating state tracking a remotely-initiated stream
+before the peer has signalled it. This would work in the happy case where the
+client is connected to a compatible server but may result in strange
+inconsistencies of QUIC internal state if a client is accidentially connected to
+an incompatible peer. Since the peer ought to be the authority on the streams it
+creates, this seems like an undesirable approach.
+
+Ergo, creation of a default remotely-initiated stream needs to be deferred
+until the *peer* signals such a stream.
+
+This leads naturally to a "first stream wins" model of implementation:
+
+- When a QUIC connection SSL object is created, default stream mode is
+  enabled, meaning that a default stream will be bound to the QUIC connection
+  SSL object at the earliest available opportunity. However, no default
+  stream is bound yet.
+
+- One of the following events happened — whichever happens first wins:
+
+  - The local application calls `SSL_write()` (`len > 0`). A locally-initiated
+    stream with ordinal 0 is created. The stream is bidirectional by default but
+    this can be changed. This stream is bound as the default stream.
+
+  - The peer creates a stream. This stream is bound as the default stream.
+
+If the local application calls `SSL_read()` before either of the above
+occur, `SSL_read()` fails as though no data is available until one
+of the above events occurs.
+
+Once one of the above events occurs, any additional stream created by the peer
+is automatically terminated using both `STOP_SENDING` and `STREAM_RESET` frames
+(to terminate both the receiving and sending parts respectively) and there is no
+API-visible effect to the local application (unless the application explicitly
+opts into supporting additional streams).