LCOV - code coverage report
Current view: top level - discof/failover - fd_failover_tls.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 118 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 10 0.0 %

          Line data    Source code
       1             : #include "fd_failover_tls.h"
       2             : 
       3             : #include "../../ballet/ed25519/fd_x25519.h"
       4             : #include "../../ballet/x509/fd_x509_mock.h"
       5             : 
       6             : #include <string.h>
       7             : 
       8             : /* ALPN id, TLS length-prefixed */
       9             : static uchar const ALPN[] = "\x17" "fd-failover/on-demand/1";
      10             : 
      11             : FD_STATIC_ASSERT( sizeof(ALPN)==25UL, alpn_size );
      12             : 
      13             : int
      14             : fd_failover_tls_ctx_init( fd_failover_tls_ctx_t * ctx,
      15             :                           uchar const             public_key[ 32 ],
      16           0 :                           fd_tls_sign_t           signer ) {
      17           0 :   fd_failover_tls_ctx_fini( ctx );
      18             : 
      19           0 :   uchar seed[ 32 ];
      20           0 :   if( FD_UNLIKELY( !fd_rng_secure( seed, 32UL ) ) ) return -1;
      21           0 :   fd_chacha_rng_t * rng = fd_chacha_rng_join( fd_chacha_rng_new( ctx->rng, FD_CHACHA_RNG_MODE_MOD ) );
      22           0 :   FD_TEST( rng );
      23           0 :   fd_chacha_rng_init( rng, seed, FD_CHACHA_RNG_ALGO_CHACHA20 );
      24           0 :   fd_memzero_explicit( seed, sizeof(seed) );
      25             : 
      26           0 :   fd_tls_t * tls = fd_tls_join( fd_tls_new( &ctx->tls ) );
      27           0 :   FD_TEST( tls );
      28           0 :   tls->rng  = rng;
      29           0 :   tls->sign = signer;
      30           0 :   fd_memcpy( tls->cert_public_key, public_key, 32UL );
      31           0 :   fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
      32           0 :   tls->cert_x509_sz = FD_X509_MOCK_CERT_SZ;
      33           0 :   fd_memcpy( tls->alpn, ALPN, sizeof(ALPN)-1UL );
      34           0 :   tls->alpn_sz = sizeof(ALPN)-1UL;
      35           0 :   tls->quic    = 0;
      36           0 :   tls->req_client_cert = 1;
      37             : 
      38             :   /* The TLS configuration is ready for new connections. */
      39           0 :   ctx->ready = 1;
      40           0 :   return 0;
      41           0 : }
      42             : 
      43             : void
      44           0 : fd_failover_tls_ctx_fini( fd_failover_tls_ctx_t * ctx ) {
      45             :   /* Wiping the context prevents new connections until reinitialization. */
      46           0 :   fd_memzero_explicit( ctx, sizeof(*ctx) );
      47           0 : }
      48             : 
      49             : int
      50             : fd_failover_tls_new( fd_failover_tls_t *     tls,
      51             :                      fd_failover_tls_ctx_t * ctx,
      52             :                      int                     fd,
      53           0 :                      int                     dial_peer ) {
      54           0 :   if( FD_UNLIKELY( !ctx->ready ) ) return -1;
      55             :   /* Start an unverified, unpaired connection. */
      56           0 :   fd_memset( tls, 0, sizeof(*tls) );
      57           0 :   tls->fd = fd;
      58           0 :   fd_tlsrec_conn_init( &tls->conn, &ctx->tls, !dial_peer );
      59             :   /* Fresh X25519 key share per connection, straight from the kernel. */
      60           0 :   uchar seed[ 32 ];
      61           0 :   if( FD_UNLIKELY( !fd_rng_secure( seed, 32UL ) ) ) return -1;
      62           0 :   fd_chacha_rng_init( ctx->rng, seed, FD_CHACHA_RNG_ALGO_CHACHA20 );
      63           0 :   fd_memzero_explicit( seed, sizeof(seed) );
      64           0 :   if( FD_UNLIKELY( !fd_rng_secure( tls->conn.tls.kex_private_key, 32UL ) ) ) return -1;
      65           0 :   fd_x25519_public( tls->conn.tls.kex_public_key, tls->conn.tls.kex_private_key );
      66           0 :   fd_tlsrec_sock_init( &tls->sock );
      67           0 :   return 0;
      68           0 : }
      69             : 
      70             : int
      71           0 : fd_failover_tls_client_hello( fd_failover_tls_t * tls, uchar * out, ulong * out_sz ) {
      72           0 :   ulong app_sz = 0UL;
      73           0 :   return fd_tlsrec_conn_rx( &tls->conn, NULL, out, out_sz, NULL, &app_sz );
      74           0 : }
      75             : 
      76             : int
      77           0 : fd_failover_tls_accept_flight( fd_failover_tls_t * tls, uchar const * data, ulong sz ) {
      78           0 :   if( fd_tlsrec_sock_tx_pending( &tls->sock ) || tls->received>FD_FAILOVER_TLS_PREPAIR_MAX ||
      79           0 :       sz>FD_FAILOVER_TLS_PREPAIR_MAX-tls->received ) return -1;
      80           0 :   fd_tlsrec_slice_t rx[1];
      81           0 :   fd_tlsrec_slice_init( rx, (uchar *)data, sz );
      82           0 :   ulong tx_sz = sizeof(tls->sock.tx_buf);
      83           0 :   ulong app_sz = 0UL;
      84           0 :   int rc = fd_tlsrec_conn_rx( &tls->conn, rx, tls->sock.tx_buf, &tx_sz, NULL, &app_sz );
      85           0 :   tls->sock.tx_sz = tx_sz;
      86           0 :   tls->received += sz;
      87           0 :   return rc || !fd_tlsrec_slice_is_empty( rx ) || app_sz ? -1 : 0;
      88           0 : }
      89             : 
      90             : void
      91           0 : fd_failover_tls_fini( fd_failover_tls_t * tls ) {
      92           0 :   if( FD_LIKELY( tls->fd!=-1 && fd_tlsrec_conn_is_ready( &tls->conn ) && !tls->conn.tx_closed ) ) {
      93           0 :     (void)fd_tlsrec_sock_close( &tls->sock, &tls->conn, tls->fd );
      94           0 :   }
      95           0 :   fd_tlsrec_conn_fini( &tls->conn );
      96           0 :   fd_memzero_explicit( &tls->sock, sizeof(tls->sock) );
      97           0 :   tls->fd       = -1;
      98           0 :   tls->verified = 0;
      99           0 :   tls->paired   = 0;
     100           0 :   tls->received = 0UL;
     101           0 : }
     102             : 
     103             : static int
     104           0 : pull( fd_failover_tls_t * tls ) {
     105           0 :   if( !tls->paired && tls->received>=FD_FAILOVER_TLS_PREPAIR_MAX ) return -1;
     106           0 :   ulong rx_sz = 0UL;
     107           0 :   int   err   = fd_tlsrec_sock_rx( &tls->sock, &tls->conn, tls->fd, &rx_sz );
     108           0 :   if( !tls->paired ) tls->received += rx_sz;
     109           0 :   if( FD_LIKELY( !err ) ) return 0;
     110           0 :   if( err==FD_TLSREC_SOCK_ERR_EOF ) tls->peer_closed = 1;
     111           0 :   return -1;
     112           0 : }
     113             : 
     114             : int
     115           0 : fd_failover_tls_handshake( fd_failover_tls_t * tls ) {
     116           0 :   if( FD_LIKELY( tls->verified ) ) return 1; /* Verification is final for this connection. */
     117             :   /* A failed handshake stays failed until the channel closes it. */
     118           0 :   if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) {
     119           0 :     FD_LOG_DEBUG(( "failover TLS handshake failed: %s", fd_tls_reason_cstr( tls->conn.hs.base.reason ) ));
     120           0 :     return -1;
     121           0 :   }
     122           0 :   if( FD_UNLIKELY( fd_tlsrec_sock_flush( &tls->sock, tls->fd )<0 ) ) return -1;
     123           0 :   if( FD_LIKELY( !fd_tlsrec_conn_is_ready( &tls->conn ) ) ) {
     124           0 :     if( FD_UNLIKELY( pull( tls ) ) ) return -1;
     125           0 :     if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) {
     126           0 :       FD_LOG_DEBUG(( "failover TLS handshake failed: %s", fd_tls_reason_cstr( tls->conn.hs.base.reason ) ));
     127           0 :       return -1;
     128           0 :     }
     129           0 :     if( FD_LIKELY( !fd_tlsrec_conn_is_ready( &tls->conn ) ) ) return 0; /* Continue on a later poll. */
     130           0 :   }
     131             : 
     132             :   /* Handshake done, the peer signed with some key and agreed on our ALPN.
     133             :      We keep that key, the channel checks it against HELLO. */
     134             : 
     135           0 :   if( !fd_tlsrec_conn_is_server( &tls->conn ) ) {
     136           0 :     fd_tls_estate_cli_t const * cli = &tls->conn.hs.cli;
     137           0 :     if( FD_UNLIKELY( cli->server_key_type!=FD_TLS_KEY_ED25519 ||
     138           0 :                      cli->server_pubkey_len!=32UL || !cli->alpn_negotiated ) ) return -1;
     139           0 :     fd_memcpy( tls->peer_pubkey, cli->server_pubkey, 32UL );
     140           0 :   } else {
     141           0 :     fd_tls_estate_srv_t const * srv = &tls->conn.hs.srv;
     142           0 :     fd_memcpy( tls->peer_pubkey, srv->client_pubkey, 32UL );
     143           0 :   }
     144             :   /* TLS verified the peer key, HELLO must still authorize the member. */
     145           0 :   tls->verified = 1;
     146           0 :   return 1;
     147           0 : }
     148             : 
     149             : long
     150             : fd_failover_tls_read( fd_failover_tls_t * tls,
     151             :                       void *              buf,
     152           0 :                       ulong               sz ) {
     153           0 :   if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) return -1L;
     154           0 :   if( FD_LIKELY( !fd_tlsrec_sock_rx_avail( &tls->sock ) ) ) {
     155           0 :     if( FD_UNLIKELY( fd_tlsrec_sock_flush( &tls->sock, tls->fd )<0 ) ) return -1L;
     156           0 :     if( FD_UNLIKELY( pull( tls ) ) ) return -1L;
     157           0 :   }
     158           0 :   return (long)fd_tlsrec_sock_rx_pop( &tls->sock, buf, sz );
     159           0 : }
     160             : 
     161             : long
     162             : fd_failover_tls_write( fd_failover_tls_t * tls,
     163             :                        void const *        buf,
     164           0 :                        ulong               sz ) {
     165           0 :   if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) || tls->conn.tx_closed ) ) return -1L;
     166           0 :   int rc = fd_tlsrec_sock_flush( &tls->sock, tls->fd );
     167           0 :   if( FD_UNLIKELY( rc ) ) return rc<0 ? -1L : 0L; /* Earlier output is still parked. */
     168           0 :   ulong consumed = 0UL;
     169           0 :   if( FD_UNLIKELY( fd_tlsrec_sock_tx( &tls->sock, &tls->conn, tls->fd, buf, sz, &consumed ) ) ) return -1L;
     170           0 :   return (long)consumed;
     171           0 : }

Generated by: LCOV version 1.14