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
17func addVersionNegotiationTests() {
18	for _, protocol := range []protocol{tls, dtls, quic} {
19		for _, shimVers := range allVersions(protocol) {
20			// Assemble flags to disable all newer versions on the shim.
21			var flags []string
22			for _, vers := range allVersions(protocol) {
23				if vers.version > shimVers.version {
24					flags = append(flags, vers.excludeFlag)
25				}
26			}
27
28			flags2 := []string{"-max-version", shimVers.shimFlag(protocol)}
29
30			// Test configuring the runner's maximum version.
31			for _, runnerVers := range allVersions(protocol) {
32				expectedVersion := shimVers.version
33				if runnerVers.version < shimVers.version {
34					expectedVersion = runnerVers.version
35				}
36
37				suffix := shimVers.name + "-" + runnerVers.name
38				suffix += "-" + protocol.String()
39
40				// Determine the expected initial record-layer versions.
41				clientVers := shimVers.version
42				if clientVers > VersionTLS10 {
43					clientVers = VersionTLS10
44				}
45				clientVers = recordVersionToWire(clientVers, protocol)
46				serverVers := expectedVersion
47				if expectedVersion >= VersionTLS13 {
48					serverVers = VersionTLS12
49				}
50				serverVers = recordVersionToWire(serverVers, protocol)
51
52				testCases = append(testCases, testCase{
53					protocol: protocol,
54					testType: clientTest,
55					name:     "VersionNegotiation-Client-" + suffix,
56					config: Config{
57						MaxVersion: runnerVers.version,
58						Bugs: ProtocolBugs{
59							ExpectInitialRecordVersion: clientVers,
60						},
61					},
62					flags: flags,
63					expectations: connectionExpectations{
64						version: expectedVersion,
65					},
66					// The version name check does not recognize the
67					// |excludeFlag| construction in |flags|.
68					skipVersionNameCheck: true,
69				})
70				testCases = append(testCases, testCase{
71					protocol: protocol,
72					testType: clientTest,
73					name:     "VersionNegotiation-Client2-" + suffix,
74					config: Config{
75						MaxVersion: runnerVers.version,
76						Bugs: ProtocolBugs{
77							ExpectInitialRecordVersion: clientVers,
78						},
79					},
80					flags: flags2,
81					expectations: connectionExpectations{
82						version: expectedVersion,
83					},
84				})
85
86				testCases = append(testCases, testCase{
87					protocol: protocol,
88					testType: serverTest,
89					name:     "VersionNegotiation-Server-" + suffix,
90					config: Config{
91						MaxVersion: runnerVers.version,
92						Bugs: ProtocolBugs{
93							ExpectInitialRecordVersion: serverVers,
94						},
95					},
96					flags: flags,
97					expectations: connectionExpectations{
98						version: expectedVersion,
99					},
100					// The version name check does not recognize the
101					// |excludeFlag| construction in |flags|.
102					skipVersionNameCheck: true,
103				})
104				testCases = append(testCases, testCase{
105					protocol: protocol,
106					testType: serverTest,
107					name:     "VersionNegotiation-Server2-" + suffix,
108					config: Config{
109						MaxVersion: runnerVers.version,
110						Bugs: ProtocolBugs{
111							ExpectInitialRecordVersion: serverVers,
112						},
113					},
114					flags: flags2,
115					expectations: connectionExpectations{
116						version: expectedVersion,
117					},
118				})
119			}
120		}
121	}
122
123	// Test the version extension at all versions.
124	for _, protocol := range []protocol{tls, dtls, quic} {
125		for _, vers := range allVersions(protocol) {
126			suffix := vers.name + "-" + protocol.String()
127
128			testCases = append(testCases, testCase{
129				protocol: protocol,
130				testType: serverTest,
131				name:     "VersionNegotiationExtension-" + suffix,
132				config: Config{
133					Bugs: ProtocolBugs{
134						SendSupportedVersions:      []uint16{0x1111, vers.wire(protocol), 0x2222},
135						IgnoreTLS13DowngradeRandom: true,
136					},
137				},
138				expectations: connectionExpectations{
139					version: vers.version,
140				},
141			})
142		}
143	}
144
145	// If all versions are unknown, negotiation fails.
146	testCases = append(testCases, testCase{
147		testType: serverTest,
148		name:     "NoSupportedVersions",
149		config: Config{
150			Bugs: ProtocolBugs{
151				SendSupportedVersions: []uint16{0x1111},
152			},
153		},
154		shouldFail:    true,
155		expectedError: ":UNSUPPORTED_PROTOCOL:",
156	})
157	testCases = append(testCases, testCase{
158		protocol: dtls,
159		testType: serverTest,
160		name:     "NoSupportedVersions-DTLS",
161		config: Config{
162			Bugs: ProtocolBugs{
163				SendSupportedVersions: []uint16{0x1111},
164			},
165		},
166		shouldFail:    true,
167		expectedError: ":UNSUPPORTED_PROTOCOL:",
168	})
169
170	testCases = append(testCases, testCase{
171		testType: serverTest,
172		name:     "ClientHelloVersionTooHigh",
173		config: Config{
174			MaxVersion: VersionTLS13,
175			Bugs: ProtocolBugs{
176				SendClientVersion:          0x0304,
177				OmitSupportedVersions:      true,
178				IgnoreTLS13DowngradeRandom: true,
179			},
180		},
181		expectations: connectionExpectations{
182			version: VersionTLS12,
183		},
184	})
185
186	testCases = append(testCases, testCase{
187		testType: serverTest,
188		name:     "ConflictingVersionNegotiation",
189		config: Config{
190			Bugs: ProtocolBugs{
191				SendClientVersion:          VersionTLS12,
192				SendSupportedVersions:      []uint16{VersionTLS11},
193				IgnoreTLS13DowngradeRandom: true,
194			},
195		},
196		// The extension takes precedence over the ClientHello version.
197		expectations: connectionExpectations{
198			version: VersionTLS11,
199		},
200	})
201
202	testCases = append(testCases, testCase{
203		testType: serverTest,
204		name:     "ConflictingVersionNegotiation-2",
205		config: Config{
206			Bugs: ProtocolBugs{
207				SendClientVersion:          VersionTLS11,
208				SendSupportedVersions:      []uint16{VersionTLS12},
209				IgnoreTLS13DowngradeRandom: true,
210			},
211		},
212		// The extension takes precedence over the ClientHello version.
213		expectations: connectionExpectations{
214			version: VersionTLS12,
215		},
216	})
217
218	// Test that TLS 1.2 isn't negotiated by the supported_versions extension in
219	// the ServerHello.
220	testCases = append(testCases, testCase{
221		testType: clientTest,
222		name:     "SupportedVersionSelection-TLS12",
223		config: Config{
224			MaxVersion: VersionTLS12,
225			Bugs: ProtocolBugs{
226				SendServerSupportedVersionExtension: VersionTLS12,
227			},
228		},
229		shouldFail:    true,
230		expectedError: ":UNEXPECTED_EXTENSION:",
231	})
232
233	// Test that the maximum version is selected regardless of the
234	// client-sent order.
235	testCases = append(testCases, testCase{
236		testType: serverTest,
237		name:     "IgnoreClientVersionOrder",
238		config: Config{
239			Bugs: ProtocolBugs{
240				SendSupportedVersions: []uint16{VersionTLS12, VersionTLS13},
241			},
242		},
243		expectations: connectionExpectations{
244			version: VersionTLS13,
245		},
246	})
247
248	// Test for version tolerance.
249	testCases = append(testCases, testCase{
250		testType: serverTest,
251		name:     "MinorVersionTolerance",
252		config: Config{
253			Bugs: ProtocolBugs{
254				SendClientVersion:          0x03ff,
255				OmitSupportedVersions:      true,
256				IgnoreTLS13DowngradeRandom: true,
257			},
258		},
259		expectations: connectionExpectations{
260			version: VersionTLS12,
261		},
262	})
263	testCases = append(testCases, testCase{
264		testType: serverTest,
265		name:     "MajorVersionTolerance",
266		config: Config{
267			Bugs: ProtocolBugs{
268				SendClientVersion:          0x0400,
269				OmitSupportedVersions:      true,
270				IgnoreTLS13DowngradeRandom: true,
271			},
272		},
273		// TLS 1.3 must be negotiated with the supported_versions
274		// extension, not ClientHello.version.
275		expectations: connectionExpectations{
276			version: VersionTLS12,
277		},
278	})
279	testCases = append(testCases, testCase{
280		testType: serverTest,
281		name:     "VersionTolerance-TLS13",
282		config: Config{
283			Bugs: ProtocolBugs{
284				// Although TLS 1.3 does not use
285				// ClientHello.version, it still tolerates high
286				// values there.
287				SendClientVersion: 0x0400,
288			},
289		},
290		expectations: connectionExpectations{
291			version: VersionTLS13,
292		},
293	})
294
295	testCases = append(testCases, testCase{
296		protocol: dtls,
297		testType: serverTest,
298		name:     "MinorVersionTolerance-DTLS",
299		config: Config{
300			Bugs: ProtocolBugs{
301				SendClientVersion:          0xfe00,
302				OmitSupportedVersions:      true,
303				IgnoreTLS13DowngradeRandom: true,
304			},
305		},
306		expectations: connectionExpectations{
307			version: VersionTLS12,
308		},
309	})
310	testCases = append(testCases, testCase{
311		protocol: dtls,
312		testType: serverTest,
313		name:     "MajorVersionTolerance-DTLS",
314		config: Config{
315			Bugs: ProtocolBugs{
316				SendClientVersion:          0xfdff,
317				OmitSupportedVersions:      true,
318				IgnoreTLS13DowngradeRandom: true,
319			},
320		},
321		expectations: connectionExpectations{
322			version: VersionTLS12,
323		},
324	})
325
326	// Test that versions below 3.0 are rejected.
327	testCases = append(testCases, testCase{
328		testType: serverTest,
329		name:     "VersionTooLow",
330		config: Config{
331			Bugs: ProtocolBugs{
332				SendClientVersion:     0x0200,
333				OmitSupportedVersions: true,
334			},
335		},
336		shouldFail:    true,
337		expectedError: ":UNSUPPORTED_PROTOCOL:",
338	})
339	testCases = append(testCases, testCase{
340		protocol: dtls,
341		testType: serverTest,
342		name:     "VersionTooLow-DTLS",
343		config: Config{
344			Bugs: ProtocolBugs{
345				SendClientVersion:     0xffff,
346				OmitSupportedVersions: true,
347			},
348		},
349		shouldFail:    true,
350		expectedError: ":UNSUPPORTED_PROTOCOL:",
351	})
352
353	testCases = append(testCases, testCase{
354		name: "ServerBogusVersion",
355		config: Config{
356			Bugs: ProtocolBugs{
357				SendServerHelloVersion: 0x1234,
358			},
359		},
360		shouldFail:    true,
361		expectedError: ":UNSUPPORTED_PROTOCOL:",
362	})
363
364	// Test TLS 1.3's downgrade signal.
365	for _, protocol := range []protocol{tls, dtls} {
366		for _, vers := range allVersions(protocol) {
367			if vers.version >= VersionTLS13 {
368				continue
369			}
370			clientShimError := "tls: downgrade from TLS 1.3 detected"
371			if vers.version < VersionTLS12 {
372				clientShimError = "tls: downgrade from TLS 1.2 detected"
373			}
374			// for _, test := range downgradeTests {
375			// The client should enforce the downgrade sentinel.
376			testCases = append(testCases, testCase{
377				protocol: protocol,
378				name:     "Downgrade-" + vers.name + "-Client-" + protocol.String(),
379				config: Config{
380					Bugs: ProtocolBugs{
381						NegotiateVersion: vers.wire(protocol),
382					},
383				},
384				expectations: connectionExpectations{
385					version: vers.version,
386				},
387				shouldFail:         true,
388				expectedError:      ":TLS13_DOWNGRADE:",
389				expectedLocalError: "remote error: illegal parameter",
390			})
391
392			// The server should emit the downgrade signal.
393			testCases = append(testCases, testCase{
394				protocol: protocol,
395				testType: serverTest,
396				name:     "Downgrade-" + vers.name + "-Server-" + protocol.String(),
397				config: Config{
398					Bugs: ProtocolBugs{
399						SendSupportedVersions: []uint16{vers.wire(protocol)},
400					},
401				},
402				expectations: connectionExpectations{
403					version: vers.version,
404				},
405				shouldFail:         true,
406				expectedLocalError: clientShimError,
407			})
408		}
409	}
410
411	// SSL 3.0 support has been removed. Test that the shim does not
412	// support it.
413	testCases = append(testCases, testCase{
414		name: "NoSSL3-Client",
415		config: Config{
416			MinVersion: VersionSSL30,
417			MaxVersion: VersionSSL30,
418		},
419		shouldFail:         true,
420		expectedLocalError: "tls: client did not offer any supported protocol versions",
421	})
422	testCases = append(testCases, testCase{
423		name: "NoSSL3-Client-Unsolicited",
424		config: Config{
425			MinVersion: VersionSSL30,
426			MaxVersion: VersionSSL30,
427			Bugs: ProtocolBugs{
428				// The above test asserts the client does not
429				// offer SSL 3.0 in the supported_versions
430				// list. Additionally assert that it rejects an
431				// unsolicited SSL 3.0 ServerHello.
432				NegotiateVersion: VersionSSL30,
433			},
434		},
435		shouldFail:         true,
436		expectedError:      ":UNSUPPORTED_PROTOCOL:",
437		expectedLocalError: "remote error: protocol version not supported",
438	})
439	testCases = append(testCases, testCase{
440		testType: serverTest,
441		name:     "NoSSL3-Server",
442		config: Config{
443			MinVersion: VersionSSL30,
444			MaxVersion: VersionSSL30,
445		},
446		shouldFail:         true,
447		expectedError:      ":UNSUPPORTED_PROTOCOL:",
448		expectedLocalError: "remote error: protocol version not supported",
449	})
450}
451
452func addMinimumVersionTests() {
453	for _, protocol := range []protocol{tls, dtls, quic} {
454		for _, shimVers := range allVersions(protocol) {
455			// Assemble flags to disable all older versions on the shim.
456			var flags []string
457			for _, vers := range allVersions(protocol) {
458				if vers.version < shimVers.version {
459					flags = append(flags, vers.excludeFlag)
460				}
461			}
462
463			flags2 := []string{"-min-version", shimVers.shimFlag(protocol)}
464
465			for _, runnerVers := range allVersions(protocol) {
466				suffix := shimVers.name + "-" + runnerVers.name
467				suffix += "-" + protocol.String()
468
469				var expectedVersion uint16
470				var shouldFail bool
471				var expectedError, expectedLocalError string
472				if runnerVers.version >= shimVers.version {
473					expectedVersion = runnerVers.version
474				} else {
475					shouldFail = true
476					expectedError = ":UNSUPPORTED_PROTOCOL:"
477					expectedLocalError = "remote error: protocol version not supported"
478				}
479
480				testCases = append(testCases, testCase{
481					protocol: protocol,
482					testType: clientTest,
483					name:     "MinimumVersion-Client-" + suffix,
484					config: Config{
485						MaxVersion: runnerVers.version,
486						Bugs: ProtocolBugs{
487							// Ensure the server does not decline to
488							// select a version (versions extension) or
489							// cipher (some ciphers depend on versions).
490							NegotiateVersion:            runnerVers.wire(protocol),
491							IgnorePeerCipherPreferences: shouldFail,
492						},
493					},
494					flags: flags,
495					expectations: connectionExpectations{
496						version: expectedVersion,
497					},
498					shouldFail:         shouldFail,
499					expectedError:      expectedError,
500					expectedLocalError: expectedLocalError,
501					// The version name check does not recognize the
502					// |excludeFlag| construction in |flags|.
503					skipVersionNameCheck: true,
504				})
505				testCases = append(testCases, testCase{
506					protocol: protocol,
507					testType: clientTest,
508					name:     "MinimumVersion-Client2-" + suffix,
509					config: Config{
510						MaxVersion: runnerVers.version,
511						Bugs: ProtocolBugs{
512							// Ensure the server does not decline to
513							// select a version (versions extension) or
514							// cipher (some ciphers depend on versions).
515							NegotiateVersion:            runnerVers.wire(protocol),
516							IgnorePeerCipherPreferences: shouldFail,
517						},
518					},
519					flags: flags2,
520					expectations: connectionExpectations{
521						version: expectedVersion,
522					},
523					shouldFail:         shouldFail,
524					expectedError:      expectedError,
525					expectedLocalError: expectedLocalError,
526				})
527
528				testCases = append(testCases, testCase{
529					protocol: protocol,
530					testType: serverTest,
531					name:     "MinimumVersion-Server-" + suffix,
532					config: Config{
533						MaxVersion: runnerVers.version,
534					},
535					flags: flags,
536					expectations: connectionExpectations{
537						version: expectedVersion,
538					},
539					shouldFail:         shouldFail,
540					expectedError:      expectedError,
541					expectedLocalError: expectedLocalError,
542					// The version name check does not recognize the
543					// |excludeFlag| construction in |flags|.
544					skipVersionNameCheck: true,
545				})
546				testCases = append(testCases, testCase{
547					protocol: protocol,
548					testType: serverTest,
549					name:     "MinimumVersion-Server2-" + suffix,
550					config: Config{
551						MaxVersion: runnerVers.version,
552					},
553					flags: flags2,
554					expectations: connectionExpectations{
555						version: expectedVersion,
556					},
557					shouldFail:         shouldFail,
558					expectedError:      expectedError,
559					expectedLocalError: expectedLocalError,
560				})
561			}
562		}
563	}
564}
565
566func addRecordVersionTests() {
567	for _, ver := range tlsVersions {
568		// Test that the record version is enforced.
569		testCases = append(testCases, testCase{
570			name: "CheckRecordVersion-" + ver.name,
571			config: Config{
572				MinVersion: ver.version,
573				MaxVersion: ver.version,
574				Bugs: ProtocolBugs{
575					SendRecordVersion: 0x03ff,
576				},
577			},
578			shouldFail:    true,
579			expectedError: ":WRONG_VERSION_NUMBER:",
580		})
581
582		// Test that the ClientHello may use any record version, for
583		// compatibility reasons.
584		testCases = append(testCases, testCase{
585			testType: serverTest,
586			name:     "LooseInitialRecordVersion-" + ver.name,
587			config: Config{
588				MinVersion: ver.version,
589				MaxVersion: ver.version,
590				Bugs: ProtocolBugs{
591					SendInitialRecordVersion: 0x03ff,
592				},
593			},
594		})
595
596		// Test that garbage ClientHello record versions are rejected.
597		testCases = append(testCases, testCase{
598			testType: serverTest,
599			name:     "GarbageInitialRecordVersion-" + ver.name,
600			config: Config{
601				MinVersion: ver.version,
602				MaxVersion: ver.version,
603				Bugs: ProtocolBugs{
604					SendInitialRecordVersion: 0xffff,
605				},
606			},
607			shouldFail:    true,
608			expectedError: ":WRONG_VERSION_NUMBER:",
609		})
610	}
611}
612