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 3330 : fd_quic_tls_cfg_t * cfg ) {
70 :
71 3330 : if( FD_UNLIKELY( !self ) ) {
72 0 : FD_LOG_WARNING(( "NULL mem" ));
73 0 : return NULL;
74 0 : }
75 3330 : if( FD_UNLIKELY( !cfg ) ) {
76 0 : FD_LOG_WARNING(( "NULL cfg" ));
77 0 : return NULL;
78 0 : }
79 3330 : if( FD_UNLIKELY( (!cfg->secret_cb ) |
80 3330 : (!cfg->handshake_complete_cb) |
81 3330 : (!cfg->peer_params_cb ) ) ) {
82 0 : FD_LOG_WARNING(( "Missing callbacks" ));
83 0 : return NULL;
84 0 : }
85 :
86 3330 : self->secret_cb = cfg->secret_cb;
87 3330 : self->handshake_complete_cb = cfg->handshake_complete_cb;
88 3330 : self->peer_params_cb = cfg->peer_params_cb;
89 :
90 : /* Initialize fd_tls */
91 3330 : fd_quic_tls_init( &self->tls, cfg->signer, cfg->cert_public_key );
92 :
93 3330 : return self;
94 3330 : }
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 3330 : uchar const cert_public_key[ static 32 ] ) {
103 3330 : tls = fd_tls_new( tls );
104 3330 : *tls = (fd_tls_t) {
105 3330 : .quic = 1,
106 3330 : .rand = {
107 3330 : .ctx = NULL,
108 3330 : .rand_fn = fd_quic_tls_rand
109 3330 : },
110 3330 : .sign = signer,
111 3330 : .secrets_fn = fd_quic_tls_secrets,
112 3330 : .sendmsg_fn = fd_quic_tls_sendmsg,
113 :
114 3330 : .quic_tp_self_fn = fd_quic_tls_tp_self,
115 3330 : .quic_tp_peer_fn = fd_quic_tls_tp_peer,
116 3330 : };
117 :
118 : /* Generate X25519 key */
119 3330 : 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 3330 : fd_x25519_public( tls->kex_public_key, tls->kex_private_key );
122 :
123 : /* Set up Ed25519 key */
124 3330 : fd_memcpy( tls->cert_public_key, cert_public_key, 32UL );
125 :
126 : /* Generate X.509 cert */
127 3330 : fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
128 3330 : 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 3330 : tls->alpn[ 0 ] = 0x0a;
134 3330 : memcpy( tls->alpn+1, "solana-tpu", 11UL );
135 3330 : tls->alpn_sz = 11UL;
136 3330 : }
137 :
138 : void *
139 3321 : fd_quic_tls_delete( fd_quic_tls_t * self ) {
140 3321 : if( FD_UNLIKELY( !self ) ) {
141 0 : FD_LOG_WARNING(( "NULL self" ));
142 0 : return NULL;
143 0 : }
144 3321 : return self;
145 3321 : }
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 12294 : fd_quic_transport_params_t const * self_transport_params ) {
153 : // clear the handshake bits
154 12294 : fd_memset( self, 0, sizeof(fd_quic_tls_hs_t) );
155 :
156 : // set properties on self
157 12294 : self->quic_tls = quic_tls;
158 12294 : self->is_server = is_server;
159 12294 : self->context = context;
160 :
161 : /* initialize handshake data */
162 :
163 : /* init free list */
164 12294 : self->hs_data_free_idx = 0u; /* head points at first */
165 208998 : for( ushort j = 0u; j < FD_QUIC_TLS_HS_DATA_CNT; ++j ) {
166 196704 : if( j < FD_QUIC_TLS_HS_DATA_CNT-1u ) {
167 184410 : self->hs_data[j].next_idx = (ushort)(j+1u); /* each point to next */
168 184410 : } else {
169 12294 : self->hs_data[j].next_idx = FD_QUIC_TLS_HS_DATA_UNUSED ;
170 12294 : }
171 196704 : }
172 :
173 : /* no data pending */
174 61470 : for( unsigned j = 0; j < 4; ++j ) {
175 49176 : self->hs_data_pend_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
176 49176 : self->hs_data_pend_end_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
177 49176 : }
178 :
179 : /* set head and tail of used hs_data */
180 12294 : self->hs_data_buf_head = 0;
181 12294 : self->hs_data_buf_tail = 0;
182 :
183 : /* all handshake offsets start at zero */
184 12294 : fd_memset( self->hs_data_offset, 0, sizeof( self->hs_data_offset ) );
185 :
186 : /* Set QUIC transport params */
187 12294 : self->self_transport_params = *self_transport_params;
188 :
189 12294 : if( is_server ) {
190 6276 : fd_tls_estate_srv_new( &self->hs.srv );
191 6276 : } else {
192 6018 : fd_tls_estate_cli_new( &self->hs.cli );
193 6018 : long res = fd_tls_client_handshake( &quic_tls->tls, &self->hs.cli, NULL, 0UL, 0 );
194 6018 : if( FD_UNLIKELY( res<0L ) ) {
195 0 : self->alert = (uint)-res;
196 0 : }
197 6018 : }
198 :
199 12294 : return self;
200 12294 : }
201 :
202 : void
203 26409 : fd_quic_tls_hs_delete( fd_quic_tls_hs_t * self ) {
204 26409 : if( !self ) return;
205 :
206 12294 : if( self->is_server )
207 6276 : fd_tls_estate_srv_delete( &self->hs.srv );
208 6018 : else
209 6018 : fd_tls_estate_cli_delete( &self->hs.cli );
210 12294 : }
211 :
212 : int
213 42126 : fd_quic_tls_process( fd_quic_tls_hs_t * self ) {
214 :
215 42126 : if( FD_UNLIKELY( self->hs.base.state==FD_TLS_HS_FAIL ) ) return FD_QUIC_FAILED;
216 42126 : if( self->hs.base.state==FD_TLS_HS_CONNECTED ) return FD_QUIC_SUCCESS;
217 :
218 : /* Process all fully received messages */
219 :
220 42126 : uint enc_level = self->rx_enc_level;
221 84252 : for(;;) {
222 84252 : uchar const * buf = self->rx_hs_buf;
223 84252 : ulong off = self->rx_off;
224 84252 : ulong avail = self->rx_sz - off;
225 84252 : if( avail<4 ) break;
226 :
227 : /* Peek the message size from fd_tls_msg_hdr_t
228 : ?? AA BB CC => 0xCCBBAA?? => 0x??AABBCC => 0x00AABBCC */
229 42126 : uint msg_sz = fd_uint_bswap( FD_LOAD( uint, buf+off ) ) & 0xFFFFFFU;
230 42126 : if( avail<msg_sz+4 ) break;
231 :
232 42126 : long res = fd_tls_handshake( &self->quic_tls->tls, &self->hs, buf+off, avail, enc_level );
233 :
234 42126 : if( FD_UNLIKELY( res<0L ) ) {
235 0 : int alert = (int)-res;
236 0 : self->alert = (uint)alert;
237 0 : return FD_QUIC_FAILED;
238 0 : }
239 42126 : if( FD_UNLIKELY( res==0UL ) ) {
240 0 : FD_LOG_WARNING(( "preventing deadlock" ));
241 0 : return FD_QUIC_FAILED;
242 0 : }
243 :
244 42126 : self->rx_off = (ushort)( off+(ulong)res );
245 42126 : }
246 :
247 42126 : switch( self->hs.base.state ) {
248 12036 : case FD_TLS_HS_CONNECTED:
249 : /* handshake completed */
250 12036 : self->quic_tls->handshake_complete_cb( self, self->context );
251 12036 : return FD_QUIC_SUCCESS;
252 0 : case FD_TLS_HS_FAIL:
253 : /* handshake permanently failed */
254 0 : return FD_QUIC_FAILED;
255 30090 : default:
256 : /* handshake not yet complete */
257 30090 : return FD_QUIC_SUCCESS;
258 42126 : }
259 42126 : }
260 :
261 : /* internal callbacks */
262 :
263 : int
264 : fd_quic_tls_sendmsg( void const * handshake,
265 : void const * data,
266 : ulong data_sz,
267 : uint enc_level,
268 42126 : int flush FD_PARAM_UNUSED ) {
269 :
270 42126 : uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
271 42126 : if( data_sz > buf_sz ) {
272 0 : return 0;
273 0 : }
274 :
275 : /* Safe because the fd_tls_estate_{srv,cli}_t object is the first
276 : element of fd_quic_tls_hs_t */
277 42126 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
278 :
279 : /* add handshake data to handshake for retrieval by user */
280 :
281 : /* find free handshake data */
282 42126 : ushort hs_data_idx = hs->hs_data_free_idx;
283 42126 : if( hs_data_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
284 : /* no free structures left. fail */
285 0 : return 0;
286 0 : }
287 :
288 : /* allocate enough space from hs data buffer */
289 42126 : uint head = hs->hs_data_buf_head;
290 42126 : uint tail = hs->hs_data_buf_tail;
291 42126 : uint alloc_head = 0; /* to be determined */
292 :
293 42126 : uint alloc_data_sz = fd_uint_align_up( (uint)data_sz, FD_QUIC_TLS_HS_DATA_ALIGN );
294 42126 : uint free_data_sz = alloc_data_sz; /* the number of bytes to free */
295 :
296 : /* we need contiguous bytes
297 : head >= buf_sz implies wrap around */
298 42126 : if( head >= buf_sz ) {
299 : /* wrap around implies entire unused block is contiguous */
300 0 : if( head - tail < alloc_data_sz ) {
301 : /* not enough free */
302 0 : return 0;
303 0 : } else {
304 0 : alloc_head = head;
305 0 : }
306 42126 : } else {
307 : /* available data split */
308 42126 : if( buf_sz - head >= alloc_data_sz ) {
309 42126 : alloc_head = head;
310 42126 : } else {
311 : /* not enough at head, try front */
312 0 : if( tail < alloc_data_sz ) {
313 : /* not enough here either */
314 0 : return 0;
315 0 : }
316 :
317 : /* since we're skipping some free space at end of buffer,
318 : we need to free that also, upon pop */
319 0 : alloc_head = 0;
320 0 : free_data_sz = alloc_data_sz + buf_sz - head;
321 0 : }
322 42126 : }
323 :
324 : /* success */
325 :
326 42126 : uint buf_mask = (uint)( buf_sz - 1u );
327 42126 : fd_quic_tls_hs_data_t * hs_data = &hs->hs_data[hs_data_idx];
328 42126 : uchar * buf = &hs->hs_data_buf[alloc_head & buf_mask];
329 :
330 : /* update free list */
331 42126 : hs->hs_data_free_idx = hs_data->next_idx;
332 :
333 : /* update buffer pointers */
334 42126 : hs->hs_data_buf_head = alloc_head + alloc_data_sz;
335 :
336 : /* copy data into buffer, and update metadata in hs_data */
337 42126 : fd_memcpy( buf, data, data_sz );
338 42126 : hs_data->enc_level = enc_level;
339 42126 : hs_data->data = buf;
340 42126 : hs_data->data_sz = (uint)data_sz;
341 42126 : hs_data->free_data_sz = free_data_sz;
342 42126 : hs_data->offset = hs->hs_data_offset[enc_level];
343 :
344 : /* offset adjusted ready for more data */
345 42126 : hs->hs_data_offset[enc_level] += (uint)data_sz;
346 :
347 : /* add to end of pending list */
348 42126 : hs_data->next_idx = FD_QUIC_TLS_HS_DATA_UNUSED;
349 42126 : ulong pend_end_idx = hs->hs_data_pend_end_idx[enc_level];
350 42126 : if( pend_end_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
351 : /* pending list is empty */
352 24072 : hs->hs_data_pend_end_idx[enc_level] = hs->hs_data_pend_idx[enc_level] = hs_data_idx;
353 24072 : } else {
354 : /* last element must point to next */
355 18054 : hs->hs_data[pend_end_idx].next_idx = hs_data_idx;
356 18054 : hs->hs_data_pend_end_idx[enc_level] = hs_data_idx;
357 18054 : }
358 :
359 42126 : return 1;
360 42126 : }
361 :
362 : void
363 : fd_quic_tls_secrets( void const * handshake,
364 : void const * recv_secret,
365 : void const * send_secret,
366 24072 : uint enc_level ) {
367 :
368 24072 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
369 :
370 24072 : fd_quic_tls_secret_t secret = { .enc_level = enc_level };
371 24072 : memcpy( secret.read_secret, recv_secret, 32UL );
372 24072 : memcpy( secret.write_secret, send_secret, 32UL );
373 :
374 24072 : hs->quic_tls->secret_cb( hs, hs->context, &secret );
375 24072 : }
376 :
377 : fd_quic_tls_hs_data_t *
378 : fd_quic_tls_get_hs_data( fd_quic_tls_hs_t * self,
379 14532332 : uint enc_level ) {
380 14532332 : if( !self ) return NULL;
381 :
382 217416 : uint idx = self->hs_data_pend_idx[enc_level];
383 217416 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return NULL;
384 :
385 108303 : return &self->hs_data[idx];
386 217416 : }
387 :
388 : fd_quic_tls_hs_data_t *
389 90231 : fd_quic_tls_get_next_hs_data( fd_quic_tls_hs_t * self, fd_quic_tls_hs_data_t * hs ) {
390 90231 : ushort idx = hs->next_idx;
391 90231 : if( idx == (ushort)(~0u) ) return NULL;
392 36090 : return self->hs_data + idx;
393 90231 : }
394 :
395 : void
396 42126 : fd_quic_tls_pop_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
397 42126 : ushort idx = self->hs_data_pend_idx[enc_level];
398 42126 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return;
399 :
400 42126 : fd_quic_tls_hs_data_t * hs_data = &self->hs_data[idx];
401 :
402 42126 : uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
403 42126 : uint free_data_sz = hs_data->free_data_sz; /* amount of data to free */
404 :
405 : /* move tail pointer */
406 42126 : uint head = self->hs_data_buf_head;
407 42126 : uint tail = self->hs_data_buf_tail;
408 :
409 42126 : tail += free_data_sz;
410 42126 : if( tail > head ) {
411 : /* logic error - tried to free more than was allocated */
412 0 : FD_LOG_ERR(( "fd_quic_tls_pop_hs_data: tried to free more than was allocated" ));
413 0 : return;
414 0 : }
415 :
416 : /* adjust to maintain invariants */
417 42126 : if( tail >= buf_sz ) {
418 0 : tail -= buf_sz;
419 0 : head -= buf_sz;
420 0 : }
421 :
422 : /* write back head and tail */
423 42126 : self->hs_data_buf_head = head;
424 42126 : self->hs_data_buf_tail = tail;
425 :
426 : /* pop from pending list */
427 42126 : self->hs_data_pend_idx[enc_level] = hs_data->next_idx;
428 :
429 : /* if idx is the last, update last */
430 42126 : if( hs_data->next_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
431 24072 : self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
432 24072 : }
433 :
434 42126 : }
435 :
436 : void *
437 : fd_quic_tls_rand( void * ctx,
438 : void * buf,
439 12036 : ulong bufsz ) {
440 12036 : (void)ctx;
441 12036 : FD_TEST( fd_rng_secure( buf, bufsz ) );
442 12036 : return buf;
443 12036 : }
444 :
445 : ulong
446 : fd_quic_tls_tp_self( void * const handshake,
447 : uchar * const quic_tp,
448 12036 : ulong const quic_tp_bufsz ) {
449 12036 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
450 :
451 12036 : ulong encoded_sz = fd_quic_encode_transport_params( quic_tp, quic_tp_bufsz, &hs->self_transport_params );
452 12036 : if( FD_UNLIKELY( encoded_sz==FD_QUIC_ENCODE_FAIL ) ) {
453 0 : FD_LOG_WARNING(( "fd_quic_encode_transport_params failed" ));
454 0 : return 0UL;
455 0 : }
456 :
457 12036 : return encoded_sz;
458 12036 : }
459 :
460 : void
461 : fd_quic_tls_tp_peer( void * handshake,
462 : uchar const * quic_tp,
463 12036 : ulong quic_tp_sz ) {
464 : /* Callback issued by fd_tls. Bubble up callback to fd_quic_tls. */
465 :
466 12036 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
467 12036 : fd_quic_tls_t * quic_tls = hs->quic_tls;
468 :
469 12036 : quic_tls->peer_params_cb( hs->context, quic_tp, quic_tp_sz );
470 12036 : }
|