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 12165 : ulong now ) {
154 : // clear the handshake bits
155 12165 : fd_memset( self, 0, sizeof(fd_quic_tls_hs_t) );
156 :
157 : // set properties on self
158 12165 : self->quic_tls = quic_tls;
159 12165 : self->is_server = is_server;
160 12165 : self->context = context;
161 :
162 : /* initialize handshake data */
163 :
164 : /* init free list */
165 12165 : self->hs_data_free_idx = 0u; /* head points at first */
166 206805 : for( ushort j = 0u; j < FD_QUIC_TLS_HS_DATA_CNT; ++j ) {
167 194640 : if( j < FD_QUIC_TLS_HS_DATA_CNT-1u ) {
168 182475 : self->hs_data[j].next_idx = (ushort)(j+1u); /* each point to next */
169 182475 : } else {
170 12165 : self->hs_data[j].next_idx = FD_QUIC_TLS_HS_DATA_UNUSED ;
171 12165 : }
172 194640 : }
173 :
174 : /* no data pending */
175 60825 : for( unsigned j = 0; j < 4; ++j ) {
176 48660 : self->hs_data_pend_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
177 48660 : self->hs_data_pend_end_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
178 48660 : }
179 :
180 : /* set head and tail of used hs_data */
181 12165 : self->hs_data_buf_head = 0;
182 12165 : self->hs_data_buf_tail = 0;
183 :
184 : /* all handshake offsets start at zero */
185 12165 : fd_memset( self->hs_data_offset, 0, sizeof( self->hs_data_offset ) );
186 :
187 : /* Set QUIC transport params */
188 12165 : self->self_transport_params = *self_transport_params;
189 :
190 12165 : if( is_server ) {
191 6108 : fd_tls_estate_srv_new( &self->hs.srv );
192 6108 : } else {
193 6057 : fd_tls_estate_cli_new( &self->hs.cli );
194 6057 : long res = fd_tls_client_handshake( &quic_tls->tls, &self->hs.cli, NULL, 0UL, 0 );
195 6057 : if( FD_UNLIKELY( res<0L ) ) {
196 0 : self->alert = (uint)-res;
197 0 : }
198 6057 : }
199 :
200 12165 : self->birthtime = now;
201 :
202 12165 : return self;
203 12165 : }
204 :
205 : void
206 12165 : fd_quic_tls_hs_delete( fd_quic_tls_hs_t * self ) {
207 12165 : if( !self ) return;
208 :
209 12165 : if( self->is_server )
210 6108 : fd_tls_estate_srv_delete( &self->hs.srv );
211 6057 : else
212 6057 : fd_tls_estate_cli_delete( &self->hs.cli );
213 12165 : }
214 :
215 : int
216 42171 : fd_quic_tls_process( fd_quic_tls_hs_t * self ) {
217 :
218 42171 : if( FD_UNLIKELY( self->hs.base.state==FD_TLS_HS_FAIL ) ) return FD_QUIC_FAILED;
219 42171 : if( self->hs.base.state==FD_TLS_HS_CONNECTED ) return FD_QUIC_SUCCESS;
220 :
221 : /* Process all fully received messages */
222 :
223 42171 : uint enc_level = self->rx_enc_level;
224 84339 : for(;;) {
225 84339 : uchar const * buf = self->rx_hs_buf;
226 84339 : ulong off = self->rx_off;
227 84339 : ulong avail = self->rx_sz - off;
228 84339 : if( avail<4 ) break;
229 :
230 : /* Peek the message size from fd_tls_msg_hdr_t
231 : ?? AA BB CC => 0xCCBBAA?? => 0x??AABBCC => 0x00AABBCC */
232 42171 : uint msg_sz = fd_uint_bswap( FD_LOAD( uint, buf+off ) ) & 0xFFFFFFU;
233 42171 : if( avail<msg_sz+4 ) break;
234 :
235 42171 : long res = fd_tls_handshake( &self->quic_tls->tls, &self->hs, buf+off, avail, enc_level );
236 :
237 42171 : if( FD_UNLIKELY( res<0L ) ) {
238 3 : int alert = (int)-res;
239 3 : self->alert = (uint)alert;
240 3 : return FD_QUIC_FAILED;
241 3 : }
242 42168 : if( FD_UNLIKELY( res==0UL ) ) {
243 0 : FD_LOG_WARNING(( "preventing deadlock" ));
244 0 : return FD_QUIC_FAILED;
245 0 : }
246 :
247 42168 : self->rx_off = (ushort)( off+(ulong)res );
248 42168 : }
249 :
250 42168 : switch( self->hs.base.state ) {
251 12048 : case FD_TLS_HS_CONNECTED:
252 : /* handshake completed */
253 12048 : self->quic_tls->handshake_complete_cb( self, self->context );
254 12048 : return FD_QUIC_SUCCESS;
255 0 : case FD_TLS_HS_FAIL:
256 : /* handshake permanently failed */
257 0 : return FD_QUIC_FAILED;
258 30120 : default:
259 : /* handshake not yet complete */
260 30120 : return FD_QUIC_SUCCESS;
261 42168 : }
262 42168 : }
263 :
264 : /* internal callbacks */
265 :
266 : int
267 : fd_quic_tls_sendmsg( void const * handshake,
268 : void const * data,
269 : ulong data_sz,
270 : uint enc_level,
271 42201 : int flush FD_PARAM_UNUSED ) {
272 :
273 42201 : uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
274 42201 : if( data_sz > buf_sz ) {
275 0 : return 0;
276 0 : }
277 :
278 : /* Safe because the fd_tls_estate_{srv,cli}_t object is the first
279 : element of fd_quic_tls_hs_t */
280 42201 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
281 :
282 : /* add handshake data to handshake for retrieval by user */
283 :
284 : /* find free handshake data */
285 42201 : ushort hs_data_idx = hs->hs_data_free_idx;
286 42201 : if( hs_data_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
287 : /* no free structures left. fail */
288 0 : return 0;
289 0 : }
290 :
291 : /* allocate enough space from hs data buffer */
292 42201 : uint head = hs->hs_data_buf_head;
293 42201 : uint tail = hs->hs_data_buf_tail;
294 42201 : uint alloc_head = 0; /* to be determined */
295 :
296 42201 : uint alloc_data_sz = fd_uint_align_up( (uint)data_sz, FD_QUIC_TLS_HS_DATA_ALIGN );
297 42201 : uint free_data_sz = alloc_data_sz; /* the number of bytes to free */
298 :
299 : /* we need contiguous bytes
300 : head >= buf_sz implies wrap around */
301 42201 : if( head >= buf_sz ) {
302 : /* wrap around implies entire unused block is contiguous */
303 : /* head - tail is bytes used */
304 0 : if( buf_sz - (head - tail) < alloc_data_sz ) {
305 : /* not enough free */
306 0 : return 0;
307 0 : } else {
308 0 : alloc_head = head;
309 0 : }
310 42201 : } else {
311 : /* available data split */
312 42201 : if( buf_sz - head >= alloc_data_sz ) {
313 42201 : alloc_head = head;
314 42201 : } else {
315 : /* not enough at head, try front */
316 0 : if( tail < alloc_data_sz ) {
317 : /* not enough here either */
318 0 : return 0;
319 0 : }
320 :
321 : /* since we're skipping some free space at end of buffer,
322 : we need to free that also, upon pop */
323 0 : alloc_head = buf_sz; /* maintain head >= tail */
324 0 : free_data_sz = alloc_data_sz + buf_sz - head;
325 0 : }
326 42201 : }
327 :
328 : /* success */
329 :
330 42201 : uint buf_mask = (uint)( buf_sz - 1u );
331 42201 : fd_quic_tls_hs_data_t * hs_data = &hs->hs_data[hs_data_idx];
332 42201 : uchar * buf = &hs->hs_data_buf[alloc_head & buf_mask];
333 :
334 : /* update free list */
335 42201 : hs->hs_data_free_idx = hs_data->next_idx;
336 :
337 : /* update buffer pointers */
338 42201 : hs->hs_data_buf_head = alloc_head + alloc_data_sz;
339 :
340 : /* copy data into buffer, and update metadata in hs_data */
341 42201 : fd_memcpy( buf, data, data_sz );
342 42201 : hs_data->enc_level = enc_level;
343 42201 : hs_data->data = buf;
344 42201 : hs_data->data_sz = (uint)data_sz;
345 42201 : hs_data->free_data_sz = free_data_sz;
346 42201 : hs_data->offset = hs->hs_data_offset[enc_level];
347 :
348 : /* offset adjusted ready for more data */
349 42201 : hs->hs_data_offset[enc_level] += (uint)data_sz;
350 :
351 : /* add to end of pending list */
352 42201 : hs_data->next_idx = FD_QUIC_TLS_HS_DATA_UNUSED;
353 42201 : ulong pend_end_idx = hs->hs_data_pend_end_idx[enc_level];
354 42201 : if( pend_end_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
355 : /* pending list is empty */
356 24129 : hs->hs_data_pend_end_idx[enc_level] = hs->hs_data_pend_idx[enc_level] = hs_data_idx;
357 24129 : } else {
358 : /* last element must point to next */
359 18072 : hs->hs_data[pend_end_idx].next_idx = hs_data_idx;
360 18072 : hs->hs_data_pend_end_idx[enc_level] = hs_data_idx;
361 18072 : }
362 :
363 42201 : return 1;
364 42201 : }
365 :
366 : void
367 : fd_quic_tls_secrets( void const * handshake,
368 : void const * recv_secret,
369 : void const * send_secret,
370 24096 : uint enc_level ) {
371 :
372 24096 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
373 :
374 24096 : fd_quic_tls_secret_t secret = { .enc_level = enc_level };
375 24096 : memcpy( secret.read_secret, recv_secret, 32UL );
376 24096 : memcpy( secret.write_secret, send_secret, 32UL );
377 :
378 24096 : hs->quic_tls->secret_cb( hs, hs->context, &secret );
379 24096 : }
380 :
381 : fd_quic_tls_hs_data_t *
382 : fd_quic_tls_get_hs_data( fd_quic_tls_hs_t * self,
383 13751389 : uint enc_level ) {
384 13751389 : if( !self ) return NULL;
385 :
386 216924 : uint idx = self->hs_data_pend_idx[enc_level];
387 216924 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return NULL;
388 :
389 108411 : return &self->hs_data[idx];
390 216924 : }
391 :
392 : fd_quic_tls_hs_data_t *
393 90321 : fd_quic_tls_get_next_hs_data( fd_quic_tls_hs_t * self, fd_quic_tls_hs_data_t * hs ) {
394 90321 : ushort idx = hs->next_idx;
395 90321 : if( idx == (ushort)(~0u) ) return NULL;
396 36126 : return self->hs_data + idx;
397 90321 : }
398 :
399 : void
400 42168 : fd_quic_tls_pop_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
401 42168 : ushort idx = self->hs_data_pend_idx[enc_level];
402 42168 : if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return;
403 :
404 42168 : fd_quic_tls_hs_data_t * hs_data = &self->hs_data[idx];
405 :
406 42168 : uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
407 42168 : uint free_data_sz = hs_data->free_data_sz; /* amount of data to free */
408 :
409 : /* move tail pointer */
410 42168 : uint head = self->hs_data_buf_head;
411 42168 : uint tail = self->hs_data_buf_tail;
412 :
413 42168 : tail += free_data_sz;
414 42168 : if( tail > head ) {
415 : /* logic error - tried to free more than was allocated */
416 0 : FD_LOG_ERR(( "fd_quic_tls_pop_hs_data: tried to free more than was allocated" ));
417 0 : return;
418 0 : }
419 :
420 : /* adjust to maintain invariants */
421 42168 : if( tail >= buf_sz ) {
422 0 : tail -= buf_sz;
423 0 : head -= buf_sz;
424 0 : }
425 :
426 : /* write back head and tail */
427 42168 : self->hs_data_buf_head = head;
428 42168 : self->hs_data_buf_tail = tail;
429 :
430 : /* pop from pending list */
431 42168 : self->hs_data_pend_idx[enc_level] = hs_data->next_idx;
432 :
433 : /* if idx is the last, update last */
434 42168 : if( hs_data->next_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
435 24096 : self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
436 24096 : }
437 :
438 42168 : }
439 :
440 : void *
441 : fd_quic_tls_rand( void * ctx,
442 : void * buf,
443 12081 : ulong bufsz ) {
444 12081 : (void)ctx;
445 12081 : FD_TEST( fd_rng_secure( buf, bufsz ) );
446 12081 : return buf;
447 12081 : }
448 :
449 : ulong
450 : fd_quic_tls_tp_self( void * const handshake,
451 : uchar * const quic_tp,
452 12084 : ulong const quic_tp_bufsz ) {
453 12084 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
454 :
455 12084 : ulong encoded_sz = fd_quic_encode_transport_params( quic_tp, quic_tp_bufsz, &hs->self_transport_params );
456 12084 : if( FD_UNLIKELY( encoded_sz==FD_QUIC_ENCODE_FAIL ) ) {
457 0 : FD_LOG_WARNING(( "fd_quic_encode_transport_params failed" ));
458 0 : return 0UL;
459 0 : }
460 :
461 12084 : return encoded_sz;
462 12084 : }
463 :
464 : void
465 : fd_quic_tls_tp_peer( void * handshake,
466 : uchar const * quic_tp,
467 12051 : ulong quic_tp_sz ) {
468 : /* Callback issued by fd_tls. Bubble up callback to fd_quic_tls. */
469 :
470 12051 : fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
471 12051 : fd_quic_tls_t * quic_tls = hs->quic_tls;
472 :
473 12051 : quic_tls->peer_params_cb( hs->context, quic_tp, quic_tp_sz );
474 12051 : }
|