|  | // 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 ( | 
|  | "bytes" | 
|  | "fmt" | 
|  | "strconv" | 
|  | ) | 
|  |  | 
|  | type stateMachineTestConfig struct { | 
|  | protocol          protocol | 
|  | async             bool | 
|  | splitHandshake    bool | 
|  | packHandshake     bool | 
|  | implicitHandshake bool | 
|  | } | 
|  |  | 
|  | // Adds tests that try to cover the range of the handshake state machine, under | 
|  | // various conditions. Some of these are redundant with other tests, but they | 
|  | // only cover the synchronous case. | 
|  | func addAllStateMachineCoverageTests() { | 
|  | for _, async := range []bool{false, true} { | 
|  | for _, protocol := range []protocol{tls, dtls, quic} { | 
|  | addStateMachineCoverageTests(stateMachineTestConfig{ | 
|  | protocol: protocol, | 
|  | async:    async, | 
|  | }) | 
|  | // QUIC doesn't work with the implicit handshake API. Additionally, | 
|  | // splitting or packing handshake records is meaningless in QUIC. | 
|  | if protocol != quic { | 
|  | addStateMachineCoverageTests(stateMachineTestConfig{ | 
|  | protocol:          protocol, | 
|  | async:             async, | 
|  | implicitHandshake: true, | 
|  | }) | 
|  | addStateMachineCoverageTests(stateMachineTestConfig{ | 
|  | protocol:       protocol, | 
|  | async:          async, | 
|  | splitHandshake: true, | 
|  | }) | 
|  | addStateMachineCoverageTests(stateMachineTestConfig{ | 
|  | protocol:      protocol, | 
|  | async:         async, | 
|  | packHandshake: true, | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func addStateMachineCoverageTests(config stateMachineTestConfig) { | 
|  | var tests []testCase | 
|  |  | 
|  | // Basic handshake, with resumption. Client and server, | 
|  | // session ID and session ticket. | 
|  | // The following tests have a max version of 1.2, so they are not suitable | 
|  | // for use with QUIC. | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | name: "Basic-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | }, | 
|  | resumeSession: true, | 
|  | // Ensure session tickets are used, not session IDs. | 
|  | noSessionCache: true, | 
|  | flags:          []string{"-expect-no-hrr"}, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "Basic-Client-RenewTicket", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | RenewTicketOnResume: true, | 
|  | }, | 
|  | }, | 
|  | flags:                []string{"-expect-ticket-renewal"}, | 
|  | resumeSession:        true, | 
|  | resumeRenewedSession: true, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "Basic-Client-NoTicket", | 
|  | config: Config{ | 
|  | MaxVersion:             VersionTLS12, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | resumeSession: true, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | RequireSessionTickets: true, | 
|  | }, | 
|  | }, | 
|  | resumeSession: true, | 
|  | flags: []string{ | 
|  | "-expect-no-session-id", | 
|  | "-expect-no-hrr", | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-NoTickets", | 
|  | config: Config{ | 
|  | MaxVersion:             VersionTLS12, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | resumeSession: true, | 
|  | flags:         []string{"-expect-session-id"}, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-EarlyCallback", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | }, | 
|  | flags:         []string{"-use-early-callback"}, | 
|  | resumeSession: true, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // TLS 1.3 basic handshake shapes. | 
|  | tests = append(tests, testCase{ | 
|  | name: "TLS13-1RTT-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | }, | 
|  | resumeSession:        true, | 
|  | resumeRenewedSession: true, | 
|  | // 0-RTT being disabled overrides all other 0-RTT reasons. | 
|  | flags: []string{"-expect-early-data-reason", "disabled"}, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "TLS13-1RTT-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | }, | 
|  | resumeSession:        true, | 
|  | resumeRenewedSession: true, | 
|  | flags: []string{ | 
|  | // TLS 1.3 uses tickets, so the session should not be | 
|  | // cached statefully. | 
|  | "-expect-no-session-id", | 
|  | // 0-RTT being disabled overrides all other 0-RTT reasons. | 
|  | "-expect-early-data-reason", "disabled", | 
|  | }, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | name: "TLS13-HelloRetryRequest-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | // P-384 requires a HelloRetryRequest against BoringSSL's default | 
|  | // configuration. Assert this with ExpectMissingKeyShare. | 
|  | CurvePreferences: []CurveID{CurveP384}, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectMissingKeyShare: true, | 
|  | }, | 
|  | }, | 
|  | // Cover HelloRetryRequest during an ECDHE-PSK resumption. | 
|  | resumeSession: true, | 
|  | flags:         []string{"-expect-hrr"}, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "TLS13-HelloRetryRequest-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | // Require a HelloRetryRequest for every curve. | 
|  | DefaultCurves: []CurveID{}, | 
|  | }, | 
|  | // Cover HelloRetryRequest during an ECDHE-PSK resumption. | 
|  | resumeSession: true, | 
|  | flags:         []string{"-expect-hrr"}, | 
|  | }) | 
|  |  | 
|  | // TLS 1.3 early data tests. DTLS 1.3 doesn't support early data yet. | 
|  | // These tests are disabled for QUIC as well because they test features | 
|  | // that do not apply to QUIC's use of TLS 1.3. | 
|  | // | 
|  | // TODO(crbug.com/381113363): Enable these tests for DTLS once we | 
|  | // support early data in DTLS 1.3. | 
|  | if config.protocol != dtls && config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "TLS13-EarlyData-TooMuchData-Client", | 
|  | config: Config{ | 
|  | MaxVersion:       VersionTLS13, | 
|  | MinVersion:       VersionTLS13, | 
|  | MaxEarlyDataSize: 2, | 
|  | }, | 
|  | resumeConfig: &Config{ | 
|  | MaxVersion:       VersionTLS13, | 
|  | MinVersion:       VersionTLS13, | 
|  | MaxEarlyDataSize: 2, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectEarlyData: [][]byte{[]byte(shimInitialWrite[:2])}, | 
|  | }, | 
|  | }, | 
|  | resumeShimPrefix: shimInitialWrite[2:], | 
|  | resumeSession:    true, | 
|  | earlyData:        true, | 
|  | }) | 
|  |  | 
|  | // Unfinished writes can only be tested when operations are async. EarlyData | 
|  | // can't be tested as part of an ImplicitHandshake in this case since | 
|  | // otherwise the early data will be sent as normal data. | 
|  | if config.async && !config.implicitHandshake { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "TLS13-EarlyData-UnfinishedWrite-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | // Write the server response before expecting early data. | 
|  | ExpectEarlyData:     [][]byte{}, | 
|  | ExpectLateEarlyData: [][]byte{[]byte(shimInitialWrite)}, | 
|  | }, | 
|  | }, | 
|  | resumeSession: true, | 
|  | earlyData:     true, | 
|  | flags:         []string{"-on-resume-read-with-unfinished-write"}, | 
|  | }) | 
|  |  | 
|  | // Rejected unfinished writes are discarded (from the | 
|  | // perspective of the calling application) on 0-RTT | 
|  | // reject. | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "TLS13-EarlyData-RejectUnfinishedWrite-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | AlwaysRejectEarlyData: true, | 
|  | }, | 
|  | }, | 
|  | resumeSession:           true, | 
|  | earlyData:               true, | 
|  | expectEarlyDataRejected: true, | 
|  | flags:                   []string{"-on-resume-read-with-unfinished-write"}, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // Early data has no size limit in QUIC. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "TLS13-MaxEarlyData-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | SendEarlyData:           [][]byte{bytes.Repeat([]byte{1}, 14336+1)}, | 
|  | ExpectEarlyDataAccepted: true, | 
|  | }, | 
|  | }, | 
|  | messageCount:  2, | 
|  | resumeSession: true, | 
|  | earlyData:     true, | 
|  | shouldFail:    true, | 
|  | expectedError: ":TOO_MUCH_READ_EARLY_DATA:", | 
|  | }) | 
|  | } | 
|  |  | 
|  | // Test that early data is disabled for DTLS 1.3. | 
|  | if config.protocol == dtls { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | protocol: dtls, | 
|  | name:     "DTLS13-EarlyData", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | MinVersion: VersionTLS13, | 
|  | }, | 
|  | resumeSession: true, | 
|  | earlyData:     true, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // TLS client auth. | 
|  | // The following tests have a max version of 1.2, so they are not suitable | 
|  | // for use with QUIC. | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-NoCertificate-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ClientAuth: RequestClientCert, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ClientAuth-NoCertificate-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | }, | 
|  | // Setting SSL_VERIFY_PEER allows anonymous clients. | 
|  | flags: []string{"-verify-peer"}, | 
|  | }) | 
|  | } | 
|  | if config.protocol != dtls { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-NoCertificate-Client-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | ClientAuth: RequestClientCert, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ClientAuth-NoCertificate-Server-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | }, | 
|  | // Setting SSL_VERIFY_PEER allows anonymous clients. | 
|  | flags: []string{"-verify-peer"}, | 
|  | }) | 
|  | } | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-RSA-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | }) | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-RSA-Client-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | }) | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-ECDSA-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &ecdsaP256Certificate, | 
|  | }) | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-ECDSA-Client-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &ecdsaP256Certificate, | 
|  | }) | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-NoCertificate-OldCallback", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ClientAuth: RequestClientCert, | 
|  | }, | 
|  | flags: []string{"-use-old-client-cert-callback"}, | 
|  | }) | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-NoCertificate-OldCallback-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | ClientAuth: RequestClientCert, | 
|  | }, | 
|  | flags: []string{"-use-old-client-cert-callback"}, | 
|  | }) | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-OldCallback", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | flags: []string{ | 
|  | "-use-old-client-cert-callback", | 
|  | }, | 
|  | }) | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientAuth-OldCallback-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | ClientAuth: RequireAnyClientCert, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | flags: []string{ | 
|  | "-use-old-client-cert-callback", | 
|  | }, | 
|  | }) | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ClientAuth-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: []string{"-require-any-client-certificate"}, | 
|  | }) | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ClientAuth-Server-TLS13", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS13, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: []string{"-require-any-client-certificate"}, | 
|  | }) | 
|  |  | 
|  | // Test each key exchange on the server side for async keys. | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-RSA", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-ECDHE-RSA", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | 
|  | }, | 
|  | shimCertificate: &rsaCertificate, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-ECDHE-ECDSA", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, | 
|  | }, | 
|  | shimCertificate: &ecdsaP256Certificate, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Basic-Server-Ed25519", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, | 
|  | }, | 
|  | shimCertificate: &ed25519Certificate, | 
|  | flags: []string{ | 
|  | "-verify-prefs", strconv.Itoa(int(signatureEd25519)), | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // No session ticket support; server doesn't send NewSessionTicket. | 
|  | tests = append(tests, testCase{ | 
|  | name: "SessionTicketsDisabled-Client", | 
|  | config: Config{ | 
|  | MaxVersion:             VersionTLS12, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "SessionTicketsDisabled-Server", | 
|  | config: Config{ | 
|  | MaxVersion:             VersionTLS12, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // Skip ServerKeyExchange in PSK key exchange if there's no | 
|  | // identity hint. | 
|  | tests = append(tests, testCase{ | 
|  | name: "EmptyPSKHint-Client", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, | 
|  | PreSharedKey: []byte("secret"), | 
|  | }, | 
|  | flags: []string{"-psk", "secret"}, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "EmptyPSKHint-Server", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, | 
|  | PreSharedKey: []byte("secret"), | 
|  | }, | 
|  | flags: []string{"-psk", "secret"}, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // OCSP stapling tests. | 
|  | for _, vers := range allVersions(config.protocol) { | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "OCSPStapling-Client-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | }, | 
|  | flags: []string{ | 
|  | "-enable-ocsp-stapling", | 
|  | "-expect-ocsp-response", | 
|  | base64FlagValue(testOCSPResponse), | 
|  | "-verify-peer", | 
|  | }, | 
|  | resumeSession: true, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "OCSPStapling-Server-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | expectations: connectionExpectations{ | 
|  | peerCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | }, | 
|  | shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | resumeSession:   true, | 
|  | }) | 
|  |  | 
|  | // The client OCSP callback is an alternate certificate | 
|  | // verification callback. | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientOCSPCallback-Pass-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | }, | 
|  | flags: []string{ | 
|  | "-enable-ocsp-stapling", | 
|  | "-use-ocsp-callback", | 
|  | }, | 
|  | }) | 
|  | var expectedLocalError string | 
|  | if !config.async { | 
|  | // TODO(davidben): Asynchronous fatal alerts are never | 
|  | // sent. https://crbug.com/boringssl/130. | 
|  | expectedLocalError = "remote error: bad certificate status response" | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientOCSPCallback-Fail-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | }, | 
|  | flags: []string{ | 
|  | "-enable-ocsp-stapling", | 
|  | "-use-ocsp-callback", | 
|  | "-fail-ocsp-callback", | 
|  | }, | 
|  | shouldFail:         true, | 
|  | expectedLocalError: expectedLocalError, | 
|  | expectedError:      ":OCSP_CB_ERROR:", | 
|  | }) | 
|  | // The callback still runs if the server does not send an OCSP | 
|  | // response. | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "ClientOCSPCallback-FailNoStaple-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-enable-ocsp-stapling", | 
|  | "-use-ocsp-callback", | 
|  | "-fail-ocsp-callback", | 
|  | }, | 
|  | shouldFail:         true, | 
|  | expectedLocalError: expectedLocalError, | 
|  | expectedError:      ":OCSP_CB_ERROR:", | 
|  | }) | 
|  |  | 
|  | // The server OCSP callback is a legacy mechanism for | 
|  | // configuring OCSP, used by unreliable server software. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ServerOCSPCallback-SetInCallback-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | expectations: connectionExpectations{ | 
|  | peerCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | }, | 
|  | flags: []string{ | 
|  | "-use-ocsp-callback", | 
|  | "-set-ocsp-in-callback", | 
|  | }, | 
|  | resumeSession: true, | 
|  | }) | 
|  |  | 
|  | // The callback may decline OCSP, in which case  we act as if | 
|  | // the client did not support it, even if a response was | 
|  | // configured. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ServerOCSPCallback-Decline-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | expectations: connectionExpectations{ | 
|  | // There should be no OCSP response from the peer. | 
|  | peerCertificate: &rsaCertificate, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-use-ocsp-callback", | 
|  | "-decline-ocsp-callback", | 
|  | }, | 
|  | resumeSession: true, | 
|  | }) | 
|  |  | 
|  | // The callback may also signal an internal error. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ServerOCSPCallback-Fail-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse), | 
|  | flags: []string{ | 
|  | "-use-ocsp-callback", | 
|  | "-fail-ocsp-callback", | 
|  | }, | 
|  | shouldFail:    true, | 
|  | expectedError: ":OCSP_CB_ERROR:", | 
|  | }) | 
|  | } | 
|  |  | 
|  | // Certificate verification tests. | 
|  | for _, vers := range allVersions(config.protocol) { | 
|  | for _, useCustomCallback := range []bool{false, true} { | 
|  | for _, testType := range []testType{clientTest, serverTest} { | 
|  | suffix := "-Client" | 
|  | if testType == serverTest { | 
|  | suffix = "-Server" | 
|  | } | 
|  | suffix += "-" + vers.name | 
|  | if useCustomCallback { | 
|  | suffix += "-CustomCallback" | 
|  | } | 
|  |  | 
|  | // The custom callback and legacy callback have different default | 
|  | // alerts. | 
|  | verifyFailLocalError := "remote error: handshake failure" | 
|  | if useCustomCallback { | 
|  | verifyFailLocalError = "remote error: unknown certificate" | 
|  | } | 
|  |  | 
|  | // We do not reliably send asynchronous fatal alerts. See | 
|  | // https://crbug.com/boringssl/130. | 
|  | if config.async { | 
|  | verifyFailLocalError = "" | 
|  | } | 
|  |  | 
|  | flags := []string{"-verify-peer"} | 
|  | if testType == serverTest { | 
|  | flags = append(flags, "-require-any-client-certificate") | 
|  | } | 
|  | if useCustomCallback { | 
|  | flags = append(flags, "-use-custom-verify-callback") | 
|  | } | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "CertificateVerificationSucceed" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags:         append([]string{"-expect-verify-result"}, flags...), | 
|  | resumeSession: true, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "CertificateVerificationFail" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags:              append([]string{"-verify-fail"}, flags...), | 
|  | shouldFail:         true, | 
|  | expectedError:      ":CERTIFICATE_VERIFY_FAILED:", | 
|  | expectedLocalError: verifyFailLocalError, | 
|  | }) | 
|  | // Tests that although the verify callback fails on resumption, by default we don't call it. | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "CertificateVerificationDoesNotFailOnResume" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags:         append([]string{"-on-resume-verify-fail"}, flags...), | 
|  | resumeSession: true, | 
|  | }) | 
|  | if testType == clientTest && useCustomCallback { | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "CertificateVerificationFailsOnResume" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: append([]string{ | 
|  | "-on-resume-verify-fail", | 
|  | "-reverify-on-resume", | 
|  | }, flags...), | 
|  | resumeSession:      true, | 
|  | shouldFail:         true, | 
|  | expectedError:      ":CERTIFICATE_VERIFY_FAILED:", | 
|  | expectedLocalError: verifyFailLocalError, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "CertificateVerificationPassesOnResume" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | }, flags...), | 
|  | resumeSession: true, | 
|  | }) | 
|  | // TODO(crbug.com/381113363): Support 0-RTT in DTLS 1.3. | 
|  | if vers.version >= VersionTLS13 && config.protocol != dtls { | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-RejectTicket-Client-Reverify" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | resumeConfig: &Config{ | 
|  | MaxVersion:             vers.version, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | resumeSession:           true, | 
|  | expectResumeRejected:    true, | 
|  | earlyData:               true, | 
|  | expectEarlyDataRejected: true, | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | // Session tickets are disabled, so the runner will not send a ticket. | 
|  | "-on-retry-expect-no-session", | 
|  | }, flags...), | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-Reject0RTT-Client-Reverify" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Bugs: ProtocolBugs{ | 
|  | AlwaysRejectEarlyData: true, | 
|  | }, | 
|  | }, | 
|  | resumeSession:           true, | 
|  | expectResumeRejected:    false, | 
|  | earlyData:               true, | 
|  | expectEarlyDataRejected: true, | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | }, flags...), | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-RejectTicket-Client-ReverifyFails" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | resumeConfig: &Config{ | 
|  | MaxVersion:             vers.version, | 
|  | SessionTicketsDisabled: true, | 
|  | }, | 
|  | resumeSession:           true, | 
|  | expectResumeRejected:    true, | 
|  | earlyData:               true, | 
|  | expectEarlyDataRejected: true, | 
|  | shouldFail:              true, | 
|  | expectedError:           ":CERTIFICATE_VERIFY_FAILED:", | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | // Session tickets are disabled, so the runner will not send a ticket. | 
|  | "-on-retry-expect-no-session", | 
|  | "-on-retry-verify-fail", | 
|  | }, flags...), | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-Reject0RTT-Client-ReverifyFails" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Bugs: ProtocolBugs{ | 
|  | AlwaysRejectEarlyData: true, | 
|  | }, | 
|  | }, | 
|  | resumeSession:           true, | 
|  | expectResumeRejected:    false, | 
|  | earlyData:               true, | 
|  | expectEarlyDataRejected: true, | 
|  | shouldFail:              true, | 
|  | expectedError:           ":CERTIFICATE_VERIFY_FAILED:", | 
|  | expectedLocalError:      verifyFailLocalError, | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | "-on-retry-verify-fail", | 
|  | }, flags...), | 
|  | }) | 
|  | // This tests that we only call the verify callback once. | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-Accept0RTT-Client-Reverify" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | resumeSession: true, | 
|  | earlyData:     true, | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | }, flags...), | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: testType, | 
|  | name:     "EarlyData-Accept0RTT-Client-ReverifyFails" + suffix, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | }, | 
|  | resumeSession: true, | 
|  | earlyData:     true, | 
|  | shouldFail:    true, | 
|  | expectedError: ":CERTIFICATE_VERIFY_FAILED:", | 
|  | // We do not set expectedLocalError here because the shim rejects | 
|  | // the connection without an alert. | 
|  | flags: append([]string{ | 
|  | "-reverify-on-resume", | 
|  | "-on-resume-verify-fail", | 
|  | }, flags...), | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // By default, the client is in a soft fail mode where the peer | 
|  | // certificate is verified but failures are non-fatal. | 
|  | tests = append(tests, testCase{ | 
|  | testType: clientTest, | 
|  | name:     "CertificateVerificationSoftFail-" + vers.name, | 
|  | config: Config{ | 
|  | MaxVersion: vers.version, | 
|  | Credential: &rsaCertificate, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-verify-fail", | 
|  | "-expect-verify-result", | 
|  | }, | 
|  | resumeSession: true, | 
|  | }) | 
|  | } | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | name:               "ShimSendAlert", | 
|  | flags:              []string{"-send-alert"}, | 
|  | shimWritesFirst:    true, | 
|  | shouldFail:         true, | 
|  | expectedLocalError: "remote error: decompression failure", | 
|  | }) | 
|  |  | 
|  | if config.protocol == tls { | 
|  | tests = append(tests, testCase{ | 
|  | name: "Renegotiate-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | }, | 
|  | renegotiate: 1, | 
|  | flags: []string{ | 
|  | "-renegotiate-freely", | 
|  | "-expect-total-renegotiations", "1", | 
|  | }, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | name: "Renegotiate-Client-Explicit", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | }, | 
|  | renegotiate: 1, | 
|  | flags: []string{ | 
|  | "-renegotiate-explicit", | 
|  | "-expect-total-renegotiations", "1", | 
|  | }, | 
|  | }) | 
|  |  | 
|  | halfHelloRequestError := ":UNEXPECTED_RECORD:" | 
|  | if config.packHandshake { | 
|  | // If the HelloRequest is sent in the same record as the server Finished, | 
|  | // BoringSSL rejects it before the handshake completes. | 
|  | halfHelloRequestError = ":EXCESS_HANDSHAKE_DATA:" | 
|  | } | 
|  | tests = append(tests, testCase{ | 
|  | name: "SendHalfHelloRequest", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | PackHelloRequestWithFinished: config.packHandshake, | 
|  | }, | 
|  | }, | 
|  | sendHalfHelloRequest: true, | 
|  | flags:                []string{"-renegotiate-ignore"}, | 
|  | shouldFail:           true, | 
|  | expectedError:        halfHelloRequestError, | 
|  | }) | 
|  |  | 
|  | // NPN on client and server; results in post-ChangeCipherSpec message. | 
|  | tests = append(tests, testCase{ | 
|  | name: "NPN-Client", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | NextProtos: []string{"foo"}, | 
|  | }, | 
|  | flags:         []string{"-select-next-proto", "foo"}, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | nextProto:     "foo", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "NPN-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | NextProtos: []string{"bar"}, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-advertise-npn", "\x03foo\x03bar\x03baz", | 
|  | "-expect-next-proto", "bar", | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | nextProto:     "bar", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // The client may select no protocol after seeing the server list. | 
|  | tests = append(tests, testCase{ | 
|  | name: "NPN-Client-ClientSelectEmpty", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | NextProtos: []string{"foo"}, | 
|  | }, | 
|  | flags:         []string{"-select-empty-next-proto"}, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | noNextProto:   true, | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "NPN-Server-ClientSelectEmpty", | 
|  | config: Config{ | 
|  | MaxVersion:          VersionTLS12, | 
|  | NextProtos:          []string{"no-match"}, | 
|  | NoFallbackNextProto: true, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-advertise-npn", "\x03foo\x03bar\x03baz", | 
|  | "-expect-no-next-proto", | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | noNextProto:   true, | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // The server may negotiate NPN, despite offering no protocols. In this | 
|  | // case, the server must still be prepared for the client to select a | 
|  | // fallback protocol. | 
|  | tests = append(tests, testCase{ | 
|  | name: "NPN-Client-ServerAdvertiseEmpty", | 
|  | config: Config{ | 
|  | MaxVersion:               VersionTLS12, | 
|  | NegotiateNPNWithNoProtos: true, | 
|  | }, | 
|  | flags:         []string{"-select-next-proto", "foo"}, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | nextProto:     "foo", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "NPN-Server-ServerAdvertiseEmpty", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | NextProtos: []string{"foo"}, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-advertise-empty-npn", | 
|  | "-expect-next-proto", "foo", | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | nextProto:     "foo", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // Client does False Start and negotiates NPN. | 
|  | tests = append(tests, testCase{ | 
|  | name: "FalseStart", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | 
|  | NextProtos:   []string{"foo"}, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectFalseStart: true, | 
|  | }, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-false-start", | 
|  | "-select-next-proto", "foo", | 
|  | }, | 
|  | shimWritesFirst: true, | 
|  | resumeSession:   true, | 
|  | }) | 
|  |  | 
|  | // Client does False Start and negotiates ALPN. | 
|  | tests = append(tests, testCase{ | 
|  | name: "FalseStart-ALPN", | 
|  | config: Config{ | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | 
|  | NextProtos:   []string{"foo"}, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectFalseStart: true, | 
|  | }, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-false-start", | 
|  | "-advertise-alpn", "\x03foo", | 
|  | "-expect-alpn", "foo", | 
|  | }, | 
|  | shimWritesFirst: true, | 
|  | resumeSession:   true, | 
|  | }) | 
|  |  | 
|  | // False Start without session tickets. | 
|  | tests = append(tests, testCase{ | 
|  | name: "FalseStart-SessionTicketsDisabled", | 
|  | config: Config{ | 
|  | MaxVersion:             VersionTLS12, | 
|  | CipherSuites:           []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | 
|  | NextProtos:             []string{"foo"}, | 
|  | SessionTicketsDisabled: true, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectFalseStart: true, | 
|  | }, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-false-start", | 
|  | "-select-next-proto", "foo", | 
|  | }, | 
|  | shimWritesFirst: true, | 
|  | }) | 
|  |  | 
|  | // Server parses a V2ClientHello. Test different lengths for the | 
|  | // challenge field. | 
|  | for _, challengeLength := range []int{16, 31, 32, 33, 48} { | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     fmt.Sprintf("SendV2ClientHello-%d", challengeLength), | 
|  | config: Config{ | 
|  | // Choose a cipher suite that does not involve | 
|  | // elliptic curves, so no extensions are | 
|  | // involved. | 
|  | MaxVersion:   VersionTLS12, | 
|  | CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, | 
|  | Bugs: ProtocolBugs{ | 
|  | SendV2ClientHello:            true, | 
|  | V2ClientHelloChallengeLength: challengeLength, | 
|  | }, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-expect-msg-callback", | 
|  | `read v2clienthello | 
|  | write hs 2 | 
|  | write hs 11 | 
|  | write hs 14 | 
|  | read hs 16 | 
|  | read ccs | 
|  | read hs 20 | 
|  | write ccs | 
|  | write hs 20 | 
|  | read alert 1 0 | 
|  | `, | 
|  | }, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // Channel ID and NPN at the same time, to ensure their relative | 
|  | // ordering is correct. | 
|  | tests = append(tests, testCase{ | 
|  | name: "ChannelID-NPN-Client", | 
|  | config: Config{ | 
|  | MaxVersion:       VersionTLS12, | 
|  | RequestChannelID: true, | 
|  | NextProtos:       []string{"foo"}, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-send-channel-id", channelIDKeyPath, | 
|  | "-select-next-proto", "foo", | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | channelID:     true, | 
|  | nextProto:     "foo", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ChannelID-NPN-Server", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | ChannelID:  &channelIDKey, | 
|  | NextProtos: []string{"bar"}, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-expect-channel-id", | 
|  | base64FlagValue(channelIDBytes), | 
|  | "-advertise-npn", "\x03foo\x03bar\x03baz", | 
|  | "-expect-next-proto", "bar", | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | channelID:     true, | 
|  | nextProto:     "bar", | 
|  | nextProtoType: npn, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // Bidirectional shutdown with the runner initiating. | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Runner", | 
|  | config: Config{ | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | flags: []string{"-check-close-notify"}, | 
|  | }) | 
|  | } | 
|  | if config.protocol != dtls { | 
|  | // Test Channel ID | 
|  | for _, ver := range allVersions(config.protocol) { | 
|  | if ver.version < VersionTLS10 { | 
|  | continue | 
|  | } | 
|  | // Client sends a Channel ID. | 
|  | tests = append(tests, testCase{ | 
|  | name: "ChannelID-Client-" + ver.name, | 
|  | config: Config{ | 
|  | MaxVersion:       ver.version, | 
|  | RequestChannelID: true, | 
|  | }, | 
|  | flags:         []string{"-send-channel-id", channelIDKeyPath}, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | channelID: true, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | // Server accepts a Channel ID. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ChannelID-Server-" + ver.name, | 
|  | config: Config{ | 
|  | MaxVersion: ver.version, | 
|  | ChannelID:  &channelIDKey, | 
|  | }, | 
|  | flags: []string{ | 
|  | "-expect-channel-id", | 
|  | base64FlagValue(channelIDBytes), | 
|  | }, | 
|  | resumeSession: true, | 
|  | expectations: connectionExpectations{ | 
|  | channelID: true, | 
|  | }, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "InvalidChannelIDSignature-" + ver.name, | 
|  | config: Config{ | 
|  | MaxVersion: ver.version, | 
|  | ChannelID:  &channelIDKey, | 
|  | Bugs: ProtocolBugs{ | 
|  | InvalidChannelIDSignature: true, | 
|  | }, | 
|  | }, | 
|  | flags:         []string{"-enable-channel-id"}, | 
|  | shouldFail:    true, | 
|  | expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:", | 
|  | }) | 
|  |  | 
|  | if ver.version < VersionTLS13 { | 
|  | // Channel ID requires ECDHE ciphers. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ChannelID-NoECDHE-" + ver.name, | 
|  | config: Config{ | 
|  | MaxVersion:   ver.version, | 
|  | CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, | 
|  | ChannelID:    &channelIDKey, | 
|  | }, | 
|  | expectations: connectionExpectations{ | 
|  | channelID: false, | 
|  | }, | 
|  | flags: []string{"-enable-channel-id"}, | 
|  | }) | 
|  |  | 
|  | // Sanity-check setting expectations.channelID false works. | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "ChannelID-ECDHE-" + ver.name, | 
|  | config: Config{ | 
|  | MaxVersion:   ver.version, | 
|  | CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, | 
|  | ChannelID:    &channelIDKey, | 
|  | }, | 
|  | expectations: connectionExpectations{ | 
|  | channelID: false, | 
|  | }, | 
|  | flags:              []string{"-enable-channel-id"}, | 
|  | shouldFail:         true, | 
|  | expectedLocalError: "channel ID unexpectedly negotiated", | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | if !config.implicitHandshake { | 
|  | // Bidirectional shutdown with the shim initiating. The runner, | 
|  | // in the meantime, sends garbage before the close_notify which | 
|  | // the shim must ignore. This test is disabled under implicit | 
|  | // handshake tests because the shim never reads or writes. | 
|  |  | 
|  | // Tests that require checking for a close notify alert don't work with | 
|  | // QUIC because alerts are handled outside of the TLS stack in QUIC. | 
|  | if config.protocol != quic { | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown:     true, | 
|  | sendEmptyRecords:  1, | 
|  | sendWarningAlerts: 1, | 
|  | flags:             []string{"-check-close-notify"}, | 
|  | }) | 
|  |  | 
|  | // The shim should reject unexpected application data | 
|  | // when shutting down. | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim-ApplicationData", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown:     true, | 
|  | messageCount:      1, | 
|  | sendEmptyRecords:  1, | 
|  | sendWarningAlerts: 1, | 
|  | flags:             []string{"-check-close-notify"}, | 
|  | shouldFail:        true, | 
|  | expectedError:     ":APPLICATION_DATA_ON_SHUTDOWN:", | 
|  | }) | 
|  |  | 
|  | // Test that SSL_shutdown still processes KeyUpdate. | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim-KeyUpdate", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS13, | 
|  | MaxVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown:    true, | 
|  | sendKeyUpdates:   1, | 
|  | keyUpdateRequest: keyUpdateRequested, | 
|  | flags:            []string{"-check-close-notify"}, | 
|  | }) | 
|  |  | 
|  | // Test that SSL_shutdown processes HelloRequest | 
|  | // correctly. | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim-HelloRequest-Ignore", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS12, | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | SendHelloRequestBeforeEveryAppDataRecord: true, | 
|  | ExpectCloseNotify:                        true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown: true, | 
|  | flags: []string{ | 
|  | "-renegotiate-ignore", | 
|  | "-check-close-notify", | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim-HelloRequest-Reject", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS12, | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown: true, | 
|  | renegotiate:   1, | 
|  | shouldFail:    true, | 
|  | expectedError: ":NO_RENEGOTIATION:", | 
|  | flags:         []string{"-check-close-notify"}, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "Shutdown-Shim-HelloRequest-CannotHandshake", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS12, | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown: true, | 
|  | renegotiate:   1, | 
|  | shouldFail:    true, | 
|  | expectedError: ":NO_RENEGOTIATION:", | 
|  | flags: []string{ | 
|  | "-check-close-notify", | 
|  | "-renegotiate-freely", | 
|  | }, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, testCase{ | 
|  | testType: serverTest, | 
|  | name:     "Shutdown-Shim-Renegotiate-Server-Forbidden", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | ExpectCloseNotify: true, | 
|  | }, | 
|  | }, | 
|  | shimShutsDown: true, | 
|  | renegotiate:   1, | 
|  | shouldFail:    true, | 
|  | expectedError: ":NO_RENEGOTIATION:", | 
|  | flags: []string{ | 
|  | "-check-close-notify", | 
|  | }, | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  | if config.protocol == dtls { | 
|  | tests = append(tests, testCase{ | 
|  | name: "SkipHelloVerifyRequest", | 
|  | config: Config{ | 
|  | MaxVersion: VersionTLS12, | 
|  | Bugs: ProtocolBugs{ | 
|  | SkipHelloVerifyRequest: true, | 
|  | }, | 
|  | }, | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "DTLS13-HelloVerifyRequest", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | ForceHelloVerifyRequest: true, | 
|  | }, | 
|  | }, | 
|  | shouldFail:    true, | 
|  | expectedError: ":INVALID_MESSAGE:", | 
|  | }) | 
|  | tests = append(tests, testCase{ | 
|  | name: "DTLS13-HelloVerifyRequestEmptyCookie", | 
|  | config: Config{ | 
|  | MinVersion: VersionTLS13, | 
|  | Bugs: ProtocolBugs{ | 
|  | ForceHelloVerifyRequest:       true, | 
|  | EmptyHelloVerifyRequestCookie: true, | 
|  | }, | 
|  | }, | 
|  | shouldFail:    true, | 
|  | expectedError: ":INVALID_MESSAGE:", | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, test := range tests { | 
|  | test.protocol = config.protocol | 
|  | test.name += "-" + config.protocol.String() | 
|  | if config.async { | 
|  | test.name += "-Async" | 
|  | test.flags = append(test.flags, "-async") | 
|  | } else { | 
|  | test.name += "-Sync" | 
|  | } | 
|  | if config.splitHandshake { | 
|  | test.name += "-SplitHandshakeRecords" | 
|  | test.config.Bugs.MaxHandshakeRecordLength = 1 | 
|  | if config.protocol == dtls { | 
|  | test.config.Bugs.MaxPacketLength = 256 | 
|  | test.flags = append(test.flags, "-mtu", "256") | 
|  | } | 
|  | } | 
|  | if config.packHandshake { | 
|  | test.name += "-PackHandshake" | 
|  | if config.protocol == dtls { | 
|  | test.config.Bugs.MaxHandshakeRecordLength = 2 | 
|  | test.config.Bugs.PackHandshakeFragments = 20 | 
|  | test.config.Bugs.PackHandshakeRecords = 1500 | 
|  | test.config.Bugs.PackAppDataWithHandshake = true | 
|  | } else { | 
|  | test.config.Bugs.PackHandshakeFlight = true | 
|  | } | 
|  | } | 
|  | if config.implicitHandshake { | 
|  | test.name += "-ImplicitHandshake" | 
|  | test.flags = append(test.flags, "-implicit-handshake") | 
|  | } | 
|  | testCases = append(testCases, test) | 
|  | } | 
|  | } |