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