1QUIC Fault Injector 2=================== 3 4The OpenSSL QUIC implementation receives QUIC packets from the network layer and 5processes them accordingly. It will need to behave appropriately in the event of 6a misbehaving peer, i.e. one which is sending protocol elements (e.g. datagrams, 7packets, frames, etc) that are not in accordance with the specifications or 8OpenSSL's expectations. 9 10The QUIC Fault Injector is a component within the OpenSSL test framework that 11can be used to simulate misbehaving peers and confirm that OpenSSL QUIC 12implementation behaves in the expected manner in the event of such misbehaviour. 13 14Typically an individual test will inject one particular misbehaviour (i.e. a 15fault) into an otherwise normal QUIC connection. Therefore the fault injector 16will have to be capable of creating fully normal QUIC protocol elements, but 17also offer the flexibility for a test to modify those normal protocol elements 18as required for the specific test circumstances. The OpenSSL QUIC implementation 19in libssl does not offer the capability to send faults since it is designed to 20be RFC compliant. 21 22The QUIC Fault Injector will be external to libssl (it will be in the test 23framework) but it will reuse the standards compliant QUIC implementation in 24libssl and will make use of 3 integration points to inject faults. 2 of these 25integration points will use new callbacks added to libssl. The final integration 26point does not require any changes to libssl to work. 27 28QUIC Integration Points 29----------------------- 30 31### TLS Handshake 32 33Fault Injector based tests may need to inject faults directly into the TLS 34handshake data (i.e. the contents of CRYPTO frames). However such faults may 35need to be done in handshake messages that would normally be encrypted. 36Additionally the contents of handshake messages are hashed and each peer 37confirms that the other peer has the same calculated hash value as part of the 38"Finished" message exchange - so any modifications would be rejected and the 39handshake would fail. 40 41An example test might be to confirm that an OpenSSL QUIC client behaves 42correctly in the case that the server provides incorrectly formatted transport 43parameters. These transport parameters are sent from the server in the 44EncryptedExtensions message. That message is encrypted and so cannot be 45modified by a "man-in-the-middle". 46 47To support this integration point two new callbacks will be introduced to libssl 48that enables modification of handshake data prior to it being encrypted and 49hashed. These callbacks will be internal only (i.e. not part of the public API) 50and so only usable by the Fault Injector. 51 52The new libssl callbacks will be as follows: 53 54```` C 55typedef int (*ossl_statem_mutate_handshake_cb)(const unsigned char *msgin, 56 size_t inlen, 57 unsigned char **msgout, 58 size_t *outlen, 59 void *arg); 60 61typedef void (*ossl_statem_finish_mutate_handshake_cb)(void *arg); 62 63int ossl_statem_set_mutator(SSL *s, 64 ossl_statem_mutate_handshake_cb mutate_handshake_cb, 65 ossl_statem_finish_mutate_handshake_cb finish_mutate_handshake_cb, 66 void *mutatearg); 67```` 68 69The two callbacks are set via a single internal function call 70`ossl_statem_set_mutator`. The mutator callback `mutate_handshake_cb` will be 71called after each handshake message has been constructed and is ready to send, but 72before it has been passed through the handshake hashing code. It will be passed 73a pointer to the constructed handshake message in `msgin` along with its 74associated length in `inlen`. The mutator will construct a replacement handshake 75message (typically by copying the input message and modifying it) and store it 76in a newly allocated buffer. A pointer to the new buffer will be passed back 77in `*msgout` and its length will be stored in `*outlen`. Optionally the mutator 78can choose to not mutate by simply creating a new buffer with a copy of the data 79in it. A return value of 1 indicates that the callback completed successfully. A 80return value of 0 indicates a fatal error. 81 82Once libssl has finished using the mutated buffer it will call the 83`finish_mutate_handshake_cb` callback which can then release the buffer and 84perform any other cleanup as required. 85 86### QUIC Pre-Encryption Packets 87 88QUIC Packets are the primary mechanism for exchanging protocol data within QUIC. 89Multiple packets may be held within a single datagram, and each packet may 90itself contain multiple frames. A packet gets protected via an AEAD encryption 91algorithm prior to it being sent. Fault Injector based tests may need to inject 92faults into these packets prior to them being encrypted. 93 94An example test might insert an unrecognised frame type into a QUIC packet to 95confirm that an OpenSSL QUIC client handles it appropriately (e.g. by raising a 96protocol error). 97 98The above functionality will be supported by the following two new callbacks 99which will provide the ability to mutate packets before they are encrypted and 100sent. As for the TLS callbacks these will be internal only and not part of the 101public API. 102 103```` C 104typedef int (*ossl_mutate_packet_cb)(const QUIC_PKT_HDR *hdrin, 105 const OSSL_QTX_IOVEC *iovecin, size_t numin, 106 QUIC_PKT_HDR **hdrout, 107 const OSSL_QTX_IOVEC **iovecout, 108 size_t *numout, 109 void *arg); 110 111typedef void (*ossl_finish_mutate_cb)(void *arg); 112 113void ossl_qtx_set_mutator(OSSL_QTX *qtx, ossl_mutate_packet_cb mutatecb, 114 ossl_finish_mutate_cb finishmutatecb, void *mutatearg); 115```` 116 117A single new function call will set both callbacks. The `mutatecb` callback will 118be invoked after each packet has been constructed but before protection has 119been applied to it. The header for the packet will be pointed to by `hdrin` and 120the payload will be in an iovec array pointed to by `iovecin` and containing 121`numin` iovecs. The `mutatecb` callback is expected to allocate a new header 122structure and return it in `*hdrout` and a new set of iovecs to be stored in 123`*iovecout`. The number of iovecs need not be the same as the input. The number 124of iovecs in the output array is stored in `*numout`. Optionally the callback 125can choose to not mutate by simply creating new iovecs/headers with a copy of the 126data in it. A return value of 1 indicates that the callback completed 127successfully. A return value of 0 indicates a fatal error. 128 129Once the OpenSSL QUIC implementation has finished using the mutated buffers the 130`finishmutatecb` callback is called. This is expected to free any resources and 131buffers that were allocated as part of the `mutatecb` call. 132 133### QUIC Datagrams 134 135Encrypted QUIC packets are sent in datagrams. There may be more than one QUIC 136packet in a single datagram. Fault Injector based tests may need to inject 137faults directly into these datagrams. 138 139An example test might modify an encrypted packet to confirm that the AEAD 140decryption process rejects it. 141 142In order to provide this functionality the QUIC Fault Injector will insert 143itself as a man-in-the-middle between the client and server. A BIO_s_dgram_pair() 144will be used with one of the pair being used on the client end and the other 145being associated with the Fault Injector. Similarly a second BIO_s_dgram_pair() 146will be created with one used on the server and other used with the Fault 147Injector. 148 149With this setup the Fault Injector will act as a proxy and simply pass 150datagrams sent from the client on to the server, and vice versa. Where a test 151requires a modification to be made, that will occur prior to the datagram being 152sent on. 153 154This will all be implemented using public BIO APIs without requiring any 155additional internal libssl callbacks. 156 157Fault Injector API 158------------------ 159 160The Fault Injector will utilise the callbacks described above in order to supply 161a more test friendly API to test authors. 162 163This API will primarily take the form of a set of event listener callbacks. A 164test will be able to "listen" for a specific event occurring and be informed about 165it when it does. Examples of events might include: 166 167- An EncryptedExtensions handshake message being sent 168- An ACK frame being sent 169- A Datagram being sent 170 171Each listener will be provided with additional data about the specific event. 172For example a listener that is listening for an EncryptedExtensions message will 173be provided with the parsed contents of that message in an easy to use 174structure. Additional helper functions will be provided to make changes to the 175message (such as to resize it). 176 177Initially listeners will only be able to listen for events on the server side. 178This is because, in MVP, it will be the client side that is under test - so the 179faults need to be injected into protocol elements sent from the server. Post 180MVP this will be extended in order to be able to test the server. It may be that 181we need to do this during MVP in order to be able to observe protocol elements 182sent from the client without modifying them (i.e. in order to confirm that the 183client is behaving as we expect). This will be added if required as we develop 184the tests. 185 186It is expected that the Fault Injector API will expand over time as new 187listeners and helper functions are added to support specific test scenarios. The 188initial API will provide a basic set of listeners and helper functions in order 189to provide the basis for future work. 190 191The following outlines an illustrative set of functions that will initially be 192provided. A number of `TODO(QUIC TESTING)` comments are inserted to explain how 193we might expand the API over time: 194 195```` C 196/* Type to represent the Fault Injector */ 197typedef struct ossl_quic_fault OSSL_QUIC_FAULT; 198 199/* 200 * Structure representing a parsed EncryptedExtension message. Listeners can 201 * make changes to the contents of structure objects as required and the fault 202 * injector will reconstruct the message to be sent on 203 */ 204typedef struct ossl_qf_encrypted_extensions { 205 /* EncryptedExtension messages just have an extensions block */ 206 unsigned char *extensions; 207 size_t extensionslen; 208} OSSL_QF_ENCRYPTED_EXTENSIONS; 209 210/* 211 * Given an SSL_CTX for the client and filenames for the server certificate and 212 * keyfile, create a server and client instances as well as a fault injector 213 * instance. |block| indicates whether we are using blocking mode or not. 214 */ 215int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx, 216 SSL_CTX *serverctx, char *certfile, char *keyfile, 217 int block, QUIC_TSERVER **qtserv, SSL **cssl, 218 OSSL_QUIC_FAULT **fault, BIO **tracebio); 219 220/* 221 * Free up a Fault Injector instance 222 */ 223void ossl_quic_fault_free(OSSL_QUIC_FAULT *fault); 224 225/* 226 * Run the TLS handshake to create a QUIC connection between the client and 227 * server. 228 */ 229int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl); 230 231/* 232 * Same as qtest_create_quic_connection but will stop (successfully) if the 233 * clientssl indicates SSL_ERROR_WANT_XXX as specified by |wanterr| 234 */ 235int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl, 236 int wanterr); 237 238/* 239 * Confirm that the server has received the given transport error code. 240 */ 241int qtest_check_server_transport_err(QUIC_TSERVER *qtserv, uint64_t code); 242 243/* 244 * Confirm the server has received a protocol error. Equivalent to calling 245 * qtest_check_server_transport_err with a code of QUIC_ERR_PROTOCOL_VIOLATION 246 */ 247int qtest_check_server_protocol_err(QUIC_TSERVER *qtserv); 248 249/* 250 * Enable tests to listen for pre-encryption QUIC packets being sent 251 */ 252typedef int (*ossl_quic_fault_on_packet_plain_cb)(OSSL_QUIC_FAULT *fault, 253 QUIC_PKT_HDR *hdr, 254 unsigned char *buf, 255 size_t len, 256 void *cbarg); 257 258int ossl_quic_fault_set_packet_plain_listener(OSSL_QUIC_FAULT *fault, 259 ossl_quic_fault_on_packet_plain_cb pplaincb, 260 void *pplaincbarg); 261 262 263/* 264 * Helper function to be called from a packet_plain_listener callback if it 265 * wants to resize the packet (either to add new data to it, or to truncate it). 266 * The buf provided to packet_plain_listener is over allocated, so this just 267 * changes the logical size and never changes the actual address of the buf. 268 * This will fail if a large resize is attempted that exceeds the over 269 * allocation. 270 */ 271int ossl_quic_fault_resize_plain_packet(OSSL_QUIC_FAULT *fault, size_t newlen); 272 273/* 274 * Prepend frame data into a packet. To be called from a packet_plain_listener 275 * callback 276 */ 277int ossl_quic_fault_prepend_frame(OSSL_QUIC_FAULT *fault, unsigned char *frame, 278 size_t frame_len); 279 280/* 281 * The general handshake message listener is sent the entire handshake message 282 * data block, including the handshake header itself 283 */ 284typedef int (*ossl_quic_fault_on_handshake_cb)(OSSL_QUIC_FAULT *fault, 285 unsigned char *msg, 286 size_t msglen, 287 void *handshakecbarg); 288 289int ossl_quic_fault_set_handshake_listener(OSSL_QUIC_FAULT *fault, 290 ossl_quic_fault_on_handshake_cb handshakecb, 291 void *handshakecbarg); 292 293/* 294 * Helper function to be called from a handshake_listener callback if it wants 295 * to resize the handshake message (either to add new data to it, or to truncate 296 * it). newlen must include the length of the handshake message header. The 297 * handshake message buffer is over allocated, so this just changes the logical 298 * size and never changes the actual address of the buf. 299 * This will fail if a large resize is attempted that exceeds the over 300 * allocation. 301 */ 302int ossl_quic_fault_resize_handshake(OSSL_QUIC_FAULT *fault, size_t newlen); 303 304/* 305 * TODO(QUIC TESTING): Add listeners for specific types of frame here. E.g. 306 * we might expect to see an "ACK" frame listener which will be passed 307 * pre-parsed ack data that can be modified as required. 308 */ 309 310/* 311 * Handshake message specific listeners. Unlike the general handshake message 312 * listener these messages are pre-parsed and supplied with message specific 313 * data and exclude the handshake header. 314 */ 315typedef int (*ossl_quic_fault_on_enc_ext_cb)(OSSL_QUIC_FAULT *fault, 316 OSSL_QF_ENCRYPTED_EXTENSIONS *ee, 317 size_t eelen, 318 void *encextcbarg); 319 320int ossl_quic_fault_set_hand_enc_ext_listener(OSSL_QUIC_FAULT *fault, 321 ossl_quic_fault_on_enc_ext_cb encextcb, 322 void *encextcbarg); 323 324/* TODO(QUIC TESTING): Add listeners for other types of handshake message here */ 325 326 327/* 328 * Helper function to be called from message specific listener callbacks. newlen 329 * is the new length of the specific message excluding the handshake message 330 * header. The buffers provided to the message specific listeners are over 331 * allocated, so this just changes the logical size and never changes the actual 332 * address of the buffer. This will fail if a large resize is attempted that 333 * exceeds the over allocation. 334 */ 335int ossl_quic_fault_resize_message(OSSL_QUIC_FAULT *fault, size_t newlen); 336 337/* 338 * Helper function to delete an extension from an extension block. |exttype| is 339 * the type of the extension to be deleted. |ext| points to the extension block. 340 * On entry |*extlen| contains the length of the extension block. It is updated 341 * with the new length on exit. 342 */ 343int ossl_quic_fault_delete_extension(OSSL_QUIC_FAULT *fault, 344 unsigned int exttype, unsigned char *ext, 345 size_t *extlen); 346 347/* 348 * TODO(QUIC TESTING): Add additional helper functions for querying extensions 349 * here (e.g. finding or adding them). We could also provide a "listener" API 350 * for listening for specific extension types. 351 */ 352 353/* 354 * Enable tests to listen for post-encryption QUIC packets being sent 355 */ 356typedef int (*ossl_quic_fault_on_packet_cipher_cb)(OSSL_QUIC_FAULT *fault, 357 /* The parsed packet header */ 358 QUIC_PKT_HDR *hdr, 359 /* The packet payload data */ 360 unsigned char *buf, 361 /* Length of the payload */ 362 size_t len, 363 void *cbarg); 364 365int ossl_quic_fault_set_packet_cipher_listener(OSSL_QUIC_FAULT *fault, 366 ossl_quic_fault_on_packet_cipher_cb pciphercb, 367 void *picphercbarg); 368 369/* 370 * Enable tests to listen for datagrams being sent 371 */ 372typedef int (*ossl_quic_fault_on_datagram_cb)(OSSL_QUIC_FAULT *fault, 373 BIO_MSG *m, 374 size_t stride, 375 void *cbarg); 376 377int ossl_quic_fault_set_datagram_listener(OSSL_QUIC_FAULT *fault, 378 ossl_quic_fault_on_datagram_cb datagramcb, 379 void *datagramcbarg); 380 381/* 382 * To be called from a datagram_listener callback. The datagram buffer is over 383 * allocated, so this just changes the logical size and never changes the actual 384 * address of the buffer. This will fail if a large resize is attempted that 385 * exceeds the over allocation. 386 */ 387int ossl_quic_fault_resize_datagram(OSSL_QUIC_FAULT *fault, size_t newlen); 388 389```` 390 391Example Tests 392------------- 393 394This section provides some example tests to illustrate how the Fault Injector 395might be used to create tests. 396 397### Unknown Frame Test 398 399An example test showing a server sending a frame of an unknown type to the 400client: 401 402```` C 403/* 404 * Test that adding an unknown frame type is handled correctly 405 */ 406static int add_unknown_frame_cb(OSSL_QUIC_FAULT *fault, QUIC_PKT_HDR *hdr, 407 unsigned char *buf, size_t len, void *cbarg) 408{ 409 static size_t done = 0; 410 /* 411 * There are no "reserved" frame types which are definitately safe for us 412 * to use for testing purposes - but we just use the highest possible 413 * value (8 byte length integer) and with no payload bytes 414 */ 415 unsigned char unknown_frame[] = { 416 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 417 }; 418 419 /* We only ever add the unknown frame to one packet */ 420 if (done++) 421 return 1; 422 423 return ossl_quic_fault_prepend_frame(fault, unknown_frame, 424 sizeof(unknown_frame)); 425} 426 427static int test_unknown_frame(void) 428{ 429 int testresult = 0, ret; 430 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method()); 431 QUIC_TSERVER *qtserv = NULL; 432 SSL *cssl = NULL; 433 char *msg = "Hello World!"; 434 size_t msglen = strlen(msg); 435 unsigned char buf[80]; 436 size_t byteswritten; 437 OSSL_QUIC_FAULT *fault = NULL; 438 439 if (!TEST_ptr(cctx)) 440 goto err; 441 442 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0, 443 &qtserv, &cssl, &fault, NULL))) 444 goto err; 445 446 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl))) 447 goto err; 448 449 /* 450 * Write a message from the server to the client and add an unknown frame 451 * type 452 */ 453 if (!TEST_true(ossl_quic_fault_set_packet_plain_listener(fault, 454 add_unknown_frame_cb, 455 NULL))) 456 goto err; 457 458 if (!TEST_true(ossl_quic_tserver_write(qtserv, (unsigned char *)msg, msglen, 459 &byteswritten))) 460 goto err; 461 462 if (!TEST_size_t_eq(msglen, byteswritten)) 463 goto err; 464 465 ossl_quic_tserver_tick(qtserv); 466 if (!TEST_true(SSL_tick(cssl))) 467 goto err; 468 469 if (!TEST_int_le(ret = SSL_read(cssl, buf, sizeof(buf)), 0)) 470 goto err; 471 472 if (!TEST_int_eq(SSL_get_error(cssl, ret), SSL_ERROR_SSL)) 473 goto err; 474 475 if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_error()), 476 SSL_R_UNKNOWN_FRAME_TYPE_RECEIVED)) 477 goto err; 478 479 if (!TEST_true(qtest_check_server_protocol_err(qtserv))) 480 goto err; 481 482 testresult = 1; 483 err: 484 ossl_quic_fault_free(fault); 485 SSL_free(cssl); 486 ossl_quic_tserver_free(qtserv); 487 SSL_CTX_free(cctx); 488 return testresult; 489} 490```` 491 492### No Transport Parameters test 493 494An example test showing the case where a server does not supply any transport 495parameters in the TLS handshake: 496 497```` C 498/* 499 * Test that a server that fails to provide transport params cannot be 500 * connected to. 501 */ 502static int drop_transport_params_cb(OSSL_QUIC_FAULT *fault, 503 OSSL_QF_ENCRYPTED_EXTENSIONS *ee, 504 size_t eelen, void *encextcbarg) 505{ 506 if (!ossl_quic_fault_delete_extension(fault, 507 TLSEXT_TYPE_quic_transport_parameters, 508 ee->extensions, &ee->extensionslen)) 509 return 0; 510 511 return 1; 512} 513 514static int test_no_transport_params(void) 515{ 516 int testresult = 0; 517 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method()); 518 QUIC_TSERVER *qtserv = NULL; 519 SSL *cssl = NULL; 520 OSSL_QUIC_FAULT *fault = NULL; 521 522 if (!TEST_ptr(cctx)) 523 goto err; 524 525 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0, 526 &qtserv, &cssl, &fault, NULL))) 527 goto err; 528 529 if (!TEST_true(ossl_quic_fault_set_hand_enc_ext_listener(fault, 530 drop_transport_params_cb, 531 NULL))) 532 goto err; 533 534 /* 535 * We expect the connection to fail because the server failed to provide 536 * transport parameters 537 */ 538 if (!TEST_false(qtest_create_quic_connection(qtserv, cssl))) 539 goto err; 540 541 if (!TEST_true(qtest_check_server_protocol_err(qtserv))) 542 goto err; 543 544 testresult = 1; 545 err: 546 ossl_quic_fault_free(fault); 547 SSL_free(cssl); 548 ossl_quic_tserver_free(qtserv); 549 SSL_CTX_free(cctx); 550 return testresult; 551 552} 553```` 554