mirror of
https://github.com/guanzhi/GmSSL.git
synced 2026-08-06 11:53:39 +08:00
1271 lines
42 KiB
C
1271 lines
42 KiB
C
/*
|
|
* Copyright 2014-2026 The GmSSL Project. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
|
* not use this file except in compliance with the License.
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <gmssl/aes.h>
|
|
#include <gmssl/digest.h>
|
|
#include <gmssl/x509_cer.h>
|
|
#include <gmssl/quic.h>
|
|
#include <gmssl/tls.h>
|
|
#include <gmssl/socket.h>
|
|
#include <gmssl/error.h>
|
|
|
|
int tls13_generate_handshake_secrets(TLS_CONNECT *conn);
|
|
int tls13_generate_master_secret(TLS_CONNECT *conn);
|
|
int tls13_generate_client_handshake_keys(TLS_CONNECT *conn);
|
|
int tls13_generate_server_handshake_keys(TLS_CONNECT *conn);
|
|
int tls13_generate_application_secrets(TLS_CONNECT *conn);
|
|
int tls13_generate_client_application_keys(TLS_CONNECT *conn);
|
|
int tls13_generate_server_application_keys(TLS_CONNECT *conn);
|
|
int tls13_record_get_handshake_encrypted_extensions(const uint8_t *record, const uint8_t **exts, size_t *extslen);
|
|
|
|
static const char *options = "-host str [-port num] [-server_name str] [-cipher_suite str] [-supported_group str] [-sig_alg str] [-get path] [-verbose]";
|
|
|
|
static const char *help =
|
|
"Options\n"
|
|
"\n"
|
|
" -host str Server's hostname\n"
|
|
" -port num Server's UDP port number, default 443\n"
|
|
" -server_name str Send server_name (SNI) request\n"
|
|
" -cipher_suite str TLS 1.3 cipher suite, default TLS_AES_128_GCM_SHA256\n"
|
|
" -supported_group str Supported elliptic curve, default prime256v1\n"
|
|
" -sig_alg str Supported signature algorithm, default ecdsa_secp256r1_sha256\n"
|
|
" -get path HTTP/3 request path kept for future use\n"
|
|
" -verbose Print QUIC packet, frame and TLS handshake messages\n"
|
|
"\n"
|
|
#include "quic_help.h"
|
|
;
|
|
|
|
static int quic_tls_record_from_handshake(const uint8_t *handshake, size_t handshake_len, uint8_t *record, size_t *record_len)
|
|
{
|
|
if (!handshake || !record || !record_len || handshake_len > TLS_MAX_PLAINTEXT_SIZE) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
record[0] = TLS_record_handshake;
|
|
record[1] = 0x03;
|
|
record[2] = 0x03;
|
|
record[3] = (uint8_t)(handshake_len >> 8);
|
|
record[4] = (uint8_t)handshake_len;
|
|
memcpy(record + TLS_RECORD_HEADER_SIZE, handshake, handshake_len);
|
|
*record_len = TLS_RECORD_HEADER_SIZE + handshake_len;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_handshake_header_get(int *type, const uint8_t **data, size_t *datalen, const uint8_t *handshake, size_t handshake_len)
|
|
{
|
|
if (!type || !data || !datalen || !handshake || handshake_len < TLS_HANDSHAKE_HEADER_SIZE) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
*type = handshake[0];
|
|
*datalen = ((size_t)handshake[1] << 16) | ((size_t)handshake[2] << 8) | handshake[3];
|
|
if (*datalen + TLS_HANDSHAKE_HEADER_SIZE > handshake_len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
*data = handshake + TLS_HANDSHAKE_HEADER_SIZE;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_client_server_hello_process(TLS_CONNECT *conn, const uint8_t *client_hello, size_t client_hello_len, const uint8_t *server_hello, size_t server_hello_len)
|
|
{
|
|
uint8_t record[TLS_MAX_RECORD_SIZE];
|
|
size_t record_len;
|
|
int legacy_version;
|
|
const uint8_t *random;
|
|
const uint8_t *legacy_session_id_echo;
|
|
size_t legacy_session_id_echo_len;
|
|
int cipher_suite;
|
|
const uint8_t *exts;
|
|
size_t extslen;
|
|
const uint8_t *supported_versions = NULL;
|
|
size_t supported_versions_len = 0;
|
|
const uint8_t *key_share = NULL;
|
|
size_t key_share_len = 0;
|
|
int selected_version;
|
|
int key_exchange_group;
|
|
const uint8_t *key_exchange;
|
|
size_t key_exchange_len;
|
|
|
|
if (!conn || !client_hello || !client_hello_len || !server_hello || !server_hello_len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (quic_tls_record_from_handshake(server_hello, server_hello_len, record, &record_len) != 1
|
|
|| tls_record_get_handshake_server_hello(record, &legacy_version, &random,
|
|
&legacy_session_id_echo, &legacy_session_id_echo_len, &cipher_suite, &exts, &extslen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (legacy_version != TLS_protocol_tls12
|
|
|| legacy_session_id_echo_len != conn->session_id_len
|
|
|| memcmp(legacy_session_id_echo, conn->session_id, conn->session_id_len) != 0
|
|
|| tls_type_is_in_list(cipher_suite, conn->ctx->cipher_suites, conn->ctx->cipher_suites_cnt) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memcpy(conn->server_random, random, 32);
|
|
conn->cipher_suite = cipher_suite;
|
|
if (tls_cipher_suite_get(cipher_suite, &conn->cipher, &conn->digest) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
while (extslen) {
|
|
int ext_type;
|
|
const uint8_t *ext_data;
|
|
size_t ext_datalen;
|
|
if (tls_ext_from_bytes(&ext_type, &ext_data, &ext_datalen, &exts, &extslen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
switch (ext_type) {
|
|
case TLS_extension_supported_versions:
|
|
supported_versions = ext_data;
|
|
supported_versions_len = ext_datalen;
|
|
break;
|
|
case TLS_extension_key_share:
|
|
key_share = ext_data;
|
|
key_share_len = ext_datalen;
|
|
break;
|
|
default:
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
if (!supported_versions || !key_share
|
|
|| tls13_server_supported_versions_from_bytes(&selected_version, supported_versions, supported_versions_len) != 1
|
|
|| selected_version != TLS_protocol_tls13
|
|
|| tls13_key_share_server_hello_from_bytes(&key_exchange_group, &key_exchange, &key_exchange_len, key_share, key_share_len) != 1
|
|
|| key_exchange_len != 65) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
conn->protocol = selected_version;
|
|
conn->key_exchange_group = key_exchange_group;
|
|
memcpy(conn->peer_key_exchange, key_exchange, key_exchange_len);
|
|
conn->peer_key_exchange_len = key_exchange_len;
|
|
conn->key_exchange_modes &= TLS_KE_CERT_DHE;
|
|
if (!conn->key_exchange_modes
|
|
|| digest_init(&conn->dgst_ctx, conn->digest) != 1
|
|
|| digest_update(&conn->dgst_ctx, client_hello, client_hello_len) != 1
|
|
|| digest_update(&conn->dgst_ctx, server_hello, server_hello_len) != 1
|
|
|| tls13_generate_handshake_secrets(conn) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
tls13_generate_master_secret(conn);
|
|
if (tls13_generate_server_handshake_keys(conn) != 1
|
|
|| tls13_generate_client_handshake_keys(conn) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_client_handshake_flight_process(TLS_CONNECT *conn, const uint8_t *handshake, size_t handshake_len, int verbose)
|
|
{
|
|
const uint8_t *p = handshake;
|
|
size_t len = handshake_len;
|
|
uint8_t record[TLS_MAX_RECORD_SIZE];
|
|
size_t record_len;
|
|
int got_finished = 0;
|
|
|
|
if (!conn || !handshake) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
while (len) {
|
|
int type;
|
|
const uint8_t *data;
|
|
size_t datalen;
|
|
const uint8_t *msg = p;
|
|
size_t msglen;
|
|
|
|
if (quic_handshake_header_get(&type, &data, &datalen, p, len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
msglen = TLS_HANDSHAKE_HEADER_SIZE + datalen;
|
|
if (verbose) {
|
|
quic_crypto_data_print(stderr, 0, 4, QUIC_encryption_handshake, msg, msglen);
|
|
}
|
|
if (quic_tls_record_from_handshake(msg, msglen, record, &record_len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
switch (type) {
|
|
case TLS_handshake_encrypted_extensions:
|
|
if (tls13_record_get_handshake_encrypted_extensions(record, &data, &datalen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (digest_update(&conn->dgst_ctx, msg, msglen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
break;
|
|
case TLS_handshake_certificate: {
|
|
const uint8_t *request_context;
|
|
size_t request_context_len;
|
|
const uint8_t *ocsp;
|
|
size_t ocsp_len;
|
|
const uint8_t *sct;
|
|
size_t sct_len;
|
|
if (tls13_record_get_handshake_certificate(record, &request_context, &request_context_len,
|
|
conn->peer_cert_chain, &conn->peer_cert_chain_len, sizeof(conn->peer_cert_chain), &ocsp, &ocsp_len, &sct, &sct_len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (digest_update(&conn->dgst_ctx, msg, msglen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
case TLS_handshake_certificate_verify: {
|
|
int sig_alg;
|
|
const uint8_t *sig;
|
|
size_t siglen;
|
|
const uint8_t *cert;
|
|
size_t certlen;
|
|
X509_KEY public_key;
|
|
if (tls13_record_get_handshake_certificate_verify(record, &sig_alg, &sig, &siglen) != 1
|
|
|| x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 0, &cert, &certlen) != 1
|
|
|| x509_cert_get_subject_public_key(cert, certlen, &public_key) != 1
|
|
|| tls13_verify_certificate_verify(TLS_server_mode, sig_alg, &public_key, &conn->dgst_ctx, sig, siglen) != 1
|
|
|| digest_update(&conn->dgst_ctx, msg, msglen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
case TLS_handshake_finished: {
|
|
const uint8_t *server_verify_data;
|
|
size_t server_verify_data_len;
|
|
uint8_t verify_data[64];
|
|
size_t verify_data_len;
|
|
if (tls13_compute_verify_data(conn->server_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1
|
|
|| tls13_record_get_handshake_finished(record, &server_verify_data, &server_verify_data_len) != 1
|
|
|| server_verify_data_len != verify_data_len
|
|
|| memcmp(server_verify_data, verify_data, verify_data_len) != 0
|
|
|| digest_update(&conn->dgst_ctx, msg, msglen) != 1
|
|
|| tls13_generate_application_secrets(conn) != 1
|
|
|| tls13_generate_server_application_keys(conn) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
got_finished = 1;
|
|
break;
|
|
}
|
|
default:
|
|
error_print();
|
|
return -1;
|
|
}
|
|
p += msglen;
|
|
len -= msglen;
|
|
}
|
|
return got_finished ? 1 : 0;
|
|
}
|
|
|
|
static int quic_client_finished_to_bytes(TLS_CONNECT *conn, uint8_t *out, size_t *outlen)
|
|
{
|
|
uint8_t record[TLS_MAX_RECORD_SIZE];
|
|
size_t record_len = 0;
|
|
uint8_t verify_data[64];
|
|
size_t verify_data_len;
|
|
const uint8_t *handshake;
|
|
size_t handshake_len;
|
|
|
|
if (!conn || !out || !outlen) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memset(record, 0, sizeof(record));
|
|
record[1] = 0x03;
|
|
record[2] = 0x03;
|
|
if (tls13_compute_verify_data(conn->client_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1
|
|
|| tls13_record_set_handshake_finished(record, &record_len, verify_data, verify_data_len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
handshake = record + TLS_RECORD_HEADER_SIZE;
|
|
handshake_len = record_len - TLS_RECORD_HEADER_SIZE;
|
|
memcpy(out, handshake, handshake_len);
|
|
*outlen = handshake_len;
|
|
if (digest_update(&conn->dgst_ctx, handshake, handshake_len) != 1
|
|
|| tls13_generate_client_application_keys(conn) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_ack_frame_to_bytes(uint64_t packet_number, uint8_t **out, size_t *outlen)
|
|
{
|
|
if (quic_varint_to_bytes(QUIC_frame_ack, out, outlen) != 1
|
|
|| quic_varint_to_bytes(packet_number, out, outlen) != 1
|
|
|| quic_varint_to_bytes(0, out, outlen) != 1
|
|
|| quic_varint_to_bytes(0, out, outlen) != 1
|
|
|| quic_varint_to_bytes(0, out, outlen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_crypto_frame_to_bytes(uint64_t offset, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen)
|
|
{
|
|
if (!data && datalen) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (quic_varint_to_bytes(QUIC_frame_crypto, out, outlen) != 1
|
|
|| quic_varint_to_bytes(offset, out, outlen) != 1
|
|
|| quic_varint_to_bytes(datalen, out, outlen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (out && *out) {
|
|
memcpy(*out, data, datalen);
|
|
*out += datalen;
|
|
}
|
|
*outlen += datalen;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_client_initial_packet_to_bytes(const uint8_t *dcid, size_t dcid_len,
|
|
const uint8_t *scid, size_t scid_len, const uint8_t *frames, size_t frames_len,
|
|
uint8_t *packet, size_t *packet_len)
|
|
{
|
|
uint8_t *p = packet;
|
|
uint8_t *pn;
|
|
uint8_t *payload;
|
|
size_t len = 0;
|
|
size_t plain_payload_len;
|
|
size_t padding_len;
|
|
size_t payload_len;
|
|
QUIC_INITIAL_SECRETS secrets;
|
|
QUIC_INITIAL_KEYS keys;
|
|
AES_KEY aes_key;
|
|
AES_KEY hp_key;
|
|
uint8_t nonce[QUIC_INITIAL_IV_SIZE];
|
|
uint8_t mask[16];
|
|
uint64_t packet_number = 1;
|
|
size_t i;
|
|
|
|
if (!dcid || dcid_len > 20 || !scid || scid_len > 20 || !frames || !packet || !packet_len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
|
|
plain_payload_len = frames_len;
|
|
padding_len = plain_payload_len < 1162 ? 1162 - plain_payload_len : 0;
|
|
payload_len = plain_payload_len + padding_len + 16;
|
|
|
|
*p++ = 0xc3; len++;
|
|
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01; len += 4;
|
|
*p++ = (uint8_t)dcid_len; len++;
|
|
memcpy(p, dcid, dcid_len); p += dcid_len; len += dcid_len;
|
|
*p++ = (uint8_t)scid_len; len++;
|
|
memcpy(p, scid, scid_len); p += scid_len; len += scid_len;
|
|
if (quic_varint_to_bytes(0, &p, &len) != 1
|
|
|| quic_varint_to_bytes(4 + payload_len, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
pn = p;
|
|
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01; len += 4;
|
|
payload = p;
|
|
memcpy(payload, frames, frames_len);
|
|
memset(payload + frames_len, 0, padding_len);
|
|
|
|
if (quic_derive_initial_secrets(dcid, dcid_len, &secrets) != 1
|
|
|| quic_derive_initial_client_keys(&secrets, &keys) != 1
|
|
|| aes_set_encrypt_key(&aes_key, keys.key, sizeof(keys.key)) != 1
|
|
|| aes_set_encrypt_key(&hp_key, keys.hp, sizeof(keys.hp)) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memcpy(nonce, keys.iv, sizeof(nonce));
|
|
for (i = 0; i < 8; i++) {
|
|
nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i));
|
|
}
|
|
if (aes_gcm_encrypt(&aes_key, nonce, sizeof(nonce), packet, len,
|
|
payload, plain_payload_len + padding_len, payload, 16,
|
|
payload + plain_payload_len + padding_len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
aes_encrypt(&hp_key, payload, mask);
|
|
packet[0] ^= mask[0] & 0x0f;
|
|
for (i = 0; i < 4; i++) {
|
|
pn[i] ^= mask[i + 1];
|
|
}
|
|
p += payload_len;
|
|
len += payload_len;
|
|
|
|
*packet_len = len;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_stream_frame_to_bytes(uint64_t stream_id, int fin, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen)
|
|
{
|
|
uint64_t type = QUIC_frame_stream_base | 0x02 | (fin ? 0x01 : 0);
|
|
|
|
if ((!data && datalen) || quic_varint_to_bytes(type, out, outlen) != 1
|
|
|| quic_varint_to_bytes(stream_id, out, outlen) != 1
|
|
|| quic_varint_to_bytes(datalen, out, outlen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (out && *out) {
|
|
memcpy(*out, data, datalen);
|
|
*out += datalen;
|
|
}
|
|
*outlen += datalen;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_http3_varint_to_bytes(uint64_t val, uint8_t **out, size_t *outlen)
|
|
{
|
|
return quic_varint_to_bytes(val, out, outlen);
|
|
}
|
|
|
|
static int quic_qpack_string_to_bytes(const char *s, uint8_t **out, size_t *outlen)
|
|
{
|
|
size_t len;
|
|
|
|
if (!s || !outlen) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
len = strlen(s);
|
|
if (len > 127) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (out && *out) {
|
|
*(*out)++ = (uint8_t)len;
|
|
memcpy(*out, s, len);
|
|
*out += len;
|
|
}
|
|
*outlen += 1 + len;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_http3_request_headers_to_bytes(const char *authority, const char *path, uint8_t *out, size_t *outlen)
|
|
{
|
|
uint8_t block[512];
|
|
uint8_t *p = block;
|
|
size_t len = 0;
|
|
uint8_t *q = out;
|
|
|
|
if (!authority || !path || !out || !outlen) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
block[len++] = 0x00;
|
|
block[len++] = 0x00;
|
|
block[len++] = 0xd1; /* QPACK static table: :method: GET */
|
|
block[len++] = 0xd7; /* QPACK static table: :scheme: https */
|
|
if (!strcmp(path, "/")) {
|
|
block[len++] = 0xc1; /* QPACK static table: :path: / */
|
|
} else {
|
|
block[len++] = 0x51; /* literal with static name reference :path */
|
|
p = block + len;
|
|
if (quic_qpack_string_to_bytes(path, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
block[len++] = 0x50; /* literal with static name reference :authority */
|
|
p = block + len;
|
|
if (quic_qpack_string_to_bytes(authority, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (quic_http3_varint_to_bytes(0x01, &q, outlen) != 1
|
|
|| quic_http3_varint_to_bytes(len, &q, outlen) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memcpy(q, block, len);
|
|
*outlen += len;
|
|
return 1;
|
|
}
|
|
|
|
static int quic_http3_client_data_to_frames(const char *authority, const char *path, uint8_t *frames, size_t *frames_len)
|
|
{
|
|
uint8_t control_stream[] = { 0x00, 0x04, 0x00 };
|
|
uint8_t qpack_encoder_stream[] = { 0x02 };
|
|
uint8_t qpack_decoder_stream[] = { 0x03 };
|
|
uint8_t headers[512];
|
|
uint8_t *p = frames;
|
|
size_t headers_len = 0;
|
|
|
|
if (!authority || !path || !frames || !frames_len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
*frames_len = 0;
|
|
if (quic_http3_request_headers_to_bytes(authority, path, headers, &headers_len) != 1
|
|
|| quic_stream_frame_to_bytes(2, 0, control_stream, sizeof(control_stream), &p, frames_len) != 1
|
|
|| quic_stream_frame_to_bytes(6, 0, qpack_encoder_stream, sizeof(qpack_encoder_stream), &p, frames_len) != 1
|
|
|| quic_stream_frame_to_bytes(10, 0, qpack_decoder_stream, sizeof(qpack_decoder_stream), &p, frames_len) != 1
|
|
|| quic_stream_frame_to_bytes(0, 1, headers, headers_len, &p, frames_len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_http3_stream_data_print(const uint8_t *data, size_t datalen, int *got_data, int verbose)
|
|
{
|
|
const uint8_t *p = data;
|
|
size_t len = datalen;
|
|
|
|
if (!data || !got_data) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
while (len) {
|
|
uint64_t frame_type, frame_len;
|
|
const uint8_t *frame_data;
|
|
if (quic_varint_from_bytes(&frame_type, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&frame_len, &p, &len) != 1
|
|
|| frame_len > len) {
|
|
return 0;
|
|
}
|
|
frame_data = p;
|
|
p += frame_len;
|
|
len -= frame_len;
|
|
if (verbose) {
|
|
fprintf(stderr, "HTTP/3 frame type=%llu len=%llu\n",
|
|
(unsigned long long)frame_type, (unsigned long long)frame_len);
|
|
}
|
|
if (frame_type == 0x00 && frame_len) {
|
|
fwrite(frame_data, 1, (size_t)frame_len, stdout);
|
|
*got_data = 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_frames_crypto_collect(const uint8_t *frames, size_t frames_len, uint8_t *crypto, size_t *crypto_len, size_t crypto_max, uint64_t *largest_ack)
|
|
{
|
|
const uint8_t *p = frames;
|
|
size_t len = frames_len;
|
|
|
|
if (!frames || !crypto || !crypto_len || !largest_ack) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
while (len) {
|
|
uint64_t type;
|
|
if (quic_varint_from_bytes(&type, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
switch (type) {
|
|
case QUIC_frame_padding:
|
|
while (len && *p == 0) {
|
|
p++;
|
|
len--;
|
|
}
|
|
break;
|
|
case QUIC_frame_ack:
|
|
case QUIC_frame_ack_ecn: {
|
|
uint64_t ack_delay;
|
|
uint64_t ack_range_count;
|
|
uint64_t first_ack_range;
|
|
uint64_t i;
|
|
if (quic_varint_from_bytes(largest_ack, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ack_delay, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ack_range_count, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&first_ack_range, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
for (i = 0; i < ack_range_count; i++) {
|
|
uint64_t gap;
|
|
uint64_t ack_range;
|
|
if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
if (type == QUIC_frame_ack_ecn) {
|
|
uint64_t ect0, ect1, ce;
|
|
if (quic_varint_from_bytes(&ect0, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ect1, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ce, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case QUIC_frame_crypto: {
|
|
uint64_t off;
|
|
uint64_t data_len;
|
|
if (quic_varint_from_bytes(&off, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&data_len, &p, &len) != 1
|
|
|| data_len > len || off + data_len > crypto_max) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memcpy(crypto + off, p, (size_t)data_len);
|
|
if (*crypto_len < off + data_len) {
|
|
*crypto_len = (size_t)(off + data_len);
|
|
}
|
|
p += data_len;
|
|
len -= data_len;
|
|
break;
|
|
}
|
|
default:
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_application_frames_process(const uint8_t *frames, size_t frames_len, uint64_t *largest_ack, int *got_stream_data, int verbose)
|
|
{
|
|
const uint8_t *p = frames;
|
|
size_t len = frames_len;
|
|
|
|
if (!frames || !largest_ack || !got_stream_data) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
while (len) {
|
|
uint64_t type;
|
|
if (quic_varint_from_bytes(&type, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (type == QUIC_frame_padding) {
|
|
while (len && *p == 0) {
|
|
p++;
|
|
len--;
|
|
}
|
|
} else if (type == QUIC_frame_ack || type == QUIC_frame_ack_ecn) {
|
|
uint64_t ack_delay, ack_range_count, first_ack_range, i;
|
|
if (quic_varint_from_bytes(largest_ack, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ack_delay, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&ack_range_count, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&first_ack_range, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
for (i = 0; i < ack_range_count; i++) {
|
|
uint64_t gap, ack_range;
|
|
if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
if (type == QUIC_frame_ack_ecn) {
|
|
uint64_t ect0, ect1, ce;
|
|
if (quic_varint_from_bytes(&ect0, &p, &len) != 1 || quic_varint_from_bytes(&ect1, &p, &len) != 1 || quic_varint_from_bytes(&ce, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
} else if (type == QUIC_frame_handshake_done) {
|
|
if (verbose) {
|
|
fprintf(stderr, "recv HANDSHAKE_DONE\n");
|
|
}
|
|
} else if (type == QUIC_frame_new_token) {
|
|
uint64_t token_len;
|
|
if (quic_varint_from_bytes(&token_len, &p, &len) != 1 || token_len > len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
p += token_len;
|
|
len -= token_len;
|
|
} else if (type == QUIC_frame_crypto) {
|
|
uint64_t offset, data_len;
|
|
const uint8_t *data;
|
|
if (quic_varint_from_bytes(&offset, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&data_len, &p, &len) != 1
|
|
|| data_len > len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
data = p;
|
|
p += data_len;
|
|
len -= data_len;
|
|
if (verbose) {
|
|
fprintf(stderr, "recv application CRYPTO offset=%llu len=%llu\n",
|
|
(unsigned long long)offset, (unsigned long long)data_len);
|
|
quic_crypto_data_print(stderr, 0, 4, QUIC_encryption_application, data, (size_t)data_len);
|
|
}
|
|
} else if (type == QUIC_frame_new_connection_id) {
|
|
uint64_t seq, retire_prior_to, cid_len;
|
|
if (quic_varint_from_bytes(&seq, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&retire_prior_to, &p, &len) != 1
|
|
|| quic_varint_from_bytes(&cid_len, &p, &len) != 1
|
|
|| cid_len > 20 || cid_len + 16 > len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
p += cid_len + 16;
|
|
len -= cid_len + 16;
|
|
} else if ((type & 0xf8) == QUIC_frame_stream_base) {
|
|
uint64_t stream_id, offset = 0, data_len;
|
|
const uint8_t *data;
|
|
if (quic_varint_from_bytes(&stream_id, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
if (type & 0x04) {
|
|
if (quic_varint_from_bytes(&offset, &p, &len) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
if (type & 0x02) {
|
|
if (quic_varint_from_bytes(&data_len, &p, &len) != 1 || data_len > len) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
} else {
|
|
data_len = len;
|
|
}
|
|
data = p;
|
|
p += data_len;
|
|
len -= data_len;
|
|
if (verbose) {
|
|
fprintf(stderr, "recv STREAM id=%llu offset=%llu len=%llu fin=%d\n",
|
|
(unsigned long long)stream_id, (unsigned long long)offset, (unsigned long long)data_len, (int)(type & 0x01));
|
|
format_bytes(stderr, 0, 4, "stream_data", data, (size_t)data_len);
|
|
}
|
|
if (data_len && stream_id == 0) {
|
|
if (quic_http3_stream_data_print(data, (size_t)data_len, got_stream_data, verbose) != 1) {
|
|
fwrite(data, 1, (size_t)data_len, stdout);
|
|
*got_stream_data = 1;
|
|
}
|
|
}
|
|
} else if (type == QUIC_frame_connection_close || type == QUIC_frame_connection_close_app) {
|
|
uint64_t error_code, frame_type = 0, reason_len;
|
|
if (quic_varint_from_bytes(&error_code, &p, &len) != 1) return -1;
|
|
if (type == QUIC_frame_connection_close && quic_varint_from_bytes(&frame_type, &p, &len) != 1) return -1;
|
|
if (quic_varint_from_bytes(&reason_len, &p, &len) != 1 || reason_len > len) return -1;
|
|
if (verbose) {
|
|
fprintf(stderr, "recv CONNECTION_CLOSE error=%llu frame=%llu\n",
|
|
(unsigned long long)error_code, (unsigned long long)frame_type);
|
|
}
|
|
p += reason_len;
|
|
len -= reason_len;
|
|
} else {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quic_client_initial_packet_decrypt_and_print(FILE *fp, const uint8_t *original_dcid, size_t original_dcid_len,
|
|
const uint8_t *packet, size_t packet_len)
|
|
{
|
|
uint8_t buf[1500];
|
|
uint8_t plaintext[1500];
|
|
const uint8_t *p;
|
|
size_t len;
|
|
uint64_t long_packet_len;
|
|
uint8_t type;
|
|
uint8_t dcid_len;
|
|
uint8_t scid_len;
|
|
size_t pn_offset;
|
|
size_t pn_len;
|
|
uint64_t packet_number = 0;
|
|
QUIC_INITIAL_SECRETS secrets;
|
|
QUIC_INITIAL_KEYS keys;
|
|
AES_KEY aes_key;
|
|
AES_KEY hp_key;
|
|
uint8_t nonce[QUIC_INITIAL_IV_SIZE];
|
|
uint8_t mask[16];
|
|
size_t aad_len;
|
|
size_t ciphertext_len;
|
|
size_t i;
|
|
|
|
if (!fp || !original_dcid || !packet || packet_len > sizeof(buf) || packet_len < 7) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
memcpy(buf, packet, packet_len);
|
|
|
|
if (!(buf[0] & 0x80)) {
|
|
return 0;
|
|
}
|
|
type = (buf[0] >> 4) & 0x03;
|
|
if (type != QUIC_packet_initial) {
|
|
return 0;
|
|
}
|
|
|
|
p = buf + 5;
|
|
len = packet_len - 5;
|
|
if (!len) return -1;
|
|
dcid_len = *p++;
|
|
len--;
|
|
if (dcid_len > len) return -1;
|
|
p += dcid_len;
|
|
len -= dcid_len;
|
|
if (!len) return -1;
|
|
scid_len = *p++;
|
|
len--;
|
|
if (scid_len > len) return -1;
|
|
p += scid_len;
|
|
len -= scid_len;
|
|
if (quic_varint_from_bytes(&long_packet_len, &p, &len) != 1) return -1; /* token length */
|
|
if (long_packet_len > len) return -1;
|
|
p += long_packet_len;
|
|
len -= long_packet_len;
|
|
if (quic_varint_from_bytes(&long_packet_len, &p, &len) != 1) return -1; /* packet length */
|
|
if (long_packet_len > len) return -1;
|
|
pn_offset = (size_t)(p - buf);
|
|
if (pn_offset + 4 + 16 > packet_len) return -1;
|
|
|
|
if (quic_derive_initial_secrets(original_dcid, original_dcid_len, &secrets) != 1
|
|
|| quic_derive_initial_server_keys(&secrets, &keys) != 1
|
|
|| aes_set_encrypt_key(&aes_key, keys.key, sizeof(keys.key)) != 1
|
|
|| aes_set_encrypt_key(&hp_key, keys.hp, sizeof(keys.hp)) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
aes_encrypt(&hp_key, buf + pn_offset + 4, mask);
|
|
buf[0] ^= mask[0] & 0x0f;
|
|
pn_len = (buf[0] & 0x03) + 1;
|
|
if (pn_len > long_packet_len) return -1;
|
|
for (i = 0; i < pn_len; i++) {
|
|
buf[pn_offset + i] ^= mask[i + 1];
|
|
packet_number = (packet_number << 8) | buf[pn_offset + i];
|
|
}
|
|
|
|
memcpy(nonce, keys.iv, sizeof(nonce));
|
|
for (i = 0; i < 8; i++) {
|
|
nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i));
|
|
}
|
|
aad_len = pn_offset + pn_len;
|
|
ciphertext_len = (size_t)long_packet_len - pn_len;
|
|
if (ciphertext_len < 16
|
|
|| aes_gcm_decrypt(&aes_key, nonce, sizeof(nonce), buf, aad_len,
|
|
buf + aad_len, ciphertext_len - 16, buf + aad_len + ciphertext_len - 16, 16, plaintext) != 1) {
|
|
error_print();
|
|
return -1;
|
|
}
|
|
|
|
format_print(fp, 0, 0, "Decrypted Initial Packet\n");
|
|
format_print(fp, 0, 4, "Packet Number: %llu\n", (unsigned long long)packet_number);
|
|
quic_frames_print(fp, 0, 4, QUIC_encryption_initial, plaintext, ciphertext_len - 16);
|
|
return 1;
|
|
}
|
|
|
|
static int quic_client_probe(const char *prog, const char *host, int port,
|
|
const char *server_name, int cipher_suite, int supported_group, int sig_alg, const char *request_path, int verbose)
|
|
{
|
|
int ret = -1;
|
|
TLS_CTX ctx;
|
|
TLS_CONNECT conn;
|
|
char *alpn = "h3";
|
|
uint8_t handshake[TLS_MAX_RECORD_SIZE];
|
|
uint8_t frames[TLS_MAX_RECORD_SIZE];
|
|
uint8_t packet[1500];
|
|
uint8_t response[4096];
|
|
uint8_t dcid[8] = { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 };
|
|
uint8_t scid[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
|
|
QUIC_TRANSPORT_PARAMS params;
|
|
uint8_t *p = handshake;
|
|
uint8_t *q = frames;
|
|
size_t handshake_len = 0;
|
|
size_t frames_len = 0;
|
|
size_t packet_len = 0;
|
|
struct sockaddr_in server;
|
|
tls_socket_t sock = tls_socket_invalid();
|
|
fd_set rfds;
|
|
struct timeval timeout;
|
|
tls_ret_t n;
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
memset(&conn, 0, sizeof(conn));
|
|
|
|
if (tls_ctx_init(&ctx, TLS_protocol_tls13, TLS_client_mode) != 1
|
|
|| tls_ctx_set_cipher_suites(&ctx, &cipher_suite, 1) != 1
|
|
|| tls_ctx_set_supported_groups(&ctx, &supported_group, 1) != 1
|
|
|| tls_ctx_set_signature_algorithms(&ctx, &sig_alg, 1) != 1
|
|
|| tls_ctx_set_application_layer_protocol_negotiation(&ctx, &alpn, 1) != 1
|
|
|| tls_init(&conn, &ctx) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose && tls_ctx_set_verbose(&ctx, TLS_verbose) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (tls_set_hostname(&conn, server_name ? server_name : host) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (server_name && tls_set_server_name(&conn) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
conn.verbose = verbose ? TLS_verbose : 0;
|
|
|
|
quic_transport_params_init(¶ms);
|
|
if (quic_transport_params_add(¶ms, QUIC_transport_param_initial_source_connection_id, scid, sizeof(scid)) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_max_udp_payload_size, 1200) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_data, 1048576) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_bidi_local, 65536) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_bidi_remote, 65536) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_uni, 65536) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_bidi, 16) != 1
|
|
|| quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_uni, 3) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (quic_client_hello_to_bytes_ex(&conn, ¶ms, &p, &handshake_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (quic_varint_to_bytes(QUIC_frame_crypto, &q, &frames_len) != 1
|
|
|| quic_varint_to_bytes(0, &q, &frames_len) != 1
|
|
|| quic_varint_to_bytes(handshake_len, &q, &frames_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
memcpy(q, handshake, handshake_len);
|
|
q += handshake_len;
|
|
frames_len += handshake_len;
|
|
|
|
if (verbose) {
|
|
quic_frames_print(stderr, 0, 0, QUIC_encryption_initial, frames, frames_len);
|
|
}
|
|
if (quic_client_initial_packet_to_bytes(dcid, sizeof(dcid), scid, sizeof(scid),
|
|
frames, frames_len, packet, &packet_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
quic_packet_print(stderr, 0, 0, packet, packet_len);
|
|
}
|
|
|
|
if (tls_socket_lib_init() != 1
|
|
|| tls_socket_get_addr(host, port, &server) != 1
|
|
|| tls_socket_create(&sock, AF_INET, SOCK_DGRAM, 0) != 1
|
|
|| tls_socket_connect(sock, &server) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
n = tls_socket_send(sock, packet, packet_len, 0);
|
|
if (n != (tls_ret_t)packet_len) {
|
|
fprintf(stderr, "%s: send QUIC Initial probe failed\n", prog);
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
fprintf(stderr, "send QUIC Initial probe: %zu bytes\n", packet_len);
|
|
}
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(sock, &rfds);
|
|
timeout.tv_sec = 2;
|
|
timeout.tv_usec = 0;
|
|
if (select((int)(sock + 1), &rfds, NULL, NULL, &timeout) > 0) {
|
|
n = tls_socket_recv(sock, response, sizeof(response), 0);
|
|
if (n > 0) {
|
|
QUIC_INITIAL_SECRETS initial_secrets;
|
|
QUIC_INITIAL_KEYS client_initial_keys;
|
|
QUIC_INITIAL_KEYS server_initial_keys;
|
|
QUIC_PACKET_KEYS client_initial_packet_keys;
|
|
QUIC_PACKET_KEYS initial_server_packet_keys;
|
|
QUIC_PACKET_KEYS server_handshake_packet_keys;
|
|
QUIC_PACKET_KEYS client_handshake_packet_keys;
|
|
QUIC_PACKET_KEYS server_application_packet_keys;
|
|
QUIC_PACKET_KEYS client_application_packet_keys;
|
|
QUIC_DECRYPTED_PACKET initial_packet;
|
|
QUIC_DECRYPTED_PACKET handshake_packet;
|
|
QUIC_DECRYPTED_PACKET application_packet;
|
|
uint8_t initial_crypto[TLS_MAX_RECORD_SIZE];
|
|
uint8_t handshake_crypto[TLS_MAX_RECORD_SIZE];
|
|
uint8_t client_finished[TLS_MAX_RECORD_SIZE];
|
|
uint8_t request_frames[TLS_MAX_RECORD_SIZE];
|
|
uint8_t send_frames[TLS_MAX_RECORD_SIZE];
|
|
uint8_t send_packet[1500];
|
|
uint8_t *r = send_frames;
|
|
size_t initial_crypto_len = 0;
|
|
size_t handshake_crypto_len = 0;
|
|
size_t client_finished_len = 0;
|
|
size_t request_frames_len = 0;
|
|
size_t send_frames_len = 0;
|
|
size_t send_packet_len = 0;
|
|
size_t response_len = (size_t)n;
|
|
size_t off = 0;
|
|
uint64_t largest_ack = 0;
|
|
uint64_t server_initial_packet_number = 0;
|
|
uint64_t server_handshake_packet_number = 0;
|
|
uint64_t client_application_packet_number = 0;
|
|
int got_stream_data = 0;
|
|
int loop_count;
|
|
if (verbose) {
|
|
fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n);
|
|
quic_packet_print(stderr, 0, 0, response, (size_t)n);
|
|
}
|
|
if (quic_derive_initial_secrets(dcid, sizeof(dcid), &initial_secrets) != 1
|
|
|| quic_derive_initial_client_keys(&initial_secrets, &client_initial_keys) != 1
|
|
|| quic_derive_initial_server_keys(&initial_secrets, &server_initial_keys) != 1
|
|
|| quic_packet_keys_from_initial(&client_initial_keys, &client_initial_packet_keys) != 1
|
|
|| quic_packet_keys_from_initial(&server_initial_keys, &initial_server_packet_keys) != 1
|
|
|| quic_long_packet_decrypt(&initial_server_packet_keys, response, response_len, QUIC_packet_initial, &initial_packet) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "Decrypted Initial Packet\n");
|
|
format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)initial_packet.packet_number);
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_initial, initial_packet.plaintext, initial_packet.plaintext_len);
|
|
}
|
|
server_initial_packet_number = initial_packet.packet_number;
|
|
if (quic_frames_crypto_collect(initial_packet.plaintext, initial_packet.plaintext_len,
|
|
initial_crypto, &initial_crypto_len, sizeof(initial_crypto), &largest_ack) != 1
|
|
|| quic_client_server_hello_process(&conn, handshake, handshake_len, initial_crypto, initial_crypto_len) != 1
|
|
|| quic_packet_keys_derive(conn.digest, conn.server_handshake_traffic_secret, conn.cipher_suite, &server_handshake_packet_keys) != 1
|
|
|| quic_packet_keys_derive(conn.digest, conn.client_handshake_traffic_secret, conn.cipher_suite, &client_handshake_packet_keys) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
off = initial_packet.packet_len;
|
|
if (off >= response_len
|
|
|| quic_long_packet_decrypt(&server_handshake_packet_keys, response + off, response_len - off, QUIC_packet_handshake, &handshake_packet) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "Decrypted Handshake Packet\n");
|
|
format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)handshake_packet.packet_number);
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_handshake, handshake_packet.plaintext, handshake_packet.plaintext_len);
|
|
}
|
|
server_handshake_packet_number = handshake_packet.packet_number;
|
|
largest_ack = 0;
|
|
if (quic_frames_crypto_collect(handshake_packet.plaintext, handshake_packet.plaintext_len,
|
|
handshake_crypto, &handshake_crypto_len, sizeof(handshake_crypto), &largest_ack) != 1
|
|
|| quic_client_handshake_flight_process(&conn, handshake_crypto, handshake_crypto_len, verbose) != 1
|
|
|| quic_client_finished_to_bytes(&conn, client_finished, &client_finished_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (quic_ack_frame_to_bytes(server_handshake_packet_number, &r, &send_frames_len) != 1
|
|
|| quic_crypto_frame_to_bytes(0, client_finished, client_finished_len, &r, &send_frames_len) != 1
|
|
|| quic_long_packet_encrypt(&client_handshake_packet_keys, QUIC_packet_handshake,
|
|
initial_packet.scid, initial_packet.scid_len, scid, sizeof(scid), 1,
|
|
send_frames, send_frames_len, send_packet, &send_packet_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "send QUIC Handshake Packet\n");
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_handshake, send_frames, send_frames_len);
|
|
quic_packet_print(stderr, 0, 4, send_packet, send_packet_len);
|
|
}
|
|
n = tls_socket_send(sock, send_packet, send_packet_len, 0);
|
|
if (n != (tls_ret_t)send_packet_len) {
|
|
fprintf(stderr, "%s: send QUIC Handshake Finished failed\n", prog);
|
|
goto end;
|
|
}
|
|
r = send_frames;
|
|
send_frames_len = 0;
|
|
if (quic_ack_frame_to_bytes(server_initial_packet_number, &r, &send_frames_len) != 1
|
|
|| quic_long_packet_encrypt(&client_initial_packet_keys, QUIC_packet_initial,
|
|
initial_packet.scid, initial_packet.scid_len, scid, sizeof(scid), 2,
|
|
send_frames, send_frames_len, send_packet, &send_packet_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
n = tls_socket_send(sock, send_packet, send_packet_len, 0);
|
|
if (n != (tls_ret_t)send_packet_len) {
|
|
fprintf(stderr, "%s: send QUIC Initial ACK failed\n", prog);
|
|
goto end;
|
|
}
|
|
if (quic_packet_keys_derive(conn.digest, conn.server_application_traffic_secret, conn.cipher_suite, &server_application_packet_keys) != 1
|
|
|| quic_packet_keys_derive(conn.digest, conn.client_application_traffic_secret, conn.cipher_suite, &client_application_packet_keys) != 1
|
|
|| quic_http3_client_data_to_frames(server_name ? server_name : host, request_path ? request_path : "/", request_frames, &request_frames_len) != 1
|
|
|| quic_short_packet_encrypt(&client_application_packet_keys, initial_packet.scid, initial_packet.scid_len,
|
|
client_application_packet_number++, request_frames, request_frames_len, send_packet, &send_packet_len) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "send QUIC 1-RTT HTTP/3 Request\n");
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_application, request_frames, request_frames_len);
|
|
quic_packet_print(stderr, 0, 4, send_packet, send_packet_len);
|
|
}
|
|
n = tls_socket_send(sock, send_packet, send_packet_len, 0);
|
|
if (n != (tls_ret_t)send_packet_len) {
|
|
fprintf(stderr, "%s: send QUIC 1-RTT request failed\n", prog);
|
|
goto end;
|
|
}
|
|
off += handshake_packet.packet_len;
|
|
if (off < response_len
|
|
&& quic_short_packet_decrypt(&server_application_packet_keys, response + off, response_len - off, scid, sizeof(scid), &application_packet) == 1) {
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "Decrypted 1-RTT Packet\n");
|
|
format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)application_packet.packet_number);
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_application, application_packet.plaintext, application_packet.plaintext_len);
|
|
}
|
|
if (quic_application_frames_process(application_packet.plaintext, application_packet.plaintext_len, &largest_ack, &got_stream_data, verbose) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
}
|
|
for (loop_count = 0; loop_count < 8 && !got_stream_data; loop_count++) {
|
|
size_t datagram_off;
|
|
FD_ZERO(&rfds);
|
|
FD_SET(sock, &rfds);
|
|
timeout.tv_sec = 2;
|
|
timeout.tv_usec = 0;
|
|
if (select((int)(sock + 1), &rfds, NULL, NULL, &timeout) <= 0) {
|
|
break;
|
|
}
|
|
n = tls_socket_recv(sock, response, sizeof(response), 0);
|
|
if (n <= 0) {
|
|
break;
|
|
}
|
|
if (verbose) {
|
|
fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n);
|
|
quic_packet_print(stderr, 0, 0, response, (size_t)n);
|
|
}
|
|
for (datagram_off = 0; datagram_off < (size_t)n && !got_stream_data; ) {
|
|
size_t current_packet_len;
|
|
if (quic_packet_total_length(response + datagram_off, (size_t)n - datagram_off, ¤t_packet_len) != 1 || !current_packet_len) {
|
|
break;
|
|
}
|
|
if (response[datagram_off] & 0x80) {
|
|
datagram_off += current_packet_len;
|
|
continue;
|
|
}
|
|
if (quic_short_packet_decrypt(&server_application_packet_keys, response + datagram_off, current_packet_len, scid, sizeof(scid), &application_packet) != 1) {
|
|
break;
|
|
}
|
|
if (verbose) {
|
|
format_print(stderr, 0, 0, "Decrypted 1-RTT Packet\n");
|
|
format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)application_packet.packet_number);
|
|
quic_frames_print(stderr, 0, 4, QUIC_encryption_application, application_packet.plaintext, application_packet.plaintext_len);
|
|
}
|
|
largest_ack = 0;
|
|
if (quic_application_frames_process(application_packet.plaintext, application_packet.plaintext_len, &largest_ack, &got_stream_data, verbose) != 1) {
|
|
error_print();
|
|
goto end;
|
|
}
|
|
r = send_frames;
|
|
send_frames_len = 0;
|
|
if (quic_ack_frame_to_bytes(application_packet.packet_number, &r, &send_frames_len) == 1
|
|
&& quic_short_packet_encrypt(&client_application_packet_keys, initial_packet.scid, initial_packet.scid_len,
|
|
client_application_packet_number++, send_frames, send_frames_len, send_packet, &send_packet_len) == 1) {
|
|
tls_socket_send(sock, send_packet, send_packet_len, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (verbose) {
|
|
fprintf(stderr, "QUIC TLS handshake completed\n");
|
|
}
|
|
ret = got_stream_data ? 1 : 0;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "%s: no response from server\n", prog);
|
|
ret = 0;
|
|
}
|
|
|
|
end:
|
|
if (tls_socket_is_valid(sock)) tls_socket_close(sock);
|
|
tls_socket_lib_cleanup();
|
|
tls_ctx_cleanup(&ctx);
|
|
return ret;
|
|
}
|
|
|
|
int quic_client_main(int argc, char **argv)
|
|
{
|
|
char *prog = argv[0];
|
|
char *host = NULL;
|
|
char *server_name = NULL;
|
|
char *get = NULL;
|
|
int cipher_suite = TLS_cipher_aes_128_gcm_sha256;
|
|
int supported_group = TLS_curve_secp256r1;
|
|
int sig_alg = TLS_sig_ecdsa_secp256r1_sha256;
|
|
int port = 443;
|
|
int verbose = 0;
|
|
int ret;
|
|
|
|
argc--;
|
|
argv++;
|
|
if (argc < 1) {
|
|
fprintf(stderr, "usage: %s %s\n", prog, options);
|
|
return 1;
|
|
}
|
|
while (argc > 0) {
|
|
if (!strcmp(*argv, "-help")) {
|
|
printf("usage: %s %s\n", prog, options);
|
|
printf("%s\n", help);
|
|
return 0;
|
|
} else if (!strcmp(*argv, "-host")) {
|
|
if (--argc < 1) goto bad;
|
|
host = *(++argv);
|
|
} else if (!strcmp(*argv, "-port")) {
|
|
if (--argc < 1) goto bad;
|
|
port = atoi(*(++argv));
|
|
} else if (!strcmp(*argv, "-server_name")) {
|
|
if (--argc < 1) goto bad;
|
|
server_name = *(++argv);
|
|
} else if (!strcmp(*argv, "-cipher_suite")) {
|
|
if (--argc < 1) goto bad;
|
|
if ((cipher_suite = tls_cipher_suite_from_name(*(++argv))) == 0) {
|
|
fprintf(stderr, "%s: invalid cipher suite '%s'\n", prog, *argv);
|
|
return 1;
|
|
}
|
|
} else if (!strcmp(*argv, "-supported_group")) {
|
|
if (--argc < 1) goto bad;
|
|
if ((supported_group = tls_named_curve_from_name(*(++argv))) == 0) {
|
|
fprintf(stderr, "%s: invalid supported group '%s'\n", prog, *argv);
|
|
return 1;
|
|
}
|
|
} else if (!strcmp(*argv, "-sig_alg")) {
|
|
if (--argc < 1) goto bad;
|
|
if ((sig_alg = tls_signature_scheme_from_name(*(++argv))) == 0) {
|
|
fprintf(stderr, "%s: invalid signature algorithm '%s'\n", prog, *argv);
|
|
return 1;
|
|
}
|
|
} else if (!strcmp(*argv, "-get")) {
|
|
if (--argc < 1) goto bad;
|
|
get = *(++argv);
|
|
(void)get;
|
|
} else if (!strcmp(*argv, "-verbose")) {
|
|
verbose = 1;
|
|
} else {
|
|
fprintf(stderr, "%s: invalid option '%s'\n", prog, *argv);
|
|
return 1;
|
|
bad:
|
|
fprintf(stderr, "%s: option '%s' argument required\n", prog, *argv);
|
|
return 1;
|
|
}
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (!host) {
|
|
fprintf(stderr, "%s: '-host' option required\n", prog);
|
|
return 1;
|
|
}
|
|
|
|
ret = quic_client_probe(prog, host, port, server_name, cipher_suite, supported_group, sig_alg, get, verbose);
|
|
return ret == 1 ? 0 : 1;
|
|
}
|