Home | History | Annotate | Line # | Download | only in ssl-tests
      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 
     12 package ssltests;
     13 
     14 use strict;
     15 use warnings;
     16 
     17 use List::Util qw/max min/;
     18 
     19 use OpenSSL::Test;
     20 use OpenSSL::Test::Utils qw/anydisabled alldisabled disabled/;
     21 setup("no_test_here");
     22 
     23 my @tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3");
     24 my @tls_protocols_fips = ("TLSv1.2", "TLSv1.3");
     25 # undef stands for "no limit".
     26 my @min_tls_protocols = (undef, "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3");
     27 my @min_tls_protocols_fips = (undef, "TLSv1.2", "TLSv1.3");
     28 my @max_tls_protocols = ("SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3", undef);
     29 my @max_tls_protocols_fips = ("TLSv1.2", "TLSv1.3", undef);
     30 
     31 my @is_tls_disabled = anydisabled("ssl3", "tls1", "tls1_1", "tls1_2", "tls1_3");
     32 my @is_tls_disabled_fips = anydisabled("tls1_2", "tls1_3");
     33 
     34 my $min_tls_enabled; my $max_tls_enabled;
     35 my $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 
     43 sub 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 
     59 sub 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 
     83 my @dtls_protocols = ("DTLSv1", "DTLSv1.2");
     84 my @dtls_protocols_fips = ("DTLSv1.2");
     85 # undef stands for "no limit".
     86 my @min_dtls_protocols = (undef, "DTLSv1", "DTLSv1.2");
     87 my @min_dtls_protocols_fips = (undef, "DTLSv1.2");
     88 my @max_dtls_protocols = ("DTLSv1", "DTLSv1.2", undef);
     89 my @max_dtls_protocols_fips = ("DTLSv1.2", undef);
     90 
     91 my @is_dtls_disabled = anydisabled("dtls1", "dtls1_2");
     92 my @is_dtls_disabled_fips = anydisabled("dtls1_2");
     93 
     94 my $min_dtls_enabled; my $max_dtls_enabled;
     95 my $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 
    104 sub 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 
    113 sub 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 
    221 sub 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 
    342 sub 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 
    391 1;
    392