Update copyright year
[openssl.git] / test / ssl-tests / protocol_version.pm
1 # -*- mode: perl; -*-
2 # Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the Apache License 2.0 (the "License").  You may not use
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;
20 use OpenSSL::Test::Utils qw/anydisabled alldisabled disabled/;
21 setup("no_test_here");
22
23 my @tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3");
24 my @tls_protocols_fips = ("TLSv1.2", "TLSv1.3");
25 # undef stands for "no limit".
26 my @min_tls_protocols = (undef, "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3");
27 my @min_tls_protocols_fips = (undef, "TLSv1.2", "TLSv1.3");
28 my @max_tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", undef);
29 my @max_tls_protocols_fips = ("TLSv1.2", "TLSv1.3", undef);
30
31 my @is_tls_disabled = anydisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3");
32 my @is_tls_disabled_fips = anydisabled("tls1_2", "tls1_3");
33
34 my $min_tls_enabled; my $max_tls_enabled;
35 my $min_tls_enabled_fips; my $max_tls_enabled_fips;
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.
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         }
55     }
56     return $min_enabled;
57 }
58
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         }
70     }
71     return $max_enabled;
72 }
73
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
80 my @dtls_protocols = ("DTLSv1", "DTLSv1.2");
81 my @dtls_protocols_fips = ("DTLSv1.2");
82 # undef stands for "no limit".
83 my @min_dtls_protocols = (undef, "DTLSv1", "DTLSv1.2");
84 my @min_dtls_protocols_fips = (undef, "DTLSv1.2");
85 my @max_dtls_protocols = ("DTLSv1", "DTLSv1.2", undef);
86 my @max_dtls_protocols_fips = ("DTLSv1.2", undef);
87
88 my @is_dtls_disabled = anydisabled("dtls1", "dtls1_2");
89 my @is_dtls_disabled_fips = anydisabled("dtls1_2");
90
91 my $min_dtls_enabled; my $max_dtls_enabled;
92 my $min_dtls_enabled_fips; my $max_dtls_enabled_fips;
93
94 # $min_enabled and $max_enabled will be correct if there is at least one
95 # protocol enabled.
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);
100
101 sub no_tests {
102     my ($dtls) = @_;
103     return $dtls ? alldisabled("dtls1", "dtls1_2") :
104       alldisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3");
105 }
106
107 sub generate_version_tests {
108     my $method = shift;
109     my $fips = shift;
110
111     my $dtls = $method eq "DTLS";
112     # Don't write the redundant "Method = TLS" into the configuration.
113     undef $method if !$dtls;
114
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     }
133
134     if (no_tests($dtls)) {
135         return;
136     }
137
138     my @tests = ();
139
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                     }
169                 }
170             }
171         }
172     }
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",
181             "Ciphersuites" => "",
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",
199             "Ciphersuites" => "",
200         },
201         "test" => {
202             "ExpectedResult" => "ServerFail",
203         }
204     };
205
206     return @tests;
207 }
208
209 sub generate_resumption_tests {
210     my $method = shift;
211     my $fips = shift;
212
213     my $dtls = $method eq "DTLS";
214     # Don't write the redundant "Method = TLS" into the configuration.
215     undef $method if !$dtls;
216
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     }
230
231     if (no_tests($dtls)) {
232         return;
233     }
234
235     my @server_tests = ();
236     my @client_tests = ();
237
238     # Obtain the first session against a fixed-version server/client.
239     foreach my $original_protocol($min_enabled..$max_enabled) {
240         # Upgrade or downgrade the server/client max version support and test
241         # that it upgrades, downgrades or resumes the session as well.
242         foreach my $resume_protocol($min_enabled..$max_enabled) {
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
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],
265                             "Options" => $ticket,
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                 }
297             }
298         }
299     }
300
301     if (!disabled("tls1_3") && !$dtls) {
302         push @client_tests, {
303             "name" => "resumption-with-hrr",
304             "client" => {
305             },
306             "server" => {
307                 "Curves" => disabled("ec") ? "ffdhe3072" : "P-256"
308             },
309             "resume_client" => {
310             },
311             "test" => {
312                 "ExpectedProtocol" => "TLSv1.3",
313                 "Method" => "TLS",
314                 "HandshakeMode" => "Resume",
315                 "ResumptionExpected" => "Yes",
316             }
317         };
318     }
319
320     return (@server_tests, @client_tests);
321 }
322
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.
342         return ("ClientFail", undef);
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) {
350         my @prots = @$protocols;
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);
355         } else {
356             # Server will try with a version that is lower than the lowest
357             # supported client version.
358             return ("ClientFail", undef);
359         }
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;