1#! /usr/bin/env perl 2# Copyright 2015-2023 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 NON_TLS1_3_KEY_SHARE => 13 30}; 31 32use constant { 33 CLIENT_TO_SERVER => 1, 34 SERVER_TO_CLIENT => 2 35}; 36 37 38use constant { 39 X25519 => 0x1d, 40 P_256 => 0x17, 41 FFDHE2048 => 0x0100, 42 FFDHE3072 => 0x0101 43}; 44 45my $testtype; 46my $direction; 47my $selectedgroupid; 48 49my $test_name = "test_key_share"; 50setup($test_name); 51 52plan skip_all => "TLSProxy isn't usable on $^O" 53 if $^O =~ /^(VMS)$/; 54 55plan skip_all => "$test_name needs the dynamic engine feature enabled" 56 if disabled("engine") || disabled("dynamic-engine"); 57 58plan skip_all => "$test_name needs the sock feature enabled" 59 if disabled("sock"); 60 61plan skip_all => "$test_name needs TLS1.3 enabled" 62 if disabled("tls1_3"); 63 64plan skip_all => "$test_name needs EC or DH enabled" 65 if disabled("ec") && disabled("dh"); 66 67my $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); 81if (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"; 87plan tests => 23; 88ok(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(); 94ok(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(); 100ok(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); 106if (disabled("ec")) { 107 $proxy->serverflags("-groups ffdhe3072"); 108} else { 109 $proxy->serverflags("-groups P-256"); 110} 111$proxy->start(); 112ok(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); 117if (disabled("ec")) { 118 $proxy->serverflags("-groups ffdhe2048"); 119} else { 120 $proxy->serverflags("-groups P-256"); 121} 122if (disabled("ec")) { 123 $proxy->clientflags("-groups ffdhe3072"); 124} else { 125 $proxy->clientflags("-groups P-384"); 126} 127$proxy->start(); 128ok(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"); 133if (disabled("ec")) { 134 $proxy->clientflags("-groups ffdhe3072"); 135} else { 136 $proxy->clientflags("-groups P-256"); 137} 138$proxy->start(); 139ok(TLSProxy::Message->success(), "Non preferred key_share"); 140$proxy->filter(\&modify_key_shares_filter); 141 142SKIP: { 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(); 158ok(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(); 164ok(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(); 170ok(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(); 176ok(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(); 182ok(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; 189if (disabled("ec")) { 190 $proxy->clientflags("-groups ffdhe3072:ffdhe2048"); 191} else { 192 $proxy->clientflags("-groups P-256:P-384"); 193} 194$proxy->start(); 195if (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(); 205if (disabled("ecx")) { 206 $proxy->clientflags("-curves ffdhe2048:ffdhe3072"); 207} else { 208 $proxy->clientflags("-curves X25519:P-256"); 209} 210$proxy->start(); 211if (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; 222if (disabled("ecx")) { 223 $proxy->clientflags("-groups ffdhe3072"); 224} else { 225 $proxy->clientflags("-groups P-256"); 226} 227$proxy->start(); 228ok(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(); 234ok(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(); 240ok(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(); 246ok(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(); 252ok(TLSProxy::Message->fail(), "key_share trailing data in ServerHello"); 253 254SKIP: { 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; 284if (disabled("ecx")) { 285 $proxy->serverflags("-groups ffdhe2048"); 286} else { 287 $proxy->serverflags("-groups X25519"); 288} 289$proxy->start(); 290ok(TLSProxy::Message->fail(), "Server sends HRR with no key_shares"); 291 292SKIP: { 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 secp192r1:P-256:P-384"); 299 } else { 300 $proxy->clientflags("-groups secp192r1: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 312sub 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