Home | History | Annotate | Line # | Download | only in recipes
      1 #! /usr/bin/env perl
      2 # Copyright 2015-2025 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 use strict;
     10 use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
     11 use OpenSSL::Test::Utils;
     12 use TLSProxy::Proxy;
     13 use File::Temp qw(tempfile);
     14 
     15 use constant {
     16     LOOK_ONLY => 0,
     17     EMPTY_EXTENSION => 1,
     18     MISSING_EXTENSION => 2,
     19     NO_ACCEPTABLE_KEY_SHARES => 3,
     20     NON_PREFERRED_KEY_SHARE => 4,
     21     ACCEPTABLE_AT_END => 5,
     22     NOT_IN_SUPPORTED_GROUPS => 6,
     23     GROUP_ID_TOO_SHORT => 7,
     24     KEX_LEN_MISMATCH => 8,
     25     ZERO_LEN_KEX_DATA => 9,
     26     TRAILING_DATA => 10,
     27     SELECT_X25519 => 11,
     28     NO_KEY_SHARES_IN_HRR => 12,
     29     NON_TLS1_3_KEY_SHARE => 13
     30 };
     31 
     32 use constant {
     33     CLIENT_TO_SERVER => 1,
     34     SERVER_TO_CLIENT => 2
     35 };
     36 
     37 
     38 use constant {
     39     X25519 => 0x1d,
     40     P_256 => 0x17,
     41     FFDHE2048 => 0x0100,
     42     FFDHE3072 => 0x0101
     43 };
     44 
     45 my $testtype;
     46 my $direction;
     47 my $selectedgroupid;
     48 
     49 my $test_name = "test_key_share";
     50 setup($test_name);
     51 
     52 plan skip_all => "TLSProxy isn't usable on $^O"
     53     if $^O =~ /^(VMS)$/;
     54 
     55 plan skip_all => "$test_name needs the dynamic engine feature enabled"
     56     if disabled("engine") || disabled("dynamic-engine");
     57 
     58 plan skip_all => "$test_name needs the sock feature enabled"
     59     if disabled("sock");
     60 
     61 plan skip_all => "$test_name needs TLS1.3 enabled"
     62     if disabled("tls1_3");
     63 
     64 plan skip_all => "$test_name needs EC or DH enabled"
     65     if disabled("ec") && disabled("dh");
     66 
     67 my $proxy = TLSProxy::Proxy->new(
     68     undef,
     69     cmdstr(app(["openssl"]), display => 1),
     70     srctop_file("apps", "server.pem"),
     71     (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
     72 );
     73 
     74 #We assume that test_ssl_new and friends will test the happy path for this,
     75 #so we concentrate on the less common scenarios
     76 
     77 #Test 1: An empty key_shares extension should succeed after a HelloRetryRequest
     78 $testtype = EMPTY_EXTENSION;
     79 $direction = CLIENT_TO_SERVER;
     80 $proxy->filter(\&modify_key_shares_filter);
     81 if (disabled("ec")) {
     82     $proxy->serverflags("-groups ffdhe3072");
     83 } else {
     84     $proxy->serverflags("-groups P-384");
     85 }
     86 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
     87 plan tests => 23;
     88 ok(TLSProxy::Message->success(), "Success after HRR");
     89 
     90 #Test 2: The server sending an HRR requesting a group the client already sent
     91 #        should fail
     92 $proxy->clear();
     93 $proxy->start();
     94 ok(TLSProxy::Message->fail(), "Server asks for group already provided");
     95 
     96 #Test 3: A missing key_shares extension should not succeed
     97 $proxy->clear();
     98 $testtype = MISSING_EXTENSION;
     99 $proxy->start();
    100 ok(TLSProxy::Message->fail(), "Missing key_shares extension");
    101 
    102 #Test 4: No initial acceptable key_shares should succeed after a
    103 #        HelloRetryRequest
    104 $proxy->clear();
    105 $proxy->filter(undef);
    106 if (disabled("ec")) {
    107     $proxy->serverflags("-groups ffdhe3072");
    108 } else {
    109     $proxy->serverflags("-groups P-256");
    110 }
    111 $proxy->start();
    112 ok(TLSProxy::Message->success(), "No initial acceptable key_shares");
    113 
    114 #Test 5: No acceptable key_shares and no shared groups should fail
    115 $proxy->clear();
    116 $proxy->filter(undef);
    117 if (disabled("ec")) {
    118     $proxy->serverflags("-groups ffdhe2048");
    119 } else {
    120     $proxy->serverflags("-groups P-256");
    121 }
    122 if (disabled("ec")) {
    123     $proxy->clientflags("-groups ffdhe3072");
    124 } else {
    125     $proxy->clientflags("-groups P-384");
    126 }
    127 $proxy->start();
    128 ok(TLSProxy::Message->fail(), "No acceptable key_shares");
    129 
    130 #Test 6: A non preferred but acceptable key_share should succeed
    131 $proxy->clear();
    132 $proxy->clientflags("-curves P-256");
    133 if (disabled("ec")) {
    134     $proxy->clientflags("-groups ffdhe3072");
    135 } else {
    136     $proxy->clientflags("-groups P-256");
    137 }
    138 $proxy->start();
    139 ok(TLSProxy::Message->success(), "Non preferred key_share");
    140 $proxy->filter(\&modify_key_shares_filter);
    141 
    142 SKIP: {
    143     skip "No ec support in this OpenSSL build", 1 if disabled("ec");
    144 
    145     #Test 7: An acceptable key_share after a list of non-acceptable ones should
    146     #succeed
    147     $proxy->clear();
    148     $testtype = ACCEPTABLE_AT_END;
    149     $proxy->start();
    150     ok(TLSProxy::Message->success(), "Acceptable key_share at end of list");
    151 }
    152 
    153 #Test 8: An acceptable key_share but for a group not in supported_groups should
    154 #fail
    155 $proxy->clear();
    156 $testtype = NOT_IN_SUPPORTED_GROUPS;
    157 $proxy->start();
    158 ok(TLSProxy::Message->fail(), "Acceptable key_share not in supported_groups");
    159 
    160 #Test 9: Too short group_id should fail
    161 $proxy->clear();
    162 $testtype = GROUP_ID_TOO_SHORT;
    163 $proxy->start();
    164 ok(TLSProxy::Message->fail(), "Group id too short");
    165 
    166 #Test 10: key_exchange length mismatch should fail
    167 $proxy->clear();
    168 $testtype = KEX_LEN_MISMATCH;
    169 $proxy->start();
    170 ok(TLSProxy::Message->fail(), "key_exchange length mismatch");
    171 
    172 #Test 11: Zero length key_exchange should fail
    173 $proxy->clear();
    174 $testtype = ZERO_LEN_KEX_DATA;
    175 $proxy->start();
    176 ok(TLSProxy::Message->fail(), "zero length key_exchange data");
    177 
    178 #Test 12: Trailing data on key_share list should fail
    179 $proxy->clear();
    180 $testtype = TRAILING_DATA;
    181 $proxy->start();
    182 ok(TLSProxy::Message->fail(), "key_share list trailing data");
    183 
    184 #Test 13: Multiple acceptable key_shares - we choose the first one
    185 $proxy->clear();
    186 $direction = SERVER_TO_CLIENT;
    187 $testtype = LOOK_ONLY;
    188 $selectedgroupid = 0;
    189 if (disabled("ec")) {
    190     $proxy->clientflags("-groups ffdhe3072:ffdhe2048");
    191 } else {
    192     $proxy->clientflags("-groups P-256:P-384");
    193 }
    194 $proxy->start();
    195 if (disabled("ec")) {
    196     ok(TLSProxy::Message->success() && ($selectedgroupid == FFDHE3072),
    197        "Multiple acceptable key_shares");
    198 } else {
    199     ok(TLSProxy::Message->success() && ($selectedgroupid == P_256),
    200        "Multiple acceptable key_shares");
    201 }
    202 
    203 #Test 14: Multiple acceptable key_shares - we choose the first one (part 2)
    204 $proxy->clear();
    205 if (disabled("ecx")) {
    206     $proxy->clientflags("-curves ffdhe2048:ffdhe3072");
    207 } else {
    208     $proxy->clientflags("-curves X25519:P-256");
    209 }
    210 $proxy->start();
    211 if (disabled("ecx")) {
    212     ok(TLSProxy::Message->success() && ($selectedgroupid == FFDHE2048),
    213        "Multiple acceptable key_shares (part 2)");
    214 } else {
    215     ok(TLSProxy::Message->success() && ($selectedgroupid == X25519),
    216        "Multiple acceptable key_shares (part 2)");
    217 }
    218 
    219 #Test 15: Server sends key_share that wasn't offered should fail
    220 $proxy->clear();
    221 $testtype = SELECT_X25519;
    222 if (disabled("ecx")) {
    223     $proxy->clientflags("-groups ffdhe3072");
    224 } else {
    225     $proxy->clientflags("-groups P-256");
    226 }
    227 $proxy->start();
    228 ok(TLSProxy::Message->fail(), "Non offered key_share");
    229 
    230 #Test 16: Too short group_id in ServerHello should fail
    231 $proxy->clear();
    232 $testtype = GROUP_ID_TOO_SHORT;
    233 $proxy->start();
    234 ok(TLSProxy::Message->fail(), "Group id too short in ServerHello");
    235 
    236 #Test 17: key_exchange length mismatch in ServerHello should fail
    237 $proxy->clear();
    238 $testtype = KEX_LEN_MISMATCH;
    239 $proxy->start();
    240 ok(TLSProxy::Message->fail(), "key_exchange length mismatch in ServerHello");
    241 
    242 #Test 18: Zero length key_exchange in ServerHello should fail
    243 $proxy->clear();
    244 $testtype = ZERO_LEN_KEX_DATA;
    245 $proxy->start();
    246 ok(TLSProxy::Message->fail(), "zero length key_exchange data in ServerHello");
    247 
    248 #Test 19: Trailing data on key_share in ServerHello should fail
    249 $proxy->clear();
    250 $testtype = TRAILING_DATA;
    251 $proxy->start();
    252 ok(TLSProxy::Message->fail(), "key_share trailing data in ServerHello");
    253 
    254 SKIP: {
    255     skip "No TLSv1.2 support in this OpenSSL build", 2 if disabled("tls1_2");
    256 
    257     #Test 20: key_share should not be sent if the client is not capable of
    258     #         negotiating TLSv1.3
    259     $proxy->clear();
    260     $proxy->filter(undef);
    261     $proxy->clientflags("-no_tls1_3");
    262     $proxy->start();
    263     my $clienthello = $proxy->message_list->[0];
    264     ok(TLSProxy::Message->success()
    265        && !defined $clienthello->extension_data->{TLSProxy::Message::EXT_KEY_SHARE},
    266        "No key_share for TLS<=1.2 client");
    267     $proxy->filter(\&modify_key_shares_filter);
    268 
    269     #Test 21: A server not capable of negotiating TLSv1.3 should not attempt to
    270     #         process a key_share
    271     $proxy->clear();
    272     $direction = CLIENT_TO_SERVER;
    273     $testtype = NO_ACCEPTABLE_KEY_SHARES;
    274     $proxy->serverflags("-no_tls1_3");
    275     $proxy->start();
    276     ok(TLSProxy::Message->success(), "Ignore key_share for TLS<=1.2 server");
    277 }
    278 
    279 #Test 22: The server sending an HRR but not requesting a new key_share should
    280 #         fail
    281 $proxy->clear();
    282 $direction = SERVER_TO_CLIENT;
    283 $testtype = NO_KEY_SHARES_IN_HRR;
    284 if (disabled("ecx")) {
    285     $proxy->serverflags("-groups ffdhe2048");
    286 } else {
    287     $proxy->serverflags("-groups X25519");
    288 }
    289 $proxy->start();
    290 ok(TLSProxy::Message->fail(), "Server sends HRR with no key_shares");
    291 
    292 SKIP: {
    293     skip "No EC support in this OpenSSL build", 1 if disabled("ec");
    294     #Test 23: Trailing data on key_share in ServerHello should fail
    295     $proxy->clear();
    296     $direction = CLIENT_TO_SERVER;
    297     if (disabled("ecx")) {
    298         $proxy->clientflags("-groups brainpoolP256r1:P-256:P-384");
    299     } else {
    300         $proxy->clientflags("-groups brainpoolP256r1:P-256:X25519");
    301     }
    302     $proxy->ciphers("AES128-SHA:\@SECLEVEL=0");
    303     $testtype = NON_TLS1_3_KEY_SHARE;
    304     $proxy->start();
    305     my $ishrr = defined ${$proxy->message_list}[2]
    306                 &&(${$proxy->message_list}[0]->mt == TLSProxy::Message::MT_CLIENT_HELLO)
    307                 && (${$proxy->message_list}[2]->mt == TLSProxy::Message::MT_CLIENT_HELLO);
    308     ok(TLSProxy::Message->success() && $ishrr,
    309        "Client sends a key_share for a Non TLSv1.3 group");
    310 }
    311 
    312 sub modify_key_shares_filter
    313 {
    314     my $proxy = shift;
    315 
    316     # We're only interested in the initial ClientHello/SererHello/HRR
    317     if (($direction == CLIENT_TO_SERVER && $proxy->flight != 0
    318                 && ($proxy->flight != 1 || $testtype != NO_KEY_SHARES_IN_HRR))
    319             || ($direction == SERVER_TO_CLIENT && $proxy->flight != 1)) {
    320         return;
    321     }
    322 
    323     foreach my $message (@{$proxy->message_list}) {
    324         if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO
    325                 && $direction == CLIENT_TO_SERVER) {
    326             my $ext;
    327             my $suppgroups;
    328 
    329             if ($testtype != NON_TLS1_3_KEY_SHARE) {
    330                 #Setup supported groups to include some unrecognised groups
    331                 if (disabled("ecx")) {
    332                     $suppgroups = pack "C8",
    333                         0x00, 0x06, #List Length
    334                         0xff, 0xfe, #Non existing group 1
    335                         0xff, 0xff, #Non existing group 2
    336                         0x00, 0x17; #P-256
    337                 } else {
    338                     $suppgroups = pack "C8",
    339                         0x00, 0x06, #List Length
    340                         0xff, 0xfe, #Non existing group 1
    341                         0xff, 0xff, #Non existing group 2
    342                         0x00, 0x1d; #X25519
    343                 }
    344             } else {
    345                 if (disabled("ecx")) {
    346                     $suppgroups = pack "C6",
    347                         0x00, 0x04, #List Length
    348                         0x00, 0x13,
    349                         0x00, 0x18; #P-384
    350                 } else {
    351                     $suppgroups = pack "C6",
    352                         0x00, 0x04, #List Length
    353                         0x00, 0x13,
    354                         0x00, 0x1d; #X25519
    355                 }
    356             }
    357 
    358             if ($testtype == EMPTY_EXTENSION) {
    359                 $ext = pack "C2",
    360                     0x00, 0x00;
    361             } elsif ($testtype == NO_ACCEPTABLE_KEY_SHARES) {
    362                 $ext = pack "C12",
    363                     0x00, 0x0a, #List Length
    364                     0xff, 0xfe, #Non existing group 1
    365                     0x00, 0x01, 0xff, #key_exchange data
    366                     0xff, 0xff, #Non existing group 2
    367                     0x00, 0x01, 0xff; #key_exchange data
    368             } elsif ($testtype == ACCEPTABLE_AT_END) {
    369                 if (disabled("ecx")) {
    370                     $ext = pack "C11H130",
    371                         0x00, 0x4A, #List Length
    372                         0xff, 0xfe, #Non existing group 1
    373                         0x00, 0x01, 0xff, #key_exchange data
    374                         0x00, 0x17, #P-256
    375                         0x00, 0x41, #key_exchange data length
    376                         "04A798ACF80B2991A0A53D084F4F649A46BE49D061EB5B8CFF9C8EC6AE792507B6".
    377                         "F77FE6E446AF3645FD86BB7CFFD2644E45CC00183343C5CEAD67BB017B082007";  #key_exchange data
    378                 } else {
    379                     $ext = pack "C11H64",
    380                         0x00, 0x29, #List Length
    381                         0xff, 0xfe, #Non existing group 1
    382                         0x00, 0x01, 0xff, #key_exchange data
    383                         0x00, 0x1d, #x25519
    384                         0x00, 0x20, #key_exchange data length
    385                         "155155B95269ED5C87EAA99C2EF5A593".
    386                         "EDF83495E80380089F831B94D14B1421";  #key_exchange data
    387                 }
    388             } elsif ($testtype == NOT_IN_SUPPORTED_GROUPS) {
    389                 $suppgroups = pack "C4",
    390                     0x00, 0x02, #List Length
    391                     0x00, 0xfe; #Non existing group 1
    392             } elsif ($testtype == GROUP_ID_TOO_SHORT) {
    393                 $ext = pack "C6H64C1",
    394                     0x00, 0x25, #List Length
    395                     0x00, 0x1d, #x25519
    396                     0x00, 0x20, #key_exchange data length
    397                     "155155B95269ED5C87EAA99C2EF5A593".
    398                     "EDF83495E80380089F831B94D14B1421";  #key_exchange data
    399                     0x00;       #Group id too short
    400             } elsif ($testtype == KEX_LEN_MISMATCH) {
    401                 $ext = pack "C8",
    402                     0x00, 0x06, #List Length
    403                     0x00, 0x1d, #x25519
    404                     0x00, 0x20, #key_exchange data length
    405                     0x15, 0x51; #Only two bytes of data, but length should be 32
    406             } elsif ($testtype == ZERO_LEN_KEX_DATA) {
    407                 $ext = pack "C10H64",
    408                     0x00, 0x28, #List Length
    409                     0xff, 0xfe, #Non existing group 1
    410                     0x00, 0x00, #zero length key_exchange data is invalid
    411                     0x00, 0x1d, #x25519
    412                     0x00, 0x20, #key_exchange data length
    413                     "155155B95269ED5C87EAA99C2EF5A593".
    414                     "EDF83495E80380089F831B94D14B1421";  #key_exchange data
    415             } elsif ($testtype == TRAILING_DATA) {
    416                 $ext = pack "C6H64C1",
    417                     0x00, 0x24, #List Length
    418                     0x00, 0x1d, #x25519
    419                     0x00, 0x20, #key_exchange data length
    420                     "155155B95269ED5C87EAA99C2EF5A593".
    421                     "EDF83495E80380089F831B94D14B1421", #key_exchange data
    422                     0x00; #Trailing garbage
    423             } elsif ($testtype == NO_KEY_SHARES_IN_HRR) {
    424                 #We trick the server into thinking we sent a P-256 key_share -
    425                 #but the client actually sent X25519
    426                 $ext = pack "C7",
    427                     0x00, 0x05, #List Length
    428                     0x00, 0x17, #P-256
    429                     0x00, 0x01, #key_exchange data length
    430                     0xff;       #Dummy key_share data
    431             } elsif ($testtype == NON_TLS1_3_KEY_SHARE) {
    432                 $ext = pack "C6H98",
    433                     0x00, 0x35, #List Length
    434                     0x00, 0x13, #P-192
    435                     0x00, 0x31, #key_exchange data length
    436                     "04EE3B38D1CB800A1A2B702FC8423599F2AC7161E175C865F8".
    437                     "3DAF78BCBAE561464E8144359BE70CB7989D28A2F43F8F2C";  #key_exchange data
    438             }
    439 
    440             if ($testtype != EMPTY_EXTENSION
    441                     && $testtype != NO_KEY_SHARES_IN_HRR) {
    442                 $message->set_extension(
    443                     TLSProxy::Message::EXT_SUPPORTED_GROUPS, $suppgroups);
    444             }
    445             if ($testtype == MISSING_EXTENSION) {
    446                 $message->delete_extension(
    447                     TLSProxy::Message::EXT_KEY_SHARE);
    448             } elsif ($testtype != NOT_IN_SUPPORTED_GROUPS) {
    449                 $message->set_extension(
    450                     TLSProxy::Message::EXT_KEY_SHARE, $ext);
    451             }
    452 
    453             $message->repack();
    454         } elsif ($message->mt == TLSProxy::Message::MT_SERVER_HELLO
    455                      && $direction == SERVER_TO_CLIENT) {
    456             my $ext;
    457             my $key_share =
    458                 $message->extension_data->{TLSProxy::Message::EXT_KEY_SHARE};
    459             $selectedgroupid = unpack("n", $key_share);
    460 
    461             if ($testtype == LOOK_ONLY) {
    462                 return;
    463             }
    464             if ($testtype == NO_KEY_SHARES_IN_HRR) {
    465                 $message->delete_extension(TLSProxy::Message::EXT_KEY_SHARE);
    466                 $message->set_extension(TLSProxy::Message::EXT_UNKNOWN, "");
    467                 $message->repack();
    468                 return;
    469             }
    470             if ($testtype == SELECT_X25519) {
    471                 $ext = pack "C4H64",
    472                     0x00, 0x1d, #x25519
    473                     0x00, 0x20, #key_exchange data length
    474                     "155155B95269ED5C87EAA99C2EF5A593".
    475                     "EDF83495E80380089F831B94D14B1421";  #key_exchange data
    476             } elsif ($testtype == GROUP_ID_TOO_SHORT) {
    477                 $ext = pack "C1",
    478                     0x00;
    479             } elsif ($testtype == KEX_LEN_MISMATCH) {
    480                 $ext = pack "C6",
    481                     0x00, 0x1d, #x25519
    482                     0x00, 0x20, #key_exchange data length
    483                     0x15, 0x51; #Only two bytes of data, but length should be 32
    484             } elsif ($testtype == ZERO_LEN_KEX_DATA) {
    485                 $ext = pack "C4",
    486                     0x00, 0x1d, #x25519
    487                     0x00, 0x00, #zero length key_exchange data is invalid
    488             } elsif ($testtype == TRAILING_DATA) {
    489                 $ext = pack "C4H64C1",
    490                     0x00, 0x1d, #x25519
    491                     0x00, 0x20, #key_exchange data length
    492                     "155155B95269ED5C87EAA99C2EF5A593".
    493                     "EDF83495E80380089F831B94D14B1421", #key_exchange data
    494                     0x00; #Trailing garbage
    495             }
    496             $message->set_extension(TLSProxy::Message::EXT_KEY_SHARE, $ext);
    497 
    498             $message->repack();
    499         }
    500     }
    501 }
    502 
    503 
    504