Now runner.go contains only the test runner, while the various test suites are moved into their own files, named foo_tests.go. (foo_test.go would be treated as a Go test.) I broadly just split by the addFooTests functions, but in a few cases I grouped them together. Now we no longer have a single 24,000 line file with all the tests. That was getting unwieldy. Change-Id: I76f372f60f5f0de5f1ba0913317918a4053372a3 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/77107 Reviewed-by: Bob Beck <bbe@google.com> Auto-Submit: David Benjamin <davidben@google.com> Commit-Queue: Bob Beck <bbe@google.com>
1492 lines
41 KiB
Go
1492 lines
41 KiB
Go
// 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)
|
|
}
|
|
}
|