LCOV - code coverage report
Current view: top level - waltz/tlsrec - fd_tlsrec.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 15 15 100.0 %
Date: 2026-09-17 04:28:31 Functions: 2 25 8.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_tlsrec_fd_tlsrec_h
       2             : #define HEADER_fd_src_waltz_tlsrec_fd_tlsrec_h
       3             : 
       4             : /* fd_tlsrec.h provides the TLS 1.3 record layer, as used in TLS over
       5             :    TCP (RFC 8446).  The record layer provides a mechanism to transfer
       6             :    handshake messages, alerts, and user data over a reliable stream.
       7             :    It also implements data authentication and encryption.
       8             : 
       9             :    This implementation provides the simplest way to secure individual
      10             :    TCP connections.  It is not designed for high-performance use.  It
      11             :    should not be used in servers that handle many concurrent connections
      12             :    due to high memory footprint.
      13             : 
      14             :    ### Middlebox Compatibility Mode
      15             : 
      16             :    fd_tlsrec ignores incoming compatibility messages as described in
      17             :    RFC 8446, Appendix D.4.  It does not generate compatibility messages.
      18             : 
      19             :    ### Cryptography
      20             : 
      21             :    Each decryption step happens after a record has been fully received
      22             :    to allow for vectorized decryption.
      23             : 
      24             :    fd_tlsrec does not randomly pad records.  It thus leaks the exact
      25             :    size of every outgoing record.  Be careful when using with plaintexts
      26             :    where the size can be used as a side channel (see CVE-2019-4929).
      27             : 
      28             :    ### Integration
      29             : 
      30             :    fd_tlsrec offers a backend-agnostic non-blocking API.  It is usually
      31             :    used with sockets (e.g. for HTTPS snapshot downloading). */
      32             : 
      33             : #include "fd_tlsrec_frag.h"
      34             : #include "../tls/fd_tls.h"
      35             : #include "../tls/fd_tls_estate.h"
      36             : #include "../../ballet/aes/fd_aes_gcm.h"
      37             : #include <stddef.h>
      38             : 
      39             : /* fd_tlsrec_conn drives a TLS 1.3 connection over a reliable byte
      40             :    stream.  The conn footprint is ~100 kB order of magnitude. */
      41             : 
      42             : struct fd_tlsrec_conn;
      43             : typedef struct fd_tlsrec_conn fd_tlsrec_conn_t;
      44             : 
      45             : /* FD_TLSREC_{SUCCESS,ERR_{...}} indicate error values returned by
      46             :    most API functions. */
      47             : 
      48   513517789 : #define FD_TLSREC_SUCCESS     (0)
      49         921 : #define FD_TLSREC_ERR_OOM     (1)  /* out of memory (forgot to poll?) */
      50         579 : #define FD_TLSREC_ERR_PROTO   (2)  /* protocol error */
      51         351 : #define FD_TLSREC_ERR_STATE   (3)  /* unexpected state */
      52          15 : #define FD_TLSREC_ERR_CRYPTO  (4)  /* crypto error */
      53             : 
      54             : /* FD_TLSREC_KEY_UPDATE_SEQ is the number of records encrypted under one
      55             :    application write key before fd_tlsrec_conn_tx rotates it with a
      56             :    KeyUpdate.  RFC 8446 Section 5.5 bounds AES-GCM at ~2^24.5 full-size
      57             :    records per key. */
      58             : 
      59           6 : #define FD_TLSREC_KEY_UPDATE_SEQ (1UL<<24)
      60             : 
      61             : /* fd_tlsrec_keys holds symmetric keys for a given encryption layer. */
      62             : 
      63             : struct __attribute__((aligned(FD_AES_GCM_ALIGN))) fd_tlsrec_keys {
      64             :   /* rebuilt on key change */
      65             :   fd_aes_gcm_t read_gcm;
      66             :   fd_aes_gcm_t write_gcm;
      67             : 
      68             :   uchar read_secret [ 32 ];
      69             :   uchar write_secret[ 32 ];
      70             :   uchar read_key    [ 16 ];
      71             :   uchar read_iv     [ 12 ];
      72             :   uchar write_key   [ 16 ];
      73             :   uchar write_iv    [ 12 ];
      74             : };
      75             : 
      76             : typedef struct fd_tlsrec_keys fd_tlsrec_keys_t;
      77             : 
      78             : /* FD_TLSREC_HS_MSG_CAP is the max supported handshake message size. */
      79             : 
      80       32001 : #define FD_TLSREC_HS_MSG_CAP (0x10000UL)
      81             : 
      82             : /* fd_tlsrec_hs_rbuf reassembles incoming handshakes messages one at a
      83             :    time.  (private API) */
      84             : 
      85             : struct fd_tlsrec_hs_rbuf {
      86             :   uchar buf[ FD_TLSREC_HS_MSG_CAP ];
      87             :   ulong sz;
      88             : };
      89             : 
      90             : typedef struct fd_tlsrec_hs_rbuf fd_tlsrec_hs_rbuf_t;
      91             : 
      92             : /* fd_tlsrec_buf_t defragments incoming TLS record data (private API) */
      93             : 
      94             : struct __attribute__((aligned(16UL))) fd_tlsrec_buf {
      95             :   uchar buf[ FD_TLSREC_CAP ];
      96             :   ulong sz;
      97             : };
      98             : 
      99             : typedef struct fd_tlsrec_buf fd_tlsrec_buf_t;
     100             : 
     101             : struct fd_tlsrec_conn {
     102             :   fd_tls_t tls;  /* TODO dedup across conns for better memory use */
     103             :   fd_tls_secrets_fn_t secrets_fn;  /* optional key-log observer */
     104             : 
     105             :   fd_tlsrec_keys_t keys[2]; /* 0=handshake 1=app */
     106             :   fd_tls_estate_t  hs;
     107             : 
     108             :   fd_tlsrec_buf_t     rec_buf; /* reassembly of TLS records */
     109             :   fd_tlsrec_hs_rbuf_t hs_rbuf; /* reassembly of TLS handshake messages */
     110             : 
     111             :   ulong read_seq;  /* Incoming encrypted record counter */
     112             :   ulong write_seq; /* Outgoing encrypted record counter */
     113             : 
     114             :   uchar rx_closed; /* 1 if peer sent close_notify: no more plaintext is
     115             :                       delivered (RFC 8446 Section 6.1), tx still works */
     116             :   uchar key_update_pending; /* 1 if the peer requested a KeyUpdate that
     117             :                                has not been answered yet */
     118             :   uchar tx_level;  /* FD_TLS_LEVEL_{INITIAL,HANDSHAKE,APPLICATION}: the
     119             :                       encryption level an alert sent now would use */
     120             :   uchar tx_closed; /* 1 once a fatal alert or close_notify went out: no
     121             :                       more records are sent */
     122             : };
     123             : 
     124             : /* TLS v1.3 record content types */
     125             : 
     126           9 : #define FD_TLS_REC_CHANGE_CIPHER_SPEC ((uchar)20)
     127         498 : #define FD_TLS_REC_ALERT              ((uchar)21)
     128       38214 : #define FD_TLS_REC_HANDSHAKE          ((uchar)22)
     129      156378 : #define FD_TLS_REC_APPLICATION_DATA   ((uchar)23)
     130             : 
     131             : /* fd_tlsrec_hdr_t is the TLS v1.3 record header. */
     132             : 
     133             : struct __attribute__((packed)) fd_tlsrec_hdr {
     134             :   uchar  content_type;           /* FD_TLS_REC_{...} */
     135             :   ushort legacy_record_version;  /* sent as 0x0303, ignored on receive */
     136             :   ushort length;
     137             : };
     138             : 
     139             : typedef struct fd_tlsrec_hdr fd_tlsrec_hdr_t;
     140             : 
     141             : FD_PROTOTYPES_BEGIN
     142             : 
     143             : FD_FN_PURE char const *
     144             : fd_tlsrec_strerror( int err );
     145             : 
     146             : static inline void
     147   127889612 : fd_tlsrec_hdr_bswap( fd_tlsrec_hdr_t * hdr ) {
     148   127889612 :   hdr->legacy_record_version = fd_ushort_bswap( hdr->legacy_record_version );
     149   127889612 :   hdr->length                = fd_ushort_bswap( hdr->length );
     150   127889612 : }
     151             : 
     152             : /* fd_tlsrec_conn_init initializes a connection object.  tls points to
     153             :    the TLS instance parameters.  tls->secrets_fn, if non-NULL, observes
     154             :    generated traffic secrets after fd_tlsrec installs them.  The
     155             :    tls->sendmsg_fn callback is ignored.  is_server is 1 if conn operates
     156             :    in server mode. */
     157             : 
     158             : fd_tlsrec_conn_t *
     159             : fd_tlsrec_conn_init( fd_tlsrec_conn_t * conn,
     160             :                      fd_tls_t const *   tls,
     161             :                      int                is_server );
     162             : 
     163             : FD_FN_PURE int
     164             : fd_tlsrec_conn_is_server( fd_tlsrec_conn_t const * conn );
     165             : 
     166             : FD_FN_PURE int
     167             : fd_tlsrec_conn_is_ready( fd_tlsrec_conn_t const * conn );
     168             : 
     169             : FD_FN_PURE int
     170             : fd_tlsrec_conn_is_failed( fd_tlsrec_conn_t const * conn );
     171             : 
     172             : /* fd_tlsrec_conn_rx consumes ciphertext from tcp_rx (may be NULL;
     173             :    any fragment size is fine), writes ciphertext to send to the peer
     174             :    into tcp_tx, and decrypted app data into app_rx.  The size pointers
     175             :    are capacity in, bytes written out.  Call once with tcp_rx==NULL on
     176             :    a fresh client conn to emit the ClientHello.  Any non-zero return is
     177             :    fatal (including ERR_OOM).  On a protocol error tcp_tx holds the
     178             :    fatal alert record for the peer (unless it did not fit), and the size
     179             :    is reported as usual: send it before closing the connection.  Once
     180             :    the peer's close_notify has been received, remaining tcp_rx bytes are
     181             :    consumed and discarded and the call returns SUCCESS with no
     182             :    plaintext; check conn->rx_closed.
     183             : 
     184             :    Post-handshake, one call writes at most one 27 byte record to tcp_tx:
     185             :    a single KeyUpdate answering however many the peer requested (RFC
     186             :    8446 Section 4.6.3).  If tcp_tx lacks room for it the reply waits
     187             :    for the next fd_tlsrec_conn_rx or fd_tlsrec_conn_tx call.
     188             : 
     189             :    app_rx is also used as decrypt scratch, so bytes past the returned
     190             :    size are clobbered.  One call produces at most tcp_rx bytes consumed
     191             :    plus FD_TLSREC_CAP bytes of plaintext. */
     192             : 
     193             : int
     194             : fd_tlsrec_conn_rx( fd_tlsrec_conn_t *  conn,
     195             :                    fd_tlsrec_slice_t * tcp_rx,
     196             :                    uchar *             tcp_tx,
     197             :                    ulong *             tcp_tx_sz_p,
     198             :                    uchar *             app_rx,
     199             :                    ulong *             app_rx_sz_p );
     200             : 
     201             : /* fd_tlsrec_conn_tx encrypts outgoing application data into TLS records.
     202             :    Returns ERR_STATE before the handshake completes and after
     203             :    fd_tlsrec_conn_close. */
     204             : 
     205             : int
     206             : fd_tlsrec_conn_tx( fd_tlsrec_conn_t *  conn,
     207             :                    uchar *             tcp_tx,
     208             :                    ulong *             tcp_tx_sz_p,
     209             :                    fd_tlsrec_slice_t * app_tx );
     210             : 
     211             : /* fd_tlsrec_conn_close writes a close_notify alert record (24 bytes)
     212             :    into tcp_tx and closes the write side (RFC 8446 Section 6.1): no
     213             :    further records are sent, receiving still works.  Returns ERR_STATE
     214             :    if the handshake has not completed or the write side is closed
     215             :    already, ERR_OOM if tcp_tx is too small. */
     216             : 
     217             : int
     218             : fd_tlsrec_conn_close( fd_tlsrec_conn_t * conn,
     219             :                       uchar *            tcp_tx,
     220             :                       ulong *            tcp_tx_sz_p );
     221             : 
     222             : /* fd_tlsrec_conn_key_update sends a TLS 1.3 KeyUpdate message and
     223             :    rotates the write traffic key.  request_peer_update must be 0 or 1. */
     224             : 
     225             : int
     226             : fd_tlsrec_conn_key_update( fd_tlsrec_conn_t * conn,
     227             :                            uchar *            tcp_tx,
     228             :                            ulong *            tcp_tx_sz_p,
     229             :                            int                request_peer_update );
     230             : 
     231             : FD_PROTOTYPES_END
     232             : 
     233             : #endif /* HEADER_fd_src_waltz_tlsrec_fd_tlsrec_h */

Generated by: LCOV version 1.14