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