LCOV - code coverage report
Current view: top level - waltz/quic/tls - fd_quic_tls.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 196 240 81.7 %
Date: 2025-07-01 05:00:49 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             : #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 : }

Generated by: LCOV version 1.14