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