// 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 "strconv" func addTLS13RecordTests() { for _, protocol := range []protocol{tls, dtls} { testCases = append(testCases, testCase{ protocol: protocol, name: "TLS13-RecordPadding-" + protocol.String(), config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ RecordPadding: 10, }, }, }) testCases = append(testCases, testCase{ protocol: protocol, name: "TLS13-EmptyRecords-" + protocol.String(), config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ OmitRecordContents: true, }, }, shouldFail: true, expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", }) testCases = append(testCases, testCase{ protocol: protocol, name: "TLS13-OnlyPadding-" + protocol.String(), config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ OmitRecordContents: true, RecordPadding: 10, }, }, shouldFail: true, expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", }) if protocol == tls { testCases = append(testCases, testCase{ protocol: protocol, name: "TLS13-WrongOuterRecord-" + protocol.String(), config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ OuterRecordType: recordTypeHandshake, }, }, shouldFail: true, expectedError: ":INVALID_OUTER_RECORD_TYPE:", }) } } } func addTLS13HandshakeTests() { testCases = append(testCases, testCase{ testType: clientTest, name: "NegotiatePSKResumption-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ NegotiatePSKResumption: true, }, }, resumeSession: true, shouldFail: true, expectedError: ":MISSING_KEY_SHARE:", }) testCases = append(testCases, testCase{ testType: clientTest, name: "MissingKeyShare-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ MissingKeyShare: true, }, }, shouldFail: true, expectedError: ":MISSING_KEY_SHARE:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "MissingKeyShare-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ MissingKeyShare: true, }, }, shouldFail: true, expectedError: ":MISSING_KEY_SHARE:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "DuplicateKeyShares-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ DuplicateKeyShares: true, }, }, shouldFail: true, expectedError: ":DUPLICATE_KEY_SHARE:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, }, }, }) // Test that enabling TLS 1.3 does not interfere with TLS 1.2 session ID // resumption. testCases = append(testCases, testCase{ testType: clientTest, name: "ResumeTLS12SessionID-TLS13", config: Config{ MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, flags: []string{"-max-version", strconv.Itoa(VersionTLS13)}, resumeSession: true, }) // Test that the client correctly handles a TLS 1.3 ServerHello which echoes // a TLS 1.2 session ID. testCases = append(testCases, testCase{ testType: clientTest, name: "TLS12SessionID-TLS13", config: Config{ MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, }, resumeSession: true, expectResumeRejected: true, }) // Test that the server correctly echoes back session IDs of // various lengths. The first test additionally asserts that // BoringSSL always sends the ChangeCipherSpec messages for // compatibility mode, rather than negotiating it based on the // ClientHello. testCases = append(testCases, testCase{ testType: serverTest, name: "EmptySessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendClientHelloSessionID: []byte{}, }, }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "Server-ShortSessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendClientHelloSessionID: make([]byte, 16), }, }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "Server-FullSessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendClientHelloSessionID: make([]byte, 32), }, }, }) // The server should reject ClientHellos whose session IDs are too long. testCases = append(testCases, testCase{ testType: serverTest, name: "Server-TooLongSessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendClientHelloSessionID: make([]byte, 33), }, }, shouldFail: true, expectedError: ":CLIENTHELLO_PARSE_FAILED:", expectedLocalError: "remote error: error decoding message", }) testCases = append(testCases, testCase{ testType: serverTest, name: "Server-TooLongSessionID-TLS12", config: Config{ MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendClientHelloSessionID: make([]byte, 33), }, }, shouldFail: true, expectedError: ":CLIENTHELLO_PARSE_FAILED:", expectedLocalError: "remote error: error decoding message", }) // Test that the client correctly accepts or rejects short session IDs from // the server. Our tests use 32 bytes by default, so the boundary condition // is already covered. testCases = append(testCases, testCase{ name: "Client-ShortSessionID", config: Config{ MaxVersion: VersionTLS12, SessionTicketsDisabled: true, Bugs: ProtocolBugs{ NewSessionIDLength: 1, }, }, resumeSession: true, }) testCases = append(testCases, testCase{ name: "Client-TooLongSessionID", config: Config{ MaxVersion: VersionTLS12, SessionTicketsDisabled: true, Bugs: ProtocolBugs{ NewSessionIDLength: 33, }, }, shouldFail: true, expectedError: ":DECODE_ERROR:", expectedLocalError: "remote error: error decoding message", }) // Test that the client sends a fake session ID in TLS 1.3. We cover both // normal and resumption handshakes to capture interactions with the // session resumption path. testCases = append(testCases, testCase{ testType: clientTest, name: "TLS13SessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ExpectClientHelloSessionID: true, }, }, resumeSession: true, }) // Test that the client omits the fake session ID when the max version is TLS 1.2 and below. testCases = append(testCases, testCase{ testType: clientTest, name: "TLS12NoSessionID-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ExpectNoSessionID: true, }, }, flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, }) testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, }, resumeSession: true, earlyData: true, flags: []string{ "-on-initial-expect-early-data-reason", "no_session_offered", "-on-resume-expect-early-data-reason", "accept", }, }) testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Reject-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ AlwaysRejectEarlyData: true, }, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-retry-expect-early-data-reason", "peer_declined", }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, }, messageCount: 2, resumeSession: true, earlyData: true, flags: []string{ "-on-initial-expect-early-data-reason", "no_session_offered", "-on-resume-expect-early-data-reason", "accept", }, }) // The above tests the most recent ticket. Additionally test that 0-RTT // works on the first ticket issued by the server. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-FirstTicket-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ UseFirstSessionTicket: true, }, }, messageCount: 2, resumeSession: true, earlyData: true, flags: []string{ "-on-resume-expect-early-data-reason", "accept", }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-OmitEarlyDataExtension-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, OmitEarlyDataExtension: true, }, }, shouldFail: true, expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-OmitEarlyDataExtension-HelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, // Require a HelloRetryRequest for every curve. DefaultCurves: []CurveID{}, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, OmitEarlyDataExtension: true, }, }, shouldFail: true, expectedError: ":UNEXPECTED_RECORD:", expectedLocalError: "remote error: unexpected message", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-TooMuchData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 16384 + 1, }, }, shouldFail: true, expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-Interleaved-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, InterleaveEarlyData: true, }, }, shouldFail: true, expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-EarlyDataInTLS12-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, }, }, shouldFail: true, expectedError: ":UNEXPECTED_RECORD:", flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-HRR-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, }, DefaultCurves: []CurveID{}, }, // Though the session is not resumed and we send HelloRetryRequest, // early data being disabled takes priority as the reject reason. flags: []string{"-expect-early-data-reason", "disabled"}, }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-HRR-Interleaved-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 4, InterleaveEarlyData: true, }, DefaultCurves: []CurveID{}, }, shouldFail: true, expectedError: ":UNEXPECTED_RECORD:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-HRR-TooMuchData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendFakeEarlyDataLength: 16384 + 1, }, DefaultCurves: []CurveID{}, }, shouldFail: true, expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:", }) // Test that skipping early data looking for cleartext correctly // processes an alert record. testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-HRR-FatalAlert-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendEarlyAlert: true, SendFakeEarlyDataLength: 4, }, DefaultCurves: []CurveID{}, }, shouldFail: true, expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipEarlyData-SecondClientHelloEarlyData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendEarlyDataOnSecondClientHello: true, }, DefaultCurves: []CurveID{}, }, shouldFail: true, expectedLocalError: "remote error: bad record MAC", }) testCases = append(testCases, testCase{ testType: clientTest, name: "EmptyEncryptedExtensions-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ EmptyEncryptedExtensions: true, }, }, shouldFail: true, expectedLocalError: "remote error: error decoding message", }) testCases = append(testCases, testCase{ testType: clientTest, name: "EncryptedExtensionsWithKeyShare-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ EncryptedExtensionsWithKeyShare: true, }, }, shouldFail: true, expectedLocalError: "remote error: unsupported extension", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SendHelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, // Require a HelloRetryRequest for every curve. DefaultCurves: []CurveID{}, CurvePreferences: []CurveID{CurveX25519}, }, expectations: connectionExpectations{ curveID: CurveX25519, }, flags: []string{"-expect-hrr"}, }) testCases = append(testCases, testCase{ testType: serverTest, name: "SendHelloRetryRequest-2-TLS13", config: Config{ MaxVersion: VersionTLS13, DefaultCurves: []CurveID{CurveP384}, CurvePreferences: []CurveID{CurveX25519, CurveP384}, }, // Although the ClientHello did not predict our preferred curve, // we always select it whether it is predicted or not. expectations: connectionExpectations{ curveID: CurveX25519, }, flags: []string{"-expect-hrr"}, }) testCases = append(testCases, testCase{ name: "UnknownCurve-HelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SendHelloRetryRequestCurve: bogusCurve, }, }, shouldFail: true, expectedError: ":WRONG_CURVE:", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-CipherChange-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SendCipherSuite: TLS_AES_128_GCM_SHA256, SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256, }, }, shouldFail: true, expectedError: ":WRONG_CIPHER_RETURNED:", }) // Test that the client does not offer a PSK in the second ClientHello if the // HelloRetryRequest is incompatible with it. testCases = append(testCases, testCase{ testType: clientTest, name: "HelloRetryRequest-NonResumableCipher-TLS13", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{ TLS_AES_128_GCM_SHA256, }, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ ExpectNoTLS13PSKAfterHRR: true, }, CipherSuites: []uint16{ TLS_AES_256_GCM_SHA384, }, }, resumeSession: true, expectResumeRejected: true, }) testCases = append(testCases, testCase{ name: "DisabledCurve-HelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, CurvePreferences: []CurveID{CurveP256}, Bugs: ProtocolBugs{ IgnorePeerCurvePreferences: true, }, }, flags: []string{"-curves", strconv.Itoa(int(CurveP384))}, shouldFail: true, expectedError: ":WRONG_CURVE:", }) testCases = append(testCases, testCase{ name: "UnnecessaryHelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, CurvePreferences: []CurveID{CurveX25519}, Bugs: ProtocolBugs{ SendHelloRetryRequestCurve: CurveX25519, }, }, shouldFail: true, expectedError: ":WRONG_CURVE:", }) testCases = append(testCases, testCase{ name: "SecondHelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SecondHelloRetryRequest: true, }, }, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-Empty-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ AlwaysSendHelloRetryRequest: true, }, }, shouldFail: true, expectedError: ":EMPTY_HELLO_RETRY_REQUEST:", expectedLocalError: "remote error: illegal parameter", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-DuplicateCurve-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires a HelloRetryRequest against BoringSSL's default // configuration. Assert this ExpectMissingKeyShare. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ ExpectMissingKeyShare: true, DuplicateHelloRetryRequestExtensions: true, }, }, shouldFail: true, expectedError: ":DUPLICATE_EXTENSION:", expectedLocalError: "remote error: illegal parameter", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-Cookie-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte("cookie"), }, }, }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-DuplicateCookie-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte("cookie"), DuplicateHelloRetryRequestExtensions: true, }, }, shouldFail: true, expectedError: ":DUPLICATE_EXTENSION:", expectedLocalError: "remote error: illegal parameter", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-EmptyCookie-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte{}, }, }, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-Cookie-Curve-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte("cookie"), ExpectMissingKeyShare: true, }, }, }) testCases = append(testCases, testCase{ name: "HelloRetryRequest-Unknown-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ CustomHelloRetryRequestExtension: "extension", }, }, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION:", expectedLocalError: "remote error: unsupported extension", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SecondClientHelloMissingKeyShare-TLS13", config: Config{ MaxVersion: VersionTLS13, DefaultCurves: []CurveID{}, Bugs: ProtocolBugs{ SecondClientHelloMissingKeyShare: true, }, }, shouldFail: true, expectedError: ":MISSING_KEY_SHARE:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "SecondClientHelloWrongCurve-TLS13", config: Config{ MaxVersion: VersionTLS13, DefaultCurves: []CurveID{}, Bugs: ProtocolBugs{ MisinterpretHelloRetryRequestCurve: CurveP521, }, }, shouldFail: true, expectedError: ":WRONG_CURVE:", }) testCases = append(testCases, testCase{ name: "HelloRetryRequestVersionMismatch-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SendServerHelloVersion: 0x0305, }, }, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ name: "HelloRetryRequestCurveMismatch-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ // Send P-384 (correct) in the HelloRetryRequest. SendHelloRetryRequestCurve: CurveP384, // But send P-256 in the ServerHello. SendCurve: CurveP256, }, }, shouldFail: true, expectedError: ":WRONG_CURVE:", }) // Test the server selecting a curve that requires a HelloRetryRequest // without sending it. testCases = append(testCases, testCase{ name: "SkipHelloRetryRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SkipHelloRetryRequest: true, }, }, shouldFail: true, expectedError: ":WRONG_CURVE:", }) testCases = append(testCases, testCase{ name: "SecondServerHelloNoVersion-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ OmitServerSupportedVersionExtension: true, }, }, shouldFail: true, expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:", }) testCases = append(testCases, testCase{ name: "SecondServerHelloWrongVersion-TLS13", config: Config{ MaxVersion: VersionTLS13, // P-384 requires HelloRetryRequest in BoringSSL. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ SendServerSupportedVersionExtension: 0x1234, }, }, shouldFail: true, expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:", }) testCases = append(testCases, testCase{ name: "RequestContextInHandshake-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, ClientAuth: RequireAnyClientCert, Bugs: ProtocolBugs{ SendRequestContext: []byte("request context"), }, }, shimCertificate: &rsaCertificate, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ name: "UnknownExtensionInCertificateRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, ClientAuth: RequireAnyClientCert, Bugs: ProtocolBugs{ SendCustomCertificateRequest: 0x1212, }, }, shimCertificate: &rsaCertificate, }) testCases = append(testCases, testCase{ name: "MissingSignatureAlgorithmsInCertificateRequest-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, ClientAuth: RequireAnyClientCert, Bugs: ProtocolBugs{ OmitCertificateRequestAlgorithms: true, }, }, shimCertificate: &rsaCertificate, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "TrailingKeyShareData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ TrailingKeyShareData: true, }, }, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ name: "AlwaysSelectPSKIdentity-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ AlwaysSelectPSKIdentity: true, }, }, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION:", }) testCases = append(testCases, testCase{ name: "InvalidPSKIdentity-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SelectPSKIdentityOnResume: 1, }, }, resumeSession: true, shouldFail: true, expectedError: ":PSK_IDENTITY_NOT_FOUND:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "ExtraPSKIdentity-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ExtraPSKIdentity: true, SendExtraPSKBinder: true, }, }, resumeSession: true, }) // Test that unknown NewSessionTicket extensions are tolerated. testCases = append(testCases, testCase{ name: "CustomTicketExtension-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ CustomTicketExtension: "1234", }, }, }) // Test the client handles 0-RTT being rejected by a full handshake // and correctly reports a certificate change. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-RejectTicket-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Credential: &rsaCertificate, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Credential: &ecdsaP256Certificate, SessionTicketsDisabled: true, }, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-retry-expect-early-data-reason", "session_not_resumed", // Test the peer certificate is reported correctly in each of the // three logical connections. "-on-initial-expect-peer-cert-file", rsaCertificate.ChainPath, "-on-resume-expect-peer-cert-file", rsaCertificate.ChainPath, "-on-retry-expect-peer-cert-file", ecdsaP256Certificate.ChainPath, // Session tickets are disabled, so the runner will not send a ticket. "-on-retry-expect-no-session", }, }) // Test the server rejects 0-RTT if it does not recognize the ticket. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-RejectTicket-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, Bugs: ProtocolBugs{ // Corrupt the ticket. FilterTicket: func(in []byte) ([]byte, error) { in[len(in)-1] ^= 1 return in, nil }, }, }, messageCount: 2, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-resume-expect-early-data-reason", "session_not_resumed", }, }) // Test the client handles 0-RTT being rejected via a HelloRetryRequest. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-HRR-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4}, }, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-retry-expect-early-data-reason", "hello_retry_request", }, }) // Test the server rejects 0-RTT if it needs to send a HelloRetryRequest. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-HRR-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, // Require a HelloRetryRequest for every curve. DefaultCurves: []CurveID{}, }, messageCount: 2, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-resume-expect-early-data-reason", "hello_retry_request", }, }) // Test the client handles a 0-RTT reject from both ticket rejection and // HelloRetryRequest. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-HRR-RejectTicket-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Credential: &rsaCertificate, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Credential: &ecdsaP256Certificate, SessionTicketsDisabled: true, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4}, }, }, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ // The client sees HelloRetryRequest before the resumption result, // though neither value is inherently preferable. "-on-retry-expect-early-data-reason", "hello_retry_request", // Test the peer certificate is reported correctly in each of the // three logical connections. "-on-initial-expect-peer-cert-file", rsaCertificate.ChainPath, "-on-resume-expect-peer-cert-file", rsaCertificate.ChainPath, "-on-retry-expect-peer-cert-file", ecdsaP256Certificate.ChainPath, // Session tickets are disabled, so the runner will not send a ticket. "-on-retry-expect-no-session", }, }) // Test the server rejects 0-RTT if it needs to send a HelloRetryRequest. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-HRR-RejectTicket-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, MinVersion: VersionTLS13, // Require a HelloRetryRequest for every curve. DefaultCurves: []CurveID{}, Bugs: ProtocolBugs{ // Corrupt the ticket. FilterTicket: func(in []byte) ([]byte, error) { in[len(in)-1] ^= 1 return in, nil }, }, }, messageCount: 2, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ // The server sees the missed resumption before HelloRetryRequest, // though neither value is inherently preferable. "-on-resume-expect-early-data-reason", "session_not_resumed", }, }) // The client must check the server does not send the early_data // extension while rejecting the session. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataWithoutResume-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, MaxEarlyDataSize: 16384, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, SessionTicketsDisabled: true, Bugs: ProtocolBugs{ SendEarlyDataExtension: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION:", }) // The client must fail with a dedicated error code if the server // responds with TLS 1.2 when offering 0-RTT. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataVersionDowngrade-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS12, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":WRONG_VERSION_ON_EARLY_DATA:", }) // Same as above, but the server also sends a warning alert before the // ServerHello. Although the shim predicts TLS 1.3 for 0-RTT, it should // still interpret data before ServerHello in a TLS-1.2-compatible way. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataVersionDowngrade-Client-TLS13-WarningAlert", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendSNIWarningAlert: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":WRONG_VERSION_ON_EARLY_DATA:", }) // Test that the client rejects an (unsolicited) early_data extension if // the server sent an HRR. testCases = append(testCases, testCase{ testType: clientTest, name: "ServerAcceptsEarlyDataOnHRR-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4}, SendEarlyDataExtension: true, }, }, resumeSession: true, earlyData: true, // The client will first process an early data reject from the HRR. expectEarlyDataRejected: true, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION:", }) testCases = append(testCases, testCase{ testType: clientTest, name: "SkipChangeCipherSpec-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SkipChangeCipherSpec: true, }, }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "SkipChangeCipherSpec-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SkipChangeCipherSpec: true, }, }, }) testCases = append(testCases, testCase{ testType: clientTest, name: "TooManyChangeCipherSpec-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendExtraChangeCipherSpec: 33, }, }, shouldFail: true, expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "TooManyChangeCipherSpec-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendExtraChangeCipherSpec: 33, }, }, shouldFail: true, expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", }) testCases = append(testCases, testCase{ name: "SendPostHandshakeChangeCipherSpec-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendPostHandshakeChangeCipherSpec: true, }, }, shouldFail: true, expectedError: ":UNEXPECTED_RECORD:", expectedLocalError: "remote error: unexpected message", }) fooString := "foo" barString := "bar" // Test that the client reports the correct ALPN after a 0-RTT reject // that changed it. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-ALPNMismatch-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ALPNProtocol: &fooString, }, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ALPNProtocol: &barString, }, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-advertise-alpn", "\x03foo\x03bar", // The client does not learn ALPN was the cause. "-on-retry-expect-early-data-reason", "peer_declined", // In the 0-RTT state, we surface the predicted ALPN. After // processing the reject, we surface the real one. "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", "-on-retry-expect-alpn", "bar", }, }) // Test that the client reports the correct ALPN after a 0-RTT reject if // ALPN was omitted from the first connection. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-ALPNOmitted1-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, NextProtos: []string{"foo"}, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-advertise-alpn", "\x03foo\x03bar", // The client does not learn ALPN was the cause. "-on-retry-expect-early-data-reason", "peer_declined", // In the 0-RTT state, we surface the predicted ALPN. After // processing the reject, we surface the real one. "-on-initial-expect-alpn", "", "-on-resume-expect-alpn", "", "-on-retry-expect-alpn", "foo", }, }) // Test that the client reports the correct ALPN after a 0-RTT reject if // ALPN was omitted from the second connection. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-ALPNOmitted2-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, NextProtos: []string{"foo"}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-advertise-alpn", "\x03foo\x03bar", // The client does not learn ALPN was the cause. "-on-retry-expect-early-data-reason", "peer_declined", // In the 0-RTT state, we surface the predicted ALPN. After // processing the reject, we surface the real one. "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", "-on-retry-expect-alpn", "", }, }) // Test that the client enforces ALPN match on 0-RTT accept. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-BadALPNMismatch-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ ALPNProtocol: &fooString, }, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ AlwaysAcceptEarlyData: true, ALPNProtocol: &barString, }, }, resumeSession: true, earlyData: true, flags: []string{ "-advertise-alpn", "\x03foo\x03bar", "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", "-on-retry-expect-alpn", "bar", }, shouldFail: true, expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:", expectedLocalError: "remote error: illegal parameter", }) // Test that the client does not offer early data if it is incompatible // with ALPN preferences. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-ALPNPreferenceChanged-TLS13", config: Config{ MaxVersion: VersionTLS13, MaxEarlyDataSize: 16384, NextProtos: []string{"foo", "bar"}, }, resumeSession: true, flags: []string{ "-enable-early-data", "-expect-ticket-supports-early-data", "-expect-no-offer-early-data", // Offer different ALPN values in the initial and resumption. "-on-initial-advertise-alpn", "\x03foo", "-on-initial-expect-alpn", "foo", "-on-resume-advertise-alpn", "\x03bar", "-on-resume-expect-alpn", "bar", // The ALPN mismatch comes from the client, so it reports it as the // reason. "-on-resume-expect-early-data-reason", "alpn_mismatch", }, }) // Test that the client does not offer 0-RTT to servers which never // advertise it. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-NonZeroRTTSession-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeSession: true, flags: []string{ "-enable-early-data", "-on-resume-expect-no-offer-early-data", // The client declines to offer 0-RTT because of the session. "-on-resume-expect-early-data-reason", "unsupported_for_session", }, }) // Test that the server correctly rejects 0-RTT when the previous // session did not allow early data on resumption. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-NonZeroRTTSession-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendEarlyData: [][]byte{{1, 2, 3, 4}}, ExpectEarlyDataAccepted: false, }, }, resumeSession: true, // This test configures early data manually instead of the earlyData // option, to customize the -enable-early-data flag. flags: []string{ "-on-resume-enable-early-data", "-expect-reject-early-data", // The server rejects 0-RTT because of the session. "-on-resume-expect-early-data-reason", "unsupported_for_session", }, }) // Test that we reject early data where ALPN is omitted from the first // connection, but negotiated in the second. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-ALPNOmitted1-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, NextProtos: []string{}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, NextProtos: []string{"foo"}, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-initial-select-alpn", "", "-on-resume-select-alpn", "foo", "-on-resume-expect-early-data-reason", "alpn_mismatch", }, }) // Test that we reject early data where ALPN is omitted from the second // connection, but negotiated in the first. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-ALPNOmitted2-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, NextProtos: []string{"foo"}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, NextProtos: []string{}, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-initial-select-alpn", "foo", "-on-resume-select-alpn", "", "-on-resume-expect-early-data-reason", "alpn_mismatch", }, }) // Test that we reject early data with mismatched ALPN. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-ALPNMismatch-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, NextProtos: []string{"foo"}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, NextProtos: []string{"bar"}, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-initial-select-alpn", "foo", "-on-resume-select-alpn", "bar", "-on-resume-expect-early-data-reason", "alpn_mismatch", }, }) // Test that the client offering 0-RTT and Channel ID forbids the server // from accepting both. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataChannelID-AcceptBoth-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, RequestChannelID: true, }, resumeSession: true, earlyData: true, expectations: connectionExpectations{ channelID: true, }, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:", expectedLocalError: "remote error: illegal parameter", flags: []string{ "-send-channel-id", channelIDKeyPath, }, }) // Test that the client offering Channel ID and 0-RTT allows the server // to decline 0-RTT. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataChannelID-AcceptChannelID-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, RequestChannelID: true, Bugs: ProtocolBugs{ AlwaysRejectEarlyData: true, }, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, expectations: connectionExpectations{ channelID: true, }, flags: []string{ "-send-channel-id", channelIDKeyPath, // The client never learns the reason was Channel ID. "-on-retry-expect-early-data-reason", "peer_declined", }, }) // Test that the client offering Channel ID and 0-RTT allows the server // to decline Channel ID. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataChannelID-AcceptEarlyData-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeSession: true, earlyData: true, flags: []string{ "-send-channel-id", channelIDKeyPath, }, }) // Test that the server supporting Channel ID and 0-RTT declines 0-RTT // if it would negotiate Channel ID. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyDataChannelID-OfferBoth-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, ChannelID: &channelIDKey, }, resumeSession: true, earlyData: true, expectEarlyDataRejected: true, expectations: connectionExpectations{ channelID: true, }, flags: []string{ "-expect-channel-id", base64FlagValue(channelIDBytes), "-on-resume-expect-early-data-reason", "channel_id", }, }) // Test that the server supporting Channel ID and 0-RTT accepts 0-RTT // if not offered Channel ID. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyDataChannelID-OfferEarlyData-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeSession: true, earlyData: true, expectations: connectionExpectations{ channelID: false, }, flags: []string{ "-enable-channel-id", "-on-resume-expect-early-data-reason", "accept", }, }) // Test that the server errors on 0-RTT streams without EndOfEarlyData. // The subsequent records should fail to decrypt. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-SkipEndOfEarlyData-TLS13", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SkipEndOfEarlyData: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedLocalError: "remote error: bad record MAC", expectedError: ":BAD_DECRYPT:", }) // Test that EndOfEarlyData is rejected in QUIC. Since we leave application // data to the QUIC implementation, we never accept any data at all in // the 0-RTT epoch, so the error is that the encryption level is rejected // outright. // // TODO(crbug.com/381113363): Test this for DTLS 1.3 as well. testCases = append(testCases, testCase{ protocol: quic, testType: serverTest, name: "EarlyData-UnexpectedEndOfEarlyData-QUIC", config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendEndOfEarlyDataInQUICAndDTLS: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":WRONG_ENCRYPTION_LEVEL_RECEIVED:", }) // Test that the server errors on 0-RTT streams with a stray handshake // message in them. testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-UnexpectedHandshake-Server-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ SendStrayEarlyHandshake: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) // Test that the client reports TLS 1.3 as the version while sending // early data. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Client-VersionAPI-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeSession: true, earlyData: true, flags: []string{ "-expect-version", strconv.Itoa(VersionTLS13), // EMS and RI are always reported as supported when we report // TLS 1.3. "-expect-extended-master-secret", "-expect-secure-renegotiation", }, }) // Test that client and server both notice handshake errors after data // has started flowing. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Client-BadFinished-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ BadFinished: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":DIGEST_CHECK_FAILED:", expectedLocalError: "remote error: error decrypting message", }) testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyData-Server-BadFinished-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ BadFinished: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":DIGEST_CHECK_FAILED:", expectedLocalError: "remote error: error decrypting message", }) testCases = append(testCases, testCase{ testType: serverTest, name: "Server-NonEmptyEndOfEarlyData-TLS13", config: Config{ MaxVersion: VersionTLS13, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ NonEmptyEndOfEarlyData: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":DECODE_ERROR:", }) testCases = append(testCases, testCase{ testType: serverTest, name: "ServerSkipCertificateVerify-TLS13", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, Credential: &rsaChainCertificate, Bugs: ProtocolBugs{ SkipCertificateVerify: true, }, }, expectations: connectionExpectations{ peerCertificate: &rsaCertificate, }, shimCertificate: &rsaCertificate, flags: []string{ "-require-any-client-certificate", }, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) testCases = append(testCases, testCase{ testType: clientTest, name: "ClientSkipCertificateVerify-TLS13", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, Credential: &rsaChainCertificate, Bugs: ProtocolBugs{ SkipCertificateVerify: true, }, }, expectations: connectionExpectations{ peerCertificate: &rsaCertificate, }, shimCertificate: &rsaCertificate, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) // PSK/resumption handshakes should not accept CertificateRequest or // Certificate messages. testCases = append(testCases, testCase{ testType: clientTest, name: "CertificateInResumption-TLS13", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ AlwaysSendCertificate: true, }, }, resumeSession: true, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) testCases = append(testCases, testCase{ testType: clientTest, name: "CertificateRequestInResumption-TLS13", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, ClientAuth: RequireAnyClientCert, Bugs: ProtocolBugs{ AlwaysSendCertificateRequest: true, }, }, shimCertificate: &rsaCertificate, resumeSession: true, shouldFail: true, expectedError: ":UNEXPECTED_MESSAGE:", expectedLocalError: "remote error: unexpected message", }) // If the client or server has 0-RTT enabled but disabled TLS 1.3, it should // report a reason of protocol_version. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyDataEnabled-Client-MaxTLS12", expectations: connectionExpectations{ version: VersionTLS12, }, flags: []string{ "-enable-early-data", "-max-version", strconv.Itoa(VersionTLS12), "-expect-early-data-reason", "protocol_version", }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyDataEnabled-Server-MaxTLS12", expectations: connectionExpectations{ version: VersionTLS12, }, flags: []string{ "-enable-early-data", "-max-version", strconv.Itoa(VersionTLS12), "-expect-early-data-reason", "protocol_version", }, }) // The server additionally reports protocol_version if it enabled TLS 1.3, // but the peer negotiated TLS 1.2. (The corresponding situation does not // exist on the client because negotiating TLS 1.2 with a 0-RTT ClientHello // is a fatal error.) testCases = append(testCases, testCase{ testType: serverTest, name: "EarlyDataEnabled-Server-NegotiateTLS12", config: Config{ MaxVersion: VersionTLS12, }, expectations: connectionExpectations{ version: VersionTLS12, }, flags: []string{ "-enable-early-data", "-expect-early-data-reason", "protocol_version", }, }) // On 0-RTT reject, the server may end up negotiating a cipher suite with a // different PRF hash. Test that the client handles this correctly. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Reject0RTT-DifferentPRF-Client", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_AES_256_GCM_SHA384}, }, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-initial-expect-cipher", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), // The client initially reports the old cipher suite while sending // early data. After processing the 0-RTT reject, it reports the // true cipher suite. "-on-resume-expect-cipher", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), "-on-retry-expect-cipher", strconv.Itoa(int(TLS_AES_256_GCM_SHA384)), }, }) testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-Reject0RTT-DifferentPRF-HRR-Client", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_AES_256_GCM_SHA384}, // P-384 requires a HelloRetryRequest against BoringSSL's default // configuration. Assert this with ExpectMissingKeyShare. CurvePreferences: []CurveID{CurveP384}, Bugs: ProtocolBugs{ ExpectMissingKeyShare: true, }, }, resumeSession: true, expectResumeRejected: true, earlyData: true, expectEarlyDataRejected: true, flags: []string{ "-on-initial-expect-cipher", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), // The client initially reports the old cipher suite while sending // early data. After processing the 0-RTT reject, it reports the // true cipher suite. "-on-resume-expect-cipher", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), "-on-retry-expect-cipher", strconv.Itoa(int(TLS_AES_256_GCM_SHA384)), }, }) // Test that the client enforces cipher suite match on 0-RTT accept. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-CipherMismatch-Client-TLS13", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, }, resumeConfig: &Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, Bugs: ProtocolBugs{ AlwaysAcceptEarlyData: true, }, }, resumeSession: true, earlyData: true, shouldFail: true, expectedError: ":CIPHER_MISMATCH_ON_EARLY_DATA:", expectedLocalError: "remote error: illegal parameter", }) // Test that the client can write early data when it has received a partial // ServerHello..Finished flight. See https://crbug.com/1208784. Note the // EncryptedExtensions test assumes EncryptedExtensions and Finished are in // separate records, i.e. that PackHandshakeFlight is disabled. testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-WriteAfterServerHello", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ // Write the server response before expecting early data. ExpectEarlyData: [][]byte{}, ExpectLateEarlyData: [][]byte{[]byte(shimInitialWrite)}, }, }, resumeSession: true, earlyData: true, flags: []string{ "-async", "-on-resume-early-write-after-message", strconv.Itoa(int(typeServerHello)), }, }) testCases = append(testCases, testCase{ testType: clientTest, name: "EarlyData-WriteAfterEncryptedExtensions", config: Config{ MinVersion: VersionTLS13, MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ // Write the server response before expecting early data. ExpectEarlyData: [][]byte{}, ExpectLateEarlyData: [][]byte{[]byte(shimInitialWrite)}, }, }, resumeSession: true, earlyData: true, flags: []string{ "-async", "-on-resume-early-write-after-message", strconv.Itoa(int(typeEncryptedExtensions)), }, }) } func addTLS13CipherPreferenceTests() { // Test that client preference is honored if the shim has AES hardware // and ChaCha20-Poly1305 is preferred otherwise. testCases = append(testCases, testCase{ testType: serverTest, name: "TLS13-CipherPreference-Server-ChaCha20-AES", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{ TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256, }, CurvePreferences: []CurveID{CurveX25519}, }, flags: []string{ "-expect-cipher-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), }, }) testCases = append(testCases, testCase{ testType: serverTest, name: "TLS13-CipherPreference-Server-AES-ChaCha20", config: Config{ MaxVersion: VersionTLS13, CipherSuites: []uint16{ TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256, }, CurvePreferences: []CurveID{CurveX25519}, }, flags: []string{ "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), }, }) // Test that the client orders ChaCha20-Poly1305 and AES-GCM based on // whether it has AES hardware. testCases = append(testCases, testCase{ name: "TLS13-CipherPreference-Client", config: Config{ MaxVersion: VersionTLS13, // Use the client cipher order. (This is the default but // is listed to be explicit.) PreferServerCipherSuites: false, }, flags: []string{ "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), }, }) }