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