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	"strconv"
20)
21
22var testCurves = []struct {
23	name string
24	id   CurveID
25}{
26	{"P-256", CurveP256},
27	{"P-384", CurveP384},
28	{"P-521", CurveP521},
29	{"X25519", CurveX25519},
30	{"Kyber", CurveX25519Kyber768},
31	{"X25519MLKEM768", CurveX25519MLKEM768},
32	{"MLKEM1024", CurveMLKEM1024},
33}
34
35const bogusCurve = 0x1234
36
37func isPqGroup(r CurveID) bool {
38	return r == CurveX25519Kyber768 || isMLKEMGroup(r)
39}
40
41func isMLKEMGroup(r CurveID) bool {
42	return r == CurveX25519MLKEM768 || r == CurveMLKEM1024
43}
44
45func isECDHGroup(r CurveID) bool {
46	return r == CurveP256 || r == CurveP384 || r == CurveP521
47}
48
49func isX25519Group(r CurveID) bool {
50	return r == CurveX25519 || r == CurveX25519Kyber768 || r == CurveX25519MLKEM768
51}
52
53func addCurveTests() {
54	// A set of cipher suites that ensures some curve-using mode is used.
55	// Without this, servers may fall back to RSA key exchange.
56	ecdheCiphers := []uint16{
57		TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
58		TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
59		TLS_AES_256_GCM_SHA384,
60	}
61
62	// Not all curves are enabled by default, so these tests explicitly enable
63	// the curve under test in the shim.
64	for _, curve := range testCurves {
65		for _, ver := range tlsVersions {
66			if isPqGroup(curve.id) && ver.version < VersionTLS13 {
67				continue
68			}
69			for _, testType := range []testType{clientTest, serverTest} {
70				suffix := fmt.Sprintf("%s-%s-%s", testType, curve.name, ver.name)
71
72				testCases = append(testCases, testCase{
73					testType: testType,
74					name:     "CurveTest-" + suffix,
75					config: Config{
76						MaxVersion:       ver.version,
77						CipherSuites:     ecdheCiphers,
78						CurvePreferences: []CurveID{curve.id},
79					},
80					flags: append(
81						[]string{"-expect-curve-id", strconv.Itoa(int(curve.id))},
82						flagCurves("-curves", []CurveID{curve.id})...,
83					),
84					expectations: connectionExpectations{
85						curveID: curve.id,
86					},
87				})
88
89				badKeyShareLocalError := "remote error: illegal parameter"
90				if testType == clientTest && ver.version >= VersionTLS13 {
91					// If the shim is a TLS 1.3 client and the runner sends a bad
92					// key share, the runner never reads the client's cleartext
93					// alert because the runner has already started encrypting by
94					// the time the client sees it.
95					badKeyShareLocalError = "local error: bad record MAC"
96				}
97
98				testCases = append(testCases, testCase{
99					testType: testType,
100					name:     "CurveTest-Invalid-TruncateKeyShare-" + suffix,
101					config: Config{
102						MaxVersion:       ver.version,
103						CipherSuites:     ecdheCiphers,
104						CurvePreferences: []CurveID{curve.id},
105						Bugs: ProtocolBugs{
106							TruncateKeyShare: true,
107						},
108					},
109					flags:              flagCurves("-curves", []CurveID{curve.id}),
110					shouldFail:         true,
111					expectedError:      ":BAD_ECPOINT:",
112					expectedLocalError: badKeyShareLocalError,
113				})
114
115				testCases = append(testCases, testCase{
116					testType: testType,
117					name:     "CurveTest-Invalid-PadKeyShare-" + suffix,
118					config: Config{
119						MaxVersion:       ver.version,
120						CipherSuites:     ecdheCiphers,
121						CurvePreferences: []CurveID{curve.id},
122						Bugs: ProtocolBugs{
123							PadKeyShare: true,
124						},
125					},
126					flags:              flagCurves("-curves", []CurveID{curve.id}),
127					shouldFail:         true,
128					expectedError:      ":BAD_ECPOINT:",
129					expectedLocalError: badKeyShareLocalError,
130				})
131
132				if isECDHGroup(curve.id) {
133					testCases = append(testCases, testCase{
134						testType: testType,
135						name:     "CurveTest-Invalid-Compressed-" + suffix,
136						config: Config{
137							MaxVersion:       ver.version,
138							CipherSuites:     ecdheCiphers,
139							CurvePreferences: []CurveID{curve.id},
140							Bugs: ProtocolBugs{
141								SendCompressedCoordinates: true,
142							},
143						},
144						flags:              flagCurves("-curves", []CurveID{curve.id}),
145						shouldFail:         true,
146						expectedError:      ":BAD_ECPOINT:",
147						expectedLocalError: badKeyShareLocalError,
148					})
149					testCases = append(testCases, testCase{
150						testType: testType,
151						name:     "CurveTest-Invalid-NotOnCurve-" + suffix,
152						config: Config{
153							MaxVersion:       ver.version,
154							CipherSuites:     ecdheCiphers,
155							CurvePreferences: []CurveID{curve.id},
156							Bugs: ProtocolBugs{
157								ECDHPointNotOnCurve: true,
158							},
159						},
160						flags:              flagCurves("-curves", []CurveID{curve.id}),
161						shouldFail:         true,
162						expectedError:      ":BAD_ECPOINT:",
163						expectedLocalError: badKeyShareLocalError,
164					})
165				}
166
167				if isX25519Group(curve.id) {
168					// Implementations should mask off the high order bit in X25519.
169					testCases = append(testCases, testCase{
170						testType: testType,
171						name:     "CurveTest-SetX25519HighBit-" + suffix,
172						config: Config{
173							MaxVersion:       ver.version,
174							CipherSuites:     ecdheCiphers,
175							CurvePreferences: []CurveID{curve.id},
176							Bugs: ProtocolBugs{
177								SetX25519HighBit: true,
178							},
179						},
180						flags: flagCurves("-curves", []CurveID{curve.id}),
181						expectations: connectionExpectations{
182							curveID: curve.id,
183						},
184					})
185
186					// Implementations should reject low order points.
187					testCases = append(testCases, testCase{
188						testType: testType,
189						name:     "CurveTest-Invalid-LowOrderX25519Point-" + suffix,
190						config: Config{
191							MaxVersion:       ver.version,
192							CipherSuites:     ecdheCiphers,
193							CurvePreferences: []CurveID{curve.id},
194							Bugs: ProtocolBugs{
195								LowOrderX25519Point: true,
196							},
197						},
198						flags:              flagCurves("-curves", []CurveID{curve.id}),
199						shouldFail:         true,
200						expectedError:      ":BAD_ECPOINT:",
201						expectedLocalError: badKeyShareLocalError,
202					})
203				}
204
205				if isMLKEMGroup(curve.id) && testType == serverTest {
206					testCases = append(testCases, testCase{
207						testType: testType,
208						name:     "CurveTest-Invalid-MLKEMEncapKeyNotReduced-" + suffix,
209						config: Config{
210							MaxVersion:       ver.version,
211							CipherSuites:     ecdheCiphers,
212							CurvePreferences: []CurveID{curve.id},
213							Bugs: ProtocolBugs{
214								MLKEMEncapKeyNotReduced: true,
215							},
216						},
217						flags:              flagCurves("-curves", []CurveID{curve.id}),
218						shouldFail:         true,
219						expectedError:      ":BAD_ECPOINT:",
220						expectedLocalError: badKeyShareLocalError,
221					})
222				}
223			}
224		}
225	}
226
227	// The server must be tolerant to bogus curves.
228	testCases = append(testCases, testCase{
229		testType: serverTest,
230		name:     "UnknownCurve",
231		config: Config{
232			MaxVersion:       VersionTLS12,
233			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
234			CurvePreferences: []CurveID{bogusCurve, CurveP256},
235		},
236	})
237
238	// The server must be tolerant to bogus curves.
239	testCases = append(testCases, testCase{
240		testType: serverTest,
241		name:     "UnknownCurve-TLS13",
242		config: Config{
243			MaxVersion:       VersionTLS13,
244			CurvePreferences: []CurveID{bogusCurve, CurveP256},
245		},
246	})
247
248	// The server must not consider ECDHE ciphers when there are no
249	// supported curves.
250	testCases = append(testCases, testCase{
251		testType: serverTest,
252		name:     "NoSupportedCurves",
253		config: Config{
254			MaxVersion:   VersionTLS12,
255			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
256			Bugs: ProtocolBugs{
257				NoSupportedCurves: true,
258			},
259		},
260		shouldFail:    true,
261		expectedError: ":NO_SHARED_CIPHER:",
262	})
263	testCases = append(testCases, testCase{
264		testType: serverTest,
265		name:     "NoSupportedCurves-TLS13",
266		config: Config{
267			MaxVersion: VersionTLS13,
268			Bugs: ProtocolBugs{
269				NoSupportedCurves: true,
270			},
271		},
272		shouldFail:    true,
273		expectedError: ":NO_SHARED_GROUP:",
274	})
275
276	// The server must fall back to another cipher when there are no
277	// supported curves.
278	testCases = append(testCases, testCase{
279		testType: serverTest,
280		name:     "NoCommonCurves",
281		config: Config{
282			MaxVersion: VersionTLS12,
283			CipherSuites: []uint16{
284				TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
285				TLS_RSA_WITH_AES_128_GCM_SHA256,
286			},
287			CurvePreferences: []CurveID{21 /* P-224 */},
288		},
289		expectations: connectionExpectations{
290			cipher: TLS_RSA_WITH_AES_128_GCM_SHA256,
291		},
292	})
293
294	// The client must reject bogus curves and disabled curves.
295	testCases = append(testCases, testCase{
296		name: "BadECDHECurve",
297		config: Config{
298			MaxVersion:   VersionTLS12,
299			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
300			Bugs: ProtocolBugs{
301				SendCurve: bogusCurve,
302			},
303		},
304		shouldFail:    true,
305		expectedError: ":WRONG_CURVE:",
306	})
307	testCases = append(testCases, testCase{
308		name: "BadECDHECurve-TLS13",
309		config: Config{
310			MaxVersion: VersionTLS13,
311			Bugs: ProtocolBugs{
312				SendCurve: bogusCurve,
313			},
314		},
315		shouldFail:    true,
316		expectedError: ":WRONG_CURVE:",
317	})
318
319	testCases = append(testCases, testCase{
320		name: "UnsupportedCurve",
321		config: Config{
322			MaxVersion:       VersionTLS12,
323			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
324			CurvePreferences: []CurveID{CurveP256},
325			Bugs: ProtocolBugs{
326				IgnorePeerCurvePreferences: true,
327			},
328		},
329		flags:         []string{"-curves", strconv.Itoa(int(CurveP384))},
330		shouldFail:    true,
331		expectedError: ":WRONG_CURVE:",
332	})
333
334	testCases = append(testCases, testCase{
335		// TODO(davidben): Add a TLS 1.3 version where
336		// HelloRetryRequest requests an unsupported curve.
337		name: "UnsupportedCurve-ServerHello-TLS13",
338		config: Config{
339			MaxVersion:       VersionTLS13,
340			CurvePreferences: []CurveID{CurveP384},
341			Bugs: ProtocolBugs{
342				SendCurve: CurveP256,
343			},
344		},
345		flags:         []string{"-curves", strconv.Itoa(int(CurveP384))},
346		shouldFail:    true,
347		expectedError: ":WRONG_CURVE:",
348	})
349
350	// The previous curve ID should be reported on TLS 1.2 resumption.
351	testCases = append(testCases, testCase{
352		name: "CurveID-Resume-Client",
353		config: Config{
354			MaxVersion:       VersionTLS12,
355			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
356			CurvePreferences: []CurveID{CurveX25519},
357		},
358		flags:         []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))},
359		resumeSession: true,
360	})
361	testCases = append(testCases, testCase{
362		testType: serverTest,
363		name:     "CurveID-Resume-Server",
364		config: Config{
365			MaxVersion:       VersionTLS12,
366			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
367			CurvePreferences: []CurveID{CurveX25519},
368		},
369		flags:         []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))},
370		resumeSession: true,
371	})
372
373	// TLS 1.3 allows resuming at a differet curve. If this happens, the new
374	// one should be reported.
375	testCases = append(testCases, testCase{
376		name: "CurveID-Resume-Client-TLS13",
377		config: Config{
378			MaxVersion:       VersionTLS13,
379			CurvePreferences: []CurveID{CurveX25519},
380		},
381		resumeConfig: &Config{
382			MaxVersion:       VersionTLS13,
383			CurvePreferences: []CurveID{CurveP256},
384		},
385		flags: []string{
386			"-on-initial-expect-curve-id", strconv.Itoa(int(CurveX25519)),
387			"-on-resume-expect-curve-id", strconv.Itoa(int(CurveP256)),
388		},
389		resumeSession: true,
390	})
391	testCases = append(testCases, testCase{
392		testType: serverTest,
393		name:     "CurveID-Resume-Server-TLS13",
394		config: Config{
395			MaxVersion:       VersionTLS13,
396			CurvePreferences: []CurveID{CurveX25519},
397		},
398		resumeConfig: &Config{
399			MaxVersion:       VersionTLS13,
400			CurvePreferences: []CurveID{CurveP256},
401		},
402		flags: []string{
403			"-on-initial-expect-curve-id", strconv.Itoa(int(CurveX25519)),
404			"-on-resume-expect-curve-id", strconv.Itoa(int(CurveP256)),
405		},
406		resumeSession: true,
407	})
408
409	// Server-sent point formats are legal in TLS 1.2, but not in TLS 1.3.
410	testCases = append(testCases, testCase{
411		name: "PointFormat-ServerHello-TLS12",
412		config: Config{
413			MaxVersion: VersionTLS12,
414			Bugs: ProtocolBugs{
415				SendSupportedPointFormats: []byte{pointFormatUncompressed},
416			},
417		},
418	})
419	testCases = append(testCases, testCase{
420		name: "PointFormat-EncryptedExtensions-TLS13",
421		config: Config{
422			MaxVersion: VersionTLS13,
423			Bugs: ProtocolBugs{
424				SendSupportedPointFormats: []byte{pointFormatUncompressed},
425			},
426		},
427		shouldFail:    true,
428		expectedError: ":ERROR_PARSING_EXTENSION:",
429	})
430
431	// Server-sent supported groups/curves are legal in TLS 1.3. They are
432	// illegal in TLS 1.2, but some servers send them anyway, so we must
433	// tolerate them.
434	testCases = append(testCases, testCase{
435		name: "SupportedCurves-ServerHello-TLS12",
436		config: Config{
437			MaxVersion: VersionTLS12,
438			Bugs: ProtocolBugs{
439				SendServerSupportedCurves: true,
440			},
441		},
442	})
443	testCases = append(testCases, testCase{
444		name: "SupportedCurves-EncryptedExtensions-TLS13",
445		config: Config{
446			MaxVersion: VersionTLS13,
447			Bugs: ProtocolBugs{
448				SendServerSupportedCurves: true,
449			},
450		},
451	})
452
453	// Test that we tolerate unknown point formats, as long as
454	// pointFormatUncompressed is present. Limit ciphers to ECDHE ciphers to
455	// check they are still functional.
456	testCases = append(testCases, testCase{
457		name: "PointFormat-Client-Tolerance",
458		config: Config{
459			MaxVersion: VersionTLS12,
460			Bugs: ProtocolBugs{
461				SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime},
462			},
463		},
464	})
465	testCases = append(testCases, testCase{
466		testType: serverTest,
467		name:     "PointFormat-Server-Tolerance",
468		config: Config{
469			MaxVersion:   VersionTLS12,
470			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
471			Bugs: ProtocolBugs{
472				SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime},
473			},
474		},
475	})
476
477	// Test TLS 1.2 does not require the point format extension to be
478	// present.
479	testCases = append(testCases, testCase{
480		name: "PointFormat-Client-Missing",
481		config: Config{
482			MaxVersion:   VersionTLS12,
483			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
484			Bugs: ProtocolBugs{
485				SendSupportedPointFormats: []byte{},
486			},
487		},
488	})
489	testCases = append(testCases, testCase{
490		testType: serverTest,
491		name:     "PointFormat-Server-Missing",
492		config: Config{
493			MaxVersion:   VersionTLS12,
494			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
495			Bugs: ProtocolBugs{
496				SendSupportedPointFormats: []byte{},
497			},
498		},
499	})
500
501	// If the point format extension is present, uncompressed points must be
502	// offered. BoringSSL requires this whether or not ECDHE is used.
503	testCases = append(testCases, testCase{
504		name: "PointFormat-Client-MissingUncompressed",
505		config: Config{
506			MaxVersion: VersionTLS12,
507			Bugs: ProtocolBugs{
508				SendSupportedPointFormats: []byte{pointFormatCompressedPrime},
509			},
510		},
511		shouldFail:    true,
512		expectedError: ":ERROR_PARSING_EXTENSION:",
513	})
514	testCases = append(testCases, testCase{
515		testType: serverTest,
516		name:     "PointFormat-Server-MissingUncompressed",
517		config: Config{
518			MaxVersion: VersionTLS12,
519			Bugs: ProtocolBugs{
520				SendSupportedPointFormats: []byte{pointFormatCompressedPrime},
521			},
522		},
523		shouldFail:    true,
524		expectedError: ":ERROR_PARSING_EXTENSION:",
525	})
526
527	// Post-quantum groups require TLS 1.3.
528	for _, curve := range testCurves {
529		if !isPqGroup(curve.id) {
530			continue
531		}
532
533		// Post-quantum groups should not be offered by a TLS 1.2 client.
534		testCases = append(testCases, testCase{
535			name: "TLS12ClientShouldNotOffer-" + curve.name,
536			config: Config{
537				Bugs: ProtocolBugs{
538					FailIfPostQuantumOffered: true,
539				},
540			},
541			flags: []string{
542				"-max-version", strconv.Itoa(VersionTLS12),
543				"-curves", strconv.Itoa(int(curve.id)),
544				"-curves", strconv.Itoa(int(CurveX25519)),
545			},
546		})
547
548		// Post-quantum groups should not be selected by a TLS 1.2 server.
549		testCases = append(testCases, testCase{
550			testType: serverTest,
551			name:     "TLS12ServerShouldNotSelect-" + curve.name,
552			flags: []string{
553				"-max-version", strconv.Itoa(VersionTLS12),
554				"-curves", strconv.Itoa(int(curve.id)),
555				"-curves", strconv.Itoa(int(CurveX25519)),
556			},
557			expectations: connectionExpectations{
558				curveID: CurveX25519,
559			},
560		})
561
562		// If a TLS 1.2 server selects a post-quantum group anyway, the client
563		// should not accept it.
564		testCases = append(testCases, testCase{
565			name: "ClientShouldNotAllowInTLS12-" + curve.name,
566			config: Config{
567				MaxVersion: VersionTLS12,
568				Bugs: ProtocolBugs{
569					SendCurve: curve.id,
570				},
571			},
572			flags: []string{
573				"-curves", strconv.Itoa(int(curve.id)),
574				"-curves", strconv.Itoa(int(CurveX25519)),
575			},
576			shouldFail:         true,
577			expectedError:      ":WRONG_CURVE:",
578			expectedLocalError: "remote error: illegal parameter",
579		})
580	}
581
582	// ML-KEM and Kyber should not be offered by default as a client.
583	testCases = append(testCases, testCase{
584		name: "PostQuantumNotEnabledByDefaultInClients",
585		config: Config{
586			MinVersion: VersionTLS13,
587			Bugs: ProtocolBugs{
588				FailIfPostQuantumOffered: true,
589			},
590		},
591	})
592
593	for _, curve := range testCurves {
594		if !isMLKEMGroup(curve.id) {
595			continue
596		}
597
598		// If ML-KEM is offered, both X25519 and ML-KEM should have a key-share.
599		testCases = append(testCases, testCase{
600			name: "NotJustMLKEMKeyShare-" + curve.name,
601			config: Config{
602				MinVersion: VersionTLS13,
603				Bugs: ProtocolBugs{
604					ExpectedKeyShares: []CurveID{curve.id, CurveX25519},
605				},
606			},
607			flags: []string{
608				"-curves", strconv.Itoa(int(curve.id)),
609				"-curves", strconv.Itoa(int(CurveX25519)),
610				"-expect-curve-id", strconv.Itoa(int(curve.id)),
611			},
612		})
613
614		// ... and the other way around
615		testCases = append(testCases, testCase{
616			name: "MLKEMKeyShareIncludedSecond-" + curve.name,
617			config: Config{
618				MinVersion: VersionTLS13,
619				Bugs: ProtocolBugs{
620					ExpectedKeyShares: []CurveID{CurveX25519, curve.id},
621				},
622			},
623			flags: []string{
624				"-curves", strconv.Itoa(int(CurveX25519)),
625				"-curves", strconv.Itoa(int(curve.id)),
626				"-expect-curve-id", strconv.Itoa(int(CurveX25519)),
627			},
628		})
629
630		// ... and even if there's another curve in the middle because it's the
631		// first classical and first post-quantum "curves" that get key shares
632		// included.
633		testCases = append(testCases, testCase{
634			name: "MLKEMKeyShareIncludedThird-" + curve.name,
635			config: Config{
636				MinVersion: VersionTLS13,
637				Bugs: ProtocolBugs{
638					ExpectedKeyShares: []CurveID{CurveX25519, curve.id},
639				},
640			},
641			flags: []string{
642				"-curves", strconv.Itoa(int(CurveX25519)),
643				"-curves", strconv.Itoa(int(CurveP256)),
644				"-curves", strconv.Itoa(int(curve.id)),
645				"-expect-curve-id", strconv.Itoa(int(CurveX25519)),
646			},
647		})
648
649		// If ML-KEM is the only configured curve, the key share is sent.
650		testCases = append(testCases, testCase{
651			name: "JustConfiguringMLKEMWorks-" + curve.name,
652			config: Config{
653				MinVersion: VersionTLS13,
654				Bugs: ProtocolBugs{
655					ExpectedKeyShares: []CurveID{curve.id},
656				},
657			},
658			flags: []string{
659				"-curves", strconv.Itoa(int(curve.id)),
660				"-expect-curve-id", strconv.Itoa(int(curve.id)),
661			},
662		})
663
664		// If both ML-KEM and Kyber are configured, only the preferred one's
665		// key share should be sent.
666		testCases = append(testCases, testCase{
667			name: "BothMLKEMAndKyber-" + curve.name,
668			config: Config{
669				MinVersion: VersionTLS13,
670				Bugs: ProtocolBugs{
671					ExpectedKeyShares: []CurveID{curve.id},
672				},
673			},
674			flags: []string{
675				"-curves", strconv.Itoa(int(curve.id)),
676				"-curves", strconv.Itoa(int(CurveX25519Kyber768)),
677				"-expect-curve-id", strconv.Itoa(int(curve.id)),
678			},
679		})
680	}
681
682	// As a server, ML-KEMs and Kyber are not yet supported by default.
683	testCases = append(testCases, testCase{
684		testType: serverTest,
685		name:     "PostQuantumNotEnabledByDefaultForAServer",
686		config: Config{
687			MinVersion:       VersionTLS13,
688			CurvePreferences: []CurveID{CurveX25519MLKEM768, CurveMLKEM1024, CurveX25519Kyber768, CurveX25519},
689			DefaultCurves:    []CurveID{CurveX25519MLKEM768, CurveMLKEM1024, CurveX25519Kyber768},
690		},
691		flags: []string{
692			"-server-preference",
693			"-expect-curve-id", strconv.Itoa(int(CurveX25519)),
694		},
695	})
696
697	// If two ML-KEMs are configured, only the preferred one's
698	// key share should be sent.
699	testCases = append(testCases, testCase{
700		name: "TwoMLKEMs",
701		config: Config{
702			MinVersion: VersionTLS13,
703			Bugs: ProtocolBugs{
704				ExpectedKeyShares: []CurveID{CurveMLKEM1024},
705			},
706		},
707		flags: []string{
708			"-curves", strconv.Itoa(int(CurveMLKEM1024)),
709			"-curves", strconv.Itoa(int(CurveX25519MLKEM768)),
710			"-expect-curve-id", strconv.Itoa(int(CurveMLKEM1024)),
711		},
712	})
713
714	// In TLS 1.2, the curve list is also used to signal ECDSA curves.
715	testCases = append(testCases, testCase{
716		testType: serverTest,
717		name:     "CheckECDSACurve-TLS12",
718		config: Config{
719			MinVersion:       VersionTLS12,
720			MaxVersion:       VersionTLS12,
721			CurvePreferences: []CurveID{CurveP384},
722		},
723		shimCertificate: &ecdsaP256Certificate,
724		shouldFail:      true,
725		expectedError:   ":WRONG_CURVE:",
726	})
727
728	// If the ECDSA certificate is ineligible due to a curve mismatch, the
729	// server may still consider a PSK cipher suite.
730	testCases = append(testCases, testCase{
731		testType: serverTest,
732		name:     "CheckECDSACurve-PSK-TLS12",
733		config: Config{
734			MinVersion: VersionTLS12,
735			MaxVersion: VersionTLS12,
736			CipherSuites: []uint16{
737				TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
738				TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
739			},
740			CurvePreferences:     []CurveID{CurveP384},
741			PreSharedKey:         []byte("12345"),
742			PreSharedKeyIdentity: "luggage combo",
743		},
744		shimCertificate: &ecdsaP256Certificate,
745		expectations: connectionExpectations{
746			cipher: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
747		},
748		flags: []string{
749			"-psk", "12345",
750			"-psk-identity", "luggage combo",
751		},
752	})
753
754	// In TLS 1.3, the curve list only controls ECDH.
755	testCases = append(testCases, testCase{
756		testType: serverTest,
757		name:     "CheckECDSACurve-NotApplicable-TLS13",
758		config: Config{
759			MinVersion:       VersionTLS13,
760			MaxVersion:       VersionTLS13,
761			CurvePreferences: []CurveID{CurveP384},
762		},
763		shimCertificate: &ecdsaP256Certificate,
764	})
765}
766