| // 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)), |
| }, |
| }) |
| } |