Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index f55bf37..c6b18b1 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5729,6 +5729,54 @@
expectResumeRejected: true,
})
+ // Sessions are not resumed if they do not use the preferred cipher.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-CipherNotPreferred",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ shouldFail: false,
+ expectResumeRejected: true,
+ })
+
+ // TLS 1.3 allows sessions to be resumed at a different cipher if their
+ // PRF hashes match, but BoringSSL will always decline such resumptions.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-CipherNotPreferred-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ FilterTicket: func(in []byte) ([]byte, error) {
+ // If the client (runner) offers ChaCha20-Poly1305 first, the
+ // server (shim) always prefers it. Switch it to AES-GCM.
+ return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ shouldFail: false,
+ expectResumeRejected: true,
+ })
+
+ // Sessions may not be resumed if they contain another version's cipher.
testCases = append(testCases, testCase{
testType: serverTest,
name: "Resume-Server-DeclineBadCipher-TLS13",
@@ -5748,8 +5796,9 @@
expectResumeRejected: true,
})
- // Clients must not advertise a session without also advertising the
- // cipher.
+ // If the client does not offer the cipher from the session, decline to
+ // resume. Clients are forbidden from doing this, but BoringSSL selects
+ // the cipher first, so we only decline.
testCases = append(testCases, testCase{
testType: serverTest,
name: "Resume-Server-UnofferedCipher",
@@ -5765,10 +5814,13 @@
SendCipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
},
},
- shouldFail: true,
- expectedError: ":REQUIRED_CIPHER_MISSING:",
+ expectResumeRejected: true,
})
+ // In TLS 1.3, clients may advertise a cipher list which does not
+ // include the selected cipher. Test that we tolerate this. Servers may
+ // resume at another cipher if the PRF matches, but BoringSSL will
+ // always decline.
testCases = append(testCases, testCase{
testType: serverTest,
name: "Resume-Server-UnofferedCipher-TLS13",
@@ -5784,8 +5836,7 @@
SendCipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
},
},
- shouldFail: true,
- expectedError: ":REQUIRED_CIPHER_MISSING:",
+ expectResumeRejected: true,
})
// Sessions may not be resumed at a different cipher.