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