Skip to content

Commit 8fab2f9

Browse files
authored
[+] Handling retry packet (#186)
Fix #58 Fix #174
1 parent b0b4d98 commit 8fab2f9

20 files changed

+558
-66
lines changed

src/tls/xqc_crypto.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,30 @@ xqc_crypto_discard_old_keys(xqc_crypto_t *crypto)
672672
xqc_ckm_free(&crypto->keys.rx_ckm[discard_key_phase]);
673673
xqc_ckm_free(&crypto->keys.tx_ckm[discard_key_phase]);
674674
}
675+
676+
xqc_int_t
677+
xqc_crypto_aead_encrypt(xqc_crypto_t *crypto,
678+
const uint8_t *plaintext, size_t plaintextlen,
679+
const uint8_t *key, size_t keylen,
680+
const uint8_t *nonce, size_t noncelen,
681+
const uint8_t *ad, size_t adlen,
682+
uint8_t *dst, size_t dst_cap, size_t *dst_len)
683+
{
684+
xqc_int_t ret;
685+
xqc_pkt_protect_aead_t *pp_aead = &crypto->pp_aead;
686+
687+
/* do aead encryption */
688+
ret = pp_aead->encrypt(pp_aead, dst, dst_cap, dst_len,
689+
plaintext, plaintextlen,
690+
key, keylen,
691+
nonce, noncelen,
692+
ad, adlen);
693+
694+
if (ret != XQC_OK) {
695+
xqc_log(crypto->log, XQC_LOG_ERROR,
696+
"|encrypt packet error|ret:%d|nwrite:%z|", ret, *dst_len);
697+
return -XQC_TLS_ENCRYPT_DATA_ERROR;
698+
}
699+
700+
return XQC_OK;
701+
}

src/tls/xqc_crypto.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,15 @@ xqc_int_t xqc_crypto_derive_updated_keys(xqc_crypto_t *crypto, xqc_key_type_t ty
259259
void xqc_crypto_discard_old_keys(xqc_crypto_t *crypto);
260260

261261

262+
/**
263+
* @brief call aead encrypt callback directly
264+
*/
265+
xqc_int_t xqc_crypto_aead_encrypt(xqc_crypto_t *crypto,
266+
const uint8_t *plaintext, size_t plaintextlen,
267+
const uint8_t *key, size_t keylen,
268+
const uint8_t *nonce, size_t noncelen,
269+
const uint8_t *ad, size_t adlen,
270+
uint8_t *dst, size_t dst_cap, size_t *dst_len);
271+
272+
262273
#endif

src/tls/xqc_tls.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,33 @@ xqc_tls_discard_old_1rtt_keys(xqc_tls_t *tls)
762762
xqc_crypto_discard_old_keys(tls->crypto[XQC_ENC_LEV_1RTT]);
763763
}
764764

765+
xqc_int_t
766+
xqc_tls_cal_retry_integrity_tag(xqc_tls_t *tls,
767+
uint8_t *retry_pseudo_packet, size_t retry_pseudo_packet_len,
768+
uint8_t *dst, size_t dst_cap, size_t *dst_len)
769+
{
770+
xqc_int_t ret = XQC_OK;
771+
772+
xqc_crypto_t *crypto = xqc_crypto_create(XQC_TLS13_AES_128_GCM_SHA256, tls->log);
773+
if (crypto == NULL) {
774+
xqc_log(tls->log, XQC_LOG_ERROR, "|create retry crypto error|");
775+
return -XQC_TLS_NOMEM;
776+
}
777+
778+
xqc_proto_version_t ver = tls->version;
779+
ret = xqc_crypto_aead_encrypt(crypto, "", 0,
780+
xqc_crypto_retry_key[ver], strlen(xqc_crypto_retry_key[ver]),
781+
xqc_crypto_retry_nonce[ver], strlen(xqc_crypto_retry_nonce[ver]),
782+
retry_pseudo_packet, retry_pseudo_packet_len,
783+
dst, dst_cap, dst_len);
784+
if (ret != XQC_OK) {
785+
xqc_log(tls->log, XQC_LOG_ERROR, "|calculate retry integrity tag error|");
786+
}
787+
788+
xqc_crypto_destroy(crypto);
789+
return ret;
790+
}
791+
765792

766793
/**
767794
* ============================================================================

src/tls/xqc_tls.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,11 @@ xqc_int_t xqc_tls_update_1rtt_keys(xqc_tls_t *tls, xqc_key_type_t type);
199199
*/
200200
void xqc_tls_discard_old_1rtt_keys(xqc_tls_t *tls);
201201

202+
/**
203+
* @brief encrypt retry pseudo-packet to calculate retry integrity tag
204+
*/
205+
xqc_int_t xqc_tls_cal_retry_integrity_tag(xqc_tls_t *tls,
206+
uint8_t *retry_pseudo_packet, size_t retry_pseudo_packet_len,
207+
uint8_t *dst, size_t dst_cap, size_t *dst_len);
208+
202209
#endif

src/tls/xqc_tls_common.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,42 @@ static const char * const (xqc_crypto_initial_salt)[] = {
5050
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
5151
};
5252

53+
static const char * const (xqc_crypto_retry_key)[] = {
54+
/* placeholder */
55+
[XQC_IDRAFT_INIT_VER] =
56+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
57+
58+
/* QUIC v1 */
59+
[XQC_VERSION_V1] =
60+
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e",
61+
62+
/* draft-29 ~ draft-32 */
63+
[XQC_IDRAFT_VER_29] =
64+
"\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1",
65+
66+
/* version negotiation */
67+
[XQC_IDRAFT_VER_NEGOTIATION] =
68+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
69+
};
70+
71+
static const char * const (xqc_crypto_retry_nonce)[] = {
72+
/* placeholder */
73+
[XQC_IDRAFT_INIT_VER] =
74+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
75+
76+
/* QUIC v1 */
77+
[XQC_VERSION_V1] =
78+
"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb",
79+
80+
/* draft-29 ~ draft-32 */
81+
[XQC_IDRAFT_VER_29] =
82+
"\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c",
83+
84+
/* version negotiation */
85+
[XQC_IDRAFT_VER_NEGOTIATION] =
86+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
87+
};
88+
5389

5490
typedef struct xqc_ssl_session_ticket_key_s {
5591
size_t size;

src/transport/xqc_conn.c

Lines changed: 139 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = {
8989
[XQC_CONN_FLAG_0RTT_OK_SHIFT] = "0RTT_OK",
9090
[XQC_CONN_FLAG_0RTT_REJ_SHIFT] = "0RTT_REJECT",
9191
[XQC_CONN_FLAG_UPPER_CONN_EXIST_SHIFT] = "UPPER_CONN_EXIST",
92-
[XQC_CONN_FLAG_SVR_INIT_RECVD_SHIFT] = "INIT_RECVD",
92+
[XQC_CONN_FLAG_INIT_RECVD_SHIFT] = "INIT_RECVD",
9393
[XQC_CONN_FLAG_NEED_RUN_SHIFT] = "NEED_RUN",
9494
[XQC_CONN_FLAG_PING_SHIFT] = "PING",
9595
[XQC_CONN_FLAG_HSK_ACKED_SHIFT] = "HSK_ACKED",
@@ -102,7 +102,7 @@ static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = {
102102
[XQC_CONN_FLAG_ADDR_VALIDATED_SHIFT] = "ADDR_VALIDATED",
103103
[XQC_CONN_FLAG_NEW_CID_RECEIVED_SHIFT] = "NEW_CID_RECEIVED",
104104
[XQC_CONN_FLAG_LINGER_CLOSING_SHIFT] = "LINGER_CLOSING",
105-
[XQC_CONN_FLAG_RECV_RETRY_SHIFT] = "RETRY_RCVD",
105+
[XQC_CONN_FLAG_RETRY_RECVD_SHIFT] = "RETRY_RECVD",
106106
[XQC_CONN_FLAG_TLS_HSK_COMPLETED_SHIFT] = "TLS_HSK_CMPTD"
107107
};
108108

@@ -330,7 +330,6 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid,
330330
xqc_init_list_head(&xc->initial_crypto_data_list);
331331
xqc_init_list_head(&xc->hsk_crypto_data_list);
332332
xqc_init_list_head(&xc->application_crypto_data_list);
333-
xqc_init_list_head(&xc->retry_crypto_data_buffer);
334333
for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT; encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++) {
335334
xqc_init_list_head(&xc->undecrypt_packet_in[encrypt_level]);
336335
}
@@ -1600,7 +1599,7 @@ xqc_conn_immediate_close(xqc_connection_t *conn)
16001599
return XQC_OK;
16011600
}
16021601

1603-
if (!(conn->conn_flag & XQC_CONN_FLAG_SVR_INIT_RECVD)
1602+
if (!(conn->conn_flag & XQC_CONN_FLAG_INIT_RECVD)
16041603
&& conn->conn_type == XQC_CONN_TYPE_SERVER)
16051604
{
16061605
conn->conn_state = XQC_CONN_STATE_CLOSED;
@@ -2857,36 +2856,141 @@ xqc_conn_has_hsk_keys(xqc_connection_t *c)
28572856
&& xqc_tls_is_key_ready(c->tls, XQC_ENC_LEV_HSK, XQC_KEY_TYPE_RX_READ);
28582857
}
28592858

2860-
xqc_int_t
2861-
xqc_conn_on_recv_retry(xqc_connection_t *conn)
2859+
2860+
static xqc_bool_t
2861+
xqc_need_reassemble_packet(xqc_packet_out_t *packet_out)
28622862
{
2863-
/* only client will receive retry packet, and can't receive */
2864-
if ((conn->conn_type != XQC_CONN_TYPE_CLIENT)
2865-
|| (conn->conn_flag & XQC_CONN_FLAG_RECV_RETRY))
2863+
if (packet_out->po_pkt.pkt_type == XQC_PTYPE_INIT
2864+
&& packet_out->po_frame_types & XQC_FRAME_BIT_CRYPTO)
28662865
{
2867-
xqc_log(conn->log, XQC_LOG_ERROR,
2868-
"|server recv retry or client recv retry two or more times|");
2869-
XQC_CONN_ERR(conn, TRA_PROTOCOL_VIOLATION);
2870-
return -XQC_EPROTO;
2866+
return XQC_TRUE;
2867+
2868+
} else if (packet_out->po_pkt.pkt_type == XQC_PTYPE_0RTT) {
2869+
return XQC_TRUE;
2870+
}
2871+
2872+
return XQC_FALSE;
2873+
}
2874+
2875+
static xqc_int_t
2876+
xqc_conn_reassemble_packet(xqc_connection_t *conn, xqc_packet_out_t *ori_po)
2877+
{
2878+
xqc_packet_out_t *new_po = xqc_write_new_packet(conn, ori_po->po_pkt.pkt_type);
2879+
if (new_po == NULL) {
2880+
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|");
2881+
return -XQC_EWRITE_PKT;
2882+
}
2883+
2884+
/* copy frame without padding */
2885+
unsigned int ori_payload_len = 0;
2886+
if (xqc_need_padding(conn, ori_po) && ori_po->po_padding != NULL) {
2887+
ori_payload_len = ori_po->po_padding - ori_po->po_payload;
2888+
2889+
} else {
2890+
ori_payload_len = ori_po->po_used_size - (ori_po->po_payload - ori_po->po_buf);
2891+
}
2892+
2893+
new_po->po_payload = new_po->po_buf + new_po->po_used_size;
2894+
memcpy(new_po->po_payload, ori_po->po_payload, ori_payload_len);
2895+
new_po->po_used_size += ori_payload_len;
2896+
2897+
/* copy packet_out info */
2898+
new_po->po_frame_types = ori_po->po_frame_types;
2899+
for (int i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) {
2900+
new_po->po_stream_frames[i] = ori_po->po_stream_frames[i];
2901+
}
2902+
2903+
/* set RESEND flag */
2904+
new_po->po_flag |= XQC_POF_RESEND;
2905+
2906+
if (new_po->po_frame_types & XQC_FRAME_BIT_CRYPTO) {
2907+
xqc_send_ctl_move_to_high_pri(&new_po->po_list, conn->conn_send_ctl);
2908+
}
2909+
2910+
xqc_log(conn->log, XQC_LOG_DEBUG,
2911+
"|pkt_num:%ui|ptype:%d|frames:%s|",
2912+
new_po->po_pkt.pkt_num, new_po->po_pkt.pkt_type,
2913+
xqc_frame_type_2_str(new_po->po_frame_types));
2914+
2915+
return XQC_OK;
2916+
}
2917+
2918+
static xqc_int_t
2919+
xqc_conn_resend_packets(xqc_connection_t *conn)
2920+
{
2921+
/*
2922+
* Generate new header and reassemble packet for Initial and 0-RTT packets
2923+
* that need to be resent, and drop all old packets with the original header.
2924+
*
2925+
* TODO: Refactoring packet generation: generate packet header before sent.
2926+
* Then we don't have to reassemble the packets.
2927+
*/
2928+
2929+
xqc_int_t ret;
2930+
xqc_send_ctl_t *ctl = conn->conn_send_ctl;
2931+
2932+
xqc_list_head_t *pos, *next;
2933+
xqc_packet_out_t *packet_out;
2934+
2935+
xqc_list_for_each_safe(pos, next, &ctl->ctl_send_packets) {
2936+
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
2937+
2938+
if (packet_out->po_flag & XQC_POF_RESEND) {
2939+
continue;
2940+
}
2941+
2942+
/* reassemble new packet with updated header and insert to send queue */
2943+
if (xqc_need_reassemble_packet(packet_out)) {
2944+
ret = xqc_conn_reassemble_packet(conn, packet_out);
2945+
if (ret != XQC_OK) {
2946+
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_reassemble_packet error|ret:%d|", ret);
2947+
return ret;
2948+
}
2949+
}
2950+
2951+
/* drop old packet */
2952+
xqc_send_ctl_remove_send(pos);
2953+
xqc_send_ctl_insert_free(pos, &ctl->ctl_free_packets, ctl);
28712954
}
28722955

2873-
/* reset connection state */
2874-
conn->conn_state = XQC_CONN_STATE_CLIENT_INIT;
2875-
conn->conn_flag |= XQC_CONN_FLAG_RECV_RETRY;
2956+
return XQC_OK;
2957+
}
2958+
2959+
xqc_int_t
2960+
xqc_conn_on_recv_retry(xqc_connection_t *conn, xqc_cid_t *retry_scid)
2961+
{
2962+
xqc_int_t ret;
2963+
2964+
conn->conn_flag |= XQC_CONN_FLAG_RETRY_RECVD;
2965+
2966+
/* change the DCID it uses for sending packets in response to Retry packet. */
2967+
xqc_cid_copy(&conn->dcid_set.current_dcid, retry_scid);
28762968

28772969
/* reset initial keys */
2878-
xqc_int_t ret = xqc_tls_reset_initial(conn->tls, conn->version, &conn->original_dcid);
2970+
ret = xqc_tls_reset_initial(conn->tls, conn->version, retry_scid);
28792971
if (ret != XQC_OK) {
2972+
xqc_log(conn->log, XQC_LOG_ERROR,
2973+
"|xqc_tls_reset_initial error|retry_scid:%s|ret:%d|",
2974+
xqc_scid_str(retry_scid), ret);
28802975
return ret;
28812976
}
28822977

2883-
/* Copy ClientHello to buffer list */
2884-
xqc_list_head_t *head = &conn->initial_crypto_data_list;
2885-
xqc_list_head_t *pos, *next;
2978+
/*
2979+
* clients that receive a Retry packet reset congestion control and loss
2980+
* recovery state, including resetting any pending timers.
2981+
*/
2982+
xqc_send_ctl_reset(conn->conn_send_ctl);
28862983

2887-
xqc_list_for_each_safe(pos, next, head) {
2888-
xqc_list_del(pos);
2889-
xqc_list_add_tail(pos, &conn->retry_crypto_data_buffer);
2984+
/*
2985+
* client responds to a Retry packet with an Initial packet that includes
2986+
* the provided Retry token to continue connection establishment.
2987+
* client SHOULD attempt to resend data in 0-RTT packets after it sends a
2988+
* new Initial packet.
2989+
*/
2990+
ret = xqc_conn_resend_packets(conn);
2991+
if (ret != XQC_OK) {
2992+
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_resend_packets error|ret:%d|", ret);
2993+
return ret;
28902994
}
28912995

28922996
return XQC_OK;
@@ -3027,6 +3131,18 @@ xqc_conn_check_transport_params(xqc_connection_t *conn, const xqc_transport_para
30273131
}
30283132
}
30293133

3134+
if (conn->conn_type == XQC_CONN_TYPE_CLIENT) {
3135+
/* check retry_source_connection_id parameter if recv retry packet */
3136+
if (conn->conn_flag & XQC_CONN_FLAG_RETRY_RECVD) {
3137+
if (!params->retry_source_connection_id_present) {
3138+
return -XQC_TLS_TRANSPORT_PARAM;
3139+
}
3140+
3141+
} else if (params->retry_source_connection_id_present) {
3142+
return -XQC_TLS_TRANSPORT_PARAM;
3143+
}
3144+
}
3145+
30303146
return XQC_OK;
30313147
}
30323148

@@ -3277,7 +3393,6 @@ xqc_conn_tls_handshake_completed_cb(void *user_data)
32773393

32783394
conn->conn_flag |= XQC_CONN_FLAG_TLS_HSK_COMPLETED;
32793395
conn->handshake_complete_time = xqc_monotonic_timestamp();
3280-
xqc_free_crypto_buffer_list(&conn->retry_crypto_data_buffer);
32813396
}
32823397

32833398
const xqc_tls_callbacks_t xqc_conn_tls_cbs = {

0 commit comments

Comments
 (0)