blob: 933da21c097edebfc822dfe0a312f7ee19fb7326 [file] [log] [blame]
// Copyright 2025 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runner
import (
"fmt"
)
func addExtensionTests() {
exampleCertificate := rootCA.Issue(X509Info{
PrivateKey: &ecdsaP256Key,
DNSNames: []string{"example.com"},
}).ToCredential()
// Repeat extensions tests at all versions.
for _, protocol := range []protocol{tls, dtls, quic} {
for _, ver := range allVersions(protocol) {
suffix := fmt.Sprintf("%s-%s", protocol.String(), ver.name)
// Test that duplicate extensions are rejected.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "DuplicateExtensionClient-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
DuplicateExtension: true,
},
},
shouldFail: true,
expectedLocalError: "remote error: error decoding message",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "DuplicateExtensionServer-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
DuplicateExtension: true,
},
},
shouldFail: true,
expectedLocalError: "remote error: error decoding message",
})
// Test SNI.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "ServerNameExtensionClient-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
ExpectServerName: "example.com",
},
Credential: &exampleCertificate,
},
flags: []string{"-host-name", "example.com"},
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "ServerNameExtensionClientMismatch-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
ExpectServerName: "mismatch.com",
},
},
flags: []string{"-host-name", "example.com"},
shouldFail: true,
expectedLocalError: "tls: unexpected server name",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "ServerNameExtensionClientMissing-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
ExpectServerName: "missing.com",
},
},
shouldFail: true,
expectedLocalError: "tls: unexpected server name",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "TolerateServerNameAck-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
SendServerNameAck: true,
},
Credential: &exampleCertificate,
},
flags: []string{"-host-name", "example.com"},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: "UnsolicitedServerNameAck-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
SendServerNameAck: true,
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "ServerNameExtensionServer-" + suffix,
config: Config{
MaxVersion: ver.version,
ServerName: "example.com",
},
flags: []string{"-expect-server-name", "example.com"},
resumeSession: true,
expectations: connectionExpectations{
serverNameAck: ptrTo(true),
},
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "ServerNameExtensionServer-NoACK-" + suffix,
config: Config{
MaxVersion: ver.version,
ServerName: "example.com",
},
flags: []string{
"-expect-server-name", "example.com",
"-no-server-name-ack",
},
resumeSession: true,
expectations: connectionExpectations{
serverNameAck: ptrTo(false),
},
})
// Test ALPN.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
skipQUICALPNConfig: true,
name: "ALPNClient-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo"},
},
flags: []string{
"-advertise-alpn", "\x03foo\x03bar\x03baz",
"-expect-alpn", "foo",
},
expectations: connectionExpectations{
nextProto: "foo",
nextProtoType: alpn,
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
skipQUICALPNConfig: true,
name: "ALPNClient-RejectUnknown-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
SendALPN: "baz",
},
},
flags: []string{
"-advertise-alpn", "\x03foo\x03bar",
},
shouldFail: true,
expectedError: ":INVALID_ALPN_PROTOCOL:",
expectedLocalError: "remote error: illegal parameter",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
skipQUICALPNConfig: true,
name: "ALPNClient-AllowUnknown-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
SendALPN: "baz",
},
},
flags: []string{
"-advertise-alpn", "\x03foo\x03bar",
"-allow-unknown-alpn-protos",
"-expect-alpn", "baz",
},
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
"-select-alpn", "foo",
},
expectations: connectionExpectations{
nextProto: "foo",
nextProtoType: alpn,
},
resumeSession: true,
})
var shouldDeclineALPNFail bool
var declineALPNError, declineALPNLocalError string
if protocol == quic {
// ALPN is mandatory in QUIC.
shouldDeclineALPNFail = true
declineALPNError = ":NO_APPLICATION_PROTOCOL:"
declineALPNLocalError = "remote error: no application protocol"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-Decline-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
},
flags: []string{"-decline-alpn"},
expectations: connectionExpectations{
noNextProto: true,
},
resumeSession: true,
shouldFail: shouldDeclineALPNFail,
expectedError: declineALPNError,
expectedLocalError: declineALPNLocalError,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-Reject-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
},
flags: []string{"-reject-alpn"},
shouldFail: true,
expectedError: ":NO_APPLICATION_PROTOCOL:",
expectedLocalError: "remote error: no application protocol",
})
// Test that the server implementation catches itself if the
// callback tries to return an invalid empty ALPN protocol.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-SelectEmpty-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
"-select-empty-alpn",
},
shouldFail: true,
expectedLocalError: "remote error: internal error",
expectedError: ":INVALID_ALPN_PROTOCOL:",
})
// Test ALPN in async mode as well to ensure that extensions callbacks are only
// called once.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-Async-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
// Prior to TLS 1.3, exercise the asynchronous session callback.
SessionTicketsDisabled: ver.version < VersionTLS13,
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
"-select-alpn", "foo",
"-async",
},
expectations: connectionExpectations{
nextProto: "foo",
nextProtoType: alpn,
},
resumeSession: true,
})
var emptyString string
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
skipQUICALPNConfig: true,
name: "ALPNClient-EmptyProtocolName-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{""},
Bugs: ProtocolBugs{
// A server returning an empty ALPN protocol
// should be rejected.
ALPNProtocol: &emptyString,
},
},
flags: []string{
"-advertise-alpn", "\x03foo",
},
shouldFail: true,
expectedError: ":PARSE_TLSEXT:",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
skipQUICALPNConfig: true,
name: "ALPNServer-EmptyProtocolName-" + suffix,
config: Config{
MaxVersion: ver.version,
// A ClientHello containing an empty ALPN protocol
// should be rejected.
NextProtos: []string{"foo", "", "baz"},
},
flags: []string{
"-select-alpn", "foo",
},
shouldFail: true,
expectedError: ":PARSE_TLSEXT:",
})
// Test NPN and the interaction with ALPN.
if ver.version < VersionTLS13 && protocol == tls {
// Test that the server prefers ALPN over NPN.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "ALPNServer-Preferred-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
"-select-alpn", "foo",
"-advertise-npn", "\x03foo\x03bar\x03baz",
},
expectations: connectionExpectations{
nextProto: "foo",
nextProtoType: alpn,
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "ALPNServer-Preferred-Swapped-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
Bugs: ProtocolBugs{
SwapNPNAndALPN: true,
},
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
"-select-alpn", "foo",
"-advertise-npn", "\x03foo\x03bar\x03baz",
},
expectations: connectionExpectations{
nextProto: "foo",
nextProtoType: alpn,
},
resumeSession: true,
})
// Test that negotiating both NPN and ALPN is forbidden.
testCases = append(testCases, testCase{
protocol: protocol,
name: "NegotiateALPNAndNPN-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
Bugs: ProtocolBugs{
NegotiateALPNAndNPN: true,
},
},
flags: []string{
"-advertise-alpn", "\x03foo",
"-select-next-proto", "foo",
},
shouldFail: true,
expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
})
testCases = append(testCases, testCase{
protocol: protocol,
name: "NegotiateALPNAndNPN-Swapped-" + suffix,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
Bugs: ProtocolBugs{
NegotiateALPNAndNPN: true,
SwapNPNAndALPN: true,
},
},
flags: []string{
"-advertise-alpn", "\x03foo",
"-select-next-proto", "foo",
},
shouldFail: true,
expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
})
}
// Test missing ALPN in QUIC
if protocol == quic {
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Client-ALPNMissingFromConfig-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
skipQUICALPNConfig: true,
shouldFail: true,
expectedError: ":NO_APPLICATION_PROTOCOL:",
})
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Client-ALPNMissing-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
flags: []string{
"-advertise-alpn", "\x03foo",
},
skipQUICALPNConfig: true,
shouldFail: true,
expectedError: ":NO_APPLICATION_PROTOCOL:",
expectedLocalError: "remote error: no application protocol",
})
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "Server-ALPNMissing-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
},
skipQUICALPNConfig: true,
shouldFail: true,
expectedError: ":NO_APPLICATION_PROTOCOL:",
expectedLocalError: "remote error: no application protocol",
})
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "Server-ALPNMismatch-" + suffix,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
NextProtos: []string{"foo"},
},
flags: []string{
"-decline-alpn",
},
skipQUICALPNConfig: true,
shouldFail: true,
expectedError: ":NO_APPLICATION_PROTOCOL:",
expectedLocalError: "remote error: no application protocol",
})
}
// Test ALPS.
if ver.version >= VersionTLS13 {
// Test basic client with different ALPS codepoint.
for _, alpsCodePoint := range []ALPSUseCodepoint{ALPSUseCodepointNew, ALPSUseCodepointOld} {
useAlpsCodepointFlag := "0"
if alpsCodePoint == ALPSUseCodepointNew {
useAlpsCodepointFlag = "1"
}
flags := []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
expectations := connectionExpectations{
peerApplicationSettingsOld: []byte("shim1"),
}
resumeExpectations := &connectionExpectations{
peerApplicationSettingsOld: []byte("shim2"),
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte("shim1"),
}
resumeExpectations = &connectionExpectations{
peerApplicationSettings: []byte("shim2"),
}
}
flags = append(flags,
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-on-initial-application-settings", "proto,shim1",
"-on-initial-expect-peer-application-settings", "runner1",
"-on-resume-application-settings", "proto,shim2",
"-on-resume-expect-peer-application-settings", "runner2")
// Test that server can negotiate ALPS, including different values
// on resumption.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-Basic-Client-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeConfig: &Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
expectations: expectations,
resumeExpectations: resumeExpectations,
flags: flags,
})
// Test basic server with different ALPS codepoint.
flags = []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
expectations = connectionExpectations{
peerApplicationSettingsOld: []byte("shim1"),
}
resumeExpectations = &connectionExpectations{
peerApplicationSettingsOld: []byte("shim2"),
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte("shim1"),
}
resumeExpectations = &connectionExpectations{
peerApplicationSettings: []byte("shim2"),
}
}
flags = append(flags,
"-select-alpn", "proto",
"-on-initial-application-settings", "proto,shim1",
"-on-initial-expect-peer-application-settings", "runner1",
"-on-resume-application-settings", "proto,shim2",
"-on-resume-expect-peer-application-settings", "runner2")
// Test that server can negotiate ALPS, including different values
// on resumption.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-Basic-Server-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeConfig: &Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
expectations: expectations,
resumeExpectations: resumeExpectations,
flags: flags,
})
// Try different ALPS codepoint for all the existing tests.
alpsFlags := []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
expectations = connectionExpectations{
peerApplicationSettingsOld: []byte("shim1"),
}
resumeExpectations = &connectionExpectations{
peerApplicationSettingsOld: []byte("shim2"),
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte("shim1"),
}
resumeExpectations = &connectionExpectations{
peerApplicationSettings: []byte("shim2"),
}
}
// Test that the server can defer its ALPS configuration to the ALPN
// selection callback.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-Basic-Server-Defer-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeConfig: &Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
expectations: expectations,
resumeExpectations: resumeExpectations,
flags: append([]string{
"-select-alpn", "proto",
"-defer-alps",
"-on-initial-application-settings", "proto,shim1",
"-on-initial-expect-peer-application-settings", "runner1",
"-on-resume-application-settings", "proto,shim2",
"-on-resume-expect-peer-application-settings", "runner2",
}, alpsFlags...),
})
expectations = connectionExpectations{
peerApplicationSettingsOld: []byte{},
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte{},
}
}
// Test the client and server correctly handle empty settings.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-Empty-Client-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": {}},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
expectations: expectations,
flags: append([]string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-application-settings", "proto,",
"-expect-peer-application-settings", "",
}, alpsFlags...),
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-Empty-Server-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": {}},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
expectations: expectations,
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto,",
"-expect-peer-application-settings", "",
}, alpsFlags...),
})
bugs := ProtocolBugs{
AlwaysNegotiateApplicationSettingsOld: true,
}
if alpsCodePoint == ALPSUseCodepointNew {
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsNew: true,
}
}
// Test the client rejects application settings from the server on
// protocols it doesn't have them.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-UnsupportedProtocol-Client-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto1"},
ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
Bugs: bugs,
ALPSUseNewCodepoint: alpsCodePoint,
},
// The client supports ALPS with "proto2", but not "proto1".
flags: append([]string{
"-advertise-alpn", "\x06proto1\x06proto2",
"-application-settings", "proto2,shim",
"-expect-alpn", "proto1",
}, alpsFlags...),
// The server sends ALPS with "proto1", which is invalid.
shouldFail: true,
expectedError: ":INVALID_ALPN_PROTOCOL:",
expectedLocalError: "remote error: illegal parameter",
})
// Test client rejects application settings from the server when
// server sends the wrong ALPS codepoint.
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsOld: true,
}
if alpsCodePoint == ALPSUseCodepointOld {
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsNew: true,
}
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-WrongServerCodepoint-Client-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": {}},
Bugs: bugs,
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: append([]string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-application-settings", "proto,",
"-expect-peer-application-settings", "",
}, alpsFlags...),
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
// Test server ignore wrong codepoint from client.
clientSends := ALPSUseCodepointNew
if alpsCodePoint == ALPSUseCodepointNew {
clientSends = ALPSUseCodepointOld
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-IgnoreClientWrongCodepoint-Server-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
ALPSUseNewCodepoint: clientSends,
},
resumeConfig: &Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
ALPSUseNewCodepoint: clientSends,
},
resumeSession: true,
flags: append([]string{
"-select-alpn", "proto",
"-on-initial-application-settings", "proto,shim1",
"-on-resume-application-settings", "proto,shim2",
}, alpsFlags...),
})
// Test the server declines ALPS if it doesn't support it for the
// specified protocol.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-UnsupportedProtocol-Server-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto1"},
ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
// The server supports ALPS with "proto2", but not "proto1".
flags: append([]string{
"-select-alpn", "proto1",
"-application-settings", "proto2,shim",
}, alpsFlags...),
})
// Test the client rejects application settings from the server when
// it always negotiate both codepoint.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-UnsupportedProtocol-Client-ServerBoth-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto1"},
ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
Bugs: ProtocolBugs{
AlwaysNegotiateApplicationSettingsBoth: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: append([]string{
"-advertise-alpn", "\x06proto1\x06proto2",
"-application-settings", "proto1,shim",
"-expect-alpn", "proto1",
}, alpsFlags...),
// The server sends ALPS with both application settings, which is invalid.
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
expectations = connectionExpectations{
peerApplicationSettingsOld: []byte("shim"),
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte("shim"),
}
}
// Test that the server rejects a missing application_settings extension.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-OmitClientApplicationSettings-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
Bugs: ProtocolBugs{
OmitClientApplicationSettings: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto,shim",
}, alpsFlags...),
// The runner is a client, so it only processes the shim's alert
// after checking connection state.
expectations: expectations,
shouldFail: true,
expectedError: ":MISSING_EXTENSION:",
expectedLocalError: "remote error: missing extension",
})
// Test that the server rejects a missing EncryptedExtensions message.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-OmitClientEncryptedExtensions-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
Bugs: ProtocolBugs{
OmitClientEncryptedExtensions: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto,shim",
}, alpsFlags...),
// The runner is a client, so it only processes the shim's alert
// after checking connection state.
expectations: expectations,
shouldFail: true,
expectedError: ":UNEXPECTED_MESSAGE:",
expectedLocalError: "remote error: unexpected message",
})
// Test that the server rejects an unexpected EncryptedExtensions message.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("UnexpectedClientEncryptedExtensions-%s-%s", alpsCodePoint, suffix),
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
AlwaysSendClientEncryptedExtensions: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
shouldFail: true,
expectedError: ":UNEXPECTED_MESSAGE:",
expectedLocalError: "remote error: unexpected message",
})
// Test that the server rejects an unexpected extension in an
// expected EncryptedExtensions message.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ExtraClientEncryptedExtension-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
Bugs: ProtocolBugs{
SendExtraClientEncryptedExtension: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto,shim",
}, alpsFlags...),
// The runner is a client, so it only processes the shim's alert
// after checking connection state.
expectations: expectations,
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
// Test that ALPS is carried over on 0-RTT.
// TODO(crbug.com/381113363): Support 0-RTT in DTLS 1.3.
if protocol != dtls {
for _, empty := range []bool{false, true} {
maybeEmpty := ""
runnerSettings := "runner"
shimSettings := "shim"
if empty {
maybeEmpty = "Empty-"
runnerSettings = ""
shimSettings = ""
}
expectations = connectionExpectations{
peerApplicationSettingsOld: []byte(shimSettings),
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: []byte(shimSettings),
}
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-EarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-application-settings", "proto," + shimSettings,
"-expect-peer-application-settings", runnerSettings,
}, alpsFlags...),
expectations: expectations,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-EarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto," + shimSettings,
"-expect-peer-application-settings", runnerSettings,
}, alpsFlags...),
expectations: expectations,
})
// Sending application settings in 0-RTT handshakes is forbidden.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
Bugs: ProtocolBugs{
SendApplicationSettingsWithEarlyData: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-application-settings", "proto," + shimSettings,
"-expect-peer-application-settings", runnerSettings,
}, alpsFlags...),
expectations: expectations,
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
expectedLocalError: "remote error: illegal parameter",
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
Bugs: ProtocolBugs{
SendApplicationSettingsWithEarlyData: true,
},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto," + shimSettings,
"-expect-peer-application-settings", runnerSettings,
}, alpsFlags...),
expectations: expectations,
shouldFail: true,
expectedError: ":UNEXPECTED_MESSAGE:",
expectedLocalError: "remote error: unexpected message",
})
}
// Test that the client and server each decline early data if local
// ALPS preferences has changed for the current connection.
alpsMismatchTests := []struct {
name string
initialSettings, resumeSettings []byte
}{
{"DifferentValues", []byte("settings1"), []byte("settings2")},
{"OnOff", []byte("settings"), nil},
{"OffOn", nil, []byte("settings")},
// The empty settings value should not be mistaken for ALPS not
// being negotiated.
{"OnEmpty", []byte("settings"), []byte{}},
{"EmptyOn", []byte{}, []byte("settings")},
{"EmptyOff", []byte{}, nil},
{"OffEmpty", nil, []byte{}},
}
for _, test := range alpsMismatchTests {
flags := []string{"-on-resume-expect-early-data-reason", "alps_mismatch"}
flags = append(flags, alpsFlags...)
if test.initialSettings != nil {
flags = append(flags, "-on-initial-application-settings", "proto,"+string(test.initialSettings))
flags = append(flags, "-on-initial-expect-peer-application-settings", "runner")
}
if test.resumeSettings != nil {
flags = append(flags, "-on-resume-application-settings", "proto,"+string(test.resumeSettings))
flags = append(flags, "-on-resume-expect-peer-application-settings", "runner")
}
expectations = connectionExpectations{
peerApplicationSettingsOld: test.initialSettings,
}
resumeExpectations = &connectionExpectations{
peerApplicationSettingsOld: test.resumeSettings,
}
if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
peerApplicationSettings: test.initialSettings,
}
resumeExpectations = &connectionExpectations{
peerApplicationSettings: test.resumeSettings,
}
}
// The client should not offer early data if the session is
// inconsistent with the new configuration. Note that if
// the session did not negotiate ALPS (test.initialSettings
// is nil), the client always offers early data.
if test.initialSettings != nil {
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s-%s", test.name, alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
MaxEarlyDataSize: 16384,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
flags: append([]string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-no-offer-early-data",
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
}, flags...),
expectations: expectations,
resumeExpectations: resumeExpectations,
})
}
// The server should reject early data if the session is
// inconsistent with the new selection.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s-%s", test.name, alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
expectEarlyDataRejected: true,
flags: append([]string{
"-select-alpn", "proto",
}, flags...),
expectations: expectations,
resumeExpectations: resumeExpectations,
})
}
// Test that 0-RTT continues working when the shim configures
// ALPS but the peer does not.
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-EarlyData-Client-ServerDecline-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-application-settings", "proto,shim",
}, alpsFlags...),
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-EarlyData-Server-ClientNoOffe-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
earlyData: true,
flags: append([]string{
"-select-alpn", "proto",
"-application-settings", "proto,shim",
}, alpsFlags...),
})
}
}
} else {
// Test the client rejects the ALPS extension if the server
// negotiated TLS 1.2 or below.
for _, alpsCodePoint := range []ALPSUseCodepoint{ALPSUseCodepointNew, ALPSUseCodepointOld} {
useAlpsCodepointFlag := "0"
if alpsCodePoint == ALPSUseCodepointNew {
useAlpsCodepointFlag = "1"
}
flags := []string{
"-advertise-alpn", "\x03foo",
"-expect-alpn", "foo",
"-application-settings", "foo,shim",
"-alps-use-new-codepoint", useAlpsCodepointFlag,
}
bugs := ProtocolBugs{
AlwaysNegotiateApplicationSettingsOld: true,
}
if alpsCodePoint == ALPSUseCodepointNew {
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsNew: true,
}
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-Reject-Client-%s-%s", alpsCodePoint, suffix),
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo"},
ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
Bugs: bugs,
ALPSUseNewCodepoint: alpsCodePoint,
},
flags: flags,
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
flags = []string{
"-on-resume-advertise-alpn", "\x03foo",
"-on-resume-expect-alpn", "foo",
"-on-resume-application-settings", "foo,shim",
"-alps-use-new-codepoint", useAlpsCodepointFlag,
}
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsOld: true,
}
if alpsCodePoint == ALPSUseCodepointNew {
bugs = ProtocolBugs{
AlwaysNegotiateApplicationSettingsNew: true,
}
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("ALPS-Reject-Client-Resume-%s-%s", alpsCodePoint, suffix),
config: Config{
MaxVersion: ver.version,
},
resumeConfig: &Config{
MaxVersion: ver.version,
NextProtos: []string{"foo"},
ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
Bugs: bugs,
ALPSUseNewCodepoint: alpsCodePoint,
},
resumeSession: true,
flags: flags,
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
// Test the server declines ALPS if it negotiates TLS 1.2 or below.
flags = []string{
"-select-alpn", "foo",
"-application-settings", "foo,shim",
"-alps-use-new-codepoint", useAlpsCodepointFlag,
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("ALPS-Decline-Server-%s-%s", alpsCodePoint, suffix),
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo"},
ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
// Test both TLS 1.2 full and resumption handshakes.
resumeSession: true,
flags: flags,
// If not specified, runner and shim both implicitly expect ALPS
// is not negotiated.
})
}
}
// Test QUIC transport params
if protocol == quic {
// Client sends params
for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
for _, serverSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
useCodepointFlag := "0"
if clientConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
flags := []string{
"-quic-transport-params",
base64FlagValue([]byte{1, 2}),
"-quic-use-legacy-codepoint", useCodepointFlag,
}
expectations := connectionExpectations{
quicTransportParams: []byte{1, 2},
}
shouldFail := false
expectedError := ""
expectedLocalError := ""
if clientConfig == QUICUseCodepointLegacy {
expectations = connectionExpectations{
quicTransportParamsLegacy: []byte{1, 2},
}
}
if serverSends != clientConfig {
expectations = connectionExpectations{}
shouldFail = true
if serverSends == QUICUseCodepointNeither {
expectedError = ":MISSING_EXTENSION:"
} else {
expectedLocalError = "remote error: unsupported extension"
}
} else {
flags = append(flags,
"-expect-quic-transport-params",
base64FlagValue([]byte{3, 4}))
}
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: fmt.Sprintf("QUICTransportParams-Client-Client%s-Server%s-%s", clientConfig, serverSends, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{3, 4},
QUICTransportParamsUseLegacyCodepoint: serverSends,
},
flags: flags,
expectations: expectations,
shouldFail: shouldFail,
expectedError: expectedError,
expectedLocalError: expectedLocalError,
skipTransportParamsConfig: true,
})
}
}
// Server sends params
for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
expectations := connectionExpectations{
quicTransportParams: []byte{3, 4},
}
shouldFail := false
expectedError := ""
useCodepointFlag := "0"
if serverConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
expectations = connectionExpectations{
quicTransportParamsLegacy: []byte{3, 4},
}
}
flags := []string{
"-quic-transport-params",
base64FlagValue([]byte{3, 4}),
"-quic-use-legacy-codepoint", useCodepointFlag,
}
if clientSends != QUICUseCodepointBoth && clientSends != serverConfig {
expectations = connectionExpectations{}
shouldFail = true
expectedError = ":MISSING_EXTENSION:"
} else {
flags = append(flags,
"-expect-quic-transport-params",
base64FlagValue([]byte{1, 2}),
)
}
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: fmt.Sprintf("QUICTransportParams-Server-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
QUICTransportParamsUseLegacyCodepoint: clientSends,
},
flags: flags,
expectations: expectations,
shouldFail: shouldFail,
expectedError: expectedError,
skipTransportParamsConfig: true,
})
}
}
} else {
// Ensure non-QUIC client doesn't send QUIC transport parameters.
for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
useCodepointFlag := "0"
if clientConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
name: fmt.Sprintf("QUICTransportParams-Client-NotSentInNonQUIC-%s-%s", clientConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParamsUseLegacyCodepoint: clientConfig,
},
flags: []string{
"-max-version",
ver.shimFlag(protocol),
"-quic-transport-params",
base64FlagValue([]byte{3, 4}),
"-quic-use-legacy-codepoint", useCodepointFlag,
},
shouldFail: true,
expectedError: ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
skipTransportParamsConfig: true,
})
}
// Ensure non-QUIC server rejects codepoint 57 but ignores legacy 0xffa5.
for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
shouldFail := false
expectedLocalError := ""
useCodepointFlag := "0"
if serverConfig == QUICUseCodepointLegacy {
useCodepointFlag = "1"
}
if clientSends == QUICUseCodepointStandard || clientSends == QUICUseCodepointBoth {
shouldFail = true
expectedLocalError = "remote error: unsupported extension"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: fmt.Sprintf("QUICTransportParams-NonQUICServer-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
QUICTransportParams: []byte{1, 2},
QUICTransportParamsUseLegacyCodepoint: clientSends,
},
flags: []string{
"-quic-use-legacy-codepoint", useCodepointFlag,
},
shouldFail: shouldFail,
expectedLocalError: expectedLocalError,
skipTransportParamsConfig: true,
})
}
}
}
// Test ticket behavior.
// Resume with a corrupt ticket.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "CorruptTicket-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
FilterTicket: func(in []byte) ([]byte, error) {
in[len(in)-1] ^= 1
return in, nil
},
},
},
resumeSession: true,
expectResumeRejected: true,
})
// Test the ticket callbacks.
for _, aeadCallback := range []bool{false, true} {
flag := "-use-ticket-callback"
callbackSuffix := suffix
if aeadCallback {
flag = "-use-ticket-aead-callback"
callbackSuffix += "-AEAD"
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketCallback-" + callbackSuffix,
config: Config{
MaxVersion: ver.version,
},
resumeSession: true,
flags: []string{flag},
})
// Only the old callback supports renewal.
if !aeadCallback {
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketCallback-Renew-" + callbackSuffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
ExpectNewTicket: true,
},
},
flags: []string{flag, "-renew-ticket"},
resumeSession: true,
})
}
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketCallback-Skip-" + callbackSuffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
ExpectNoNonEmptyNewSessionTicket: true,
},
},
flags: []string{flag, "-skip-ticket"},
})
// Test that the ticket callback is only called once when everything before
// it in the ClientHello is asynchronous. This corrupts the ticket so
// certificate selection callbacks run.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketCallback-SingleCall-" + callbackSuffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
FilterTicket: func(in []byte) ([]byte, error) {
in[len(in)-1] ^= 1
return in, nil
},
},
},
resumeSession: true,
expectResumeRejected: true,
flags: []string{
flag,
"-async",
},
})
}
// Resume with various lengths of ticket session id.
if ver.version < VersionTLS13 {
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketSessionIDLength-0-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
EmptyTicketSessionID: true,
},
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketSessionIDLength-16-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
TicketSessionIDLength: 16,
},
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketSessionIDLength-32-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
TicketSessionIDLength: 32,
},
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "TicketSessionIDLength-33-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
TicketSessionIDLength: 33,
},
},
resumeSession: true,
shouldFail: true,
// The maximum session ID length is 32.
expectedError: ":CLIENTHELLO_PARSE_FAILED:",
})
}
// Basic DTLS-SRTP tests. Include fake profiles to ensure they
// are ignored.
if protocol == dtls {
testCases = append(testCases, testCase{
protocol: protocol,
name: "SRTP-Client-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
},
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "SRTP-Server-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
},
})
// Test that the MKI is ignored.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "SRTP-Server-IgnoreMKI-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{SRTP_AES128_CM_HMAC_SHA1_80},
Bugs: ProtocolBugs{
SRTPMasterKeyIdentifier: "bogus",
},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
},
})
// Test that SRTP isn't negotiated on the server if there were
// no matching profiles.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "SRTP-Server-NoMatch-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{100, 101, 102},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: 0,
},
})
// Test that the server returning an invalid SRTP profile is
// flagged as an error by the client.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SRTP-Client-NoMatch-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
SendSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_32,
},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80",
},
shouldFail: true,
expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:",
})
} else {
// DTLS-SRTP is not defined for other protocols. Configuring it
// on the client and server should ignore the extension.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SRTP-Client-Ignore-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: 0,
},
})
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "SRTP-Server-Ignore-" + suffix,
config: Config{
MaxVersion: ver.version,
SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
},
flags: []string{
"-srtp-profiles",
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
},
expectations: connectionExpectations{
srtpProtectionProfile: 0,
},
})
}
// Test SCT list.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SignedCertificateTimestampList-Client-" + suffix,
testType: clientTest,
config: Config{
MaxVersion: ver.version,
Credential: rsaCertificate.WithSCTList(testSCTList),
},
flags: []string{
"-enable-signed-cert-timestamps",
"-expect-signed-cert-timestamps",
base64FlagValue(testSCTList),
},
resumeSession: true,
})
var differentSCTList []byte
differentSCTList = append(differentSCTList, testSCTList...)
differentSCTList[len(differentSCTList)-1] ^= 1
// The SCT extension did not specify that it must only be sent on the inital handshake as it
// should have, so test that we tolerate but ignore it. This is only an issue pre-1.3, since
// SCTs are sent in the CertificateEntry message in 1.3, whereas they were previously sent
// in an extension in the ServerHello pre-1.3.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SendSCTListOnResume-" + suffix,
config: Config{
MaxVersion: ver.version,
Credential: rsaCertificate.WithSCTList(testSCTList),
Bugs: ProtocolBugs{
SendSCTListOnResume: differentSCTList,
},
},
flags: []string{
"-enable-signed-cert-timestamps",
"-expect-signed-cert-timestamps",
base64FlagValue(testSCTList),
},
resumeSession: true,
})
testCases = append(testCases, testCase{
protocol: protocol,
name: "SignedCertificateTimestampList-Server-" + suffix,
testType: serverTest,
config: Config{
MaxVersion: ver.version,
},
shimCertificate: rsaCertificate.WithSCTList(testSCTList),
expectations: connectionExpectations{
peerCertificate: rsaCertificate.WithSCTList(testSCTList),
},
resumeSession: true,
})
// Test empty SCT list.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SignedCertificateTimestampListEmpty-Client-" + suffix,
testType: clientTest,
config: Config{
MaxVersion: ver.version,
Credential: rsaCertificate.WithSCTList([]byte{0, 0}),
},
flags: []string{
"-enable-signed-cert-timestamps",
},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
// Test empty SCT in non-empty list.
testCases = append(testCases, testCase{
protocol: protocol,
name: "SignedCertificateTimestampListEmptySCT-Client-" + suffix,
testType: clientTest,
config: Config{
MaxVersion: ver.version,
Credential: rsaCertificate.WithSCTList([]byte{0, 6, 0, 2, 1, 2, 0, 0}),
},
flags: []string{
"-enable-signed-cert-timestamps",
},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
// Test that certificate-related extensions are not sent unsolicited.
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
name: "UnsolicitedCertificateExtensions-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
NoOCSPStapling: true,
NoSignedCertificateTimestamps: true,
},
},
shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
})
// Extension permutation should interact correctly with other extensions,
// HelloVerifyRequest, HelloRetryRequest, and ECH. SSLTest.PermuteExtensions
// in ssl_test.cc tests that the extensions are actually permuted. This
// tests the handshake still works.
//
// This test also tests that all our extensions interact with each other.
for _, ech := range []bool{false, true} {
if ech && ver.version < VersionTLS13 {
continue
}
test := testCase{
protocol: protocol,
name: "AllExtensions-Client-Permute",
skipQUICALPNConfig: true,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
Credential: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
Bugs: ProtocolBugs{
SendServerNameAck: true,
ExpectServerName: "example.com",
ExpectGREASE: true,
},
},
resumeSession: true,
flags: []string{
"-permute-extensions",
"-enable-grease",
"-enable-ocsp-stapling",
"-enable-signed-cert-timestamps",
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
"-host-name", "example.com",
},
}
if ech {
test.name += "-ECH"
echConfig := generateServerECHConfig(&ECHConfig{ConfigID: 42})
test.config.ServerECHConfigs = []ServerECHConfig{echConfig}
test.flags = append(test.flags,
"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
"-expect-ech-accept",
)
test.expectations.echAccepted = true
}
if ver.version >= VersionTLS13 {
// Trigger a HelloRetryRequest to test both ClientHellos. Note
// our DTLS tests always enable HelloVerifyRequest.
test.name += "-HelloRetryRequest"
// ALPS is only available on TLS 1.3.
test.config.ApplicationSettings = map[string][]byte{"proto": []byte("runner")}
test.flags = append(test.flags,
"-application-settings", "proto,shim",
"-alps-use-new-codepoint", "1",
"-expect-peer-application-settings", "runner")
test.expectations.peerApplicationSettings = []byte("shim")
}
if protocol == dtls {
test.config.SRTPProtectionProfiles = []uint16{SRTP_AES128_CM_HMAC_SHA1_80}
test.flags = append(test.flags, "-srtp-profiles", "SRTP_AES128_CM_SHA1_80")
test.expectations.srtpProtectionProfile = SRTP_AES128_CM_HMAC_SHA1_80
}
test.name += "-" + suffix
testCases = append(testCases, test)
}
}
}
testCases = append(testCases, testCase{
testType: clientTest,
name: "ClientHelloPadding",
config: Config{
Bugs: ProtocolBugs{
RequireClientHelloSize: 512,
},
},
// This hostname just needs to be long enough to push the
// ClientHello into F5's danger zone between 256 and 511 bytes
// long.
flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"},
})
// Test that illegal extensions in TLS 1.3 are rejected by the client if
// in ServerHello.
testCases = append(testCases, testCase{
name: "NPN-Forbidden-TLS13",
config: Config{
MaxVersion: VersionTLS13,
NextProtos: []string{"foo"},
Bugs: ProtocolBugs{
NegotiateNPNAtAllVersions: true,
},
},
flags: []string{"-select-next-proto", "foo"},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "EMS-Forbidden-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
NegotiateEMSAtAllVersions: true,
},
},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "RenegotiationInfo-Forbidden-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
NegotiateRenegotiationInfoAtAllVersions: true,
},
},
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "Ticket-Forbidden-TLS13",
config: Config{
MaxVersion: VersionTLS12,
},
resumeConfig: &Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
AdvertiseTicketExtension: true,
},
},
resumeSession: true,
shouldFail: true,
expectedError: ":ERROR_PARSING_EXTENSION:",
})
// Test that illegal extensions in TLS 1.3 are declined by the server if
// offered in ClientHello. The runner's server will fail if this occurs,
// so we exercise the offering path. (EMS and Renegotiation Info are
// implicit in every test.)
testCases = append(testCases, testCase{
testType: serverTest,
name: "NPN-Declined-TLS13",
config: Config{
MaxVersion: VersionTLS13,
NextProtos: []string{"bar"},
},
flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"},
})
// OpenSSL sends the status_request extension on resumption in TLS 1.2. Test that this is
// tolerated.
testCases = append(testCases, testCase{
name: "SendOCSPResponseOnResume-TLS12",
config: Config{
MaxVersion: VersionTLS12,
Credential: rsaCertificate.WithOCSP(testOCSPResponse),
Bugs: ProtocolBugs{
SendOCSPResponseOnResume: []byte("bogus"),
},
},
flags: []string{
"-enable-ocsp-stapling",
"-expect-ocsp-response",
base64FlagValue(testOCSPResponse),
},
resumeSession: true,
})
testCases = append(testCases, testCase{
name: "SendUnsolicitedOCSPOnCertificate-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendExtensionOnCertificate: testOCSPExtension,
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "SendUnsolicitedSCTOnCertificate-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendExtensionOnCertificate: testSCTExtension,
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
})
// Test that extensions on client certificates are never accepted.
testCases = append(testCases, testCase{
name: "SendExtensionOnClientCertificate-TLS13",
testType: serverTest,
config: Config{
MaxVersion: VersionTLS13,
Credential: &rsaCertificate,
Bugs: ProtocolBugs{
SendExtensionOnCertificate: testOCSPExtension,
},
},
flags: []string{
"-enable-ocsp-stapling",
"-require-any-client-certificate",
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "SendUnknownExtensionOnCertificate-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendExtensionOnCertificate: []byte{0x00, 0x7f, 0, 0},
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
})
// Test that extensions on intermediates are allowed but ignored.
testCases = append(testCases, testCase{
name: "IgnoreExtensionsOnIntermediates-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Credential: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
Bugs: ProtocolBugs{
// Send different values on the intermediate. This tests
// the intermediate's extensions do not override the
// leaf's.
SendOCSPOnIntermediates: testOCSPResponse2,
SendSCTOnIntermediates: testSCTList2,
},
},
flags: []string{
"-enable-ocsp-stapling",
"-expect-ocsp-response",
base64FlagValue(testOCSPResponse),
"-enable-signed-cert-timestamps",
"-expect-signed-cert-timestamps",
base64FlagValue(testSCTList),
},
resumeSession: true,
})
// Test that extensions are not sent on intermediates when configured
// only for a leaf.
testCases = append(testCases, testCase{
testType: serverTest,
name: "SendNoExtensionsOnIntermediate-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
ExpectNoExtensionsOnIntermediate: true,
},
},
shimCertificate: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
})
// Test that extensions are not sent on client certificates.
testCases = append(testCases, testCase{
name: "SendNoClientCertificateExtensions-TLS13",
config: Config{
MaxVersion: VersionTLS13,
ClientAuth: RequireAnyClientCert,
},
shimCertificate: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
})
testCases = append(testCases, testCase{
name: "SendDuplicateExtensionsOnCerts-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Credential: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
Bugs: ProtocolBugs{
SendDuplicateCertExtensions: true,
},
},
flags: []string{
"-enable-ocsp-stapling",
"-enable-signed-cert-timestamps",
},
resumeSession: true,
shouldFail: true,
expectedError: ":DUPLICATE_EXTENSION:",
})
testCases = append(testCases, testCase{
name: "SignedCertificateTimestampListInvalid-Server",
testType: serverTest,
shimCertificate: rsaCertificate.WithSCTList([]byte{0, 0}),
shouldFail: true,
expectedError: ":INVALID_SCT_LIST:",
})
}
func addUnknownExtensionTests() {
// Test an unknown extension from the server.
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnknownExtension-Client",
config: Config{
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
CustomExtension: "custom extension",
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnknownExtension-Client-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
CustomExtension: "custom extension",
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnknownUnencryptedExtension-Client-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
CustomUnencryptedExtension: "custom extension",
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
// The shim must send an alert, but alerts at this point do not
// get successfully decrypted by the runner.
expectedLocalError: "local error: bad record MAC",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnexpectedUnencryptedExtension-Client-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendUnencryptedALPN: "foo",
},
},
flags: []string{
"-advertise-alpn", "\x03foo\x03bar",
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
// The shim must send an alert, but alerts at this point do not
// get successfully decrypted by the runner.
expectedLocalError: "local error: bad record MAC",
})
// Test a known but unoffered extension from the server.
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnofferedExtension-Client",
config: Config{
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
SendALPN: "alpn",
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "UnofferedExtension-Client-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendALPN: "alpn",
},
},
shouldFail: true,
expectedError: ":UNEXPECTED_EXTENSION:",
expectedLocalError: "remote error: unsupported extension",
})
}
// Test that omitted and empty extensions blocks are tolerated.
func addOmitExtensionsTests() {
// Check the ExpectOmitExtensions setting works.
testCases = append(testCases, testCase{
testType: serverTest,
name: "ExpectOmitExtensions",
config: Config{
MinVersion: VersionTLS12,
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
ExpectOmitExtensions: true,
},
},
shouldFail: true,
expectedLocalError: "tls: ServerHello did not omit extensions",
})
for _, ver := range tlsVersions {
if ver.version > VersionTLS12 {
continue
}
testCases = append(testCases, testCase{
testType: serverTest,
name: "OmitExtensions-ClientHello-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
SessionTicketsDisabled: true,
Bugs: ProtocolBugs{
OmitExtensions: true,
// With no client extensions, the ServerHello must not have
// extensions. It should then omit the extensions field.
ExpectOmitExtensions: true,
},
},
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "EmptyExtensions-ClientHello-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
SessionTicketsDisabled: true,
Bugs: ProtocolBugs{
EmptyExtensions: true,
// With no client extensions, the ServerHello must not have
// extensions. It should then omit the extensions field.
ExpectOmitExtensions: true,
},
},
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "OmitExtensions-ServerHello-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
SessionTicketsDisabled: true,
Bugs: ProtocolBugs{
OmitExtensions: true,
// Disable all ServerHello extensions so
// OmitExtensions works.
NoExtendedMasterSecret: true,
NoRenegotiationInfo: true,
NoOCSPStapling: true,
NoSignedCertificateTimestamps: true,
},
},
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "EmptyExtensions-ServerHello-" + ver.name,
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
SessionTicketsDisabled: true,
Bugs: ProtocolBugs{
EmptyExtensions: true,
// Disable all ServerHello extensions so
// EmptyExtensions works.
NoExtendedMasterSecret: true,
NoRenegotiationInfo: true,
NoOCSPStapling: true,
NoSignedCertificateTimestamps: true,
},
},
})
}
}