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 */ 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 */ 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 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 */ 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 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 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 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 */ 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 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 407 void cleanup_tests(void) 408 { 409 OPENSSL_free(cert); 410 OPENSSL_free(privkey); 411 } 412