]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | */ | |
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 | ||
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 | 70 | static 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 | ||
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; | |
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 | 164 | static 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 | 177 | static 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 | */ |
228 | static int docorrupt = 0; | |
229 | ||
c12e1113 | 230 | static 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 | 242 | static 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 | */ | |
260 | static 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 |
372 | OPT_TEST_DECLARE_USAGE("certsdir\n") |
373 | ||
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 | ||
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 | ||
407 | void cleanup_tests(void) | |
408 | { | |
409 | OPENSSL_free(cert); | |
410 | OPENSSL_free(privkey); | |
411 | } |