Draft: draft-ietf-quic-tls

Commentary: This PR is the output of the discard keys design team , and has reached consensus amongst design team members ( modulo wordsmithing ) . It describes methods for discarding QUIC packet protection keys , and adds normative text to ensure that this mechanism is not vulnerable to deadlocks . This PR also formalizes the concept of handshake complete and handshake confirmed . Finally , this PR also moved some text around to avoid repetitions . We believe that this PR prevents the potential deadlocks discussed in issues and . 
 So , there is the byzantine generals problem ( messengers get shot , so you never really know until you do ) , but relying on ACK for handshake complete could lead to long delays if the ACK packet is lost . For crypto there is an aggressive retransmission timer , but I 'm not sure that applies to the handshake complete transition . 
 these ACKs uses the next level of keys , and that 's the property that solves the two - general problem . The signal comes out of band so we do n't need to rely on a given level reaching consensus 
 Right - so the lost ACK is solved because of regular 1 - RTT retransmission logic which kicks in after Handshake Complete but before Handshake Confirmed . 
 Hi your latest editorializing suggestions are backing out what I had changed in response to previous comments from other editors . I 'm going to declare that this PR " good enough to land " from an editorial standpoint , meaning that I 'd rather stop making editorial changes now . Once we confirm WG consensus and this PR lands , the editors will be free to make editorial changes . Regarding the addition of " in the current key phase " at line 1154 , that is not necessary because you can not go back to previous key phases with increasing packet numbers . And you ca n't receive an ACK for a packet for a subsequent key phase because this MUST prevents you from getting there in the first place . 
 : Fair enough , I 'm fine with the PR as it is , and I 'm happy to do more editorializing work later . Thanks for clarifying the point about 1RTT packets , that makes sense . 
 Assume that client and server handshake successfully , all packets containing handshake data are received , but the ACK for the CFIN message is lost . Furthermore , assume that the RTT is and there is negligible RTT variance ( just to simplify my example ) . At this point , the server considers the handshake as completed , and starts its timer to drop the Handshake keys after 3 times the PTO timeout . 3 PTOs equals a duration of . The client does n't consider the handshake as completed , since it did n't receive the ACK for the last Handshake packet it sent . On sending that packet , it already set a the crypto timeout timer to a duration of twice the RTT , i.e. . When the crypto timer fires , the client retransmits the CFIN . Now assume that this packet is lost ( or , leading to the same outcome , the packet is received , but the ACK for it is lost again ) . The client resets the timer ( now with backoff ) to after sending the retransmission . By the time this timer fires , the server will already have dropped the Handshake key . We 're now in the situation where the client will never receive an acknowledgement for the CFIN . Note that a similar situation ( with the roles reversed ) can occur when an ACK for the certificate sent by the server is lost . This scenario gets interesting when the client begins sending 1 - RTT data after sending the CFIN . The server will be able to read the 1 - RTT data , and send acknowledgements for it . There 's nothing stopping client and server from running the application protocol , as they normally would , with one interesting caveat : The client will continue to retransmit the CFIN indefinitely and always declare it lost ( and decrease its congestion window ) . Furthermore , the client will only be able to use ACK - based loss recovery , since it is not allowed to use probe timeouts as long as crypto data is still outstanding . 
 It seems like this is an argument that the time to drop the keys is way too low . FWIW , DTLS uses 2MSL for its counterpart timer . 
 4 minutes is a bit long in my opinion . ( And longer than the time used in DTLS in practice . ) This demonstrates that we need to capture retransmissions with these timers . We do n't set a limit on the PTO timer , do we ? I could n't see one . If there was a limit , then it would be that . Which is probably how the TCP MSL should have been calculated . 
 I think this issue is an indirect justification for implicit ACK . The server may have dropped the handshake key , but it can send and receive 1 - RTT packets . So can the client . If the client sends an 1 - RTT packet after sending the CFIN , it should receive an ACK from the server . At that point , it knows there is no point in retransmitting the CFIN . If neither the 1 - RTT not the CFIN handshake are acked , both can be repeated . If the handshake CFIN was received but the ACK lost , as envisages , the server may not be able to process handshake packets anymore , but it can receive and ACK the 1 - RTT packet . Of course , that falls apart if the handshake and 1 - RTT packets are coalesced . Maybe we need a warning there ... 
 MSL in TCP is determine by the practical observations of how long packets can reside in networks . There are measurement papers describing that . At least 30 seconds has been observed in practice , for example due to routers freezing and then restarting , or other bugs of that nature . Since TCP has by default no crypto , the very old packet could be accepted by chance in a new connection and cause havoc . Hence the use of the MSL timer , especially in the FIN - WAIT state . 
 I am not sure if it is . To me , it seems that the fact that we have an unrecoverable situation for lower epochs and if we need to shutdown the connection in such a situation are different . Ca n't the endpoints simply continue communicating using higher epochs , regardless of if there was something not ACKed when they drop the context of lower epochs ? 
 I think we agree . If the endpoints can continue communicating at epoch=4 , there is no reason to drop a connection because of an ACK or timer issue at epoch=3 . 
 Then , I 'd assume that there is nothing we need to address ? 
 I agree , but it 's better if the server send an ack - eliciting 1 - RTT packet right after the handshake completes , since there 's no uncertainty on the server 's side if the client already has 1 - RTT keys . In the stream-0 design team we once had a proposal fo a HANDSHAKE_DONE frame ( a 1 - byte ack - eliciting frame that is delivered reliably ) . I consider this a much cleaner solution than relying on timers which might or might not be chosen appropriately to work with a particular loss pattern . Dropping handshake keys , and moving forward from the crypto mode in the loss recovery ( so we can use the PTO timer instead of the crypto timer ) , is then really straightforward . 
 I think we should do something , because in this case the CFIN would be in - flight , and that would cause only the handshake timer to be active in the recovery draft , even if the CFIN is no longer ACK - able . Even without HANDSHAKE_DONE , anytime you receive a 1 - RTT ACK , you can assume all Handshake data has been received and no longer needs retransmission , so you can drop the Handshake send keys . That 's a unilateral action that the peer does n't need to be aware of I believe , as long as it 's only the send keys being dropped and not receive . We could advise coalescing a 1 - RTT PING with a Handshake timeout retransmission if 1 - RTT send keys are available and also advise to consider all Handshake data delivered once a 1 - RTT ACK is received and processed ? 
 I do n't think that you can drop the Handshake send keys unilaterally just because you received a 1RTT ACK . You can safely mark all the locally sent handshake packets as acknowledged , and you know that they will never need to be retransmitted . But if you receive a repeated handshake packet from the peer , it means that the peer is not implementing implicit ACK , and also is expecting an ACK on the repeated handshake packet . If you do n't send the ACK , the peer could repeat the handshake packet N times and drop the connection . 
 If we want to drop handshake keys , it has to be coordinated with the peer in a standard way . For example , we could update the spec and say that handshake keys are expected to be dropped after the first 1RTT ack has been received ( client side ) or the first 1RTT - protected packet has been received ( server side ) . That will work because it compels both peers to do implicit acks . But without stating that in the standard , no , you can not do it . 
 Thanks for the correction , I keep forgetting about sending ACKs . 
 FWIW , current TLS draft states : After all CRYPTO frames for a given encryption level have been sent and all expected CRYPTO frames received , and all the corresponding acknowledgments have been received or sent , an endpoint starts a timer . To me this sounds safe and that there is nothing we need to fix . 
 I believe in the case Marten is concerned about " After all CRYPTO frames for a given encryption level have been sent " is not true , because the acknowledgements are not received ? 
 Seems to me that that is the case . 
 Having thought about this some more , I think that the HANDSHAKEDONE proposal might be our best way forward . Knowing when to stop sending ClientFinished / Ack is a variant of the two generals problem , meaning it 's actually impossible to know when to stop sending over a lossy network without out - of - band information . There are two ways to solve this . One is timeouts , similar to what TCP does on graceful connection close . However timeouts are n't great , because they 're by definition always too short or too long . In this case , we can do better by using an explicit event that would trigger key discarding . We even have a suitable stream of out - of - band information : our 1 - RTT protected packets . One could consider having the client treat the receipt of any valid 1 - RTT packet as confirmation that the ClientFinished was received , but that does n't work because the server is allowed to send non - sensitive application data before validating the ClientFinished . Having the server send a retransmittable HANDSHAKEDONE frame as soon as it receives ClientFinished solves this issue . The server discards its handshake keys when it sends HANDSHAKE_DONE and the client discards them when it receives it . I personally find this preferable to a short timeout ( which can cause an infinite send ) and a long timeout ( which costs resources ) . 
 Having thought about this some more myself , I 'm pretty happy with what we have . We do n't throw away keys until they are n't needed ( as points out ) , plus a little extra . So I think that this is a case of " phew , false alarm " . If anything , we should be discussing whether the timer is needed at all . 
 I do n’t think this is the case . In the scenario I described , the server sent and received all CRYPTO frames , it received acknowledgements for all CRYPTO frames that it sent and it furthermore sent acknowledgments for all the packets received . This means it will now drop the keys ( after timer expiration ) . However , the ACK for the last crypto packet the client sent ( the CFIN ) is lost . So the client will retransmit the CFIN , but never get an acknowledgement because the server already dropped the key . Since we do n’t acknowledge acknowledgements , within one encryption level there ’s no way for the server to know if the ACK for the CFIN was lost or was received . 
 I just completed a PR in Picoquic to fix this issue . What it does : 1 ) Client side , implicit ACK of the first Initial Packet if the handshake key is received . After that , apply a special parser for Initial frames ( see point 4 below ) 2 ) Server side , move to a " ready " state when the 1st 1 - RTT packet from client is received ; as part of that move , implicit ACK of Initial and Handshake packets sent by the server . 3 ) Client side , move to the " ready " state when first ACK of 1 - RTT packet is received from server . This triggers implicit ACK of Initial and Handshake packets sent by the client . 4 ) In Ready state , use a specific parser for Initial and Handshake message . Effectively ignore all frames in these packets , but if there is an ACK eliciting frame , schedule an ACK . This removes a bunch of corner cases , such as spurious retransmit of Initial packets in the middle of a migration . It also closes an avenue for DOS against established connections , since Initial packets can not anymore change the state of the connection once the handshake is complete . Well , not quite all attacks : Initial packets can still trigger an ACK that might cause an allergic reaction at the peer . But then , the attacker could also send spoofed ACK in Initial packets directly to the peer , so this is not exactly a new attack . Plus , if the peer adopts the defenses outlined above , the spoofed ACK will have no side effect . 
 This will only work if the application protocol actually makes the client send any data after establishing a new connection . 
 yes , that 's a potential issue . The clients that I see in test seem to always send something in practice , but that 's not guaranteed . Actually , the server can do without the client data if the TLS stack provides an " all done " signal . PicoTLS does that by only installing the 1 - RTT read key after the CFIN has been processed , so that availability can trigger the move to " ready " . The client side could also track ACK of CFIN instead of ACK of DATA , or in addition to ACK of DATA . If either is received , then the client can be considered ready . 
 The issue is that the ACK for the CFIN is lost . I think the cleanest solution here is making the server send a HANDSHAKEDONE frame in 1 - RTT once the handshake is complete . At this point , it can immediately drop the Handshake keys . The HANDSHAKEDONE is retransmitted according to normal 1 - RTT retransmission rules , and as soon as the client receives it , it also drops Handshake keys and switches loss recovery to 1 - RTT mode . 
 Yes , adding an handshake done frame would be a very good idea 
 That 's much more aggressive than what is required now . At the moment , we keep the handshake keys 3 PTO after the client learns that all the handshake data have been received by the server ( see ) . I think it makes sense to use a 3 PTO timer to avoid having negative side - effects to the congestion controller , and I do not understand why we need an explicit signal ( HandshakeDone ) . 
 My vote would be to have an explicit signal instead of a timer based solution . The loss recovery / congestion controller could use the exact same signal to fix any necessary state . I like the Handshake Done solution . 
 See why that does n't work . 
 I disagree , as I have stated in Current design guarantees that both endpoints will have 1 - RTT read and write keys 3 - PTO before the handshake keys are dropped ( I think we can agree on this ) . It seems to me that you consider loss of an ACK for a Handshake packet should lead to the termination of the connection . Would you mind clarifying why ? Because I do not think that 's necessary . 
 Both endpoints have the 1 - RTT keys , but the client does n't know that the server has them , since the ACK for the CFIN was lost . For the client , this situation is indistinguishable from the CFIN itself being lost , so it has to keep retransmitting it . 
 I encountered this issue while testing quinn during interop . client kept retransmitting Handshake but server had already dropped handshake key . The problem is that server starts timer too soon ; client has no way to know that handshake is received by server and it started timer . My preference is to have a explicit signal from server . It is much better to doing something complicated using ACKs . 
 That 's true , however I am not sure if introducing a new frame ( that needs to be delivered reliably ) is the correct solution . At least for my implementation , consulting if an ACK for 1 - RTT packet has been received each time a Handshake packet is deemed lost is easier than reliably transmitting a HANDSHAKEDONE frame and using it 's receipt as a signal . Having that said , if we are going to depart from the current approach , possibly by introducing a HANDSHAKEDONE frame , I think it 's worth reconsidering when we drop the Initial keys . Current text requires endpoints to drop Initial packets as soon as they know that the peer knows the Handshake keys . The downside of this approach is delivery of Initial packets and their RTT is sometimes lost . If we are to have an explicit signal for indicating the end of the handshake , we could delay dropping not only the Handshake keys but also the Initial keys until that signal is being emitted . The design would be : each endpoint emits HANDSHAKEDONE packet when all the CRYPTO data sent using Initial and Handshake packets are acknowledged an endpoint would retain it 's Initial and Handshake keys until it sends HANDSHAKEDONE frame and also receives HANDSHAKE_DONE frame This is simpler than the current design . There 's no fear of key being dropped having negative impact on loss recovery / congestion control . There 's only one event for dropping the keys ( instead of two ) . And the logic can be exactly the same on the server and the client . 
 Perhaps another way to do this , similar to HANDSHAKEDONE , would be to have a KEYRETIRE frame that can be used to throw the Initial and Handshake keys away individually . This might be slightly better than the proposed HANDSHAKE_DONE method proposed above because the Initial key could be thrown away earlier than the end of the handshake . 
 Tokyo conclusion : we will explore the addition of the proposal had in This will allow us to address the multiple key update issue , and make the implicit acknowledgment mess transitioning from Initial to Handshake ( i.e. , ) together with the one solution . Tentatively , the signal we 're looking for is a frame that says " I have emplaced read and write keys for epoch N " . Once a peer has both sent and received this frame , then they can discard keys for earlier epochs ( and indeed SHOULD for Initial ) . We might also use this as a signal to drive the solution to . 
 Imagine the following ( somewhat contrived ) situation : peers are currently both sending with key phase 0 , and simultaneously and independently decide that it 's time for a key update . They both derive the new keys for the next key phase , and start sending packets with key phase 1 . B receives one of the packets with key phase 1 . It ( incorrectly ) concludes that A received one of its key phase 1 packets , and initiates the next key update , and continues sending packets with key phase 0 . packets with key phase 1 sent by B are lost . A now receives the new packets with key phase 0 sent by B. Since it did n't receive any key phase 1 packets , it does n't derive new keys , and tries to decrypt the packets with the old key phase 0 key . All of those packets will end up undecrytable , leading to a stalling of the connection . 's nothing A can do to recover from this situation ( other than trial decryption ) . If it continues to send key phase 1 packets , B will interpret this as yet another key update . One might argue that it 's improbable that all packets with a certain key phase are lost , and certainly the probability decreases with the number of packets sent in one key phase . However , there 's nothing in the spec that prevents a peer from rotating keys as frequently as it is allowed to ( i.e. directly after receiving a single packet with a matching key phase ) . 
 FWIW , I had assumed that we agreed that an endpoint is required to have two generation counters , one for the send key and the other for the receive key , and to update it 's send key either when it wants to , or when the receive key 's generation counter becomes greater than that of the send key . However , I do not see that specified , and I agree that we have an issue . 
 that 's not a fix the problem that is talking about , but if that 's not written down , then that 's also a problem . The problem here is that one endpoint is updating too aggressively , even if it is entirely permissible . If that coincides with an update on the other end , there is the possibility that one side thinks that no update has occurred , when two updates have happened . The fix here is - I think - waiting for acknowledgment of a packet with the key phase . Which means that you do n't get to just use the key phase bit to drive this . The interesting thing here is that when you couple this to acknowledgments you can validate updates . If someone acknowledges an update with old keys , then you know they are misbehaving . 
 If we want to avoid relying on acknowledgements as proposed in , another way to solve this issue would be to extend the key phase to two bits ( and increment the key phase by 1 every time we do a key update ) . This would allow peer A to detect in step 3 that a simultaneous key update happened , and to catch up with updating its read keys . 
 and will confer and produce a proposal . 
 A little bit of editorializing , with one important change . Thank you so much for defining " Handshake Complete " !
Paragraph (version B):
In this document, the TLS handshake is considered complete when the
TLS stack has reported that the handshake is complete.  This happens
when the TLS stack has both sent a Finished message and verified the
peer's Finished message.  Verifying the peer's Finished provides the
endpoints with an assurance that previous handshake messages have not
been modified.  Note that the handshake does not complete at both
endpoints simultaneously.  Consequently, any requirement that is
based on the completion of the handshake depends on the perspective
of the endpoint in question.
Paragraph (version B):
In this document, the TLS handshake is considered confirmed at an
endpoint when the following two conditions are met: the handshake is
complete, and the endpoint has received an acknowledgment for a
packet sent with 1-RTT keys.  This second condition can be
implemented by recording the lowest packet number sent with 1-RTT
keys, and the highest value of the Largest Acknowledged field in any
received 1-RTT ACK frame: once the latter is higher than or equal to
the former, the handshake is confirmed.
Paragraph (version A):
Until the handshake is reported as complete, the connection and
key exchange are not properly authenticated at the server.  Even
though 1-RTT keys are available to a server after receiving the
first handshake messages from a client, the server cannot consider
the client to be authenticated until it receives and validates the
client's Finished message.  A server MUST NOT process 1-RTT
packets until the handshake is complete.  A server MAY buffer or
discard 1-RTT packets that it cannot read.
Paragraph (version A):
The requirement for the server to wait for the client Finished
message creates a dependency on that message being delivered.  A
client can avoid the potential for head-of-line blocking that this
implies by sending a copy of the CRYPTO frame that carries the
Finished message in multiple packets.  This enables immediate
server processing for those packets.
Paragraph (version B):
After QUIC moves to a new encryption level, packet protection keys
for previous encryption levels can be discarded.  This occurs several
times during the handshake, as well as when keys are updated; see
key-update.
Paragraph (version A):
Once this timer expires, an endpoint MUST NOT either accept or
generate new packets using those packet protection keys.  An endpoint
can discard packet protection keys for that encryption level.
Paragraph (version A):
Key updates (see key-update) can be used to update 1-RTT keys before
keys from other encryption levels are discarded.  In that case,
packets protected with the newest packet protection keys and packets
sent two updates prior will appear to use the same keys.  After the
handshake is complete, endpoints only need to maintain the two latest
sets of packet protection keys and MAY discard older keys.  Updating
keys multiple times rapidly can cause packets to be effectively lost
if packets are significantly delayed.  Because key updates can only
be performed once per round trip time, only packets that are delayed
by more than a round trip will be lost as a result of changing keys;
such packets will be marked as lost before this, as they leave a gap
in the sequence of packet numbers.
Paragraph (version B):
An endpoint MUST NOT discard its handshake keys until the TLS
handshake is confirmed (handshake-confirmed).  An endpoint SHOULD
discard its handshake keys as soon as it has confirmed the handshake.
Most application protocols will send data after the handshake,
resulting in acknowledgements that allow both endpoints to discard
their handshake keys promptly.  Endpoints that do not have reason to
send immediately after completing the handshake MAY send ack-
eliciting frames, such as PING, which will cause the handshake to be
confirmed when they are acknowledged.
Paragraph (version B):
0-RTT and 1-RTT packets share the same packet number space, and
clients do not send 0-RTT packets after sending a 1-RTT packet
(using-early-data).
Paragraph (version B):
Therefore, a client SHOULD discard 0-RTT keys as soon as it installs
1-RTT keys, since they have no use after that moment.
Paragraph (version B):
Additionally, a server MAY discard 0-RTT keys as soon as it receives
a 1-RTT packet.  However, due to packet reordering, a 0-RTT packet
could arrive after a 1-RTT packet.  Servers MAY temporarily retain
0-RTT keys to allow decrypting reordered packets without requiring
their contents to be retransmitted with 1-RTT keys.  After receiving
a 1-RTT packet, servers MUST discard 0-RTT keys within a short time;
the RECOMMENDED time period is three times the Probe Timeout (PTO,
see QUIC-RECOVERY).  A server MAY discard 0-RTT keys earlier if it
determines that it has received all 0-RTT packets, which can be done
by keeping track of missing packet numbers.
Paragraph (version B):
Once a client has installed 1-RTT keys, it MUST NOT send any more
0-RTT packets.
Paragraph (version A):
However, a server MUST NOT process data from incoming 1-RTT protected
packets before verifying either the client Finished message or - in
the case that the server has chosen to use a pre-shared key - the
pre-shared key binder (see Section 4.2.11 of TLS13).  Verifying these
values provides the server with an assurance that the ClientHello has
not been modified.  Packets protected with 1-RTT keys MAY be stored
and later decrypted and used once the handshake is complete.
Paragraph (version B):
The client is not authenticated, unless the server has chosen to
use a pre-shared key and validated the client's pre-shared key
binder; see Section 4.2.11 of TLS13.
Paragraph (version B):
Any received 0-RTT data that the server responds to might be due
to a replay attack.
Paragraph (version B):
The requirement for the server to wait for the client Finished
message creates a dependency on that message being delivered.  A
client can avoid the potential for head-of-line blocking that this
implies by sending its 1-RTT packets coalesced with a handshake
packet containing a copy of the CRYPTO frame that carries the
Finished message, until one of the handshake packets is acknowledged.
This enables immediate server processing for those packets.
Paragraph (version A):
Once the 1-RTT keys are established and the short header is in use,
it is possible to update the keys.  The KEY_PHASE bit in the short
header is used to indicate whether key updates have occurred.  The
KEY_PHASE bit is initially set to 0 and then inverted with each key
update.
Paragraph (version A):
An endpoint MUST NOT initiate more than one key update at a time.  A
new key cannot be used until the endpoint has received and
successfully decrypted a packet with a matching KEY_PHASE.
Paragraph (version A):
If the packet can be decrypted and authenticated using the updated
key and IV, then the keys the endpoint uses for packet protection are
also updated.  The next packet sent by the endpoint will then use the
new keys.
Paragraph (version A):
The quic_transport_parameters extension is carried in the ClientHello
and the EncryptedExtensions messages during the handshake.