LCOV - code coverage report
Current view: top level - waltz/quic/tls - fd_quic_tls.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 180 209 86.1 %
Date: 2026-02-14 05:50:46 Functions: 15 15 100.0 %

          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_self 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        3411 :                  fd_quic_tls_cfg_t * cfg ) {
      69             : 
      70        3411 :   if( FD_UNLIKELY( !self ) ) {
      71           0 :     FD_LOG_WARNING(( "NULL mem" ));
      72           0 :     return NULL;
      73           0 :   }
      74        3411 :   if( FD_UNLIKELY( !cfg ) ) {
      75           0 :     FD_LOG_WARNING(( "NULL cfg" ));
      76           0 :     return NULL;
      77           0 :   }
      78        3411 :   if( FD_UNLIKELY( (!cfg->secret_cb            ) |
      79        3411 :                    (!cfg->handshake_complete_cb) |
      80        3411 :                    (!cfg->peer_params_cb       ) ) ) {
      81           0 :     FD_LOG_WARNING(( "Missing callbacks" ));
      82           0 :     return NULL;
      83           0 :   }
      84             : 
      85        3411 :   self->secret_cb             = cfg->secret_cb;
      86        3411 :   self->handshake_complete_cb = cfg->handshake_complete_cb;
      87        3411 :   self->peer_params_cb        = cfg->peer_params_cb;
      88             : 
      89             :   /* Initialize fd_tls */
      90        3411 :   fd_quic_tls_init( &self->tls, cfg->signer, cfg->cert_public_key );
      91             : 
      92        3411 :   return self;
      93        3411 : }
      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        3411 :                   uchar const   cert_public_key[ static 32 ] ) {
     102        3411 :   tls = fd_tls_new( tls );
     103        3411 :   *tls = (fd_tls_t) {
     104        3411 :     .quic = 1,
     105        3411 :     .rand = {
     106        3411 :       .ctx     = NULL,
     107        3411 :       .rand_fn = fd_quic_tls_rand
     108        3411 :     },
     109        3411 :     .sign = signer,
     110        3411 :     .secrets_fn = fd_quic_tls_secrets,
     111        3411 :     .sendmsg_fn = fd_quic_tls_sendmsg,
     112             : 
     113        3411 :     .quic_tp_self_fn = fd_quic_tls_tp_self,
     114        3411 :     .quic_tp_peer_fn = fd_quic_tls_tp_peer,
     115        3411 :   };
     116             : 
     117             :   /* Generate X25519 key */
     118        3411 :   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        3411 :   fd_x25519_public( tls->kex_public_key, tls->kex_private_key );
     121             : 
     122             :   /* Set up Ed25519 key */
     123        3411 :   fd_memcpy( tls->cert_public_key, cert_public_key, 32UL );
     124             : 
     125             :   /* Generate X.509 cert */
     126        3411 :   fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
     127        3411 :   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        3411 :   tls->alpn[ 0 ] = 0x0a;
     133        3411 :   memcpy( tls->alpn+1, "solana-tpu", 11UL );
     134        3411 :   tls->alpn_sz = 11UL;
     135        3411 : }
     136             : 
     137             : void *
     138        3339 : fd_quic_tls_delete( fd_quic_tls_t * self ) {
     139        3339 :   if( FD_UNLIKELY( !self ) ) {
     140           0 :     FD_LOG_WARNING(( "NULL self" ));
     141           0 :     return NULL;
     142           0 :   }
     143        3339 :   return self;
     144        3339 : }
     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       12261 :                     long               now ) {
     153             :   // clear the handshake bits
     154       12261 :   fd_memset( self, 0, sizeof(fd_quic_tls_hs_t) );
     155             : 
     156             :   // set properties on self
     157       12261 :   self->quic_tls  = quic_tls;
     158       12261 :   self->is_server = is_server;
     159       12261 :   self->context   = context;
     160             : 
     161             :   /* initialize handshake data */
     162             : 
     163             :   /* init free list */
     164       12261 :   self->hs_data_free_idx = 0u; /* head points at first */
     165      208437 :   for( ushort j = 0u; j < FD_QUIC_TLS_HS_DATA_CNT; ++j ) {
     166      196176 :     if( j < FD_QUIC_TLS_HS_DATA_CNT-1u ) {
     167      183915 :       self->hs_data[j].next_idx = (ushort)(j+1u); /* each point to next */
     168      183915 :     } else {
     169       12261 :       self->hs_data[j].next_idx = FD_QUIC_TLS_HS_DATA_UNUSED ;
     170       12261 :     }
     171      196176 :   }
     172             : 
     173             :   /* no data pending */
     174       61305 :   for( unsigned j = 0; j < 4; ++j ) {
     175       49044 :     self->hs_data_pend_idx[j]     = FD_QUIC_TLS_HS_DATA_UNUSED;
     176       49044 :     self->hs_data_pend_end_idx[j] = FD_QUIC_TLS_HS_DATA_UNUSED;
     177       49044 :   }
     178             : 
     179             :   /* clear hs_data_buf */
     180       12261 :   self->hs_data_buf_ptr = 0;
     181             : 
     182             :   /* all handshake offsets start at zero */
     183       12261 :   fd_memset( self->hs_data_offset, 0, sizeof( self->hs_data_offset ) );
     184             : 
     185             :   /* Set QUIC transport params */
     186       12261 :   self->self_transport_params = *self_transport_params;
     187             : 
     188       12261 :   if( is_server ) {
     189        6156 :     fd_tls_estate_srv_new( &self->hs.srv );
     190        6156 :   } else {
     191        6105 :     fd_tls_estate_cli_new( &self->hs.cli );
     192        6105 :     long res = fd_tls_client_handshake( &quic_tls->tls, &self->hs.cli, NULL, 0UL, 0 );
     193        6105 :     if( FD_UNLIKELY( res<0L ) ) {
     194           0 :       self->alert = (uint)-res;
     195           0 :     }
     196        6105 :   }
     197             : 
     198       12261 :   self->birthtime = now;
     199             : 
     200       12261 :   return self;
     201       12261 : }
     202             : 
     203             : void
     204       12258 : fd_quic_tls_hs_delete( fd_quic_tls_hs_t * self ) {
     205       12258 :   if( !self ) return;
     206             : 
     207       12258 :   if( self->is_server )
     208        6153 :     fd_tls_estate_srv_delete( &self->hs.srv );
     209        6105 :   else
     210        6105 :     fd_tls_estate_cli_delete( &self->hs.cli );
     211       12258 : }
     212             : 
     213             : int
     214       42510 : fd_quic_tls_process( fd_quic_tls_hs_t * self ) {
     215             : 
     216       42510 :   if( FD_UNLIKELY( self->hs.base.state==FD_TLS_HS_FAIL ) ) return FD_QUIC_FAILED;
     217       42510 :   if( self->hs.base.state==FD_TLS_HS_CONNECTED ) return FD_QUIC_SUCCESS;
     218             : 
     219             :   /* Process all fully received messages */
     220             : 
     221       42507 :   uint enc_level = self->rx_enc_level;
     222       85011 :   for(;;) {
     223       85011 :     uchar const * buf   = self->rx_hs_buf;
     224       85011 :     ulong         off   = self->rx_off;
     225       85011 :     ulong         avail = self->rx_sz - off;
     226       85011 :     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       42507 :     uint msg_sz = fd_uint_bswap( FD_LOAD( uint, buf+off ) ) & 0xFFFFFFU;
     231       42507 :     if( avail<msg_sz+4 ) break;
     232             : 
     233       42507 :     long res = fd_tls_handshake( &self->quic_tls->tls, &self->hs, buf+off, avail, enc_level );
     234             : 
     235       42507 :     if( FD_UNLIKELY( res<0L ) ) {
     236           3 :       int alert = (int)-res;
     237           3 :       self->alert = (uint)alert;
     238           3 :       return FD_QUIC_FAILED;
     239           3 :     }
     240       42504 :     if( FD_UNLIKELY( res==0UL ) ) {
     241           0 :       FD_LOG_WARNING(( "preventing deadlock" ));
     242           0 :       return FD_QUIC_FAILED;
     243           0 :     }
     244             : 
     245       42504 :     self->rx_off = (ushort)( off+(ulong)res );
     246       42504 :   }
     247             : 
     248       42504 :   switch( self->hs.base.state ) {
     249       12144 :   case FD_TLS_HS_CONNECTED:
     250             :     /* handshake completed */
     251       12144 :     self->quic_tls->handshake_complete_cb( self, self->context );
     252       12144 :     return FD_QUIC_SUCCESS;
     253           0 :   case FD_TLS_HS_FAIL:
     254             :     /* handshake permanently failed */
     255           0 :     return FD_QUIC_FAILED;
     256       30360 :   default:
     257             :     /* handshake not yet complete */
     258       30360 :     return FD_QUIC_SUCCESS;
     259       42504 :   }
     260       42504 : }
     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       42537 :                      int          flush FD_PARAM_UNUSED ) {
     270             : 
     271       42537 :   uint buf_sz = FD_QUIC_TLS_HS_DATA_SZ;
     272       42537 :   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       42537 :   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       42537 :   ushort hs_data_idx = hs->hs_data_free_idx;
     284       42537 :   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       42537 :   uint ptr           = hs->hs_data_buf_ptr;
     291       42537 :   uint alloc_data_sz = fd_uint_align_up( (uint)data_sz, FD_QUIC_TLS_HS_DATA_ALIGN );
     292       42537 :   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       42537 :   fd_quic_tls_hs_data_t * hs_data = &hs->hs_data[hs_data_idx];
     300       42537 :   uchar *                 buf     = &hs->hs_data_buf[ptr];
     301             : 
     302             :   /* update free list */
     303       42537 :   hs->hs_data_free_idx = hs_data->next_idx;
     304             : 
     305             :   /* write back new buf ptr */
     306       42537 :   hs->hs_data_buf_ptr = ptr + alloc_data_sz;
     307             : 
     308             :   /* copy data into buffer, and update metadata in hs_data */
     309       42537 :   fd_memcpy( buf, data, data_sz );
     310       42537 :   hs_data->enc_level    = enc_level;
     311       42537 :   hs_data->data         = buf;
     312       42537 :   hs_data->data_sz      = (uint)data_sz;
     313       42537 :   hs_data->offset       = hs->hs_data_offset[enc_level];
     314             : 
     315             :   /* offset adjusted ready for more data */
     316       42537 :   hs->hs_data_offset[enc_level] += (uint)data_sz;
     317             : 
     318             :   /* add to end of pending list */
     319       42537 :   hs_data->next_idx = FD_QUIC_TLS_HS_DATA_UNUSED;
     320       42537 :   ulong pend_end_idx = hs->hs_data_pend_end_idx[enc_level];
     321       42537 :   if( pend_end_idx == FD_QUIC_TLS_HS_DATA_UNUSED  ) {
     322             :     /* pending list is empty */
     323       24321 :     hs->hs_data_pend_end_idx[enc_level] = hs->hs_data_pend_idx[enc_level] = hs_data_idx;
     324       24321 :   } else {
     325             :     /* last element must point to next */
     326       18216 :     hs->hs_data[pend_end_idx].next_idx  = hs_data_idx;
     327       18216 :     hs->hs_data_pend_end_idx[enc_level] = hs_data_idx;
     328       18216 :   }
     329             : 
     330       42537 :   return 1;
     331       42537 : }
     332             : 
     333             : void
     334             : fd_quic_tls_secrets( void const * handshake,
     335             :                      void const * recv_secret,
     336             :                      void const * send_secret,
     337       24288 :                      uint         enc_level ) {
     338             : 
     339       24288 :   fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
     340             : 
     341       24288 :   fd_quic_tls_secret_t secret = { .enc_level = enc_level };
     342       24288 :   memcpy( secret.read_secret,  recv_secret, 32UL );
     343       24288 :   memcpy( secret.write_secret, send_secret, 32UL );
     344             : 
     345       24288 :   hs->quic_tls->secret_cb( hs, hs->context, &secret );
     346       24288 : }
     347             : 
     348             : fd_quic_tls_hs_data_t *
     349             : fd_quic_tls_get_hs_data( fd_quic_tls_hs_t * self,
     350    19879345 :                          uint               enc_level ) {
     351    19879345 :   if( !self ) return NULL;
     352             : 
     353      250899 :   uint idx = self->hs_data_pend_idx[enc_level];
     354      250899 :   if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return NULL;
     355             : 
     356      127407 :   return &self->hs_data[idx];
     357      250899 : }
     358             : 
     359             : fd_quic_tls_hs_data_t *
     360      121158 : fd_quic_tls_get_next_hs_data( fd_quic_tls_hs_t * self, fd_quic_tls_hs_data_t * hs ) {
     361      121158 :   ushort idx = hs->next_idx;
     362      121158 :   if( idx == (ushort)(~0u) ) return NULL;
     363       54477 :   return self->hs_data + idx;
     364      121158 : }
     365             : 
     366             : void
     367       42504 : fd_quic_tls_pop_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
     368       42504 :   ushort idx = self->hs_data_pend_idx[enc_level];
     369       42504 :   if( idx == FD_QUIC_TLS_HS_DATA_UNUSED ) return;
     370             : 
     371       42504 :   fd_quic_tls_hs_data_t * hs_data = &self->hs_data[idx];
     372             : 
     373             :   /* pop from pending list */
     374       42504 :   self->hs_data_pend_idx[enc_level] = hs_data->next_idx;
     375             : 
     376             :   /* if idx is the last, update last */
     377       42504 :   if( hs_data->next_idx == FD_QUIC_TLS_HS_DATA_UNUSED ) {
     378       24288 :     self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
     379       24288 :   }
     380       42504 : }
     381             : 
     382             : void
     383       12138 : fd_quic_tls_clear_hs_data( fd_quic_tls_hs_t * self, uint enc_level ) {
     384       12138 :   self->hs_data_pend_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
     385       12138 :   self->hs_data_pend_end_idx[enc_level] = FD_QUIC_TLS_HS_DATA_UNUSED;
     386       12138 : }
     387             : 
     388             : void *
     389             : fd_quic_tls_rand( void * ctx,
     390             :                   void * buf,
     391       12177 :                   ulong  bufsz ) {
     392       12177 :   (void)ctx;
     393       12177 :   FD_TEST( fd_rng_secure( buf, bufsz ) );
     394       12177 :   return buf;
     395       12177 : }
     396             : 
     397             : ulong
     398             : fd_quic_tls_tp_self( void *  const handshake,
     399             :                      uchar * const quic_tp,
     400       12180 :                      ulong   const quic_tp_bufsz ) {
     401       12180 :   fd_quic_tls_hs_t * hs = (fd_quic_tls_hs_t *)handshake;
     402             : 
     403       12180 :   ulong encoded_sz = fd_quic_encode_transport_params( quic_tp, quic_tp_bufsz, &hs->self_transport_params );
     404       12180 :   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       12180 :   return encoded_sz;
     410       12180 : }
     411             : 
     412             : void
     413             : fd_quic_tls_tp_peer( void *        handshake,
     414             :                      uchar const * quic_tp,
     415       12147 :                      ulong         quic_tp_sz ) {
     416             :   /* Callback issued by fd_tls.  Bubble up callback to fd_quic_tls. */
     417             : 
     418       12147 :   fd_quic_tls_hs_t * hs       = (fd_quic_tls_hs_t *)handshake;
     419       12147 :   fd_quic_tls_t *    quic_tls = hs->quic_tls;
     420             : 
     421       12147 :   quic_tls->peer_params_cb( hs->context, quic_tp, quic_tp_sz );
     422       12147 : }

Generated by: LCOV version 1.14