Line data Source code
1 : #ifndef HEADER_fd_src_waltz_quic_fd_quic_retry_h
2 : #define HEADER_fd_src_waltz_quic_fd_quic_retry_h
3 :
4 : #include "fd_quic_conn_id.h"
5 : #include "fd_quic_enum.h"
6 : #include "fd_quic_proto_structs.h"
7 : #include "crypto/fd_quic_crypto_suites.h"
8 : #include "../../ballet/aes/fd_aes_gcm.h"
9 :
10 : /* fd_quic_retry.h contains APIs for
11 : - the QUIC v1 Retry mechanism (RFC 9000)
12 : - the QUIC-TLS v1 Retry Integrity Tag (RFC 9001)
13 : - the fd_quic retry token scheme (loosely based on draft-ietf-quic-
14 : retry-offload-00 but incompatible) */
15 :
16 : /* Retry Integrity Tag ************************************************/
17 :
18 : /* The retry integrity tag is the 16-byte tag output of AES-128-GCM */
19 : #define FD_QUIC_RETRY_INTEGRITY_TAG_SZ FD_QUIC_CRYPTO_TAG_SZ
20 6002763 : #define FD_QUIC_RETRY_INTEGRITY_TAG_KEY ((uchar *)"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e")
21 6002763 : #define FD_QUIC_RETRY_INTEGRITY_TAG_NONCE ((uchar *)"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb")
22 :
23 : FD_PROTOTYPES_BEGIN
24 :
25 : /* fd_quic_retry_integrity_tag_{sign,verify} implement the RFC 9001
26 : "Retry Integrity Tag" AEAD scheme.
27 :
28 : This is a standard and mandatory step in the QUIC retry process, both
29 : on the server (sign) and client (verify) side. Confusingly, all
30 : inputs to these functions are either public constants (e.g. the
31 : hardcoded encryption key) or sent in plain text over the wire. Thus,
32 : the "retry_integrity_tag" is more like a hash function than a MAC and
33 : the retry_pseudo_pkt is just obfuscated, but not securely encrypted.
34 :
35 : Failure to generate a correct integrity tag as part of the retry
36 : handshake is considered a protocol error that typically results in
37 : connection termination.
38 :
39 : fd_quic_retry_integrity_tag_sign creates a MAC over the byte range at
40 : retry_pseudo_pkt and writes it into retry_integrity_tag. It is
41 : infallible.
42 :
43 : fd_quic_retry_integrity_tag_decrypt checks whether a Retry Integrity
44 : Tag matches the byte range at retry_pseudo_pkt. It returns
45 : FD_QUIC_SUCCESS if the integrity tag is valid, and FD_QUIC_FAILURE
46 : otherwise. */
47 :
48 : static inline void
49 : fd_quic_retry_integrity_tag_sign(
50 : fd_aes_gcm_t * aes_gcm,
51 : uchar const * retry_pseudo_pkt,
52 : ulong retry_pseudo_pkt_len,
53 : uchar retry_integrity_tag[static FD_QUIC_RETRY_INTEGRITY_TAG_SZ]
54 3000024 : ) {
55 3000024 : fd_aes_128_gcm_init( aes_gcm, FD_QUIC_RETRY_INTEGRITY_TAG_KEY, FD_QUIC_RETRY_INTEGRITY_TAG_NONCE );
56 3000024 : fd_aes_gcm_encrypt( aes_gcm, NULL, NULL, 0UL, retry_pseudo_pkt, retry_pseudo_pkt_len, retry_integrity_tag );
57 3000024 : }
58 :
59 : static inline int
60 : fd_quic_retry_integrity_tag_verify(
61 : fd_aes_gcm_t * aes_gcm,
62 : uchar const * retry_pseudo_pkt,
63 : ulong retry_pseudo_pkt_len,
64 : uchar const retry_integrity_tag[static FD_QUIC_RETRY_INTEGRITY_TAG_SZ]
65 3002739 : ) {
66 3002739 : fd_aes_128_gcm_init( aes_gcm, FD_QUIC_RETRY_INTEGRITY_TAG_KEY, FD_QUIC_RETRY_INTEGRITY_TAG_NONCE );
67 3002739 : int ok = fd_aes_gcm_decrypt( aes_gcm, NULL, NULL, 0UL, retry_pseudo_pkt, retry_pseudo_pkt_len, retry_integrity_tag );
68 3002739 : return ok ? FD_QUIC_SUCCESS : FD_QUIC_FAILED;
69 3002739 : }
70 :
71 : FD_PROTOTYPES_END
72 :
73 : /* fd_quic retry token (non-standard) **********************************
74 :
75 : The QUIC Retry mechanism as specified in RFC 9000 does not
76 : authenticate retry packets. To safely and statelessly handle retries
77 : in fd_quic, we need to authenticate the token itself. A construction
78 : similar to a HMAC scheme is used, but using the OTM in AES-GCM.
79 : Although AES-GCM is not the ideal algorithm for the job, it was
80 : chosen because it's common throughout QUIC v1, and also quite fast.
81 :
82 : Security Note: This scheme relies on a 128-bit auth key and 96-bit
83 : unique nonces. The encryption key is sourced from CSPRNG on startup
84 : and stays secret. Nonces only have to be unique, not unguessable,
85 : but if the same 96-bit nonce is ever generated twice the retry token
86 : authentication mechanism breaks down entirely (AES-GCM IV reuse).
87 : Callers therefore source them from fd_quic_rng_ulong. */
88 :
89 : /* fd_quic_retry_data_t encodes data within the QUIC Retry token.
90 : It contains claims about the client. */
91 :
92 : struct __attribute__((packed)) fd_quic_retry_data {
93 : /* 0x00 */ ushort magic;
94 3000021 : # define FD_QUIC_RETRY_TOKEN_MAGIC 0xdaa5
95 : /* 0x02 */ uchar token_id[12]; /* pseudorandom, guessable */
96 : /* 0x0e */ uchar ip6_addr[16]; /* Source IPv6 or IPv4-mapped IPv6 address, net order */
97 : /* 0x1e */ ushort udp_port; /* Source UDP port, host order */
98 : /* 0x20 */ ulong expire_comp; /* unix_nanos>>22 */
99 : /* 0x28 */ ulong rscid; /* Retry Source Connection ID */
100 : /* 0x30 */ uchar odcid[20]; /* Original Destination Connection ID */
101 : /* 0x44 */ uchar odcid_sz; /* in [1,20] */
102 : /* 0x45 */
103 : };
104 :
105 : typedef struct fd_quic_retry_data fd_quic_retry_data_t;
106 :
107 : /* fd_quic_retry_token_t encodes the QUIC Retry token itself. */
108 :
109 : struct fd_quic_retry_token {
110 : union {
111 : fd_quic_retry_data_t data;
112 : uchar data_opaque[ sizeof(fd_quic_retry_data_t) ];
113 : };
114 : uchar mac_tag[ FD_AES_GCM_TAG_SZ ];
115 : };
116 :
117 : typedef struct fd_quic_retry_token fd_quic_retry_token_t;
118 :
119 : FD_PROTOTYPES_BEGIN
120 :
121 : /* fd_quic_retry_data_new initializes fd_quic_retry_data_t with a
122 : randomly generated 96-bit nonce. */
123 :
124 : static inline fd_quic_retry_data_t *
125 : fd_quic_retry_data_new( fd_quic_retry_data_t * data,
126 : ulong nonce0, /* rand */
127 3000021 : ulong nonce1 ) { /* rand */
128 3000021 : memset( data, 0, sizeof(fd_quic_retry_data_t) );
129 3000021 : data->magic = FD_QUIC_RETRY_TOKEN_MAGIC;
130 3000021 : FD_STORE( ulong, data->token_id + 0, nonce0 );
131 3000021 : FD_STORE( uint, data->token_id + 8, (uint)nonce1 );
132 3000021 : return data;
133 3000021 : }
134 :
135 : /* fd_quic_retry_data_set_ip4 sets the IP address of the token payload
136 : to an IPv4-mapped IPv6 address. ip4_addr is in big endian order. */
137 :
138 : static inline fd_quic_retry_data_t *
139 : fd_quic_retry_data_set_ip4( fd_quic_retry_data_t * data,
140 3000021 : uint ip4_addr ) {
141 3000021 : memset( data->ip6_addr, 0x00, 10 );
142 3000021 : memset( data->ip6_addr + 10, 0xFF, 2 );
143 3000021 : FD_STORE( uint, data->ip6_addr + 12, ip4_addr );
144 3000021 : return data;
145 3000021 : }
146 :
147 : /* fd_quic_retry_token_sign creates mac_tag using the AEAD instance in
148 : aes_gcm and the associated data in token->data.
149 :
150 : WARNING: The same token->data->token_id value may not be reused
151 : across two sign function calls. */
152 :
153 : static inline void
154 : fd_quic_retry_token_sign( fd_quic_retry_token_t * token,
155 : fd_aes_gcm_t * aes_gcm,
156 : uchar const * aes_key,
157 3000021 : uchar const * aes_iv ) {
158 3000021 : uchar iv[12];
159 39000273 : for( ulong j=0; j<12; j++ ) iv[j] = (uchar)( aes_iv[j] ^ token->data.token_id[j] );
160 3000021 : fd_aes_128_gcm_init( aes_gcm, aes_key, iv );
161 :
162 3000021 : void const * aad = token->data_opaque;
163 3000021 : ulong aad_sz = sizeof(fd_quic_retry_data_t);
164 3000021 : fd_aes_gcm_encrypt( aes_gcm, NULL, NULL, 0UL, aad, aad_sz, token->mac_tag );
165 3000021 : }
166 :
167 : /* fd_quic_retry_token_verify checks if token->mac_tag is valid given
168 : AEAD params and associated data in token->data. Does not validate
169 : the content of token->data.
170 : Returns FD_QUIC_SUCCESS if valid, otherwise FD_QUIC_FAILED. */
171 :
172 : static inline int
173 : fd_quic_retry_token_verify( fd_quic_retry_token_t const * token,
174 : fd_aes_gcm_t * aes_gcm,
175 : uchar const * aes_key,
176 3002067 : uchar const * aes_iv ) {
177 3002067 : uchar iv[12];
178 39026871 : for( ulong j=0; j<12; j++ ) iv[j] = (uchar)( aes_iv[j] ^ token->data.token_id[j] );
179 3002067 : fd_aes_128_gcm_init( aes_gcm, aes_key, iv );
180 :
181 3002067 : void const * aad = token->data_opaque;
182 3002067 : ulong aad_sz = sizeof(fd_quic_retry_data_t);
183 3002067 : int ok = fd_aes_gcm_decrypt( aes_gcm, NULL, NULL, 0UL, aad, aad_sz, token->mac_tag );
184 3002067 : return ok ? FD_QUIC_SUCCESS : FD_QUIC_FAILED;
185 3002067 : }
186 :
187 : FD_PROTOTYPES_END
188 :
189 : /* Retry Packets ******************************************************/
190 :
191 : FD_PROTOTYPES_BEGIN
192 :
193 : /* FD_QUIC_RETRY_LOCAL_SZ is the encoded size of Retry packets generated
194 : by fd_quic. (Other QUIC implementations may produce differently
195 : sized retry packets) */
196 :
197 6000042 : #define FD_QUIC_RETRY_LOCAL_SZ (148UL)
198 :
199 : /* fd_quic_retry_{create,verify} do end-to-end issuance and verification
200 : of fd_quic retry tokens. Used by the server-side.
201 :
202 : orig_dst_conn_id is the DCID chosen by the client in the Initial that
203 : triggered a Retry. retry_src_conn_id is the SCID chosen by the server
204 : in the Retry packet. nonce0 and nonce1 are the two halves of the
205 : token's single 96-bit AES-GCM nonce. */
206 :
207 : ulong
208 : fd_quic_retry_create(
209 : uchar retry[FD_QUIC_RETRY_LOCAL_SZ], /* out */
210 : fd_quic_pkt_t const * pkt,
211 : ulong nonce0, /* rand */
212 : ulong nonce1, /* rand */
213 : uchar const retry_secret[ FD_QUIC_RETRY_SECRET_SZ ],
214 : uchar const retry_iv[ FD_QUIC_RETRY_IV_SZ ],
215 : fd_quic_conn_id_t const * orig_dst_conn_id,
216 : fd_quic_conn_id_t const * src_conn_id,
217 : ulong retry_src_conn_id,
218 : long expire_at
219 : );
220 :
221 : int
222 : fd_quic_retry_server_verify(
223 : fd_quic_pkt_t const * pkt,
224 : fd_quic_initial_t const * initial,
225 : fd_quic_conn_id_t * orig_dst_conn_id, /* out */
226 : ulong * retry_src_conn_id, /* out */
227 : uchar const retry_secret[ FD_QUIC_RETRY_SECRET_SZ ],
228 : uchar const retry_iv[ FD_QUIC_RETRY_IV_SZ ],
229 : long now,
230 : long ttl
231 : );
232 :
233 : int
234 : fd_quic_retry_client_verify(
235 : uchar const * const retry_ptr,
236 : ulong const retry_sz,
237 : fd_quic_conn_id_t const * orig_dst_conn_id,
238 : fd_quic_conn_id_t * src_conn_id, /* out */
239 : uchar const ** token,
240 : ulong * token_sz
241 : );
242 :
243 : FD_PROTOTYPES_END
244 :
245 : #endif /* HEADER_fd_src_waltz_quic_fd_quic_retry_h */
|