| // Copyright 2025 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package runner | |
| import ( | |
| "crypto" | |
| "fmt" | |
| "slices" | |
| "time" | |
| ) | |
| func addResumptionVersionTests() { | |
| for _, sessionVers := range tlsVersions { | |
| for _, resumeVers := range tlsVersions { | |
| protocols := []protocol{tls} | |
| if sessionVers.hasDTLS && resumeVers.hasDTLS { | |
| protocols = append(protocols, dtls) | |
| } | |
| if sessionVers.hasQUIC && resumeVers.hasQUIC { | |
| protocols = append(protocols, quic) | |
| } | |
| for _, protocol := range protocols { | |
| suffix := "-" + sessionVers.name + "-" + resumeVers.name | |
| suffix += "-" + protocol.String() | |
| if sessionVers.version == resumeVers.version { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: "Resume-Client" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectNoTLS13PSK: sessionVers.version < VersionTLS13, | |
| }, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| }) | |
| } else if protocol != tls && sessionVers.version >= VersionTLS13 && resumeVers.version < VersionTLS13 { | |
| // In TLS 1.2 and below, the server indicates resumption by echoing | |
| // the client's session ID, which is impossible if the client did | |
| // not send a session ID. If the client offers a TLS 1.3 session, it | |
| // only fills in session ID in TLS (not DTLS or QUIC) for middlebox | |
| // compatibility mode. So, instead, test that the session ID was | |
| // empty and it was indeed impossible to hit this path | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: "Resume-Client-Impossible" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: resumeVers.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectNoSessionID: true, | |
| }, | |
| }, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| } else { | |
| // Test that the client rejects ServerHellos which resume | |
| // sessions at inconsistent versions. | |
| expectedError := ":OLD_SESSION_VERSION_NOT_RETURNED:" | |
| if sessionVers.version < VersionTLS13 && resumeVers.version >= VersionTLS13 { | |
| // The server will "resume" the session by sending pre_shared_key, | |
| // but the shim will not have sent pre_shared_key at all. The shim | |
| // should reject this because the extension was not allowed at all. | |
| expectedError = ":UNEXPECTED_EXTENSION:" | |
| } | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: "Resume-Client-Mismatch" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: resumeVers.version, | |
| Bugs: ProtocolBugs{ | |
| AcceptAnySession: true, | |
| }, | |
| }, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| shouldFail: true, | |
| expectedError: expectedError, | |
| }) | |
| } | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: "Resume-Client-NoResume" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: resumeVers.version, | |
| }, | |
| newSessionsOnResume: true, | |
| expectResumeRejected: true, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: "Resume-Server" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| expectResumeRejected: sessionVers != resumeVers, | |
| resumeConfig: &Config{ | |
| MaxVersion: resumeVers.version, | |
| Bugs: ProtocolBugs{ | |
| SendBothTickets: true, | |
| }, | |
| }, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| }) | |
| // Repeat the test using session IDs, rather than tickets. | |
| if sessionVers.version < VersionTLS13 && resumeVers.version < VersionTLS13 { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: "Resume-Server-NoTickets" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: sessionVers.version, | |
| SessionTicketsDisabled: true, | |
| }, | |
| expectations: connectionExpectations{ | |
| version: sessionVers.version, | |
| }, | |
| expectResumeRejected: sessionVers != resumeVers, | |
| resumeConfig: &Config{ | |
| MaxVersion: resumeVers.version, | |
| SessionTicketsDisabled: true, | |
| }, | |
| resumeExpectations: &connectionExpectations{ | |
| version: resumeVers.version, | |
| }, | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| // Make sure shim ticket mutations are functional. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "ShimTicketRewritable", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| in, err := SetShimTicketVersion(in, VersionTLS12) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| }) | |
| // Resumptions are declined if the version does not match. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-DeclineCrossVersion", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| ExpectNewTicket: true, | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| return SetShimTicketVersion(in, VersionTLS13) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-DeclineCrossVersion-TLS13", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| return SetShimTicketVersion(in, VersionTLS12) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| // Resumptions are declined if the cipher is invalid or disabled. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-DeclineBadCipher", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| ExpectNewTicket: true, | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-DeclineBadCipher-2", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| ExpectNewTicket: true, | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-cipher", "AES128", | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| 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, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, | |
| }, | |
| // The first connection should establish a session with a CBC cipher. | |
| expectations: connectionExpectations{ | |
| cipher: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| // The second connection will offer GCM (preferred) and CBC, and a | |
| // CBC-based session. The shim can either resume with CBC or do a full | |
| // handshake with GCM. (It cannot resume a CBC session with GCM.) GCM | |
| // should take precedence. | |
| expectResumeRejected: true, | |
| resumeExpectations: &connectionExpectations{ | |
| cipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| }, | |
| }) | |
| // 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_AES_256_GCM_SHA384}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_256_GCM_SHA384}, | |
| }, | |
| // The first connection should establish a session with AES. | |
| expectations: connectionExpectations{ | |
| cipher: TLS_AES_256_GCM_SHA384, | |
| }, | |
| // The second connection will offer ChaCha and AES. ChaCha is listed | |
| // first, so the server should, a priori, pick ChaCha. Although the | |
| // second connection also offers an AES-based session, the cipher choice | |
| // should take predecence. | |
| expectResumeRejected: true, | |
| resumeExpectations: &connectionExpectations{ | |
| cipher: TLS_CHACHA20_POLY1305_SHA256, | |
| }}) | |
| // Sessions may not be resumed if they contain another version's cipher. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-DeclineBadCipher-TLS13", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| FilterTicket: func(in []byte) ([]byte, error) { | |
| return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) | |
| }, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-ticket-key", | |
| base64FlagValue(TestShimTicketKey), | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| // 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", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendCipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| }, | |
| }, | |
| 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 and are not doing 0-RTT, but | |
| // BoringSSL will always decline. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-UnofferedCipher-TLS13", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendCipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| }, | |
| }, | |
| expectResumeRejected: true, | |
| }) | |
| // Sessions may not be resumed at a different cipher. | |
| testCases = append(testCases, testCase{ | |
| name: "Resume-Client-CipherMismatch", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendCipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:", | |
| }) | |
| // Session resumption in TLS 1.3 may change the cipher suite if the PRF | |
| // matches. | |
| testCases = append(testCases, testCase{ | |
| name: "Resume-Client-CipherMismatch-TLS13", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, | |
| }, | |
| }) | |
| // Session resumption in TLS 1.3 is forbidden if the PRF does not match. | |
| testCases = append(testCases, testCase{ | |
| name: "Resume-Client-PRFMismatch-TLS13", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendCipherSuite: TLS_AES_256_GCM_SHA384, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":OLD_SESSION_PRF_HASH_MISMATCH:", | |
| }) | |
| for _, secondBinder := range []bool{false, true} { | |
| var suffix string | |
| var defaultCurves []CurveID | |
| if secondBinder { | |
| suffix = "-SecondBinder" | |
| // Force a HelloRetryRequest by predicting an empty curve list. | |
| defaultCurves = []CurveID{} | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-BinderWrongLength" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendShortPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decrypting message", | |
| expectedError: ":DIGEST_CHECK_FAILED:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-NoPSKBinder" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendNoPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decoding message", | |
| expectedError: ":DECODE_ERROR:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-ExtraPSKBinder" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendExtraPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-ExtraIdentityNoBinder" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| ExtraPSKIdentity: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-InvalidPSKBinder" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendInvalidPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decrypting message", | |
| expectedError: ":DIGEST_CHECK_FAILED:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-PSKBinderFirstExtension" + suffix, | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| PSKBinderFirst: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PRE_SHARED_KEY_MUST_BE_LAST:", | |
| }) | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Resume-Server-OmitAllPSKsOnSecondClientHello", | |
| resumeSession: true, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| DefaultCurves: []CurveID{}, | |
| Bugs: ProtocolBugs{ | |
| OmitAllPSKsOnSecondClientHello: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: missing extension", | |
| expectedError: ":MISSING_EXTENSION:", | |
| }) | |
| } | |
| func addSessionTicketTests() { | |
| testCases = append(testCases, testCase{ | |
| // In TLS 1.2 and below, empty NewSessionTicket messages | |
| // mean the server changed its mind on sending a ticket. | |
| name: "SendEmptySessionTicket-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendEmptySessionTicket: true, | |
| }, | |
| }, | |
| flags: []string{"-expect-no-session"}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| // In TLS 1.3, empty NewSessionTicket messages are not | |
| // allowed. | |
| name: "SendEmptySessionTicket-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendEmptySessionTicket: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":DECODE_ERROR:", | |
| expectedLocalError: "remote error: error decoding message", | |
| }) | |
| // Test that the server ignores unknown PSK modes. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-SendUnknownModeSessionTicket-Server", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a}, | |
| }, | |
| }, | |
| resumeSession: true, | |
| expectations: connectionExpectations{ | |
| version: VersionTLS13, | |
| }, | |
| }) | |
| // Test that the server does not send session tickets with no matching key exchange mode. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-ExpectNoSessionTicketOnBadKEMode-Server", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{0x1a}, | |
| ExpectNoNewSessionTicket: true, | |
| }, | |
| }, | |
| }) | |
| // Test that the server does not accept a session with no matching key exchange mode. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-SendBadKEModeSessionTicket-Server", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{0x1a}, | |
| }, | |
| }, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| // Test that the server rejects ClientHellos with pre_shared_key but without | |
| // psk_key_exchange_modes. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-SendNoKEMModesWithPSK-Server", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{}, | |
| }, | |
| }, | |
| resumeSession: true, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: missing extension", | |
| expectedError: ":MISSING_EXTENSION:", | |
| }) | |
| // Test that the client ticket age is sent correctly. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-TestValidTicketAge-Client", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectTicketAge: 10 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| flags: []string{ | |
| "-resumption-delay", "10", | |
| }, | |
| }) | |
| // Test that the client ticket age is enforced. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-TestBadTicketAge-Client", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectTicketAge: 1000 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| shouldFail: true, | |
| expectedLocalError: "tls: invalid ticket age", | |
| }) | |
| // Test that the server's ticket age skew reporting works. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Forward", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 15 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| resumeRenewedSession: true, | |
| flags: []string{ | |
| "-resumption-delay", "10", | |
| "-expect-ticket-age-skew", "5", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Backward", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 5 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| resumeRenewedSession: true, | |
| flags: []string{ | |
| "-resumption-delay", "10", | |
| "-expect-ticket-age-skew", "-5", | |
| }, | |
| }) | |
| // Test that ticket age skew up to 60 seconds in either direction is accepted. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Forward-60-Accept", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 70 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| earlyData: true, | |
| flags: []string{ | |
| "-resumption-delay", "10", | |
| "-expect-ticket-age-skew", "60", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Backward-60-Accept", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 10 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| earlyData: true, | |
| flags: []string{ | |
| "-resumption-delay", "70", | |
| "-expect-ticket-age-skew", "-60", | |
| }, | |
| }) | |
| // Test that ticket age skew beyond 60 seconds in either direction is rejected. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Forward-61-Reject", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 71 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| earlyData: true, | |
| expectEarlyDataRejected: true, | |
| flags: []string{ | |
| "-resumption-delay", "10", | |
| "-expect-ticket-age-skew", "61", | |
| "-on-resume-expect-early-data-reason", "ticket_age_skew", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-TicketAgeSkew-Backward-61-Reject", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketAge: 10 * time.Second, | |
| }, | |
| }, | |
| resumeSession: true, | |
| earlyData: true, | |
| expectEarlyDataRejected: true, | |
| flags: []string{ | |
| "-resumption-delay", "71", | |
| "-expect-ticket-age-skew", "-61", | |
| "-on-resume-expect-early-data-reason", "ticket_age_skew", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-SendTicketEarlyDataSupport", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| MaxEarlyDataSize: 16384, | |
| }, | |
| flags: []string{ | |
| "-enable-early-data", | |
| "-expect-ticket-supports-early-data", | |
| }, | |
| }) | |
| // Test that 0-RTT tickets are still recorded as such when early data is disabled overall. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-SendTicketEarlyDataSupport-Disabled", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| MaxEarlyDataSize: 16384, | |
| }, | |
| flags: []string{ | |
| "-expect-ticket-supports-early-data", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-DuplicateTicketEarlyDataSupport", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| MaxEarlyDataSize: 16384, | |
| Bugs: ProtocolBugs{ | |
| DuplicateTicketEarlyData: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":DUPLICATE_EXTENSION:", | |
| expectedLocalError: "remote error: illegal parameter", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS13-ExpectTicketEarlyDataSupport", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectTicketEarlyData: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-enable-early-data", | |
| }, | |
| }) | |
| // Test that, in TLS 1.3, the server-offered NewSessionTicket lifetime | |
| // is honored. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-HonorServerSessionTicketLifetime", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketLifetime: 20 * time.Second, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-resumption-delay", "19", | |
| }, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "TLS13-HonorServerSessionTicketLifetime-2", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendTicketLifetime: 20 * time.Second, | |
| // The client should not offer the expired session. | |
| ExpectNoTLS13PSK: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-resumption-delay", "21", | |
| }, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| for _, ver := range tlsVersions { | |
| // Prior to TLS 1.3, disabling session tickets enables session IDs. | |
| useStatefulResumption := ver.version < VersionTLS13 | |
| // SSL_OP_NO_TICKET implies the server must not mint any tickets. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: ver.name + "-NoTicket-NoMint", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectNoNewSessionTicket: true, | |
| RequireSessionIDs: useStatefulResumption, | |
| }, | |
| }, | |
| resumeSession: useStatefulResumption, | |
| flags: []string{"-no-ticket"}, | |
| }) | |
| // SSL_OP_NO_TICKET implies the server must not accept any tickets. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: ver.name + "-NoTicket-NoAccept", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| // Set SSL_OP_NO_TICKET on the second connection, after the first | |
| // has established tickets. | |
| flags: []string{"-on-resume-no-ticket"}, | |
| }) | |
| // SSL_OP_NO_TICKET implies the client must not offer ticket-based | |
| // sessions. The client not only should not send the session ticket | |
| // extension, but if the server echos the session ID, the client should | |
| // reject this. | |
| if ver.version < VersionTLS13 { | |
| testCases = append(testCases, testCase{ | |
| name: ver.name + "-NoTicket-NoOffer", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| resumeConfig: &Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectNoTLS12TicketSupport: true, | |
| // Pretend to accept the session, even though the client | |
| // did not offer it. The client should reject this as | |
| // invalid. A buggy client will still fail because it | |
| // expects resumption, but with a different error. | |
| // Ideally, we would test this by actually resuming the | |
| // previous session, even though the client did not | |
| // provide a ticket. | |
| EchoSessionIDInFullHandshake: true, | |
| }, | |
| }, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| // Set SSL_OP_NO_TICKET on the second connection, after the first | |
| // has established tickets. | |
| flags: []string{"-on-resume-no-ticket"}, | |
| shouldFail: true, | |
| expectedError: ":SERVER_ECHOED_INVALID_SESSION_ID:", | |
| expectedLocalError: "remote error: illegal parameter", | |
| }) | |
| } | |
| // Test ticket flags. | |
| if ver.version >= VersionTLS13 { | |
| // The client should parse and ignore unknown ticket flags. 2039 | |
| // is the highest possible flag number (8*255 flags total). | |
| for i, flags := range [][]uint{{1}, {31}, {100}, {2039}, {1, 31, 100, 2039}} { | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("%s-Client-UnknownTicketFlags-%d", ver.name, i), | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| SendTicketFlags: flags, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("%s-Client-KnownAndUnknownTicketFlags-%d", ver.name, i), | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| ResumptionAcrossNames: true, | |
| Bugs: ProtocolBugs{ | |
| SendTicketFlags: flags, | |
| }, | |
| }, | |
| flags: []string{"-expect-resumable-across-names"}, | |
| }) | |
| } | |
| // The client should reject invalid ticket flag extensions. | |
| testCases = append(testCases, testCase{ | |
| name: ver.name + "-Client-NonminimalTicketFlags", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| SendTicketFlags: []uint{1}, | |
| TicketFlagPadding: 1, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":DECODE_ERROR:", | |
| expectedLocalError: "remote error: illegal parameter", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: ver.name + "-Client-EmptyTicketFlags", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| AlwaysSendTicketFlags: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":DECODE_ERROR:", | |
| expectedLocalError: "remote error: error decoding message", | |
| }) | |
| // The client should parse the resumption_across_names flag. | |
| testCases = append(testCases, testCase{ | |
| name: ver.name + "-Client-NoResumptionAcrossNames", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| flags: []string{"-expect-not-resumable-across-names"}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: ver.name + "-Client-ResumptionAcrossNames", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| ResumptionAcrossNames: true, | |
| }, | |
| flags: []string{"-expect-resumable-across-names"}, | |
| }) | |
| // The server should offer resumption_across_names as configured. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: ver.name + "-Server-NoResumptionAcrossNames", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectResumptionAcrossNames: new(false), | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: ver.name + "-Server-ResumptionAcrossNames", | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| Bugs: ProtocolBugs{ | |
| ExpectResumptionAcrossNames: new(true), | |
| }, | |
| }, | |
| flags: []string{"-resumption-across-names-enabled"}, | |
| }) | |
| } | |
| } | |
| } | |
| func addCredentialSessionIDContextTests() { | |
| context1 := []byte("session_id_context_1") | |
| context2 := []byte("session_id_context_2") | |
| context3 := []byte("session_id_context_3") | |
| for _, ver := range tlsVersions { | |
| // A session ID context configured on the credential partitions resumption. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-Match-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-Mismatch-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context2)}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| // The credential's session ID context overrides the SSL_CTX-level default. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-OverrideCTX-Match-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| flags: []string{ | |
| "-on-initial-session-id-context", base64FlagValue(context1), | |
| "-on-resume-session-id-context", base64FlagValue(context2), | |
| }, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context3)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context3)}, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-OverrideCTX-Mismatch-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| flags: []string{ | |
| "-session-id-context", base64FlagValue(context1), | |
| }, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context2)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context3)}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| // When the credential's session ID context is unset, it falls back to the SSL_CTX-level default. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-FallbackToCTX-Match-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| flags: []string{ | |
| "-on-initial-session-id-context", base64FlagValue(context1), | |
| }, | |
| shimCredentials: []*Credential{&rsaCertificate}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-FallbackToCTX-Mismatch-" + ver.name, | |
| config: Config{ | |
| MinVersion: ver.version, | |
| MaxVersion: ver.version, | |
| }, | |
| flags: []string{ | |
| "-on-initial-session-id-context", base64FlagValue(context1), | |
| "-on-resume-session-id-context", base64FlagValue(context2), | |
| }, | |
| shimCredentials: []*Credential{&rsaCertificate}, | |
| resumeShimCredentials: []*Credential{&rsaCertificate}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| } | |
| // Test with stateful TLS 1.2 resumption. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-NoTicket-Match-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| RequireSessionIDs: true, | |
| }, | |
| }, | |
| flags: []string{"-no-ticket"}, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-NoTicket-Mismatch-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| RequireSessionIDs: true, | |
| }, | |
| }, | |
| flags: []string{"-no-ticket"}, | |
| shimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{rsaCertificate.WithSessionIDContext(context2)}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| // Test with PSK credentials. | |
| pskCred := Credential{ | |
| Type: CredentialTypePreSharedKey, | |
| PreSharedKey: slices.Repeat([]byte{'A', 'B', 'C', 'D'}, 8), | |
| PSKIdentity: []byte("psk1"), | |
| PSKContext: []byte("context1"), | |
| PSKHash: crypto.SHA256, | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-PSK-Match-TLS13", | |
| config: Config{ | |
| Credential: &pskCred, | |
| MaxVersion: VersionTLS13, | |
| }, | |
| shimCredentials: []*Credential{pskCred.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{pskCred.WithSessionIDContext(context1)}, | |
| resumeSession: true, | |
| resumeExpectations: &connectionExpectations{}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-PSK-Mismatch-TLS13", | |
| config: Config{ | |
| Credential: &pskCred, | |
| MaxVersion: VersionTLS13, | |
| }, | |
| shimCredentials: []*Credential{pskCred.WithSessionIDContext(context1)}, | |
| resumeShimCredentials: []*Credential{pskCred.WithSessionIDContext(context2)}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| // The selected credential's session ID context should apply. | |
| for _, vers := range tlsVersions { | |
| rsaConfig := Config{ | |
| MaxVersion: vers.version, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, | |
| } | |
| p256Config := Config{ | |
| MaxVersion: vers.version, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-MultipleCredentials-ResumeSame-" + vers.name, | |
| config: rsaConfig, | |
| resumeConfig: new(rsaConfig), | |
| shimCredentials: []*Credential{ | |
| rsaCertificate.WithSessionIDContext(context1), | |
| ecdsaP256Certificate.WithSessionIDContext(context2), | |
| }, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-MultipleCredentials-ResumeDifferent-" + vers.name, | |
| config: rsaConfig, | |
| resumeConfig: new(p256Config), | |
| shimCredentials: []*Credential{ | |
| rsaCertificate.WithSessionIDContext(context1), | |
| ecdsaP256Certificate.WithSessionIDContext(context2), | |
| }, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CredentialSessionIDContext-MultipleCredentials-ResumeDifferent-SamePartition-" + vers.name, | |
| config: rsaConfig, | |
| resumeConfig: new(p256Config), | |
| shimCredentials: []*Credential{ | |
| rsaCertificate.WithSessionIDContext(context1), | |
| ecdsaP256Certificate.WithSessionIDContext(context1), | |
| }, | |
| resumeSession: true, | |
| // In TLS 1.2, the cipher suite changes and resumption is rejected anyway. | |
| // Starting TLS 1.3, this is a useful test. | |
| expectResumeRejected: vers.version <= VersionTLS12, | |
| }) | |
| } | |
| } |