1// Copyright 2025 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package runner
16
17import (
18	"fmt"
19)
20
21func addExtensionTests() {
22	exampleCertificate := rootCA.Issue(X509Info{
23		PrivateKey: &ecdsaP256Key,
24		DNSNames:   []string{"example.com"},
25	}).ToCredential()
26
27	// Repeat extensions tests at all versions.
28	for _, protocol := range []protocol{tls, dtls, quic} {
29		for _, ver := range allVersions(protocol) {
30			suffix := fmt.Sprintf("%s-%s", protocol.String(), ver.name)
31
32			// Test that duplicate extensions are rejected.
33			testCases = append(testCases, testCase{
34				protocol: protocol,
35				testType: clientTest,
36				name:     "DuplicateExtensionClient-" + suffix,
37				config: Config{
38					MaxVersion: ver.version,
39					Bugs: ProtocolBugs{
40						DuplicateExtension: true,
41					},
42				},
43				shouldFail:         true,
44				expectedLocalError: "remote error: error decoding message",
45			})
46			testCases = append(testCases, testCase{
47				protocol: protocol,
48				testType: serverTest,
49				name:     "DuplicateExtensionServer-" + suffix,
50				config: Config{
51					MaxVersion: ver.version,
52					Bugs: ProtocolBugs{
53						DuplicateExtension: true,
54					},
55				},
56				shouldFail:         true,
57				expectedLocalError: "remote error: error decoding message",
58			})
59
60			// Test SNI.
61			testCases = append(testCases, testCase{
62				protocol: protocol,
63				testType: clientTest,
64				name:     "ServerNameExtensionClient-" + suffix,
65				config: Config{
66					MaxVersion: ver.version,
67					Bugs: ProtocolBugs{
68						ExpectServerName: "example.com",
69					},
70					Credential: &exampleCertificate,
71				},
72				flags: []string{"-host-name", "example.com"},
73			})
74			testCases = append(testCases, testCase{
75				protocol: protocol,
76				testType: clientTest,
77				name:     "ServerNameExtensionClientMismatch-" + suffix,
78				config: Config{
79					MaxVersion: ver.version,
80					Bugs: ProtocolBugs{
81						ExpectServerName: "mismatch.com",
82					},
83				},
84				flags:              []string{"-host-name", "example.com"},
85				shouldFail:         true,
86				expectedLocalError: "tls: unexpected server name",
87			})
88			testCases = append(testCases, testCase{
89				protocol: protocol,
90				testType: clientTest,
91				name:     "ServerNameExtensionClientMissing-" + suffix,
92				config: Config{
93					MaxVersion: ver.version,
94					Bugs: ProtocolBugs{
95						ExpectServerName: "missing.com",
96					},
97				},
98				shouldFail:         true,
99				expectedLocalError: "tls: unexpected server name",
100			})
101			testCases = append(testCases, testCase{
102				protocol: protocol,
103				testType: clientTest,
104				name:     "TolerateServerNameAck-" + suffix,
105				config: Config{
106					MaxVersion: ver.version,
107					Bugs: ProtocolBugs{
108						SendServerNameAck: true,
109					},
110					Credential: &exampleCertificate,
111				},
112				flags:         []string{"-host-name", "example.com"},
113				resumeSession: true,
114			})
115			testCases = append(testCases, testCase{
116				protocol: protocol,
117				testType: clientTest,
118				name:     "UnsolicitedServerNameAck-" + suffix,
119				config: Config{
120					MaxVersion: ver.version,
121					Bugs: ProtocolBugs{
122						SendServerNameAck: true,
123					},
124				},
125				shouldFail:         true,
126				expectedError:      ":UNEXPECTED_EXTENSION:",
127				expectedLocalError: "remote error: unsupported extension",
128			})
129			testCases = append(testCases, testCase{
130				protocol: protocol,
131				testType: serverTest,
132				name:     "ServerNameExtensionServer-" + suffix,
133				config: Config{
134					MaxVersion: ver.version,
135					ServerName: "example.com",
136				},
137				flags:         []string{"-expect-server-name", "example.com"},
138				resumeSession: true,
139				expectations: connectionExpectations{
140					serverNameAck: ptrTo(true),
141				},
142			})
143			testCases = append(testCases, testCase{
144				protocol: protocol,
145				testType: serverTest,
146				name:     "ServerNameExtensionServer-NoACK-" + suffix,
147				config: Config{
148					MaxVersion: ver.version,
149					ServerName: "example.com",
150				},
151				flags: []string{
152					"-expect-server-name", "example.com",
153					"-no-server-name-ack",
154				},
155				resumeSession: true,
156				expectations: connectionExpectations{
157					serverNameAck: ptrTo(false),
158				},
159			})
160
161			// Test ALPN.
162			testCases = append(testCases, testCase{
163				protocol:           protocol,
164				testType:           clientTest,
165				skipQUICALPNConfig: true,
166				name:               "ALPNClient-" + suffix,
167				config: Config{
168					MaxVersion: ver.version,
169					NextProtos: []string{"foo"},
170				},
171				flags: []string{
172					"-advertise-alpn", "\x03foo\x03bar\x03baz",
173					"-expect-alpn", "foo",
174				},
175				expectations: connectionExpectations{
176					nextProto:     "foo",
177					nextProtoType: alpn,
178				},
179				resumeSession: true,
180			})
181			testCases = append(testCases, testCase{
182				protocol:           protocol,
183				testType:           clientTest,
184				skipQUICALPNConfig: true,
185				name:               "ALPNClient-RejectUnknown-" + suffix,
186				config: Config{
187					MaxVersion: ver.version,
188					Bugs: ProtocolBugs{
189						SendALPN: "baz",
190					},
191				},
192				flags: []string{
193					"-advertise-alpn", "\x03foo\x03bar",
194				},
195				shouldFail:         true,
196				expectedError:      ":INVALID_ALPN_PROTOCOL:",
197				expectedLocalError: "remote error: illegal parameter",
198			})
199			testCases = append(testCases, testCase{
200				protocol:           protocol,
201				testType:           clientTest,
202				skipQUICALPNConfig: true,
203				name:               "ALPNClient-AllowUnknown-" + suffix,
204				config: Config{
205					MaxVersion: ver.version,
206					Bugs: ProtocolBugs{
207						SendALPN: "baz",
208					},
209				},
210				flags: []string{
211					"-advertise-alpn", "\x03foo\x03bar",
212					"-allow-unknown-alpn-protos",
213					"-expect-alpn", "baz",
214				},
215			})
216			testCases = append(testCases, testCase{
217				protocol:           protocol,
218				testType:           serverTest,
219				skipQUICALPNConfig: true,
220				name:               "ALPNServer-" + suffix,
221				config: Config{
222					MaxVersion: ver.version,
223					NextProtos: []string{"foo", "bar", "baz"},
224				},
225				flags: []string{
226					"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
227					"-select-alpn", "foo",
228				},
229				expectations: connectionExpectations{
230					nextProto:     "foo",
231					nextProtoType: alpn,
232				},
233				resumeSession: true,
234			})
235
236			var shouldDeclineALPNFail bool
237			var declineALPNError, declineALPNLocalError string
238			if protocol == quic {
239				// ALPN is mandatory in QUIC.
240				shouldDeclineALPNFail = true
241				declineALPNError = ":NO_APPLICATION_PROTOCOL:"
242				declineALPNLocalError = "remote error: no application protocol"
243			}
244			testCases = append(testCases, testCase{
245				protocol:           protocol,
246				testType:           serverTest,
247				skipQUICALPNConfig: true,
248				name:               "ALPNServer-Decline-" + suffix,
249				config: Config{
250					MaxVersion: ver.version,
251					NextProtos: []string{"foo", "bar", "baz"},
252				},
253				flags: []string{"-decline-alpn"},
254				expectations: connectionExpectations{
255					noNextProto: true,
256				},
257				resumeSession:      true,
258				shouldFail:         shouldDeclineALPNFail,
259				expectedError:      declineALPNError,
260				expectedLocalError: declineALPNLocalError,
261			})
262
263			testCases = append(testCases, testCase{
264				protocol:           protocol,
265				testType:           serverTest,
266				skipQUICALPNConfig: true,
267				name:               "ALPNServer-Reject-" + suffix,
268				config: Config{
269					MaxVersion: ver.version,
270					NextProtos: []string{"foo", "bar", "baz"},
271				},
272				flags:              []string{"-reject-alpn"},
273				shouldFail:         true,
274				expectedError:      ":NO_APPLICATION_PROTOCOL:",
275				expectedLocalError: "remote error: no application protocol",
276			})
277
278			// Test that the server implementation catches itself if the
279			// callback tries to return an invalid empty ALPN protocol.
280			testCases = append(testCases, testCase{
281				protocol:           protocol,
282				testType:           serverTest,
283				skipQUICALPNConfig: true,
284				name:               "ALPNServer-SelectEmpty-" + suffix,
285				config: Config{
286					MaxVersion: ver.version,
287					NextProtos: []string{"foo", "bar", "baz"},
288				},
289				flags: []string{
290					"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
291					"-select-empty-alpn",
292				},
293				shouldFail:         true,
294				expectedLocalError: "remote error: internal error",
295				expectedError:      ":INVALID_ALPN_PROTOCOL:",
296			})
297
298			// Test ALPN in async mode as well to ensure that extensions callbacks are only
299			// called once.
300			testCases = append(testCases, testCase{
301				protocol:           protocol,
302				testType:           serverTest,
303				skipQUICALPNConfig: true,
304				name:               "ALPNServer-Async-" + suffix,
305				config: Config{
306					MaxVersion: ver.version,
307					NextProtos: []string{"foo", "bar", "baz"},
308					// Prior to TLS 1.3, exercise the asynchronous session callback.
309					SessionTicketsDisabled: ver.version < VersionTLS13,
310				},
311				flags: []string{
312					"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
313					"-select-alpn", "foo",
314					"-async",
315				},
316				expectations: connectionExpectations{
317					nextProto:     "foo",
318					nextProtoType: alpn,
319				},
320				resumeSession: true,
321			})
322
323			var emptyString string
324			testCases = append(testCases, testCase{
325				protocol:           protocol,
326				testType:           clientTest,
327				skipQUICALPNConfig: true,
328				name:               "ALPNClient-EmptyProtocolName-" + suffix,
329				config: Config{
330					MaxVersion: ver.version,
331					NextProtos: []string{""},
332					Bugs: ProtocolBugs{
333						// A server returning an empty ALPN protocol
334						// should be rejected.
335						ALPNProtocol: &emptyString,
336					},
337				},
338				flags: []string{
339					"-advertise-alpn", "\x03foo",
340				},
341				shouldFail:    true,
342				expectedError: ":PARSE_TLSEXT:",
343			})
344			testCases = append(testCases, testCase{
345				protocol:           protocol,
346				testType:           serverTest,
347				skipQUICALPNConfig: true,
348				name:               "ALPNServer-EmptyProtocolName-" + suffix,
349				config: Config{
350					MaxVersion: ver.version,
351					// A ClientHello containing an empty ALPN protocol
352					// should be rejected.
353					NextProtos: []string{"foo", "", "baz"},
354				},
355				flags: []string{
356					"-select-alpn", "foo",
357				},
358				shouldFail:    true,
359				expectedError: ":PARSE_TLSEXT:",
360			})
361
362			// Test NPN and the interaction with ALPN.
363			if ver.version < VersionTLS13 && protocol == tls {
364				// Test that the server prefers ALPN over NPN.
365				testCases = append(testCases, testCase{
366					protocol: protocol,
367					testType: serverTest,
368					name:     "ALPNServer-Preferred-" + suffix,
369					config: Config{
370						MaxVersion: ver.version,
371						NextProtos: []string{"foo", "bar", "baz"},
372					},
373					flags: []string{
374						"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
375						"-select-alpn", "foo",
376						"-advertise-npn", "\x03foo\x03bar\x03baz",
377					},
378					expectations: connectionExpectations{
379						nextProto:     "foo",
380						nextProtoType: alpn,
381					},
382					resumeSession: true,
383				})
384				testCases = append(testCases, testCase{
385					protocol: protocol,
386					testType: serverTest,
387					name:     "ALPNServer-Preferred-Swapped-" + suffix,
388					config: Config{
389						MaxVersion: ver.version,
390						NextProtos: []string{"foo", "bar", "baz"},
391						Bugs: ProtocolBugs{
392							SwapNPNAndALPN: true,
393						},
394					},
395					flags: []string{
396						"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
397						"-select-alpn", "foo",
398						"-advertise-npn", "\x03foo\x03bar\x03baz",
399					},
400					expectations: connectionExpectations{
401						nextProto:     "foo",
402						nextProtoType: alpn,
403					},
404					resumeSession: true,
405				})
406
407				// Test that negotiating both NPN and ALPN is forbidden.
408				testCases = append(testCases, testCase{
409					protocol: protocol,
410					name:     "NegotiateALPNAndNPN-" + suffix,
411					config: Config{
412						MaxVersion: ver.version,
413						NextProtos: []string{"foo", "bar", "baz"},
414						Bugs: ProtocolBugs{
415							NegotiateALPNAndNPN: true,
416						},
417					},
418					flags: []string{
419						"-advertise-alpn", "\x03foo",
420						"-select-next-proto", "foo",
421					},
422					shouldFail:    true,
423					expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
424				})
425				testCases = append(testCases, testCase{
426					protocol: protocol,
427					name:     "NegotiateALPNAndNPN-Swapped-" + suffix,
428					config: Config{
429						MaxVersion: ver.version,
430						NextProtos: []string{"foo", "bar", "baz"},
431						Bugs: ProtocolBugs{
432							NegotiateALPNAndNPN: true,
433							SwapNPNAndALPN:      true,
434						},
435					},
436					flags: []string{
437						"-advertise-alpn", "\x03foo",
438						"-select-next-proto", "foo",
439					},
440					shouldFail:    true,
441					expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:",
442				})
443			}
444
445			// Test missing ALPN in QUIC
446			if protocol == quic {
447				testCases = append(testCases, testCase{
448					testType: clientTest,
449					protocol: protocol,
450					name:     "Client-ALPNMissingFromConfig-" + suffix,
451					config: Config{
452						MinVersion: ver.version,
453						MaxVersion: ver.version,
454					},
455					skipQUICALPNConfig: true,
456					shouldFail:         true,
457					expectedError:      ":NO_APPLICATION_PROTOCOL:",
458				})
459				testCases = append(testCases, testCase{
460					testType: clientTest,
461					protocol: protocol,
462					name:     "Client-ALPNMissing-" + suffix,
463					config: Config{
464						MinVersion: ver.version,
465						MaxVersion: ver.version,
466					},
467					flags: []string{
468						"-advertise-alpn", "\x03foo",
469					},
470					skipQUICALPNConfig: true,
471					shouldFail:         true,
472					expectedError:      ":NO_APPLICATION_PROTOCOL:",
473					expectedLocalError: "remote error: no application protocol",
474				})
475				testCases = append(testCases, testCase{
476					testType: serverTest,
477					protocol: protocol,
478					name:     "Server-ALPNMissing-" + suffix,
479					config: Config{
480						MinVersion: ver.version,
481						MaxVersion: ver.version,
482					},
483					skipQUICALPNConfig: true,
484					shouldFail:         true,
485					expectedError:      ":NO_APPLICATION_PROTOCOL:",
486					expectedLocalError: "remote error: no application protocol",
487				})
488				testCases = append(testCases, testCase{
489					testType: serverTest,
490					protocol: protocol,
491					name:     "Server-ALPNMismatch-" + suffix,
492					config: Config{
493						MinVersion: ver.version,
494						MaxVersion: ver.version,
495						NextProtos: []string{"foo"},
496					},
497					flags: []string{
498						"-decline-alpn",
499					},
500					skipQUICALPNConfig: true,
501					shouldFail:         true,
502					expectedError:      ":NO_APPLICATION_PROTOCOL:",
503					expectedLocalError: "remote error: no application protocol",
504				})
505			}
506
507			// Test ALPS.
508			if ver.version >= VersionTLS13 {
509				// Test basic client with different ALPS codepoint.
510				for _, alpsCodePoint := range []ALPSUseCodepoint{ALPSUseCodepointNew, ALPSUseCodepointOld} {
511					useAlpsCodepointFlag := "0"
512					if alpsCodePoint == ALPSUseCodepointNew {
513						useAlpsCodepointFlag = "1"
514					}
515
516					flags := []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
517					expectations := connectionExpectations{
518						peerApplicationSettingsOld: []byte("shim1"),
519					}
520					resumeExpectations := &connectionExpectations{
521						peerApplicationSettingsOld: []byte("shim2"),
522					}
523
524					if alpsCodePoint == ALPSUseCodepointNew {
525						expectations = connectionExpectations{
526							peerApplicationSettings: []byte("shim1"),
527						}
528						resumeExpectations = &connectionExpectations{
529							peerApplicationSettings: []byte("shim2"),
530						}
531					}
532
533					flags = append(flags,
534						"-advertise-alpn", "\x05proto",
535						"-expect-alpn", "proto",
536						"-on-initial-application-settings", "proto,shim1",
537						"-on-initial-expect-peer-application-settings", "runner1",
538						"-on-resume-application-settings", "proto,shim2",
539						"-on-resume-expect-peer-application-settings", "runner2")
540
541					// Test that server can negotiate ALPS, including different values
542					// on resumption.
543					testCases = append(testCases, testCase{
544						protocol:           protocol,
545						testType:           clientTest,
546						name:               fmt.Sprintf("ALPS-Basic-Client-%s-%s", alpsCodePoint, suffix),
547						skipQUICALPNConfig: true,
548						config: Config{
549							MaxVersion:          ver.version,
550							NextProtos:          []string{"proto"},
551							ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
552							ALPSUseNewCodepoint: alpsCodePoint,
553						},
554						resumeConfig: &Config{
555							MaxVersion:          ver.version,
556							NextProtos:          []string{"proto"},
557							ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
558							ALPSUseNewCodepoint: alpsCodePoint,
559						},
560						resumeSession:      true,
561						expectations:       expectations,
562						resumeExpectations: resumeExpectations,
563						flags:              flags,
564					})
565
566					// Test basic server with different ALPS codepoint.
567					flags = []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
568					expectations = connectionExpectations{
569						peerApplicationSettingsOld: []byte("shim1"),
570					}
571					resumeExpectations = &connectionExpectations{
572						peerApplicationSettingsOld: []byte("shim2"),
573					}
574
575					if alpsCodePoint == ALPSUseCodepointNew {
576						expectations = connectionExpectations{
577							peerApplicationSettings: []byte("shim1"),
578						}
579						resumeExpectations = &connectionExpectations{
580							peerApplicationSettings: []byte("shim2"),
581						}
582					}
583
584					flags = append(flags,
585						"-select-alpn", "proto",
586						"-on-initial-application-settings", "proto,shim1",
587						"-on-initial-expect-peer-application-settings", "runner1",
588						"-on-resume-application-settings", "proto,shim2",
589						"-on-resume-expect-peer-application-settings", "runner2")
590
591					// Test that server can negotiate ALPS, including different values
592					// on resumption.
593					testCases = append(testCases, testCase{
594						protocol:           protocol,
595						testType:           serverTest,
596						name:               fmt.Sprintf("ALPS-Basic-Server-%s-%s", alpsCodePoint, suffix),
597						skipQUICALPNConfig: true,
598						config: Config{
599							MaxVersion:          ver.version,
600							NextProtos:          []string{"proto"},
601							ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
602							ALPSUseNewCodepoint: alpsCodePoint,
603						},
604						resumeConfig: &Config{
605							MaxVersion:          ver.version,
606							NextProtos:          []string{"proto"},
607							ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
608							ALPSUseNewCodepoint: alpsCodePoint,
609						},
610						resumeSession:      true,
611						expectations:       expectations,
612						resumeExpectations: resumeExpectations,
613						flags:              flags,
614					})
615
616					// Try different ALPS codepoint for all the existing tests.
617					alpsFlags := []string{"-alps-use-new-codepoint", useAlpsCodepointFlag}
618					expectations = connectionExpectations{
619						peerApplicationSettingsOld: []byte("shim1"),
620					}
621					resumeExpectations = &connectionExpectations{
622						peerApplicationSettingsOld: []byte("shim2"),
623					}
624					if alpsCodePoint == ALPSUseCodepointNew {
625						expectations = connectionExpectations{
626							peerApplicationSettings: []byte("shim1"),
627						}
628						resumeExpectations = &connectionExpectations{
629							peerApplicationSettings: []byte("shim2"),
630						}
631					}
632
633					// Test that the server can defer its ALPS configuration to the ALPN
634					// selection callback.
635					testCases = append(testCases, testCase{
636						protocol:           protocol,
637						testType:           serverTest,
638						name:               fmt.Sprintf("ALPS-Basic-Server-Defer-%s-%s", alpsCodePoint, suffix),
639						skipQUICALPNConfig: true,
640						config: Config{
641							MaxVersion:          ver.version,
642							NextProtos:          []string{"proto"},
643							ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
644							ALPSUseNewCodepoint: alpsCodePoint,
645						},
646						resumeConfig: &Config{
647							MaxVersion:          ver.version,
648							NextProtos:          []string{"proto"},
649							ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
650							ALPSUseNewCodepoint: alpsCodePoint,
651						},
652						resumeSession:      true,
653						expectations:       expectations,
654						resumeExpectations: resumeExpectations,
655						flags: append([]string{
656							"-select-alpn", "proto",
657							"-defer-alps",
658							"-on-initial-application-settings", "proto,shim1",
659							"-on-initial-expect-peer-application-settings", "runner1",
660							"-on-resume-application-settings", "proto,shim2",
661							"-on-resume-expect-peer-application-settings", "runner2",
662						}, alpsFlags...),
663					})
664
665					expectations = connectionExpectations{
666						peerApplicationSettingsOld: []byte{},
667					}
668					if alpsCodePoint == ALPSUseCodepointNew {
669						expectations = connectionExpectations{
670							peerApplicationSettings: []byte{},
671						}
672					}
673					// Test the client and server correctly handle empty settings.
674					testCases = append(testCases, testCase{
675						protocol:           protocol,
676						testType:           clientTest,
677						name:               fmt.Sprintf("ALPS-Empty-Client-%s-%s", alpsCodePoint, suffix),
678						skipQUICALPNConfig: true,
679						config: Config{
680							MaxVersion:          ver.version,
681							NextProtos:          []string{"proto"},
682							ApplicationSettings: map[string][]byte{"proto": {}},
683							ALPSUseNewCodepoint: alpsCodePoint,
684						},
685						resumeSession: true,
686						expectations:  expectations,
687						flags: append([]string{
688							"-advertise-alpn", "\x05proto",
689							"-expect-alpn", "proto",
690							"-application-settings", "proto,",
691							"-expect-peer-application-settings", "",
692						}, alpsFlags...),
693					})
694					testCases = append(testCases, testCase{
695						protocol:           protocol,
696						testType:           serverTest,
697						name:               fmt.Sprintf("ALPS-Empty-Server-%s-%s", alpsCodePoint, suffix),
698						skipQUICALPNConfig: true,
699						config: Config{
700							MaxVersion:          ver.version,
701							NextProtos:          []string{"proto"},
702							ApplicationSettings: map[string][]byte{"proto": {}},
703							ALPSUseNewCodepoint: alpsCodePoint,
704						},
705						resumeSession: true,
706						expectations:  expectations,
707						flags: append([]string{
708							"-select-alpn", "proto",
709							"-application-settings", "proto,",
710							"-expect-peer-application-settings", "",
711						}, alpsFlags...),
712					})
713
714					bugs := ProtocolBugs{
715						AlwaysNegotiateApplicationSettingsOld: true,
716					}
717					if alpsCodePoint == ALPSUseCodepointNew {
718						bugs = ProtocolBugs{
719							AlwaysNegotiateApplicationSettingsNew: true,
720						}
721					}
722					// Test the client rejects application settings from the server on
723					// protocols it doesn't have them.
724					testCases = append(testCases, testCase{
725						protocol:           protocol,
726						testType:           clientTest,
727						name:               fmt.Sprintf("ALPS-UnsupportedProtocol-Client-%s-%s", alpsCodePoint, suffix),
728						skipQUICALPNConfig: true,
729						config: Config{
730							MaxVersion:          ver.version,
731							NextProtos:          []string{"proto1"},
732							ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
733							Bugs:                bugs,
734							ALPSUseNewCodepoint: alpsCodePoint,
735						},
736						// The client supports ALPS with "proto2", but not "proto1".
737						flags: append([]string{
738							"-advertise-alpn", "\x06proto1\x06proto2",
739							"-application-settings", "proto2,shim",
740							"-expect-alpn", "proto1",
741						}, alpsFlags...),
742						// The server sends ALPS with "proto1", which is invalid.
743						shouldFail:         true,
744						expectedError:      ":INVALID_ALPN_PROTOCOL:",
745						expectedLocalError: "remote error: illegal parameter",
746					})
747
748					// Test client rejects application settings from the server when
749					// server sends the wrong ALPS codepoint.
750					bugs = ProtocolBugs{
751						AlwaysNegotiateApplicationSettingsOld: true,
752					}
753					if alpsCodePoint == ALPSUseCodepointOld {
754						bugs = ProtocolBugs{
755							AlwaysNegotiateApplicationSettingsNew: true,
756						}
757					}
758
759					testCases = append(testCases, testCase{
760						protocol:           protocol,
761						testType:           clientTest,
762						name:               fmt.Sprintf("ALPS-WrongServerCodepoint-Client-%s-%s", alpsCodePoint, suffix),
763						skipQUICALPNConfig: true,
764						config: Config{
765							MaxVersion:          ver.version,
766							NextProtos:          []string{"proto"},
767							ApplicationSettings: map[string][]byte{"proto": {}},
768							Bugs:                bugs,
769							ALPSUseNewCodepoint: alpsCodePoint,
770						},
771						flags: append([]string{
772							"-advertise-alpn", "\x05proto",
773							"-expect-alpn", "proto",
774							"-application-settings", "proto,",
775							"-expect-peer-application-settings", "",
776						}, alpsFlags...),
777						shouldFail:         true,
778						expectedError:      ":UNEXPECTED_EXTENSION:",
779						expectedLocalError: "remote error: unsupported extension",
780					})
781
782					// Test server ignore wrong codepoint from client.
783					clientSends := ALPSUseCodepointNew
784					if alpsCodePoint == ALPSUseCodepointNew {
785						clientSends = ALPSUseCodepointOld
786					}
787
788					testCases = append(testCases, testCase{
789						protocol:           protocol,
790						testType:           serverTest,
791						name:               fmt.Sprintf("ALPS-IgnoreClientWrongCodepoint-Server-%s-%s", alpsCodePoint, suffix),
792						skipQUICALPNConfig: true,
793						config: Config{
794							MaxVersion:          ver.version,
795							NextProtos:          []string{"proto"},
796							ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
797							ALPSUseNewCodepoint: clientSends,
798						},
799						resumeConfig: &Config{
800							MaxVersion:          ver.version,
801							NextProtos:          []string{"proto"},
802							ApplicationSettings: map[string][]byte{"proto": []byte("runner2")},
803							ALPSUseNewCodepoint: clientSends,
804						},
805						resumeSession: true,
806						flags: append([]string{
807							"-select-alpn", "proto",
808							"-on-initial-application-settings", "proto,shim1",
809							"-on-resume-application-settings", "proto,shim2",
810						}, alpsFlags...),
811					})
812
813					// Test the server declines ALPS if it doesn't support it for the
814					// specified protocol.
815					testCases = append(testCases, testCase{
816						protocol:           protocol,
817						testType:           serverTest,
818						name:               fmt.Sprintf("ALPS-UnsupportedProtocol-Server-%s-%s", alpsCodePoint, suffix),
819						skipQUICALPNConfig: true,
820						config: Config{
821							MaxVersion:          ver.version,
822							NextProtos:          []string{"proto1"},
823							ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
824							ALPSUseNewCodepoint: alpsCodePoint,
825						},
826						// The server supports ALPS with "proto2", but not "proto1".
827						flags: append([]string{
828							"-select-alpn", "proto1",
829							"-application-settings", "proto2,shim",
830						}, alpsFlags...),
831					})
832
833					// Test the client rejects application settings from the server when
834					// it always negotiate both codepoint.
835					testCases = append(testCases, testCase{
836						protocol:           protocol,
837						testType:           clientTest,
838						name:               fmt.Sprintf("ALPS-UnsupportedProtocol-Client-ServerBoth-%s-%s", alpsCodePoint, suffix),
839						skipQUICALPNConfig: true,
840						config: Config{
841							MaxVersion:          ver.version,
842							NextProtos:          []string{"proto1"},
843							ApplicationSettings: map[string][]byte{"proto1": []byte("runner")},
844							Bugs: ProtocolBugs{
845								AlwaysNegotiateApplicationSettingsBoth: true,
846							},
847							ALPSUseNewCodepoint: alpsCodePoint,
848						},
849						flags: append([]string{
850							"-advertise-alpn", "\x06proto1\x06proto2",
851							"-application-settings", "proto1,shim",
852							"-expect-alpn", "proto1",
853						}, alpsFlags...),
854						// The server sends ALPS with both application settings, which is invalid.
855						shouldFail:         true,
856						expectedError:      ":UNEXPECTED_EXTENSION:",
857						expectedLocalError: "remote error: unsupported extension",
858					})
859
860					expectations = connectionExpectations{
861						peerApplicationSettingsOld: []byte("shim"),
862					}
863					if alpsCodePoint == ALPSUseCodepointNew {
864						expectations = connectionExpectations{
865							peerApplicationSettings: []byte("shim"),
866						}
867					}
868
869					// Test that the server rejects a missing application_settings extension.
870					testCases = append(testCases, testCase{
871						protocol:           protocol,
872						testType:           serverTest,
873						name:               fmt.Sprintf("ALPS-OmitClientApplicationSettings-%s-%s", alpsCodePoint, suffix),
874						skipQUICALPNConfig: true,
875						config: Config{
876							MaxVersion:          ver.version,
877							NextProtos:          []string{"proto"},
878							ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
879							Bugs: ProtocolBugs{
880								OmitClientApplicationSettings: true,
881							},
882							ALPSUseNewCodepoint: alpsCodePoint,
883						},
884						flags: append([]string{
885							"-select-alpn", "proto",
886							"-application-settings", "proto,shim",
887						}, alpsFlags...),
888						// The runner is a client, so it only processes the shim's alert
889						// after checking connection state.
890						expectations:       expectations,
891						shouldFail:         true,
892						expectedError:      ":MISSING_EXTENSION:",
893						expectedLocalError: "remote error: missing extension",
894					})
895
896					// Test that the server rejects a missing EncryptedExtensions message.
897					testCases = append(testCases, testCase{
898						protocol:           protocol,
899						testType:           serverTest,
900						name:               fmt.Sprintf("ALPS-OmitClientEncryptedExtensions-%s-%s", alpsCodePoint, suffix),
901						skipQUICALPNConfig: true,
902						config: Config{
903							MaxVersion:          ver.version,
904							NextProtos:          []string{"proto"},
905							ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
906							Bugs: ProtocolBugs{
907								OmitClientEncryptedExtensions: true,
908							},
909							ALPSUseNewCodepoint: alpsCodePoint,
910						},
911						flags: append([]string{
912							"-select-alpn", "proto",
913							"-application-settings", "proto,shim",
914						}, alpsFlags...),
915						// The runner is a client, so it only processes the shim's alert
916						// after checking connection state.
917						expectations:       expectations,
918						shouldFail:         true,
919						expectedError:      ":UNEXPECTED_MESSAGE:",
920						expectedLocalError: "remote error: unexpected message",
921					})
922
923					// Test that the server rejects an unexpected EncryptedExtensions message.
924					testCases = append(testCases, testCase{
925						protocol: protocol,
926						testType: serverTest,
927						name:     fmt.Sprintf("UnexpectedClientEncryptedExtensions-%s-%s", alpsCodePoint, suffix),
928						config: Config{
929							MaxVersion: ver.version,
930							Bugs: ProtocolBugs{
931								AlwaysSendClientEncryptedExtensions: true,
932							},
933							ALPSUseNewCodepoint: alpsCodePoint,
934						},
935						shouldFail:         true,
936						expectedError:      ":UNEXPECTED_MESSAGE:",
937						expectedLocalError: "remote error: unexpected message",
938					})
939
940					// Test that the server rejects an unexpected extension in an
941					// expected EncryptedExtensions message.
942					testCases = append(testCases, testCase{
943						protocol:           protocol,
944						testType:           serverTest,
945						name:               fmt.Sprintf("ExtraClientEncryptedExtension-%s-%s", alpsCodePoint, suffix),
946						skipQUICALPNConfig: true,
947						config: Config{
948							MaxVersion:          ver.version,
949							NextProtos:          []string{"proto"},
950							ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
951							Bugs: ProtocolBugs{
952								SendExtraClientEncryptedExtension: true,
953							},
954							ALPSUseNewCodepoint: alpsCodePoint,
955						},
956						flags: append([]string{
957							"-select-alpn", "proto",
958							"-application-settings", "proto,shim",
959						}, alpsFlags...),
960						// The runner is a client, so it only processes the shim's alert
961						// after checking connection state.
962						expectations:       expectations,
963						shouldFail:         true,
964						expectedError:      ":UNEXPECTED_EXTENSION:",
965						expectedLocalError: "remote error: unsupported extension",
966					})
967
968					// Test that ALPS is carried over on 0-RTT.
969					// TODO(crbug.com/381113363): Support 0-RTT in DTLS 1.3.
970					if protocol != dtls {
971						for _, empty := range []bool{false, true} {
972							maybeEmpty := ""
973							runnerSettings := "runner"
974							shimSettings := "shim"
975							if empty {
976								maybeEmpty = "Empty-"
977								runnerSettings = ""
978								shimSettings = ""
979							}
980
981							expectations = connectionExpectations{
982								peerApplicationSettingsOld: []byte(shimSettings),
983							}
984							if alpsCodePoint == ALPSUseCodepointNew {
985								expectations = connectionExpectations{
986									peerApplicationSettings: []byte(shimSettings),
987								}
988							}
989							testCases = append(testCases, testCase{
990								protocol:           protocol,
991								testType:           clientTest,
992								name:               fmt.Sprintf("ALPS-EarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
993								skipQUICALPNConfig: true,
994								config: Config{
995									MaxVersion:          ver.version,
996									NextProtos:          []string{"proto"},
997									ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
998									ALPSUseNewCodepoint: alpsCodePoint,
999								},
1000								resumeSession: true,
1001								earlyData:     true,
1002								flags: append([]string{
1003									"-advertise-alpn", "\x05proto",
1004									"-expect-alpn", "proto",
1005									"-application-settings", "proto," + shimSettings,
1006									"-expect-peer-application-settings", runnerSettings,
1007								}, alpsFlags...),
1008								expectations: expectations,
1009							})
1010							testCases = append(testCases, testCase{
1011								protocol:           protocol,
1012								testType:           serverTest,
1013								name:               fmt.Sprintf("ALPS-EarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
1014								skipQUICALPNConfig: true,
1015								config: Config{
1016									MaxVersion:          ver.version,
1017									NextProtos:          []string{"proto"},
1018									ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
1019									ALPSUseNewCodepoint: alpsCodePoint,
1020								},
1021								resumeSession: true,
1022								earlyData:     true,
1023								flags: append([]string{
1024									"-select-alpn", "proto",
1025									"-application-settings", "proto," + shimSettings,
1026									"-expect-peer-application-settings", runnerSettings,
1027								}, alpsFlags...),
1028								expectations: expectations,
1029							})
1030
1031							// Sending application settings in 0-RTT handshakes is forbidden.
1032							testCases = append(testCases, testCase{
1033								protocol:           protocol,
1034								testType:           clientTest,
1035								name:               fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
1036								skipQUICALPNConfig: true,
1037								config: Config{
1038									MaxVersion:          ver.version,
1039									NextProtos:          []string{"proto"},
1040									ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
1041									Bugs: ProtocolBugs{
1042										SendApplicationSettingsWithEarlyData: true,
1043									},
1044									ALPSUseNewCodepoint: alpsCodePoint,
1045								},
1046								resumeSession: true,
1047								earlyData:     true,
1048								flags: append([]string{
1049									"-advertise-alpn", "\x05proto",
1050									"-expect-alpn", "proto",
1051									"-application-settings", "proto," + shimSettings,
1052									"-expect-peer-application-settings", runnerSettings,
1053								}, alpsFlags...),
1054								expectations:       expectations,
1055								shouldFail:         true,
1056								expectedError:      ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
1057								expectedLocalError: "remote error: illegal parameter",
1058							})
1059							testCases = append(testCases, testCase{
1060								protocol:           protocol,
1061								testType:           serverTest,
1062								name:               fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
1063								skipQUICALPNConfig: true,
1064								config: Config{
1065									MaxVersion:          ver.version,
1066									NextProtos:          []string{"proto"},
1067									ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
1068									Bugs: ProtocolBugs{
1069										SendApplicationSettingsWithEarlyData: true,
1070									},
1071									ALPSUseNewCodepoint: alpsCodePoint,
1072								},
1073								resumeSession: true,
1074								earlyData:     true,
1075								flags: append([]string{
1076									"-select-alpn", "proto",
1077									"-application-settings", "proto," + shimSettings,
1078									"-expect-peer-application-settings", runnerSettings,
1079								}, alpsFlags...),
1080								expectations:       expectations,
1081								shouldFail:         true,
1082								expectedError:      ":UNEXPECTED_MESSAGE:",
1083								expectedLocalError: "remote error: unexpected message",
1084							})
1085						}
1086
1087						// Test that the client and server each decline early data if local
1088						// ALPS preferences has changed for the current connection.
1089						alpsMismatchTests := []struct {
1090							name                            string
1091							initialSettings, resumeSettings []byte
1092						}{
1093							{"DifferentValues", []byte("settings1"), []byte("settings2")},
1094							{"OnOff", []byte("settings"), nil},
1095							{"OffOn", nil, []byte("settings")},
1096							// The empty settings value should not be mistaken for ALPS not
1097							// being negotiated.
1098							{"OnEmpty", []byte("settings"), []byte{}},
1099							{"EmptyOn", []byte{}, []byte("settings")},
1100							{"EmptyOff", []byte{}, nil},
1101							{"OffEmpty", nil, []byte{}},
1102						}
1103						for _, test := range alpsMismatchTests {
1104							flags := []string{"-on-resume-expect-early-data-reason", "alps_mismatch"}
1105							flags = append(flags, alpsFlags...)
1106							if test.initialSettings != nil {
1107								flags = append(flags, "-on-initial-application-settings", "proto,"+string(test.initialSettings))
1108								flags = append(flags, "-on-initial-expect-peer-application-settings", "runner")
1109							}
1110							if test.resumeSettings != nil {
1111								flags = append(flags, "-on-resume-application-settings", "proto,"+string(test.resumeSettings))
1112								flags = append(flags, "-on-resume-expect-peer-application-settings", "runner")
1113							}
1114
1115							expectations = connectionExpectations{
1116								peerApplicationSettingsOld: test.initialSettings,
1117							}
1118							resumeExpectations = &connectionExpectations{
1119								peerApplicationSettingsOld: test.resumeSettings,
1120							}
1121							if alpsCodePoint == ALPSUseCodepointNew {
1122								expectations = connectionExpectations{
1123									peerApplicationSettings: test.initialSettings,
1124								}
1125								resumeExpectations = &connectionExpectations{
1126									peerApplicationSettings: test.resumeSettings,
1127								}
1128							}
1129							// The client should not offer early data if the session is
1130							// inconsistent with the new configuration. Note that if
1131							// the session did not negotiate ALPS (test.initialSettings
1132							// is nil), the client always offers early data.
1133							if test.initialSettings != nil {
1134								testCases = append(testCases, testCase{
1135									protocol:           protocol,
1136									testType:           clientTest,
1137									name:               fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s-%s", test.name, alpsCodePoint, suffix),
1138									skipQUICALPNConfig: true,
1139									config: Config{
1140										MaxVersion:          ver.version,
1141										MaxEarlyDataSize:    16384,
1142										NextProtos:          []string{"proto"},
1143										ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
1144										ALPSUseNewCodepoint: alpsCodePoint,
1145									},
1146									resumeSession: true,
1147									flags: append([]string{
1148										"-enable-early-data",
1149										"-expect-ticket-supports-early-data",
1150										"-expect-no-offer-early-data",
1151										"-advertise-alpn", "\x05proto",
1152										"-expect-alpn", "proto",
1153									}, flags...),
1154									expectations:       expectations,
1155									resumeExpectations: resumeExpectations,
1156								})
1157							}
1158
1159							// The server should reject early data if the session is
1160							// inconsistent with the new selection.
1161							testCases = append(testCases, testCase{
1162								protocol:           protocol,
1163								testType:           serverTest,
1164								name:               fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s-%s", test.name, alpsCodePoint, suffix),
1165								skipQUICALPNConfig: true,
1166								config: Config{
1167									MaxVersion:          ver.version,
1168									NextProtos:          []string{"proto"},
1169									ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
1170									ALPSUseNewCodepoint: alpsCodePoint,
1171								},
1172								resumeSession:           true,
1173								earlyData:               true,
1174								expectEarlyDataRejected: true,
1175								flags: append([]string{
1176									"-select-alpn", "proto",
1177								}, flags...),
1178								expectations:       expectations,
1179								resumeExpectations: resumeExpectations,
1180							})
1181						}
1182
1183						// Test that 0-RTT continues working when the shim configures
1184						// ALPS but the peer does not.
1185						testCases = append(testCases, testCase{
1186							protocol:           protocol,
1187							testType:           clientTest,
1188							name:               fmt.Sprintf("ALPS-EarlyData-Client-ServerDecline-%s-%s", alpsCodePoint, suffix),
1189							skipQUICALPNConfig: true,
1190							config: Config{
1191								MaxVersion:          ver.version,
1192								NextProtos:          []string{"proto"},
1193								ALPSUseNewCodepoint: alpsCodePoint,
1194							},
1195							resumeSession: true,
1196							earlyData:     true,
1197							flags: append([]string{
1198								"-advertise-alpn", "\x05proto",
1199								"-expect-alpn", "proto",
1200								"-application-settings", "proto,shim",
1201							}, alpsFlags...),
1202						})
1203						testCases = append(testCases, testCase{
1204							protocol:           protocol,
1205							testType:           serverTest,
1206							name:               fmt.Sprintf("ALPS-EarlyData-Server-ClientNoOffe-%s-%s", alpsCodePoint, suffix),
1207							skipQUICALPNConfig: true,
1208							config: Config{
1209								MaxVersion:          ver.version,
1210								NextProtos:          []string{"proto"},
1211								ALPSUseNewCodepoint: alpsCodePoint,
1212							},
1213							resumeSession: true,
1214							earlyData:     true,
1215							flags: append([]string{
1216								"-select-alpn", "proto",
1217								"-application-settings", "proto,shim",
1218							}, alpsFlags...),
1219						})
1220					}
1221				}
1222			} else {
1223				// Test the client rejects the ALPS extension if the server
1224				// negotiated TLS 1.2 or below.
1225				for _, alpsCodePoint := range []ALPSUseCodepoint{ALPSUseCodepointNew, ALPSUseCodepointOld} {
1226					useAlpsCodepointFlag := "0"
1227					if alpsCodePoint == ALPSUseCodepointNew {
1228						useAlpsCodepointFlag = "1"
1229					}
1230
1231					flags := []string{
1232						"-advertise-alpn", "\x03foo",
1233						"-expect-alpn", "foo",
1234						"-application-settings", "foo,shim",
1235						"-alps-use-new-codepoint", useAlpsCodepointFlag,
1236					}
1237					bugs := ProtocolBugs{
1238						AlwaysNegotiateApplicationSettingsOld: true,
1239					}
1240					if alpsCodePoint == ALPSUseCodepointNew {
1241						bugs = ProtocolBugs{
1242							AlwaysNegotiateApplicationSettingsNew: true,
1243						}
1244					}
1245					testCases = append(testCases, testCase{
1246						protocol: protocol,
1247						testType: clientTest,
1248						name:     fmt.Sprintf("ALPS-Reject-Client-%s-%s", alpsCodePoint, suffix),
1249						config: Config{
1250							MaxVersion:          ver.version,
1251							NextProtos:          []string{"foo"},
1252							ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
1253							Bugs:                bugs,
1254							ALPSUseNewCodepoint: alpsCodePoint,
1255						},
1256						flags:              flags,
1257						shouldFail:         true,
1258						expectedError:      ":UNEXPECTED_EXTENSION:",
1259						expectedLocalError: "remote error: unsupported extension",
1260					})
1261
1262					flags = []string{
1263						"-on-resume-advertise-alpn", "\x03foo",
1264						"-on-resume-expect-alpn", "foo",
1265						"-on-resume-application-settings", "foo,shim",
1266						"-alps-use-new-codepoint", useAlpsCodepointFlag,
1267					}
1268					bugs = ProtocolBugs{
1269						AlwaysNegotiateApplicationSettingsOld: true,
1270					}
1271					if alpsCodePoint == ALPSUseCodepointNew {
1272						bugs = ProtocolBugs{
1273							AlwaysNegotiateApplicationSettingsNew: true,
1274						}
1275					}
1276					testCases = append(testCases, testCase{
1277						protocol: protocol,
1278						testType: clientTest,
1279						name:     fmt.Sprintf("ALPS-Reject-Client-Resume-%s-%s", alpsCodePoint, suffix),
1280						config: Config{
1281							MaxVersion: ver.version,
1282						},
1283						resumeConfig: &Config{
1284							MaxVersion:          ver.version,
1285							NextProtos:          []string{"foo"},
1286							ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
1287							Bugs:                bugs,
1288							ALPSUseNewCodepoint: alpsCodePoint,
1289						},
1290						resumeSession:      true,
1291						flags:              flags,
1292						shouldFail:         true,
1293						expectedError:      ":UNEXPECTED_EXTENSION:",
1294						expectedLocalError: "remote error: unsupported extension",
1295					})
1296
1297					// Test the server declines ALPS if it negotiates TLS 1.2 or below.
1298					flags = []string{
1299						"-select-alpn", "foo",
1300						"-application-settings", "foo,shim",
1301						"-alps-use-new-codepoint", useAlpsCodepointFlag,
1302					}
1303
1304					testCases = append(testCases, testCase{
1305						protocol: protocol,
1306						testType: serverTest,
1307						name:     fmt.Sprintf("ALPS-Decline-Server-%s-%s", alpsCodePoint, suffix),
1308						config: Config{
1309							MaxVersion:          ver.version,
1310							NextProtos:          []string{"foo"},
1311							ApplicationSettings: map[string][]byte{"foo": []byte("runner")},
1312							ALPSUseNewCodepoint: alpsCodePoint,
1313						},
1314						// Test both TLS 1.2 full and resumption handshakes.
1315						resumeSession: true,
1316						flags:         flags,
1317						// If not specified, runner and shim both implicitly expect ALPS
1318						// is not negotiated.
1319					})
1320				}
1321			}
1322
1323			// Test QUIC transport params
1324			if protocol == quic {
1325				// Client sends params
1326				for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
1327					for _, serverSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
1328						useCodepointFlag := "0"
1329						if clientConfig == QUICUseCodepointLegacy {
1330							useCodepointFlag = "1"
1331						}
1332						flags := []string{
1333							"-quic-transport-params",
1334							base64FlagValue([]byte{1, 2}),
1335							"-quic-use-legacy-codepoint", useCodepointFlag,
1336						}
1337						expectations := connectionExpectations{
1338							quicTransportParams: []byte{1, 2},
1339						}
1340						shouldFail := false
1341						expectedError := ""
1342						expectedLocalError := ""
1343						if clientConfig == QUICUseCodepointLegacy {
1344							expectations = connectionExpectations{
1345								quicTransportParamsLegacy: []byte{1, 2},
1346							}
1347						}
1348						if serverSends != clientConfig {
1349							expectations = connectionExpectations{}
1350							shouldFail = true
1351							if serverSends == QUICUseCodepointNeither {
1352								expectedError = ":MISSING_EXTENSION:"
1353							} else {
1354								expectedLocalError = "remote error: unsupported extension"
1355							}
1356						} else {
1357							flags = append(flags,
1358								"-expect-quic-transport-params",
1359								base64FlagValue([]byte{3, 4}))
1360						}
1361						testCases = append(testCases, testCase{
1362							testType: clientTest,
1363							protocol: protocol,
1364							name:     fmt.Sprintf("QUICTransportParams-Client-Client%s-Server%s-%s", clientConfig, serverSends, suffix),
1365							config: Config{
1366								MinVersion:                            ver.version,
1367								MaxVersion:                            ver.version,
1368								QUICTransportParams:                   []byte{3, 4},
1369								QUICTransportParamsUseLegacyCodepoint: serverSends,
1370							},
1371							flags:                     flags,
1372							expectations:              expectations,
1373							shouldFail:                shouldFail,
1374							expectedError:             expectedError,
1375							expectedLocalError:        expectedLocalError,
1376							skipTransportParamsConfig: true,
1377						})
1378					}
1379				}
1380				// Server sends params
1381				for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
1382					for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
1383						expectations := connectionExpectations{
1384							quicTransportParams: []byte{3, 4},
1385						}
1386						shouldFail := false
1387						expectedError := ""
1388						useCodepointFlag := "0"
1389						if serverConfig == QUICUseCodepointLegacy {
1390							useCodepointFlag = "1"
1391							expectations = connectionExpectations{
1392								quicTransportParamsLegacy: []byte{3, 4},
1393							}
1394						}
1395						flags := []string{
1396							"-quic-transport-params",
1397							base64FlagValue([]byte{3, 4}),
1398							"-quic-use-legacy-codepoint", useCodepointFlag,
1399						}
1400						if clientSends != QUICUseCodepointBoth && clientSends != serverConfig {
1401							expectations = connectionExpectations{}
1402							shouldFail = true
1403							expectedError = ":MISSING_EXTENSION:"
1404						} else {
1405							flags = append(flags,
1406								"-expect-quic-transport-params",
1407								base64FlagValue([]byte{1, 2}),
1408							)
1409						}
1410						testCases = append(testCases, testCase{
1411							testType: serverTest,
1412							protocol: protocol,
1413							name:     fmt.Sprintf("QUICTransportParams-Server-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
1414							config: Config{
1415								MinVersion:                            ver.version,
1416								MaxVersion:                            ver.version,
1417								QUICTransportParams:                   []byte{1, 2},
1418								QUICTransportParamsUseLegacyCodepoint: clientSends,
1419							},
1420							flags:                     flags,
1421							expectations:              expectations,
1422							shouldFail:                shouldFail,
1423							expectedError:             expectedError,
1424							skipTransportParamsConfig: true,
1425						})
1426					}
1427				}
1428			} else {
1429				// Ensure non-QUIC client doesn't send QUIC transport parameters.
1430				for _, clientConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
1431					useCodepointFlag := "0"
1432					if clientConfig == QUICUseCodepointLegacy {
1433						useCodepointFlag = "1"
1434					}
1435					testCases = append(testCases, testCase{
1436						protocol: protocol,
1437						testType: clientTest,
1438						name:     fmt.Sprintf("QUICTransportParams-Client-NotSentInNonQUIC-%s-%s", clientConfig, suffix),
1439						config: Config{
1440							MinVersion:                            ver.version,
1441							MaxVersion:                            ver.version,
1442							QUICTransportParamsUseLegacyCodepoint: clientConfig,
1443						},
1444						flags: []string{
1445							"-max-version",
1446							ver.shimFlag(protocol),
1447							"-quic-transport-params",
1448							base64FlagValue([]byte{3, 4}),
1449							"-quic-use-legacy-codepoint", useCodepointFlag,
1450						},
1451						shouldFail:                true,
1452						expectedError:             ":QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED:",
1453						skipTransportParamsConfig: true,
1454					})
1455				}
1456				// Ensure non-QUIC server rejects codepoint 57 but ignores legacy 0xffa5.
1457				for _, clientSends := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy, QUICUseCodepointBoth, QUICUseCodepointNeither} {
1458					for _, serverConfig := range []QUICUseCodepoint{QUICUseCodepointStandard, QUICUseCodepointLegacy} {
1459						shouldFail := false
1460						expectedLocalError := ""
1461						useCodepointFlag := "0"
1462						if serverConfig == QUICUseCodepointLegacy {
1463							useCodepointFlag = "1"
1464						}
1465						if clientSends == QUICUseCodepointStandard || clientSends == QUICUseCodepointBoth {
1466							shouldFail = true
1467							expectedLocalError = "remote error: unsupported extension"
1468						}
1469						testCases = append(testCases, testCase{
1470							protocol: protocol,
1471							testType: serverTest,
1472							name:     fmt.Sprintf("QUICTransportParams-NonQUICServer-Client%s-Server%s-%s", clientSends, serverConfig, suffix),
1473							config: Config{
1474								MinVersion:                            ver.version,
1475								MaxVersion:                            ver.version,
1476								QUICTransportParams:                   []byte{1, 2},
1477								QUICTransportParamsUseLegacyCodepoint: clientSends,
1478							},
1479							flags: []string{
1480								"-quic-use-legacy-codepoint", useCodepointFlag,
1481							},
1482							shouldFail:                shouldFail,
1483							expectedLocalError:        expectedLocalError,
1484							skipTransportParamsConfig: true,
1485						})
1486					}
1487				}
1488
1489			}
1490
1491			// Test ticket behavior.
1492
1493			// Resume with a corrupt ticket.
1494			testCases = append(testCases, testCase{
1495				protocol: protocol,
1496				testType: serverTest,
1497				name:     "CorruptTicket-" + suffix,
1498				config: Config{
1499					MaxVersion: ver.version,
1500					Bugs: ProtocolBugs{
1501						FilterTicket: func(in []byte) ([]byte, error) {
1502							in[len(in)-1] ^= 1
1503							return in, nil
1504						},
1505					},
1506				},
1507				resumeSession:        true,
1508				expectResumeRejected: true,
1509			})
1510			// Test the ticket callbacks.
1511			for _, aeadCallback := range []bool{false, true} {
1512				flag := "-use-ticket-callback"
1513				callbackSuffix := suffix
1514				if aeadCallback {
1515					flag = "-use-ticket-aead-callback"
1516					callbackSuffix += "-AEAD"
1517				}
1518				testCases = append(testCases, testCase{
1519					protocol: protocol,
1520					testType: serverTest,
1521					name:     "TicketCallback-" + callbackSuffix,
1522					config: Config{
1523						MaxVersion: ver.version,
1524					},
1525					resumeSession: true,
1526					flags:         []string{flag},
1527				})
1528				// Only the old callback supports renewal.
1529				if !aeadCallback {
1530					testCases = append(testCases, testCase{
1531						protocol: protocol,
1532						testType: serverTest,
1533						name:     "TicketCallback-Renew-" + callbackSuffix,
1534						config: Config{
1535							MaxVersion: ver.version,
1536							Bugs: ProtocolBugs{
1537								ExpectNewTicket: true,
1538							},
1539						},
1540						flags:         []string{flag, "-renew-ticket"},
1541						resumeSession: true,
1542					})
1543				}
1544				testCases = append(testCases, testCase{
1545					protocol: protocol,
1546					testType: serverTest,
1547					name:     "TicketCallback-Skip-" + callbackSuffix,
1548					config: Config{
1549						MaxVersion: ver.version,
1550						Bugs: ProtocolBugs{
1551							ExpectNoNonEmptyNewSessionTicket: true,
1552						},
1553					},
1554					flags: []string{flag, "-skip-ticket"},
1555				})
1556
1557				// Test that the ticket callback is only called once when everything before
1558				// it in the ClientHello is asynchronous. This corrupts the ticket so
1559				// certificate selection callbacks run.
1560				testCases = append(testCases, testCase{
1561					protocol: protocol,
1562					testType: serverTest,
1563					name:     "TicketCallback-SingleCall-" + callbackSuffix,
1564					config: Config{
1565						MaxVersion: ver.version,
1566						Bugs: ProtocolBugs{
1567							FilterTicket: func(in []byte) ([]byte, error) {
1568								in[len(in)-1] ^= 1
1569								return in, nil
1570							},
1571						},
1572					},
1573					resumeSession:        true,
1574					expectResumeRejected: true,
1575					flags: []string{
1576						flag,
1577						"-async",
1578					},
1579				})
1580			}
1581
1582			// Resume with various lengths of ticket session id.
1583			if ver.version < VersionTLS13 {
1584				testCases = append(testCases, testCase{
1585					protocol: protocol,
1586					testType: serverTest,
1587					name:     "TicketSessionIDLength-0-" + suffix,
1588					config: Config{
1589						MaxVersion: ver.version,
1590						Bugs: ProtocolBugs{
1591							EmptyTicketSessionID: true,
1592						},
1593					},
1594					resumeSession: true,
1595				})
1596				testCases = append(testCases, testCase{
1597					protocol: protocol,
1598					testType: serverTest,
1599					name:     "TicketSessionIDLength-16-" + suffix,
1600					config: Config{
1601						MaxVersion: ver.version,
1602						Bugs: ProtocolBugs{
1603							TicketSessionIDLength: 16,
1604						},
1605					},
1606					resumeSession: true,
1607				})
1608				testCases = append(testCases, testCase{
1609					protocol: protocol,
1610					testType: serverTest,
1611					name:     "TicketSessionIDLength-32-" + suffix,
1612					config: Config{
1613						MaxVersion: ver.version,
1614						Bugs: ProtocolBugs{
1615							TicketSessionIDLength: 32,
1616						},
1617					},
1618					resumeSession: true,
1619				})
1620				testCases = append(testCases, testCase{
1621					protocol: protocol,
1622					testType: serverTest,
1623					name:     "TicketSessionIDLength-33-" + suffix,
1624					config: Config{
1625						MaxVersion: ver.version,
1626						Bugs: ProtocolBugs{
1627							TicketSessionIDLength: 33,
1628						},
1629					},
1630					resumeSession: true,
1631					shouldFail:    true,
1632					// The maximum session ID length is 32.
1633					expectedError: ":CLIENTHELLO_PARSE_FAILED:",
1634				})
1635			}
1636
1637			// Basic DTLS-SRTP tests. Include fake profiles to ensure they
1638			// are ignored.
1639			if protocol == dtls {
1640				testCases = append(testCases, testCase{
1641					protocol: protocol,
1642					name:     "SRTP-Client-" + suffix,
1643					config: Config{
1644						MaxVersion:             ver.version,
1645						SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
1646					},
1647					flags: []string{
1648						"-srtp-profiles",
1649						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1650					},
1651					expectations: connectionExpectations{
1652						srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
1653					},
1654				})
1655				testCases = append(testCases, testCase{
1656					protocol: protocol,
1657					testType: serverTest,
1658					name:     "SRTP-Server-" + suffix,
1659					config: Config{
1660						MaxVersion:             ver.version,
1661						SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
1662					},
1663					flags: []string{
1664						"-srtp-profiles",
1665						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1666					},
1667					expectations: connectionExpectations{
1668						srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
1669					},
1670				})
1671				// Test that the MKI is ignored.
1672				testCases = append(testCases, testCase{
1673					protocol: protocol,
1674					testType: serverTest,
1675					name:     "SRTP-Server-IgnoreMKI-" + suffix,
1676					config: Config{
1677						MaxVersion:             ver.version,
1678						SRTPProtectionProfiles: []uint16{SRTP_AES128_CM_HMAC_SHA1_80},
1679						Bugs: ProtocolBugs{
1680							SRTPMasterKeyIdentifier: "bogus",
1681						},
1682					},
1683					flags: []string{
1684						"-srtp-profiles",
1685						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1686					},
1687					expectations: connectionExpectations{
1688						srtpProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80,
1689					},
1690				})
1691				// Test that SRTP isn't negotiated on the server if there were
1692				// no matching profiles.
1693				testCases = append(testCases, testCase{
1694					protocol: protocol,
1695					testType: serverTest,
1696					name:     "SRTP-Server-NoMatch-" + suffix,
1697					config: Config{
1698						MaxVersion:             ver.version,
1699						SRTPProtectionProfiles: []uint16{100, 101, 102},
1700					},
1701					flags: []string{
1702						"-srtp-profiles",
1703						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1704					},
1705					expectations: connectionExpectations{
1706						srtpProtectionProfile: 0,
1707					},
1708				})
1709				// Test that the server returning an invalid SRTP profile is
1710				// flagged as an error by the client.
1711				testCases = append(testCases, testCase{
1712					protocol: protocol,
1713					name:     "SRTP-Client-NoMatch-" + suffix,
1714					config: Config{
1715						MaxVersion: ver.version,
1716						Bugs: ProtocolBugs{
1717							SendSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_32,
1718						},
1719					},
1720					flags: []string{
1721						"-srtp-profiles",
1722						"SRTP_AES128_CM_SHA1_80",
1723					},
1724					shouldFail:    true,
1725					expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:",
1726				})
1727			} else {
1728				// DTLS-SRTP is not defined for other protocols. Configuring it
1729				// on the client and server should ignore the extension.
1730				testCases = append(testCases, testCase{
1731					protocol: protocol,
1732					name:     "SRTP-Client-Ignore-" + suffix,
1733					config: Config{
1734						MaxVersion:             ver.version,
1735						SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
1736					},
1737					flags: []string{
1738						"-srtp-profiles",
1739						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1740					},
1741					expectations: connectionExpectations{
1742						srtpProtectionProfile: 0,
1743					},
1744				})
1745				testCases = append(testCases, testCase{
1746					protocol: protocol,
1747					testType: serverTest,
1748					name:     "SRTP-Server-Ignore-" + suffix,
1749					config: Config{
1750						MaxVersion:             ver.version,
1751						SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42},
1752					},
1753					flags: []string{
1754						"-srtp-profiles",
1755						"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32",
1756					},
1757					expectations: connectionExpectations{
1758						srtpProtectionProfile: 0,
1759					},
1760				})
1761			}
1762
1763			// Test SCT list.
1764			testCases = append(testCases, testCase{
1765				protocol: protocol,
1766				name:     "SignedCertificateTimestampList-Client-" + suffix,
1767				testType: clientTest,
1768				config: Config{
1769					MaxVersion: ver.version,
1770					Credential: rsaCertificate.WithSCTList(testSCTList),
1771				},
1772				flags: []string{
1773					"-enable-signed-cert-timestamps",
1774					"-expect-signed-cert-timestamps",
1775					base64FlagValue(testSCTList),
1776				},
1777				resumeSession: true,
1778			})
1779
1780			var differentSCTList []byte
1781			differentSCTList = append(differentSCTList, testSCTList...)
1782			differentSCTList[len(differentSCTList)-1] ^= 1
1783
1784			// The SCT extension did not specify that it must only be sent on the inital handshake as it
1785			// should have, so test that we tolerate but ignore it. This is only an issue pre-1.3, since
1786			// SCTs are sent in the CertificateEntry message in 1.3, whereas they were previously sent
1787			// in an extension in the ServerHello pre-1.3.
1788			testCases = append(testCases, testCase{
1789				protocol: protocol,
1790				name:     "SendSCTListOnResume-" + suffix,
1791				config: Config{
1792					MaxVersion: ver.version,
1793					Credential: rsaCertificate.WithSCTList(testSCTList),
1794					Bugs: ProtocolBugs{
1795						SendSCTListOnResume: differentSCTList,
1796					},
1797				},
1798				flags: []string{
1799					"-enable-signed-cert-timestamps",
1800					"-expect-signed-cert-timestamps",
1801					base64FlagValue(testSCTList),
1802				},
1803				resumeSession: true,
1804			})
1805
1806			testCases = append(testCases, testCase{
1807				protocol: protocol,
1808				name:     "SignedCertificateTimestampList-Server-" + suffix,
1809				testType: serverTest,
1810				config: Config{
1811					MaxVersion: ver.version,
1812				},
1813				shimCertificate: rsaCertificate.WithSCTList(testSCTList),
1814				expectations: connectionExpectations{
1815					peerCertificate: rsaCertificate.WithSCTList(testSCTList),
1816				},
1817				resumeSession: true,
1818			})
1819
1820			// Test empty SCT list.
1821			testCases = append(testCases, testCase{
1822				protocol: protocol,
1823				name:     "SignedCertificateTimestampListEmpty-Client-" + suffix,
1824				testType: clientTest,
1825				config: Config{
1826					MaxVersion: ver.version,
1827					Credential: rsaCertificate.WithSCTList([]byte{0, 0}),
1828				},
1829				flags: []string{
1830					"-enable-signed-cert-timestamps",
1831				},
1832				shouldFail:    true,
1833				expectedError: ":ERROR_PARSING_EXTENSION:",
1834			})
1835
1836			// Test empty SCT in non-empty list.
1837			testCases = append(testCases, testCase{
1838				protocol: protocol,
1839				name:     "SignedCertificateTimestampListEmptySCT-Client-" + suffix,
1840				testType: clientTest,
1841				config: Config{
1842					MaxVersion: ver.version,
1843					Credential: rsaCertificate.WithSCTList([]byte{0, 6, 0, 2, 1, 2, 0, 0}),
1844				},
1845				flags: []string{
1846					"-enable-signed-cert-timestamps",
1847				},
1848				shouldFail:    true,
1849				expectedError: ":ERROR_PARSING_EXTENSION:",
1850			})
1851
1852			// Test that certificate-related extensions are not sent unsolicited.
1853			testCases = append(testCases, testCase{
1854				protocol: protocol,
1855				testType: serverTest,
1856				name:     "UnsolicitedCertificateExtensions-" + suffix,
1857				config: Config{
1858					MaxVersion: ver.version,
1859					Bugs: ProtocolBugs{
1860						NoOCSPStapling:                true,
1861						NoSignedCertificateTimestamps: true,
1862					},
1863				},
1864				shimCertificate: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
1865			})
1866
1867			// Extension permutation should interact correctly with other extensions,
1868			// HelloVerifyRequest, HelloRetryRequest, and ECH. SSLTest.PermuteExtensions
1869			// in ssl_test.cc tests that the extensions are actually permuted. This
1870			// tests the handshake still works.
1871			//
1872			// This test also tests that all our extensions interact with each other.
1873			for _, ech := range []bool{false, true} {
1874				if ech && ver.version < VersionTLS13 {
1875					continue
1876				}
1877
1878				test := testCase{
1879					protocol:           protocol,
1880					name:               "AllExtensions-Client-Permute",
1881					skipQUICALPNConfig: true,
1882					config: Config{
1883						MinVersion:          ver.version,
1884						MaxVersion:          ver.version,
1885						Credential:          rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
1886						NextProtos:          []string{"proto"},
1887						ApplicationSettings: map[string][]byte{"proto": []byte("runner1")},
1888						Bugs: ProtocolBugs{
1889							SendServerNameAck: true,
1890							ExpectServerName:  "example.com",
1891							ExpectGREASE:      true,
1892						},
1893					},
1894					resumeSession: true,
1895					flags: []string{
1896						"-permute-extensions",
1897						"-enable-grease",
1898						"-enable-ocsp-stapling",
1899						"-enable-signed-cert-timestamps",
1900						"-advertise-alpn", "\x05proto",
1901						"-expect-alpn", "proto",
1902						"-host-name", "example.com",
1903					},
1904				}
1905
1906				if ech {
1907					test.name += "-ECH"
1908					echConfig := generateServerECHConfig(&ECHConfig{ConfigID: 42})
1909					test.config.ServerECHConfigs = []ServerECHConfig{echConfig}
1910					test.flags = append(test.flags,
1911						"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
1912						"-expect-ech-accept",
1913					)
1914					test.expectations.echAccepted = true
1915				}
1916
1917				if ver.version >= VersionTLS13 {
1918					// Trigger a HelloRetryRequest to test both ClientHellos. Note
1919					// our DTLS tests always enable HelloVerifyRequest.
1920					test.name += "-HelloRetryRequest"
1921
1922					// ALPS is only available on TLS 1.3.
1923					test.config.ApplicationSettings = map[string][]byte{"proto": []byte("runner")}
1924					test.flags = append(test.flags,
1925						"-application-settings", "proto,shim",
1926						"-alps-use-new-codepoint", "1",
1927						"-expect-peer-application-settings", "runner")
1928					test.expectations.peerApplicationSettings = []byte("shim")
1929				}
1930
1931				if protocol == dtls {
1932					test.config.SRTPProtectionProfiles = []uint16{SRTP_AES128_CM_HMAC_SHA1_80}
1933					test.flags = append(test.flags, "-srtp-profiles", "SRTP_AES128_CM_SHA1_80")
1934					test.expectations.srtpProtectionProfile = SRTP_AES128_CM_HMAC_SHA1_80
1935				}
1936
1937				test.name += "-" + suffix
1938				testCases = append(testCases, test)
1939			}
1940		}
1941	}
1942
1943	testCases = append(testCases, testCase{
1944		testType: clientTest,
1945		name:     "ClientHelloPadding",
1946		config: Config{
1947			Bugs: ProtocolBugs{
1948				RequireClientHelloSize: 512,
1949			},
1950		},
1951		// This hostname just needs to be long enough to push the
1952		// ClientHello into F5's danger zone between 256 and 511 bytes
1953		// long.
1954		flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"},
1955	})
1956
1957	// Test that illegal extensions in TLS 1.3 are rejected by the client if
1958	// in ServerHello.
1959	testCases = append(testCases, testCase{
1960		name: "NPN-Forbidden-TLS13",
1961		config: Config{
1962			MaxVersion: VersionTLS13,
1963			NextProtos: []string{"foo"},
1964			Bugs: ProtocolBugs{
1965				NegotiateNPNAtAllVersions: true,
1966			},
1967		},
1968		flags:         []string{"-select-next-proto", "foo"},
1969		shouldFail:    true,
1970		expectedError: ":ERROR_PARSING_EXTENSION:",
1971	})
1972	testCases = append(testCases, testCase{
1973		name: "EMS-Forbidden-TLS13",
1974		config: Config{
1975			MaxVersion: VersionTLS13,
1976			Bugs: ProtocolBugs{
1977				NegotiateEMSAtAllVersions: true,
1978			},
1979		},
1980		shouldFail:    true,
1981		expectedError: ":ERROR_PARSING_EXTENSION:",
1982	})
1983	testCases = append(testCases, testCase{
1984		name: "RenegotiationInfo-Forbidden-TLS13",
1985		config: Config{
1986			MaxVersion: VersionTLS13,
1987			Bugs: ProtocolBugs{
1988				NegotiateRenegotiationInfoAtAllVersions: true,
1989			},
1990		},
1991		shouldFail:    true,
1992		expectedError: ":ERROR_PARSING_EXTENSION:",
1993	})
1994	testCases = append(testCases, testCase{
1995		name: "Ticket-Forbidden-TLS13",
1996		config: Config{
1997			MaxVersion: VersionTLS12,
1998		},
1999		resumeConfig: &Config{
2000			MaxVersion: VersionTLS13,
2001			Bugs: ProtocolBugs{
2002				AdvertiseTicketExtension: true,
2003			},
2004		},
2005		resumeSession: true,
2006		shouldFail:    true,
2007		expectedError: ":ERROR_PARSING_EXTENSION:",
2008	})
2009
2010	// Test that illegal extensions in TLS 1.3 are declined by the server if
2011	// offered in ClientHello. The runner's server will fail if this occurs,
2012	// so we exercise the offering path. (EMS and Renegotiation Info are
2013	// implicit in every test.)
2014	testCases = append(testCases, testCase{
2015		testType: serverTest,
2016		name:     "NPN-Declined-TLS13",
2017		config: Config{
2018			MaxVersion: VersionTLS13,
2019			NextProtos: []string{"bar"},
2020		},
2021		flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"},
2022	})
2023
2024	// OpenSSL sends the status_request extension on resumption in TLS 1.2. Test that this is
2025	// tolerated.
2026	testCases = append(testCases, testCase{
2027		name: "SendOCSPResponseOnResume-TLS12",
2028		config: Config{
2029			MaxVersion: VersionTLS12,
2030			Credential: rsaCertificate.WithOCSP(testOCSPResponse),
2031			Bugs: ProtocolBugs{
2032				SendOCSPResponseOnResume: []byte("bogus"),
2033			},
2034		},
2035		flags: []string{
2036			"-enable-ocsp-stapling",
2037			"-expect-ocsp-response",
2038			base64FlagValue(testOCSPResponse),
2039		},
2040		resumeSession: true,
2041	})
2042
2043	testCases = append(testCases, testCase{
2044		name: "SendUnsolicitedOCSPOnCertificate-TLS13",
2045		config: Config{
2046			MaxVersion: VersionTLS13,
2047			Bugs: ProtocolBugs{
2048				SendExtensionOnCertificate: testOCSPExtension,
2049			},
2050		},
2051		shouldFail:    true,
2052		expectedError: ":UNEXPECTED_EXTENSION:",
2053	})
2054
2055	testCases = append(testCases, testCase{
2056		name: "SendUnsolicitedSCTOnCertificate-TLS13",
2057		config: Config{
2058			MaxVersion: VersionTLS13,
2059			Bugs: ProtocolBugs{
2060				SendExtensionOnCertificate: testSCTExtension,
2061			},
2062		},
2063		shouldFail:    true,
2064		expectedError: ":UNEXPECTED_EXTENSION:",
2065	})
2066
2067	// Test that extensions on client certificates are never accepted.
2068	testCases = append(testCases, testCase{
2069		name:     "SendExtensionOnClientCertificate-TLS13",
2070		testType: serverTest,
2071		config: Config{
2072			MaxVersion: VersionTLS13,
2073			Credential: &rsaCertificate,
2074			Bugs: ProtocolBugs{
2075				SendExtensionOnCertificate: testOCSPExtension,
2076			},
2077		},
2078		flags: []string{
2079			"-enable-ocsp-stapling",
2080			"-require-any-client-certificate",
2081		},
2082		shouldFail:    true,
2083		expectedError: ":UNEXPECTED_EXTENSION:",
2084	})
2085
2086	testCases = append(testCases, testCase{
2087		name: "SendUnknownExtensionOnCertificate-TLS13",
2088		config: Config{
2089			MaxVersion: VersionTLS13,
2090			Bugs: ProtocolBugs{
2091				SendExtensionOnCertificate: []byte{0x00, 0x7f, 0, 0},
2092			},
2093		},
2094		shouldFail:    true,
2095		expectedError: ":UNEXPECTED_EXTENSION:",
2096	})
2097
2098	// Test that extensions on intermediates are allowed but ignored.
2099	testCases = append(testCases, testCase{
2100		name: "IgnoreExtensionsOnIntermediates-TLS13",
2101		config: Config{
2102			MaxVersion: VersionTLS13,
2103			Credential: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
2104			Bugs: ProtocolBugs{
2105				// Send different values on the intermediate. This tests
2106				// the intermediate's extensions do not override the
2107				// leaf's.
2108				SendOCSPOnIntermediates: testOCSPResponse2,
2109				SendSCTOnIntermediates:  testSCTList2,
2110			},
2111		},
2112		flags: []string{
2113			"-enable-ocsp-stapling",
2114			"-expect-ocsp-response",
2115			base64FlagValue(testOCSPResponse),
2116			"-enable-signed-cert-timestamps",
2117			"-expect-signed-cert-timestamps",
2118			base64FlagValue(testSCTList),
2119		},
2120		resumeSession: true,
2121	})
2122
2123	// Test that extensions are not sent on intermediates when configured
2124	// only for a leaf.
2125	testCases = append(testCases, testCase{
2126		testType: serverTest,
2127		name:     "SendNoExtensionsOnIntermediate-TLS13",
2128		config: Config{
2129			MaxVersion: VersionTLS13,
2130			Bugs: ProtocolBugs{
2131				ExpectNoExtensionsOnIntermediate: true,
2132			},
2133		},
2134		shimCertificate: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
2135	})
2136
2137	// Test that extensions are not sent on client certificates.
2138	testCases = append(testCases, testCase{
2139		name: "SendNoClientCertificateExtensions-TLS13",
2140		config: Config{
2141			MaxVersion: VersionTLS13,
2142			ClientAuth: RequireAnyClientCert,
2143		},
2144		shimCertificate: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
2145	})
2146
2147	testCases = append(testCases, testCase{
2148		name: "SendDuplicateExtensionsOnCerts-TLS13",
2149		config: Config{
2150			MaxVersion: VersionTLS13,
2151			Credential: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList),
2152			Bugs: ProtocolBugs{
2153				SendDuplicateCertExtensions: true,
2154			},
2155		},
2156		flags: []string{
2157			"-enable-ocsp-stapling",
2158			"-enable-signed-cert-timestamps",
2159		},
2160		resumeSession: true,
2161		shouldFail:    true,
2162		expectedError: ":DUPLICATE_EXTENSION:",
2163	})
2164
2165	testCases = append(testCases, testCase{
2166		name:            "SignedCertificateTimestampListInvalid-Server",
2167		testType:        serverTest,
2168		shimCertificate: rsaCertificate.WithSCTList([]byte{0, 0}),
2169		shouldFail:      true,
2170		expectedError:   ":INVALID_SCT_LIST:",
2171	})
2172}
2173
2174func addUnknownExtensionTests() {
2175	// Test an unknown extension from the server.
2176	testCases = append(testCases, testCase{
2177		testType: clientTest,
2178		name:     "UnknownExtension-Client",
2179		config: Config{
2180			MaxVersion: VersionTLS12,
2181			Bugs: ProtocolBugs{
2182				CustomExtension: "custom extension",
2183			},
2184		},
2185		shouldFail:         true,
2186		expectedError:      ":UNEXPECTED_EXTENSION:",
2187		expectedLocalError: "remote error: unsupported extension",
2188	})
2189	testCases = append(testCases, testCase{
2190		testType: clientTest,
2191		name:     "UnknownExtension-Client-TLS13",
2192		config: Config{
2193			MaxVersion: VersionTLS13,
2194			Bugs: ProtocolBugs{
2195				CustomExtension: "custom extension",
2196			},
2197		},
2198		shouldFail:         true,
2199		expectedError:      ":UNEXPECTED_EXTENSION:",
2200		expectedLocalError: "remote error: unsupported extension",
2201	})
2202	testCases = append(testCases, testCase{
2203		testType: clientTest,
2204		name:     "UnknownUnencryptedExtension-Client-TLS13",
2205		config: Config{
2206			MaxVersion: VersionTLS13,
2207			Bugs: ProtocolBugs{
2208				CustomUnencryptedExtension: "custom extension",
2209			},
2210		},
2211		shouldFail:    true,
2212		expectedError: ":UNEXPECTED_EXTENSION:",
2213		// The shim must send an alert, but alerts at this point do not
2214		// get successfully decrypted by the runner.
2215		expectedLocalError: "local error: bad record MAC",
2216	})
2217	testCases = append(testCases, testCase{
2218		testType: clientTest,
2219		name:     "UnexpectedUnencryptedExtension-Client-TLS13",
2220		config: Config{
2221			MaxVersion: VersionTLS13,
2222			Bugs: ProtocolBugs{
2223				SendUnencryptedALPN: "foo",
2224			},
2225		},
2226		flags: []string{
2227			"-advertise-alpn", "\x03foo\x03bar",
2228		},
2229		shouldFail:    true,
2230		expectedError: ":UNEXPECTED_EXTENSION:",
2231		// The shim must send an alert, but alerts at this point do not
2232		// get successfully decrypted by the runner.
2233		expectedLocalError: "local error: bad record MAC",
2234	})
2235
2236	// Test a known but unoffered extension from the server.
2237	testCases = append(testCases, testCase{
2238		testType: clientTest,
2239		name:     "UnofferedExtension-Client",
2240		config: Config{
2241			MaxVersion: VersionTLS12,
2242			Bugs: ProtocolBugs{
2243				SendALPN: "alpn",
2244			},
2245		},
2246		shouldFail:         true,
2247		expectedError:      ":UNEXPECTED_EXTENSION:",
2248		expectedLocalError: "remote error: unsupported extension",
2249	})
2250	testCases = append(testCases, testCase{
2251		testType: clientTest,
2252		name:     "UnofferedExtension-Client-TLS13",
2253		config: Config{
2254			MaxVersion: VersionTLS13,
2255			Bugs: ProtocolBugs{
2256				SendALPN: "alpn",
2257			},
2258		},
2259		shouldFail:         true,
2260		expectedError:      ":UNEXPECTED_EXTENSION:",
2261		expectedLocalError: "remote error: unsupported extension",
2262	})
2263}
2264
2265// Test that omitted and empty extensions blocks are tolerated.
2266func addOmitExtensionsTests() {
2267	// Check the ExpectOmitExtensions setting works.
2268	testCases = append(testCases, testCase{
2269		testType: serverTest,
2270		name:     "ExpectOmitExtensions",
2271		config: Config{
2272			MinVersion: VersionTLS12,
2273			MaxVersion: VersionTLS12,
2274			Bugs: ProtocolBugs{
2275				ExpectOmitExtensions: true,
2276			},
2277		},
2278		shouldFail:         true,
2279		expectedLocalError: "tls: ServerHello did not omit extensions",
2280	})
2281
2282	for _, ver := range tlsVersions {
2283		if ver.version > VersionTLS12 {
2284			continue
2285		}
2286
2287		testCases = append(testCases, testCase{
2288			testType: serverTest,
2289			name:     "OmitExtensions-ClientHello-" + ver.name,
2290			config: Config{
2291				MinVersion:             ver.version,
2292				MaxVersion:             ver.version,
2293				SessionTicketsDisabled: true,
2294				Bugs: ProtocolBugs{
2295					OmitExtensions: true,
2296					// With no client extensions, the ServerHello must not have
2297					// extensions. It should then omit the extensions field.
2298					ExpectOmitExtensions: true,
2299				},
2300			},
2301		})
2302
2303		testCases = append(testCases, testCase{
2304			testType: serverTest,
2305			name:     "EmptyExtensions-ClientHello-" + ver.name,
2306			config: Config{
2307				MinVersion:             ver.version,
2308				MaxVersion:             ver.version,
2309				SessionTicketsDisabled: true,
2310				Bugs: ProtocolBugs{
2311					EmptyExtensions: true,
2312					// With no client extensions, the ServerHello must not have
2313					// extensions. It should then omit the extensions field.
2314					ExpectOmitExtensions: true,
2315				},
2316			},
2317		})
2318
2319		testCases = append(testCases, testCase{
2320			testType: clientTest,
2321			name:     "OmitExtensions-ServerHello-" + ver.name,
2322			config: Config{
2323				MinVersion:             ver.version,
2324				MaxVersion:             ver.version,
2325				SessionTicketsDisabled: true,
2326				Bugs: ProtocolBugs{
2327					OmitExtensions: true,
2328					// Disable all ServerHello extensions so
2329					// OmitExtensions works.
2330					NoExtendedMasterSecret:        true,
2331					NoRenegotiationInfo:           true,
2332					NoOCSPStapling:                true,
2333					NoSignedCertificateTimestamps: true,
2334				},
2335			},
2336		})
2337
2338		testCases = append(testCases, testCase{
2339			testType: clientTest,
2340			name:     "EmptyExtensions-ServerHello-" + ver.name,
2341			config: Config{
2342				MinVersion:             ver.version,
2343				MaxVersion:             ver.version,
2344				SessionTicketsDisabled: true,
2345				Bugs: ProtocolBugs{
2346					EmptyExtensions: true,
2347					// Disable all ServerHello extensions so
2348					// EmptyExtensions works.
2349					NoExtendedMasterSecret:        true,
2350					NoRenegotiationInfo:           true,
2351					NoOCSPStapling:                true,
2352					NoSignedCertificateTimestamps: true,
2353				},
2354			},
2355		})
2356	}
2357}
2358