Run extension tests at all protocols.
Change-Id: Ied0ef856de3a1ae2c65b10645fbce614726f4e20
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44989
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index f3847d6..1ac7da2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -6550,1771 +6550,1854 @@
}
func addExtensionTests() {
- // TODO(davidben): Extensions, where applicable, all move their server
- // halves to EncryptedExtensions in TLS 1.3. Duplicate each of these
- // tests for both. Also test interaction with 0-RTT when implemented.
-
// Repeat extensions tests at all versions.
- for _, ver := range tlsVersions {
- // Test that duplicate extensions are rejected.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "DuplicateExtensionClient-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- DuplicateExtension: true,
- },
- },
- shouldFail: true,
- expectedLocalError: "remote error: error decoding message",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "DuplicateExtensionServer-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- DuplicateExtension: true,
- },
- },
- shouldFail: true,
- expectedLocalError: "remote error: error decoding message",
- })
+ for _, protocol := range []protocol{tls, dtls, quic} {
+ for _, ver := range allVersions(protocol) {
+ suffix := fmt.Sprintf("%s-%s", protocol.String(), ver.name)
- // Test SNI.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ServerNameExtensionClient-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- ExpectServerName: "example.com",
- },
- },
- flags: []string{"-host-name", "example.com"},
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ServerNameExtensionClientMismatch-" + ver.name,
- 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{
- testType: clientTest,
- name: "ServerNameExtensionClientMissing-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- ExpectServerName: "missing.com",
- },
- },
- shouldFail: true,
- expectedLocalError: "tls: unexpected server name",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TolerateServerNameAck-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- SendServerNameAck: true,
- },
- },
- flags: []string{"-host-name", "example.com"},
- resumeSession: true,
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "UnsolicitedServerNameAck-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- SendServerNameAck: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- expectedLocalError: "remote error: unsupported extension",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ServerNameExtensionServer-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- ServerName: "example.com",
- },
- flags: []string{"-expect-server-name", "example.com"},
- resumeSession: true,
- })
-
- // Test ALPN.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPNClient-" + ver.name,
- 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{
- testType: clientTest,
- name: "ALPNClient-RejectUnknown-" + ver.name,
- 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{
- testType: clientTest,
- name: "ALPNClient-AllowUnknown-" + ver.name,
- 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{
- testType: serverTest,
- name: "ALPNServer-" + ver.name,
- 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,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPNServer-Decline-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"foo", "bar", "baz"},
- },
- flags: []string{"-decline-alpn"},
- expectations: connectionExpectations{
- noNextProto: true,
- },
- resumeSession: true,
- })
- // Test that the server implementation catches itself if the
- // callback tries to return an invalid empty ALPN protocol.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPNServer-SelectEmpty-" + ver.name,
- 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{
- testType: serverTest,
- name: "ALPNServer-Async-" + ver.name,
- 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{
- testType: clientTest,
- name: "ALPNClient-EmptyProtocolName-" + ver.name,
- 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{
- testType: serverTest,
- name: "ALPNServer-EmptyProtocolName-" + ver.name,
- 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 {
- // Test that the server prefers ALPN over NPN.
+ // Test that duplicate extensions are rejected.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPNServer-Preferred-" + ver.name,
- 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{
- testType: serverTest,
- name: "ALPNServer-Preferred-Swapped-" + ver.name,
- 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{
- name: "NegotiateALPNAndNPN-" + ver.name,
- 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{
- name: "NegotiateALPNAndNPN-Swapped-" + ver.name,
- 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 ver.version >= VersionTLS13 {
- testCases = append(testCases, testCase{
+ protocol: protocol,
testType: clientTest,
- protocol: quic,
- name: "QUIC-Client-ALPNMissingFromConfig-" + ver.name,
+ name: "DuplicateExtensionClient-" + suffix,
config: Config{
- MinVersion: ver.version,
MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ DuplicateExtension: true,
+ },
},
- skipQUICALPNConfig: true,
shouldFail: true,
- expectedError: ":MISSING_ALPN:",
+ 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,
- protocol: quic,
- name: "QUIC-Client-ALPNMissing-" + ver.name,
+ name: "ServerNameExtensionClient-" + suffix,
config: Config{
- MinVersion: ver.version,
MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ ExpectServerName: "example.com",
+ },
},
- flags: []string{
- "-advertise-alpn", "\x03foo",
- },
- skipQUICALPNConfig: true,
- shouldFail: true,
- expectedError: ":MISSING_ALPN:",
- expectedLocalError: "remote error: no application protocol",
+ flags: []string{"-host-name", "example.com"},
})
testCases = append(testCases, testCase{
- testType: serverTest,
- protocol: quic,
- name: "QUIC-Server-ALPNMissing-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- },
- skipQUICALPNConfig: true,
- shouldFail: true,
- expectedError: ":MISSING_ALPN:",
- expectedLocalError: "remote error: no application protocol",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- protocol: quic,
- name: "QUIC-Server-ALPNMismatch-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- NextProtos: []string{"foo"},
- },
- flags: []string{
- "-decline-alpn",
- },
- skipQUICALPNConfig: true,
- shouldFail: true,
- expectedError: ":MISSING_ALPN:",
- expectedLocalError: "remote error: no application protocol",
- })
- }
-
- // Test ALPS.
- if ver.version >= VersionTLS13 {
- // Test that client and server can negotiate ALPS, including
- // different values on resumption.
- testCases = append(testCases, testCase{
+ protocol: protocol,
testType: clientTest,
- name: "ALPS-Basic-Client-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
- },
- resumeConfig: &Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim1"),
- },
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: []byte("shim2"),
- },
- flags: []string{
- "-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",
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-Basic-Server-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
- },
- resumeConfig: &Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim1"),
- },
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: []byte("shim2"),
- },
- flags: []string{
- "-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",
- },
- })
-
- // TODO(davidben): This is a copy of ALPS-Basic-Server to test a
- // QUIC-specific ALPS issue. Remove this and instead run all the
- // tests in this function at all protocols.
- testCases = append(testCases, testCase{
- protocol: quic,
- testType: serverTest,
- name: "ALPS-Basic-Server-QUIC-" + ver.name,
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
- },
- resumeConfig: &Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim1"),
- },
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: []byte("shim2"),
- },
- flags: []string{
- "-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 the server can defer its ALPS configuration to the ALPN
- // selection callback.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-Basic-Server-Defer-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
- },
- resumeConfig: &Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim1"),
- },
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: []byte("shim2"),
- },
- flags: []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",
- },
- })
-
- // Test the client and server correctly handle empty settings.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-Empty-Client-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte{}},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte{},
- },
- flags: []string{
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
- "-application-settings", "proto,",
- "-expect-peer-application-settings", "",
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-Empty-Server-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte{}},
- },
- resumeSession: true,
- expectations: connectionExpectations{
- peerApplicationSettings: []byte{},
- },
- flags: []string{
- "-select-alpn", "proto",
- "-application-settings", "proto,",
- "-expect-peer-application-settings", "",
- },
- })
-
- // Test the client rejects application settings from the server on
- // protocols it doesn't have them.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-UnsupportedProtocol-Client-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto1"},
- ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
- Bugs: ProtocolBugs{
- AlwaysNegotiateApplicationSettings: true,
- },
- },
- // The client supports ALPS with "proto2", but not "proto1".
- flags: []string{
- "-advertise-alpn", "\x06proto1\x06proto2",
- "-application-settings", "proto2,shim",
- "-expect-alpn", "proto1",
- },
- // The server sends ALPS with "proto1", which is invalid.
- shouldFail: true,
- expectedError: ":INVALID_ALPN_PROTOCOL:",
- expectedLocalError: "remote error: illegal parameter",
- })
-
- // Test the server declines ALPS if it doesn't support it for the
- // specified protocol.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-UnsupportedProtocol-Server-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto1"},
- ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
- },
- // The server supports ALPS with "proto2", but not "proto1".
- flags: []string{
- "-select-alpn", "proto1",
- "-application-settings", "proto2,shim",
- },
- })
-
- // Test that the server rejects a missing application_settings extension.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-OmitClientApplicationSettings-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
- Bugs: ProtocolBugs{
- OmitClientApplicationSettings: true,
- },
- },
- flags: []string{
- "-select-alpn", "proto",
- "-application-settings", "proto,shim",
- },
- // The runner is a client, so it only processes the shim's alert
- // after checking connection state.
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim"),
- },
- shouldFail: true,
- expectedError: ":MISSING_EXTENSION:",
- expectedLocalError: "remote error: missing extension",
- })
-
- // Test that the server rejects a missing EncryptedExtensions message.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-OmitClientEncryptedExtensions-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
- Bugs: ProtocolBugs{
- OmitClientEncryptedExtensions: true,
- },
- },
- flags: []string{
- "-select-alpn", "proto",
- "-application-settings", "proto,shim",
- },
- // The runner is a client, so it only processes the shim's alert
- // after checking connection state.
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim"),
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- expectedLocalError: "remote error: unexpected message",
- })
-
- // Test that the server rejects an unexpected EncryptedExtensions message.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "UnexpectedClientEncryptedExtensions-" + ver.name,
+ name: "ServerNameExtensionClientMismatch-" + suffix,
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
- AlwaysSendClientEncryptedExtensions: true,
+ 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,
- expectedError: ":UNEXPECTED_MESSAGE:",
- expectedLocalError: "remote error: unexpected message",
+ expectedLocalError: "tls: unexpected server name",
})
-
- // Test that the server rejects an unexpected extension in an
- // expected EncryptedExtensions message.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ExtraClientEncryptedExtension-" + ver.name,
+ protocol: protocol,
+ testType: clientTest,
+ name: "TolerateServerNameAck-" + suffix,
config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ MaxVersion: ver.version,
Bugs: ProtocolBugs{
- SendExtraClientEncryptedExtension: true,
+ SendServerNameAck: true,
},
},
- flags: []string{
- "-select-alpn", "proto",
- "-application-settings", "proto,shim",
- },
- // The runner is a client, so it only processes the shim's alert
- // after checking connection state.
- expectations: connectionExpectations{
- peerApplicationSettings: []byte("shim"),
+ 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,
+ })
- // Test that ALPS is carried over on 0-RTT.
- for _, empty := range []bool{false, true} {
- suffix := ver.name
- runnerSettings := "runner"
- shimSettings := "shim"
- if empty {
- suffix = "Empty-" + ver.name
- runnerSettings = ""
- shimSettings = ""
- }
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-EarlyData-Client-" + suffix,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ // 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",
},
- resumeSession: true,
- earlyData: true,
+ },
+ 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 = ":MISSING_ALPN:"
+ 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,
+ })
+
+ // 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{
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
+ "-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
+ "-select-alpn", "foo",
+ "-advertise-npn", "\x03foo\x03bar\x03baz",
},
expectations: connectionExpectations{
- peerApplicationSettings: []byte(shimSettings),
+ 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: ":MISSING_ALPN:",
+ })
+ 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: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
})
testCases = append(testCases, testCase{
testType: serverTest,
- name: "ALPS-EarlyData-Server-" + suffix,
+ protocol: protocol,
+ name: "Server-ALPNMissing-" + suffix,
config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
},
- resumeSession: true,
- earlyData: true,
- flags: []string{
- "-select-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
- },
- expectations: connectionExpectations{
- peerApplicationSettings: []byte(shimSettings),
- },
+ skipQUICALPNConfig: true,
+ shouldFail: true,
+ expectedError: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
})
-
- // Sending application settings in 0-RTT handshakes is forbidden.
testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-" + suffix,
+ 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: ":MISSING_ALPN:",
+ expectedLocalError: "remote error: no application protocol",
+ })
+ }
+
+ // Test ALPS.
+ if ver.version >= VersionTLS13 {
+ // Test that client and server can negotiate ALPS, including
+ // different values on resumption.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-Basic-Client-" + suffix,
+ skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
- Bugs: ProtocolBugs{
- SendApplicationSettingsWithEarlyData: true,
- },
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
+ },
+ resumeConfig: &Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
},
resumeSession: true,
- earlyData: true,
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte("shim1"),
+ },
+ resumeExpectations: &connectionExpectations{
+ peerApplicationSettings: []byte("shim2"),
+ },
flags: []string{
"-advertise-alpn", "\x05proto",
"-expect-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
+ "-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",
},
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-Basic-Server-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
+ },
+ resumeConfig: &Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
+ },
+ resumeSession: true,
expectations: connectionExpectations{
- peerApplicationSettings: []byte(shimSettings),
+ peerApplicationSettings: []byte("shim1"),
},
+ resumeExpectations: &connectionExpectations{
+ peerApplicationSettings: []byte("shim2"),
+ },
+ flags: []string{
+ "-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 the server can defer its ALPS configuration to the ALPN
+ // selection callback.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-Basic-Server-Defer-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
+ },
+ resumeConfig: &Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
+ },
+ resumeSession: true,
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte("shim1"),
+ },
+ resumeExpectations: &connectionExpectations{
+ peerApplicationSettings: []byte("shim2"),
+ },
+ flags: []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",
+ },
+ })
+
+ // Test the client and server correctly handle empty settings.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-Empty-Client-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte{}},
+ },
+ resumeSession: true,
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte{},
+ },
+ flags: []string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto,",
+ "-expect-peer-application-settings", "",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-Empty-Server-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte{}},
+ },
+ resumeSession: true,
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte{},
+ },
+ flags: []string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto,",
+ "-expect-peer-application-settings", "",
+ },
+ })
+
+ // 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: "ALPS-UnsupportedProtocol-Client-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto1"},
+ ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
+ Bugs: ProtocolBugs{
+ AlwaysNegotiateApplicationSettings: true,
+ },
+ },
+ // The client supports ALPS with "proto2", but not "proto1".
+ flags: []string{
+ "-advertise-alpn", "\x06proto1\x06proto2",
+ "-application-settings", "proto2,shim",
+ "-expect-alpn", "proto1",
+ },
+ // The server sends ALPS with "proto1", which is invalid.
shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+ expectedError: ":INVALID_ALPN_PROTOCOL:",
expectedLocalError: "remote error: illegal parameter",
})
+
+ // Test the server declines ALPS if it doesn't support it for the
+ // specified protocol.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-" + suffix,
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-UnsupportedProtocol-Server-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto1"},
+ ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
+ },
+ // The server supports ALPS with "proto2", but not "proto1".
+ flags: []string{
+ "-select-alpn", "proto1",
+ "-application-settings", "proto2,shim",
+ },
+ })
+
+ // Test that the server rejects a missing application_settings extension.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-OmitClientApplicationSettings-" + suffix,
+ skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
Bugs: ProtocolBugs{
- SendApplicationSettingsWithEarlyData: true,
+ OmitClientApplicationSettings: true,
},
},
- resumeSession: true,
- earlyData: true,
flags: []string{
"-select-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
+ "-application-settings", "proto,shim",
},
+ // The runner is a client, so it only processes the shim's alert
+ // after checking connection state.
expectations: connectionExpectations{
- peerApplicationSettings: []byte(shimSettings),
+ peerApplicationSettings: []byte("shim"),
+ },
+ 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: "ALPS-OmitClientEncryptedExtensions-" + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ Bugs: ProtocolBugs{
+ OmitClientEncryptedExtensions: true,
+ },
+ },
+ flags: []string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto,shim",
+ },
+ // The runner is a client, so it only processes the shim's alert
+ // after checking connection state.
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte("shim"),
},
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"}
- 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")
- }
-
- // The client should not offer early data.
+ // Test that the server rejects an unexpected EncryptedExtensions message.
testCases = append(testCases, testCase{
- testType: clientTest,
- name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s", test.name, ver.name),
+ protocol: protocol,
+ testType: serverTest,
+ name: "UnexpectedClientEncryptedExtensions-" + suffix,
+ config: Config{
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ AlwaysSendClientEncryptedExtensions: true,
+ },
+ },
+ 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: "ExtraClientEncryptedExtension-" + suffix,
+ skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
- MaxEarlyDataSize: 16384,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ Bugs: ProtocolBugs{
+ SendExtraClientEncryptedExtension: true,
+ },
+ },
+ flags: []string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto,shim",
+ },
+ // The runner is a client, so it only processes the shim's alert
+ // after checking connection state.
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte("shim"),
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ expectedLocalError: "remote error: unsupported extension",
+ })
+
+ // Test that ALPS is carried over on 0-RTT.
+ for _, empty := range []bool{false, true} {
+ maybeEmpty := ""
+ runnerSettings := "runner"
+ shimSettings := "shim"
+ if empty {
+ maybeEmpty = "Empty-"
+ runnerSettings = ""
+ shimSettings = ""
+ }
+
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-EarlyData-Client-" + maybeEmpty + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: []string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ },
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte(shimSettings),
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-EarlyData-Server-" + maybeEmpty + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: []string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ },
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte(shimSettings),
+ },
+ })
+
+ // Sending application settings in 0-RTT handshakes is forbidden.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-" + maybeEmpty + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ Bugs: ProtocolBugs{
+ SendApplicationSettingsWithEarlyData: true,
+ },
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: []string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ },
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte(shimSettings),
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+ expectedLocalError: "remote error: illegal parameter",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-" + maybeEmpty + suffix,
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ Bugs: ProtocolBugs{
+ SendApplicationSettingsWithEarlyData: true,
+ },
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: []string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ },
+ expectations: connectionExpectations{
+ peerApplicationSettings: []byte(shimSettings),
+ },
+ 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"}
+ 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")
+ }
+
+ // The client should not offer early data.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s", test.name, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ MaxEarlyDataSize: 16384,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ },
+ 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: connectionExpectations{
+ peerApplicationSettings: test.initialSettings,
+ },
+ resumeExpectations: &connectionExpectations{
+ peerApplicationSettings: test.resumeSettings,
+ },
+ })
+
+ // The server should reject early data.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s", test.name, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ },
+ resumeSession: true,
+ earlyData: true,
+ expectEarlyDataRejected: true,
+ flags: append([]string{
+ "-select-alpn", "proto",
+ }, flags...),
+ expectations: connectionExpectations{
+ peerApplicationSettings: test.initialSettings,
+ },
+ resumeExpectations: &connectionExpectations{
+ peerApplicationSettings: test.resumeSettings,
+ },
+ })
+ }
+ } else {
+ // Test the client rejects the ALPS extension if the server
+ // negotiated TLS 1.2 or below.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-Reject-Client-" + suffix,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"foo"},
+ ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
+ Bugs: ProtocolBugs{
+ AlwaysNegotiateApplicationSettings: true,
+ },
+ },
+ flags: []string{
+ "-advertise-alpn", "\x03foo",
+ "-expect-alpn", "foo",
+ "-application-settings", "foo,shim",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ expectedLocalError: "remote error: unsupported extension",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "ALPS-Reject-Client-Resume-" + suffix,
+ config: Config{
+ MaxVersion: ver.version,
+ },
+ resumeConfig: &Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"foo"},
+ ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
+ Bugs: ProtocolBugs{
+ AlwaysNegotiateApplicationSettings: true,
+ },
},
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: connectionExpectations{
- peerApplicationSettings: test.initialSettings,
+ flags: []string{
+ "-on-resume-advertise-alpn", "\x03foo",
+ "-on-resume-expect-alpn", "foo",
+ "-on-resume-application-settings", "foo,shim",
},
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: test.resumeSettings,
- },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ expectedLocalError: "remote error: unsupported extension",
})
- // The server should reject early data.
+ // Test the server declines ALPS if it negotiates TLS 1.2 or below.
testCases = append(testCases, testCase{
+ protocol: protocol,
testType: serverTest,
- name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s", test.name, ver.name),
+ name: "ALPS-Decline-Server-" + suffix,
config: Config{
MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ NextProtos: []string{"foo"},
+ ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
},
- resumeSession: true,
- earlyData: true,
- expectEarlyDataRejected: true,
- flags: append([]string{
- "-select-alpn", "proto",
- }, flags...),
- expectations: connectionExpectations{
- peerApplicationSettings: test.initialSettings,
+ // Test both TLS 1.2 full and resumption handshakes.
+ resumeSession: true,
+ flags: []string{
+ "-select-alpn", "foo",
+ "-application-settings", "foo,shim",
},
- resumeExpectations: &connectionExpectations{
- peerApplicationSettings: test.resumeSettings,
- },
+ // If not specified, runner and shim both implicitly expect ALPS
+ // is not negotiated.
})
}
- } else {
- // Test the client rejects the ALPS extension if the server
- // negotiated TLS 1.2 or below.
+
+ // Test Token Binding.
+ if protocol != dtls {
+ const maxTokenBindingVersion = 16
+ const minTokenBindingVersion = 13
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ expectations: connectionExpectations{
+ tokenBinding: true,
+ tokenBindingParam: 2,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-UnsupportedParam-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{3},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-OldVersion-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: minTokenBindingVersion - 1,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-NewVersion-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: maxTokenBindingVersion + 1,
+ },
+ expectations: connectionExpectations{
+ tokenBinding: true,
+ tokenBindingParam: 2,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-NoParams-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-RepeatedParam" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2, 2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ expectations: connectionExpectations{
+ tokenBinding: true,
+ tokenBindingParam: 2,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-Unexpected-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-ExtraParams-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2, 1},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-NoParams-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-WrongParam-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{3},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-OldVersion-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: minTokenBindingVersion - 1,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-MinVersion-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: minTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ "-expect-token-binding-param",
+ "2",
+ },
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-VersionTooNew-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion + 1,
+ ExpectTokenBindingParams: []byte{0, 1, 2},
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
+ },
+ shouldFail: true,
+ expectedError: "ERROR_PARSING_EXTENSION",
+ })
+ if ver.version < VersionTLS13 {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-NoEMS-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{2, 1, 0},
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: true,
+ },
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-NoEMS-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ Bugs: ProtocolBugs{
+ NoExtendedMasterSecret: true,
+ },
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-Client-NoRI-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{2, 1, 0},
+ Bugs: ProtocolBugs{
+ NoRenegotiationInfo: true,
+ },
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-Server-NoRI-" + suffix,
+
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ Bugs: ProtocolBugs{
+ NoRenegotiationInfo: true,
+ },
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
+ })
+ } else {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "TokenBinding-WithEarlyDataFails-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ ExpectTokenBindingParams: []byte{2, 1, 0},
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TokenBinding-EarlyDataRejected-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ TokenBindingParams: []byte{0, 1, 2},
+ TokenBindingVersion: maxTokenBindingVersion,
+ },
+ resumeSession: true,
+ earlyData: true,
+ expectEarlyDataRejected: true,
+ expectations: connectionExpectations{
+ tokenBinding: true,
+ tokenBindingParam: 2,
+ },
+ flags: []string{
+ "-token-binding-params",
+ base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ "-on-retry-expect-early-data-reason", "token_binding",
+ },
+ })
+ }
+ }
+
+ // Test QUIC transport params
+ if protocol == quic {
+ // Client sends params
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ protocol: protocol,
+ name: "QUICTransportParams-Client-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ QUICTransportParams: []byte{1, 2},
+ },
+ flags: []string{
+ "-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{3, 4}),
+ "-expect-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{1, 2}),
+ },
+ expectations: connectionExpectations{
+ quicTransportParams: []byte{3, 4},
+ },
+ skipTransportParamsConfig: true,
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ protocol: protocol,
+ name: "QUICTransportParams-Client-RejectMissing-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{3, 4}),
+ },
+ shouldFail: true,
+ expectedError: ":MISSING_EXTENSION:",
+ skipTransportParamsConfig: true,
+ })
+ // Server sends params
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: protocol,
+ name: "QUICTransportParams-Server-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ QUICTransportParams: []byte{1, 2},
+ },
+ flags: []string{
+ "-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{3, 4}),
+ "-expect-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{1, 2}),
+ },
+ expectations: connectionExpectations{
+ quicTransportParams: []byte{3, 4},
+ },
+ skipTransportParamsConfig: true,
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: protocol,
+ name: "QUICTransportParams-Server-RejectMissing-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{3, 4}),
+ },
+ expectations: connectionExpectations{
+ quicTransportParams: []byte{3, 4},
+ },
+ shouldFail: true,
+ expectedError: ":MISSING_EXTENSION:",
+ skipTransportParamsConfig: true,
+ })
+ } else {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "QUICTransportParams-Client-NotSentInNonQUIC-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-max-version",
+ strconv.Itoa(int(ver.versionWire)),
+ "-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{3, 4}),
+ },
+ shouldFail: true,
+ expectedError: ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
+ skipTransportParamsConfig: true,
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "QUICTransportParams-Server-RejectedInNonQUIC-" + suffix,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ QUICTransportParams: []byte{1, 2},
+ },
+ flags: []string{
+ "-expect-quic-transport-params",
+ base64.StdEncoding.EncodeToString([]byte{1, 2}),
+ },
+ shouldFail: true,
+ expectedLocalError: "remote error: unsupported extension",
+ skipTransportParamsConfig: true,
+ })
+ }
+
+ // Test ticket behavior.
+
+ // Resume with a corrupt ticket.
testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-Reject-Client-" + ver.name,
+ protocol: protocol,
+ testType: serverTest,
+ name: "CorruptTicket-" + suffix,
config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"foo"},
- ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
+ MaxVersion: ver.version,
Bugs: ProtocolBugs{
- AlwaysNegotiateApplicationSettings: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in[len(in)-1] ^= 1
+ return in, nil
+ },
},
},
- flags: []string{
- "-advertise-alpn", "\x03foo",
- "-expect-alpn", "foo",
- "-application-settings", "foo,shim",
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- expectedLocalError: "remote error: unsupported extension",
+ resumeSession: true,
+ expectResumeRejected: true,
})
+ // Test the ticket callback, with and without renewal.
testCases = append(testCases, testCase{
- testType: clientTest,
- name: "ALPS-Reject-Client-Resume-" + ver.name,
+ protocol: protocol,
+ testType: serverTest,
+ name: "TicketCallback-" + suffix,
config: Config{
MaxVersion: ver.version,
},
- resumeConfig: &Config{
- MaxVersion: ver.version,
- NextProtos: []string{"foo"},
- ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
+ resumeSession: true,
+ flags: []string{"-use-ticket-callback"},
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "TicketCallback-Renew-" + suffix,
+ config: Config{
+ MaxVersion: ver.version,
Bugs: ProtocolBugs{
- AlwaysNegotiateApplicationSettings: true,
+ ExpectNewTicket: true,
},
},
+ flags: []string{"-use-ticket-callback", "-renew-ticket"},
+ resumeSession: true,
+ })
+
+ // 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-" + 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,
+ flags: []string{
+ "-use-ticket-callback",
+ "-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: ":DECODE_ERROR:",
+ })
+ }
+
+ // 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{
+ SRTPMasterKeyIdentifer: "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:",
+ })
+ }
+
+ // Test SCT list.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "SignedCertificateTimestampList-Client-" + suffix,
+ testType: clientTest,
+ config: Config{
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-enable-signed-cert-timestamps",
+ "-expect-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
resumeSession: true,
- flags: []string{
- "-on-resume-advertise-alpn", "\x03foo",
- "-on-resume-expect-alpn", "foo",
- "-on-resume-application-settings", "foo,shim",
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- expectedLocalError: "remote error: unsupported extension",
})
- // Test the server declines ALPS if it negotiates TLS 1.2 or below.
+ 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 resumption as it
+ // should have, so test that we tolerate but ignore it.
testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ALPS-Decline-Server-" + ver.name,
+ protocol: protocol,
+ name: "SendSCTListOnResume-" + suffix,
config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"foo"},
- ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
- },
- // Test both TLS 1.2 full and resumption handshakes.
- resumeSession: true,
- flags: []string{
- "-select-alpn", "foo",
- "-application-settings", "foo,shim",
- },
- // If not specified, runner and shim both implicitly expect ALPS
- // is not negotiated.
- })
- }
-
- // Test Token Binding.
-
- const maxTokenBindingVersion = 16
- const minTokenBindingVersion = 13
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: maxTokenBindingVersion,
- },
- expectations: connectionExpectations{
- tokenBinding: true,
- tokenBindingParam: 2,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expect-token-binding-param",
- "2",
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-UnsupportedParam-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{3},
- TokenBindingVersion: maxTokenBindingVersion,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-OldVersion-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: minTokenBindingVersion - 1,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-NewVersion-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: maxTokenBindingVersion + 1,
- },
- expectations: connectionExpectations{
- tokenBinding: true,
- tokenBindingParam: 2,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expect-token-binding-param",
- "2",
- },
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-NoParams-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{},
- TokenBindingVersion: maxTokenBindingVersion,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-RepeatedParam" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2, 2},
- TokenBindingVersion: maxTokenBindingVersion,
- },
- expectations: connectionExpectations{
- tokenBinding: true,
- tokenBindingParam: 2,
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expect-token-binding-param",
- "2",
- },
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expect-token-binding-param",
- "2",
- },
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-Unexpected-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion,
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-ExtraParams-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2, 1},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expect-token-binding-param",
- "2",
- },
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-NoParams-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expect-token-binding-param",
- "2",
- },
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-WrongParam-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{3},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expect-token-binding-param",
- "2",
- },
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-OldVersion-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: minTokenBindingVersion - 1,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- },
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-MinVersion-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: minTokenBindingVersion,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expect-token-binding-param",
- "2",
- },
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-VersionTooNew-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion + 1,
- ExpectTokenBindingParams: []byte{0, 1, 2},
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- },
- shouldFail: true,
- expectedError: "ERROR_PARSING_EXTENSION",
- })
- if ver.version < VersionTLS13 {
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-NoEMS-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{2, 1, 0},
+ MaxVersion: ver.version,
Bugs: ProtocolBugs{
- NoExtendedMasterSecret: true,
+ SendSCTListOnResume: differentSCTList,
},
},
flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-NoEMS-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: maxTokenBindingVersion,
- Bugs: ProtocolBugs{
- NoExtendedMasterSecret: true,
- },
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-Client-NoRI-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{2, 1, 0},
- Bugs: ProtocolBugs{
- NoRenegotiationInfo: true,
- },
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TokenBinding-Server-NoRI-" + ver.name,
-
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: maxTokenBindingVersion,
- Bugs: ProtocolBugs{
- NoRenegotiationInfo: true,
- },
- },
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":NEGOTIATED_TB_WITHOUT_EMS_OR_RI:",
- })
- } else {
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TokenBinding-WithEarlyDataFails-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{2},
- TokenBindingVersion: maxTokenBindingVersion,
- ExpectTokenBindingParams: []byte{2, 1, 0},
+ "-enable-signed-cert-timestamps",
+ "-expect-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
},
resumeSession: true,
- earlyData: true,
- flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
})
+
testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "SignedCertificateTimestampList-Server-" + suffix,
testType: serverTest,
- name: "TokenBinding-EarlyDataRejected-" + ver.name,
config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- TokenBindingParams: []byte{0, 1, 2},
- TokenBindingVersion: maxTokenBindingVersion,
+ MaxVersion: ver.version,
},
- resumeSession: true,
- earlyData: true,
- expectEarlyDataRejected: true,
+ flags: []string{
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
expectations: connectionExpectations{
- tokenBinding: true,
- tokenBindingParam: 2,
+ sctList: testSCTList,
+ },
+ resumeSession: true,
+ })
+
+ emptySCTListCert := *testCerts[0].cert
+ emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0}
+
+ // Test empty SCT list.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "SignedCertificateTimestampListEmpty-Client-" + suffix,
+ testType: clientTest,
+ config: Config{
+ MaxVersion: ver.version,
+ Certificates: []Certificate{emptySCTListCert},
},
flags: []string{
- "-token-binding-params",
- base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-on-retry-expect-early-data-reason", "token_binding",
+ "-enable-signed-cert-timestamps",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+
+ emptySCTCert := *testCerts[0].cert
+ emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0}
+
+ // Test empty SCT in non-empty list.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "SignedCertificateTimestampListEmptySCT-Client-" + suffix,
+ testType: clientTest,
+ config: Config{
+ MaxVersion: ver.version,
+ Certificates: []Certificate{emptySCTCert},
+ },
+ 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,
+ },
+ },
+ flags: []string{
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
},
})
}
-
- // Test QUIC transport params
- if ver.version >= VersionTLS13 {
- // Client sends params
- testCases = append(testCases, testCase{
- testType: clientTest,
- protocol: quic,
- name: "QUICTransportParams-Client-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- QUICTransportParams: []byte{1, 2},
- },
- flags: []string{
- "-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{3, 4}),
- "-expect-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{1, 2}),
- },
- expectations: connectionExpectations{
- quicTransportParams: []byte{3, 4},
- },
- skipTransportParamsConfig: true,
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- protocol: quic,
- name: "QUICTransportParams-Client-RejectMissing-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- },
- flags: []string{
- "-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{3, 4}),
- },
- shouldFail: true,
- expectedError: ":MISSING_EXTENSION:",
- skipTransportParamsConfig: true,
- })
- // Server sends params
- testCases = append(testCases, testCase{
- testType: serverTest,
- protocol: quic,
- name: "QUICTransportParams-Server-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- QUICTransportParams: []byte{1, 2},
- },
- flags: []string{
- "-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{3, 4}),
- "-expect-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{1, 2}),
- },
- expectations: connectionExpectations{
- quicTransportParams: []byte{3, 4},
- },
- skipTransportParamsConfig: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- protocol: quic,
- name: "QUICTransportParams-Server-RejectMissing-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- },
- flags: []string{
- "-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{3, 4}),
- },
- expectations: connectionExpectations{
- quicTransportParams: []byte{3, 4},
- },
- shouldFail: true,
- expectedError: ":MISSING_EXTENSION:",
- skipTransportParamsConfig: true,
- })
- }
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "QUICTransportParams-Client-NotSentInTLS-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- },
- flags: []string{
- "-max-version",
- strconv.Itoa(int(ver.version)),
- "-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{3, 4}),
- },
- shouldFail: true,
- expectedError: ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
- skipTransportParamsConfig: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "QUICTransportParams-Server-RejectedInTLS-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- QUICTransportParams: []byte{1, 2},
- },
- flags: []string{
- "-expect-quic-transport-params",
- base64.StdEncoding.EncodeToString([]byte{1, 2}),
- },
- shouldFail: true,
- expectedLocalError: "remote error: unsupported extension",
- skipTransportParamsConfig: true,
- })
-
- // Test ticket behavior.
-
- // Resume with a corrupt ticket.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "CorruptTicket-" + ver.name,
- 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 callback, with and without renewal.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketCallback-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- },
- resumeSession: true,
- flags: []string{"-use-ticket-callback"},
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketCallback-Renew-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- ExpectNewTicket: true,
- },
- },
- flags: []string{"-use-ticket-callback", "-renew-ticket"},
- resumeSession: true,
- })
-
- // 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{
- testType: serverTest,
- name: "TicketCallback-SingleCall-" + ver.name,
- 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{
- "-use-ticket-callback",
- "-async",
- },
- })
-
- // Resume with various lengths of ticket session id.
- if ver.version < VersionTLS13 {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketSessionIDLength-0-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- EmptyTicketSessionID: true,
- },
- },
- resumeSession: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketSessionIDLength-16-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- TicketSessionIDLength: 16,
- },
- },
- resumeSession: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketSessionIDLength-32-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- TicketSessionIDLength: 32,
- },
- },
- resumeSession: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketSessionIDLength-33-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- TicketSessionIDLength: 33,
- },
- },
- resumeSession: true,
- shouldFail: true,
- // The maximum session ID length is 32.
- expectedError: ":DECODE_ERROR:",
- })
- }
-
- // Basic DTLS-SRTP tests. Include fake profiles to ensure they
- // are ignored.
- if ver.hasDTLS {
- testCases = append(testCases, testCase{
- protocol: dtls,
- name: "SRTP-Client-" + ver.name,
- 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: dtls,
- testType: serverTest,
- name: "SRTP-Server-" + ver.name,
- 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: dtls,
- testType: serverTest,
- name: "SRTP-Server-IgnoreMKI-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- SRTPProtectionProfiles: []uint16{SRTP_AES128_CM_HMAC_SHA1_80},
- Bugs: ProtocolBugs{
- SRTPMasterKeyIdentifer: "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: dtls,
- testType: serverTest,
- name: "SRTP-Server-NoMatch-" + ver.name,
- 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: dtls,
- name: "SRTP-Client-NoMatch-" + ver.name,
- 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:",
- })
- }
-
- // Test SCT list.
- testCases = append(testCases, testCase{
- name: "SignedCertificateTimestampList-Client-" + ver.name,
- testType: clientTest,
- config: Config{
- MaxVersion: ver.version,
- },
- flags: []string{
- "-enable-signed-cert-timestamps",
- "-expect-signed-cert-timestamps",
- base64.StdEncoding.EncodeToString(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 resumption as it
- // should have, so test that we tolerate but ignore it.
- testCases = append(testCases, testCase{
- name: "SendSCTListOnResume-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- SendSCTListOnResume: differentSCTList,
- },
- },
- flags: []string{
- "-enable-signed-cert-timestamps",
- "-expect-signed-cert-timestamps",
- base64.StdEncoding.EncodeToString(testSCTList),
- },
- resumeSession: true,
- })
-
- testCases = append(testCases, testCase{
- name: "SignedCertificateTimestampList-Server-" + ver.name,
- testType: serverTest,
- config: Config{
- MaxVersion: ver.version,
- },
- flags: []string{
- "-signed-cert-timestamps",
- base64.StdEncoding.EncodeToString(testSCTList),
- },
- expectations: connectionExpectations{
- sctList: testSCTList,
- },
- resumeSession: true,
- })
-
- emptySCTListCert := *testCerts[0].cert
- emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0}
-
- // Test empty SCT list.
- testCases = append(testCases, testCase{
- name: "SignedCertificateTimestampListEmpty-Client-" + ver.name,
- testType: clientTest,
- config: Config{
- MaxVersion: ver.version,
- Certificates: []Certificate{emptySCTListCert},
- },
- flags: []string{
- "-enable-signed-cert-timestamps",
- },
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
-
- emptySCTCert := *testCerts[0].cert
- emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0}
-
- // Test empty SCT in non-empty list.
- testCases = append(testCases, testCase{
- name: "SignedCertificateTimestampListEmptySCT-Client-" + ver.name,
- testType: clientTest,
- config: Config{
- MaxVersion: ver.version,
- Certificates: []Certificate{emptySCTCert},
- },
- 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{
- testType: serverTest,
- name: "UnsolicitedCertificateExtensions-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- NoOCSPStapling: true,
- NoSignedCertificateTimestamps: true,
- },
- },
- flags: []string{
- "-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
- "-signed-cert-timestamps",
- base64.StdEncoding.EncodeToString(testSCTList),
- },
- })
}
testCases = append(testCases, testCase{
diff --git a/util/testresult/testresult.go b/util/testresult/testresult.go
index 5226716..b353ece 100644
--- a/util/testresult/testresult.go
+++ b/util/testresult/testresult.go
@@ -18,6 +18,7 @@
import (
"encoding/json"
+ "fmt"
"os"
"time"
)
@@ -44,7 +45,7 @@
func (t *Results) addResult(name, result, expected string) {
if _, found := t.Tests[name]; found {
- panic(name)
+ panic(fmt.Sprintf("duplicate test name %q", name))
}
t.Tests[name] = Result{
Actual: result,