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

Generated by: LCOV version 1.14