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 for
22 : fd_tls_client_t and mandatory for fd_tls_server_t.
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 0 : #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==0 when another message for the same conn will follow
102 : immediately on return. flush==1 hints that no more sendmsg callbacks
103 : are issued until the next call to fd_tls_server_handshake. It is
104 : safe to "flush" (i.e. transmit data out through the NIC) even when
105 : flush==0. Returns 1 on success and 0 on failure. */
106 :
107 : typedef int
108 : (* fd_tls_sendmsg_fn_t)( void const * handshake,
109 : void const * msg,
110 : ulong msg_sz,
111 : uint encryption_level,
112 : int flush );
113 :
114 : /* fd_tls_quic_tp_self_fn_t is called by fd_tls to request QUIC
115 : transport params to be sent to the peer. quic_tp points to the
116 : buffer that may hold serialized QUIC transport parameters (RFC
117 : Section 18). quic_tp_bufsz is the size of the buffer at quic_tp.
118 : Return value is actual serialized (<=quic_tp_bufsz) on success.
119 : On failure, returns (>quic_tp_bufsz) to indicate insufficient bufsz.
120 : (Zero implies success, however!) */
121 :
122 : typedef ulong __attribute__((warn_unused_result))
123 : (* fd_tls_quic_tp_self_fn_t)( void * handshake,
124 : uchar * quic_tp,
125 : ulong quic_tp_bufsz );
126 :
127 : /* fd_tls_quic_tp_peer_fn_t is called by fd_tls to inform the user of
128 : the peer's QUIC transport params. quic_tp points to the serialized
129 : QUIC transport parameters (RFC 9000 Section 18). quic_tp_sz is the
130 : serialized size. Lifetime of quic_tp buffer ends at return. fd_tls
131 : does not do any validation on the peer's QUIC TP -- Please ensure
132 : your deserializer is robust given arbitrary data. */
133 :
134 : typedef void
135 : (* fd_tls_quic_tp_peer_fn_t)( void * handshake,
136 : uchar const * quic_tp,
137 : ulong quic_tp_sz );
138 :
139 : /* fd_tls_sign_fn_t is called by by fd_tls to request signing of a
140 : TLS 1.3 certificate verify payload.
141 :
142 : ctx is an arbitrary pointer that is provided as a callback argument.
143 : sig points to a 64 byte buffer where the implementor should store the
144 : ed25519 signature of the payload. Payload will point to a
145 : FD_TLS_CV_SIGN_SZ byte buffer containing the TLS 1.3 CertificateVerify
146 : payload.
147 :
148 : This function must not fail. Lifetime of the payload buffer ends at
149 : return.
150 :
151 : May be NULL for clients that never present a client certificate. If
152 : such a client (or one without a cert_x509) receives a CertificateRequest,
153 : fd_tls does not abort: it sends a Certificate message with an empty
154 : certificate_list and no CertificateVerify, and continues the
155 : handshake (RFC 8446 Section 4.4.2). The server then decides whether
156 : to proceed or to abort with an alert. Servers must always install a
157 : signer. */
158 :
159 : typedef void
160 : (* fd_tls_sign_fn_t)( void * ctx,
161 : uchar sig[ static FD_ED25519_SIG_SZ ],
162 : uchar const payload[ static FD_TLS_CV_SIGN_SZ ] );
163 :
164 : struct fd_tls_sign_vt {
165 : void * ctx;
166 : fd_tls_sign_fn_t sign_fn;
167 : };
168 :
169 : typedef struct fd_tls_sign_vt fd_tls_sign_t;
170 :
171 : static inline void
172 : fd_tls_sign( fd_tls_sign_t const * sign,
173 : uchar sig[ static FD_ED25519_SIG_SZ ],
174 0 : uchar const payload[ static FD_TLS_CV_SIGN_SZ ] ) {
175 0 : sign->sign_fn( sign->ctx, sig, payload );
176 0 : }
177 :
178 : extern char const fd_tls13_cli_sign_prefix[ 98 ];
179 : extern char const fd_tls13_srv_sign_prefix[ 98 ];
180 :
181 : /* Public API *********************************************************/
182 :
183 : /* Handshake state identifiers */
184 :
185 0 : #define FD_TLS_HS_FAIL ( 0) /* client, server */
186 0 : #define FD_TLS_HS_CONNECTED ( 1) /* client, server */
187 0 : #define FD_TLS_HS_START ( 2) /* client, server */
188 0 : #define FD_TLS_HS_WAIT_CERT ( 3) /* client, server */
189 0 : #define FD_TLS_HS_WAIT_CV ( 4) /* client, server */
190 0 : #define FD_TLS_HS_WAIT_FINISHED ( 5) /* client, server */
191 0 : #define FD_TLS_HS_WAIT_SH ( 6) /* client */
192 0 : #define FD_TLS_HS_WAIT_EE ( 7) /* client */
193 0 : #define FD_TLS_HS_WAIT_CERT_CR ( 8) /* client */
194 :
195 : /* TLS encryption levels */
196 :
197 0 : #define FD_TLS_LEVEL_INITIAL (0)
198 : #define FD_TLS_LEVEL_EARLY (1)
199 0 : #define FD_TLS_LEVEL_HANDSHAKE (2)
200 0 : #define FD_TLS_LEVEL_APPLICATION (3)
201 :
202 : /* FD_TLS_SERVER_CERT_SZ_MAX is the max permitted size of the DER-
203 : serialized X.509 server certificate. */
204 :
205 0 : #define FD_TLS_SERVER_CERT_SZ_MAX (1011UL)
206 :
207 : /* FD_TLS_SERVER_CERT_MSG_SZ_MAX is the max permitted size of the pre-
208 : buffered X.509 server certificate message. */
209 :
210 0 : #define FD_TLS_SERVER_CERT_MSG_SZ_MAX (FD_TLS_SERVER_CERT_SZ_MAX+13UL)
211 :
212 : /* FD_TLS_EXT_QUIC_PARAMS_SZ is the max permitted byte size of encoded
213 : QUIC transport parameters */
214 :
215 0 : # define FD_TLS_EXT_QUIC_PARAMS_SZ_MAX (510UL)
216 :
217 : /* fd_tls_t contains the local TLS config. It is typically shared
218 : across multiple TLS handshakes. */
219 :
220 : struct fd_tls {
221 : /* CSPRNG, caller-provided. caller is responsible for reseeding. */
222 : fd_chacha_rng_t * rng;
223 : fd_tls_secrets_fn_t secrets_fn;
224 : fd_tls_sendmsg_fn_t sendmsg_fn;
225 :
226 : /* QUIC specific callbacks -- Only called if quic flag is set.
227 : TODO: Will optional function pointers stall the pipeline? */
228 : fd_tls_quic_tp_self_fn_t quic_tp_self_fn;
229 : fd_tls_quic_tp_peer_fn_t quic_tp_peer_fn;
230 :
231 : /* Trust store for the server certificate chain (client mode). If
232 : non-NULL, the chain is verified against it and against server_name
233 : (RFC 5280 path validation, RFC 6125 hostname matching) and the
234 : handshake is aborted on failure. If NULL, no chain validation is
235 : done (QUIC peer-to-peer mode, or verification disabled). */
236 : fd_x509_ca_store_t const * ca_store;
237 :
238 : /* key_{private,public}_key is an X25519 key pair. During the TLS
239 : handshake, it is used to establish symmetric encryption keys.
240 : kex_private_key is an arbitrary 32 byte vector. It is recommended
241 : to generate a new X25519 key on startup from cryptographically
242 : secure randomness. kex_public_key is the corresponding public key
243 : curve point derived via fd_x25519_public.
244 :
245 : Security notes:
246 : - May not be changed while conns are active. */
247 : uchar kex_private_key[ 32 ];
248 : uchar kex_public_key [ 32 ];
249 :
250 : /* Signing function holding the Ed25519 key pair that identifies the
251 : server. During TLS handshakes, used to sign a transcript of the
252 : handshake to prove to the peer that we are in possession of this
253 : key. This function should sign with the Solana node identity key.
254 :
255 : Security notes:
256 : - May not be changed while conns are active.
257 : - Using a public key that is not derived from the private key may
258 : reveal the private key (!!!) */
259 : fd_tls_sign_t sign;
260 :
261 : /* cert_public_key is the Ed25519 public key that identifies the
262 : server. Must be the public key corresponding to the Solana node
263 : identity key used by the signer function above. */
264 : uchar cert_public_key [ 32 ];
265 :
266 : /* X.509 certificate to present to peer (optional).
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 0 : #define FD_TLS_REASON_ILLEGAL_STATE ( 1) /* illegal hs state */
300 0 : #define FD_TLS_REASON_SENDMSG_FAIL ( 2) /* sendmsg callback failed */
301 0 : #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 0 : #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 0 : #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 0 : #define FD_TLS_REASON_CH_PARSE (103) /* failed to parse ClientHello */
312 0 : #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 0 : #define FD_TLS_REASON_CH_RETRY_KS (107) /* ClientHello still missing key share after a retry */
315 0 : #define FD_TLS_REASON_CH_NEG_VER (108) /* Unsupported TLS version */
316 0 : #define FD_TLS_REASON_CH_NEG_KX (109) /* Unsupported key exchange alg */
317 0 : #define FD_TLS_REASON_CH_NEG_SIG (110) /* Unsupported signature alg */
318 0 : #define FD_TLS_REASON_CH_NEG_CIPHER (111) /* Unsupported cipher suite */
319 :
320 0 : #define FD_TLS_REASON_SH_EXPECTED (201) /* wanted ServerHello, got another msg type */
321 0 : #define FD_TLS_REASON_SH_PARSE (203) /* failed to parse ServerHello */
322 0 : #define FD_TLS_REASON_SH_ENCODE (204) /* failed to encode ServerHello */
323 0 : #define FD_TLS_REASON_SH_NEG_CIPHER (205) /* ServerHello selected unoffered cipher suite */
324 0 : #define FD_TLS_REASON_SH_SESSION_ID (206) /* ServerHello session_id echo mismatch */
325 :
326 0 : #define FD_TLS_REASON_EE_NO_QUIC (301) /* Missing QUIC transport params in EncryptedExtensions */
327 0 : #define FD_TLS_REASON_EE_EXPECTED (302) /* wanted EncryptedExtensions, got another msg type */
328 0 : #define FD_TLS_REASON_EE_PARSE (304) /* failed to parse EncryptedExtensions */
329 0 : #define FD_TLS_REASON_EE_ENCODE (305) /* failed to encode EncryptedExtensions */
330 0 : #define FD_TLS_REASON_QUIC_TP_OVERSZ (306) /* Buffer overflow in QUIC transport params callback */
331 :
332 0 : #define FD_TLS_REASON_CV_EXPECTED (401) /* wanted CertificateVerify, got another msg type */
333 0 : #define FD_TLS_REASON_CV_SIGALG (402) /* CertificateVerify sig alg doesn't match cert key type */
334 0 : #define FD_TLS_REASON_CV_PARSE (404) /* failed to parse CertificateVerify */
335 0 : #define FD_TLS_REASON_CV_ENCODE (405) /* failed to encode CertificateVerify */
336 :
337 0 : #define FD_TLS_REASON_CERT_CR_EXPECTED (501) /* wanted Certificate or CertificateRequest, got another msg type */
338 0 : #define FD_TLS_REASON_CERT_CR_PARSE (503) /* failed to parse Certificate or CertificateRequest */
339 :
340 0 : #define FD_TLS_REASON_CERT_KEY_TYPE (601) /* unsupported certificate key type */
341 0 : #define FD_TLS_REASON_CERT_EXPECTED (602) /* wanted Certificate, got another msg type */
342 0 : #define FD_TLS_REASON_CERT_PARSE (604) /* failed to parse Certificate */
343 0 : #define FD_TLS_REASON_X509_PARSE (605) /* X.509 DER parse failed */
344 0 : #define FD_TLS_REASON_CERT_ENCODE (606) /* failed to encode Certificate */
345 0 : #define FD_TLS_REASON_CERT_VERIFY (607) /* certificate chain failed verification against ca_store */
346 :
347 0 : #define FD_TLS_REASON_CERT_CHAIN_EMPTY (701) /* cert chain contains no certs */
348 0 : #define FD_TLS_REASON_CERT_CHAIN_PARSE (702) /* failed to parse cert chain */
349 :
350 0 : #define FD_TLS_REASON_FINI_PARSE (901) /* invalid Finished message */
351 0 : #define FD_TLS_REASON_FINI_EXPECTED (902) /* wanted Finished, got another msg type */
352 0 : #define FD_TLS_REASON_FINI_FAIL (904) /* Finished data mismatch */
353 :
354 0 : #define FD_TLS_REASON_ALPN_PARSE (1001) /* failed to parse ALPN */
355 0 : #define FD_TLS_REASON_ALPN_NEG (1002) /* ALPN negotiation failed */
356 0 : #define FD_TLS_REASON_NO_ALPN (1003) /* no ALPN extension */
357 :
358 0 : #define FD_TLS_REASON_POST_HS_MSG (1101) /* unexpected post-handshake msg type */
359 0 : #define FD_TLS_REASON_KEY_UPDATE_PARSE (1102) /* failed to parse KeyUpdate */
360 0 : #define FD_TLS_REASON_CCS (1103) /* unexpected ChangeCipherSpec record */
361 0 : #define FD_TLS_REASON_ALERT_PARSE (1104) /* malformed alert record */
362 0 : #define FD_TLS_REASON_PEER_ALERT (1105) /* peer sent a fatal alert */
363 0 : #define FD_TLS_REASON_HS_INTERLEAVED (1106) /* record interleaved with a fragmented handshake message */
364 0 : #define FD_TLS_REASON_REC_TYPE (1108) /* unexpected record layer content type */
365 0 : #define FD_TLS_REASON_REC_OVERFLOW (1109) /* record larger than the protocol allows */
366 0 : #define FD_TLS_REASON_HS_KEY_CHANGE (1110) /* handshake data follows a key change in the same record */
367 0 : #define FD_TLS_REASON_HS_MSG_SIZE (1111) /* handshake message length invalid */
368 0 : #define FD_TLS_REASON_REC_MAC (1112) /* record failed authentication */
369 0 : #define FD_TLS_REASON_REC_PADDING (1113) /* record has no content type byte */
370 :
371 : FD_PROTOTYPES_BEGIN
372 :
373 : FD_FN_CONST ulong
374 : fd_tls_align( void );
375 :
376 : FD_FN_CONST ulong
377 : fd_tls_footprint( void );
378 :
379 : /* TODO document new/join/leave/delete */
380 :
381 : void *
382 : fd_tls_new( void * mem );
383 :
384 : fd_tls_t *
385 : fd_tls_join( void * );
386 :
387 : void *
388 : fd_tls_leave( fd_tls_t * );
389 :
390 : void *
391 : fd_tls_delete( void * );
392 :
393 : char const *
394 : fd_tls_alert_cstr( uint alert );
395 :
396 : char const *
397 : fd_tls_reason_cstr( uint reason );
398 :
399 : /* fd_tls_server_handshake ingests a TLS message from the client.
400 : Synchronously processes the message (API may become async in the
401 : future). Record must be complete (does not defragment). Returns
402 : number of bytes read on success. On failure, returns negated TLS
403 : alert code. */
404 :
405 : long
406 : fd_tls_server_handshake( fd_tls_t const * tls,
407 : fd_tls_estate_srv_t * handshake,
408 : void const * record,
409 : ulong record_sz,
410 : uint encryption_level );
411 :
412 : /* fd_tls_client_handshake is the client-side equivalent of
413 : fd_tls_server_handshake. Must not be called with messages sent after
414 : the handshake was completed (such as NewSessionTicket). */
415 :
416 : long
417 : fd_tls_client_handshake( fd_tls_t const * client,
418 : fd_tls_estate_cli_t * handshake,
419 : void const * record,
420 : ulong record_sz,
421 : uint encryption_level );
422 :
423 : static inline long
424 : fd_tls_handshake( fd_tls_t const * tls,
425 : fd_tls_estate_t * handshake,
426 : void const * record,
427 : ulong record_sz,
428 0 : uint encryption_level ) {
429 0 : if( handshake->base.server )
430 0 : return fd_tls_server_handshake( tls, &handshake->srv, record, record_sz, encryption_level );
431 0 : else
432 0 : return fd_tls_client_handshake( tls, &handshake->cli, record, record_sz, encryption_level );
433 0 : }
434 :
435 : /* fd_tls_hkdf_expand_label implements the TLS 1.3 HKDF-Expand function
436 : with SHA-256. Writes the resulting hash to out. secret is a 32 byte
437 : secret value. label points to the label string. label_sz is the
438 : number of chars in label (not including terminating NUL). context
439 : points to the context byte array. context_sz is the number of bytes
440 : in context.
441 :
442 : Constraints:
443 :
444 : out !=NULL
445 : secret!=NULL
446 : label_sz ==0 || label !=NULL
447 : context_sz==0 || context!=NULL
448 : 1<=out_sz <=32
449 : 0<=label_sz <=64
450 : 0<=context_sz<=64 */
451 :
452 : void *
453 : fd_tls_hkdf_expand_label( uchar * out,
454 : ulong out_sz,
455 : uchar const secret[ static 32 ],
456 : char const * label,
457 : ulong label_sz,
458 : uchar const * context,
459 : ulong context_sz );
460 :
461 : FD_PROTOTYPES_END
462 :
463 : #endif /* HEADER_fd_src_waltz_tls_fd_tls_h */
|