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,
};
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