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