]> git.ipfire.org Git - thirdparty/openssl.git/blame - test/quicfaultstest.c
Copyright year updates
[thirdparty/openssl.git] / test / quicfaultstest.c
CommitLineData
adef87a2 1/*
b6461792 2 * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
adef87a2
MC
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"
71587f2b 13#include "internal/quic_error.h"
adef87a2
MC
14#include "testutil.h"
15
16static char *cert = NULL;
17static char *privkey = NULL;
18
19/*
20 * Basic test that just creates a connection and sends some data without any
21 * faults injected.
22 */
23static 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
cf355bd6 37 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
8d8c0a90 38 &qtserv, &cssl, NULL, NULL)))
adef87a2
MC
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);
b757beb5 48 if (!TEST_true(ossl_quic_tserver_read(qtserv, 0, buf, sizeof(buf), &bytesread)))
adef87a2
MC
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
71587f2b
MC
67/*
68 * Test that adding an unknown frame type is handled correctly
69 */
c12e1113 70static int add_unknown_frame_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
71587f2b
MC
71 unsigned char *buf, size_t len, void *cbarg)
72{
7eaaaaaa 73 static size_t done = 0;
71587f2b
MC
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 */
7eaaaaaa 84 if (done++)
71587f2b 85 return 1;
71587f2b 86
c12e1113
MC
87 return qtest_fault_prepend_frame(fault, unknown_frame,
88 sizeof(unknown_frame));
71587f2b
MC
89}
90
91static 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;
c12e1113 101 QTEST_FAULT *fault = NULL;
9caf9812 102 uint64_t sid = UINT64_MAX;
71587f2b
MC
103
104 if (!TEST_ptr(cctx))
105 goto err;
106
cf355bd6 107 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
8d8c0a90 108 &qtserv, &cssl, &fault, NULL)))
71587f2b
MC
109 goto err;
110
111 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
112 goto err;
113
114 /*
da81f1e5 115 * Write a message from the server to the client and add an unknown frame
71587f2b
MC
116 * type
117 */
c12e1113
MC
118 if (!TEST_true(qtest_fault_set_packet_plain_listener(fault,
119 add_unknown_frame_cb,
120 NULL)))
71587f2b
MC
121 goto err;
122
9caf9812
HL
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,
71587f2b
MC
128 &byteswritten)))
129 goto err;
130
131 if (!TEST_size_t_eq(msglen, byteswritten))
132 goto err;
133
134 ossl_quic_tserver_tick(qtserv);
6084e04b 135 if (!TEST_true(SSL_handle_events(cssl)))
71587f2b
MC
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
71587f2b 144 if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_error()),
2b8126d8 145 SSL_R_QUIC_PROTOCOL_ERROR))
71587f2b 146 goto err;
71587f2b 147
6c1d0e28 148 if (!TEST_true(qtest_check_server_frame_encoding_err(qtserv)))
f10e5885 149 goto err;
71587f2b 150
f10e5885
MC
151 testresult = 1;
152 err:
c12e1113 153 qtest_fault_free(fault);
f10e5885
MC
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 */
d0123191 164static int drop_extensions_cb(QTEST_FAULT *fault,
c12e1113 165 QTEST_ENCRYPTED_EXTENSIONS *ee,
f10e5885
MC
166 size_t eelen, void *encextcbarg)
167{
d0123191
MC
168 int *ext = (int *)encextcbarg;
169
170 if (!qtest_fault_delete_extension(fault, *ext, ee->extensions,
1d8a399f 171 &ee->extensionslen, NULL))
f10e5885
MC
172 return 0;
173
174 return 1;
175}
176
d0123191 177static int test_drop_extensions(int idx)
f10e5885
MC
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;
c12e1113 183 QTEST_FAULT *fault = NULL;
d0123191 184 int ext, err;
f10e5885
MC
185
186 if (!TEST_ptr(cctx))
187 goto err;
188
cf355bd6 189 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
8d8c0a90 190 &qtserv, &cssl, &fault, NULL)))
f10e5885
MC
191 goto err;
192
d0123191
MC
193 if (idx == 0) {
194 ext = TLSEXT_TYPE_quic_transport_parameters;
4b4b9c9e 195 err = OSSL_QUIC_ERR_CRYPTO_MISSING_EXT;
d0123191
MC
196 } else {
197 ext = TLSEXT_TYPE_application_layer_protocol_negotiation;
4b4b9c9e 198 err = OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO;
d0123191
MC
199 }
200
c12e1113 201 if (!TEST_true(qtest_fault_set_hand_enc_ext_listener(fault,
d0123191
MC
202 drop_extensions_cb,
203 &ext)))
f10e5885 204 goto err;
71587f2b
MC
205
206 /*
f10e5885
MC
207 * We expect the connection to fail because the server failed to provide
208 * transport parameters
71587f2b 209 */
f10e5885
MC
210 if (!TEST_false(qtest_create_quic_connection(qtserv, cssl)))
211 goto err;
212
d0123191 213 if (!TEST_true(qtest_check_server_transport_err(qtserv, err)))
71587f2b
MC
214 goto err;
215
216 testresult = 1;
217 err:
c12e1113 218 qtest_fault_free(fault);
71587f2b
MC
219 SSL_free(cssl);
220 ossl_quic_tserver_free(qtserv);
221 SSL_CTX_free(cctx);
222 return testresult;
be5b3b37
MC
223}
224
225/*
6a9ab9bc 226 * Test that corrupted packets/datagrams are dropped and retransmitted
be5b3b37
MC
227 */
228static int docorrupt = 0;
229
c12e1113 230static int on_packet_cipher_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
be5b3b37
MC
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;
f10e5885 238
be5b3b37
MC
239 return 1;
240}
241
c12e1113 242static int on_datagram_cb(QTEST_FAULT *fault, BIO_MSG *m, size_t stride,
6a9ab9bc
MC
243 void *cbarg)
244{
245 if (!docorrupt || m->data_len == 0)
246 return 1;
247
c12e1113 248 if (!qtest_fault_resize_datagram(fault, m->data_len - 1))
6a9ab9bc
MC
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 */
260static int test_corrupted_data(int idx)
be5b3b37 261{
c12e1113 262 QTEST_FAULT *fault = NULL;
be5b3b37
MC
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;
9caf9812 271 uint64_t sid = UINT64_MAX;
be5b3b37
MC
272
273 if (!TEST_ptr(cctx))
274 goto err;
275
cf355bd6 276 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey,
f9fcc7c7 277 QTEST_FLAG_FAKE_TIME, &qtserv,
8d8c0a90 278 &cssl, &fault, NULL)))
be5b3b37
MC
279 goto err;
280
6a9ab9bc
MC
281 if (idx == 0) {
282 /* Listen for encrypted packets being sent */
c12e1113
MC
283 if (!TEST_true(qtest_fault_set_packet_cipher_listener(fault,
284 on_packet_cipher_cb,
285 NULL)))
6a9ab9bc
MC
286 goto err;
287 } else {
288 /* Listen for datagrams being sent */
c12e1113
MC
289 if (!TEST_true(qtest_fault_set_datagram_listener(fault,
290 on_datagram_cb,
291 NULL)))
6a9ab9bc
MC
292 goto err;
293 }
be5b3b37
MC
294 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
295 goto err;
296
297 /* Corrupt the next server packet*/
298 docorrupt = 1;
299
9caf9812
HL
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
be5b3b37
MC
304 /*
305 * Send first 5 bytes of message. This will get corrupted and is treated as
306 * "lost"
307 */
9caf9812 308 if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg, 5,
be5b3b37
MC
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
f9fcc7c7 319 * always at least 1ms though. We skip forward 100ms
be5b3b37 320 */
f9fcc7c7 321 qtest_add_time(100);
be5b3b37
MC
322
323 /* Send rest of message */
9caf9812 324 if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg + 5,
be5b3b37
MC
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 */
6084e04b 336 if (!TEST_true(SSL_handle_events(cssl)))
be5b3b37
MC
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 */
6084e04b 346 if (!TEST_true(SSL_handle_events(cssl)))
be5b3b37
MC
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:
c12e1113 365 qtest_fault_free(fault);
be5b3b37
MC
366 SSL_free(cssl);
367 ossl_quic_tserver_free(qtserv);
368 SSL_CTX_free(cctx);
369 return testresult;
71587f2b
MC
370}
371
adef87a2
MC
372OPT_TEST_DECLARE_USAGE("certsdir\n")
373
374int 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
adef87a2
MC
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);
71587f2b 395 ADD_TEST(test_unknown_frame);
d0123191 396 ADD_ALL_TESTS(test_drop_extensions, 2);
6a9ab9bc 397 ADD_ALL_TESTS(test_corrupted_data, 2);
adef87a2
MC
398
399 return 1;
400
401 err:
402 OPENSSL_free(cert);
403 OPENSSL_free(privkey);
404 return 0;
405}
406
407void cleanup_tests(void)
408{
409 OPENSSL_free(cert);
410 OPENSSL_free(privkey);
411}