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