kejsarn
kejsarn

Reputation: 75

How does the TLS Record Protocol reassemble received data?

I can't find detailed information about how the TLS Record Protocol is able to reassemble received data. In the RFC 5246:

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

But HOW? This is how the record layer data looks like:

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

The length field is just the length of the fragment:

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

I would expect to see the complete length in the Record Protocol Header. Google gives almost no results for this which makes me feel I'm missing something obvious...

Upvotes: 5

Views: 2553

Answers (2)

Andrea B.
Andrea B.

Reputation: 41

I've had the same doubt, and through some research I've come to the following conclusion: if you use TLS on top of a reliable protocol (e.g. TCP) then reassembly is simply the result of the concatenation of the received Record Protocol fragments. Since such a reliable protocol guarantees that the data is received in the same order it was sent (and is, of course, not corrupt), the simple concatenation of the received bytes is sufficient for reassembly, and it's up to the client to know how to interpret those received bytes. However, what happens when TLS is not run over a reliable protocol like TCP? There is a protocol called Datagram Transport Layer Security that does that. Quoting directly from Wikipedia:

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

How exactly does it work in this case? I've found this draft from IETF which outlining the workings of DTLS. The issue for messages containing application data is solved with the following constraint:

Each DTLS message MUST fit within a single transport layer datagram

Handshake messages, however, are a different matter:

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

In particular, the format of the Handshake Message is the following:

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;

As you can see, it includes a sequence number, a fragment offset and a fragment length. Those are the fields that I also expected to see in the header of messages of the Record Protocol of TLS, but they turn out to not be necessary when using TCP.

I hope you may find this useful.

Upvotes: 1

Maarten Bodewes
Maarten Bodewes

Reputation: 94098

TLS is performed on streams, and the data in those streams are put into one or more fragments (of max 2^14 bytes). This is called fragmentation.

The spec itself talks reads:

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).

which is simply the same as putting them into a stream, and then breaking the stream in separate fragments again.

If you receive data through TLS then you'll have to recreate that stream from the separate fragments. The "reassembly" that happens is therefore simple concatenation of the fragments into a stream.

A socket contains an output and input stream. The output stream needs to fragment and the input stream needs to reassemble.

Nothing magical happens, which is probably why you cannot find much.


Often the application layer will however be given a relatively low level interface to the TLS layer, which will allow it to wrap or send its own fragments. For instance, this API by IBM shows that it allows the user to wrap & send or receive and unwrap each message itself. In that case the user of the library needs to take care of any message fragmentation / reassembly.

As the actual method of fragmentation / assembly is not specified by TLS it should be considered implementation specific.

Upvotes: 2

Related Questions