RFC 8548
Cryptographic Protection of TCP Streams (tcpcrypt)
RFC 8548: Cryptographic Protection of TCP Streams (tcpcrypt) ๐ฆ
Okay bestie, let's get the tea on this one. The title block says this is RFC 8548, published in May 2019 by the IETF, with ISSN 2070-1721. It's Experimental, so it's a "let's try it and see" doc and not a Standards Track spec ๐งช. The whole thing is about tcpcrypt (Cryptographic Protection of TCP Streams), and it's written by a squad of six: A. Bittau (Google), D. Giffin (Stanford University), M. Handley (University College London), D. Mazieres (Stanford University), Q. Slack (Sourcegraph), and E. Smith (Kestrel Institute) ๐. The title block doesn't list anything it obsoletes or updates, so it's not replacing or changing any other RFC ๐ .
Abstract
Okay so tcpcrypt is a TCP encryption protocol that's built to work with the TCP Encryption Negotiation Option (TCP-ENO) ๐. What's giving? It plays nicely with middleboxes, meaning it keeps working even when things like resegmentation, NATs, and other messing-around with the TCP header happen ๐ซถ. It's self-contained and tailored to TCP implementations, which is a big deal because those often live in kernels or other places where huge external software dependencies are a no-go ๐.
The catch is that TCP options have a size limit, so tcpcrypt needs one extra one-way message latency to do key exchange before any application data can go out โณ. But here's the good part: two hosts that recently had a tcpcrypt connection can skip that extra wait ๐.
Status of This Memo
This doc is not an Internet Standards Track spec. It's published for examination, experimental implementation, and evaluation ๐งช.
It defines an Experimental Protocol for the Internet community โจ. It's a product of the IETF, it represents IETF consensus, it got public review, and the IESG approved it for publication ๐. Not every IESG-approved doc is up for any level of Internet Standard, so check Section 2 of RFC 7841 if you want the deets ๐ค.
Info on the current status, errata, and how to give feedback lives at https://www.rfc-editor.org/info/rfc8548 ๐.
Copyright Notice
Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved ๐.
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the publication date. Please read these carefully, because they spell out your rights and restrictions for this doc ๐. Code Components pulled out of this doc must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License ๐ธ.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Requirements Language . . . . . . . . . . . . . . . . . . . . 4
3. Encryption Protocol . . . . . . . . . . . . . . . . . . . . . 4
3.1. Cryptographic Algorithms . . . . . . . . . . . . . . . . 4
3.2. Protocol Negotiation . . . . . . . . . . . . . . . . . . 6
3.3. Key Exchange . . . . . . . . . . . . . . . . . . . . . . 7
3.4. Session ID . . . . . . . . . . . . . . . . . . . . . . . 10
3.5. Session Resumption . . . . . . . . . . . . . . . . . . . 10
3.6. Data Encryption and Authentication . . . . . . . . . . . 14
3.7. TCP Header Protection . . . . . . . . . . . . . . . . . . 16
3.8. Rekeying . . . . . . . . . . . . . . . . . . . . . . . . 16
3.9. Keep-Alive . . . . . . . . . . . . . . . . . . . . . . . 17
4. Encodings . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1. Key-Exchange Messages . . . . . . . . . . . . . . . . . . 18
4.2. Encryption Frames . . . . . . . . . . . . . . . . . . . . 20
4.2.1. Plaintext . . . . . . . . . . . . . . . . . . . . . . 20
4.2.2. Associated Data . . . . . . . . . . . . . . . . . . . 21
4.2.3. Frame ID . . . . . . . . . . . . . . . . . . . . . . 21
4.3. Constant Values . . . . . . . . . . . . . . . . . . . . . 22
5. Key-Agreement Schemes . . . . . . . . . . . . . . . . . . . . 22
6. AEAD Algorithms . . . . . . . . . . . . . . . . . . . . . . . 24
7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 24
8. Security Considerations . . . . . . . . . . . . . . . . . . . 25
8.1. Asymmetric Roles . . . . . . . . . . . . . . . . . . . . 27
8.2. Verified Liveness . . . . . . . . . . . . . . . . . . . . 27
8.3. Mandatory Key-Agreement Schemes . . . . . . . . . . . . . 27
9. Experiments . . . . . . . . . . . . . . . . . . . . . . . . . 28
10. References . . . . . . . . . . . . . . . . . . . . . . . . . 29
10.1. Normative References . . . . . . . . . . . . . . . . . . 29
10.2. Informative References . . . . . . . . . . . . . . . . . 30
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 31
Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 31
1. Introduction โจ
Okay, so tcpcrypt is an extension to TCP that adds cryptographic protection for session data ๐. It was designed to hit these goals:
- Meet the requirements of the TCP Encryption Negotiation Option (TCP-ENO) [RFC8547] for protecting connection data ๐ .
- Be easy to implement as a small, self-contained piece inside TCP stacks ๐งฉ.
- Keep the extra latency at connection startup to a minimum โฑ๏ธ.
- As much as possible, avoid connection failures when there are NATs and other middleboxes that might normalize traffic or otherwise mess with TCP segments ๐ก๏ธ.
- Work independently of IP addresses, so resumed sessions can still be authenticated efficiently even when either end changes its IP address ๐ฆ.
There's also a companion doc, [TCPINC-API], that describes recommended interfaces for configuring certain parameters of this protocol ๐.
2. Requirements Language ๐
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are the BCP 14 key words in this doc. They get read as described in BCP 14 [RFC2119] [RFC8174], but only when they show up in all capitals, exactly like this ๐ง.
3. Encryption Protocol ๐
This section is where the tcpcrypt protocol actually operates ๐. The wire format of all messages is specified in Section 4, so go there for the bits on the wire ๐ฆ.
3.1. Cryptographic Algorithms ๐งฎ
Setting up a tcpcrypt connection uses three types of cryptographic algorithms, and honestly each one has a job ๐:
- A key agreement scheme is used with a short-lived public key to agree upon a shared secret ๐ค.
- An extract function is used to generate a pseudo-random key (PRK) from some initial keying material produced by the key agreement scheme ๐. The notation
Extract(S, IKM)denotes the output of the extract function with saltSand initial keying materialIKM๐ง. - A collision-resistant pseudo-random function (CPRF) is used to generate multiple cryptographic keys from a pseudo-random key, which is typically the output of the extract function ๐. The CPRF produces an arbitrary amount of Output Keying Material (OKM), and the notation
CPRF(K, CONST, L)designates the firstLbytes of the OKM that the CPRF produces when it's parameterized by keyKand the constantCONSTโจ.
The Extract and CPRF functions used by the tcpcrypt variants in this doc are the Extract and Expand functions of the HMAC-based Key Derivation Function (HKDF) [RFC5869], which is built on Keyed-Hashing for Message Authentication (HMAC) [RFC2104] ๐. They're defined like this in terms of the function HMAC-Hash(key, value) for a negotiated Hash function such as SHA-256. The symbol | means concatenation, and the counter concatenated to the right of CONST takes up a single octet ๐.
HKDF-Extract(salt, IKM) -> PRK
PRK = HMAC-Hash(salt, IKM)
HKDF-Expand(PRK, CONST, L) -> OKM
T(0) = empty string (zero length)
T(1) = HMAC-Hash(PRK, T(0) | CONST | 0x01)
T(2) = HMAC-Hash(PRK, T(1) | CONST | 0x02)
T(3) = HMAC-Hash(PRK, T(2) | CONST | 0x03)
...
OKM = first L octets of T(1) | T(2) | T(3) | ...
where L <= 255*OutputLength(Hash)
Figure 1: HKDF Functions Used for Key Derivation ๐
Last but definitely not least, once tcpcrypt is successfully set up and the encryption keys are derived, an algorithm for Authenticated Encryption with Associated Data (AEAD) protects the confidentiality and integrity of all transmitted application data ๐ก๏ธ๐. AEAD algorithms use a single key both to encrypt their input data and to generate a cryptographic tag that comes along with the resulting ciphertext ๐. When decryption happens, that tag lets you authenticate the encrypted data and any optional associated plaintext data ๐ซถ.
3.2. Protocol Negotiation ๐
Okay so tcpcrypt leans on TCP-ENO [RFC8547] to figure out whether encryption gets switched on for a connection ๐ and which key-agreement scheme to use. TCP-ENO negotiates a particular TCP encryption protocol (TEP) by sticking protocol identifiers into ENO suboptions. This document links four TEP identifiers to the tcpcrypt protocol, listed in Table 4 of Section 7. Each identifier means a specific key-agreement scheme, with its own CPRF and length parameter. Future standards can add more TEP identifiers to tcpcrypt, as long as they follow the assignment policy in TCP-ENO. ๐
An active opener that wants to negotiate tcpcrypt includes an ENO option in its SYN segment ๐. That option carries suboptions with tcpcrypt TEP identifiers, which show the key-agreement schemes it's willing to turn on. The active opener MAY also include suboptions for encryption protocols other than tcpcrypt, plus global suboptions as TCP-ENO describes. Extra options, all fine ๐
If a passive opener gets an ENO option with tcpcrypt TEPs it supports, it MAY then attach an ENO option to its SYN-ACK segment. In that reply it includes only the one TEP it wants to enable. Just one, bestie, keep it simple ๐ฆ
To give the two hosts distinct roles in each connection, tcpcrypt relies on the role-negotiation mechanism of TCP-ENO. As one result of that negotiation, TCP-ENO gives the hosts abstract roles called "A" at one end and "B" at the other. Usually the active opener is "A" and the passive opener is "B", but during a simultaneous open, an extra mechanism breaks the tie and gives each host its own role ๐. TCP-ENO uses the terms "host A" and "host B" to pin down each end of a connection, and this document uses them the same way ๐
An ENO suboption has a flag "v" that says whether there's variable-length data attached. To propose fresh key agreement with a particular tcpcrypt TEP, a host sends a one-byte suboption with the TEP identifier and v = 0. To propose session resumption (more on that below) with a particular TEP, a host sends a variable-length suboption. That one has the TEP identifier, the flag v = 1, an identifier derived from a session secret previously negotiated with the same host and the same TEP, and a nonce ๐ง
Once two hosts have swapped SYN segments, TCP-ENO defines the negotiated TEP as the last valid TEP identifier in host B's SYN segment (that's the passive opener when there's no simultaneous open) that also shows up in host A's. If there's no such TEP, hosts MUST disable TCP-ENO and tcpcrypt. No match, no encryption, that's the vibe ๐
If the negotiated TEP was sent by host B with v = 0, fresh key agreement gets performed as described in Section 3.3. If, on the other hand, host B sent the TEP with v = 1 and both hosts sent appropriate resumption identifiers in their suboption data, the key-exchange messages get skipped. Instead, keys come from session resumption as described in Section 3.5. With session resumption, protected application data MAY be sent immediately as detailed in Section 3.6 โญ
Note that the negotiated TEP gets picked without looking at the "v" bits in ENO suboptions. So if host A offers resumption with a particular TEP and host B replies with a non-resumption suboption using that same TEP, that TEP can become the negotiated one, and then fresh key agreement happens. ๐คฏ In other words, sending a resumption suboption also means you're willing to do fresh key agreement with that TEP. Plot twist, but it makes sense ๐ธ
As REQUIRED by TCP-ENO, once a host has both sent and received an ACK segment containing a valid ENO option, encryption MUST be enabled and plaintext application data MUST NOT ever be exchanged on the connection. ๐ If the negotiated TEP is among those listed in Table 4, a host MUST follow the protocol described in this document. No shortcuts, periodt ๐
3.3. Key Exchange ๐
Once a tcpcrypt TEP has been successfully negotiated, all further signaling happens in the Data portion of TCP segments ๐ฆ. Except when resumption was negotiated (see Section 3.5), the two hosts do key exchange through two messages, Init1 and Init2, at the start of the data streams of host A and host B, respectively. These messages MAY span multiple TCP segments and don't have to end at a segment boundary. However, the segment containing the last byte of an Init1 or Init2 message MUST have TCP's push flag (PSH) set. The PSH flag is the finish line ๐
The key exchange protocol, in the abstract, goes like this:
A -> B: Init1 = { INIT1_MAGIC, sym_cipher_list, N_A, Pub_A }
B -> A: Init2 = { INIT2_MAGIC, sym_cipher, N_B, Pub_B }
The concrete format of these messages is specified in Section 4.1 ๐
Here's what each parameter means ๐ง
INIT1_MAGIC,INIT2_MAGIC: Constants defined in Section 4.3.sym_cipher_list: A list of identifiers of symmetric ciphers (AEAD algorithms) that host A finds acceptable. These are specified in Table 5 of Section 7.sym_cipher: The symmetric cipher that host B picks from thesym_cipher_listhost A sent.N_A,N_B: Nonces chosen at random by hosts A and B, respectively.Pub_A,Pub_B: Ephemeral public keys for hosts A and B, respectively. These, along with their matching private keys, are short-lived values that MUST be refreshed frequently โณ. The private keys SHOULD NOT ever be written to persistent storage. The security risks of storing these keys are discussed in Section 8. Not giving your keys a long life is giving healthy boundaries ๐
If a host gets an ephemeral public key from its peer and a key-validation step fails (see Section 5), it MUST abort the connection and raise an error condition that's distinct from the end-of-file condition. ๐จ
The ephemeral secret ES is the result of the key-agreement algorithm (see Section 5) indicated by the negotiated TEP. The inputs to the algorithm are the local host's ephemeral private key and the remote host's ephemeral public key. For example, host A computes ES using its own private key (which never gets transmitted) and host B's public key, Pub_B ๐๏ธ
Then both sides compute a pseudo-random key, PRK, which all session secrets come from:
PRK = Extract(N_A, eno_transcript | Init1 | Init2 | ES)
In that formula, "|" means concatenation. eno_transcript is the protocol-negotiation transcript defined in Section 4.8 of [RFC8547], and Init1 and Init2 are the transmitted encodings of the messages described in Section 4.1 ๐งฉ
A series of session secrets gets computed from PRK like this:
ss[0] = PRK
ss[i] = CPRF(ss[i-1], CONST_NEXTK, K_LEN)
The value ss[0] is used to generate all key material for the current connection ๐. The values ss[i] for i > 0 are used by session resumption, so subsequent connections between the same two hosts can skip public key cryptography (see Section 3.5). The CONST_* values are constants defined in Section 4.3. The length K_LEN depends on the tcpcrypt TEP in use, and it's specified in Section 5.
Given a session secret ss[i], the two sides compute a series of master keys like so:
mk[0] = CPRF(ss[i], CONST_REKEY | sn[i], K_LEN)
mk[j] = CPRF(mk[j-1], CONST_REKEY, K_LEN)
Moving along that series of master keys is described in Section 3.8 ๐. The values represented by sn[i] are session nonces. For the initial session, where i = 0, the session nonce is zero bytes long. The values for later sessions come from fresh connection data, as described in Section 3.5.
Last step: each master key mk[j] generates traffic keys for protecting application data with authenticated encryption ๐ก๏ธ
k_ab[j] = CPRF(mk[j], CONST_KEY_A, ae_key_len + ae_nonce_len)
k_ba[j] = CPRF(mk[j], CONST_KEY_B, ae_key_len + ae_nonce_len)
In the first session from fresh key agreement, host A uses traffic keys k_ab[j] to encrypt, and host B uses them to decrypt. Meanwhile, host B uses k_ba[j] to encrypt, and host A uses those to decrypt. โ๏ธ In a resumed session (more thoroughly covered in Section 3.5), each host uses the keys the same way it did in the original session, no matter what role it has in the current session. For example, if a host played role "A" in the first session, it'll use k_ab[j] to encrypt in every derived session. Consistency is everything ๐
The values ae_key_len and ae_nonce_len depend on the authenticated-encryption algorithm selected, and they're given in Table 3 of Section 6. The algorithm uses the first ae_key_len bytes of each traffic key as an authenticated-encryption key, and it uses the following ae_nonce_len bytes as a nonce randomizer ๐ฒ
Implementations SHOULD provide an interface that lets the user specify, for a particular connection, the set of AEAD algorithms to advertise in sym_cipher_list (when playing role "A") and the order of preference for picking an algorithm from those offered (when playing role "B") โ๏ธ. A companion document [TCPINC-API] describes recommended interfaces for this purpose ๐
After host B sends Init2, or host A receives it, that host MAY immediately begin transmitting protected application data, as described in Section 3.6 ๐
If host A receives an Init2 with a sym_cipher value that wasn't in the sym_cipher_list it sent in Init1, it MUST abort the connection and raise an error condition distinct from the end-of-file condition. ๐ฑ Yikes, that's a hard no.
Throughout this document, to "abort the connection" means issuing the "Abort" command described in Section 3.8 of [RFC793]. In other words, the TCP connection gets destroyed, RESET is transmitted, and the local user is alerted to the abort event ๐จ
3.4. Session ID ๐ชช
TCP-ENO requires each TEP to define a session ID value that uniquely identifies each encrypted connection ๐
A tcpcrypt session ID begins with the byte host B transmits that contains the negotiated TEP identifier along with the "v" bit. The rest of the ID is derived from the session secret and session nonce, like this:
session_id[i] = TEP-byte | CPRF(ss[i], CONST_SESSID | sn[i], K_LEN)
Again, the length K_LEN depends on the TEP and is specified in Section 5 โจ
3.5. Session Resumption โจ
okay so if two hosts already negotiated a session with secret ss[i-1], bestie, they can connect again WITHOUT any public-key operations ๐ฅ they just use ss[i], the next secret in the chain that was derived from the original PRK. so efficient, it's giving main character energy ๐
to say "hey, I wanna resume this session," a host sends a SYN segment with a resumption suboption. that's an ENO suboption carrying three things: the negotiated TEP identifier from the previous session, half of the resumption identifier for the new session, and a resumption nonce ๐
The resumption nonce MUST have a minimum length of zero bytes and a maximum length of eight bytes. The value MUST be picked randomly, or with a mechanism that guarantees uniqueness even if a VM gets cloned or the same session gets re-executed ๐
ngl this is the scariest part ๐ an attacker who can force either side of a connection to reuse a session secret with the same nonce will completely break the security of tcpcrypt. reuse can happen with VM cloning or when system-level hibernation state gets reused, so be careful bestie ๐ญ
Implementations SHOULD give you an API for setting the resumption nonce length, and they MUST default to eight bytes if they can't prevent session secret reuse ๐ซถ
The resumption identifier is calculated from a session secret ss[i] like this:
resume[i] = CPRF(ss[i], CONST_RESUME, 18)
To name a session for resumption, a host sends either the first or the second half of the resumption identifier, depending on the role it played in the original session with secret ss[0] ๐ฆ
A host that originally played role "A" and wants to resume from a cached session sends a suboption with the first half of the resumption identifier:
byte 0 1 9 10
+------+------+--...--+------+------+--...--+------+
| TEP- | resume[i]{0..8} | nonce_a |
| byte | | |
+------+------+--...--+------+------+--...--+------+
Figure 2: Resumption suboption sent when original role was "A".
The TEP-byte contains a tcpcrypt TEP identifier and v = 1. The nonce value MUST have a length between 0 and 8 bytes ๐ธ
Similarly, a host that originally played role "B" sends a suboption with the second half of the resumption identifier:
byte 0 1 9 10
+------+------+--...--+------+------+--...--+------+
| TEP- | resume[i]{9..17} | nonce_b |
| byte | | |
+------+------+--...--+------+------+--...--+------+
Figure 3: Resumption suboption sent when original role was "B".
The TEP-byte contains a tcpcrypt TEP identifier and v = 1. The nonce value MUST have a length between 0 and 8 bytes ๐ง
If a passive opener gets a resumption suboption with an identifier-half that names a session secret it has cached, and the suboption's TEP matches the TEP used in the previous session, it SHOULD agree to resume (with exceptions, specified below) by sending its own resumption suboption containing the other half of the identifier โจ Otherwise, it MUST NOT agree to resumption ๐ โโ๏ธ
If a passive opener doesn't agree to resume with a particular TEP, it MAY either ask for a fresh key exchange by replying with a non-resumption suboption using the same TEP, or respond to any other TEP suboption it received ๐
If a passive opener gets an ENO suboption with a TEP identifier and v = 1, but the suboption data is less than 9 bytes long, it MUST behave as if that same TEP had been sent with v = 0. In other words, the suboption MUST be read as an offer to negotiate a fresh key exchange with that TEP ๐
If an active opener sends a resumption suboption with a particular TEP and the appropriate half of a resumption identifier, and then, in the same TCP handshake, gets a resumption suboption with the same TEP and an identifier-half that does NOT match that resumption identifier, it MUST ignore that suboption. In the typical case where this was the only ENO suboption received, the host MUST disable TCP-ENO and tcpcrypt. It MUST NOT send any more ENO options and MUST NOT encrypt the connection ๐ญ๐
When a host concludes that TCP-ENO negotiation has succeeded for some TEP that was received in a resumption suboption, it MUST then enable encryption with that TEP using the cached session secret. To do this, it first builds sn[i] like so:
sn[i] = nonce_a | nonce_b
Master keys are then computed from s[i] and sn[i], as described in Section 3.3, as well as from application data encrypted as described in Section 3.6 ๐ฅ
The session ID (Section 3.4) is built the same way for resumed sessions as it is for fresh ones. In this case the first byte will always have v = 1. The rest of the ID is derived from the cached session secret and the session nonce that was generated during resumption ๐ฆ
In a simultaneous open where TCP-ENO is able to establish asymmetric roles, two hosts that simultaneously send SYN segments with compatible resumption suboptions MAY resume the associated session ๐
In a particular SYN segment, a host SHOULD NOT send more than one resumption suboption (because that eats up TCP option space and is unlikely to be a useful practice), and it MUST NOT send more than one resumption suboption with the same TEP identifier. But in addition to any resumption suboptions, an active opener MAY include non-resumption suboptions describing other TEPs it supports (in addition to the TEP in the resumption suboption) ๐
After using the session secret ss[i] to compute mk[0], implementations SHOULD compute and cache ss[i+1] for possible use by a later session, and then erase ss[i] from memory. Hosts MAY keep ss[i+1] until it gets used or the memory needs to be reclaimed. Hosts SHOULD NOT write any session secrets to non-volatile storage ๐๐
When proposing resumption, the active opener MUST use the lowest value of "i" that hasn't already been used (successfully or not) to negotiate resumption with the same host and for the same original session secret ss[0] โญ
A given session secret ss[i] MUST NOT be used to secure more than one TCP connection. To prevent this, a host MUST NOT resume with a session secret if it has EVER enabled encryption with that same secret in the past, in either role ๐ซ If two hosts simultaneously send SYN segments to each other that propose resumption with the same session secret, but the two segments are not part of a simultaneous open, both connections would need to fall back to fresh key exchange. To avoid this limitation, implementations MAY choose to implement session resumption so that all session secrets derived from a given ss[0] are used for either passive or active opens at the same host, but not both ๐
If two hosts have previously negotiated a tcpcrypt session, either host MAY later start session resumption, regardless of which host was the active opener or played the "A" role in the previous session ๐
However, a given host MUST either encrypt with keys k_ab[j] for all sessions derived from the same original session secret ss[0], or with keys k_ba[j]. So which keys a host uses to send segments is NOT affected by the role it plays in the current connection. It depends only on whether the host played the "A" or "B" role in the initial session ๐ง
Implementations that cache session secrets MUST provide a way for applications to control that caching. In particular, when an application requests a new TCP connection, it MUST have a way to specify two policies for the duration of the connection: 1) that resumption requests will be ignored, so fresh key exchange will be necessary; and 2) that no session secrets will be cached. (These policies can be specified independently or together as a unit.) And for an established connection, an application MUST have a means to cause any cache state that was used in or resulted from establishing the connection to be flushed. A companion document, [TCPINC-API], describes recommended interfaces for this purpose ๐ซถ๐ธ
3.6. Data Encryption and Authentication โจ
okay so once the key exchange is done (or skipped, thanks to session resumption ๐ ), every bit of communication on a tcpcrypt-enabled connection gets wrapped up in delimited encryption frames. Those frames are encrypted and authenticated with the keys you agreed on. main character energy, fr fr ๐ฅ
The protection comes from AEAD (Authenticated Encryption with Associated Data) algorithms ๐. The allowed ones are listed in Table 5 of Section 7, and more can be added later under the policy in that same section. One algorithm gets picked during the negotiation from Section 3.3. The ae_key_len and ae_nonce_len values for each algorithm live in Table 3 of Section 6, along with the list of which algorithms MUST be implemented ๐.
The frame format is in Section 4.2 ๐ฆ. A sending host chops its stream of application data into a bunch of chunks. Each chunk goes into the data field of a plaintext value, and that gets encrypted into the frame's ciphertext field. The chunks MUST be small enough that the ciphertext (which depends on the AEAD cipher and is usually a little longer than the plaintext) comes out under 2^16 bytes, so it's less than that length. no cap, it's a hard limit ๐
Next, an "associated data" value (see Section 4.2.2) gets built for each frame. It holds the frame's control field and the length of the ciphertext ๐ง.
There's also a "frame ID" value (see Section 4.2.3) built for each frame, but it never actually gets sent over the wire ๐ธ. It's a 64-bit offset field, and its integer value is the zero-indexed byte offset where the current encryption frame starts in the underlying TCP datastream. (Note: that's the offset in the framing stream, not the plaintext application stream ๐.) Then the offset gets left-padded with zero-valued bytes until it's exactly ae_nonce_len long. This part is seriously important: because the security of the AEAD algorithms in this document depends on it, an implementation MUST NOT ever transmit distinct frames with the same frame ID value under the same encryption key ๐. Retransmitted TCP segments MUST contain the same payload bytes for the same TCP sequence numbers. And a host MUST NOT transmit more than 2^64 bytes in the underlying TCP datastream before rekeying as described in Section 3.8, because that would make the offset field wrap around ๐ญ.
The AEAD keys come from the traffic key k_ab[j] or k_ba[j] (for some "j"), depending on the host's role from Section 3.3 ๐๏ธ. First, the traffic key gets split into two parts:
ae_key_len + ae_nonce_len - 1
|
byte 0 ae_key_len |
| | |
v v v
+----+----+--...--+----+----+----+--...--+----+
| K | NR |
+----+----+--...--+----+----+----+--...--+----+
Figure 4: Format of Traffic Key
Following the "AEAD Interface" from Section 2 of [RFC5116], the first ae_key_len bytes of the traffic key give you the AEAD key K ๐. The leftover ae_nonce_len bytes give you a nonce randomizer value NR. That gets XORed with the frame ID to make N, the AEAD nonce for the frame โจ:
N = frame_ID XOR NR
The other two AEAD inputs, P and A, come from the frame's plaintext value and associated data, respectively. The output of the AEAD operation, C, is what gets sent in the frame's ciphertext field ๐.
When a frame shows up, tcpcrypt rebuilds the associated data and frame ID values (the associated data only has stuff that was sent in the clear, and the frame ID is implicit in the TCP stream) ๐งฉ. Then it works out the nonce N the same way as above, and hands those plus the ciphertext to the AEAD decryption operation. That operation returns either a plaintext value P or the special symbol FAIL. If it's FAIL, the implementation SHOULD abort the connection and raise an error condition that's different from the end-of-file condition ๐จ. But if none of the TCP segment(s) holding the frame have been acknowledged, and retransmitting them could possibly result in a valid frame, the implementation MAY drop those segments instead. In that case it can renege on them if they were SACKed, following Section 8 of [RFC2018] ๐.
3.7. TCP Header Protection
the ciphertext field of each encryption frame carries protected versions of certain TCP header values ๐ก๏ธ. It's giving security, lowkey iconic.
When the URGp bit is set, the urgent field tells you an offset from the current frame's starting offset. Add up those offsets and you get the index of the last byte of urgent data in the application datastream ๐.
A sender MUST set the FINp bit on the last frame it sends in the connection (unless it's aborting the connection), and it MUST NOT set FINp on any other frame ๐. Not me setting FINp on the wrong frame, bestie ๐ โโ๏ธ.
TCP normally sets the FIN flag when a sender runs out of data. With tcpcrypt, that means setting FIN on the segment that holds the last byte of the last frame ๐ฌ. But here's the twist: a receiver MUST report the end-of-file condition to the connection's local user when, and only when, it receives a frame with the FINp bit set ๐ง. If a host gets a segment with the TCP FIN flag set, but the datastream it's received so far (including that segment) doesn't have a frame with FINp set, the host SHOULD abort the connection and raise an error condition that's different from the end-of-file condition ๐ค. But if there are unacknowledged segments that could possibly turn into a valid frame when retransmitted, the host MAY drop the segment with the TCP FIN flag set instead. It can renege on it if it's been SACKed, following Section 8 of [RFC2018] ๐.
3.8. Rekeying
Rekeying lets hosts wipe keys out of memory that could otherwise decrypt segments they already sent ๐งน. It also makes it possible to use AEAD ciphers that can only safely encrypt a limited number of messages under a single key ๐.
As Section 3.3 described, a master key mk[j] generates two encryption keys, k_ab[j] and k_ba[j]. We call these a key set with generation number "j" ๐๏ธ. Every host keeps two numbers: a local generation number, which decides which key set it uses to encrypt outgoing frames, and a remote generation number, which equals the highest generation used in frames received from its peer ๐. Both of these start at zero.
A host MAY increment its local generation number past the remote generation number it has recorded. We call that "initiating rekeying" ๐.
When a host has bumped its local generation number and uses the new key set to encrypt an outgoing frame for the very first time, it MUST set rekey = 1 for that frame. It MUST set rekey = 0 in all other cases ๐ข.
When a host receives a frame with rekey = 1, it increments its record of the remote generation number. If that remote generation number is now bigger than the local generation number, the receiver MUST immediately increment its local generation number to match ๐ช. Also, if the receiver hasn't transmitted a segment with the FIN flag set yet, it MUST immediately send a frame (with empty application data if necessary) with rekey = 1 ๐จ.
A host MUST NOT initiate more than one concurrent rekey operation if it has no data to send. In other words, it MUST NOT initiate rekeying with an empty encryption frame more than once while its record of the remote generation number is still less than its own ๐ โโ๏ธ.
Here's a detail to keep in mind: when parts of the datastream get retransmitted, TCP requires implementations to always send the same data bytes for the same TCP sequence numbers ๐งต. So frame data in retransmitted segments MUST be encrypted with the same key it was originally sent with, no matter what the current local generation number is ๐.
Implementations SHOULD delete older-generation keys from memory once they've received all the frames they'll need to decrypt with the old keys, and once they've encrypted every outgoing frame under the old keys ๐๏ธ.
3.9. Keep-Alive
Rather than using TCP keep-alives to check that the remote endpoint is still responsive, tcpcrypt implementations SHOULD use the rekeying mechanism for this job ๐. When it's necessary, a host SHOULD probe its peer's liveness by initiating rekeying and transmitting a new frame right away (with empty application data if necessary) โฐ.
As Section 3.8 described, a host that receives a frame encrypted under a generation number greater than its own MUST increment its own generation number. If it hasn't already transmitted a segment with FIN set, it should immediately transmit a new frame (with zero-length application data if necessary) too ๐ค. Okay wait, I need to be precise here: the original says it MUST increment and then immediately transmit, so that's exactly what happens ๐ .
Implementations MAY use TCP keep-alives for purposes that don't require endpoint authentication, as discussed in Section 8.2 ๐.
4. Encodings โจ
This section gives the byte-level encodings for the values the protocol transmits or computes ๐งโโ๏ธ๐.
4.1. Key-Exchange Messages
okay so the key-exchange messages are where the handshake gets real ๐ฆ Init1 comes first and it's very structured, bestie ๐
byte 0 1 2 3
+-------+-------+-------+-------+
| INIT1_MAGIC |
| |
+-------+-------+-------+-------+
4 5 6 7
+-------+-------+-------+-------+
| message_len |
| = M |
+-------+-------+-------+-------+
8
+--------+-----+----+-----+----+---...---+-----+-----+
|nciphers|sym_ |sym_ | |sym_ |
| = K |cipher[0] |cipher[1] | |cipher[K-1]|
+--------+-----+----+-----+----+---...---+-----+-----+
2*K + 9 2*K + 9 + N_A_LEN
| |
v v
+-------+---...---+-------+-------+---...---+-------+
| N_A | Pub_A |
| | |
+-------+---...---+-------+-------+---...---+-------+
M - 1
+-------+---...---+-------+
| ignored |
| |
+-------+---...---+-------+
So INIT1_MAGIC is the opener, and it's defined in Section 4.3 โจ The four-byte message_len field tells you the length of the whole Init1 message, written as a big-endian integer ๐ Then nciphers says how many two-byte symmetric-cipher identifiers come after it. Those sym_cipher[i] identifiers point at the cryptographic algorithms listed in Table 5 in Section 7 ๐ And the lengths of N_A and Pub_A aren't fixed. They depend on the negotiated TEP, as explained in Section 5 ๐ธ
Now, implementations of this protocol MUST construct Init1 so that the ignored field has zero length ๐ญ In other words, MUST construct the message so that its end (as set by message_len) lines up exactly with the end of Pub_A. But when they receive Init1, they MUST permit and ignore any bytes after Pub_A. Build it tight, read it loose ๐ง
Next up is Init2, the response ๐
byte 0 1 2 3
+-------+-------+-------+-------+
| INIT2_MAGIC |
| |
+-------+-------+-------+-------+
4 5 6 7 8 9
+-------+-------+-------+-------+-------+-------+
| message_len | sym_cipher |
| = M | |
+-------+-------+-------+-------+-------+-------+
10 10 + N_B_LEN
| |
v v
+-------+---...---+-------+-------+---...---+-------+
| N_B | Pub_B |
| | |
+-------+---...---+-------+-------+---...---+-------+
M - 1
+-------+---...---+-------+
| ignored |
| |
+-------+---...---+-------+
INIT2_MAGIC is also defined in Section 4.3 ๐ The four-byte message_len is the length of the entire Init2 message, big-endian, same energy as before. The sym_cipher value is picked from the symmetric-cipher identifiers you got in the earlier Init1 message ๐ And N_B_LEN and Pub_B are sized by the negotiated TEP, per Section 5.
Same rule applies here, bestie ๐ซถ Implementations of this protocol MUST construct Init2 so that the field "ignored" has zero length. That means they MUST construct the message so its end (set by message_len) coincides with the end of the Pub_B field. And when receiving Init2, implementations MUST permit and ignore any bytes following Pub_B.
4.2. Encryption Frames
An encryption frame is basically a control byte plus a length-prefixed ciphertext chunk ๐
byte 0 1 2 3 clen+2
+-------+-------+-------+-------+---...---+-------+
|control| clen | ciphertext |
+-------+-------+-------+-------+---...---+-------+
The clen field is a big-endian integer, and it tells you how long the ciphertext field is ๐ฆ
The control field has its own little layout:
bit 7 1 0
+-------+---...---+-------+-------+
| cres | rekey |
+-------+---...---+-------+-------+
The seven-bit cres field is reserved ๐ Implementations MUST set those bits to zero when sending, and MUST ignore them when receiving. Zero on the way out, shrug on the way in. And the rekey field? That's all explained in Section 3.8 ๐
4.2.1. Plaintext
The ciphertext field is what you get after the negotiated authenticated-encryption algorithm works on a plaintext value ๐ฅ That plaintext comes in one of two shapes:
byte 0 1 plen-1
+-------+-------+---...---+-------+
| flags | data |
+-------+-------+---...---+-------+
byte 0 1 2 3 plen-1
+-------+-------+-------+-------+---...---+-------+
| flags | urgent | data |
+-------+-------+-------+-------+---...---+-------+
Quick heads-up ๐clenis usually bigger thanplen. That's because the AEAD output both encrypts the application data and adds redundancy so the integrity can be checked. It's giving "extra armor," iykyk โจ
The flags field is structured like this:
bit 7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+
| fres |URGp|FINp|
+----+----+----+----+----+----+----+----+
The six-bit fres field is reserved too ๐ธ Implementations MUST set those six bits to zero when sending, and MUST ignore them when receiving. Same vibe as cres, periodt ๐
If the URGp bit is set, that tells you the urgent field is there, so the plaintext uses the second (longer) format. If it isn't set, you get the first format ๐ What the urgent field and the flag bits actually mean is covered in Section 3.7 ๐ง
4.2.2. Associated Data
Every encryption frame has its own associated data ๐ฆ This is the stuff handed to the AEAD algorithm when it decrypts the ciphertext and checks the frame's integrity. It looks like this:
byte 0 1 2
+-------+-------+-------+
|control| clen |
+-------+-------+-------+
Fun fact (well, it's a fact, so it's fun) ๐ It contains the very same values as the frame's control and clen fields. Copy-paste energy, but make it cryptographic ๐ญ
4.2.3. Frame ID
Last but not least, the frame ID is what gets used to build the nonce for the AEAD algorithm ๐
byte 0 ae_nonce_len - 8 ae_nonce_len - 1
| | |
v v v
+-----+--...--+-----+-----+--...--+-----+
| 0 | | 0 | offset |
+-----+--...--+-----+-----+--...--+-----+
The 8-byte offset field is a big-endian integer, and its value is specified in Section 3.6 ๐ To make the whole thing come out to ae_nonce_len bytes, you prepend zero-valued bytes in front of the offset field. Math is the mood here ๐
4.3. Constant Values
Here are the constants the protocol uses, all lined up in one neat table ๐โจ
+------------+--------------+
| Value | Name |
+------------+--------------+
| 0x01 | CONST_NEXTK |
| 0x02 | CONST_SESSID |
| 0x03 | CONST_REKEY |
| 0x04 | CONST_KEY_A |
| 0x05 | CONST_KEY_B |
| 0x06 | CONST_RESUME |
| 0x15101a0e | INIT1_MAGIC |
| 0x097105e0 | INIT2_MAGIC |
+------------+--------------+
Table 1: Constant Values Used in the Protocol ๐
5. Key-Agreement Schemes ๐
okay so the TEP you negotiate through TCP-ENO tells you which key-agreement scheme to use from Table 4 in Section 7 ๐ for example, TCPCRYPT_ECDHE_P256 means tcpcrypt running ECDHE-P256, with the CPRF and length parameters below. iconic naming, honestly ๐
All the TEPs in this document use HKDF-Expand-SHA256 as the CPRF, and they all use these lengths for nonces and session secrets ๐ฆ
N_A_LEN: 32 bytes
N_B_LEN: 32 bytes
K_LEN: 32 bytes
Future documents that assign more TEPs for tcpcrypt might pick different values for those lengths ๐ but here's the key bit: the minimum session ID length that TCP-ENO sets, combined with how tcpcrypt builds session IDs, means K_LEN MUST be at least 32 bytes. no skipping that one ๐ฅ
Key-agreement schemes ECDHE-P256 and ECDHE-P521 use the Elliptic Curve Secret Value Derivation Primitive, Diffie-Hellman version (ECSVDP-DH) from [IEEE-1363] ๐ธ the named curves come from [NIST-DSS]. When the public-key values Pub_A and Pub_B get sent as described in Section 4.1, they're encoded with the "Elliptic Curve Point to Octet String Conversion Primitive" from Section E.2.3 of [IEEE-1363] and get a two-byte length prefix in big-endian format ๐ง
byte 0 1 2 L - 1
+-------+-------+-------+---...---+-------+
| pubkey_len | pubkey |
| = L | |
+-------+-------+-------+---...---+-------+
Implementations MUST encode these pubkey values in "compressed format" โจ and implementations MUST validate these pubkey values using the algorithm in Section A.16.10 of [IEEE-1363]. non-negotiable, bestie ๐ซถ
Key-agreement schemes ECDHE-Curve25519 and ECDHE-Curve448 do Diffie-Hellman with the functions X25519 and X448, respectively ๐ implementations SHOULD compute these functions with the algorithms in [RFC7748]. When they do, implementations MUST check whether the computed Diffie-Hellman shared secret is the all-zero value, and abort if it is, as described in Section 6 of [RFC7748] ๐ alternative implementations of these functions SHOULD abort when either input forces the shared secret into one of a small set of values, as discussed in Section 7 of [RFC7748].
For these schemes, Pub_A and Pub_B are sent directly with no length prefix: 32 bytes for ECDHE-Curve25519 and 56 bytes for ECDHE-Curve448 ๐
Table 2 below lays out the requirement levels for the four TEPs in this document ๐ the big one: all implementations of tcpcrypt MUST support TCPCRYPT_ECDHE_Curve25519 ๐ system administrators MAY configure which TEPs a host negotiates, separate from these implementation requirements ๐
+-------------+---------------------------+
| Requirement | TEP |
+-------------+---------------------------+
| REQUIRED | TCPCRYPT_ECDHE_Curve25519 |
| RECOMMENDED | TCPCRYPT_ECDHE_Curve448 |
| OPTIONAL | TCPCRYPT_ECDHE_P256 |
| OPTIONAL | TCPCRYPT_ECDHE_P521 |
+-------------+---------------------------+
Table 2: Requirements for Implementation of TEPs
6. AEAD Algorithms ๐ง
This document uses sym_cipher identifiers in the Init1 and Init2 messages (see Section 3.3) to negotiate AEAD algorithms ๐ the values for those identifiers are in Table 5 in Section 7. AEAD_AES_128_GCM and AEAD_AES_256_GCM are specified in [RFC5116], and AEAD_CHACHA20_POLY1305 is specified in [RFC8439] ๐ฆ
Implementations MUST support certain AEAD algorithms, per Table 3 ๐ฅ system administrators MAY configure which algorithms a host negotiates, independent of these requirements ๐
Last thing: this document uses the lengths ae_key_len and ae_nonce_len to describe aspects of encryption and data formats ๐ซง those values depend on the negotiated AEAD algorithm, so check the table below ๐ธ
+------------------------+-------------+------------+--------------+
| AEAD Algorithm | Requirement | ae_key_len | ae_nonce_len |
+------------------------+-------------+------------+--------------+
| AEAD_AES_128_GCM | REQUIRED | 16 bytes | 12 bytes |
| AEAD_AES_256_GCM | RECOMMENDED | 32 bytes | 12 bytes |
| AEAD_CHACHA20_POLY1305 | RECOMMENDED | 32 bytes | 12 bytes |
+------------------------+-------------+------------+--------------+
Table 3: Requirement and Lengths for Each AEAD Algorithm
7. IANA Considerations ๐๏ธ
For TCP-ENO's negotiation mechanism, tcpcrypt's TEP identifiers have been added to IANA's "TCP Encryption Protocol Identifiers" registry, which lives under the "Transmission Control Protocol (TCP) Parameters" registry ๐ see Table 4. The key-agreement schemes these tcpcrypt variants use are defined in Section 5 โจ
+-------+---------------------------+-----------+
| Value | Meaning | Reference |
+-------+---------------------------+-----------+
| 0x21 | TCPCRYPT_ECDHE_P256 | [RFC8548] |
| 0x22 | TCPCRYPT_ECDHE_P521 | [RFC8548] |
| 0x23 | TCPCRYPT_ECDHE_Curve25519 | [RFC8548] |
| 0x24 | TCPCRYPT_ECDHE_Curve448 | [RFC8548] |
+-------+---------------------------+-----------+
Table 4: TEP Identifiers for Use with tcpcrypt
In Section 6, this document defines several AEAD algorithms for encrypting application data ๐ to name them, tcpcrypt uses two-byte identifiers in the range 0x0001 to 0xFFFF, inclusive. IANA maintains a new "tcpcrypt AEAD Algorithms" registry for these, under the "Transmission Control Protocol (TCP) Parameters" registry ๐ the initial values are in Table 5. Future assignments have to satisfy one of two policies from [RFC8126]: "IETF Review" or, for non-IETF stream specifications, "Expert Review with RFC Required." IANA will also do early allocation [RFC7120] so testing can happen before RFCs are finalized ๐ง
+--------+------------------------+----------------------+
| Value | AEAD Algorithm | Reference |
+--------+------------------------+----------------------+
| 0x0001 | AEAD_AES_128_GCM | [RFC8548], Section 6 |
| 0x0002 | AEAD_AES_256_GCM | [RFC8548], Section 6 |
| 0x0010 | AEAD_CHACHA20_POLY1305 | [RFC8548], Section 6 |
+--------+------------------------+----------------------+
Table 5: Authenticated-Encryption Algorithms for Use with tcpcrypt
8. Security Considerations ๐จ
Okay this section is SERIOUS bestie ๐ญ every security consideration of TCP-ENO applies to tcpcrypt too. Most importantly, tcpcrypt does NOT protect against active network attackers unless applications authenticate the session ID ๐ if the session IDs computed at each end match, tcpcrypt guarantees there were no man-in-the-middle attacks, UNLESS the attacker broke the underlying crypto primitives, like Elliptic Curve Diffie-Hellman (ECDH). A proof of this property for an earlier version of the protocol has been published [tcpcrypt] ๐
To keep middleboxes happy, tcpcrypt does not protect TCP headers ๐ that means it's open to denial-of-service from off-path attackers, just like plain TCP. Possible attacks include desynchronizing the underlying TCP stream, injecting RST or FIN segments, and forging rekey bits. These attacks make a tcpcrypt connection hang or fail with an error, but never in a situation where plain TCP could have kept going uncorrupted ๐ค implementations MUST give higher-level software a way to tell these errors apart from a clean end-of-stream (indicated by an authenticated FINp bit), so applications can avoid semantic truncation attacks. this part is a must-have ๐ซถ
There is no "key confirmation" step in tcpcrypt. This is not needed because tcpcrypt's threat model already includes the possibility of a connection to an adversary. If key negotiation is compromised and yields two different keys, failed integrity checks on every subsequent frame will make the connection either hang or abort. This is not a new threat, since an active attacker can get the same results against a plain TCP connection by injecting RST segments or modifying sequence and acknowledgement numbers.
tcpcrypt uses short-lived public keys for forward secrecy ๐ง once an implementation removes these keys from memory, a compromise of the system can't be used to derive the session secrets for past connections. All currently-specified key-agreement schemes use Ephemeral Elliptic Curve Diffie-Hellman (ECDHE), which means a new key pair can be computed efficiently for each connection โจ if implementations reuse these parameters, they MUST limit the lifetime of the private parameters as far as is practical, so fewer past connections are vulnerable ๐ฅ placing private keys in persistent storage is a huge risk, since they might not get destroyed reliably and in a timely way, so that SHOULD be avoided whenever possible ๐
Attackers can't force passive openers to move forward in their session resumption chain without guessing the content of the resumption identifier, which is hard without key knowledge ๐
The cipher-suites in this document all use HMAC-SHA256 to implement the collision-resistant pseudo-random function called CPRF ๐ a collision-resistant function is one where, for a sufficiently large L, an attacker can't find two distinct inputs (K_1, CONST_1) and (K_2, CONST_2) such that CPRF(K_1, CONST_1, L) = CPRF(K_2, CONST_2, L). Collision resistance matters for keeping session IDs unique, since those are generated with the CPRF ๐
Lastly, a lot of tcpcrypt's crypto functions need random input, so any host implementing tcpcrypt MUST have access to a cryptographically-secure source of randomness or pseudo-randomness ๐ฒ [RFC4086] has recommendations on how to get that ๐
Most implementations will rely on a device's pseudo-random generator, seeded from hardware events and a seed carried over from the previous boot ๐ฅ๏ธ once a pseudo-random generator is properly seeded, it can generate effectively arbitrary amounts of pseudo-random data โจ but until it's seeded with sufficient entropy, tcpcrypt isn't just insecure, it will also leak information that further weakens the pseudo-random generator, potentially harming other applications ๐ฑ As REQUIRED by TCP-ENO, implementations MUST NOT send ENO options unless they have access to an adequate source of randomness ๐ซ๐ฒ
8.1. Asymmetric Roles ๐
okay so tcpcrypt takes one shared pseudo-random key (PRK) and turns it into cryptographic traffic keys, one for each direction ๐. To pull that off, the protocol needs an asymmetry, because the key derivation function has to be perturbed differently to make a different key for each direction. ๐ง The two hosts also aren't identical in other ways. For example, one side proposes cipher suites while the other side selects one, so the roles are lowkey different ๐
8.2. Verified Liveness ๐ฆ
so a lot of hosts support TCP keep-alives [RFC1122] as an option, so applications can check that the other end of a TCP connection is still around even when nobody's sending data ๐ A TCP keep-alive segment carries a sequence number one prior to the beginning of the send window and may carry one byte of "garbage" data. That segment makes the remote side send back an acknowledgment, and that's the whole trick.
BUT here's the tea ๐ญ tcpcrypt cannot cryptographically verify keep-alive acknowledgments. That means an attacker could keep a session alive on one host long after the other end is gone. ๐ That might stop a process holding sensitive data from exiting, which gives an attacker more time to compromise the host and grab that data. Not okay!
To shut that down, tcpcrypt has a way to poke the remote host into sending data that is verifiably fresh and authentic, which is described in Section 3.9. โจ
Also, the TCP keep-alive mechanism has been used for its effects on middleboxes, like keeping flow state from expiring at NAT boxes or firewalls ๐ง Those jobs don't need endpoint authentication, so implementations MAY safely use either the existing TCP keep-alive mechanism or tcpcrypt's verified keep-alive mechanism for them. Pick your fighter ๐ธ
8.3. Mandatory Key-Agreement Schemes ๐
this document says tcpcrypt implementations have to support at least one key-agreement scheme: ECDHE using Curve25519 ๐ Picking just one mandatory algorithm was a seriously hard tradeoff between cryptographic diversity and how easy and secure it is to actually deploy.
The IETF's current best practice on this [RFC7696] says, "Ideally, two independent sets of mandatory-to-implement algorithms will be specified, allowing for a primary suite and a secondary suite. This approach ensures that the secondary suite is widely deployed if a flaw is found in the primary one."
So you'd think we'd also mandate ECDHE using P-256 to hit that ideal, right? Not so fast ๐ โโ๏ธ Implementing the Diffie-Hellman function with NIST elliptic curves (including P-256 and P-521, the ones specified for tcpcrypt) seems really hard to do without opening up side-channel attacks [NIST-fail]. Good trusted implementations do exist inside big crypto libraries, but those are a pain to pull into operating-system kernels, which is usually where tcpcrypt gets implemented. Curve25519 is the opposite vibe: its characteristics plus its recent popularity have produced lots of safe, efficient implementations, some of which fit right into the kernel ๐ซถ
[RFC7696] also insists that, "The selected algorithms need to be resistant to side-channel attacks and also meet the performance, power, and code size requirements on a wide variety of platforms." Following that principle, tcpcrypt leaves the NIST curves out of the mandatory-to-implement key-agreement set. ๐ โโ๏ธ
Lastly, this document encourages key agreement with Curve448, and it's categorized as RECOMMENDED ๐ Curve448 looks likely to allow safe and efficient implementations. Support isn't REQUIRED though, because existing implementations might not be well proven yet.
9. Experiments ๐งช
some experience is going to be needed to figure out whether the tcpcrypt protocol can be deployed safely and successfully across all the wildly different corners of the global internet ๐
Safety means TCP implementations that support tcpcrypt can still talk reliably in every setting where they'd work without tcpcrypt. As described in Section 9 of [RFC8547], this can get broken if middleboxes strip ENO options from non-SYN segments after letting them through in SYN segments, or if tcpcrypt's communication patterns annoy the policies of middleboxes doing deep-packet inspection. ๐
Success, on top of safety, means hosts that implement tcpcrypt actually turn on encryption when they connect to each other โจ That depends on how the network treats the TCP-ENO handshake, and it can get sabotaged if middleboxes just strip unknown TCP options, or if they terminate TCP connections and relay data back and forth unencrypted. Yikes ๐ญ
Ease of implementation is another challenge. Since tcpcrypt needs encryption operations on frames that may span TCP segments, kernel implementations have to buffer segments in different ways than plain TCP does. ๐งฉ More implementation experience will reveal how much extra code complexity shows up across operating systems, and what kind of performance effects to expect.
10. References ๐
10.1. Normative References
[IEEE-1363]
IEEE, "IEEE Standard Specifications for Public-Key
Cryptography", IEEE Standard 1363-2000,
DOI 10.1109/IEEESTD.2000.92292.
[NIST-DSS] National Institute of Standards and Technology (NIST),
"Digital Signature Standard (DSS)", FIPS PUB 186-4,
DOI 10.6028/NIST.FIPS.186-4, July 2013.
[RFC793] Postel, J., "Transmission Control Protocol", STD 7,
RFC 793, DOI 10.17487/RFC0793, September 1981,
<https://www.rfc-editor.org/info/rfc793>.
[RFC2018] Mathis, M., Mahdavi, J., Floyd, S., and A. Romanow, "TCP
Selective Acknowledgment Options", RFC 2018,
DOI 10.17487/RFC2018, October 1996,
<https://www.rfc-editor.org/info/rfc2018>.
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104,
DOI 10.17487/RFC2104, February 1997,
<https://www.rfc-editor.org/info/rfc2104>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC5116] McGrew, D., "An Interface and Algorithms for Authenticated
Encryption", RFC 5116, DOI 10.17487/RFC5116, January 2008,
<https://www.rfc-editor.org/info/rfc5116>.
[RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand
Key Derivation Function (HKDF)", RFC 5869,
DOI 10.17487/RFC5869, May 2010,
<https://www.rfc-editor.org/info/rfc5869>.
[RFC7120] Cotton, M., "Early IANA Allocation of Standards Track Code
Points", BCP 100, RFC 7120, DOI 10.17487/RFC7120, January
2014, <https://www.rfc-editor.org/info/rfc7120>.
[RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves
for Security", RFC 7748, DOI 10.17487/RFC7748, January
2016, <https://www.rfc-editor.org/info/rfc7748>.
[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for
Writing an IANA Considerations Section in RFCs", BCP 26,
RFC 8126, DOI 10.17487/RFC8126, June 2017,
<https://www.rfc-editor.org/info/rfc8126>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8439] Nir, Y. and A. Langley, "ChaCha20 and Poly1305 for IETF
Protocols", RFC 8439, DOI 10.17487/RFC8439, June 2018,
<https://www.rfc-editor.org/info/rfc8439>.
[RFC8547] Bittau, A., Giffin, D., Handley, M., Mazieres, D., and
E. Smith, "TCP-ENO: Encryption Negotiation Option",
RFC 8547, DOI 10.17487/RFC8547, May 2019,
<https://www.rfc-editor.org/info/rfc8547>.
10.2. Informative References
[NIST-fail]
Bernstein, D. and T. Lange, "Failures in NIST's ECC
Standards", January 2016,
<https://cr.yp.to/newelliptic/nistecc-20160106.pdf>.
[RFC1122] Braden, R., Ed., "Requirements for Internet Hosts -
Communication Layers", STD 3, RFC 1122,
DOI 10.17487/RFC1122, October 1989,
<https://www.rfc-editor.org/info/rfc1122>.
[RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker,
"Randomness Requirements for Security", BCP 106, RFC 4086,
DOI 10.17487/RFC4086, June 2005,
<https://www.rfc-editor.org/info/rfc4086>.
[RFC7696] Housley, R., "Guidelines for Cryptographic Algorithm
Agility and Selecting Mandatory-to-Implement Algorithms",
BCP 201, RFC 7696, DOI 10.17487/RFC7696, November 2015,
<https://www.rfc-editor.org/info/rfc7696>.
[tcpcrypt] Bittau, A., Hamburg, M., Handley, M., Mazieres, D., and
D. Boneh, "The case for ubiquitous transport-level
encryption", USENIX Security Symposium, August 2010.
[TCPINC-API]
Bittau, A., Boneh, D., Giffin, D., Handley, M., Mazieres,
D., and E. Smith, "Interface Extensions for TCP-ENO and
tcpcrypt", Work in Progress, draft-ietf-tcpinc-api-06,
June 2018.
Acknowledgments ๐ซถ
huge thank yous to the TCPINC Working Group and to the other IETF reviewers whose contributions, help, discussions, and feedback made this possible ๐ That includes Marcelo Bagnulo, David Black, Bob Briscoe, Jana Iyengar, Stephen Kent, Tero Kivinen, Mirja Kuhlewind, Yoav Nir, Christoph Paasch, Eric Rescorla, Kyle Rose, and Dale Worley. Iconic squad ๐
This work was funded by gifts from Intel (to Brad Karp) and from Google, plus NSF award CNS-0716806 (A Clean-Slate Infrastructure for Information Flow Control), DARPA CRASH under contract #N66001-10-2-4088, and the Stanford Secure Internet of Things Project. Money well spent, periodt ๐ธโจ
Contributors ๐ธ
Dan Boneh and Michael Hamburg were coauthors of the draft that became this document. Literal co-creators, no notes ๐ง
Authors' Addresses ๐ฆ
Andrea Bittau
Google
345 Spear Street
San Francisco, CA 94105
United States of America
Email: bittau@google.com
Daniel B. Giffin
Stanford University
353 Serra Mall, Room 288
Stanford, CA 94305
United States of America
Email: daniel@beech-grove.net
Mark Handley
University College London
Gower St.
London WC1E 6BT
United Kingdom
Email: M.Handley@cs.ucl.ac.uk
David Mazieres
Stanford University
353 Serra Mall, Room 290
Stanford, CA 94305
United States of America
Email: dm@uun.org
Quinn Slack
Sourcegraph
121 2nd St Ste 200
San Francisco, CA 94105
United States of America
Email: sqs@sourcegraph.com
Eric W. Smith
Kestrel Institute
3260 Hillview Avenue
Palo Alto, CA 94304
United States of America
Email: eric.smith@kestrel.edu
๐ฆ where this page comes from
- ietf: rfc8548 2019-05
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms