1c7da899bSchristos# -*- mode: perl; -*- 2*b0d17251Schristos# Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved. 3c7da899bSchristos# 4*b0d17251Schristos# Licensed under the Apache License 2.0 (the "License"). You may not use 5c7da899bSchristos# this file except in compliance with the License. You can obtain a copy 6c7da899bSchristos# in the file LICENSE in the source distribution or at 7c7da899bSchristos# https://www.openssl.org/source/license.html 8c7da899bSchristos 9c7da899bSchristos 10c7da899bSchristos## Test version negotiation 11c7da899bSchristos 12c7da899bSchristospackage ssltests; 13c7da899bSchristos 14c7da899bSchristosuse strict; 15c7da899bSchristosuse warnings; 16c7da899bSchristos 17c7da899bSchristosuse List::Util qw/max min/; 18c7da899bSchristos 19c7da899bSchristosuse OpenSSL::Test; 2013d40330Schristosuse OpenSSL::Test::Utils qw/anydisabled alldisabled disabled/; 21c7da899bSchristossetup("no_test_here"); 22c7da899bSchristos 2313d40330Schristosmy @tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"); 24*b0d17251Schristosmy @tls_protocols_fips = ("TLSv1.2", "TLSv1.3"); 25c7da899bSchristos# undef stands for "no limit". 2613d40330Schristosmy @min_tls_protocols = (undef, "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"); 27*b0d17251Schristosmy @min_tls_protocols_fips = (undef, "TLSv1.2", "TLSv1.3"); 2813d40330Schristosmy @max_tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", undef); 29*b0d17251Schristosmy @max_tls_protocols_fips = ("TLSv1.2", "TLSv1.3", undef); 30c7da899bSchristos 3113d40330Schristosmy @is_tls_disabled = anydisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3"); 32*b0d17251Schristosmy @is_tls_disabled_fips = anydisabled("tls1_2", "tls1_3"); 33c7da899bSchristos 34c7da899bSchristosmy $min_tls_enabled; my $max_tls_enabled; 35*b0d17251Schristosmy $min_tls_enabled_fips; my $max_tls_enabled_fips; 36c7da899bSchristos 37c7da899bSchristos# Protocol configuration works in cascades, i.e., 38c7da899bSchristos# $no_tls1_1 disables TLSv1.1 and below. 39c7da899bSchristos# 40c7da899bSchristos# $min_enabled and $max_enabled will be correct if there is at least one 41c7da899bSchristos# protocol enabled. 42*b0d17251Schristos 43*b0d17251Schristossub min_prot_enabled { 44*b0d17251Schristos my $protref = shift; 45*b0d17251Schristos my $disabledref = shift; 46*b0d17251Schristos my @protocols = @{$protref}; 47*b0d17251Schristos my @is_disabled = @{$disabledref}; 48*b0d17251Schristos my $min_enabled; 49*b0d17251Schristos 50*b0d17251Schristos foreach my $i (0..$#protocols) { 51*b0d17251Schristos if (!$is_disabled[$i]) { 52*b0d17251Schristos $min_enabled = $i; 53c7da899bSchristos last; 54c7da899bSchristos } 55c7da899bSchristos } 56*b0d17251Schristos return $min_enabled; 57*b0d17251Schristos} 58c7da899bSchristos 59*b0d17251Schristossub max_prot_enabled { 60*b0d17251Schristos my $protref = shift; 61*b0d17251Schristos my $disabledref = shift; 62*b0d17251Schristos my @protocols = @{$protref}; 63*b0d17251Schristos my @is_disabled = @{$disabledref}; 64*b0d17251Schristos my $max_enabled; 65*b0d17251Schristos 66*b0d17251Schristos foreach my $i (0..$#protocols) { 67*b0d17251Schristos if (!$is_disabled[$i] 68*b0d17251Schristos && ($protocols[$i] ne "TLSv1.3" 69*b0d17251Schristos || !disabled("ec") 70*b0d17251Schristos || !disabled("dh"))) { 71*b0d17251Schristos $max_enabled = $i; 72c7da899bSchristos } 73c7da899bSchristos } 74*b0d17251Schristos return $max_enabled; 75*b0d17251Schristos} 76*b0d17251Schristos 77*b0d17251Schristos$min_tls_enabled = min_prot_enabled(\@tls_protocols, \@is_tls_disabled); 78*b0d17251Schristos$max_tls_enabled = max_prot_enabled(\@tls_protocols, \@is_tls_disabled); 79*b0d17251Schristos$min_tls_enabled_fips = min_prot_enabled(\@tls_protocols_fips, \@is_tls_disabled_fips); 80*b0d17251Schristos$max_tls_enabled_fips = max_prot_enabled(\@tls_protocols_fips, \@is_tls_disabled_fips); 81*b0d17251Schristos 82c7da899bSchristos 83c7da899bSchristosmy @dtls_protocols = ("DTLSv1", "DTLSv1.2"); 84*b0d17251Schristosmy @dtls_protocols_fips = ("DTLSv1.2"); 85c7da899bSchristos# undef stands for "no limit". 86c7da899bSchristosmy @min_dtls_protocols = (undef, "DTLSv1", "DTLSv1.2"); 87*b0d17251Schristosmy @min_dtls_protocols_fips = (undef, "DTLSv1.2"); 88c7da899bSchristosmy @max_dtls_protocols = ("DTLSv1", "DTLSv1.2", undef); 89*b0d17251Schristosmy @max_dtls_protocols_fips = ("DTLSv1.2", undef); 90c7da899bSchristos 91c7da899bSchristosmy @is_dtls_disabled = anydisabled("dtls1", "dtls1_2"); 92*b0d17251Schristosmy @is_dtls_disabled_fips = anydisabled("dtls1_2"); 93c7da899bSchristos 94c7da899bSchristosmy $min_dtls_enabled; my $max_dtls_enabled; 95*b0d17251Schristosmy $min_dtls_enabled_fips; my $max_dtls_enabled_fips; 96c7da899bSchristos 97c7da899bSchristos# $min_enabled and $max_enabled will be correct if there is at least one 98c7da899bSchristos# protocol enabled. 99*b0d17251Schristos$min_dtls_enabled = min_prot_enabled(\@dtls_protocols, \@is_dtls_disabled); 100*b0d17251Schristos$max_dtls_enabled = max_prot_enabled(\@dtls_protocols, \@is_dtls_disabled); 101*b0d17251Schristos$min_dtls_enabled_fips = min_prot_enabled(\@dtls_protocols_fips, \@is_dtls_disabled_fips); 102*b0d17251Schristos$max_dtls_enabled_fips = max_prot_enabled(\@dtls_protocols_fips, \@is_dtls_disabled_fips); 103c7da899bSchristos 104c7da899bSchristossub no_tests { 105*b0d17251Schristos my ($dtls, $fips) = @_; 106*b0d17251Schristos if ($dtls && $fips) { 107*b0d17251Schristos return disabled("dtls1_2"); 108*b0d17251Schristos } 109c7da899bSchristos return $dtls ? alldisabled("dtls1", "dtls1_2") : 11013d40330Schristos alldisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3"); 111c7da899bSchristos} 112c7da899bSchristos 113c7da899bSchristossub generate_version_tests { 114*b0d17251Schristos my $method = shift; 115*b0d17251Schristos my $fips = shift; 116c7da899bSchristos 117c7da899bSchristos my $dtls = $method eq "DTLS"; 118c7da899bSchristos # Don't write the redundant "Method = TLS" into the configuration. 119c7da899bSchristos undef $method if !$dtls; 120c7da899bSchristos 121*b0d17251Schristos my @protocols; 122*b0d17251Schristos my @min_protocols; 123*b0d17251Schristos my @max_protocols; 124*b0d17251Schristos my $min_enabled; 125*b0d17251Schristos my $max_enabled; 126*b0d17251Schristos if ($fips) { 127*b0d17251Schristos @protocols = $dtls ? @dtls_protocols_fips : @tls_protocols_fips; 128*b0d17251Schristos @min_protocols = $dtls ? @min_dtls_protocols_fips : @min_tls_protocols_fips; 129*b0d17251Schristos @max_protocols = $dtls ? @max_dtls_protocols_fips : @max_tls_protocols_fips; 130*b0d17251Schristos $min_enabled = $dtls ? $min_dtls_enabled_fips : $min_tls_enabled_fips; 131*b0d17251Schristos $max_enabled = $dtls ? $max_dtls_enabled_fips : $max_tls_enabled_fips; 132*b0d17251Schristos } else { 133*b0d17251Schristos @protocols = $dtls ? @dtls_protocols : @tls_protocols; 134*b0d17251Schristos @min_protocols = $dtls ? @min_dtls_protocols : @min_tls_protocols; 135*b0d17251Schristos @max_protocols = $dtls ? @max_dtls_protocols : @max_tls_protocols; 136*b0d17251Schristos $min_enabled = $dtls ? $min_dtls_enabled : $min_tls_enabled; 137*b0d17251Schristos $max_enabled = $dtls ? $max_dtls_enabled : $max_tls_enabled; 138*b0d17251Schristos } 139c7da899bSchristos 140*b0d17251Schristos if (no_tests($dtls, $fips)) { 141c7da899bSchristos return; 142c7da899bSchristos } 143c7da899bSchristos 144c7da899bSchristos my @tests = (); 145c7da899bSchristos 14613d40330Schristos for (my $sctp = 0; $sctp < ($dtls && !disabled("sctp") ? 2 : 1); $sctp++) { 147c7da899bSchristos foreach my $c_min (0..$#min_protocols) { 148c7da899bSchristos my $c_max_min = $c_min == 0 ? 0 : $c_min - 1; 149c7da899bSchristos foreach my $c_max ($c_max_min..$#max_protocols) { 150c7da899bSchristos foreach my $s_min (0..$#min_protocols) { 151c7da899bSchristos my $s_max_min = $s_min == 0 ? 0 : $s_min - 1; 152c7da899bSchristos foreach my $s_max ($s_max_min..$#max_protocols) { 153c7da899bSchristos my ($result, $protocol) = 154c7da899bSchristos expected_result($c_min, $c_max, $s_min, $s_max, 15513d40330Schristos $min_enabled, $max_enabled, 15613d40330Schristos \@protocols); 157c7da899bSchristos push @tests, { 158c7da899bSchristos "name" => "version-negotiation", 159c7da899bSchristos "client" => { 160*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 161c7da899bSchristos "MinProtocol" => $min_protocols[$c_min], 162c7da899bSchristos "MaxProtocol" => $max_protocols[$c_max], 163c7da899bSchristos }, 164c7da899bSchristos "server" => { 165*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 166c7da899bSchristos "MinProtocol" => $min_protocols[$s_min], 167c7da899bSchristos "MaxProtocol" => $max_protocols[$s_max], 168c7da899bSchristos }, 169c7da899bSchristos "test" => { 170c7da899bSchristos "ExpectedResult" => $result, 171c7da899bSchristos "ExpectedProtocol" => $protocol, 172c7da899bSchristos "Method" => $method, 173c7da899bSchristos } 174c7da899bSchristos }; 17513d40330Schristos $tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; 176c7da899bSchristos } 177c7da899bSchristos } 178c7da899bSchristos } 179c7da899bSchristos } 18013d40330Schristos } 181*b0d17251Schristos return @tests 182*b0d17251Schristos if disabled("tls1_3") 183*b0d17251Schristos || disabled("tls1_2") 184*b0d17251Schristos || (disabled("ec") && disabled("dh")) 185*b0d17251Schristos || $dtls; 18613d40330Schristos 18713d40330Schristos #Add some version/ciphersuite sanity check tests 18813d40330Schristos push @tests, { 18913d40330Schristos "name" => "ciphersuite-sanity-check-client", 19013d40330Schristos "client" => { 19113d40330Schristos #Offering only <=TLSv1.2 ciphersuites with TLSv1.3 should fail 19213d40330Schristos "CipherString" => "AES128-SHA", 19313d40330Schristos "Ciphersuites" => "", 19413d40330Schristos }, 19513d40330Schristos "server" => { 19613d40330Schristos "MaxProtocol" => "TLSv1.2" 19713d40330Schristos }, 19813d40330Schristos "test" => { 19913d40330Schristos "ExpectedResult" => "ClientFail", 20013d40330Schristos } 20113d40330Schristos }; 20213d40330Schristos push @tests, { 20313d40330Schristos "name" => "ciphersuite-sanity-check-server", 20413d40330Schristos "client" => { 20513d40330Schristos "CipherString" => "AES128-SHA", 20613d40330Schristos "MaxProtocol" => "TLSv1.2" 20713d40330Schristos }, 20813d40330Schristos "server" => { 20913d40330Schristos #Allowing only <=TLSv1.2 ciphersuites with TLSv1.3 should fail 21013d40330Schristos "CipherString" => "AES128-SHA", 21113d40330Schristos "Ciphersuites" => "", 21213d40330Schristos }, 21313d40330Schristos "test" => { 21413d40330Schristos "ExpectedResult" => "ServerFail", 21513d40330Schristos } 21613d40330Schristos }; 21713d40330Schristos 218c7da899bSchristos return @tests; 219c7da899bSchristos} 220c7da899bSchristos 221c7da899bSchristossub generate_resumption_tests { 222*b0d17251Schristos my $method = shift; 223*b0d17251Schristos my $fips = shift; 224c7da899bSchristos 225c7da899bSchristos my $dtls = $method eq "DTLS"; 226c7da899bSchristos # Don't write the redundant "Method = TLS" into the configuration. 227c7da899bSchristos undef $method if !$dtls; 228c7da899bSchristos 229*b0d17251Schristos my @protocols; 230*b0d17251Schristos my $min_enabled; 231*b0d17251Schristos my $max_enabled; 232*b0d17251Schristos 233*b0d17251Schristos if ($fips) { 234*b0d17251Schristos @protocols = $dtls ? @dtls_protocols_fips : @tls_protocols_fips; 235*b0d17251Schristos $min_enabled = $dtls ? $min_dtls_enabled_fips : $min_tls_enabled_fips; 236*b0d17251Schristos $max_enabled = $dtls ? $max_dtls_enabled_fips : $max_tls_enabled_fips; 237*b0d17251Schristos } else { 238*b0d17251Schristos @protocols = $dtls ? @dtls_protocols : @tls_protocols; 239*b0d17251Schristos $min_enabled = $dtls ? $min_dtls_enabled : $min_tls_enabled; 240*b0d17251Schristos $max_enabled = $dtls ? $max_dtls_enabled : $max_tls_enabled; 241*b0d17251Schristos } 242c7da899bSchristos 243c7da899bSchristos if (no_tests($dtls)) { 244c7da899bSchristos return; 245c7da899bSchristos } 246c7da899bSchristos 247c7da899bSchristos my @server_tests = (); 248c7da899bSchristos my @client_tests = (); 249c7da899bSchristos 250c7da899bSchristos # Obtain the first session against a fixed-version server/client. 25113d40330Schristos foreach my $original_protocol($min_enabled..$max_enabled) { 252c7da899bSchristos # Upgrade or downgrade the server/client max version support and test 253c7da899bSchristos # that it upgrades, downgrades or resumes the session as well. 25413d40330Schristos foreach my $resume_protocol($min_enabled..$max_enabled) { 255c7da899bSchristos my $resumption_expected; 256c7da899bSchristos # We should only resume on exact version match. 257c7da899bSchristos if ($original_protocol eq $resume_protocol) { 258c7da899bSchristos $resumption_expected = "Yes"; 259c7da899bSchristos } else { 260c7da899bSchristos $resumption_expected = "No"; 261c7da899bSchristos } 262c7da899bSchristos 26313d40330Schristos for (my $sctp = 0; $sctp < ($dtls && !disabled("sctp") ? 2 : 1); 26413d40330Schristos $sctp++) { 265c7da899bSchristos foreach my $ticket ("SessionTicket", "-SessionTicket") { 266c7da899bSchristos # Client is flexible, server upgrades/downgrades. 267c7da899bSchristos push @server_tests, { 268c7da899bSchristos "name" => "resumption", 269*b0d17251Schristos "client" => { 270*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 271*b0d17251Schristos }, 272c7da899bSchristos "server" => { 273*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 274c7da899bSchristos "MinProtocol" => $protocols[$original_protocol], 275c7da899bSchristos "MaxProtocol" => $protocols[$original_protocol], 276c7da899bSchristos "Options" => $ticket, 277c7da899bSchristos }, 278c7da899bSchristos "resume_server" => { 279*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 280c7da899bSchristos "MaxProtocol" => $protocols[$resume_protocol], 28113d40330Schristos "Options" => $ticket, 282c7da899bSchristos }, 283c7da899bSchristos "test" => { 284c7da899bSchristos "ExpectedProtocol" => $protocols[$resume_protocol], 285c7da899bSchristos "Method" => $method, 286c7da899bSchristos "HandshakeMode" => "Resume", 287c7da899bSchristos "ResumptionExpected" => $resumption_expected, 288c7da899bSchristos } 289c7da899bSchristos }; 29013d40330Schristos $server_tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; 291c7da899bSchristos # Server is flexible, client upgrades/downgrades. 292c7da899bSchristos push @client_tests, { 293c7da899bSchristos "name" => "resumption", 294c7da899bSchristos "client" => { 295*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 296c7da899bSchristos "MinProtocol" => $protocols[$original_protocol], 297c7da899bSchristos "MaxProtocol" => $protocols[$original_protocol], 298c7da899bSchristos }, 299c7da899bSchristos "server" => { 300*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 301c7da899bSchristos "Options" => $ticket, 302c7da899bSchristos }, 303c7da899bSchristos "resume_client" => { 304*b0d17251Schristos "CipherString" => "DEFAULT:\@SECLEVEL=0", 305c7da899bSchristos "MaxProtocol" => $protocols[$resume_protocol], 306c7da899bSchristos }, 307c7da899bSchristos "test" => { 308c7da899bSchristos "ExpectedProtocol" => $protocols[$resume_protocol], 309c7da899bSchristos "Method" => $method, 310c7da899bSchristos "HandshakeMode" => "Resume", 311c7da899bSchristos "ResumptionExpected" => $resumption_expected, 312c7da899bSchristos } 313c7da899bSchristos }; 31413d40330Schristos $client_tests[-1]{"test"}{"UseSCTP"} = "Yes" if $sctp; 315c7da899bSchristos } 316c7da899bSchristos } 317c7da899bSchristos } 31813d40330Schristos } 31913d40330Schristos 320*b0d17251Schristos if (!disabled("tls1_3") && (!disabled("ec") || !disabled("dh")) && !$dtls) { 32113d40330Schristos push @client_tests, { 32213d40330Schristos "name" => "resumption-with-hrr", 32313d40330Schristos "client" => { 32413d40330Schristos }, 32513d40330Schristos "server" => { 326*b0d17251Schristos "Curves" => disabled("ec") ? "ffdhe3072" : "P-256" 32713d40330Schristos }, 32813d40330Schristos "resume_client" => { 32913d40330Schristos }, 33013d40330Schristos "test" => { 33113d40330Schristos "ExpectedProtocol" => "TLSv1.3", 33213d40330Schristos "Method" => "TLS", 33313d40330Schristos "HandshakeMode" => "Resume", 33413d40330Schristos "ResumptionExpected" => "Yes", 33513d40330Schristos } 33613d40330Schristos }; 33713d40330Schristos } 338c7da899bSchristos 339c7da899bSchristos return (@server_tests, @client_tests); 340c7da899bSchristos} 341c7da899bSchristos 342c7da899bSchristossub expected_result { 343c7da899bSchristos my ($c_min, $c_max, $s_min, $s_max, $min_enabled, $max_enabled, 344c7da899bSchristos $protocols) = @_; 345*b0d17251Schristos my @prots = @$protocols; 346c7da899bSchristos 347*b0d17251Schristos my $orig_c_max = $c_max; 348c7da899bSchristos # Adjust for "undef" (no limit). 349c7da899bSchristos $c_min = $c_min == 0 ? 0 : $c_min - 1; 350c7da899bSchristos $c_max = $c_max == scalar @$protocols ? $c_max - 1 : $c_max; 351c7da899bSchristos $s_min = $s_min == 0 ? 0 : $s_min - 1; 352c7da899bSchristos $s_max = $s_max == scalar @$protocols ? $s_max - 1 : $s_max; 353c7da899bSchristos 354c7da899bSchristos # We now have at least one protocol enabled, so $min_enabled and 355c7da899bSchristos # $max_enabled are well-defined. 356c7da899bSchristos $c_min = max $c_min, $min_enabled; 357c7da899bSchristos $s_min = max $s_min, $min_enabled; 358c7da899bSchristos $c_max = min $c_max, $max_enabled; 359c7da899bSchristos $s_max = min $s_max, $max_enabled; 360c7da899bSchristos 361*b0d17251Schristos if ($c_min > $c_max 362*b0d17251Schristos || ($orig_c_max != scalar @$protocols 363*b0d17251Schristos && $prots[$orig_c_max] eq "TLSv1.3" 364*b0d17251Schristos && $c_max != $orig_c_max 365*b0d17251Schristos && !disabled("tls1_3"))) { 366c7da899bSchristos # Client should fail to even send a hello. 36713d40330Schristos return ("ClientFail", undef); 368c7da899bSchristos } elsif ($s_min > $s_max) { 369c7da899bSchristos # Server has no protocols, should always fail. 370c7da899bSchristos return ("ServerFail", undef); 371c7da899bSchristos } elsif ($s_min > $c_max) { 372c7da899bSchristos # Server doesn't support the client range. 373c7da899bSchristos return ("ServerFail", undef); 374c7da899bSchristos } elsif ($c_min > $s_max) { 37513d40330Schristos if ($prots[$c_max] eq "TLSv1.3") { 37613d40330Schristos # Client will have sent supported_versions, so server will know 37713d40330Schristos # that there are no overlapping versions. 37813d40330Schristos return ("ServerFail", undef); 37913d40330Schristos } else { 380c7da899bSchristos # Server will try with a version that is lower than the lowest 381c7da899bSchristos # supported client version. 382c7da899bSchristos return ("ClientFail", undef); 38313d40330Schristos } 384c7da899bSchristos } else { 385c7da899bSchristos # Server and client ranges overlap. 386c7da899bSchristos my $max_common = $s_max < $c_max ? $s_max : $c_max; 387c7da899bSchristos return ("Success", $protocols->[$max_common]); 388c7da899bSchristos } 389c7da899bSchristos} 390c7da899bSchristos 391c7da899bSchristos1; 392