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-21 04:28:53 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 (128 byte per handshake).
      88             : 
      89             :    *** Security ********************************************************
      90             : 
      91             :    fd_tls servers must handle handshake requests from untrusted clients,
      92             :    and thus require hardening.  A typical lifetime of an estate object
      93             :    is in the order of ~3 seconds (conn timeout).  This requires some
      94             :    care to avoid memory exhaustion attacks.
      95             : 
      96             :    For example, an off-the-shelf TLS implementation uses a handshake
      97             :    state size of about 10 KB (via multiple allocations on global heap).
      98             :    Assuming 3 second conn timeout, and flood rate of 1 million
      99             :    ClientHello msg/s,
     100             :    an attacker could indefinitely occupy ~30 GB of raw heap allocations!
     101             :    More concerning -- Global heap pressure will cause latent allocation
     102             :    failures in unrelated code, which might escalate to OOM kills.
     103             : 
     104             :    fd_tls_estate would only require 0.38 GB of memory for the same spam
     105             :    rate.  The memory of estate_srv objects is provided by the transport
     106             :    layer (e.g. QUIC or TLS record layer).  Each estate_srv object has
     107             :    static footprint.  This allows dense packing in custom memory arenas.
     108             : 
     109             :    This allows for robust handling of packet floods:  Even in the event
     110             :    the estate_srv arena is exhausted, the impact is limited to temporary
     111             :    inability to accept new connections.  Processing of established conns
     112             :    and unrelated code is unaffected.
     113             : 
     114             :    *** State Machine ***************************************************
     115             : 
     116             :    Each estate_srv object corresponds to an instance of a server-side
     117             :    TLS handshake state machine.  It is first instantiated when the
     118             :    client sent its ClientHello, the first message of a TLS handshake.
     119             : 
     120             :    Currently, only one external state exists:
     121             : 
     122             :      FD_TLS_HS_WAIT_FINISHED:  Processed ClientHello.
     123             : 
     124             :        At this point, the server has responded with all messages up to
     125             :        server Finished and is waiting for the client to respond with
     126             :        client Finished (and optionally, a certificate).
     127             : 
     128             :    State data contains:
     129             : 
     130             :    - The transcript hash state, which commits both sides to the entire
     131             :      sequence of handshake messages (such that they cannot be tampered
     132             :      with).
     133             : 
     134             :    - The client handshake secret, which is used to derive the "client
     135             :      Finished" verify data. */
     136             : 
     137             : struct fd_tls_estate_srv {
     138             :   /* TLS base (hs) handles are deliberately placed at the start.
     139             :    Allows for type punning between fd_quic_tls_hs_t and
     140             :    fd_tls_estate_{srv,cli}_t.  DO NOT MOVE.
     141             :    Type of handshake object depends on is_server. */
     142             :   fd_tls_estate_base_t base;
     143             : 
     144             :   uchar client_cert : 1;  /* 0: no client auth  1: client cert */
     145             :   uchar hello_retry : 1;
     146             : 
     147             :   fd_tls_transcript_t transcript;
     148             :   uchar               client_hs_secret[32];
     149             :   uchar               client_pubkey[32];
     150             : };
     151             : 
     152             : typedef struct fd_tls_estate_srv fd_tls_estate_srv_t;
     153             : 
     154             : 
     155             : /* Note:  When requesting a cert, the server should consider stopping
     156             :    after CertificateVerify to save compute resources (at the expense of
     157             :    more memory required to save state carried over from
     158             :    CertificateRequest...server Finished). */
     159             : 
     160             : FD_PROTOTYPES_BEGIN
     161             : 
     162             : /* fd_tls_estate_srv_new initializes an estate object for an incoming
     163             :    conn.  mem points to a memory region suitable for storing an
     164             :    fd_tls_estate_srv_t.  Returns cast of mem, which will be initialized
     165             :    to state FD_TLS_HS_START. */
     166             : 
     167             : fd_tls_estate_srv_t *
     168             : fd_tls_estate_srv_new( void * mem );
     169             : 
     170             : static inline void *
     171        6090 : fd_tls_estate_srv_delete( fd_tls_estate_srv_t * estate ) {
     172        6090 :   if( FD_LIKELY( estate ) ) fd_memzero_explicit( estate, sizeof(fd_tls_estate_srv_t) );
     173        6090 :   return (void *)estate;
     174        6090 : }
     175             : 
     176             : FD_PROTOTYPES_END
     177             : 
     178             : 
     179             : /* Client *************************************************************/
     180             : 
     181             : /* fd_tls_estate_cli contains TLS client handshake state while waiting
     182             :    for the server to send a message.
     183             : 
     184             :    TLS client side handshake state is considerably more complex than
     185             :    the server-side state.  Thus, estate_cli memory requirements are
     186             :    larger than for estate_srv.
     187             : 
     188             :    However, clients are not vulnerable to handshake packet floods:
     189             :    - The packet count per handshake is limited
     190             :    - Peers cannot initiate connections to clients (clients will simply
     191             :      drop unsolicited packets)
     192             : 
     193             :    Thus, estate_cli is not optimized for memory use. */
     194             : 
     195             : struct fd_tls_estate_cli {
     196             :   /* TLS handshake handles are deliberately placed at the start.
     197             :    Allows for type punning between fd_quic_tls_hs_t and
     198             :    fd_tls_estate_{srv,cli}_t.  DO NOT MOVE.
     199             :    Type of handshake object depends on is_server. */
     200             :   fd_tls_estate_base_t base;
     201             : 
     202             :   uchar server_pubkey[ FD_X509_PUBKEY_MAX ]; /* leaf cert subjectPublicKey */
     203             :   ulong server_pubkey_len;
     204             :   uchar server_key_type; /* FD_TLS_KEY_{...} */
     205             :   uchar server_hs_secret[ 32 ];
     206             :   uchar client_hs_secret[ 32 ];
     207             :   uchar master_secret   [ 32 ];
     208             : 
     209             :   uchar client_cert       : 1;  /* 0=anon  1=client auth */
     210             :   uchar client_cert_empty : 1;  /* no compatible CertificateVerify scheme */
     211             :   uchar server_pubkey_pin : 1;  /* if 1, require cert to match server_pubkey */
     212             :   uchar alpn_negotiated   : 1;  /* if 1, server selected the offered ALPN */
     213             :   uchar cert_verify_err;         /* FD_X509_VERIFY_{...} if ca_store is set, else 0 */
     214             : 
     215             :   /* TCP middlebox compat (RFC 8446 Section 4.1.2) */
     216             :   uchar session_id[ 32 ];
     217             :   uchar session_id_sz;
     218             : 
     219             :   fd_sha256_t transcript;
     220             : };
     221             : 
     222             : typedef struct fd_tls_estate_cli fd_tls_estate_cli_t;
     223             : 
     224             : FD_PROTOTYPES_BEGIN
     225             : 
     226             : fd_tls_estate_cli_t *
     227             : fd_tls_estate_cli_new( void * mem );
     228             : 
     229             : /* fd_tls_estate_cli_delete destroys the handshake state.  Securely
     230             :    erases the object, which holds the handshake secrets, the master
     231             :    secret, and the transcript hash state.  See
     232             :    fd_tls_estate_srv_delete. */
     233             : 
     234             : static inline void *
     235        6300 : fd_tls_estate_cli_delete( fd_tls_estate_cli_t * estate ) {
     236        6300 :   if( FD_LIKELY( estate ) ) fd_memzero_explicit( estate, sizeof(fd_tls_estate_cli_t) );
     237        6300 :   return (void *)estate;
     238        6300 : }
     239             : 
     240             : FD_PROTOTYPES_END
     241             : 
     242             : 
     243             : /* Common *************************************************************/
     244             : 
     245             : union fd_tls_estate {
     246             :   fd_tls_estate_base_t base;
     247             :   fd_tls_estate_srv_t  srv;
     248             :   fd_tls_estate_cli_t  cli;
     249             : };
     250             : 
     251             : typedef union fd_tls_estate fd_tls_estate_t;
     252             : 
     253             : #endif /* HEADER_fd_src_waltz_tls_fd_tls_estate_h */

Generated by: LCOV version 1.14