Line data Source code
1 : #include "fd_quic_tls.h"
2 : #include "../../../ballet/ed25519/fd_x25519.h"
3 : #include "../../../ballet/x509/fd_x509_mock.h"
4 :
5 : #include <errno.h>
6 : #include <stdlib.h>
7 : #include <string.h>
8 : #include <sys/uio.h>
9 :
10 : /* fd_tls callbacks provided by fd_quic *******************************/
11 :
12 : /* fd_quic_tls_sendmsg is called by fd_tls when fd_quic should send a
13 : CRYPTO frame to the peer. Currently, we can assume that the
14 : encryption_level will never decrease (INITIAL => HANDSHAKE => APP) */
15 :
16 : int
17 : fd_quic_tls_sendmsg( void const * handshake,
18 : void const * record,
19 : ulong record_sz,
20 : uint encryption_level,
21 : int flush );
22 :
23 : /* fd_quic_tls_secrets is called by fd_tls when new encryption keys
24 : become available. Currently, this is called at most two times per
25 : connection: For the handshake secrets, and for the initial app-level
26 : secrets. */
27 :
28 : void
29 : fd_quic_tls_secrets( void const * handshake,
30 : void const * recv_secret,
31 : void const * send_secret,
32 : uint encryption_level );
33 :
34 : /* fd_quic_tls_rand is the RNG provided to fd_tls. Note: This is
35 : a layering violation ... The user should pass the CSPRNG handle to
36 : both fd_quic and fd_tls. Currently, implemented via the getrandom()
37 : syscall ... Inefficient! */
38 :
39 : void *
40 : fd_quic_tls_rand( void * ctx,
41 : void * buf,
42 : ulong bufsz );
43 :
44 : /* fd_quic_tls_tp_self is called by fd_tls to retrieve fd_quic's QUIC
45 : transport parameters. */
46 :
47 : ulong
48 : fd_quic_tls_tp_self( void * handshake,
49 : uchar * quic_tp,
50 : ulong quic_tp_bufsz );
51 :
52 : /* fd_quic_tls_tp_self is called by fd_tls to inform fd_quic of the
53 : peer's QUIC transport parameters. */
54 :
55 : void
56 : fd_quic_tls_tp_peer( void * handshake,
57 : uchar const * quic_tp,
58 : ulong quic_tp_sz );
59 :
60 : /* fd_quic_tls lifecycle API ******************************************/
61 :
62 : static void
63 : fd_quic_tls_init( fd_tls_t * tls,
64 : fd_tls_sign_t signer,
65 : uchar const cert_public_key[ static 32 ] );
66 :
67 : fd_quic_tls_t *
68 : fd_quic_tls_new( fd_quic_tls_t * self,
69 3381 : fd_quic_tls_cfg_t * cfg ) {
70 :
71 3381 : if( FD_UNLIKELY( !self ) ) {
72 0 : FD_LOG_WARNING(( "NULL mem" ));
73 0 : return NULL;
74 0 : }
75 3381 : if( FD_UNLIKELY( !cfg ) ) {
76 0 : FD_LOG_WARNING(( "NULL cfg" ));
77 0 : return NULL;
78 0 : }
79 3381 : if( FD_UNLIKELY( (!cfg->secret_cb ) |
80 3381 : (!cfg->handshake_complete_cb) |
81 3381 : (!cfg->peer_params_cb ) ) ) {
82 0 : FD_LOG_WARNING(( "Missing callbacks" ));
83 0 : return NULL;
84 0 : }
85 :
86 3381 : self->secret_cb = cfg->secret_cb;
87 3381 : self->handshake_complete_cb = cfg->handshake_complete_cb;
88 3381 : self->peer_params_cb = cfg->peer_params_cb;
89 :
90 : /* Initialize fd_tls */
91 3381 : fd_quic_tls_init( &self->tls, cfg->signer, cfg->cert_public_key );
92 :
93 3381 : return self;
94 3381 : }
95 :
96 : /* fd_quic_tls_init is called as part of fd_quic_tls_new. It sets up
97 : the embedded fd_tls instance. */
98 :
99 : static void
100 : fd_quic_tls_init( fd_tls_t * tls,
101 : fd_tls_sign_t signer,
102 3381 : uchar const cert_public_key[ static 32 ] ) {
103 3381 : tls = fd_tls_new( tls );
104 3381 : *tls = (fd_tls_t) {
105 3381 : .quic = 1,
106 3381 : .rand = {
107 3381 : .ctx = NULL,
108 3381 : .rand_fn = fd_quic_tls_rand
109 3381 : },
110 3381 : .sign = signer,
111 3381 : .secrets_fn = fd_quic_tls_secrets,
112 3381 : .sendmsg_fn = fd_quic_tls_sendmsg,
113 :
114 3381 : .quic_tp_self_fn = fd_quic_tls_tp_self,
115 3381 : .quic_tp_peer_fn = fd_quic_tls_tp_peer,
116 3381 : };
117 :
118 : /* Generate X25519 key */
119 3381 : if( FD_UNLIKELY( !fd_rng_secure( tls->kex_private_key, 32UL ) ) )
120 0 : FD_LOG_ERR(( "fd_rng_secure failed: %s", fd_io_strerror( errno ) ));
121 3381 : fd_x25519_public( tls->kex_public_key, tls->kex_private_key );
122 :
123 : /* Set up Ed25519 key */
124 3381 : fd_memcpy( tls->cert_public_key, cert_public_key, 32UL );
125 :
126 : /* Generate X.509 cert */
127 3381 : fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
128 3381 : tls->cert_x509_sz = FD_X509_MOCK_CERT_SZ;
129 :
130 : /* Set ALPN protocol ID
131 : (Technically, don't need to copy the length prefix but we'll do
132 : so anyways.) */
133 3381 : tls->alpn[ 0 ] = 0x0a;
134 3381 : memcpy( tls->alpn+1, "solana-tpu", 11UL );
135 3381 : tls->alpn_sz = 11UL;
136 3381 : }
137 :
138 : void *
139 3327 : fd_quic_tls_delete( fd_quic_tls_t * self ) {
140 3327 : if( FD_UNLIKELY( !self ) ) {
141 0 : FD_LOG_WARNING(( "NULL self" ));
142 0 : return NULL;
143 0 : }
144 3327 : return self;
145 3327 : }
146 :
147 : fd_quic_tls_hs_t *
148 : fd_quic_tls_hs_new( fd_quic_tls_hs_t * self,
149 : fd_quic_tls_t * quic_tls,
150 : void * context,
151 : int is_server,
152 : fd_quic_transport_params_t const * self_transport_params,
153 12171 : ulong now ) {
154 : // clear the handshake bits
155 12171 : fd_memset( self, 0, sizeof(fd_quic_tls_hs_t) );
156 :
157 : // set properties on self
158 12171 : self->quic_tls = quic_tls;
159 12171 : self->is_server = is_server;
160 12171 : self->context = context;
161 :
162 : /* initialize handshake data */
163 :
164 : /* init free list */
165 12171 : self->hs_data_free_idx = 0u; /* head points at first */
166 206907 : for( ushort j = 0u; j < FD_QUIC_TLS_HS_DATA_CNT; ++j ) {
167 194736 : if( j < FD_QUIC_TLS_HS_DATA_CNT-1u ) {
168 182565 : self->hs_data[j].next_idx = (ushort)(j+1u); /* each point to next */
169 182565 : } else {
170 12171 : self->hs_data[j].next_idx = FD_QUIC_TLS_HS_DATA_UNUSED ;
171 12171 : }
172 194736 : }
173 :
174 : /* no data pending */
175 60855 : for( unsigned j = 0; j < 4; ++j ) {
176 48684 : self->hs_data_pend_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
177 48684 : self->hs_data_pend_end_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
178 48684 : }
179 :
180 : /* clear hs_data_buf */
181 12171 : self->hs_data_buf_ptr = 0;
182 :
183 : /* all handshake offsets start at zero */
184 12171 : fd_memset( self->hs_data_offset, 0, sizeof( self->hs_data_offset ) );
185 :
186 : /* Set QUIC transport params */
187 12171 : self->self_transport_params = *self_transport_params;
188 :
189 12171 : if( is_server ) {
190 6111 : fd_tls_estate_srv_new( &self->hs.srv );
191 6111 : } else {
192 6060 : fd_tls_estate_cli_new( &self->hs.cli );
193 6060 : long res = fd_tls_client_handshake( &quic_tls->tls, &self->hs.cli, NULL, 0UL, 0 );
194 6060 : if( FD_UNLIKELY( res<0L ) ) {
195 0 : self->alert = (uint)-res;
196 0 : }
197 6060 : }
198 :
199 12171 : self->birthtime = now;
200 :
201 12171 : return self;
202 12171 : }
203 :
204 : void
205 12171 : fd_quic_tls_hs_delete( fd_quic_tls_hs_t * self ) {
206 12171 : if( !self ) return;
207 :
208 12171 : if( self->is_server )
209 6111 : fd_tls_estate_srv_delete( &self->hs.srv );
210 6060 : else
211 6060 : fd_tls_estate_cli_delete( &self->hs.cli );
212 12171 : }
213 :
214 : int
215 42192 : fd_quic_tls_process( fd_quic_tls_hs_t * self ) {
216 :
217 42192 : if( FD_UNLIKELY( self->hs.base.state==FD_TLS_HS_FAIL ) ) return FD_QUIC_FAILED;
218 42192 : if( self->hs.base.state==FD_TLS_HS_CONNECTED ) return FD_QUIC_SUCCESS;
219 :
220 : /* Process all fully received messages */
221 :
222 42192 : uint enc_level = self->rx_enc_level;
223 84381 : for(;;) {
224 84381 : uchar const * buf = self->rx_hs_buf;
225 84381 : ulong off = self->rx_off;
226 84381 : ulong avail = self->rx_sz - off;
227 84381 : if( avail<4 ) break;
228 :
229 : /* Peek the message size from fd_tls_msg_hdr_t
230 : ?? AA BB CC => 0xCCBBAA?? => 0x??AABBCC => 0x00AABBCC */
231 42192 : uint msg_sz = fd_uint_bswap( FD_LOAD( uint, buf+off ) ) & 0xFFFFFFU;
232 42192 : if( avail<msg_sz+4 ) break;
233 :
234 42192 : long res = fd_tls_handshake( &self->quic_tls->tls, &self->hs, buf+off, avail, enc_level );
235 :
236 42192 : if( FD_UNLIKELY( res<0L ) ) {
237 3 : int alert = (int)-res;
238 3 : self->alert = (uint)alert;
239 3 : return FD_QUIC_FAILED;
240 3 : }
241 42189 : if( FD_UNLIKELY( res==0UL ) ) {
242 0 : FD_LOG_WARNING(( "preventing deadlock" ));
243 0 : return FD_QUIC_FAILED;
244 0 : }
245 :
246 42189 : self->rx_off = (ushort)( off+(ulong)res );
247 42189 : }
248 :
249 42189 : switch( self->hs.base.state ) {
250 12054 : case FD_TLS_HS_CONNECTED:
251 : /* handshake completed */
252 12054 : self->quic_tls->handshake_complete_cb( self, self->context );
253 12054 : return FD_QUIC_SUCCESS;
254 0 : case FD_TLS_HS_FAIL:
255 : /* handshake permanently failed */
256 0 : return FD_QUIC_FAILED;
257 30135 : default:
258 : /* handshake not yet complete */
259 30135 : return FD_QUIC_SUCCESS;
260 42189 : }
261 42189 : }
262 :
263 : /* internal callbacks */
264 :
265 : int
266 : fd_quic_tls_sendmsg( void const * handshake,
267 : void const * data,
268 : ulong data_sz,
269 : uint enc_level,
270 42222 : int flush FD_PARAM_UNUSED ) {
271 :
272 42222 : uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
273 42222 : if( data_sz > buf_sz ) {
274 0 : return 0;
275 0 : }
276 :
277 : /* Safe because the fd_tls_estate_{srv,cli}_t object is the first
278 : element of fd_quic_tls_hs_t */
279 42222 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
280 :
281 : /* add handshake data to handshake for retrieval by user */
282 :
283 : /* find free handshake data */
284 42222 : ushort hs_data_idx = hs->hs_data_free_idx;
285 42222 : if( hs_data_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
286 : /* no free structures left. fail */
287 0 : return 0;
288 0 : }
289 :
290 : /* allocate enough space from hs data buffer */
291 42222 : uint ptr = hs->hs_data_buf_ptr;
292 42222 : uint alloc_data_sz = fd_uint_align_up( (uint)data_sz, FD_QUIC_TLS_HS_DATA_ALIGN );
293 42222 : if( ptr + alloc_data_sz > FD_QUIC_TLS_HS_DATA_SZ ) {
294 : /* not enough space */
295 0 : return 0;
296 0 : }
297 :
298 : /* success */
299 :
300 42222 : fd_quic_tls_hs_data_t * hs_data = &hs->hs_data[hs_data_idx];
301 42222 : uchar * buf = &hs->hs_data_buf[ptr];
302 :
303 : /* update free list */
304 42222 : hs->hs_data_free_idx = hs_data->next_idx;
305 :
306 : /* write back new buf ptr */
307 42222 : hs->hs_data_buf_ptr = ptr + alloc_data_sz;
308 :
309 : /* copy data into buffer, and update metadata in hs_data */
310 42222 : fd_memcpy( buf, data, data_sz );
311 42222 : hs_data->enc_level = enc_level;
312 42222 : hs_data->data = buf;
313 42222 : hs_data->data_sz = (uint)data_sz;
314 42222 : hs_data->offset = hs->hs_data_offset[enc_level];
315 :
316 : /* offset adjusted ready for more data */
317 42222 : hs->hs_data_offset[enc_level] += (uint)data_sz;
318 :
319 : /* add to end of pending list */
320 42222 : hs_data->next_idx = FD_QUIC_TLS_HS_DATA_UNUSED;
321 42222 : ulong pend_end_idx = hs->hs_data_pend_end_idx[enc_level];
322 42222 : if( pend_end_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
323 : /* pending list is empty */
324 24141 : hs->hs_data_pend_end_idx[enc_level] = hs->hs_data_pend_idx[enc_level] = hs_data_idx;
325 24141 : } else {
326 : /* last element must point to next */
327 18081 : hs->hs_data[pend_end_idx].next_idx = hs_data_idx;
328 18081 : hs->hs_data_pend_end_idx[enc_level] = hs_data_idx;
329 18081 : }
330 :
331 42222 : return 1;
332 42222 : }
333 :
334 : void
335 : fd_quic_tls_secrets( void const * handshake,
336 : void const * recv_secret,
337 : void const * send_secret,
338 24108 : uint enc_level ) {
339 :
340 24108 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
341 :
342 24108 : fd_quic_tls_secret_t secret = { .enc_level = enc_level };
343 24108 : memcpy( secret.read_secret, recv_secret, 32UL );
344 24108 : memcpy( secret.write_secret, send_secret, 32UL );
345 :
346 24108 : hs->quic_tls->secret_cb( hs, hs->context, &secret );
347 24108 : }
348 :
349 : fd_quic_tls_hs_data_t *
350 : fd_quic_tls_get_hs_data( fd_quic_tls_hs_t * self,
351 13060589 : uint enc_level ) {
352 13060589 : if( !self ) return NULL;
353 :
354 205005 : uint idx = self->hs_data_pend_idx[enc_level];
355 205005 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return NULL;
356 :
357 108474 : return &self->hs_data[idx];
358 205005 : }
359 :
360 : fd_quic_tls_hs_data_t *
361 90372 : fd_quic_tls_get_next_hs_data( fd_quic_tls_hs_t * self, fd_quic_tls_hs_data_t * hs ) {
362 90372 : ushort idx = hs->next_idx;
363 90372 : if( idx == (ushort)(~0u) ) return NULL;
364 36144 : return self->hs_data + idx;
365 90372 : }
366 :
367 : void
368 42189 : fd_quic_tls_pop_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
369 42189 : ushort idx = self->hs_data_pend_idx[enc_level];
370 42189 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return;
371 :
372 42189 : fd_quic_tls_hs_data_t * hs_data = &self->hs_data[idx];
373 :
374 : /* pop from pending list */
375 42189 : self->hs_data_pend_idx[enc_level] = hs_data->next_idx;
376 :
377 : /* if idx is the last, update last */
378 42189 : if( hs_data->next_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
379 24108 : self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
380 24108 : }
381 42189 : }
382 :
383 : void
384 12048 : fd_quic_tls_clear_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
385 12048 : self->hs_data_pend_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
386 12048 : self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
387 12048 : }
388 :
389 : void *
390 : fd_quic_tls_rand( void * ctx,
391 : void * buf,
392 12087 : ulong bufsz ) {
393 12087 : (void)ctx;
394 12087 : FD_TEST( fd_rng_secure( buf, bufsz ) );
395 12087 : return buf;
396 12087 : }
397 :
398 : ulong
399 : fd_quic_tls_tp_self( void * const handshake,
400 : uchar * const quic_tp,
401 12090 : ulong const quic_tp_bufsz ) {
402 12090 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
403 :
404 12090 : ulong encoded_sz = fd_quic_encode_transport_params( quic_tp, quic_tp_bufsz, &hs->self_transport_params );
405 12090 : if( FD_UNLIKELY( encoded_sz==FD_QUIC_ENCODE_FAIL ) ) {
406 0 : FD_LOG_WARNING(( "fd_quic_encode_transport_params failed" ));
407 0 : return 0UL;
408 0 : }
409 :
410 12090 : return encoded_sz;
411 12090 : }
412 :
413 : void
414 : fd_quic_tls_tp_peer( void * handshake,
415 : uchar const * quic_tp,
416 12057 : ulong quic_tp_sz ) {
417 : /* Callback issued by fd_tls. Bubble up callback to fd_quic_tls. */
418 :
419 12057 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
420 12057 : fd_quic_tls_t * quic_tls = hs->quic_tls;
421 :
422 12057 : quic_tls->peer_params_cb( hs->context, quic_tp, quic_tp_sz );
423 12057 : }
|