Test that post-handshake flows do not implicitly ACK Finished Resolves an old TODO. Bug: 42290594 Change-Id: I23d90f589ef8f2440f6d581efb4c5967428ad710 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/73288 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Nick Harper <nharper@chromium.org>
diff --git a/ssl/d1_both.cc b/ssl/d1_both.cc index b337b37..a40710d 100644 --- a/ssl/d1_both.cc +++ b/ssl/d1_both.cc
@@ -408,9 +408,6 @@ // apply immediately after the handshake. As a client, receiving a // KeyUpdate or NewSessionTicket does not imply the server has received // our Finished. The server may have sent those messages in half-RTT. - // - // TODO(crbug.com/42290594): Once post-handshake messages are working, - // write a test for the half-RTT KeyUpdate case. implicit_ack = true; }
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index fbb8bf9..84fc93e 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -12351,6 +12351,76 @@ expectedError: ":READ_TIMEOUT_EXPIRED:", }) + // Neither post-handshake messages nor application data implicitly + // ACK the Finished flight. The server may have sent either in + // half-RTT data. Test that the client continues to retransmit + // despite this. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Retransmit-Client-NoImplictACKFinished" + suffix, + config: Config{ + MaxVersion: vers.version, + Bugs: ProtocolBugs{ + ACKFlightDTLS: func(c *DTLSController, prev, received []DTLSMessage, records []DTLSRecordNumberInfo) { + // Merge the Finished flight into the NewSessionTicket. + c.MergeIntoNextFlight() + }, + WriteFlightDTLS: func(c *DTLSController, prev, received, next []DTLSMessage, records []DTLSRecordNumberInfo) { + if next[0].Type != typeNewSessionTicket { + c.WriteFlight(next) + return + } + if len(received) == 0 || received[0].Type != typeFinished { + panic("Finished should be merged with NewSessionTicket") + } + // Merge NewSessionTicket into the KeyUpdate. + if next[len(next)-1].Type != typeKeyUpdate { + c.MergeIntoNextFlight() + return + } + + // Write NewSessionTicket and the KeyUpdate and + // read the ACK. + c.WriteFlight(next) + ackTimeout := useTimeouts[0] / 4 + c.AdvanceClock(ackTimeout) + c.ReadACK(c.InEpoch()) + + // The retransmit timer is still running. + c.AdvanceClock(useTimeouts[0] - ackTimeout) + c.ReadRetransmit() + + // Application data can flow at the old epoch. + msg := []byte("test") + c.WriteAppData(c.OutEpoch()-1, msg) + c.ReadAppData(c.InEpoch(), expectedReply(msg)) + + // The retransmit timer is still running. + c.AdvanceClock(useTimeouts[1]) + c.ReadRetransmit() + + // Advance the shim to the next epoch. + c.WriteAppData(c.OutEpoch(), msg) + c.ReadAppData(c.InEpoch(), expectedReply(msg)) + + // The retransmit timer is still running. The shim + // actually could implicitly ACK at this point, but + // RFC 9147 does not list this as an implicit ACK. + c.AdvanceClock(useTimeouts[2]) + c.ReadRetransmit() + + // Finally ACK the final flight. Now the shim will + // stop the timer. + c.WriteACK(c.OutEpoch(), records) + c.ExpectNoNextTimeout() + }, + }, + }, + sendKeyUpdates: 1, + keyUpdateRequest: keyUpdateNotRequested, + flags: flags, + }) + // If the server never receives an ACK for NewSessionTicket, it // is eventually fatal. testCases = append(testCases, testCase{