WebRTC DTLS
Abstract |
WebRTC Pacer |
Authors |
Walter Fan |
Status |
WIP |
Updated |
2024-08-21 |
Overview
API of openssl/boringssl
SSL_do_handshake
DTLSv1_handle_timeout
refer to https://boringssl.googlesource.com/boringssl/+/master/include/openssl/ssl.h
// SSL contexts.
//
// |SSL_CTX| objects manage shared state and configuration between multiple TLS
// or DTLS connections. Whether the connections are TLS or DTLS is selected by
// an |SSL_METHOD| on creation.
//
// |SSL_CTX| are reference-counted and may be shared by connections across
// multiple threads. Once shared, functions which change the |SSL_CTX|'s
// configuration may not be used.
// TLS_method is the |SSL_METHOD| used for TLS connections.
OPENSSL_EXPORT const SSL_METHOD *TLS_method(void);
// DTLS_method is the |SSL_METHOD| used for DTLS connections.
OPENSSL_EXPORT const SSL_METHOD *DTLS_method(void);
SSL_do_handshake
// SSL_do_handshake continues the current handshake. If there is none or the
// handshake has completed or False Started, it returns one. Otherwise, it
// returns <= 0. The caller should pass the value into |SSL_get_error| to
// determine how to proceed.
//
// In DTLS, the caller must drive retransmissions. Whenever |SSL_get_error|
// signals |SSL_ERROR_WANT_READ|, use |DTLSv1_get_timeout| to determine the
// current timeout. If it expires before the next retry, call
// |DTLSv1_handle_timeout|. Note that DTLS handshake retransmissions use fresh
// sequence numbers, so it is not sufficient to replay packets at the transport.
//
// TODO(davidben): Ensure 0 is only returned on transport EOF.
// https://crbug.com/466303.
OPENSSL_EXPORT int SSL_do_handshake(SSL *ssl);
SSL_shutdown
// SSL_shutdown shuts down |ssl|. It runs in two stages. First, it sends
// close_notify and returns zero or one on success or -1 on failure. Zero
// indicates that close_notify was sent, but not received, and one additionally
// indicates that the peer's close_notify had already been received.
//
// To then wait for the peer's close_notify, run |SSL_shutdown| to completion a
// second time. This returns 1 on success and -1 on failure. Application data
// is considered a fatal error at this point. To process or discard it, read
// until close_notify with |SSL_read| instead.
//
// In both cases, on failure, pass the return value into |SSL_get_error| to
// determine how to proceed.
//
// Most callers should stop at the first stage. Reading for close_notify is
// primarily used for uncommon protocols where the underlying transport is
// reused after TLS completes. Additionally, DTLS uses an unordered transport
// and is unordered, so the second stage is a no-op in DTLS.
OPENSSL_EXPORT int SSL_shutdown(SSL *ssl);
SSL_set_bio
// SSL_set_bio configures |ssl| to read from |rbio| and write to |wbio|. |ssl|
// takes ownership of the two |BIO|s. If |rbio| and |wbio| are the same, |ssl|
// only takes ownership of one reference.
//
// In DTLS, |rbio| must be non-blocking to properly handle timeouts and
// retransmits.
//
// If |rbio| is the same as the currently configured |BIO| for reading, that
// side is left untouched and is not freed.
//
// If |wbio| is the same as the currently configured |BIO| for writing AND |ssl|
// is not currently configured to read from and write to the same |BIO|, that
// side is left untouched and is not freed. This asymmetry is present for
// historical reasons.
//
// Due to the very complex historical behavior of this function, calling this
// function if |ssl| already has |BIO|s configured is deprecated. Prefer
// |SSL_set0_rbio| and |SSL_set0_wbio| instead.
OPENSSL_EXPORT void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio);
classes
OpenSSLAdapter
rtc_base/openssl_adapter.cc
OpenSSLStreamAdapter
openssl_stream_adapter.cc
DtlsTransport
dtls_transport.cc
SSLStreamAdapter
SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS.
After SSL has been started, the stream will only open on successful SSL verification of certificates, and the communication is encrypted of course.
This class was written with SSLAdapter as a starting point. It offers a similar interface, with two differences: there is no support for a restartable SSL connection, and this class has a peer-to-peer mode.
The SSL library requires initialization and cleanup. Static method for doing this are in SSLAdapter. They should possibly be moved out to a neutral class.
Snippets
enum SSLRole { SSL_CLIENT, SSL_SERVER };
enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS };
enum SSLProtocolVersion {
SSL_PROTOCOL_NOT_GIVEN = -1,
SSL_PROTOCOL_TLS_10 = 0,
SSL_PROTOCOL_TLS_11,
SSL_PROTOCOL_TLS_12,
SSL_PROTOCOL_DTLS_10 = SSL_PROTOCOL_TLS_11,
SSL_PROTOCOL_DTLS_12 = SSL_PROTOCOL_TLS_12,
};
enum class SSLPeerCertificateDigestError {
NONE,
UNKNOWN_ALGORITHM,
INVALID_LENGTH,
VERIFICATION_FAILED,
};
enum SSLState {
SSL_NONE,
SSL_WAIT,
SSL_CONNECTING,
SSL_CONNECTED,
SSL_ERROR
};
// client handshake state
enum ssl_client_hs_state_t {
state_start_connect = 0,
state_enter_early_data,
state_early_reverify_server_certificate,
state_read_hello_verify_request,
state_read_server_hello,
state_tls13,
state_read_server_certificate,
state_read_certificate_status,
state_verify_server_certificate,
state_reverify_server_certificate,
state_read_server_key_exchange,
state_read_certificate_request,
state_read_server_hello_done,
state_send_client_certificate,
state_send_client_key_exchange,
state_send_client_certificate_verify,
state_send_client_finished,
state_finish_flight,
state_read_session_ticket, // read session ticket
state_process_change_cipher_spec,
state_read_server_finished,
state_finish_client_handshake,
state_done,
};
// server handshake state
//refer to https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;l=1720?q=handshake_client.cc
enum tls12_server_hs_state_t {
state12_start_accept = 0,
state12_read_client_hello,
state12_read_client_hello_after_ech,
state12_select_certificate,
state12_tls13,
state12_select_parameters,
state12_send_server_hello,
state12_send_server_certificate,
state12_send_server_key_exchange,
state12_send_server_hello_done,
state12_read_client_certificate,
state12_verify_client_certificate,
state12_read_client_key_exchange,
state12_read_client_certificate_verify,
state12_read_change_cipher_spec,
state12_process_change_cipher_spec,
state12_read_next_proto,
state12_read_channel_id,
state12_read_client_finished,
state12_send_server_finished,
state12_finish_server_handshake,
state12_done,
};
ssl_client_handshake 方法负责进行客户端的 SSL 握手
webrtc-checkout/src/third_party/boringssl/src/ssl/handshake_client.cc
enum ssl_hs_wait_t ssl_client_handshake(SSL_HANDSHAKE *hs) {
while (hs->state != state_done) {
enum ssl_hs_wait_t ret = ssl_hs_error;
enum ssl_client_hs_state_t state =
static_cast<enum ssl_client_hs_state_t>(hs->state);
switch (state) {
case state_start_connect:
ret = do_start_connect(hs);
break;
case state_enter_early_data:
ret = do_enter_early_data(hs);
break;
case state_early_reverify_server_certificate:
ret = do_early_reverify_server_certificate(hs);
break;
case state_read_hello_verify_request:
ret = do_read_hello_verify_request(hs);
break;
case state_read_server_hello:
ret = do_read_server_hello(hs);
break;
case state_tls13:
ret = do_tls13(hs);
break;
case state_read_server_certificate:
ret = do_read_server_certificate(hs);
break;
case state_read_certificate_status:
ret = do_read_certificate_status(hs);
break;
case state_verify_server_certificate:
ret = do_verify_server_certificate(hs);
break;
case state_reverify_server_certificate:
ret = do_reverify_server_certificate(hs);
break;
case state_read_server_key_exchange:
ret = do_read_server_key_exchange(hs);
break;
case state_read_certificate_request:
ret = do_read_certificate_request(hs);
break;
case state_read_server_hello_done:
ret = do_read_server_hello_done(hs);
break;
case state_send_client_certificate:
ret = do_send_client_certificate(hs);
break;
case state_send_client_key_exchange:
ret = do_send_client_key_exchange(hs);
break;
case state_send_client_certificate_verify:
ret = do_send_client_certificate_verify(hs);
break;
case state_send_client_finished:
ret = do_send_client_finished(hs);
break;
case state_finish_flight:
ret = do_finish_flight(hs);
break;
case state_read_session_ticket:
ret = do_read_session_ticket(hs);
break;
case state_process_change_cipher_spec:
ret = do_process_change_cipher_spec(hs);
break;
case state_read_server_finished:
ret = do_read_server_finished(hs);
break;
case state_finish_client_handshake:
ret = do_finish_client_handshake(hs);
break;
case state_done:
ret = ssl_hs_ok;
break;
}
if (hs->state != state) {
ssl_do_info_callback(hs->ssl, SSL_CB_CONNECT_LOOP, 1);
}
if (ret != ssl_hs_ok) {
return ret;
}
}
ssl_do_info_callback(hs->ssl, SSL_CB_HANDSHAKE_DONE, 1);
return ssl_hs_ok;
}
logs
[38367:49923:0619/142328.887408:INFO:openssl_stream_adapter.cc(828)] DTLS retransmission
[38367:49923:0619/142328.887456:INFO:openssl_adapter.cc(833)] connect_exit TLS client read_session_ticket
[38367:49923:0619/142917.365608:INFO:openssl_stream_adapter.cc(830)] DTLSv1_handle_timeout() return -1
[38367:49923:0619/142917.365738:WARNING:openssl_stream_adapter.cc(952)] OpenSSLStreamAdapter::Error(DTLSv1_handle_timeout, -1, 255)
[38367:49923:0619/142917.365838:WARNING:openssl_adapter.cc(836)] write_alert fatal unknown TLS client read_session_ticket
[38367:49923:0619/142917.365904:INFO:dtls_transport.cc(733)] DtlsTransport[1|1|__]: DTLS transport error, code=-1
[38367:49923:0619/142917.365939:VERBOSE1:dtls_transport.cc(840)] DtlsTransport[1|1|__]: set_dtls_state from:1 to 4