Line data Source code
1 : #ifndef HEADER_fd_src_waltz_tls_fd_tls_h
2 : #define HEADER_fd_src_waltz_tls_fd_tls_h
3 :
4 : #include "fd_tls_estate.h"
5 : #include "../../ballet/chacha/fd_chacha_rng.h"
6 : #include "../../ballet/ed25519/fd_ed25519.h"
7 : #include "../../ballet/x509/fd_x509_ca_store.h"
8 :
9 : /* fd_tls implements a subset of the TLS v1.3 (RFC 8446) handshake
10 : protocol.
11 :
12 : fd_tls is not a general purpose TLS library. It only provides the
13 : TLS components required to secure peer-to-peer QUIC connections as
14 : they appear in Solana network protocol. Specifics are listed below.
15 :
16 : Older TLS versions, such as TLS v1.2, are not supported.
17 :
18 : ### Peer Authentication
19 :
20 : Peers are authenticated with X.509 certificates containing Ed25519
21 : public keys. Client cert authentication is optional; servers
22 : require it when req_client_cert is set.
23 :
24 : Outside of QUIC mode, the client additionally accepts servers with
25 : ECDSA P-256 and RSA (2048 to 4096 bit) leaf keys, signing the
26 : handshake with ecdsa_secp256r1_sha256 or rsa_pss_rsae_sha{256,384,
27 : 512}, so that it can talk to Web PKI servers.
28 :
29 : ### Key Exchange
30 :
31 : Peers exchange symmetric keys using X25519, an Elliptic Curve Diffie-
32 : Hellman key exchange scheme using Curve25519. Pre-shared keys and
33 : other key exchange schemes are currently not supported.
34 :
35 : ### Data Confidentiality and Integratity
36 :
37 : fd_tls provides an API for the TLS_AES_128_GCM_SHA256 cipher suite.
38 : Other cipher suites are currently not supported.
39 :
40 : ### References
41 :
42 : This library implements parts of protocols specified in the following
43 : IETF RFCs:
44 :
45 : RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
46 : https://datatracker.ietf.org/doc/html/rfc8446
47 :
48 : RFC 6066: Transport Layer Security (TLS) Extensions:
49 : Extension Definitions
50 : https://datatracker.ietf.org/doc/html/rfc6066
51 :
52 : RFC 9001: Using TLS to Secure QUIC
53 : https://datatracker.ietf.org/doc/html/rfc9001
54 :
55 : RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral
56 : Parameters for Transport Layer Security (TLS)
57 : RFC 4492: Elliptic Curve Cryptography (ECC) Cipher Suites for
58 : Transport Layer Security (TLS)
59 : https://datatracker.ietf.org/doc/html/rfc7919
60 : https://datatracker.ietf.org/doc/html/rfc4492
61 :
62 : RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)
63 : https://datatracker.ietf.org/doc/html/rfc8032
64 : Note: fd_ed25519 uses stricter signature malleability checks!
65 :
66 : RFC 7748: Elliptic Curves for Security
67 : https://datatracker.ietf.org/doc/html/rfc7748
68 :
69 : RFC 5288: AES Galois Counter Mode (GCM) Cipher Suites for TLS
70 : https://datatracker.ietf.org/doc/html/rfc5288 */
71 :
72 : /* Constants **********************************************************/
73 :
74 : /* FD_TLS_CV_SIGN_SZ is the size of the TLS 1.3 CertificateVerify
75 : signing payload: 64 bytes of 0x20 padding, the 33 byte context
76 : string, a zero separator and the 32 byte transcript hash (RFC 8446,
77 : Section 4.4.3). */
78 :
79 8160 : #define FD_TLS_CV_SIGN_SZ (130UL)
80 :
81 : /* Callbacks **********************************************************/
82 :
83 : /* fd_tls_secrets_fn_t is called by fd_tls when new encryption secrets
84 : have been generated. {recv/send}_secret are used for incoming/out-
85 : going data respectively and point to a 32-byte buffer valid for the
86 : lifetime of the function call. This function is invoked for each
87 : new encryption_level, which is FD_TLS_LEVEL_{HANDSHAKE,APPLICATION}.
88 : It is safe to discard handshake-level decryption secrets after the
89 : handshake has been completed. */
90 :
91 : typedef void
92 : (* fd_tls_secrets_fn_t)( void const * handshake,
93 : void const * recv_secret,
94 : void const * send_secret,
95 : uint encryption_level );
96 :
97 : /* fd_tls_sendmsg_fn_t is called by fd_tls to request transmission of a
98 : TLS message to the peer. msg points to a buffer containing msg_sz
99 : message bytes. The smallest message size is 4 bytes (the size of a
100 : message header). encryption_level indicates which key to use.
101 : flush==1 requests transmission of buffered data; more callbacks may
102 : follow in the same handshake call. It is safe to "flush" (i.e.
103 : transmit data out through the NIC) even when flush==0. Returns 1
104 : on success and 0 on failure. */
105 :
106 : typedef int
107 : (* fd_tls_sendmsg_fn_t)( void const * handshake,
108 : void const * msg,
109 : ulong msg_sz,
110 : uint encryption_level,
111 : int flush );
112 :
113 : /* fd_tls_quic_tp_self_fn_t is called by fd_tls to request QUIC
114 : transport params to be sent to the peer. quic_tp points to the
115 : buffer that may hold serialized QUIC transport parameters (RFC
116 : Section 18). quic_tp_bufsz is the size of the buffer at quic_tp.
117 : Return value is actual serialized (<=quic_tp_bufsz) on success.
118 : On failure, returns (>quic_tp_bufsz) to indicate insufficient bufsz.
119 : (Zero implies success, however!) */
120 :
121 : typedef ulong __attribute__((warn_unused_result))
122 : (* fd_tls_quic_tp_self_fn_t)( void * handshake,
123 : uchar * quic_tp,
124 : ulong quic_tp_bufsz );
125 :
126 : /* fd_tls_quic_tp_peer_fn_t is called by fd_tls to inform the user of
127 : the peer's QUIC transport params. quic_tp points to the serialized
128 : QUIC transport parameters (RFC 9000 Section 18). quic_tp_sz is the
129 : serialized size. Lifetime of quic_tp buffer ends at return. fd_tls
130 : does not do any validation on the peer's QUIC TP -- Please ensure
131 : your deserializer is robust given arbitrary data. */
132 :
133 : typedef void
134 : (* fd_tls_quic_tp_peer_fn_t)( void * handshake,
135 : uchar const * quic_tp,
136 : ulong quic_tp_sz );
137 :
138 : /* fd_tls_sign_fn_t is called by by fd_tls to request signing of a
139 : TLS 1.3 certificate verify payload.
140 :
141 : ctx is an arbitrary pointer that is provided as a callback argument.
142 : sig points to a 64 byte buffer where the implementor should store the
143 : ed25519 signature of the payload. Payload will point to a
144 : FD_TLS_CV_SIGN_SZ byte buffer containing the TLS 1.3 CertificateVerify
145 : payload.
146 :
147 : This function must not fail. Lifetime of the payload buffer ends at
148 : return.
149 :
150 : May be NULL for clients that never present a client certificate. If
151 : such a client (or one without a cert_x509) receives a CertificateRequest,
152 : fd_tls does not abort: it sends a Certificate message with an empty
153 : certificate_list and no CertificateVerify, and continues the
154 : handshake (RFC 8446 Section 4.4.2). The server then decides whether
155 : to proceed or to abort with an alert. Servers must always install a
156 : signer. */
157 :
158 : typedef void
159 : (* fd_tls_sign_fn_t)( void * ctx,
160 : uchar sig[ static FD_ED25519_SIG_SZ ],
161 : uchar const payload[ static FD_TLS_CV_SIGN_SZ ] );
162 :
163 : struct fd_tls_sign_vt {
164 : void * ctx;
165 : fd_tls_sign_fn_t sign_fn;
166 : };
167 :
168 : typedef struct fd_tls_sign_vt fd_tls_sign_t;
169 :
170 : static inline void
171 : fd_tls_sign( fd_tls_sign_t const * sign,
172 : uchar sig[ static FD_ED25519_SIG_SZ ],
173 7944 : uchar const payload[ static FD_TLS_CV_SIGN_SZ ] ) {
174 7944 : sign->sign_fn( sign->ctx, sig, payload );
175 7944 : }
176 :
177 : extern char const fd_tls13_cli_sign_prefix[ 98 ];
178 : extern char const fd_tls13_srv_sign_prefix[ 98 ];
179 :
180 : /* Public API *********************************************************/
181 :
182 : /* Handshake state identifiers */
183 :
184 128294039 : #define FD_TLS_HS_FAIL ( 0) /* client, server */
185 388101 : #define FD_TLS_HS_CONNECTED ( 1) /* client, server */
186 430476 : #define FD_TLS_HS_START ( 2) /* client, server */
187 7500 : #define FD_TLS_HS_WAIT_CERT ( 3) /* client, server */
188 20070 : #define FD_TLS_HS_WAIT_CV ( 4) /* client, server */
189 39246 : #define FD_TLS_HS_WAIT_FINISHED ( 5) /* client, server */
190 212790 : #define FD_TLS_HS_WAIT_SH ( 6) /* client */
191 18852 : #define FD_TLS_HS_WAIT_EE ( 7) /* client */
192 18867 : #define FD_TLS_HS_WAIT_CERT_CR ( 8) /* client */
193 :
194 : /* TLS encryption levels */
195 :
196 35664 : #define FD_TLS_LEVEL_INITIAL (0)
197 : #define FD_TLS_LEVEL_EARLY (1)
198 128524730 : #define FD_TLS_LEVEL_HANDSHAKE (2)
199 370164 : #define FD_TLS_LEVEL_APPLICATION (3)
200 :
201 : /* FD_TLS_SERVER_CERT_SZ_MAX is the max permitted size of the DER-
202 : serialized X.509 server certificate. */
203 :
204 20736 : #define FD_TLS_SERVER_CERT_SZ_MAX (1011UL)
205 :
206 : /* FD_TLS_SERVER_CERT_MSG_SZ_MAX is the max permitted size of the pre-
207 : buffered X.509 server certificate message. */
208 :
209 20727 : #define FD_TLS_SERVER_CERT_MSG_SZ_MAX (FD_TLS_SERVER_CERT_SZ_MAX+13UL)
210 :
211 : /* FD_TLS_EXT_QUIC_PARAMS_SZ is the max permitted byte size of encoded
212 : QUIC transport parameters */
213 :
214 12234 : # define FD_TLS_EXT_QUIC_PARAMS_SZ_MAX (510UL)
215 :
216 : /* fd_tls_t contains the local TLS config. It is typically shared
217 : across multiple TLS handshakes. */
218 :
219 : struct fd_tls {
220 : /* CSPRNG, caller-provided. caller is responsible for reseeding. */
221 : fd_chacha_rng_t * rng;
222 : fd_tls_secrets_fn_t secrets_fn;
223 : fd_tls_sendmsg_fn_t sendmsg_fn;
224 :
225 : /* QUIC specific callbacks -- Only called if quic flag is set.
226 : TODO: Will optional function pointers stall the pipeline? */
227 : fd_tls_quic_tp_self_fn_t quic_tp_self_fn;
228 : fd_tls_quic_tp_peer_fn_t quic_tp_peer_fn;
229 :
230 : /* Trust store for the server certificate chain (client mode). If
231 : non-NULL, the chain is verified against it and against server_name
232 : (RFC 5280 path validation, RFC 6125 hostname matching) and the
233 : handshake is aborted on failure. If NULL, no chain validation is
234 : done (QUIC peer-to-peer mode, or verification disabled). */
235 : fd_x509_ca_store_t const * ca_store;
236 :
237 : /* key_{private,public}_key is an X25519 key pair. During the TLS
238 : handshake, it is used to establish symmetric encryption keys.
239 : kex_private_key is an arbitrary 32 byte vector. It is recommended
240 : to generate a new X25519 key on startup from cryptographically
241 : secure randomness. kex_public_key is the corresponding public key
242 : curve point derived via fd_x25519_public.
243 :
244 : Security notes:
245 : - May not be changed while conns are active. */
246 : uchar kex_private_key[ 32 ];
247 : uchar kex_public_key [ 32 ];
248 :
249 : /* Signing function holding the Ed25519 key pair that identifies the
250 : server. During TLS handshakes, used to sign a transcript of the
251 : handshake to prove to the peer that we are in possession of this
252 : key. This function should sign with the Solana node identity key.
253 :
254 : Security notes:
255 : - May not be changed while conns are active.
256 : - Using a public key that is not derived from the private key may
257 : reveal the private key (!!!) */
258 : fd_tls_sign_t sign;
259 :
260 : /* cert_public_key is the Ed25519 public key that identifies the
261 : server. Must be the public key corresponding to the Solana node
262 : identity key used by the signer function above. */
263 : uchar cert_public_key [ 32 ];
264 :
265 : /* X.509 certificate to present to peer (required for servers,
266 : optional for clients).
267 : SubjectPublicKeyInfo must be Ed25519 and match cert_public_key. */
268 : uchar cert_x509[ FD_TLS_SERVER_CERT_MSG_SZ_MAX ];
269 : ulong cert_x509_sz;
270 :
271 : /* ALPN protocol identifier.
272 : Format: <1 byte length prefix> <ASCII chars>.
273 : Is not NUL delimited. */
274 : uchar alpn[ 32 ];
275 : ulong alpn_sz;
276 :
277 : /* Server Name Indication (SNI) for client mode (RFC 6066).
278 : Set before starting a TLS client handshake. Omitted when
279 : server_name_len==0 (e.g. QUIC mode). NUL terminated. */
280 : char server_name[ 254 ];
281 : ushort server_name_len;
282 :
283 : /* Flags */
284 : ulong quic : 1;
285 : ulong req_client_cert : 1; /* server: send CertificateRequest */
286 : ulong _flags_reserved : 62;
287 : };
288 :
289 : typedef struct fd_tls fd_tls_t;
290 :
291 : /* Extended Alert Reasons *********************************************/
292 :
293 : /* fd_tls-specific error codes to identify reasons for alerts. These
294 : can help with debugging when the error cause is not evident by the
295 : alert itself. */
296 :
297 0 : #define FD_TLS_REASON_NULL ( 0)
298 :
299 30 : #define FD_TLS_REASON_ILLEGAL_STATE ( 1) /* illegal hs state */
300 0 : #define FD_TLS_REASON_SENDMSG_FAIL ( 2) /* sendmsg callback failed */
301 30 : #define FD_TLS_REASON_WRONG_ENC_LVL ( 3) /* wrong encryption level */
302 :
303 0 : #define FD_TLS_REASON_X25519_FAIL ( 9) /* fd_x25519_exchange failed */
304 0 : #define FD_TLS_REASON_NO_X509 (10) /* no X.509 cert installed */
305 24 : #define FD_TLS_REASON_WRONG_PUBKEY (11) /* peer cert has different pubkey than expected */
306 0 : #define FD_TLS_REASON_ED25519_FAIL (12) /* Ed25519 signature validation failed */
307 0 : #define FD_TLS_REASON_SECP256R1_FAIL (14) /* ECDSA P-256 signature validation failed */
308 45 : #define FD_TLS_REASON_RSA_FAIL (15) /* RSA-PSS signature validation failed */
309 :
310 0 : #define FD_TLS_REASON_CH_EXPECTED (101) /* wanted ClientHello, got another msg type */
311 9 : #define FD_TLS_REASON_CH_PARSE (103) /* failed to parse ClientHello */
312 3 : #define FD_TLS_REASON_CH_ENCODE (104) /* failed to encode ClientHello */
313 0 : #define FD_TLS_REASON_CH_NO_QUIC (106) /* Missing QUIC transport params in ClientHello */
314 3 : #define FD_TLS_REASON_CH_RETRY_KS (107) /* ClientHello still missing key share after a retry */
315 24 : #define FD_TLS_REASON_CH_NEG_VER (108) /* Unsupported TLS version */
316 33 : #define FD_TLS_REASON_CH_NEG_KX (109) /* Unsupported key exchange alg */
317 30 : #define FD_TLS_REASON_CH_NEG_SIG (110) /* Unsupported signature alg */
318 27 : #define FD_TLS_REASON_CH_NEG_CIPHER (111) /* Unsupported cipher suite */
319 9 : #define FD_TLS_REASON_CH_SESSION_ID (112) /* QUIC ClientHello requested compatibility mode */
320 :
321 0 : #define FD_TLS_REASON_SH_EXPECTED (201) /* wanted ServerHello, got another msg type */
322 0 : #define FD_TLS_REASON_SH_PARSE (203) /* failed to parse ServerHello */
323 0 : #define FD_TLS_REASON_SH_ENCODE (204) /* failed to encode ServerHello */
324 3 : #define FD_TLS_REASON_SH_NEG_CIPHER (205) /* ServerHello selected unoffered cipher suite */
325 6 : #define FD_TLS_REASON_SH_SESSION_ID (206) /* ServerHello session_id echo mismatch */
326 :
327 0 : #define FD_TLS_REASON_EE_NO_QUIC (301) /* Missing QUIC transport params in EncryptedExtensions */
328 0 : #define FD_TLS_REASON_EE_EXPECTED (302) /* wanted EncryptedExtensions, got another msg type */
329 15 : #define FD_TLS_REASON_EE_PARSE (304) /* failed to parse EncryptedExtensions */
330 0 : #define FD_TLS_REASON_EE_ENCODE (305) /* failed to encode EncryptedExtensions */
331 0 : #define FD_TLS_REASON_QUIC_TP_OVERSZ (306) /* Buffer overflow in QUIC transport params callback */
332 :
333 0 : #define FD_TLS_REASON_CV_EXPECTED (401) /* wanted CertificateVerify, got another msg type */
334 18 : #define FD_TLS_REASON_CV_SIGALG (402) /* CertificateVerify sig alg doesn't match cert key type */
335 9 : #define FD_TLS_REASON_CV_PARSE (404) /* failed to parse CertificateVerify */
336 0 : #define FD_TLS_REASON_CV_ENCODE (405) /* failed to encode CertificateVerify */
337 :
338 0 : #define FD_TLS_REASON_CERT_CR_EXPECTED (501) /* wanted Certificate or CertificateRequest, got another msg type */
339 108 : #define FD_TLS_REASON_CERT_CR_PARSE (503) /* failed to parse Certificate or CertificateRequest */
340 :
341 9 : #define FD_TLS_REASON_CERT_KEY_TYPE (601) /* unsupported certificate key type */
342 0 : #define FD_TLS_REASON_CERT_EXPECTED (602) /* wanted Certificate, got another msg type */
343 906 : #define FD_TLS_REASON_CERT_PARSE (604) /* failed to parse Certificate */
344 3 : #define FD_TLS_REASON_X509_PARSE (605) /* X.509 DER parse failed */
345 3 : #define FD_TLS_REASON_CERT_ENCODE (606) /* failed to encode Certificate */
346 177 : #define FD_TLS_REASON_CERT_VERIFY (607) /* certificate chain failed verification against ca_store */
347 :
348 51 : #define FD_TLS_REASON_CERT_CHAIN_EMPTY (701) /* cert chain contains no certs */
349 0 : #define FD_TLS_REASON_CERT_CHAIN_PARSE (702) /* failed to parse cert chain */
350 :
351 0 : #define FD_TLS_REASON_FINI_PARSE (901) /* invalid Finished message */
352 0 : #define FD_TLS_REASON_FINI_EXPECTED (902) /* wanted Finished, got another msg type */
353 0 : #define FD_TLS_REASON_FINI_FAIL (904) /* Finished data mismatch */
354 :
355 0 : #define FD_TLS_REASON_ALPN_PARSE (1001) /* failed to parse ALPN */
356 24 : #define FD_TLS_REASON_ALPN_NEG (1002) /* ALPN negotiation failed */
357 27 : #define FD_TLS_REASON_NO_ALPN (1003) /* no ALPN extension */
358 :
359 0 : #define FD_TLS_REASON_POST_HS_MSG (1101) /* unexpected post-handshake msg type */
360 18 : #define FD_TLS_REASON_KEY_UPDATE_PARSE (1102) /* failed to parse KeyUpdate */
361 87 : #define FD_TLS_REASON_CCS (1103) /* unexpected ChangeCipherSpec record */
362 129 : #define FD_TLS_REASON_ALERT_PARSE (1104) /* malformed alert record */
363 177 : #define FD_TLS_REASON_PEER_ALERT (1105) /* peer sent a fatal alert */
364 12 : #define FD_TLS_REASON_HS_INTERLEAVED (1106) /* record interleaved with a fragmented handshake message */
365 57 : #define FD_TLS_REASON_REC_TYPE (1108) /* unexpected record layer content type */
366 36 : #define FD_TLS_REASON_REC_OVERFLOW (1109) /* record larger than the protocol allows */
367 21 : #define FD_TLS_REASON_HS_KEY_CHANGE (1110) /* handshake data follows a key change in the same record */
368 24 : #define FD_TLS_REASON_HS_MSG_SIZE (1111) /* handshake message length invalid */
369 54 : #define FD_TLS_REASON_REC_MAC (1112) /* record failed authentication */
370 6 : #define FD_TLS_REASON_REC_PADDING (1113) /* record has no content type byte */
371 :
372 : FD_PROTOTYPES_BEGIN
373 :
374 : FD_FN_CONST ulong
375 : fd_tls_align( void );
376 :
377 : FD_FN_CONST ulong
378 : fd_tls_footprint( void );
379 :
380 : /* TODO document new/join/leave/delete */
381 :
382 : void *
383 : fd_tls_new( void * mem );
384 :
385 : fd_tls_t *
386 : fd_tls_join( void * );
387 :
388 : void *
389 : fd_tls_leave( fd_tls_t * );
390 :
391 : void *
392 : fd_tls_delete( void * );
393 :
394 : char const *
395 : fd_tls_alert_cstr( uint alert );
396 :
397 : char const *
398 : fd_tls_reason_cstr( uint reason );
399 :
400 : /* fd_tls_server_handshake ingests a TLS message from the client.
401 : Synchronously processes the message (API may become async in the
402 : future). Record must be complete (does not defragment). Returns
403 : number of bytes read on success. On failure, returns negated TLS
404 : alert code. */
405 :
406 : long
407 : fd_tls_server_handshake( fd_tls_t const * tls,
408 : fd_tls_estate_srv_t * handshake,
409 : void const * record,
410 : ulong record_sz,
411 : uint encryption_level );
412 :
413 : /* fd_tls_client_handshake is the client-side equivalent of
414 : fd_tls_server_handshake. Must not be called with messages sent after
415 : the handshake was completed (such as NewSessionTicket). */
416 :
417 : long
418 : fd_tls_client_handshake( fd_tls_t const * client,
419 : fd_tls_estate_cli_t * handshake,
420 : void const * record,
421 : ulong record_sz,
422 : uint encryption_level );
423 :
424 : static inline long
425 : fd_tls_handshake( fd_tls_t const * tls,
426 : fd_tls_estate_t * handshake,
427 : void const * record,
428 : ulong record_sz,
429 67623 : uint encryption_level ) {
430 67623 : if( handshake->base.server )
431 15171 : return fd_tls_server_handshake( tls, &handshake->srv, record, record_sz, encryption_level );
432 52452 : else
433 52452 : return fd_tls_client_handshake( tls, &handshake->cli, record, record_sz, encryption_level );
434 67623 : }
435 :
436 : /* fd_tls_hkdf_expand_label implements the TLS 1.3 HKDF-Expand function
437 : with SHA-256. Writes the resulting hash to out. secret is a 32 byte
438 : secret value. label points to the label string. label_sz is the
439 : number of chars in label (not including terminating NUL). context
440 : points to the context byte array. context_sz is the number of bytes
441 : in context.
442 :
443 : Constraints:
444 :
445 : out !=NULL
446 : secret!=NULL
447 : label_sz ==0 || label !=NULL
448 : context_sz==0 || context!=NULL
449 : 1<=out_sz <=32
450 : 0<=label_sz <=64
451 : 0<=context_sz<=64 */
452 :
453 : void *
454 : fd_tls_hkdf_expand_label( uchar * out,
455 : ulong out_sz,
456 : uchar const secret[ static 32 ],
457 : char const * label,
458 : ulong label_sz,
459 : uchar const * context,
460 : ulong context_sz );
461 :
462 : FD_PROTOTYPES_END
463 :
464 : #endif /* HEADER_fd_src_waltz_tls_fd_tls_h */
|