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 addExtendedMasterSecretTests() {
18	const expectEMSFlag = "-expect-extended-master-secret"
19
20	for _, with := range []bool{false, true} {
21		prefix := "No"
22		if with {
23			prefix = ""
24		}
25
26		for _, isClient := range []bool{false, true} {
27			suffix := "-Server"
28			testType := serverTest
29			if isClient {
30				suffix = "-Client"
31				testType = clientTest
32			}
33
34			for _, ver := range tlsVersions {
35				// In TLS 1.3, the extension is irrelevant and
36				// always reports as enabled.
37				var flags []string
38				if with || ver.version >= VersionTLS13 {
39					flags = []string{expectEMSFlag}
40				}
41
42				testCases = append(testCases, testCase{
43					testType: testType,
44					name:     prefix + "ExtendedMasterSecret-" + ver.name + suffix,
45					config: Config{
46						MinVersion: ver.version,
47						MaxVersion: ver.version,
48						Bugs: ProtocolBugs{
49							NoExtendedMasterSecret:      !with,
50							RequireExtendedMasterSecret: with,
51						},
52					},
53					flags: flags,
54				})
55			}
56		}
57	}
58
59	for _, isClient := range []bool{false, true} {
60		for _, supportedInFirstConnection := range []bool{false, true} {
61			for _, supportedInResumeConnection := range []bool{false, true} {
62				boolToWord := func(b bool) string {
63					if b {
64						return "Yes"
65					}
66					return "No"
67				}
68				suffix := boolToWord(supportedInFirstConnection) + "To" + boolToWord(supportedInResumeConnection) + "-"
69				if isClient {
70					suffix += "Client"
71				} else {
72					suffix += "Server"
73				}
74
75				supportedConfig := Config{
76					MaxVersion: VersionTLS12,
77					Bugs: ProtocolBugs{
78						RequireExtendedMasterSecret: true,
79					},
80				}
81
82				noSupportConfig := Config{
83					MaxVersion: VersionTLS12,
84					Bugs: ProtocolBugs{
85						NoExtendedMasterSecret: true,
86					},
87				}
88
89				test := testCase{
90					name:          "ExtendedMasterSecret-" + suffix,
91					resumeSession: true,
92				}
93
94				if !isClient {
95					test.testType = serverTest
96				}
97
98				if supportedInFirstConnection {
99					test.config = supportedConfig
100				} else {
101					test.config = noSupportConfig
102				}
103
104				if supportedInResumeConnection {
105					test.resumeConfig = &supportedConfig
106				} else {
107					test.resumeConfig = &noSupportConfig
108				}
109
110				switch suffix {
111				case "YesToYes-Client", "YesToYes-Server":
112					// When a session is resumed, it should
113					// still be aware that its master
114					// secret was generated via EMS and
115					// thus it's safe to use tls-unique.
116					test.flags = []string{expectEMSFlag}
117				case "NoToYes-Server":
118					// If an original connection did not
119					// contain EMS, but a resumption
120					// handshake does, then a server should
121					// not resume the session.
122					test.expectResumeRejected = true
123				case "YesToNo-Server":
124					// Resuming an EMS session without the
125					// EMS extension should cause the
126					// server to abort the connection.
127					test.shouldFail = true
128					test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"
129				case "NoToYes-Client":
130					// A client should abort a connection
131					// where the server resumed a non-EMS
132					// session but echoed the EMS
133					// extension.
134					test.shouldFail = true
135					test.expectedError = ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:"
136				case "YesToNo-Client":
137					// A client should abort a connection
138					// where the server didn't echo EMS
139					// when the session used it.
140					test.shouldFail = true
141					test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:"
142				}
143
144				testCases = append(testCases, test)
145			}
146		}
147	}
148
149	// Switching EMS on renegotiation is forbidden.
150	testCases = append(testCases, testCase{
151		name: "ExtendedMasterSecret-Renego-NoEMS",
152		config: Config{
153			MaxVersion: VersionTLS12,
154			Bugs: ProtocolBugs{
155				NoExtendedMasterSecret:                true,
156				NoExtendedMasterSecretOnRenegotiation: true,
157			},
158		},
159		renegotiate: 1,
160		flags: []string{
161			"-renegotiate-freely",
162			"-expect-total-renegotiations", "1",
163		},
164	})
165
166	testCases = append(testCases, testCase{
167		name: "ExtendedMasterSecret-Renego-Upgrade",
168		config: Config{
169			MaxVersion: VersionTLS12,
170			Bugs: ProtocolBugs{
171				NoExtendedMasterSecret: true,
172			},
173		},
174		renegotiate: 1,
175		flags: []string{
176			"-renegotiate-freely",
177			"-expect-total-renegotiations", "1",
178		},
179		shouldFail:    true,
180		expectedError: ":RENEGOTIATION_EMS_MISMATCH:",
181	})
182
183	testCases = append(testCases, testCase{
184		name: "ExtendedMasterSecret-Renego-Downgrade",
185		config: Config{
186			MaxVersion: VersionTLS12,
187			Bugs: ProtocolBugs{
188				NoExtendedMasterSecretOnRenegotiation: true,
189			},
190		},
191		renegotiate: 1,
192		flags: []string{
193			"-renegotiate-freely",
194			"-expect-total-renegotiations", "1",
195		},
196		shouldFail:    true,
197		expectedError: ":RENEGOTIATION_EMS_MISMATCH:",
198	})
199}
200