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