TLS 记录协议如何重组接收到的数据?

How does the TLS Record Protocol reassemble received data?

我找不到有关 TLS 记录协议如何重组接收到的数据的详细信息。在 RFC 5246 中:

Received data is decrypted, verified, decompressed, reassembled, and then delivered to higher-level clients.

但是怎么做呢?这是记录层数据的样子:

struct {
    ContentType type;
    ProtocolVersion version;
    uint16 length;
    opaque fragment[TLSPlaintext.length];
} TLSPlaintext;

长度字段就是片段的长度:

The length (in bytes) of the following TLSPlaintext.fragment

我希望在记录协议中看到完整的长度 Header。 Google 几乎没有给出任何结果,这让我觉得我遗漏了一些明显的东西......

TLS 在流上执行,这些流中的数据被放入一个或多个片段(最大 2^14 字节)。这叫做碎片化。

规范本身的内容如下:

Client message boundaries are not preserved in the record layer (i.e., multiple client messages of the same ContentType MAY be coalesced into a single TLSPlaintext record, or a single message MAY be fragmented across several records).

这与将它们放入一个流中,然后再次将流分成单独的片段是一样的。

如果您通过 TLS 接收数据,则必须从单独的片段中重新创建该流。因此,发生的 "reassembly" 是将片段简单地串联成一个流。

套接字包含输出和输入流。输出流需要分片,输入流需要重组。

没有什么神奇的事情发生,这可能就是为什么你找不到太多东西的原因。


然而,应用层通常会被提供一个相对较低级别的 TLS 层接口,这将允许它包装或发送自己的片段。例如,此 API by IBM 表明它允许用户包装和发送或接收和解包每条消息本身。在这种情况下,库的用户需要处理任何消息碎片/重组。

由于 TLS 未指定分段/组装的实际方法,因此应将其视为特定于实现。

我也有过同样的疑问,通过一些研究我得出以下结论:如果你在可靠的协议(例如 TCP)之上使用 TLS,那么重组只是串联的结果收到的记录协议片段。由于这样一个可靠的协议保证数据以与发送时相同的顺序接收(当然,没有损坏),接收到的字节的简单连接就足以进行重组,并且由客户端知道如何解释那些接收到的字节。但是,当 TLS 不是 运行 通过像 TCP 这样的可靠协议时会发生什么?有一个名为 Datagram Transport Layer Security 的协议可以做到这一点。直接引用自维基百科:

because it uses UDP or SCTP, the application has to deal with packet reordering, loss of datagram and data larger than the size of a datagram network packet

在这种情况下它究竟是如何工作的?我从 IETF 找到了这个 draft,它概述了 DTLS 的工作原理。包含应用程序数据的消息的问题已通过以下约束解决:

Each DTLS message MUST fit within a single transport layer datagram

然而,握手消息是另一回事:

However, handshake messages are potentially bigger than the maximum record size. Therefore, DTLS provides a mechanism for fragmenting a handshake message over a number of records, each of which can be transmitted separately, thus avoiding IP fragmentation

具体的握手消息格式如下:

struct {
      HandshakeType msg_type;    /* handshake type */
      uint24 length;             /* bytes in message */
      uint16 message_seq;        /* DTLS-required field */
      uint24 fragment_offset;    /* DTLS-required field */
      uint24 fragment_length;    /* DTLS-required field */
      select (HandshakeType) {
          case client_hello:          ClientHello;
          case server_hello:          ServerHello;
          case end_of_early_data:     EndOfEarlyData;
          case hello_retry_request:   HelloRetryRequest;
          case encrypted_extensions:  EncryptedExtensions;
          case certificate_request:   CertificateRequest;
          case certificate:           Certificate;
          case certificate_verify:    CertificateVerify;
          case finished:              Finished;
          case new_session_ticket:    NewSessionTicket;
          case key_update:            KeyUpdate; /* reserved */
      } body;
  } Handshake;

如您所见,它包括一个序列号、一个片段偏移量和一个片段长度。这些是我还希望在 TLS 记录协议的消息 header 中看到的字段,但结果证明在使用 TCP 时不需要它们。

我希望你会觉得这有用。