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