xref: /openssl/test/quicfaultstest.c (revision b6461792)
1 /*
2  * Copyright 2022-2024 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 
10 #include <string.h>
11 #include <openssl/ssl.h>
12 #include "helpers/quictestlib.h"
13 #include "internal/quic_error.h"
14 #include "testutil.h"
15 
16 static char *cert = NULL;
17 static char *privkey = NULL;
18 
19 /*
20  * Basic test that just creates a connection and sends some data without any
21  * faults injected.
22  */
test_basic(void)23 static int test_basic(void)
24 {
25     int testresult = 0;
26     SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
27     QUIC_TSERVER *qtserv = NULL;
28     SSL *cssl = NULL;
29     char *msg = "Hello World!";
30     size_t msglen = strlen(msg);
31     unsigned char buf[80];
32     size_t bytesread;
33 
34     if (!TEST_ptr(cctx))
35         goto err;
36 
37     if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
38                                              &qtserv, &cssl, NULL, NULL)))
39         goto err;
40 
41     if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
42         goto err;
43 
44     if (!TEST_int_eq(SSL_write(cssl, msg, msglen), msglen))
45         goto err;
46 
47     ossl_quic_tserver_tick(qtserv);
48     if (!TEST_true(ossl_quic_tserver_read(qtserv, 0, buf, sizeof(buf), &bytesread)))
49         goto err;
50 
51     /*
52      * We assume the entire message is read from the server in one go. In
53      * theory this could get fragmented but its a small message so we assume
54      * not.
55      */
56     if (!TEST_mem_eq(msg, msglen, buf, bytesread))
57         goto err;
58 
59     testresult = 1;
60  err:
61     SSL_free(cssl);
62     ossl_quic_tserver_free(qtserv);
63     SSL_CTX_free(cctx);
64     return testresult;
65 }
66 
67 /*
68  * Test that adding an unknown frame type is handled correctly
69  */
add_unknown_frame_cb(QTEST_FAULT * fault,QUIC_PKT_HDR * hdr,unsigned char * buf,size_t len,void * cbarg)70 static int add_unknown_frame_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
71                                 unsigned char *buf, size_t len, void *cbarg)
72 {
73     static size_t done = 0;
74     /*
75      * There are no "reserved" frame types which are definitately safe for us
76      * to use for testing purposes - but we just use the highest possible
77      * value (8 byte length integer) and with no payload bytes
78      */
79     unsigned char unknown_frame[] = {
80         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
81     };
82 
83     /* We only ever add the unknown frame to one packet */
84     if (done++)
85         return 1;
86 
87     return qtest_fault_prepend_frame(fault, unknown_frame,
88                                      sizeof(unknown_frame));
89 }
90 
test_unknown_frame(void)91 static int test_unknown_frame(void)
92 {
93     int testresult = 0, ret;
94     SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
95     QUIC_TSERVER *qtserv = NULL;
96     SSL *cssl = NULL;
97     char *msg = "Hello World!";
98     size_t msglen = strlen(msg);
99     unsigned char buf[80];
100     size_t byteswritten;
101     QTEST_FAULT *fault = NULL;
102     uint64_t sid = UINT64_MAX;
103 
104     if (!TEST_ptr(cctx))
105         goto err;
106 
107     if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
108                                              &qtserv, &cssl, &fault, NULL)))
109         goto err;
110 
111     if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
112         goto err;
113 
114     /*
115      * Write a message from the server to the client and add an unknown frame
116      * type
117      */
118     if (!TEST_true(qtest_fault_set_packet_plain_listener(fault,
119                                                          add_unknown_frame_cb,
120                                                          NULL)))
121         goto err;
122 
123     if (!TEST_true(ossl_quic_tserver_stream_new(qtserv, /*is_uni=*/0, &sid))
124         || !TEST_uint64_t_eq(sid, 1))
125         goto err;
126 
127     if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg, msglen,
128                                            &byteswritten)))
129         goto err;
130 
131     if (!TEST_size_t_eq(msglen, byteswritten))
132         goto err;
133 
134     ossl_quic_tserver_tick(qtserv);
135     if (!TEST_true(SSL_handle_events(cssl)))
136         goto err;
137 
138     if (!TEST_int_le(ret = SSL_read(cssl, buf, sizeof(buf)), 0))
139         goto err;
140 
141     if (!TEST_int_eq(SSL_get_error(cssl, ret), SSL_ERROR_SSL))
142         goto err;
143 
144     if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_error()),
145                      SSL_R_QUIC_PROTOCOL_ERROR))
146         goto err;
147 
148     if (!TEST_true(qtest_check_server_frame_encoding_err(qtserv)))
149         goto err;
150 
151     testresult = 1;
152  err:
153     qtest_fault_free(fault);
154     SSL_free(cssl);
155     ossl_quic_tserver_free(qtserv);
156     SSL_CTX_free(cctx);
157     return testresult;
158 }
159 
160 /*
161  * Test that a server that fails to provide transport params cannot be
162  * connected to.
163  */
drop_extensions_cb(QTEST_FAULT * fault,QTEST_ENCRYPTED_EXTENSIONS * ee,size_t eelen,void * encextcbarg)164 static int drop_extensions_cb(QTEST_FAULT *fault,
165                                     QTEST_ENCRYPTED_EXTENSIONS *ee,
166                                     size_t eelen, void *encextcbarg)
167 {
168     int *ext = (int *)encextcbarg;
169 
170     if (!qtest_fault_delete_extension(fault, *ext, ee->extensions,
171                                       &ee->extensionslen, NULL))
172         return 0;
173 
174     return 1;
175 }
176 
test_drop_extensions(int idx)177 static int test_drop_extensions(int idx)
178 {
179     int testresult = 0;
180     SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
181     QUIC_TSERVER *qtserv = NULL;
182     SSL *cssl = NULL;
183     QTEST_FAULT *fault = NULL;
184     int ext, err;
185 
186     if (!TEST_ptr(cctx))
187         goto err;
188 
189     if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
190                                              &qtserv, &cssl, &fault, NULL)))
191         goto err;
192 
193     if (idx == 0) {
194         ext = TLSEXT_TYPE_quic_transport_parameters;
195         err = OSSL_QUIC_ERR_CRYPTO_MISSING_EXT;
196     } else {
197         ext = TLSEXT_TYPE_application_layer_protocol_negotiation;
198         err = OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO;
199     }
200 
201     if (!TEST_true(qtest_fault_set_hand_enc_ext_listener(fault,
202                                                          drop_extensions_cb,
203                                                          &ext)))
204         goto err;
205 
206     /*
207      * We expect the connection to fail because the server failed to provide
208      * transport parameters
209      */
210     if (!TEST_false(qtest_create_quic_connection(qtserv, cssl)))
211         goto err;
212 
213     if (!TEST_true(qtest_check_server_transport_err(qtserv, err)))
214         goto err;
215 
216     testresult = 1;
217  err:
218     qtest_fault_free(fault);
219     SSL_free(cssl);
220     ossl_quic_tserver_free(qtserv);
221     SSL_CTX_free(cctx);
222     return testresult;
223 }
224 
225 /*
226  * Test that corrupted packets/datagrams are dropped and retransmitted
227  */
228 static int docorrupt = 0;
229 
on_packet_cipher_cb(QTEST_FAULT * fault,QUIC_PKT_HDR * hdr,unsigned char * buf,size_t len,void * cbarg)230 static int on_packet_cipher_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
231                                unsigned char *buf, size_t len, void *cbarg)
232 {
233     if (!docorrupt || len == 0)
234         return 1;
235 
236     buf[(size_t)test_random() % len] ^= 0xff;
237     docorrupt = 0;
238 
239     return 1;
240 }
241 
on_datagram_cb(QTEST_FAULT * fault,BIO_MSG * m,size_t stride,void * cbarg)242 static int on_datagram_cb(QTEST_FAULT *fault, BIO_MSG *m, size_t stride,
243                           void *cbarg)
244 {
245     if (!docorrupt || m->data_len == 0)
246         return 1;
247 
248     if (!qtest_fault_resize_datagram(fault, m->data_len - 1))
249         return 1;
250 
251     docorrupt = 0;
252 
253     return 1;
254 }
255 
256 /*
257  * Test 1: Corrupt by flipping bits in an encrypted packet
258  * Test 2: Corrupt by truncating an entire datagram
259  */
test_corrupted_data(int idx)260 static int test_corrupted_data(int idx)
261 {
262     QTEST_FAULT *fault = NULL;
263     int testresult = 0;
264     SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
265     QUIC_TSERVER *qtserv = NULL;
266     SSL *cssl = NULL;
267     char *msg = "Hello World!";
268     size_t msglen = strlen(msg);
269     unsigned char buf[80];
270     size_t bytesread, byteswritten;
271     uint64_t sid = UINT64_MAX;
272 
273     if (!TEST_ptr(cctx))
274         goto err;
275 
276     if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey,
277                                              QTEST_FLAG_FAKE_TIME, &qtserv,
278                                              &cssl, &fault, NULL)))
279         goto err;
280 
281     if (idx == 0) {
282         /* Listen for encrypted packets being sent */
283         if (!TEST_true(qtest_fault_set_packet_cipher_listener(fault,
284                                                               on_packet_cipher_cb,
285                                                               NULL)))
286             goto err;
287     } else {
288         /* Listen for datagrams being sent */
289         if (!TEST_true(qtest_fault_set_datagram_listener(fault,
290                                                          on_datagram_cb,
291                                                          NULL)))
292             goto err;
293     }
294     if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
295         goto err;
296 
297     /* Corrupt the next server packet*/
298     docorrupt = 1;
299 
300     if (!TEST_true(ossl_quic_tserver_stream_new(qtserv, /*is_uni=*/0, &sid))
301         || !TEST_uint64_t_eq(sid, 1))
302         goto err;
303 
304     /*
305      * Send first 5 bytes of message. This will get corrupted and is treated as
306      * "lost"
307      */
308     if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg, 5,
309                                            &byteswritten)))
310         goto err;
311 
312     if (!TEST_size_t_eq(byteswritten, 5))
313         goto err;
314 
315     /*
316      * Introduce a small delay so that the above packet has time to be detected
317      * as lost. Loss detection times are based on RTT which should be very
318      * fast for us since there isn't really a network. The loss delay timer is
319      * always at least 1ms though. We skip forward 100ms
320      */
321     qtest_add_time(100);
322 
323     /* Send rest of message */
324     if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg + 5,
325                                            msglen - 5, &byteswritten)))
326         goto err;
327 
328     if (!TEST_size_t_eq(byteswritten, msglen - 5))
329         goto err;
330 
331     /*
332      * Receive the corrupted packet. This should get dropped and is effectively
333      * "lost". We also process the second packet which should be decrypted
334      * successfully. Therefore we ack the frames in it
335      */
336     if (!TEST_true(SSL_handle_events(cssl)))
337         goto err;
338 
339     /*
340      * Process the ack. Detect that the first part of the message must have
341      * been lost due to the time elapsed since it was sent and resend it
342      */
343     ossl_quic_tserver_tick(qtserv);
344 
345     /* Receive and process the newly arrived message data resend */
346     if (!TEST_true(SSL_handle_events(cssl)))
347         goto err;
348 
349     /* The whole message should now have arrived */
350     if (!TEST_true(SSL_read_ex(cssl, buf, sizeof(buf), &bytesread)))
351         goto err;
352 
353     if (!TEST_mem_eq(msg, msglen, buf, bytesread))
354         goto err;
355 
356     /*
357      * If the test was successful then we corrupted exactly one packet and
358      * docorrupt was reset
359      */
360     if (!TEST_false(docorrupt))
361         goto err;
362 
363     testresult = 1;
364  err:
365     qtest_fault_free(fault);
366     SSL_free(cssl);
367     ossl_quic_tserver_free(qtserv);
368     SSL_CTX_free(cctx);
369     return testresult;
370 }
371 
372 OPT_TEST_DECLARE_USAGE("certsdir\n")
373 
setup_tests(void)374 int setup_tests(void)
375 {
376     char *certsdir = NULL;
377 
378     if (!test_skip_common_options()) {
379         TEST_error("Error parsing test options\n");
380         return 0;
381     }
382 
383     if (!TEST_ptr(certsdir = test_get_argument(0)))
384         return 0;
385 
386     cert = test_mk_file_path(certsdir, "servercert.pem");
387     if (cert == NULL)
388         goto err;
389 
390     privkey = test_mk_file_path(certsdir, "serverkey.pem");
391     if (privkey == NULL)
392         goto err;
393 
394     ADD_TEST(test_basic);
395     ADD_TEST(test_unknown_frame);
396     ADD_ALL_TESTS(test_drop_extensions, 2);
397     ADD_ALL_TESTS(test_corrupted_data, 2);
398 
399     return 1;
400 
401  err:
402     OPENSSL_free(cert);
403     OPENSSL_free(privkey);
404     return 0;
405 }
406 
cleanup_tests(void)407 void cleanup_tests(void)
408 {
409     OPENSSL_free(cert);
410     OPENSSL_free(privkey);
411 }
412