Line data Source code
1 : #ifndef HEADER_src_ballet_tls_fd_tls_h
2 : #define HEADER_src_ballet_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 12039 : ulong bufsz ) {
160 12039 : return rand->rand_fn( rand->ctx, buf, bufsz );
161 12039 : }
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 6018 : uchar const payload[ static 130 ] ) {
190 6018 : sign->sign_fn( sign->ctx, sig, payload );
191 6018 : }
192 :
193 : /* Public API *********************************************************/
194 :
195 : /* Handshake state identifiers */
196 :
197 0 : #define FD_TLS_HS_FAIL ( 0) /* client, server */
198 24066 : #define FD_TLS_HS_CONNECTED ( 1) /* client, server */
199 72576 : #define FD_TLS_HS_START ( 2) /* client, server */
200 0 : #define FD_TLS_HS_WAIT_CERT ( 3) /* client, server */
201 12036 : #define FD_TLS_HS_WAIT_CV ( 4) /* client, server */
202 24072 : #define FD_TLS_HS_WAIT_FINISHED ( 5) /* client, server */
203 12042 : #define FD_TLS_HS_WAIT_SH ( 6) /* client */
204 12036 : #define FD_TLS_HS_WAIT_EE ( 7) /* client */
205 12036 : #define FD_TLS_HS_WAIT_CERT_CR ( 8) /* client */
206 :
207 : /* TLS encryption levels */
208 :
209 6027 : #define FD_TLS_LEVEL_INITIAL (0)
210 : #define FD_TLS_LEVEL_EARLY (1)
211 24060 : #define FD_TLS_LEVEL_HANDSHAKE (2)
212 24060 : #define FD_TLS_LEVEL_APPLICATION (3)
213 :
214 : /* FD_TLS_SERVER_CERT_SZ_MAX is the max permitted size of the DER-
215 : serialized X.509 server certificate. */
216 :
217 : #define FD_TLS_SERVER_CERT_SZ_MAX (1011UL)
218 :
219 : /* FD_TLS_SERVER_CERT_MSG_SZ_MAX is the max permitted size of the pre-
220 : buffered X.509 server certificate message. */
221 :
222 : #define FD_TLS_SERVER_CERT_MSG_SZ_MAX (FD_TLS_SERVER_CERT_SZ_MAX+13UL)
223 :
224 : /* FD_TLS_EXT_QUIC_PARAMS_SZ is the max permitted byte size of encoded
225 : QUIC transport parameters */
226 :
227 12030 : # define FD_TLS_EXT_QUIC_PARAMS_SZ_MAX (510UL)
228 :
229 : /* fd_tls_t contains the local TLS config. It is typically shared
230 : across multiple TLS handshakes. */
231 :
232 : struct fd_tls {
233 : fd_tls_rand_t rand;
234 : fd_tls_secrets_fn_t secrets_fn;
235 : fd_tls_sendmsg_fn_t sendmsg_fn;
236 :
237 : /* QUIC specific callbacks -- Only called if quic flag is set.
238 : TODO: Will optional function pointers stall the pipeline? */
239 : fd_tls_quic_tp_self_fn_t quic_tp_self_fn;
240 : fd_tls_quic_tp_peer_fn_t quic_tp_peer_fn;
241 :
242 : /* key_{private,public}_key is an X25519 key pair. During the TLS
243 : handshake, it is used to establish symmetric encryption keys.
244 : kex_private_key is an arbitrary 32 byte vector. It is recommended
245 : to generate a new X25519 key on startup from cryptographically
246 : secure randomness. kex_public_key is the corresponding public key
247 : curve point derived via fd_x25519_public.
248 :
249 : Security notes:
250 : - May not be changed while conns are active. */
251 : uchar kex_private_key[ 32 ];
252 : uchar kex_public_key [ 32 ];
253 :
254 : /* Signing function holding the Ed25519 key pair that identifies the
255 : server. During TLS handshakes, used to sign a transcript of the
256 : handshake to prove to the peer that we are in possession of this
257 : key. This function should sign with the Solana node identity key.
258 :
259 : Security notes:
260 : - May not be changed while conns are active.
261 : - Using a public key that is not derived from the private key may
262 : reveal the private key (!!!) */
263 : fd_tls_sign_t sign;
264 :
265 : /* cert_public_key is the Ed25519 public key that identifies the
266 : server. Must be the public key corresponding to the Solana node
267 : identity key used by the signer function above. */
268 : uchar cert_public_key [ 32 ];
269 :
270 : /* X.509 certificate to present to peer (optional).
271 : SubjectPublicKeyInfo must be Ed25519 and match cert_public_key. */
272 : uchar cert_x509[ FD_TLS_SERVER_CERT_MSG_SZ_MAX ];
273 : ulong cert_x509_sz;
274 :
275 : /* ALPN protocol identifier. Written by fd_tls_server_set_alpn.
276 : Format: <1 byte length prefix> <ASCII chars>.
277 : Is not NUL delimited. */
278 : uchar alpn[ 32 ];
279 : ulong alpn_sz;
280 :
281 : /* Flags */
282 : ulong quic : 1;
283 : ulong _flags_reserved : 63;
284 : };
285 :
286 : typedef struct fd_tls fd_tls_t;
287 :
288 : /* Extended Alert Reasons *********************************************/
289 :
290 : /* fd_tls-specific error codes to identify reasons for alerts. These
291 : can help with debugging when the error cause is not evident by the
292 : alert itself. */
293 :
294 0 : #define FD_TLS_REASON_NULL ( 0)
295 :
296 0 : #define FD_TLS_REASON_ILLEGAL_STATE ( 1) /* illegal hs state */
297 0 : #define FD_TLS_REASON_SENDMSG_FAIL ( 2) /* sendmsg callback failed */
298 0 : #define FD_TLS_REASON_WRONG_ENC_LVL ( 3) /* wrong encryption level */
299 0 : #define FD_TLS_REASON_RAND_FAIL ( 4) /* rand fn failed */
300 :
301 0 : #define FD_TLS_REASON_X25519_FAIL ( 9) /* fd_x25519_exchange failed */
302 0 : #define FD_TLS_REASON_NO_X509 (10) /* no X.509 cert installed */
303 0 : #define FD_TLS_REASON_WRONG_PUBKEY (11) /* peer cert has different pubkey than expected */
304 0 : #define FD_TLS_REASON_ED25519_FAIL (12) /* Ed25519 signature validation failed */
305 :
306 0 : #define FD_TLS_REASON_CH_EXPECTED (101) /* wanted ClientHello, got another msg type */
307 0 : #define FD_TLS_REASON_CH_PARSE (103) /* failed to parse ClientHello */
308 0 : #define FD_TLS_REASON_CH_ENCODE (104) /* failed to encode ClientHello */
309 3 : #define FD_TLS_REASON_CH_CRYPTO_NEG (105) /* ClientHello crypto negotiation failed */
310 0 : #define FD_TLS_REASON_CH_NO_QUIC (106) /* Missing QUIC transport params in ClientHello */
311 :
312 0 : #define FD_TLS_REASON_SH_EXPECTED (201) /* wanted ServerHello, got another msg type */
313 3 : #define FD_TLS_REASON_SH_PARSE (203) /* failed to parse ServerHello */
314 0 : #define FD_TLS_REASON_SH_ENCODE (204) /* failed to encode ServerHello */
315 :
316 0 : #define FD_TLS_REASON_EE_NO_QUIC (301) /* Missing QUIC transport params in EncryptedExtensions */
317 0 : #define FD_TLS_REASON_EE_EXPECTED (302) /* wanted EncryptedExtensions, got another msg type */
318 0 : #define FD_TLS_REASON_EE_PARSE (304) /* failed to parse EncryptedExtensions */
319 0 : #define FD_TLS_REASON_EE_ENCODE (305) /* failed to encode EncryptedExtensions */
320 0 : #define FD_TLS_REASON_QUIC_TP_OVERSZ (306) /* Buffer overflow in QUIC transport params callback */
321 :
322 0 : #define FD_TLS_REASON_CV_EXPECTED (401) /* wanted CertificateVerify, got another msg type */
323 0 : #define FD_TLS_REASON_CV_SIGALG (402) /* CertificateVerify sig is not Ed25519 */
324 0 : #define FD_TLS_REASON_CV_PARSE (404) /* failed to parse CertificateVerify */
325 0 : #define FD_TLS_REASON_CV_ENCODE (405) /* failed to encode CertificateVerify */
326 :
327 0 : #define FD_TLS_REASON_CERT_CR_EXPECTED (501) /* wanted Certificate or CertificateRequest, got another msg type */
328 0 : #define FD_TLS_REASON_CERT_CR_PARSE (503) /* failed to parse Certificate or CertificateRequest */
329 :
330 0 : #define FD_TLS_REASON_CERT_TYPE (601) /* unsupported certificate type */
331 0 : #define FD_TLS_REASON_CERT_EXPECTED (602) /* wanted Certificate, got another msg type */
332 0 : #define FD_TLS_REASON_CERT_PARSE (604) /* failed to parse Certificate */
333 0 : #define FD_TLS_REASON_X509_PARSE (605) /* X.509 DER parse failed */
334 0 : #define FD_TLS_REASON_SPKI_PARSE (606) /* Subject public key info parse failed */
335 :
336 0 : #define FD_TLS_REASON_CERT_CHAIN_EMPTY (701) /* cert chain contains no certs */
337 0 : #define FD_TLS_REASON_CERT_CHAIN_PARSE (702) /* failed to parse cert chain */
338 :
339 0 : #define FD_TLS_REASON_FINI_PARSE (901) /* invalid Finished message */
340 0 : #define FD_TLS_REASON_FINI_EXPECTED (902) /* wanted Finished, got another msg type */
341 0 : #define FD_TLS_REASON_FINI_FAIL (904) /* Finished data mismatch */
342 :
343 0 : #define FD_TLS_REASON_ALPN_PARSE (1001) /* failed to parse ALPN */
344 0 : #define FD_TLS_REASON_ALPN_NEG (1002) /* ALPN negotiation failed */
345 0 : #define FD_TLS_REASON_NO_ALPN (1003) /* no ALPN extension */
346 :
347 : FD_PROTOTYPES_BEGIN
348 :
349 : FD_FN_CONST ulong
350 : fd_tls_align( void );
351 :
352 : FD_FN_CONST ulong
353 : fd_tls_footprint( void );
354 :
355 : /* TODO document new/join/leave/delete */
356 :
357 : void *
358 : fd_tls_new( void * mem );
359 :
360 : fd_tls_t *
361 : fd_tls_join( void * );
362 :
363 : void *
364 : fd_tls_leave( fd_tls_t * );
365 :
366 : void *
367 : fd_tls_delete( void * );
368 :
369 : FD_FN_PURE char const *
370 : fd_tls_alert_cstr( uint alert );
371 :
372 : FD_FN_PURE char const *
373 : fd_tls_reason_cstr( uint reason );
374 :
375 : /* fd_tls_server_handshake ingests a TLS message from the client.
376 : Synchronously processes the message (API may become async in the
377 : future). Record must be complete (does not defragment). Returns
378 : number of bytes read on success. On failure, returns negated TLS
379 : alert code. */
380 :
381 : long
382 : fd_tls_server_handshake( fd_tls_t const * tls,
383 : fd_tls_estate_srv_t * handshake,
384 : void const * record,
385 : ulong record_sz,
386 : uint encryption_level );
387 :
388 : /* fd_tls_client_handshake is the client-side equivalent of
389 : fd_tls_server_handshake. Must not be called with messages sent after
390 : the handshake was completed (such as NewSessionTicket). */
391 :
392 : long
393 : fd_tls_client_handshake( fd_tls_t const * client,
394 : fd_tls_estate_cli_t * handshake,
395 : void const * record,
396 : ulong record_sz,
397 : uint encryption_level );
398 :
399 : static inline long
400 : fd_tls_handshake( fd_tls_t const * tls,
401 : fd_tls_estate_t * handshake,
402 : void const * record,
403 : ulong record_sz,
404 42105 : uint encryption_level ) {
405 42105 : if( handshake->base.server )
406 12030 : return fd_tls_server_handshake( tls, &handshake->srv, record, record_sz, encryption_level );
407 30075 : else
408 30075 : return fd_tls_client_handshake( tls, &handshake->cli, record, record_sz, encryption_level );
409 42105 : }
410 :
411 : /* fd_tls_hkdf_expand_label implements the TLS 1.3 HKDF-Expand function
412 : with SHA-256. Writes the resulting hash to out. secret is a 32 byte
413 : secret value. label points to the label string. label_sz is the
414 : number of chars in label (not including terminating NUL). context
415 : points to the context byte array. context_sz is the number of bytes
416 : in context.
417 :
418 : Constraints:
419 :
420 : out !=NULL
421 : secret!=NULL
422 : label_sz ==0 || label !=NULL
423 : context_sz==0 || context!=NULL
424 : 1<=out_sz <=32
425 : 0<=label_sz <=64
426 : 0<=context_sz<=64 */
427 :
428 : void *
429 : fd_tls_hkdf_expand_label( uchar * out,
430 : ulong out_sz,
431 : uchar const secret[ static 32 ],
432 : char const * label,
433 : ulong label_sz,
434 : uchar const * context,
435 : ulong context_sz );
436 :
437 : FD_PROTOTYPES_END
438 :
439 : #endif /* HEADER_src_ballet_tls_fd_tls_h */
|