blob: 2b08ec8d880d64f70945c27e831afe25fc53b414 [file] [log] [blame]
// 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)),
},
})
}