LCOV - code coverage report
Current view: top level - waltz/tls - fd_tls_estate.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 22 22 100.0 %
Date: 2026-10-30 04:26:52 Functions: 6 352 1.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_tls_fd_tls_estate_h
       2             : #define HEADER_fd_src_waltz_tls_fd_tls_estate_h
       3             : 
       4             : #include "../fd_waltz_base.h"
       5             : #include "../../ballet/sha256/fd_sha256.h"
       6             : #include "../../ballet/x509/fd_x509.h"
       7             : 
       8             : /* Peer key types, same values as FD_X509_KEY_{...} */
       9             : 
      10         579 : #define FD_TLS_KEY_ED25519    ((uchar)0)
      11          18 : #define FD_TLS_KEY_ECDSA_P256 ((uchar)1)
      12             : #define FD_TLS_KEY_ECDSA_P384 ((uchar)2)
      13          90 : #define FD_TLS_KEY_RSA        ((uchar)3)
      14             : 
      15             : /* Base ***************************************************************/
      16             : 
      17             : /* fd_tls_estate_base_t is the shared header of the
      18             :    fd_tls_estate_{srv,cli} objects. */
      19             : 
      20             : struct fd_tls_estate_base {
      21             :   uchar  state;
      22             :   uchar  server   : 1;  /* 1 if server, 0 if client */
      23             :   ushort reason;      /* FD_TLS_REASON_{...} */
      24             : 
      25             :   /* Sadly required for SSLKEYLOGFILE */
      26             :   uchar client_random[ 32 ];
      27             : };
      28             : 
      29             : typedef struct fd_tls_estate_base fd_tls_estate_base_t;
      30             : 
      31             : /* Transcript-Hash (RFC 8446 Section 4.4.1)
      32             :    The transcript is a running hash over all handshake messages.  The
      33             :    hash state depends on the current handshake progression.  The hash
      34             :    order is as follows:
      35             : 
      36             :      client   ClientHello           always
      37             :      server   ServerHello           always
      38             :      server   EncryptedExtensions   always
      39             :      server   CertificateRequest    optional
      40             :      server   Certificate           always
      41             :      server   CertificateVerify     always
      42             :      server   Finished              always
      43             :      client   Certificate           optional
      44             :      client   CertificateVerify     optional
      45             :      client   Finished              always */
      46             : 
      47             : struct fd_tls_transcript {
      48             :   uchar buf[ 64 ];  /* Pending SHA block */
      49             :   uint  sha[ 8 ];   /* Current internal SHA state */
      50             :   uint  len;        /* Number of bytes so far compressed into SHA state
      51             :                        plus number of bytes in pending in buf */
      52             : };
      53             : 
      54             : typedef struct fd_tls_transcript fd_tls_transcript_t;
      55             : 
      56             : /* Note:  An experimental memory optimization that is not implemented
      57             :    here is alignment of SHA state.  It might be possible to craft a
      58             :    transcript preimage that is aligned to SHA block size.  This allows
      59             :    omitting the SHA block buffer, saving 64 bytes per transcript (and
      60             :    thus per in-flight handshake). */
      61             : 
      62             : FD_PROTOTYPES_BEGIN
      63             : 
      64             : static inline void
      65             : fd_tls_transcript_store( fd_tls_transcript_t * script,
      66        8247 :                          fd_sha256_t const *   sha ) {
      67        8247 :   memcpy( script->buf, sha->buf,   64UL );
      68        8247 :   memcpy( script->sha, sha->state, 32UL );
      69        8247 :   script->len = (uint)( sha->bit_cnt / 8UL );
      70        8247 : }
      71             : 
      72             : static inline void
      73             : fd_tls_transcript_load( fd_tls_transcript_t const * script,
      74        8166 :                         fd_sha256_t *               sha ) {
      75        8166 :   memcpy( sha->buf,   script->buf, 64UL );
      76        8166 :   memcpy( sha->state, script->sha, 32UL );
      77        8166 :   sha->bit_cnt = (ulong)( script->len ) * 8UL;
      78        8166 :   sha->buf_used = (uint)( script->len % 64U );
      79        8166 : }
      80             : 
      81             : FD_PROTOTYPES_END
      82             : 
      83             : /* Server *************************************************************/
      84             : 
      85             : /* fd_tls_estate_srv contains compressed TLS server handshake state
      86             :    while waiting for the client to send a message.  estate is optimized
      87             :    for small memory use for incoming conns (sizeof(fd_tls_estate_srv_t)
      88             :    bytes per handshake).
      89             : 
      90             :    *** Security ********************************************************
      91             : 
      92             :    fd_tls servers must handle handshake requests from untrusted clients,
      93             :    and thus require hardening.  A typical lifetime of an estate object
      94             :    is in the order of ~3 seconds (conn timeout).  This requires some
      95             :    care to avoid memory exhaustion attacks.
      96             : 
      97             :    For example, an off-the-shelf TLS implementation uses a handshake
      98             :    state size of about 10 KB (via multiple allocations on global heap).
      99             :    Assuming 3 second conn timeout, and flood rate of 1 million
     100             :    ClientHello msg/s,
     101             :    an attacker could indefinitely occupy ~30 GB of raw heap allocations!
     102             :    More concerning -- Global heap pressure will cause latent allocation
     103             :    failures in unrelated code, which might escalate to OOM kills.
     104             : 
     105             :    For estate_srv, the corresponding memory is three million times
     106             :    sizeof(fd_tls_estate_srv_t), plus transport overhead.  The memory of
     107             :    estate_srv objects is provided by the transport
     108             :    layer (e.g. QUIC or TLS record layer).  Each estate_srv object has
     109             :    static footprint.  This allows dense packing in custom memory arenas.
     110             : 
     111             :    This allows for robust handling of packet floods:  Even in the event
     112             :    the estate_srv arena is exhausted, the impact is limited to temporary
     113             :    inability to accept new connections.  Processing of established conns
     114             :    and unrelated code is unaffected.
     115             : 
     116             :    *** State Machine ***************************************************
     117             : 
     118             :    Each estate_srv object corresponds to an instance of a server-side
     119             :    TLS handshake state machine.  It is first instantiated when the
     120             :    client sent its ClientHello, the first message of a TLS handshake.
     121             : 
     122             :    After processing ClientHello and sending through server Finished,
     123             :    the server waits in one of these states:
     124             : 
     125             :      FD_TLS_HS_WAIT_CERT:      Receive the requested client certificate.
     126             :      FD_TLS_HS_WAIT_CV:        Verify the client's CertificateVerify.
     127             :      FD_TLS_HS_WAIT_FINISHED:  Verify client Finished.
     128             : 
     129             :    Without client authentication, the server goes directly to
     130             :    WAIT_FINISHED.
     131             : 
     132             :    State data contains:
     133             : 
     134             :    - The transcript hash state, which commits both sides to the entire
     135             :      sequence of handshake messages (such that they cannot be tampered
     136             :      with).
     137             : 
     138             :    - The client handshake secret, which is used to derive the "client
     139             :      Finished" verify data.
     140             : 
     141             :    - The client authentication flags and Ed25519 public key extracted
     142             :      from the client certificate when client authentication is enabled. */
     143             : 
     144             : struct fd_tls_estate_srv {
     145             :   /* TLS base (hs) handles are deliberately placed at the start.
     146             :    Allows for type punning between fd_quic_tls_hs_t and
     147             :    fd_tls_estate_{srv,cli}_t.  DO NOT MOVE.
     148             :    Type of handshake object depends on is_server. */
     149             :   fd_tls_estate_base_t base;
     150             : 
     151             :   uchar client_cert : 1;  /* 0: no client auth  1: client cert */
     152             :   uchar hello_retry : 1;
     153             : 
     154             :   fd_tls_transcript_t transcript;
     155             :   uchar               client_hs_secret[32];
     156             :   uchar               client_pubkey[32];
     157             : };
     158             : 
     159             : typedef struct fd_tls_estate_srv fd_tls_estate_srv_t;
     160             : 
     161             : 
     162             : /* Note:  When requesting a cert, the server should consider stopping
     163             :    after CertificateVerify to save compute resources (at the expense of
     164             :    more memory required to save state carried over from
     165             :    CertificateRequest...server Finished). */
     166             : 
     167             : FD_PROTOTYPES_BEGIN
     168             : 
     169             : /* fd_tls_estate_srv_new initializes an estate object for an incoming
     170             :    conn.  mem points to a memory region suitable for storing an
     171             :    fd_tls_estate_srv_t.  Returns cast of mem, which will be initialized
     172             :    to state FD_TLS_HS_START. */
     173             : 
     174             : fd_tls_estate_srv_t *
     175             : fd_tls_estate_srv_new( void * mem );
     176             : 
     177             : static inline void *
     178        6162 : fd_tls_estate_srv_delete( fd_tls_estate_srv_t * estate ) {
     179        6162 :   if( FD_LIKELY( estate ) ) fd_memzero_explicit( estate, sizeof(fd_tls_estate_srv_t) );
     180        6162 :   return (void *)estate;
     181        6162 : }
     182             : 
     183             : FD_PROTOTYPES_END
     184             : 
     185             : 
     186             : /* Client *************************************************************/
     187             : 
     188             : /* fd_tls_estate_cli contains TLS client handshake state while waiting
     189             :    for the server to send a message.
     190             : 
     191             :    TLS client side handshake state is considerably more complex than
     192             :    the server-side state.  Thus, estate_cli memory requirements are
     193             :    larger than for estate_srv.
     194             : 
     195             :    However, clients are not vulnerable to handshake packet floods:
     196             :    - The packet count per handshake is limited
     197             :    - Peers cannot initiate connections to clients (clients will simply
     198             :      drop unsolicited packets)
     199             : 
     200             :    Thus, estate_cli is not optimized for memory use. */
     201             : 
     202             : struct fd_tls_estate_cli {
     203             :   /* TLS handshake handles are deliberately placed at the start.
     204             :    Allows for type punning between fd_quic_tls_hs_t and
     205             :    fd_tls_estate_{srv,cli}_t.  DO NOT MOVE.
     206             :    Type of handshake object depends on is_server. */
     207             :   fd_tls_estate_base_t base;
     208             : 
     209             :   uchar server_pubkey[ FD_X509_PUBKEY_MAX ]; /* leaf cert subjectPublicKey */
     210             :   ulong server_pubkey_len;
     211             :   uchar server_key_type; /* FD_TLS_KEY_{...} */
     212             :   uchar server_hs_secret[ 32 ];
     213             :   uchar client_hs_secret[ 32 ];
     214             :   uchar master_secret   [ 32 ];
     215             : 
     216             :   uchar client_cert       : 1;  /* 0=anon  1=client auth */
     217             :   uchar client_cert_empty : 1;  /* no compatible CertificateVerify scheme */
     218             :   uchar server_pubkey_pin : 1;  /* if 1, require cert to match server_pubkey */
     219             :   uchar alpn_negotiated   : 1;  /* if 1, server selected the offered ALPN */
     220             :   uchar cert_verify_err;         /* FD_X509_VERIFY_{...} if ca_store is set, else 0 */
     221             : 
     222             :   /* TCP middlebox compat (RFC 8446 Section 4.1.2) */
     223             :   uchar session_id[ 32 ];
     224             :   uchar session_id_sz;
     225             : 
     226             :   fd_sha256_t transcript;
     227             : };
     228             : 
     229             : typedef struct fd_tls_estate_cli fd_tls_estate_cli_t;
     230             : 
     231             : FD_PROTOTYPES_BEGIN
     232             : 
     233             : fd_tls_estate_cli_t *
     234             : fd_tls_estate_cli_new( void * mem );
     235             : 
     236             : /* fd_tls_estate_cli_delete destroys the handshake state.  Securely
     237             :    erases the object, which holds the handshake secrets, the master
     238             :    secret, and the transcript hash state.  See
     239             :    fd_tls_estate_srv_delete. */
     240             : 
     241             : static inline void *
     242        6300 : fd_tls_estate_cli_delete( fd_tls_estate_cli_t * estate ) {
     243        6300 :   if( FD_LIKELY( estate ) ) fd_memzero_explicit( estate, sizeof(fd_tls_estate_cli_t) );
     244        6300 :   return (void *)estate;
     245        6300 : }
     246             : 
     247             : FD_PROTOTYPES_END
     248             : 
     249             : 
     250             : /* Common *************************************************************/
     251             : 
     252             : union fd_tls_estate {
     253             :   fd_tls_estate_base_t base;
     254             :   fd_tls_estate_srv_t  srv;
     255             :   fd_tls_estate_cli_t  cli;
     256             : };
     257             : 
     258             : typedef union fd_tls_estate fd_tls_estate_t;
     259             : 
     260             : #endif /* HEADER_fd_src_waltz_tls_fd_tls_estate_h */

Generated by: LCOV version 1.14