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