Line data Source code
1 : #include "fd_tlsrec.h"
2 : #include "../tls/fd_tls.h"
3 : #include "../tls/fd_tls_proto.h"
4 : #include "../../ballet/aes/fd_aes_gcm.h"
5 :
6 : FD_FN_PURE char const *
7 0 : fd_tlsrec_strerror( int err ) {
8 0 : switch( err ) {
9 0 : case FD_TLSREC_SUCCESS: return "success";
10 0 : case FD_TLSREC_ERR_OOM: return "out of memory";
11 0 : case FD_TLSREC_ERR_PROTO: return "protocol error";
12 0 : case FD_TLSREC_ERR_STATE: return "unexpected state";
13 0 : case FD_TLSREC_ERR_CRYPTO: return "crypto error";
14 0 : default: return "unknown";
15 0 : }
16 0 : }
17 :
18 : /* RFC 8446 §5.3: per-record nonce = write_iv XOR (0-padded seq) */
19 :
20 : static void
21 167424 : fd_tlsrec_nonce( uchar iv[ static 12 ], uchar const base[ static 12 ], ulong seq ) {
22 167424 : memcpy( iv, base, 12 );
23 1506816 : for( uint i=0; i<8; i++ ) iv[11-i] ^= (uchar)(seq>>(8*i));
24 167424 : }
25 :
26 : /* RFC 8446 §5.2: AEAD decrypt/encrypt with 5-byte record header as AAD */
27 :
28 : static int
29 : fd_tlsrec_decrypt( uchar * p, uchar const * c, ulong sz,
30 : fd_tlsrec_hdr_t const * hdr, ulong seq,
31 144948 : uchar const tag[16], fd_tlsrec_keys_t * k ) {
32 144948 : uchar iv[12]; fd_tlsrec_nonce( iv, k->read_iv, seq );
33 144948 : fd_aes_gcm_set_iv( &k->read_gcm, iv );
34 144948 : return fd_aes_gcm_decrypt( &k->read_gcm, c, p, sz, (uchar const *)hdr, sizeof(*hdr), tag );
35 144948 : }
36 :
37 : static void
38 : fd_tlsrec_encrypt( uchar * c, uchar const * p, ulong sz,
39 : fd_tlsrec_hdr_t const * hdr, ulong seq,
40 22476 : uchar tag[16], fd_tlsrec_keys_t * k ) {
41 22476 : uchar iv[12]; fd_tlsrec_nonce( iv, k->write_iv, seq );
42 22476 : fd_aes_gcm_set_iv( &k->write_gcm, iv );
43 22476 : fd_aes_gcm_encrypt( &k->write_gcm, c, p, sz, (uchar const *)hdr, sizeof(*hdr), tag );
44 22476 : }
45 :
46 : /* Transmit path ********************************************************/
47 :
48 : static int
49 : fd_tlsrec_tx( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * tcp_tx,
50 : uchar const * payload, ulong payload_sz,
51 27276 : uint content_type, uint enc_level ) {
52 :
53 27276 : if( FD_UNLIKELY( conn->tx_closed ) ) return FD_TLSREC_ERR_STATE;
54 :
55 : /* Size checks precede the first write so that on ERR_OOM tcp_tx is
56 : untouched and the caller can retry later. */
57 :
58 27276 : if( enc_level==FD_TLS_LEVEL_INITIAL ) {
59 4791 : if( FD_UNLIKELY( fd_tlsrec_slice_sz(tcp_tx) < sizeof(fd_tlsrec_hdr_t)+payload_sz ) )
60 0 : return FD_TLSREC_ERR_OOM;
61 4791 : fd_tlsrec_hdr_t * hdr = fd_type_pun( fd_tlsrec_slice_pop( tcp_tx, sizeof(fd_tlsrec_hdr_t) ) );
62 4791 : *hdr = (fd_tlsrec_hdr_t){
63 4791 : .content_type = (uchar)content_type,
64 4791 : .legacy_record_version = fd_ushort_bswap(0x0303),
65 4791 : .length = fd_ushort_bswap((ushort)payload_sz),
66 4791 : };
67 4791 : fd_memcpy( fd_tlsrec_slice_pop(tcp_tx, payload_sz), payload, payload_sz );
68 4791 : return FD_TLSREC_SUCCESS;
69 4791 : }
70 :
71 : /* Encrypted record: TLSInnerPlaintext = payload || content_type */
72 22485 : ulong inner_sz = payload_sz + 1 + FD_AES_GCM_TAG_SZ;
73 22485 : ulong outer_sz = sizeof(fd_tlsrec_hdr_t) + inner_sz;
74 22485 : if( FD_UNLIKELY( outer_sz > FD_TLSREC_CAP || outer_sz > fd_tlsrec_slice_sz(tcp_tx) ) )
75 9 : return FD_TLSREC_ERR_OOM;
76 :
77 22476 : fd_tlsrec_hdr_t * hdr = fd_type_pun( fd_tlsrec_slice_pop( tcp_tx, sizeof(fd_tlsrec_hdr_t) ) );
78 22476 : *hdr = (fd_tlsrec_hdr_t){
79 22476 : .content_type = FD_TLS_REC_APPLICATION_DATA,
80 22476 : .legacy_record_version = fd_ushort_bswap(0x0303),
81 22476 : .length = fd_ushort_bswap((ushort)inner_sz),
82 22476 : };
83 :
84 22476 : fd_tlsrec_keys_t * keys = &conn->keys[ enc_level==FD_TLS_LEVEL_APPLICATION ];
85 22476 : uchar * c = fd_tlsrec_slice_pop( tcp_tx, payload_sz+1 );
86 22476 : uchar * tag = fd_tlsrec_slice_pop( tcp_tx, FD_AES_GCM_TAG_SZ );
87 22476 : fd_memcpy( c, payload, payload_sz );
88 22476 : c[ payload_sz ] = (uchar)content_type;
89 22476 : fd_tlsrec_encrypt( c, c, payload_sz+1, hdr, conn->write_seq, tag, keys );
90 22476 : conn->write_seq++;
91 22476 : return FD_TLSREC_SUCCESS;
92 22485 : }
93 :
94 : /* RFC 8446 §7.3 */
95 :
96 : static void
97 : fd_tlsrec_derive_traffic_key( fd_aes_gcm_t * gcm,
98 : uchar key[ static 16 ],
99 : uchar iv[ static 12 ],
100 22068 : uchar const secret[ static 32 ] ) {
101 22068 : fd_tls_hkdf_expand_label( key, 16UL, secret, "key", 3UL, NULL, 0UL );
102 22068 : fd_tls_hkdf_expand_label( iv, 12UL, secret, "iv", 2UL, NULL, 0UL );
103 22068 : fd_aes_gcm_init( gcm, key, 16UL, iv );
104 22068 : }
105 :
106 : static void
107 : fd_tlsrec_update_traffic_secret( fd_aes_gcm_t * gcm,
108 : uchar secret[ static 32 ],
109 : uchar key[ static 16 ],
110 5718 : uchar iv[ static 12 ] ) {
111 5718 : uchar next_secret[ 32 ];
112 5718 : fd_tls_hkdf_expand_label( next_secret, 32UL, secret, "traffic upd", 11UL, NULL, 0UL );
113 5718 : fd_memcpy( secret, next_secret, 32UL );
114 5718 : fd_tlsrec_derive_traffic_key( gcm, key, iv, secret );
115 5718 : }
116 :
117 : static int
118 : fd_tlsrec_send_key_update( fd_tlsrec_conn_t * conn,
119 : fd_tlsrec_slice_t * tcp_tx,
120 2859 : uchar request_peer_update ) {
121 2859 : struct __attribute__((packed)) {
122 2859 : fd_tls_msg_hdr_t hdr;
123 2859 : uchar request_update;
124 2859 : } msg = {
125 2859 : .hdr = {
126 2859 : .type = FD_TLS_MSG_KEY_UPDATE,
127 2859 : .sz = fd_uint_to_tls_u24( 1U ),
128 2859 : },
129 2859 : .request_update = request_peer_update,
130 2859 : };
131 2859 : fd_tls_msg_hdr_bswap( &msg.hdr );
132 :
133 2859 : int rc = fd_tlsrec_tx( conn, tcp_tx, (uchar const *)&msg, sizeof(msg),
134 2859 : FD_TLS_REC_HANDSHAKE, FD_TLS_LEVEL_APPLICATION );
135 2859 : if( FD_UNLIKELY( rc ) ) return rc;
136 :
137 2853 : fd_tlsrec_keys_t * keys = &conn->keys[1];
138 2853 : fd_tlsrec_update_traffic_secret( &keys->write_gcm, keys->write_secret, keys->write_key,
139 2853 : keys->write_iv );
140 2853 : conn->write_seq = 0UL;
141 2853 : return FD_TLSREC_SUCCESS;
142 2859 : }
143 :
144 : /* fd_tlsrec_answer_key_update sends the one KeyUpdate that answers every
145 : update the peer requested since the last one (RFC 8446 Section 4.6.3
146 : lets a silent receiver collapse them). Not having room in tcp_tx is
147 : not an error: the reply stays pending. */
148 :
149 : static int
150 128346408 : fd_tlsrec_answer_key_update( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * tcp_tx ) {
151 128346408 : if( conn->tx_closed || !conn->key_update_pending ) return FD_TLSREC_SUCCESS;
152 912 : int rc = fd_tlsrec_send_key_update( conn, tcp_tx, 0U );
153 912 : if( rc==FD_TLSREC_ERR_OOM ) return FD_TLSREC_SUCCESS;
154 906 : if( FD_UNLIKELY( rc ) ) return rc;
155 906 : conn->key_update_pending = 0;
156 906 : return FD_TLSREC_SUCCESS;
157 906 : }
158 :
159 : /* Handshake message reassembly *****************************************/
160 :
161 : static inline ulong
162 31998 : fd_tlsrec_peek_msg_sz( uchar const * buf, ulong buf_sz ) {
163 31998 : if( buf_sz < sizeof(fd_tls_msg_hdr_t) ) return 0;
164 31998 : fd_tls_msg_hdr_t hdr;
165 31998 : fd_memcpy( &hdr, buf, sizeof(hdr) );
166 31998 : fd_tls_msg_hdr_bswap( &hdr );
167 31998 : ulong payload = fd_tls_u24_to_uint( hdr.sz );
168 31998 : ulong msg_sz = sizeof(fd_tls_msg_hdr_t) + payload;
169 31998 : return ( msg_sz <= FD_TLSREC_HS_MSG_CAP ) ? msg_sz : 0;
170 31998 : }
171 :
172 : /* Thread-local buffer coalescing handshake messages into one record */
173 : static FD_TL struct {
174 : uchar buf[ FD_TLSREC_CAP ];
175 : uint sz;
176 : uint enc_level;
177 : fd_tlsrec_slice_t tcp_tx;
178 : } hs_tbuf;
179 :
180 : static void
181 128346957 : hs_tbuf_init( fd_tlsrec_slice_t const * tx ) {
182 128346957 : hs_tbuf.sz = 0U;
183 128346957 : hs_tbuf.tcp_tx = *tx;
184 128346957 : }
185 :
186 : static int
187 15342 : hs_tbuf_push( uchar const * msg, ulong msg_sz, uint enc_level ) {
188 15342 : if( hs_tbuf.sz && hs_tbuf.enc_level != enc_level ) return FD_TLSREC_ERR_PROTO;
189 15342 : if( hs_tbuf.sz + msg_sz > FD_TLSREC_CAP ) return FD_TLSREC_ERR_OOM;
190 15342 : fd_memcpy( hs_tbuf.buf + hs_tbuf.sz, msg, msg_sz );
191 15342 : hs_tbuf.sz += (uint)msg_sz;
192 15342 : hs_tbuf.enc_level = enc_level;
193 15342 : return FD_TLSREC_SUCCESS;
194 15342 : }
195 :
196 : /* Emits coalesced handshake messages as records of at most 2^14 bytes
197 : of plaintext (RFC 8446 Section 5.1). */
198 :
199 : static int
200 128355072 : hs_tbuf_flush( fd_tlsrec_conn_t * conn ) {
201 128355072 : ulong off = 0UL;
202 128363736 : while( off < hs_tbuf.sz ) {
203 8664 : ulong sz = fd_ulong_min( hs_tbuf.sz - off, FD_TLSREC_PLAINTEXT_MAX );
204 8664 : int rc = fd_tlsrec_tx( conn, &hs_tbuf.tcp_tx, hs_tbuf.buf + off, sz,
205 8664 : FD_TLS_REC_HANDSHAKE, hs_tbuf.enc_level );
206 8664 : if( FD_UNLIKELY( rc ) ) { hs_tbuf.sz = 0; return rc; }
207 8664 : off += sz;
208 8664 : }
209 : /* The server moves off plaintext when its encrypted flight goes out.
210 : Never lower the write epoch or advance it on an empty flush, such
211 : as when discarding CCS before ServerHello. */
212 128355072 : if( hs_tbuf.sz && hs_tbuf.enc_level==FD_TLS_LEVEL_HANDSHAKE && conn->tx_level<FD_TLS_LEVEL_HANDSHAKE )
213 819 : conn->tx_level = FD_TLS_LEVEL_HANDSHAKE;
214 128355072 : hs_tbuf.sz = 0;
215 128355072 : return FD_TLSREC_SUCCESS;
216 128355072 : }
217 :
218 : /* fd_tlsrec_send_alert writes an alert record (RFC 8446 Section 6) at
219 : the current write level into tcp_tx and closes the write side. */
220 :
221 : static int
222 399 : fd_tlsrec_send_alert( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * tcp_tx, uchar level, uchar desc ) {
223 399 : uchar const alert[2] = { level, desc };
224 399 : int rc = fd_tlsrec_tx( conn, tcp_tx, alert, sizeof(alert), FD_TLS_REC_ALERT, conn->tx_level );
225 399 : if( FD_UNLIKELY( rc ) ) return rc;
226 396 : conn->tx_closed = 1;
227 396 : conn->key_update_pending = 0;
228 396 : return FD_TLSREC_SUCCESS;
229 399 : }
230 :
231 : /* fd_tlsrec_fail marks conn failed and queues the fatal alert for the
232 : peer behind whatever fd_tlsrec_conn_rx already produced in this call.
233 : Pending handshake output is dropped: the peer only needs the alert.
234 : Called from the receive path only, where hs_tbuf is initialized. */
235 :
236 : static int
237 381 : fd_tlsrec_fail( fd_tlsrec_conn_t * conn, uint alert, ushort reason ) {
238 381 : conn->hs.base.state = FD_TLS_HS_FAIL;
239 381 : conn->hs.base.reason = reason;
240 381 : FD_LOG_WARNING(( "TLS connection failed (alert %u-%s; reason %u-%s)",
241 381 : alert, fd_tls_alert_cstr( alert ),
242 381 : reason, fd_tls_reason_cstr( reason ) ));
243 381 : hs_tbuf.sz = 0U;
244 381 : if( !conn->tx_closed ) fd_tlsrec_send_alert( conn, &hs_tbuf.tcp_tx, 2U, (uchar)alert );
245 381 : return FD_TLSREC_ERR_PROTO;
246 381 : }
247 :
248 : /* fd_tlsrec_alert_rx handles an alert record payload (plaintext or
249 : decrypted). RFC 8446 Section 5.1: alerts may not be fragmented or
250 : coalesced, so the payload is exactly two bytes. close_notify marks
251 : the receive side closed (RFC 8446 Section 6.1). user_canceled is
252 : ignored; every other alert is fatal regardless of the level byte. */
253 :
254 : static int
255 315 : fd_tlsrec_alert_rx( fd_tlsrec_conn_t * conn, uchar const * pt, ulong p_sz ) {
256 315 : if( FD_UNLIKELY( p_sz!=2UL ) )
257 60 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_ALERT_PARSE );
258 255 : uint level = pt[0];
259 255 : uint desc = pt[1];
260 255 : if( desc==FD_TLS_ALERT_CLOSE_NOTIFY ) {
261 36 : conn->rx_closed = 1;
262 36 : return FD_TLSREC_SUCCESS;
263 36 : }
264 219 : if( desc==FD_TLS_ALERT_USER_CANCELED ) return FD_TLSREC_SUCCESS;
265 168 : FD_LOG_WARNING(( "TLS peer sent alert (level %u; alert %u-%s)",
266 168 : level, desc, fd_tls_alert_cstr( desc ) ));
267 168 : conn->hs.base.state = FD_TLS_HS_FAIL;
268 168 : conn->hs.base.reason = FD_TLS_REASON_PEER_ALERT;
269 168 : return FD_TLSREC_ERR_PROTO;
270 219 : }
271 :
272 : static int
273 8922 : fd_tlsrec_post_hs_rx( fd_tlsrec_conn_t * conn, uchar const * msg, ulong msg_sz ) {
274 8922 : switch( msg[0] ) {
275 :
276 2874 : case FD_TLS_MSG_KEY_UPDATE: {
277 2874 : if( FD_UNLIKELY( msg_sz!=sizeof(fd_tls_msg_hdr_t)+1UL ) )
278 6 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_KEY_UPDATE_PARSE );
279 2868 : if( FD_UNLIKELY( msg[4]>1U ) )
280 3 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_ILLEGAL_PARAMETER, FD_TLS_REASON_KEY_UPDATE_PARSE );
281 :
282 2865 : fd_tlsrec_keys_t * keys = &conn->keys[1];
283 2865 : fd_tlsrec_update_traffic_secret( &keys->read_gcm, keys->read_secret, keys->read_key,
284 2865 : keys->read_iv );
285 2865 : conn->read_seq = 0UL;
286 :
287 2865 : if( msg[4] && !conn->tx_closed ) conn->key_update_pending = 1;
288 2865 : return FD_TLSREC_SUCCESS;
289 2868 : }
290 :
291 6048 : case FD_TLS_MSG_NEW_SESSION_TICKET:
292 6048 : if( !conn->hs.base.server ) return FD_TLSREC_SUCCESS;
293 0 : __attribute__((fallthrough));
294 :
295 0 : default:
296 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_POST_HS_MSG );
297 8922 : }
298 8922 : }
299 :
300 : /* Handshake message delivery *******************************************/
301 :
302 : /* fd_tlsrec_read_keys_plaintext is 1 while the peer's records arrive
303 : unencrypted (before it has our ServerHello, or before we have its). */
304 :
305 : static inline int
306 196872 : fd_tlsrec_read_keys_plaintext( fd_tlsrec_conn_t const * conn ) {
307 196872 : uint state = conn->hs.base.state;
308 196872 : return state==FD_TLS_HS_START || state==FD_TLS_HS_WAIT_SH;
309 196872 : }
310 :
311 : /* fd_tlsrec_hs_rx consumes handshake bytes from rx, at most one message
312 : completion per call. *key_change is set to 1 if that message changed
313 : the read keys (ServerHello, Finished, KeyUpdate): RFC 8446 Section
314 : 5.1 requires such a message to end its record. */
315 :
316 : static int
317 32031 : fd_tlsrec_hs_rx( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * rx, uint enc_level, int * key_change ) {
318 32031 : fd_tlsrec_hs_rbuf_t * rbuf = &conn->hs_rbuf;
319 32031 : *key_change = 0;
320 :
321 : /* Reassemble message header */
322 32031 : if( rbuf->sz < sizeof(fd_tls_msg_hdr_t) ) {
323 30879 : ulong want = sizeof(fd_tls_msg_hdr_t);
324 30879 : ulong have = fd_ulong_min( want, rbuf->sz + fd_tlsrec_slice_sz(rx) );
325 30879 : ulong n = have - rbuf->sz;
326 30879 : fd_memcpy( rbuf->buf + rbuf->sz, rx->data, n );
327 30879 : rbuf->sz = have; rx->data += n;
328 30879 : if( have < want ) return FD_TLSREC_SUCCESS;
329 30879 : }
330 :
331 : /* Reassemble message body */
332 31998 : ulong msg_sz = fd_tlsrec_peek_msg_sz( rbuf->buf, sizeof(fd_tls_msg_hdr_t) );
333 31998 : if( FD_UNLIKELY( !msg_sz ) )
334 3 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_HS_MSG_SIZE );
335 :
336 31995 : ulong have = fd_ulong_min( msg_sz, rbuf->sz + fd_tlsrec_slice_sz(rx) );
337 31995 : ulong n = have - rbuf->sz;
338 31995 : fd_memcpy( rbuf->buf + rbuf->sz, rx->data, n );
339 31995 : rbuf->sz = have; rx->data += n;
340 31995 : if( have < msg_sz ) return FD_TLSREC_SUCCESS;
341 :
342 30840 : rbuf->sz = 0;
343 :
344 30840 : if( conn->hs.base.state == FD_TLS_HS_CONNECTED ) {
345 8922 : *key_change = rbuf->buf[0]==FD_TLS_MSG_KEY_UPDATE;
346 8922 : return fd_tlsrec_post_hs_rx( conn, rbuf->buf, msg_sz );
347 8922 : }
348 :
349 : /* Dispatch to fd_tls */
350 21918 : int plaintext_0 = fd_tlsrec_read_keys_plaintext( conn );
351 21918 : long rc = fd_tls_handshake( &conn->tls, &conn->hs, rbuf->buf, msg_sz, enc_level );
352 21918 : if( FD_UNLIKELY( rc<0 ) ) return fd_tlsrec_fail( conn, (uint)(-rc), conn->hs.base.reason );
353 21726 : if( FD_UNLIKELY( (ulong)rc != msg_sz ) )
354 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_DECODE_ERROR, FD_TLS_REASON_HS_MSG_SIZE );
355 21726 : if( conn->hs.base.state == FD_TLS_HS_CONNECTED ) {
356 3957 : conn->read_seq = 0;
357 3957 : if( !conn->hs.base.server ) {
358 3219 : conn->write_seq = 0;
359 3219 : conn->tx_level = FD_TLS_LEVEL_APPLICATION;
360 3219 : }
361 3957 : *key_change = 1;
362 3957 : }
363 21726 : if( plaintext_0 != fd_tlsrec_read_keys_plaintext( conn ) ) *key_change = 1;
364 21726 : return FD_TLSREC_SUCCESS;
365 21726 : }
366 :
367 : /* fd_tlsrec_hs_rx_record delivers the handshake payload of one record. */
368 :
369 : static int
370 31032 : fd_tlsrec_hs_rx_record( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * payload, uint enc_level ) {
371 31032 : if( FD_UNLIKELY( fd_tlsrec_slice_is_empty(payload) ) )
372 9 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_HS_MSG_SIZE );
373 62844 : while( !fd_tlsrec_slice_is_empty(payload) ) {
374 32031 : int key_change;
375 32031 : int rc = fd_tlsrec_hs_rx( conn, payload, enc_level, &key_change );
376 32031 : if( FD_UNLIKELY( rc ) ) return rc;
377 : /* RFC 8446 Section 5.1: a message that changes keys ends its record.
378 : Anything after it in this record was authenticated under the old
379 : keys yet would be handled under the new ones. This also bounds
380 : KeyUpdate processing to one per record. */
381 31827 : if( FD_UNLIKELY( key_change && !fd_tlsrec_slice_is_empty(payload) ) )
382 6 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_HS_KEY_CHANGE );
383 31827 : }
384 30813 : return FD_TLSREC_SUCCESS;
385 31023 : }
386 :
387 : /* TLS callbacks ********************************************************/
388 :
389 : FD_FN_CONST static fd_tlsrec_conn_t *
390 23517 : cb_ctx( void const * hs ) {
391 23517 : return (fd_tlsrec_conn_t *)((ulong)hs - offsetof(fd_tlsrec_conn_t, hs));
392 23517 : }
393 :
394 : static void
395 8175 : cb_secrets( void const * hs, void const * rx_secret, void const * tx_secret, uint level ) {
396 8175 : fd_tlsrec_conn_t * conn = cb_ctx( hs );
397 :
398 8175 : fd_tlsrec_keys_t * out = &conn->keys[ level==FD_TLS_LEVEL_APPLICATION ];
399 8175 : fd_memcpy( out->read_secret, rx_secret, 32UL );
400 8175 : fd_memcpy( out->write_secret, tx_secret, 32UL );
401 :
402 8175 : fd_tlsrec_derive_traffic_key( &out->read_gcm, out->read_key, out->read_iv, rx_secret );
403 8175 : fd_tlsrec_derive_traffic_key( &out->write_gcm, out->write_key, out->write_iv, tx_secret );
404 :
405 : /* The server has sent Finished before deriving application secrets,
406 : so its write epoch changes now, independently of the read epoch.
407 : The client still has its own Finished to send with handshake keys. */
408 8175 : if( level==FD_TLS_LEVEL_HANDSHAKE && !conn->hs.base.server ) conn->tx_level = FD_TLS_LEVEL_HANDSHAKE;
409 8175 : if( level==FD_TLS_LEVEL_APPLICATION && conn->hs.base.server ) {
410 819 : conn->tx_level = FD_TLS_LEVEL_APPLICATION;
411 819 : conn->write_seq = 0UL;
412 819 : }
413 :
414 8175 : if( conn->secrets_fn ) conn->secrets_fn( hs, rx_secret, tx_secret, level );
415 8175 : }
416 :
417 : static int
418 15342 : cb_sendmsg( void const * hs, void const * msg, ulong msg_sz, uint enc_level, int flush ) {
419 15342 : fd_tlsrec_conn_t * conn = cb_ctx( hs );
420 15342 : int rc = hs_tbuf_push( msg, msg_sz, enc_level );
421 15342 : if( FD_UNLIKELY( rc ) ) return 0;
422 15342 : if( flush ) { rc = hs_tbuf_flush( conn ); if( FD_UNLIKELY(rc) ) return 0; }
423 15342 : return 1;
424 15342 : }
425 :
426 : /* Record layer (receive path) ******************************************/
427 :
428 : static inline ulong
429 128013345 : fd_tlsrec_peek_rec_sz( uchar const * buf, ulong buf_sz ) {
430 128013345 : if( buf_sz < sizeof(fd_tlsrec_hdr_t) ) return 0;
431 127889606 : fd_tlsrec_hdr_t hdr;
432 127889606 : fd_memcpy( &hdr, buf, sizeof(hdr) );
433 127889606 : fd_tlsrec_hdr_bswap( &hdr );
434 127889606 : ulong payload = hdr.length;
435 127889606 : if( payload > FD_TLSREC_PAYLOAD_MAX ) return 0;
436 127889606 : return sizeof(fd_tlsrec_hdr_t) + payload;
437 127889606 : }
438 :
439 : static int
440 128364805 : fd_tlsrec_rx( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * tcp_rx, fd_tlsrec_slice_t * app_rx ) {
441 128364805 : fd_tlsrec_buf_t * rb = &conn->rec_buf;
442 :
443 : /* Skip rec_buf if tcp_rx already holds a whole record */
444 128364805 : uchar * rec;
445 128364805 : ulong rec_sz;
446 128364805 : if( !rb->sz &&
447 128364805 : (rec_sz = fd_tlsrec_peek_rec_sz( tcp_rx->data, fd_tlsrec_slice_sz(tcp_rx) )) &&
448 128364805 : rec_sz <= fd_tlsrec_slice_sz(tcp_rx) ) {
449 18111 : rec = fd_tlsrec_slice_pop( tcp_rx, rec_sz );
450 128346694 : } else {
451 : /* Reassemble record header (5 bytes) */
452 128346694 : if( rb->sz < sizeof(fd_tlsrec_hdr_t) ) {
453 621694 : ulong want = sizeof(fd_tlsrec_hdr_t);
454 621694 : ulong have = fd_ulong_min( want, rb->sz + fd_tlsrec_slice_sz(tcp_rx) );
455 621694 : ulong n = have - rb->sz;
456 621694 : fd_memcpy( rb->buf + rb->sz, tcp_rx->data, n );
457 621694 : rb->sz = have; tcp_rx->data += n;
458 621694 : if( have < want ) return FD_TLSREC_SUCCESS;
459 621694 : }
460 :
461 : /* Reassemble full record */
462 127860117 : rec_sz = fd_tlsrec_peek_rec_sz( rb->buf, sizeof(fd_tlsrec_hdr_t) );
463 127860117 : if( FD_UNLIKELY( !rec_sz ) )
464 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_RECORD_OVERFLOW, FD_TLS_REASON_REC_OVERFLOW );
465 :
466 127860117 : ulong have = fd_ulong_min( rec_sz, rb->sz + fd_tlsrec_slice_sz(tcp_rx) );
467 127860117 : ulong n = have - rb->sz;
468 127860117 : fd_memcpy( rb->buf + rb->sz, tcp_rx->data, n );
469 127860117 : rb->sz = have; tcp_rx->data += n;
470 127860117 : if( have < rec_sz ) return FD_TLSREC_SUCCESS;
471 :
472 135117 : rb->sz = 0; /* consume record */
473 135117 : rec = rb->buf;
474 135117 : }
475 :
476 153228 : fd_tlsrec_hdr_t * hdr = fd_type_pun( rec );
477 153228 : fd_tls_estate_base_t * hs = &conn->hs.base;
478 :
479 : /* RFC 8446 Sections 5.1-5.2: ignore legacy_record_version on receive,
480 : but preserve its wire bytes for AEAD additional data. */
481 153228 : int plaintext = fd_tlsrec_read_keys_plaintext( conn );
482 153228 : int peer_plaintext = hs->server ? hs->state!=FD_TLS_HS_CONNECTED : plaintext;
483 :
484 : /* RFC 8446 Section 5: discard compatibility CCS throughout the
485 : window after the first ClientHello and before the peer Finished,
486 : even between fragments of a handshake message. */
487 153228 : if( FD_UNLIKELY( hdr->content_type == FD_TLS_REC_CHANGE_CIPHER_SPEC ) ) {
488 3597 : int allowed = hs->server
489 3597 : ? ( hs->state!=FD_TLS_HS_CONNECTED && ( hs->state!=FD_TLS_HS_START || conn->hs.srv.hello_retry ) )
490 3597 : : ( hs->state!=FD_TLS_HS_CONNECTED && hs->state!=FD_TLS_HS_START );
491 3597 : if( FD_UNLIKELY( !allowed ||
492 3597 : rec_sz != sizeof(fd_tlsrec_hdr_t)+1UL ||
493 3597 : rec[ sizeof(fd_tlsrec_hdr_t) ] != 0x01 ) )
494 42 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_CCS );
495 3555 : return FD_TLSREC_SUCCESS;
496 3597 : }
497 :
498 149631 : if( FD_UNLIKELY( hdr->content_type == FD_TLS_REC_ALERT && peer_plaintext ) ) {
499 249 : if( FD_UNLIKELY( conn->hs_rbuf.sz ) )
500 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_HS_INTERLEAVED );
501 249 : return fd_tlsrec_alert_rx( conn, rec + sizeof(fd_tlsrec_hdr_t), rec_sz - sizeof(fd_tlsrec_hdr_t) );
502 249 : }
503 :
504 : /* Unencrypted handshake records (pre-ServerHello) */
505 149382 : if( FD_UNLIKELY( plaintext ) ) {
506 : /* RFC 8446 Section 5.1: TLSPlaintext.length may not exceed 2^14 */
507 4422 : if( FD_UNLIKELY( rec_sz > sizeof(fd_tlsrec_hdr_t)+FD_TLSREC_PLAINTEXT_MAX ) )
508 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_RECORD_OVERFLOW, FD_TLS_REASON_REC_OVERFLOW );
509 4422 : fd_tlsrec_slice_t payload[1];
510 4422 : fd_tlsrec_slice_init( payload, rec + sizeof(fd_tlsrec_hdr_t), rec_sz - sizeof(fd_tlsrec_hdr_t) );
511 4422 : if( FD_UNLIKELY( hdr->content_type != FD_TLS_REC_HANDSHAKE ) )
512 3 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_REC_TYPE );
513 4419 : return fd_tlsrec_hs_rx_record( conn, payload, FD_TLS_LEVEL_INITIAL );
514 4422 : }
515 :
516 : /* RFC 8446 Section 5.1: handshake messages MUST NOT be interleaved
517 : with other record types. hs_rbuf holds the head of a handshake
518 : message until the next handshake record completes it. */
519 144960 : if( FD_UNLIKELY( conn->hs_rbuf.sz && hdr->content_type != FD_TLS_REC_APPLICATION_DATA ) )
520 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_HS_INTERLEAVED );
521 :
522 : /* Encrypted record (RFC 8446 Section 5.2: TLSCiphertext has outer
523 : type application_data). The header is also AEAD additional data,
524 : so a wrong outer type would fail authentication anyway; checking
525 : first gives the right alert. */
526 144960 : if( FD_UNLIKELY( hdr->content_type != FD_TLS_REC_APPLICATION_DATA ) )
527 12 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_REC_TYPE );
528 144948 : if( FD_UNLIKELY( rec_sz < sizeof(fd_tlsrec_hdr_t) + FD_AES_GCM_TAG_SZ ) )
529 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_BAD_RECORD_MAC, FD_TLS_REASON_REC_MAC );
530 :
531 144948 : uint enc_level = ( hs->state == FD_TLS_HS_CONNECTED )
532 144948 : ? FD_TLS_LEVEL_APPLICATION : FD_TLS_LEVEL_HANDSHAKE;
533 144948 : fd_tlsrec_keys_t * keys = &conn->keys[ enc_level==FD_TLS_LEVEL_APPLICATION ];
534 :
535 144948 : uchar const * tag = rec + rec_sz - FD_AES_GCM_TAG_SZ;
536 144948 : uchar * c = rec + sizeof(fd_tlsrec_hdr_t);
537 144948 : ulong c_sz = (ulong)(tag - c);
538 :
539 : /* Decrypt into app_rx if it fits (content type is only known after
540 : decrypting; non-app records don't advance the cursor). Otherwise
541 : decrypt in place in rec_buf. */
542 144948 : uchar * pt = app_rx->data;
543 144948 : if( FD_UNLIKELY( fd_tlsrec_slice_sz(app_rx) < c_sz ) ) {
544 321 : if( rec != rb->buf ) {
545 0 : fd_memcpy( rb->buf, rec, rec_sz );
546 0 : rec = rb->buf;
547 0 : hdr = fd_type_pun( rec );
548 0 : tag = rec + rec_sz - FD_AES_GCM_TAG_SZ;
549 0 : c = rec + sizeof(fd_tlsrec_hdr_t);
550 0 : }
551 321 : pt = c;
552 321 : }
553 144948 : if( FD_UNLIKELY( !fd_tlsrec_decrypt( pt, c, c_sz, hdr, conn->read_seq, tag, keys ) ) ) {
554 15 : fd_tlsrec_fail( conn, FD_TLS_ALERT_BAD_RECORD_MAC, FD_TLS_REASON_REC_MAC );
555 15 : return FD_TLSREC_ERR_CRYPTO;
556 15 : }
557 144933 : conn->read_seq++;
558 :
559 : /* RFC 8446 Section 5.4: the entire TLSInnerPlaintext, including
560 : content type and padding, must fit in 2^14+1 bytes. */
561 144933 : if( FD_UNLIKELY( c_sz > FD_TLSREC_PLAINTEXT_MAX+1UL ) )
562 15 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_RECORD_OVERFLOW, FD_TLS_REASON_REC_OVERFLOW );
563 :
564 : /* Strip padding and content type (RFC 8446 §5.4) */
565 144918 : ulong p_sz = c_sz;
566 243270 : while( p_sz > 0 && pt[p_sz-1] == 0 ) p_sz--;
567 144918 : if( FD_UNLIKELY( !p_sz ) )
568 3 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_REC_PADDING );
569 144915 : uint ct = pt[--p_sz];
570 144915 : if( FD_UNLIKELY( conn->hs_rbuf.sz && ct != FD_TLS_REC_HANDSHAKE ) )
571 6 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_HS_INTERLEAVED );
572 :
573 : /* Dispatch by inner content type */
574 144909 : switch( ct ) {
575 :
576 26613 : case FD_TLS_REC_HANDSHAKE:
577 26613 : { fd_tlsrec_slice_t payload[1];
578 26613 : fd_tlsrec_slice_init( payload, pt, p_sz );
579 26613 : int rc = fd_tlsrec_hs_rx_record( conn, payload, enc_level );
580 26613 : if( FD_UNLIKELY( rc ) ) return rc;
581 26613 : }
582 26472 : break;
583 :
584 118224 : case FD_TLS_REC_APPLICATION_DATA:
585 118224 : if( FD_UNLIKELY( hs->state != FD_TLS_HS_CONNECTED ) )
586 0 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_REC_TYPE );
587 118224 : if( pt != app_rx->data ) {
588 6 : if( FD_UNLIKELY( p_sz > fd_tlsrec_slice_sz(app_rx) ) ) return FD_TLSREC_ERR_OOM;
589 6 : fd_memcpy( app_rx->data, pt, p_sz );
590 6 : }
591 118224 : app_rx->data += p_sz;
592 118224 : break;
593 :
594 66 : case FD_TLS_REC_ALERT:
595 66 : return fd_tlsrec_alert_rx( conn, pt, p_sz );
596 :
597 6 : default:
598 6 : return fd_tlsrec_fail( conn, FD_TLS_ALERT_UNEXPECTED_MESSAGE, FD_TLS_REASON_REC_TYPE );
599 144909 : }
600 :
601 144696 : return FD_TLSREC_SUCCESS;
602 144909 : }
603 :
604 : /* Public API ***********************************************************/
605 :
606 : fd_tlsrec_conn_t *
607 4530 : fd_tlsrec_conn_init( fd_tlsrec_conn_t * conn, fd_tls_t const * tls, int is_server ) {
608 4530 : fd_memset( conn, 0, sizeof(*conn) );
609 4530 : fd_memcpy( &conn->tls, tls, sizeof(*tls) );
610 4530 : conn->secrets_fn = conn->tls.secrets_fn;
611 4530 : conn->tls.quic = 0;
612 4530 : conn->tls.secrets_fn = cb_secrets;
613 4530 : conn->tls.sendmsg_fn = cb_sendmsg;
614 4530 : if( is_server ) fd_tls_estate_srv_new( &conn->hs.srv );
615 3618 : else fd_tls_estate_cli_new( &conn->hs.cli );
616 4530 : return conn;
617 4530 : }
618 :
619 : int
620 : fd_tlsrec_conn_rx( fd_tlsrec_conn_t * conn, fd_tlsrec_slice_t * tcp_rx,
621 : uchar * tcp_tx, ulong * tcp_tx_sz,
622 128347125 : uchar * app_rx, ulong * app_rx_sz ) {
623 128347125 : ulong _z = 0; if( !tcp_tx_sz ) tcp_tx_sz = &_z; if( !app_rx_sz ) app_rx_sz = &_z;
624 :
625 128347125 : fd_tlsrec_slice_t tx[1]; fd_tlsrec_slice_init( tx, tcp_tx, *tcp_tx_sz );
626 128347125 : fd_tlsrec_slice_t rx[1]; fd_tlsrec_slice_init( rx, app_rx, *app_rx_sz );
627 128347125 : *tcp_tx_sz = *app_rx_sz = 0;
628 :
629 128347125 : if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( conn ) ) ) return FD_TLSREC_ERR_STATE;
630 :
631 128346957 : hs_tbuf_init( tx );
632 :
633 : /* Client: send ClientHello on first call */
634 128346957 : if( FD_UNLIKELY( conn->hs.base.state==FD_TLS_HS_START && !conn->hs.base.server ) ) {
635 3615 : long rc = fd_tls_handshake( &conn->tls, &conn->hs, NULL, 0, FD_TLS_LEVEL_INITIAL );
636 3615 : if( FD_UNLIKELY( rc<0 ) ) return FD_TLSREC_ERR_PROTO;
637 3615 : }
638 :
639 : /* Process incoming TLS records. RFC 8446 Section 6.1: anything
640 : received after close_notify is ignored. */
641 128346957 : int rc = FD_TLSREC_SUCCESS;
642 128346957 : if( tcp_rx ) {
643 256707598 : while( !fd_tlsrec_slice_is_empty(tcp_rx) && !conn->rx_closed ) {
644 128364805 : rc = fd_tlsrec_rx( conn, tcp_rx, rx );
645 128364805 : if( FD_UNLIKELY(rc) ) break;
646 128364805 : }
647 128343342 : if( conn->rx_closed ) tcp_rx->data = tcp_rx->data_end;
648 128343342 : }
649 :
650 128346957 : if( !rc ) rc = hs_tbuf_flush( conn );
651 128346957 : if( !rc ) rc = fd_tlsrec_answer_key_update( conn, &hs_tbuf.tcp_tx );
652 :
653 : /* Output (including a fatal alert) is reported even on error so the
654 : caller can send it before tearing down; plaintext is not. */
655 128346957 : *tx = hs_tbuf.tcp_tx;
656 128346957 : *tcp_tx_sz = (ulong)(tx->data - tcp_tx);
657 128346957 : *app_rx_sz = rc ? 0UL : (ulong)(rx->data - app_rx);
658 128346957 : return rc;
659 128346957 : }
660 :
661 : int
662 : fd_tlsrec_conn_tx( fd_tlsrec_conn_t * conn, uchar * tcp_tx, ulong * tcp_tx_sz,
663 15525 : fd_tlsrec_slice_t * app_tx ) {
664 15525 : fd_tlsrec_slice_t tx[1]; fd_tlsrec_slice_init( tx, tcp_tx, *tcp_tx_sz );
665 15525 : *tcp_tx_sz = 0;
666 :
667 15525 : if( FD_UNLIKELY( !fd_tlsrec_conn_is_ready(conn) || conn->tx_closed ) ) return FD_TLSREC_ERR_STATE;
668 15354 : if( !fd_tlsrec_slice_sz(app_tx) ) return FD_TLSREC_SUCCESS;
669 :
670 15354 : ulong overhead = sizeof(fd_tlsrec_hdr_t) + 1 + FD_AES_GCM_TAG_SZ;
671 15354 : if( FD_UNLIKELY( fd_tlsrec_slice_sz(tx) < overhead + 128 ) ) return FD_TLSREC_ERR_OOM;
672 :
673 : /* RFC 8446 Section 4.6.3: a requested KeyUpdate goes out before the
674 : next application data record */
675 15354 : if( FD_UNLIKELY( conn->key_update_pending ) ) {
676 3 : int rc = fd_tlsrec_send_key_update( conn, tx, 0U );
677 3 : if( FD_UNLIKELY( rc ) ) return rc;
678 3 : conn->key_update_pending = 0;
679 15351 : } else if( FD_UNLIKELY( conn->write_seq >= FD_TLSREC_KEY_UPDATE_SEQ ) ) {
680 6 : int rc = fd_tlsrec_send_key_update( conn, tx, 0U );
681 6 : if( FD_UNLIKELY( rc ) ) return rc;
682 6 : }
683 :
684 15354 : ulong sz = fd_ulong_min( fd_tlsrec_slice_sz(tx) - overhead, FD_TLSREC_PLAINTEXT_MAX );
685 15354 : sz = fd_ulong_min( sz, fd_tlsrec_slice_sz(app_tx) );
686 :
687 15354 : int rc = fd_tlsrec_tx( conn, tx, fd_tlsrec_slice_pop(app_tx, sz), sz,
688 15354 : FD_TLS_REC_APPLICATION_DATA, FD_TLS_LEVEL_APPLICATION );
689 15354 : *tcp_tx_sz = (ulong)(tx->data - tcp_tx);
690 15354 : return rc;
691 15354 : }
692 :
693 : int
694 : fd_tlsrec_conn_key_update( fd_tlsrec_conn_t * conn, uchar * tcp_tx, ulong * tcp_tx_sz,
695 1941 : int request_peer_update ) {
696 1941 : fd_tlsrec_slice_t tx[1]; fd_tlsrec_slice_init( tx, tcp_tx, *tcp_tx_sz );
697 1941 : *tcp_tx_sz = 0UL;
698 :
699 1941 : if( FD_UNLIKELY( !fd_tlsrec_conn_is_ready(conn) || conn->tx_closed ) ) return FD_TLSREC_ERR_STATE;
700 1938 : if( FD_UNLIKELY( request_peer_update<0 || request_peer_update>1 ) ) return FD_TLSREC_ERR_PROTO;
701 :
702 1938 : int rc = fd_tlsrec_send_key_update( conn, tx, (uchar)request_peer_update );
703 1938 : *tcp_tx_sz = (ulong)(tx->data - tcp_tx);
704 1938 : return rc;
705 1938 : }
706 :
707 : int
708 27 : fd_tlsrec_conn_close( fd_tlsrec_conn_t * conn, uchar * tcp_tx, ulong * tcp_tx_sz ) {
709 27 : fd_tlsrec_slice_t tx[1]; fd_tlsrec_slice_init( tx, tcp_tx, *tcp_tx_sz );
710 27 : *tcp_tx_sz = 0UL;
711 :
712 27 : if( FD_UNLIKELY( !fd_tlsrec_conn_is_ready(conn) || conn->tx_closed ) ) return FD_TLSREC_ERR_STATE;
713 :
714 18 : int rc = fd_tlsrec_send_alert( conn, tx, 1U, FD_TLS_ALERT_CLOSE_NOTIFY );
715 18 : *tcp_tx_sz = (ulong)(tx->data - tcp_tx);
716 18 : return rc;
717 27 : }
718 :
719 0 : FD_FN_PURE int fd_tlsrec_conn_is_server( fd_tlsrec_conn_t const * c ) { return c->hs.base.server; }
720 21600 : FD_FN_PURE int fd_tlsrec_conn_is_ready ( fd_tlsrec_conn_t const * c ) { return c->hs.base.state == FD_TLS_HS_CONNECTED; }
721 128347710 : FD_FN_PURE int fd_tlsrec_conn_is_failed( fd_tlsrec_conn_t const * c ) { return c->hs.base.state == FD_TLS_HS_FAIL; }
|