]>
Commit | Line | Data |
---|---|---|
74726750 | 1 | # -*- mode: perl; -*- |
33388b44 | 2 | # Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. |
74726750 | 3 | # |
909f1a2e | 4 | # Licensed under the Apache License 2.0 (the "License"). You may not use |
74726750 EK |
5 | # this file except in compliance with the License. You can obtain a copy |
6 | # in the file LICENSE in the source distribution or at | |
7 | # https://www.openssl.org/source/license.html | |
8 | ||
9 | ||
10 | ## Test version negotiation | |
11 | ||
12 | package ssltests; | |
13 | ||
14 | use strict; | |
15 | use warnings; | |
16 | ||
17 | use List::Util qw/max min/; | |
18 | ||
19 | use OpenSSL::Test; | |
1763ab10 | 20 | use OpenSSL::Test::Utils qw/anydisabled alldisabled disabled/; |
74726750 EK |
21 | setup("no_test_here"); |
22 | ||
582a17d6 | 23 | my @tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"); |
682bc861 | 24 | my @tls_protocols_fips = ("TLSv1.2", "TLSv1.3"); |
74726750 | 25 | # undef stands for "no limit". |
582a17d6 | 26 | my @min_tls_protocols = (undef, "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"); |
682bc861 | 27 | my @min_tls_protocols_fips = (undef, "TLSv1.2", "TLSv1.3"); |
582a17d6 | 28 | my @max_tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", undef); |
682bc861 | 29 | my @max_tls_protocols_fips = ("TLSv1.2", "TLSv1.3", undef); |
74726750 | 30 | |
582a17d6 | 31 | my @is_tls_disabled = anydisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3"); |
682bc861 | 32 | my @is_tls_disabled_fips = anydisabled("tls1_2", "tls1_3"); |
74726750 EK |
33 | |
34 | my $min_tls_enabled; my $max_tls_enabled; | |
682bc861 | 35 | my $min_tls_enabled_fips; my $max_tls_enabled_fips; |
74726750 EK |
36 | |
37 | # Protocol configuration works in cascades, i.e., | |
38 | # $no_tls1_1 disables TLSv1.1 and below. | |
39 | # | |
40 | # $min_enabled and $max_enabled will be correct if there is at least one | |
41 | # protocol enabled. | |
682bc861 MC |
42 | |
43 | sub min_prot_enabled { | |
44 | my $protref = shift; | |
45 | my $disabledref = shift; | |
46 | my @protocols = @{$protref}; | |
47 | my @is_disabled = @{$disabledref}; | |
48 | my $min_enabled; | |
49 | ||
50 | foreach my $i (0..$#protocols) { | |
51 | if (!$is_disabled[$i]) { | |
52 | $min_enabled = $i; | |
53 | last; | |
54 | } | |
74726750 | 55 | } |
682bc861 | 56 | return $min_enabled; |
74726750 EK |
57 | } |
58 | ||
682bc861 MC |
59 | sub max_prot_enabled { |
60 | my $protref = shift; | |
61 | my $disabledref = shift; | |
62 | my @protocols = @{$protref}; | |
63 | my @is_disabled = @{$disabledref}; | |
64 | my $max_enabled; | |
65 | ||
66 | foreach my $i (0..$#protocols) { | |
67 | if (!$is_disabled[$i]) { | |
68 | $max_enabled = $i; | |
69 | } | |
74726750 | 70 | } |
682bc861 | 71 | return $max_enabled; |
74726750 EK |
72 | } |
73 | ||
682bc861 MC |
74 | $min_tls_enabled = min_prot_enabled(\@tls_protocols, \@is_tls_disabled); |
75 | $max_tls_enabled = max_prot_enabled(\@tls_protocols, \@is_tls_disabled); | |
76 | $min_tls_enabled_fips = min_prot_enabled(\@tls_protocols_fips, \@is_tls_disabled_fips); | |
77 | $max_tls_enabled_fips = max_prot_enabled(\@tls_protocols_fips, \@is_tls_disabled_fips); | |
78 | ||
79 | ||
74726750 | 80 | my @dtls_protocols = ("DTLSv1", "DTLSv1.2"); |
682bc861 | 81 | my @dtls_protocols_fips = ("DTLSv1.2"); |
74726750 EK |
82 | # undef stands for "no limit". |
83 | my @min_dtls_protocols = (undef, "DTLSv1", "DTLSv1.2"); | |
682bc861 | 84 | my @min_dtls_protocols_fips = (undef, "DTLSv1.2"); |
74726750 | 85 | my @max_dtls_protocols = ("DTLSv1", "DTLSv1.2", undef); |
682bc861 | 86 | my @max_dtls_protocols_fips = ("DTLSv1.2", undef); |
74726750 EK |
87 | |
88 | my @is_dtls_disabled = anydisabled("dtls1", "dtls1_2"); | |
682bc861 | 89 | my @is_dtls_disabled_fips = anydisabled("dtls1_2"); |
74726750 EK |
90 | |
91 | my $min_dtls_enabled; my $max_dtls_enabled; | |
682bc861 | 92 | my $min_dtls_enabled_fips; my $max_dtls_enabled_fips; |
74726750 EK |
93 | |
94 | # $min_enabled and $max_enabled will be correct if there is at least one | |
95 | # protocol enabled. | |
682bc861 MC |
96 | $min_dtls_enabled = min_prot_enabled(\@dtls_protocols, \@is_dtls_disabled); |
97 | $max_dtls_enabled = max_prot_enabled(\@dtls_protocols, \@is_dtls_disabled); | |
98 | $min_dtls_enabled_fips = min_prot_enabled(\@dtls_protocols_fips, \@is_dtls_disabled_fips); | |
99 | $max_dtls_enabled_fips = max_prot_enabled(\@dtls_protocols_fips, \@is_dtls_disabled_fips); | |
74726750 | 100 | |
590ed3d7 EK |
101 | sub no_tests { |
102 | my ($dtls) = @_; | |
103 | return $dtls ? alldisabled("dtls1", "dtls1_2") : | |
582a17d6 | 104 | alldisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3"); |
590ed3d7 EK |
105 | } |
106 | ||
107 | sub generate_version_tests { | |
682bc861 MC |
108 | my $method = shift; |
109 | my $fips = shift; | |
74726750 EK |
110 | |
111 | my $dtls = $method eq "DTLS"; | |
112 | # Don't write the redundant "Method = TLS" into the configuration. | |
590ed3d7 | 113 | undef $method if !$dtls; |
74726750 | 114 | |
682bc861 MC |
115 | my @protocols; |
116 | my @min_protocols; | |
117 | my @max_protocols; | |
118 | my $min_enabled; | |
119 | my $max_enabled; | |
120 | if ($fips) { | |
121 | @protocols = $dtls ? @dtls_protocols_fips : @tls_protocols_fips; | |
122 | @min_protocols = $dtls ? @min_dtls_protocols_fips : @min_tls_protocols_fips; | |
123 | @max_protocols = $dtls ? @max_dtls_protocols_fips : @max_tls_protocols_fips; | |
124 | $min_enabled = $dtls ? $min_dtls_enabled_fips : $min_tls_enabled_fips; | |
125 | $max_enabled = $dtls ? $max_dtls_enabled_fips : $max_tls_enabled_fips; | |
126 | } else { | |
127 | @protocols = $dtls ? @dtls_protocols : @tls_protocols; | |
128 | @min_protocols = $dtls ? @min_dtls_protocols : @min_tls_protocols; | |
129 | @max_protocols = $dtls ? @max_dtls_protocols : @max_tls_protocols; | |
130 | $min_enabled = $dtls ? $min_dtls_enabled : $min_tls_enabled; | |
131 | $max_enabled = $dtls ? $max_dtls_enabled : $max_tls_enabled; | |
132 | } | |
74726750 | 133 | |
590ed3d7 | 134 | if (no_tests($dtls)) { |
74726750 EK |
135 | return; |
136 | } | |
137 | ||
138 | my @tests = (); | |
139 | ||
00da4f4d MC |
140 | for (my $sctp = 0; $sctp < ($dtls && !disabled("sctp") ? 2 : 1); $sctp++) { |
141 | foreach my $c_min (0..$#min_protocols) { | |
142 | my $c_max_min = $c_min == 0 ? 0 : $c_min - 1; | |
143 | foreach my $c_max ($c_max_min..$#max_protocols) { | |
144 | foreach my $s_min (0..$#min_protocols) { | |
145 | my $s_max_min = $s_min == 0 ? 0 : $s_min - 1; | |
146 | foreach my $s_max ($s_max_min..$#max_protocols) { | |
147 | my ($result, $protocol) = | |
148 | expected_result($c_min, $c_max, $s_min, $s_max, | |
149 | $min_enabled, $max_enabled, | |
150 | \@protocols); | |
151 | push @tests, { | |
152 | "name" => "version-negotiation", | |
153 | "client" => { | |
154 | "MinProtocol" => $min_protocols[$c_min], | |
155 | "MaxProtocol" => $max_protocols[$c_max], | |
156 | }, | |
157 | "server" => { | |
158 | "MinProtocol" => $min_protocols[$s_min], | |
159 | "MaxProtocol" => $max_protocols[$s_max], | |
160 | }, | |
161 | "test" => { | |
162 | "ExpectedResult" => $result, | |
163 | "ExpectedProtocol" => $protocol, | |
164 | "Method" => $method, | |
165 | } | |
166 | }; | |
167 | $tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; | |
168 | } | |
74726750 EK |
169 | } |
170 | } | |
171 | } | |
172 | } | |
975922fd MC |
173 | return @tests if disabled("tls1_3") || disabled("tls1_2") || $dtls; |
174 | ||
175 | #Add some version/ciphersuite sanity check tests | |
176 | push @tests, { | |
177 | "name" => "ciphersuite-sanity-check-client", | |
178 | "client" => { | |
179 | #Offering only <=TLSv1.2 ciphersuites with TLSv1.3 should fail | |
180 | "CipherString" => "AES128-SHA", | |
f865b081 | 181 | "Ciphersuites" => "", |
975922fd MC |
182 | }, |
183 | "server" => { | |
184 | "MaxProtocol" => "TLSv1.2" | |
185 | }, | |
186 | "test" => { | |
187 | "ExpectedResult" => "ClientFail", | |
188 | } | |
189 | }; | |
190 | push @tests, { | |
191 | "name" => "ciphersuite-sanity-check-server", | |
192 | "client" => { | |
193 | "CipherString" => "AES128-SHA", | |
194 | "MaxProtocol" => "TLSv1.2" | |
195 | }, | |
196 | "server" => { | |
197 | #Allowing only <=TLSv1.2 ciphersuites with TLSv1.3 should fail | |
198 | "CipherString" => "AES128-SHA", | |
f865b081 | 199 | "Ciphersuites" => "", |
975922fd MC |
200 | }, |
201 | "test" => { | |
202 | "ExpectedResult" => "ServerFail", | |
203 | } | |
204 | }; | |
205 | ||
74726750 EK |
206 | return @tests; |
207 | } | |
208 | ||
590ed3d7 | 209 | sub generate_resumption_tests { |
682bc861 MC |
210 | my $method = shift; |
211 | my $fips = shift; | |
590ed3d7 EK |
212 | |
213 | my $dtls = $method eq "DTLS"; | |
214 | # Don't write the redundant "Method = TLS" into the configuration. | |
215 | undef $method if !$dtls; | |
216 | ||
682bc861 MC |
217 | my @protocols; |
218 | my $min_enabled; | |
219 | my $max_enabled; | |
220 | ||
221 | if ($fips) { | |
222 | @protocols = $dtls ? @dtls_protocols_fips : @tls_protocols_fips; | |
223 | $min_enabled = $dtls ? $min_dtls_enabled_fips : $min_tls_enabled_fips; | |
224 | $max_enabled = $dtls ? $max_dtls_enabled_fips : $max_tls_enabled_fips; | |
225 | } else { | |
226 | @protocols = $dtls ? @dtls_protocols : @tls_protocols; | |
227 | $min_enabled = $dtls ? $min_dtls_enabled : $min_tls_enabled; | |
228 | $max_enabled = $dtls ? $max_dtls_enabled : $max_tls_enabled; | |
229 | } | |
590ed3d7 EK |
230 | |
231 | if (no_tests($dtls)) { | |
232 | return; | |
233 | } | |
234 | ||
11279b13 EK |
235 | my @server_tests = (); |
236 | my @client_tests = (); | |
590ed3d7 | 237 | |
11279b13 | 238 | # Obtain the first session against a fixed-version server/client. |
84a68336 | 239 | foreach my $original_protocol($min_enabled..$max_enabled) { |
11279b13 | 240 | # Upgrade or downgrade the server/client max version support and test |
590ed3d7 | 241 | # that it upgrades, downgrades or resumes the session as well. |
84a68336 | 242 | foreach my $resume_protocol($min_enabled..$max_enabled) { |
590ed3d7 EK |
243 | my $resumption_expected; |
244 | # We should only resume on exact version match. | |
245 | if ($original_protocol eq $resume_protocol) { | |
246 | $resumption_expected = "Yes"; | |
247 | } else { | |
248 | $resumption_expected = "No"; | |
249 | } | |
250 | ||
cf156009 MC |
251 | for (my $sctp = 0; $sctp < ($dtls && !disabled("sctp") ? 2 : 1); |
252 | $sctp++) { | |
253 | foreach my $ticket ("SessionTicket", "-SessionTicket") { | |
254 | # Client is flexible, server upgrades/downgrades. | |
255 | push @server_tests, { | |
256 | "name" => "resumption", | |
257 | "client" => { }, | |
258 | "server" => { | |
259 | "MinProtocol" => $protocols[$original_protocol], | |
260 | "MaxProtocol" => $protocols[$original_protocol], | |
261 | "Options" => $ticket, | |
262 | }, | |
263 | "resume_server" => { | |
264 | "MaxProtocol" => $protocols[$resume_protocol], | |
6cc0b3c2 | 265 | "Options" => $ticket, |
cf156009 MC |
266 | }, |
267 | "test" => { | |
268 | "ExpectedProtocol" => $protocols[$resume_protocol], | |
269 | "Method" => $method, | |
270 | "HandshakeMode" => "Resume", | |
271 | "ResumptionExpected" => $resumption_expected, | |
272 | } | |
273 | }; | |
274 | $server_tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; | |
275 | # Server is flexible, client upgrades/downgrades. | |
276 | push @client_tests, { | |
277 | "name" => "resumption", | |
278 | "client" => { | |
279 | "MinProtocol" => $protocols[$original_protocol], | |
280 | "MaxProtocol" => $protocols[$original_protocol], | |
281 | }, | |
282 | "server" => { | |
283 | "Options" => $ticket, | |
284 | }, | |
285 | "resume_client" => { | |
286 | "MaxProtocol" => $protocols[$resume_protocol], | |
287 | }, | |
288 | "test" => { | |
289 | "ExpectedProtocol" => $protocols[$resume_protocol], | |
290 | "Method" => $method, | |
291 | "HandshakeMode" => "Resume", | |
292 | "ResumptionExpected" => $resumption_expected, | |
293 | } | |
294 | }; | |
295 | $client_tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; | |
296 | } | |
590ed3d7 EK |
297 | } |
298 | } | |
299 | } | |
300 | ||
1763ab10 MC |
301 | if (!disabled("tls1_3") && !$dtls) { |
302 | push @client_tests, { | |
303 | "name" => "resumption-with-hrr", | |
304 | "client" => { | |
305 | }, | |
306 | "server" => { | |
dbc6268f | 307 | "Curves" => disabled("ec") ? "ffdhe3072" : "P-256" |
1763ab10 MC |
308 | }, |
309 | "resume_client" => { | |
310 | }, | |
311 | "test" => { | |
312 | "ExpectedProtocol" => "TLSv1.3", | |
313 | "Method" => "TLS", | |
314 | "HandshakeMode" => "Resume", | |
315 | "ResumptionExpected" => "Yes", | |
316 | } | |
317 | }; | |
318 | } | |
319 | ||
11279b13 | 320 | return (@server_tests, @client_tests); |
590ed3d7 EK |
321 | } |
322 | ||
74726750 EK |
323 | sub expected_result { |
324 | my ($c_min, $c_max, $s_min, $s_max, $min_enabled, $max_enabled, | |
325 | $protocols) = @_; | |
326 | ||
327 | # Adjust for "undef" (no limit). | |
328 | $c_min = $c_min == 0 ? 0 : $c_min - 1; | |
329 | $c_max = $c_max == scalar @$protocols ? $c_max - 1 : $c_max; | |
330 | $s_min = $s_min == 0 ? 0 : $s_min - 1; | |
331 | $s_max = $s_max == scalar @$protocols ? $s_max - 1 : $s_max; | |
332 | ||
333 | # We now have at least one protocol enabled, so $min_enabled and | |
334 | # $max_enabled are well-defined. | |
335 | $c_min = max $c_min, $min_enabled; | |
336 | $s_min = max $s_min, $min_enabled; | |
337 | $c_max = min $c_max, $max_enabled; | |
338 | $s_max = min $s_max, $max_enabled; | |
339 | ||
340 | if ($c_min > $c_max) { | |
341 | # Client should fail to even send a hello. | |
83964ca0 | 342 | return ("ClientFail", undef); |
74726750 EK |
343 | } elsif ($s_min > $s_max) { |
344 | # Server has no protocols, should always fail. | |
345 | return ("ServerFail", undef); | |
346 | } elsif ($s_min > $c_max) { | |
347 | # Server doesn't support the client range. | |
348 | return ("ServerFail", undef); | |
349 | } elsif ($c_min > $s_max) { | |
582a17d6 | 350 | my @prots = @$protocols; |
7b21c00e MC |
351 | if ($prots[$c_max] eq "TLSv1.3") { |
352 | # Client will have sent supported_versions, so server will know | |
353 | # that there are no overlapping versions. | |
354 | return ("ServerFail", undef); | |
582a17d6 MC |
355 | } else { |
356 | # Server will try with a version that is lower than the lowest | |
357 | # supported client version. | |
358 | return ("ClientFail", undef); | |
359 | } | |
74726750 EK |
360 | } else { |
361 | # Server and client ranges overlap. | |
362 | my $max_common = $s_max < $c_max ? $s_max : $c_max; | |
363 | return ("Success", $protocols->[$max_common]); | |
364 | } | |
365 | } | |
366 | ||
367 | 1; |