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 */