google quic tls握手原理(一)

前言

  • 本文意在分析google quic项目中tls握手引擎的工作原理
  • 认识client helloserver hello是在哪里生成的,同时了解quic的握手基本流程
  • 同时通过本文学习来进一步了解TlsHandshaker模块和其他模块之间的关系

认识TlsConnection模块

TlsConnection模块核心定义

class QUIC_EXPORT_PRIVATE TlsConnection {
 public:
  // A TlsConnection::Delegate implements the methods that are set as callbacks
  // of TlsConnection.
  class QUIC_EXPORT_PRIVATE Delegate {
   public:
    virtual ~Delegate() {}

   protected:
    // Certificate management functions:

    // Verifies the peer's certificate chain. It may use
    // SSL_get0_peer_certificates to get the cert chain. This method returns
    // ssl_verify_ok if the cert is valid, ssl_verify_invalid if it is invalid,
    // or ssl_verify_retry if verification is happening asynchronously.
    virtual enum ssl_verify_result_t VerifyCert(uint8_t* out_alert) = 0;

    // QUIC-TLS interface functions:

    // SetWriteSecret provides the encryption secret used to encrypt messages at
    // encryption level |level|. The secret provided here is the one from the
    // TLS 1.3 key schedule (RFC 8446 section 7.1), in particular the handshake
    // traffic secrets and application traffic secrets. The provided write
    // secret must be used with the provided cipher suite |cipher|.
    virtual void SetWriteSecret(EncryptionLevel level, const SSL_CIPHER* cipher,
                                absl::Span write_secret) = 0;

    // SetReadSecret is similar to SetWriteSecret, except that it is used for
    // decrypting messages. SetReadSecret at a particular level is always called
    // after SetWriteSecret for that level, except for ENCRYPTION_ZERO_RTT,
    // where the EncryptionLevel for SetWriteSecret is
    // ENCRYPTION_FORWARD_SECURE.
    virtual bool SetReadSecret(EncryptionLevel level, const SSL_CIPHER* cipher,
                               absl::Span read_secret) = 0;

    // WriteMessage is called when there is |data| from the TLS stack ready for
    // the QUIC stack to write in a crypto frame. The data must be transmitted
    // at encryption level |level|.
    virtual void WriteMessage(EncryptionLevel level,
                              absl::string_view data) = 0;

    // FlushFlight is called to signal that the current flight of messages have
    // all been written (via calls to WriteMessage) and can be flushed to the
    // underlying transport.
    virtual void FlushFlight() = 0;

    // SendAlert causes this TlsConnection to close the QUIC connection with an
    // error code corersponding to the TLS alert description |desc| sent at
    // level |level|.
    virtual void SendAlert(EncryptionLevel level, uint8_t desc) = 0;

    // Informational callback from BoringSSL. This callback is disabled by
    // default, but can be enabled by TlsConnection::EnableInfoCallback.
    //
    // See |SSL_CTX_set_info_callback| for the meaning of |type| and |value|.
    virtual void InfoCallback(int type, int value) = 0;

    // Message callback from BoringSSL, for debugging purposes. See
    // |SSL_CTX_set_msg_callback| for how to interpret |version|,
    // |content_type|, and |data|.
    virtual void MessageCallback(bool is_write, int version, int content_type,
                                 absl::string_view data) = 0;

    friend class TlsConnection;
  };
  ...
  SSL* ssl() const { return ssl_.get(); }      
      
 protected:
  // TlsConnection does not take ownership of |ssl_ctx| or |delegate|; they must
  // outlive the TlsConnection object.
  TlsConnection(SSL_CTX* ssl_ctx, Delegate* delegate, QuicSSLConfig ssl_config);
  ....
  // From a given SSL* |ssl|, returns a pointer to the TlsConnection that it
  // belongs to. This helper method allows the callbacks set in BoringSSL to be
  // dispatched to the correct TlsConnection from the SSL* passed into the
  // callback.
  static TlsConnection* ConnectionFromSsl(const SSL* ssl);
  ...
 private:
  ...
  Delegate* delegate_;
  bssl::UniquePtr ssl_;
  QuicSSLConfig ssl_config_;
};
  • 以上为TlsConnection模块的核心定义,核心成员是Delegate、SSL、QuicSSLConfig
  • 其中SSL为boring ssl引擎核心、QuicSSLConfig为加密引擎相关配置,用于初始化SSL引擎使用
  • Delegate为一个ssl 委托接口类,在该项目中,ssl并没有真实绑定socket 连接,而是通过注册回调的方式使用ssl引擎,也就是通过回调的方式给上次提供秘钥套件等功能
  • 同时通过成员函数SSL ssl()*将ssl引擎提供给到其他模块使用
  • 通过TlsConnection模块的构造函数可以看出,实例化一个TlsConnection模块需哟传入SSL_CTX、Delegate以及QuicSSLConfig
  • TlsConnection模块本身也提供了创建SSL_CTX的静态函数,所有的SSL指针创建都需要传入一个SSL_CTX
  • 以下为TlsConnection构造函数
TlsConnection::TlsConnection(SSL_CTX* ssl_ctx,
                             TlsConnection::Delegate* delegate,
                             QuicSSLConfig ssl_config)
    : delegate_(delegate),
      ssl_(SSL_new(ssl_ctx)),
      ssl_config_(std::move(ssl_config)) {
  SSL_set_ex_data(
      ssl(), SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection(),
      this);
  if (ssl_config_.early_data_enabled.has_value()) {
    const int early_data_enabled = *ssl_config_.early_data_enabled ? 1 : 0;
    SSL_set_early_data_enabled(ssl(), early_data_enabled);
  }
  if (ssl_config_.signing_algorithm_prefs.has_value()) {
    SSL_set_signing_algorithm_prefs(
        ssl(), ssl_config_.signing_algorithm_prefs->data(),
        ssl_config_.signing_algorithm_prefs->size());
  }
  if (ssl_config_.disable_ticket_support.has_value()) {
    if (*ssl_config_.disable_ticket_support) {
      SSL_set_options(ssl(), SSL_OP_NO_TICKET);
    }
  }
}
  • 首先创建SSL实例,并通过Api SSL_set_ex_data()TlsConnection和当前创建的SSL实例进行关联
  • 其次是通过QuicSSLConfig对当前创建的SSL实例进行配置
  • 那么怎么通过SSL实例拿到当前的TlsConnection指针呢?
TlsConnection* TlsConnection::ConnectionFromSsl(const SSL* ssl) {
  return reinterpret_cast(SSL_get_ex_data(
      ssl, SslIndexSingleton::GetInstance()->ssl_ex_data_index_connection()));
}
  • TlsConnection模块通过静态成员函数ConnectionFromSsl(...)拿到自己的指针实例

SSL_CTX创建

  • 上面提到了所有的SSL实例都需要SSL_CTX,那么它是在什么时候创建的,以及在google quic中是怎么使用的?
class QUIC_EXPORT_PRIVATE TlsConnection {
 ....
 protected:
  // Creates an SSL_CTX and configures it with the options that are appropriate
  // for both client and server. The caller is responsible for ownership of the
  // newly created struct.
  static bssl::UniquePtr CreateSslCtx();

 private:
  // TlsConnection implements SSL_QUIC_METHOD, which provides the interface
  // between BoringSSL's TLS stack and a QUIC implementation.
  static const SSL_QUIC_METHOD kSslQuicMethod;
};
const SSL_QUIC_METHOD TlsConnection::kSslQuicMethod{
    TlsConnection::SetReadSecretCallback, // int (*set_read_secret)->用于设置用于解密传入数据的加密密钥
    TlsConnection::SetWriteSecretCallback,// int (*set_write_secret)->用于设置加密传入数据的加密密钥
    TlsConnection::WriteMessageCallback,  // int (*add_handshake_data)->将TLS握手期间产生的数据添加到SSL连接的缓冲区中
    TlsConnection::FlushFlightCallback,   // int (*flush_flight)
    TlsConnection::SendAlertCallback};    // int (*send_alert)->握手出错的时候回调
// static
void TlsConnection::MessageCallback(int is_write, int version, int content_type,
                                    const void* buf, size_t len, SSL* ssl,
                                    void*) {
  ConnectionFromSsl(ssl)->delegate_->MessageCallback(
      is_write != 0, version, content_type,
      absl::string_view(static_cast(buf), len));
}
// static
bssl::UniquePtr TlsConnection::CreateSslCtx() {
  CRYPTO_library_init();
  bssl::UniquePtr ssl_ctx(SSL_CTX_new(TLS_with_buffers_method()));
  SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
  SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
  SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod);
  SSL_CTX_set_msg_callback(ssl_ctx.get(), &MessageCallback);
  return ssl_ctx;
}
  • SslCtx的创建和初始化使用SSL_CTX_set_quic_method从外部实现的方式,并未真正绑定真实的socket io
  • 通过自定义实现SSL_QUIC_METHOD函数结构指针对外提供加解密的秘钥套件
  • 在各函数回调的时候通过ConnectionFromSsl(ssl)拿到对应的TlsConnection实例,然后再通过其成员Delegate,在Delegate的实现中进行处理

TlsConnection子类介绍

001.png
  • TlsConnection主要有两大派生子类,TlsClientConnection用于客户端、TlsServerConnection用于服务端
  • 服务端和客户端对SSL_CTX的初始化会有些区别,同时对SSL实例的设置也有些差异
// static
bssl::UniquePtr TlsClientConnection::CreateSslCtx(
    bool enable_early_data) {
  bssl::UniquePtr ssl_ctx = TlsConnection::CreateSslCtx();
  // Configure certificate verification.
  // 默认验证服务端证书,通过VerifyCallback来验证证书的有效性
  SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback);
  int reverify_on_resume_enabled = 1;
  SSL_CTX_set_reverify_on_resume(ssl_ctx.get(), reverify_on_resume_enabled);

  // Configure session caching.
  // 禁用缓存,仅为客户端会话启用缓存
  SSL_CTX_set_session_cache_mode(
      ssl_ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
  SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);

  // TODO(wub): Always enable early data on the SSL_CTX, but allow it to be
  // overridden on the SSL object, via QuicSSLConfig.
  SSL_CTX_set_early_data_enabled(ssl_ctx.get(), enable_early_data);
  return ssl_ctx;
}
  • SSL_CTX_set_custom_verify允许应用程序设置自定义验证函数,以在SSL / TLS握手过程中验证对等证书。该函数允许应用程序执行超出OpenSSL执行的标准证书验证之外的其他检查。自定义验证函数接受对等方提供的X509证书的指针,并返回一个值,指示证书是否有效。如果证书无效,则SSL / TLS握手将失败,并且连接将终止。
  • SSL_CTX_set_reverify_on_resume用于设置 SSL/TLS 会话恢复时是否重新验证对端证书。SSL/TLS 会话恢复是一种优化方式,可以在 SSL/TLS 握手时重用之前的会话参数,避免重新执行完整的 SSL/TLS 握手。但是,由于会话恢复时不重新验证对端证书,存在一定的安全风险。因此,SSL_CTX_set_reverify_on_resume 函数可以用于控制 SSL/TLS 会话恢复时是否重新验证对端证书
  • SSL_CTX_set_session_cache_mode用于设置给定SSL上下文的会话缓存模式。会话缓存模式决定如何存储和检索SSL会话信息以便在随后的连接中重用。有几种可用的模式,包括禁用缓存,仅为客户端会话启用缓存,仅为服务器会话启用缓存以及同时为客户端和服务器会话启用缓存。适当的模式取决于应用程序的具体用例和安全要求
  • SSL_CTX_set_session_cache_mode用于设置给定SSL上下文的会话缓存模式。会话缓存模式确定SSL会话信息如何存储和检索以便在后续连接中重用。有几种可用的模式,包括禁用缓存,仅为客户端会话启用缓存,仅为服务器会话启用缓存,以及同时为客户端和服务器会话启用缓存。适当的模式取决于应用程序的特定用例和安全要求。
  • SSL_CTX_sess_set_new_cb用于设置在创建新的SSL会话时调用的回调函数。当SSL会话被创建时,SSL会调用这个回调函数,并将新会话的SSL_SESSION结构体指针作为参数传递给它。应用程序可以使用这个回调函数来执行与新SSL会话相关的自定义操作,例如将会话信息存储在数据库或磁盘上以供将来重用。如果未设置此回调函数,则新SSL会话将不会被自动缓存,而是每次都会创建新的会话
  • SSL_CTX_set_early_data_enabled用于启用或禁用TLS 1.3中的0-RTT数据功能。0-RTT数据允许客户端在完成握手之前发送加密数据,从而提高了应用程序的性能。启用此功能时,客户端可以在第一次连接时发送一些数据,而无需等待服务器确认握手已完成。但是需要注意,0-RTT数据的安全性可能受到威胁,因为它可能会被中间人攻击者拦截和篡改。因此,应该谨慎使用0-RTT数据功能,并且只在对应用程序的性能提升有明确需求且安全性风险可接受的情况下启用它。
// static
bssl::UniquePtr TlsServerConnection::CreateSslCtx(
    ProofSource* proof_source) {
  bssl::UniquePtr ssl_ctx = TlsConnection::CreateSslCtx();

  // Server does not request/verify client certs by default. Individual server
  // connections may call SSL_set_custom_verify on their SSL object to request
  // client certs.

  SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(),
                                         &TlsExtServernameCallback);
  SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), &SelectAlpnCallback, nullptr);
  // We don't actually need the TicketCrypter here, but we need to know
  // whether it's set.
  if (proof_source->GetTicketCrypter()) {
    QUIC_CODE_COUNT(quic_session_tickets_enabled);
    SSL_CTX_set_ticket_aead_method(ssl_ctx.get(),
                                   &TlsServerConnection::kSessionTicketMethod);
  } else {
    QUIC_CODE_COUNT(quic_session_tickets_disabled);
  }

  SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);

  SSL_CTX_set_select_certificate_cb(
      ssl_ctx.get(), &TlsServerConnection::EarlySelectCertCallback);
  SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);

  // Allow ProofSource to change SSL_CTX settings.
  proof_source->OnNewSslCtx(ssl_ctx.get());

  return ssl_ctx;
}
  • SSL_OP_CIPHER_SERVER_PREFERENCE用于设置SSL/TLS握手期间的密码套件协商顺序。启用此选项后,服务器将优先选择自己支持的密码套件中在客户端支持的密码套件中排名靠前的那个。这样可以增加服务器端选择更安全的密码套件的可能性,并减少使用较弱密码套件的风险。需要注意的是,启用此选项可能会导致某些客户端无法连接到服务器,因为它们可能仅支持不在服务器的优先列表中的密码套件。

  • SSL_CTX_set_alpn_select_cb用于设置用于应用程序自定义协议协商逻辑的回调函数。在TLS握手期间,客户端和服务器需要协商选择一个共同支持的应用层协议,这个协议可以是HTTP/1.1、HTTP/2、WebSocket等等。如果应用程序需要使用自定义的协议,可以通过实现一个回调函数并使用SSL_CTX_set_alpn_select_cb函数将其注册到SSL上下文中,来进行自定义的协议协商。回调函数将在客户端和服务器进行协议协商时被调用,它需要输入一个支持的协议列表,以及它们的长度,然后输出选择的协议。需要注意的是,回调函数必须是线程安全的,因为它可能会在多个线程中同时被调用。

  • SSL_CTX_set_tlsext_servername_callback用于设置当客户端发送Server Name Indication (SNI)扩展时的回调函数。 SNI扩展允许客户端在建立TLS连接时指定想要连接的服务器名称。如果服务器支持SNI扩展,它可以根据客户端发送的SNI扩展来选择相应的证书和密钥,从而使得一个服务器可以为多个域名提供服务。回调函数需要输入SSL连接、SNI扩展的类型和值,并返回0或1,以指示是否成功选择了相应的证书和密钥。如果回调函数返回0,则表示没有选择证书和密钥,TLS握手将失败。

  • SSL_CTX_set_select_certificate_cb用于设置一个回调函数,以在TLS握手期间选择服务器端证书。回调函数将在服务器端收到客户端的ClientHello消息后被调用,它需要从SSL连接中获取客户端发送的信息,然后选择合适的证书来进行TLS握手。需要注意的是,回调函数必须是线程安全的,因为它可能会在多个线程中同时被调用。

  • SSL_CTX_set_ticket_aead_method用于设置用于处理TLS会话票据加密的加密算法。TLS会话票据是一种机制,用于在客户端和服务器之间共享TLS会话状态,从而提高HTTPS连接的性能和安全性。在TLS 1.3中,会话票据被加密并使用AEAD算法进行保护。SSL_CTX_set_ticket_aead_method函数可以用于设置用于加密和解密会话票据的AEAD算法,以及相关的加密密钥和解密密钥

认识TlsHandshaker

TlsHandshaker的派生关系

002.png
  • TlsHandshakerTlsConnection::DelegateCrytoMessageParser派生,同时重写两个父类的接口API
  • 其中CrytoMessageParser提供如下方法:
bool TlsHandshaker::ProcessInput(absl::string_view input,
                                 EncryptionLevel level) {
  ....
  // TODO(nharper): Call SSL_quic_read_level(ssl()) and check whether the
  // encryption level BoringSSL expects matches the encryption level that we
  // just received input at. If they mismatch, should ProcessInput return true
  // or false? If data is for a future encryption level, it should be queued for
  // later?
  if (SSL_provide_quic_data(ssl(), TlsConnection::BoringEncryptionLevel(level),
                            reinterpret_cast(input.data()),
                            input.size()) != 1) {
    // SSL_provide_quic_data can fail for 3 reasons:
    // - API misuse (calling it before SSL_set_custom_quic_method, which we
    //   call in the TlsHandshaker c'tor)
    // - Memory exhaustion when appending data to its buffer
    // - Data provided at the wrong encryption level
    //
    // Of these, the only sensible error to handle is data provided at the wrong
    // encryption level.
    //
    // Note: the error provided below has a good-sounding enum value, although
    // it doesn't match the description as it's a QUIC Crypto specific error.
    parser_error_ = QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
    parser_error_detail_ = "TLS stack failed to receive data";
    return false;
  }
  AdvanceHandshake();
  return true;
}
  • SSL_provide_quic_data 用于向 TLS 连接提供 QUIC数据。当使用 QUIC 作为 TLS 的传输层协议时,它将被用到,可以实现更快速和更有效的互联网通信。该函数接受一个指向 SSL 对象的指针和一个包含 QUIC 数据的缓冲区的指针作为输入,并返回一个整数值,指示操作的成功或失败
  • 该函数的核心作用是将收到的对端的CryptoStream的信息,如Initial包和Handshake包的payload部分输入到ssl引擎,如果输入成功则回调用AdvanceHandshake()函数进而调用SSL_do_handshake()进行握手处理
  • TlsConnection::Delegate接口提供的API在上一节中有提到,主要有加解密引擎的设置函数、握手信息回调函数、错误处理函数等,该模块实现了这些接口并在对应的函数中进行了处理,如创建加解密引擎,转发握手信息等,其实现如下:
void TlsHandshaker::SetWriteSecret(EncryptionLevel level,
                                   const SSL_CIPHER* cipher,
                                   absl::Span write_secret) {
  std::unique_ptr encrypter =
      QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
  const EVP_MD* prf = Prf(cipher);
  CryptoUtils::SetKeyAndIV(prf, write_secret,
                           handshaker_delegate_->parsed_version(),
                           encrypter.get());
  std::vector header_protection_key =
      CryptoUtils::GenerateHeaderProtectionKey(
          prf, write_secret, handshaker_delegate_->parsed_version(),
          encrypter->GetKeySize());
  encrypter->SetHeaderProtectionKey(
      absl::string_view(reinterpret_cast(header_protection_key.data()),
                        header_protection_key.size()));
  if (level == ENCRYPTION_FORWARD_SECURE) {
    QUICHE_DCHECK(latest_write_secret_.empty());
    latest_write_secret_.assign(write_secret.begin(), write_secret.end());
    one_rtt_write_header_protection_key_ = header_protection_key;
  }
  handshaker_delegate_->OnNewEncryptionKeyAvailable(level,
                                                    std::move(encrypter));
}
  • 通过ssl提供的加密套件秘钥创建QuicEncrypter加密模块引擎,并通过HandshakerDelegateInterface接口将其转发到其他模块
bool TlsHandshaker::SetReadSecret(EncryptionLevel level,
                                  const SSL_CIPHER* cipher,
                                  absl::Span read_secret) {
  std::unique_ptr decrypter =
      QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
  const EVP_MD* prf = Prf(cipher);
  CryptoUtils::SetKeyAndIV(prf, read_secret,
                           handshaker_delegate_->parsed_version(),
                           decrypter.get());
  std::vector header_protection_key =
      CryptoUtils::GenerateHeaderProtectionKey(
          prf, read_secret, handshaker_delegate_->parsed_version(),
          decrypter->GetKeySize());
  decrypter->SetHeaderProtectionKey(
      absl::string_view(reinterpret_cast(header_protection_key.data()),
                        header_protection_key.size()));
  if (level == ENCRYPTION_FORWARD_SECURE) {
    QUICHE_DCHECK(latest_read_secret_.empty());
    latest_read_secret_.assign(read_secret.begin(), read_secret.end());
    one_rtt_read_header_protection_key_ = header_protection_key;
  }
  return handshaker_delegate_->OnNewDecryptionKeyAvailable(
      level, std::move(decrypter),
      /*set_alternative_decrypter=*/false,
      /*latch_once_used=*/false);
}
  • 通过ssl提供的解密套件秘钥创建QuicDecrypter解密模块引擎,并通过HandshakerDelegateInterface接口将其转发到其他模块
void TlsHandshaker::WriteMessage(EncryptionLevel level,
                                 absl::string_view data) {
  stream_->WriteCryptoData(level, data);
}
  • ssl生成握手信息,然后通过QuicCryptoStream进行对应的发送处理,如initial包和handshake包的payload信息就是这个吧?

TlsHandshaker的核心成员变量

class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate,
                                          public CryptoMessageParser {
 public:
  // TlsHandshaker does not take ownership of any of its arguments; they must
  // outlive the TlsHandshaker.
  TlsHandshaker(QuicCryptoStream* stream, QuicSession* session); 
  ...
 private:
  ...
  QuicCryptoStream* stream_;
  HandshakerDelegateInterface* handshaker_delegate_;
};
TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream, QuicSession* session)
    : stream_(stream), handshaker_delegate_(session) {}
  • 由此可看出QuicSessionHandshakerDelegateInterface接口的派生类,这样TlsHandshakerQuicSession就关联起来了
  • QuicCryptoStream模块对ssl引擎生成的握手信息进行组装打包成QuicCryptoFrame,再经由其他模块发生到网络

TlsHandshaker的子类介绍

TlsServerHandshaker
class QUIC_EXPORT_PRIVATE TlsServerHandshaker
    : public TlsHandshaker,
      public TlsServerConnection::Delegate,
      public ProofSourceHandleCallback,
      public QuicCryptoServerStreamBase {
      ......
 protected:          
  const TlsConnection* tls_connection() const override {
    return &tls_connection_;
  }      
          
 private:
  TlsServerConnection tls_connection_;
}
TlsServerHandshaker::TlsServerHandshaker(
    QuicSession* session, const QuicCryptoServerConfig* crypto_config)
    : TlsHandshaker(this, session),
      QuicCryptoServerStreamBase(session),
      proof_source_(crypto_config->proof_source()),
      pre_shared_key_(crypto_config->pre_shared_key()),
      crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
      tls_connection_(crypto_config->ssl_ctx(), this, session->GetSSLConfig()),
      crypto_config_(crypto_config) {

  // Configure the SSL to be a server.
  SSL_set_accept_state(ssl());

  // Make sure we use the right TLS extension codepoint.
  int use_legacy_extension = 0;
  if (session->version().UsesLegacyTlsExtension()) {
    use_legacy_extension = 1;
  }
  SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);

#if BORINGSSL_API_VERSION >= 22
  if (!crypto_config->preferred_groups().empty()) {
    SSL_set1_group_ids(ssl(), crypto_config->preferred_groups().data(),
                       crypto_config->preferred_groups().size());
  }
#endif  // BORINGSSL_API_VERSION
}
  • TlsServerHandshakerTlsHandshaker、TlsServerConnection::Delegate、ProofSourceHandleCallback、QuicCryptoServerStreamBase派生

  • QuicCryptoServerStreamBaseQuicCryptoStream的子类,由此可见在上面提及到的TlsHandshaker模块中的核心成员变量QuicCryptoStream stream_针对服务端的实现其实就是TlsServerHandshaker*自身

  • 在上一节中提到的TlsServerConnection模块其内部操作,实际上都是基于其Delegate成员派发进行处理,从而可知,TlsServerConnection中的大部分回调操作都是在TlsServerHandshaker完成

  • ProofSourceHandleCallback是证书相关的API,针对服务端也是在该模块中进行处理诸如证书签名、证书选择、应用参数设置等等很多都是在该模块中具体实现

  • TlsServerHandshaker定义成员变量TlsServerConnection tls_connection_,这样TlsServerHandshakerTlsServerConnection就正式关联起来了,在该模块中可以对TlsServerConnection进行相应的操作

TlsClientHandshaker
// An implementation of QuicCryptoClientStream::HandshakerInterface which uses
// TLS 1.3 for the crypto handshake protocol.
class QUIC_EXPORT_PRIVATE TlsClientHandshaker
    : public TlsHandshaker,
      public QuicCryptoClientStream::HandshakerInterface,
      public TlsClientConnection::Delegate {
 public:
  // |crypto_config| must outlive TlsClientHandshaker.
  TlsClientHandshaker(const QuicServerId& server_id, QuicCryptoStream* stream,
                      QuicSession* session,
                      std::unique_ptr verify_context,
                      QuicCryptoClientConfig* crypto_config,
                      QuicCryptoClientStream::ProofHandler* proof_handler,
                      bool has_application_state);
  // From QuicCryptoClientStream::HandshakerInterface
  bool CryptoConnect() override;          
 protected:
  const TlsConnection* tls_connection() const override {
    return &tls_connection_;
  }
  ...
 private:
  ...
  QuicSession* session_;
  TlsClientConnection tls_connection_;
  // Used for session resumption. |session_cache_| is owned by the
  // QuicCryptoClientConfig passed into TlsClientHandshaker's constructor.
  SessionCache* session_cache_;
};
  • TlsClientHandshakerTlsHandshaker、QuicCryptoClientStream::HandshakerInterface、TlsClientConnection::Delegate派生
  • QuicCryptoClientStream::HandshakerInterface为更上层业务提供对应的API,比如CryptoConnect(),这个是客户端连接服务端的入口
  • TlsClientConnection::DelegateTlsConnection子类介绍中有提及主要提供InsertSession()API,在TlsClientConnection::CreateSslCtx函数中,通过调用SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback)设置了一个回调函数,该函数的核心作用是当tls创建一个TLS session的时候触发,而该回调函数的核心处理就是通过TlsClientConnection::Delegate接口的InsertSession()进行转发,实现如下
void TlsClientHandshaker::InsertSession(bssl::UniquePtr session) {
  if (!received_transport_params_) {
    QUIC_BUG(quic_bug_10576_8) << "Transport parameters isn't received";
    return;
  }
  if (session_cache_ == nullptr) {
    QUIC_DVLOG(1) << "No session cache, not inserting a session";
    return;
  }
  if (has_application_state_ && !received_application_state_) {
    // Application state is not received yet. cache the sessions.
    if (cached_tls_sessions_[0] != nullptr) {
      cached_tls_sessions_[1] = std::move(cached_tls_sessions_[0]);
    }
    cached_tls_sessions_[0] = std::move(session);
    return;
  }
  session_cache_->Insert(server_id_, std::move(session),
                         *received_transport_params_,
                         received_application_state_.get());
}
  • SessionCache是在服务启动阶段初始化的一个模块,用于状态恢复,在后续会有单独文章分析,这里只做引出
  • 这里只要了解,同ssl引擎收到服务端的握手信息,并成功创建SSL_SESSION后会触发该函数

总结:

  • 本文并未深入学习QUIC TLS1.3握手的深入流程,只是从google quiche的代码设计上简单了解和握手相关的一些模块的核心成员和定义以及对应的关系
  • TlsClientHandshaker为客户端握手的基础模块,在其构造函数中做了握手前准备如设置SSL/TLS握手过程中支持的签名算法列表、为SSL/TLS连接设置证书链和私钥、设置SSL/TLS握手过程中支持的密钥交换算法组等
  • 同时TlsClientHandshaker可通过其提供的CryptoConnect()函数开始进行握手,在该函数中首先为设置传输参数、设置应用协议等,最终调用SSL_do_handshake() 函数触发ssl引擎生成client hello信息并通过QuicCryptoStream模块进行处理最终转发到网络
  • TlsHandshaker模块由CryptoMessageParser派生提供ProcessInput函数用于输入client hello信息,作为服务端,当收到客户端的client hello后,会通过QuicCryptoStream进行处理,并提取出client hello信息,在经过TlsServerHandshaker、TlsHandshaker、TlsServerConnection将该信息输入到ssl引擎进行处理,从而触发握手流程
  • QuicCryptoStream、TlsClientHandshaker、TlsHandshaker、TlsClientConnetion、TlsConnetion五大模块组成了客户端握手核心引擎
  • TlsServerHandshaker、TlsHandshaker、TlsServerConnetion、TlsConnetion四大模块组成了服务端握手核心引擎
  • 后续基于本文会对TLS1.3 握手进行详细的分析

你可能感兴趣的:(google quic tls握手原理(一))