| // 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 | |
| // TODO(crbug.com/42290035): Asynchronous fatal alerts are never sent. | |
| ifSync := func(expectedLocalError string) string { | |
| if config.async { | |
| return "" | |
| } | |
| return expectedLocalError | |
| } | |
| // 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", | |
| }, | |
| }) | |
| 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: ifSync("remote error: bad certificate status response"), | |
| 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: ifSync("remote error: bad certificate status response"), | |
| 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" | |
| } | |
| verifyFailLocalError = ifSync(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:", | |
| expectedLocalError: ifSync("remote error: error decrypting message"), | |
| // In TLS 1.3 and later, the client (runner) sends Channel ID in | |
| // the last flight. Although the server (shim) will reject it, | |
| // the handshake has already completed from the client's | |
| // perspective. That means, from the client's perspective, | |
| // Channel ID is successfully negotiated. | |
| expectations: connectionExpectations{ | |
| channelID: ver.version >= VersionTLS13, | |
| }, | |
| }) | |
| 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) | |
| } | |
| } |