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 :
6 : /* fd_tls implements a subset of the TLS v1.3 (RFC 8446) handshake
7 : protocol.
8 :
9 : fd_tls is not a general purpose TLS library. It only provides the
10 : TLS components required to secure peer-to-peer QUIC connections as
11 : they appear in Solana network protocol. Specifics are listed below.
12 :
13 : Older TLS versions, such as TLS v1.2, are not supported.
14 :
15 : ### Peer Authentication
16 :
17 : Peers are authenticated via Ed25519 using the TLS v1.3 raw public key
18 : (RPK) extension. Minimal support for X.509 is included. Client
19 : cert authentication is optional for fd_tls_client_t and mandatory
20 : for fd_tls_server_t.
21 :
22 : ### Key Exchange
23 :
24 : Peers exchange symmetric keys using X25519, an Elliptic Curve Diffie-
25 : Hellman key exchange scheme using Curve25519. Pre-shared keys and
26 : other key exchange schemes are currently not supported.
27 :
28 : ### Data Confidentiality and Integratity
29 :
30 : fd_tls provides an API for the TLS_AES_128_GCM_SHA256 cipher suite.
31 : Other cipher suites are currently not supported.
32 :
33 : ### References
34 :
35 : This library implements parts of protocols specified in the following
36 : IETF RFCs:
37 :
38 : RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
39 : https://datatracker.ietf.org/doc/html/rfc8446
40 :
41 : RFC 6066: Transport Layer Security (TLS) Extensions:
42 : Extension Definitions
43 : https://datatracker.ietf.org/doc/html/rfc6066
44 :
45 : RFC 9001: Using TLS to Secure QUIC
46 : https://datatracker.ietf.org/doc/html/rfc9001
47 :
48 : RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral
49 : Parameters for Transport Layer Security (TLS)
50 : RFC 4492: Elliptic Curve Cryptography (ECC) Cipher Suites for
51 : Transport Layer Security (TLS)
52 : https://datatracker.ietf.org/doc/html/rfc7919
53 : https://datatracker.ietf.org/doc/html/rfc4492
54 :
55 : RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS)
56 : https://datatracker.ietf.org/doc/html/rfc7250
57 :
58 : RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)
59 : https://datatracker.ietf.org/doc/html/rfc8032
60 : Note: fd_ed25519 uses stricter signature malleability checks!
61 :
62 : RFC 7748: Elliptic Curves for Security
63 : https://datatracker.ietf.org/doc/html/rfc7748
64 :
65 : RFC 5288: AES Galois Counter Mode (GCM) Cipher Suites for TLS
66 : https://datatracker.ietf.org/doc/html/rfc5288 */
67 :
68 : /* Callbacks **********************************************************/
69 :
70 : /* fd_tls_secrets_fn_t is called by fd_tls when new encryption secrets
71 : have been generated. {recv/send}_secret are used for incoming/out-
72 : going data respectively and point to a 32-byte buffer valid for the
73 : lifetime of the function call. This function is invoked for each
74 : new encryption_level, which is FD_TLS_LEVEL_{HANDSHAKE,APPLICATION}.
75 : It is safe to discard handshake-level decryption secrets after the
76 : handshake has been completed. */
77 :
78 : typedef void
79 : (* fd_tls_secrets_fn_t)( void const * handshake,
80 : void const * recv_secret,
81 : void const * send_secret,
82 : uint encryption_level );
83 :
84 : /* fd_tls_sendmsg_fn_t is called by fd_tls to request transmission of a
85 : TLS message to the peer. msg points to a buffer containing msg_sz
86 : message bytes. The smallest message size is 4 bytes (the size of a
87 : message header). encryption_level indicates which key to use.
88 : flush==0 when another message for the same conn will follow
89 : immediately on return. flush==1 hints that no more sendmsg callbacks
90 : are issued until the next call to fd_tls_server_handshake. It is
91 : safe to "flush" (i.e. transmit data out through the NIC) even when
92 : flush==0. Returns 1 on success and 0 on failure. */
93 :
94 : typedef int
95 : (* fd_tls_sendmsg_fn_t)( void const * handshake,
96 : void const * msg,
97 : ulong msg_sz,
98 : uint encryption_level,
99 : int flush );
100 :
101 : /* fd_tls_quic_tp_self_fn_t is called by fd_tls to request QUIC
102 : transport params to be sent to the peer. quic_tp points to the
103 : buffer that may hold serialized QUIC transport parameters (RFC
104 : Section 18). quic_tp_bufsz is the size of the buffer at quic_tp.
105 : Return value is actual serialized (<=quic_tp_bufsz) on success.
106 : On failure, returns (>quic_tp_bufsz) to indicate insufficient bufsz.
107 : (Zero implies success, however!) */
108 :
109 : typedef ulong __attribute__((warn_unused_result))
110 : (* fd_tls_quic_tp_self_fn_t)( void * handshake,
111 : uchar * quic_tp,
112 : ulong quic_tp_bufsz );
113 :
114 : /* fd_tls_quic_tp_peer_fn_t is called by fd_tls to inform the user of
115 : the peer's QUIC transport params. quic_tp points to the serialized
116 : QUIC transport parameters (RFC 9000 Section 18). quic_tp_sz is the
117 : serialized size. Lifetime of quic_tp buffer ends at return. fd_tls
118 : does not do any validation on the peer's QUIC TP -- Please ensure
119 : your deserializer is robust given arbitrary data. */
120 :
121 : typedef void
122 : (* fd_tls_quic_tp_peer_fn_t)( void * handshake,
123 : uchar const * quic_tp,
124 : ulong quic_tp_sz );
125 :
126 : /* fd_tls_rand_vt_t is an abstraction for retrieving secure pseudorandom
127 : values. When fd_tls needs random values, it calls fd_tls_rand_fn_t.
128 :
129 : ctx is an arbitrary pointer that is provided as a callback argument.
130 : buf points to a buffer of bufsz bytes that is to be filled with
131 : cryptographically secure randomness. bufsz is usually 32 bytes.
132 : Assume buf is unaligned. Returns buf on success and NULL on failure.
133 :
134 : Function must not block, but may synchronously pre-calculate a
135 : reasonable amount of data ahead of time. NULL return value implies
136 : inability to keep up with demand for random values. In this case,
137 : function should return NULL. Function should minimize side effects
138 : (notably, should not log).
139 :
140 : TODO API considerations:
141 : - read() style error codes?
142 : - Buffering to reduce amount of virtual function calls? */
143 :
144 : typedef void *
145 : (* fd_tls_rand_fn_t)( void * ctx,
146 : void * buf,
147 : ulong bufsz );
148 :
149 : struct fd_tls_rand_vt {
150 : void * ctx;
151 : fd_tls_rand_fn_t rand_fn;
152 : };
153 :
154 : typedef struct fd_tls_rand_vt fd_tls_rand_t;
155 :
156 : static inline void *
157 : fd_tls_rand( fd_tls_rand_t const * rand,
158 : void * buf,
159 12096 : ulong bufsz ) {
160 12096 : return rand->rand_fn( rand->ctx, buf, bufsz );
161 12096 : }
162 :
163 : /* fd_tls_sign_fn_t is called by by fd_tls to request signing of a
164 : TLS 1.3 certificate verify payload.
165 :
166 : ctx is an arbitrary pointer that is provided as a callback argument.
167 : sig points to a 64 byte buffer where the implementor should store the
168 : ed25519 signature of the payload. Payload will point to a 130 byte
169 : buffer containing the TLS 1.3 CertificateVerify payload.
170 :
171 : This function must not fail. Lifetime of the payload buffer ends at
172 : return. */
173 :
174 : typedef void
175 : (* fd_tls_sign_fn_t)( void * ctx,
176 : uchar sig[ static 64 ],
177 : uchar const payload[ static 130 ] );
178 :
179 : struct fd_tls_sign_vt {
180 : void * ctx;
181 : fd_tls_sign_fn_t sign_fn;
182 : };
183 :
184 : typedef struct fd_tls_sign_vt fd_tls_sign_t;
185 :
186 : static inline void
187 : fd_tls_sign( fd_tls_sign_t const * sign,
188 : uchar sig[ static 64 ],
189 6030 : uchar const payload[ static 130 ] ) {
190 6030 : sign->sign_fn( sign->ctx, sig, payload );
191 6030 : }
192 :
193 : extern char const fd_tls13_cli_sign_prefix[ 98 ];
194 :
195 : /* Public API *********************************************************/
196 :
197 : /* Handshake state identifiers */
198 :
199 0 : #define FD_TLS_HS_FAIL ( 0) /* client, server */
200 66306 : #define FD_TLS_HS_CONNECTED ( 1) /* client, server */
201 24291 : #define FD_TLS_HS_START ( 2) /* client, server */
202 0 : #define FD_TLS_HS_WAIT_CERT ( 3) /* client, server */
203 12060 : #define FD_TLS_HS_WAIT_CV ( 4) /* client, server */
204 24120 : #define FD_TLS_HS_WAIT_FINISHED ( 5) /* client, server */
205 12099 : #define FD_TLS_HS_WAIT_SH ( 6) /* client */
206 12060 : #define FD_TLS_HS_WAIT_EE ( 7) /* client */
207 12060 : #define FD_TLS_HS_WAIT_CERT_CR ( 8) /* client */
208 :
209 : /* TLS encryption levels */
210 :
211 12 : #define FD_TLS_LEVEL_INITIAL (0)
212 : #define FD_TLS_LEVEL_EARLY (1)
213 24108 : #define FD_TLS_LEVEL_HANDSHAKE (2)
214 24108 : #define FD_TLS_LEVEL_APPLICATION (3)
215 :
216 : /* FD_TLS_SERVER_CERT_SZ_MAX is the max permitted size of the DER-
217 : serialized X.509 server certificate. */
218 :
219 : #define FD_TLS_SERVER_CERT_SZ_MAX (1011UL)
220 :
221 : /* FD_TLS_SERVER_CERT_MSG_SZ_MAX is the max permitted size of the pre-
222 : buffered X.509 server certificate message. */
223 :
224 : #define FD_TLS_SERVER_CERT_MSG_SZ_MAX (FD_TLS_SERVER_CERT_SZ_MAX+13UL)
225 :
226 : /* FD_TLS_EXT_QUIC_PARAMS_SZ is the max permitted byte size of encoded
227 : QUIC transport parameters */
228 :
229 12090 : # define FD_TLS_EXT_QUIC_PARAMS_SZ_MAX (510UL)
230 :
231 : /* fd_tls_t contains the local TLS config. It is typically shared
232 : across multiple TLS handshakes. */
233 :
234 : struct fd_tls {
235 : fd_tls_rand_t rand;
236 : fd_tls_secrets_fn_t secrets_fn;
237 : fd_tls_sendmsg_fn_t sendmsg_fn;
238 :
239 : /* QUIC specific callbacks -- Only called if quic flag is set.
240 : TODO: Will optional function pointers stall the pipeline? */
241 : fd_tls_quic_tp_self_fn_t quic_tp_self_fn;
242 : fd_tls_quic_tp_peer_fn_t quic_tp_peer_fn;
243 :
244 : /* key_{private,public}_key is an X25519 key pair. During the TLS
245 : handshake, it is used to establish symmetric encryption keys.
246 : kex_private_key is an arbitrary 32 byte vector. It is recommended
247 : to generate a new X25519 key on startup from cryptographically
248 : secure randomness. kex_public_key is the corresponding public key
249 : curve point derived via fd_x25519_public.
250 :
251 : Security notes:
252 : - May not be changed while conns are active. */
253 : uchar kex_private_key[ 32 ];
254 : uchar kex_public_key [ 32 ];
255 :
256 : /* Signing function holding the Ed25519 key pair that identifies the
257 : server. During TLS handshakes, used to sign a transcript of the
258 : handshake to prove to the peer that we are in possession of this
259 : key. This function should sign with the Solana node identity key.
260 :
261 : Security notes:
262 : - May not be changed while conns are active.
263 : - Using a public key that is not derived from the private key may
264 : reveal the private key (!!!) */
265 : fd_tls_sign_t sign;
266 :
267 : /* cert_public_key is the Ed25519 public key that identifies the
268 : server. Must be the public key corresponding to the Solana node
269 : identity key used by the signer function above. */
270 : uchar cert_public_key [ 32 ];
271 :
272 : /* X.509 certificate to present to peer (optional).
273 : SubjectPublicKeyInfo must be Ed25519 and match cert_public_key. */
274 : uchar cert_x509[ FD_TLS_SERVER_CERT_MSG_SZ_MAX ];
275 : ulong cert_x509_sz;
276 :
277 : /* ALPN protocol identifier. Written by fd_tls_server_set_alpn.
278 : Format: <1 byte length prefix> <ASCII chars>.
279 : Is not NUL delimited. */
280 : uchar alpn[ 32 ];
281 : ulong alpn_sz;
282 :
283 : /* Flags */
284 : ulong quic : 1;
285 : ulong _flags_reserved : 63;
286 : };
287 :
288 : typedef struct fd_tls fd_tls_t;
289 :
290 : /* Extended Alert Reasons *********************************************/
291 :
292 : /* fd_tls-specific error codes to identify reasons for alerts. These
293 : can help with debugging when the error cause is not evident by the
294 : alert itself. */
295 :
296 0 : #define FD_TLS_REASON_NULL ( 0)
297 :
298 0 : #define FD_TLS_REASON_ILLEGAL_STATE ( 1) /* illegal hs state */
299 0 : #define FD_TLS_REASON_SENDMSG_FAIL ( 2) /* sendmsg callback failed */
300 0 : #define FD_TLS_REASON_WRONG_ENC_LVL ( 3) /* wrong encryption level */
301 0 : #define FD_TLS_REASON_RAND_FAIL ( 4) /* rand fn failed */
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 :
308 0 : #define FD_TLS_REASON_CH_EXPECTED (101) /* wanted ClientHello, got another msg type */
309 0 : #define FD_TLS_REASON_CH_PARSE (103) /* failed to parse ClientHello */
310 0 : #define FD_TLS_REASON_CH_ENCODE (104) /* failed to encode ClientHello */
311 0 : #define FD_TLS_REASON_CH_NO_QUIC (106) /* Missing QUIC transport params in ClientHello */
312 0 : #define FD_TLS_REASON_CH_RETRY_KS (107) /* ClientHello still missing key share after a retry */
313 0 : #define FD_TLS_REASON_CH_NEG_VER (108) /* Unsupported TLS version */
314 0 : #define FD_TLS_REASON_CH_NEG_KX (109) /* Unsupported key exchange alg */
315 0 : #define FD_TLS_REASON_CH_NEG_SIG (110) /* Unsupported signature alg */
316 3 : #define FD_TLS_REASON_CH_NEG_CIPHER (111) /* Unsupported cipher suite */
317 :
318 0 : #define FD_TLS_REASON_SH_EXPECTED (201) /* wanted ServerHello, got another msg type */
319 3 : #define FD_TLS_REASON_SH_PARSE (203) /* failed to parse ServerHello */
320 0 : #define FD_TLS_REASON_SH_ENCODE (204) /* failed to encode ServerHello */
321 :
322 0 : #define FD_TLS_REASON_EE_NO_QUIC (301) /* Missing QUIC transport params in EncryptedExtensions */
323 0 : #define FD_TLS_REASON_EE_EXPECTED (302) /* wanted EncryptedExtensions, got another msg type */
324 0 : #define FD_TLS_REASON_EE_PARSE (304) /* failed to parse EncryptedExtensions */
325 0 : #define FD_TLS_REASON_EE_ENCODE (305) /* failed to encode EncryptedExtensions */
326 0 : #define FD_TLS_REASON_QUIC_TP_OVERSZ (306) /* Buffer overflow in QUIC transport params callback */
327 :
328 0 : #define FD_TLS_REASON_CV_EXPECTED (401) /* wanted CertificateVerify, got another msg type */
329 0 : #define FD_TLS_REASON_CV_SIGALG (402) /* CertificateVerify sig is not Ed25519 */
330 0 : #define FD_TLS_REASON_CV_PARSE (404) /* failed to parse CertificateVerify */
331 0 : #define FD_TLS_REASON_CV_ENCODE (405) /* failed to encode CertificateVerify */
332 :
333 0 : #define FD_TLS_REASON_CERT_CR_EXPECTED (501) /* wanted Certificate or CertificateRequest, got another msg type */
334 0 : #define FD_TLS_REASON_CERT_CR_PARSE (503) /* failed to parse Certificate or CertificateRequest */
335 :
336 0 : #define FD_TLS_REASON_CERT_TYPE (601) /* unsupported certificate type */
337 0 : #define FD_TLS_REASON_CERT_EXPECTED (602) /* wanted Certificate, got another msg type */
338 0 : #define FD_TLS_REASON_CERT_PARSE (604) /* failed to parse Certificate */
339 0 : #define FD_TLS_REASON_X509_PARSE (605) /* X.509 DER parse failed */
340 0 : #define FD_TLS_REASON_SPKI_PARSE (606) /* Subject public key info parse failed */
341 :
342 0 : #define FD_TLS_REASON_CERT_CHAIN_EMPTY (701) /* cert chain contains no certs */
343 0 : #define FD_TLS_REASON_CERT_CHAIN_PARSE (702) /* failed to parse cert chain */
344 :
345 0 : #define FD_TLS_REASON_FINI_PARSE (901) /* invalid Finished message */
346 0 : #define FD_TLS_REASON_FINI_EXPECTED (902) /* wanted Finished, got another msg type */
347 0 : #define FD_TLS_REASON_FINI_FAIL (904) /* Finished data mismatch */
348 :
349 0 : #define FD_TLS_REASON_ALPN_PARSE (1001) /* failed to parse ALPN */
350 0 : #define FD_TLS_REASON_ALPN_NEG (1002) /* ALPN negotiation failed */
351 3 : #define FD_TLS_REASON_NO_ALPN (1003) /* no ALPN extension */
352 :
353 : FD_PROTOTYPES_BEGIN
354 :
355 : FD_FN_CONST ulong
356 : fd_tls_align( void );
357 :
358 : FD_FN_CONST ulong
359 : fd_tls_footprint( void );
360 :
361 : /* TODO document new/join/leave/delete */
362 :
363 : void *
364 : fd_tls_new( void * mem );
365 :
366 : fd_tls_t *
367 : fd_tls_join( void * );
368 :
369 : void *
370 : fd_tls_leave( fd_tls_t * );
371 :
372 : void *
373 : fd_tls_delete( void * );
374 :
375 : char const *
376 : fd_tls_alert_cstr( uint alert );
377 :
378 : char const *
379 : fd_tls_reason_cstr( uint reason );
380 :
381 : /* fd_tls_server_handshake ingests a TLS message from the client.
382 : Synchronously processes the message (API may become async in the
383 : future). Record must be complete (does not defragment). Returns
384 : number of bytes read on success. On failure, returns negated TLS
385 : alert code. */
386 :
387 : long
388 : fd_tls_server_handshake( fd_tls_t const * tls,
389 : fd_tls_estate_srv_t * handshake,
390 : void const * record,
391 : ulong record_sz,
392 : uint encryption_level );
393 :
394 : /* fd_tls_client_handshake is the client-side equivalent of
395 : fd_tls_server_handshake. Must not be called with messages sent after
396 : the handshake was completed (such as NewSessionTicket). */
397 :
398 : long
399 : fd_tls_client_handshake( fd_tls_t const * client,
400 : fd_tls_estate_cli_t * handshake,
401 : void const * record,
402 : ulong record_sz,
403 : uint encryption_level );
404 :
405 : static inline long
406 : fd_tls_handshake( fd_tls_t const * tls,
407 : fd_tls_estate_t * handshake,
408 : void const * record,
409 : ulong record_sz,
410 42192 : uint encryption_level ) {
411 42192 : if( handshake->base.server )
412 12057 : return fd_tls_server_handshake( tls, &handshake->srv, record, record_sz, encryption_level );
413 30135 : else
414 30135 : return fd_tls_client_handshake( tls, &handshake->cli, record, record_sz, encryption_level );
415 42192 : }
416 :
417 : /* fd_tls_hkdf_expand_label implements the TLS 1.3 HKDF-Expand function
418 : with SHA-256. Writes the resulting hash to out. secret is a 32 byte
419 : secret value. label points to the label string. label_sz is the
420 : number of chars in label (not including terminating NUL). context
421 : points to the context byte array. context_sz is the number of bytes
422 : in context.
423 :
424 : Constraints:
425 :
426 : out !=NULL
427 : secret!=NULL
428 : label_sz ==0 || label !=NULL
429 : context_sz==0 || context!=NULL
430 : 1<=out_sz <=32
431 : 0<=label_sz <=64
432 : 0<=context_sz<=64 */
433 :
434 : void *
435 : fd_tls_hkdf_expand_label( uchar * out,
436 : ulong out_sz,
437 : uchar const secret[ static 32 ],
438 : char const * label,
439 : ulong label_sz,
440 : uchar const * context,
441 : ulong context_sz );
442 :
443 : FD_PROTOTYPES_END
444 :
445 : #endif /* HEADER_fd_src_waltz_tls_fd_tls_h */
|