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 0 : #define FD_TLSREC_SUCCESS (0) 49 0 : #define FD_TLSREC_ERR_OOM (1) /* out of memory (forgot to poll?) */ 50 0 : #define FD_TLSREC_ERR_PROTO (2) /* protocol error */ 51 0 : #define FD_TLSREC_ERR_STATE (3) /* unexpected state */ 52 0 : #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 0 : #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 0 : #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 0 : #define FD_TLS_REC_CHANGE_CIPHER_SPEC ((uchar)20) 127 0 : #define FD_TLS_REC_ALERT ((uchar)21) 128 0 : #define FD_TLS_REC_HANDSHAKE ((uchar)22) 129 0 : #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 0 : fd_tlsrec_hdr_bswap( fd_tlsrec_hdr_t * hdr ) { 148 0 : hdr->legacy_record_version = fd_ushort_bswap( hdr->legacy_record_version ); 149 0 : hdr->length = fd_ushort_bswap( hdr->length ); 150 0 : } 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. tls must not overlap conn. Call fd_tlsrec_conn_fini 157 : when the connection is discarded. */ 158 : 159 : fd_tlsrec_conn_t * 160 : fd_tlsrec_conn_init( fd_tlsrec_conn_t * conn, 161 : fd_tls_t const * tls, 162 : int is_server ); 163 : 164 : /* fd_tlsrec_conn_fini clears the connection, including its keys and 165 : buffered data. It does not send close_notify. Reinitialize conn 166 : before using it again. */ 167 : 168 : void 169 : fd_tlsrec_conn_fini( fd_tlsrec_conn_t * conn ); 170 : 171 : FD_FN_PURE int 172 : fd_tlsrec_conn_is_server( fd_tlsrec_conn_t const * conn ); 173 : 174 : FD_FN_PURE int 175 : fd_tlsrec_conn_is_ready( fd_tlsrec_conn_t const * conn ); 176 : 177 : FD_FN_PURE int 178 : fd_tlsrec_conn_is_failed( fd_tlsrec_conn_t const * conn ); 179 : 180 : /* fd_tlsrec_conn_rx consumes ciphertext from tcp_rx (may be NULL; 181 : any fragment size is fine), writes ciphertext to send to the peer 182 : into tcp_tx, and decrypted app data into app_rx. The size pointers 183 : are capacity in, bytes written out. Call once with tcp_rx==NULL on 184 : a fresh client conn to emit the ClientHello. Any non-zero return is 185 : fatal (including ERR_OOM). On a protocol error tcp_tx holds the 186 : fatal alert record for the peer (unless it did not fit), and the size 187 : is reported as usual: send it before closing the connection. Once 188 : the peer's close_notify has been received, remaining tcp_rx bytes are 189 : consumed and discarded and the call returns SUCCESS with no 190 : plaintext; check conn->rx_closed. 191 : 192 : Post-handshake, one call writes at most one 27 byte record to tcp_tx: 193 : a single KeyUpdate answering however many the peer requested (RFC 194 : 8446 Section 4.6.3). If tcp_tx lacks room for it the reply waits 195 : for the next fd_tlsrec_conn_rx or fd_tlsrec_conn_tx call. 196 : 197 : app_rx is also used as decrypt scratch, so bytes past the returned 198 : size are clobbered. One call produces at most tcp_rx bytes consumed 199 : plus FD_TLSREC_CAP bytes of plaintext. */ 200 : 201 : int 202 : fd_tlsrec_conn_rx( fd_tlsrec_conn_t * conn, 203 : fd_tlsrec_slice_t * tcp_rx, 204 : uchar * tcp_tx, 205 : ulong * tcp_tx_sz_p, 206 : uchar * app_rx, 207 : ulong * app_rx_sz_p ); 208 : 209 : /* fd_tlsrec_conn_tx encrypts outgoing application data into TLS records. 210 : Returns ERR_STATE before the handshake completes and after 211 : fd_tlsrec_conn_close. */ 212 : 213 : int 214 : fd_tlsrec_conn_tx( fd_tlsrec_conn_t * conn, 215 : uchar * tcp_tx, 216 : ulong * tcp_tx_sz_p, 217 : fd_tlsrec_slice_t * app_tx ); 218 : 219 : /* fd_tlsrec_conn_close writes a close_notify alert record (24 bytes) 220 : into tcp_tx and closes the write side (RFC 8446 Section 6.1): no 221 : further records are sent, receiving still works. Returns ERR_STATE 222 : if the handshake has not completed or the write side is closed 223 : already, ERR_OOM if tcp_tx is too small. */ 224 : 225 : int 226 : fd_tlsrec_conn_close( fd_tlsrec_conn_t * conn, 227 : uchar * tcp_tx, 228 : ulong * tcp_tx_sz_p ); 229 : 230 : /* fd_tlsrec_conn_key_update sends a TLS 1.3 KeyUpdate message and 231 : rotates the write traffic key. request_peer_update must be 0 or 1. */ 232 : 233 : int 234 : fd_tlsrec_conn_key_update( fd_tlsrec_conn_t * conn, 235 : uchar * tcp_tx, 236 : ulong * tcp_tx_sz_p, 237 : int request_peer_update ); 238 : 239 : FD_PROTOTYPES_END 240 : 241 : #endif /* HEADER_fd_src_waltz_tlsrec_fd_tlsrec_h */