Line data Source code
1 : #include "fd_failover_tls.h"
2 :
3 : #include "../../ballet/ed25519/fd_x25519.h"
4 : #include "../../ballet/x509/fd_x509_mock.h"
5 :
6 : #include <string.h>
7 :
8 : /* ALPN id, TLS length-prefixed */
9 : static uchar const ALPN[] = "\x17" "fd-failover/on-demand/1";
10 :
11 : FD_STATIC_ASSERT( sizeof(ALPN)==25UL, alpn_size );
12 :
13 : int
14 : fd_failover_tls_ctx_init( fd_failover_tls_ctx_t * ctx,
15 : uchar const public_key[ 32 ],
16 30 : fd_tls_sign_t signer ) {
17 30 : fd_failover_tls_ctx_fini( ctx );
18 :
19 30 : uchar seed[ 32 ];
20 30 : if( FD_UNLIKELY( !fd_rng_secure( seed, 32UL ) ) ) return -1;
21 30 : fd_chacha_rng_t * rng = fd_chacha_rng_join( fd_chacha_rng_new( ctx->rng, FD_CHACHA_RNG_MODE_MOD ) );
22 30 : FD_TEST( rng );
23 30 : fd_chacha_rng_init( rng, seed, FD_CHACHA_RNG_ALGO_CHACHA20 );
24 30 : fd_memzero_explicit( seed, sizeof(seed) );
25 :
26 30 : fd_tls_t * tls = fd_tls_join( fd_tls_new( &ctx->tls ) );
27 30 : FD_TEST( tls );
28 30 : tls->rng = rng;
29 30 : tls->sign = signer;
30 30 : fd_memcpy( tls->cert_public_key, public_key, 32UL );
31 30 : fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
32 30 : tls->cert_x509_sz = FD_X509_MOCK_CERT_SZ;
33 30 : fd_memcpy( tls->alpn, ALPN, sizeof(ALPN)-1UL );
34 30 : tls->alpn_sz = sizeof(ALPN)-1UL;
35 30 : tls->quic = 0;
36 30 : tls->req_client_cert = 1;
37 :
38 : /* The TLS configuration is ready for new connections. */
39 30 : ctx->ready = 1;
40 30 : return 0;
41 30 : }
42 :
43 : void
44 198 : fd_failover_tls_ctx_fini( fd_failover_tls_ctx_t * ctx ) {
45 : /* Wiping the context prevents new connections until reinitialization. */
46 198 : fd_memzero_explicit( ctx, sizeof(*ctx) );
47 198 : }
48 :
49 : int
50 : fd_failover_tls_new( fd_failover_tls_t * tls,
51 : fd_failover_tls_ctx_t * ctx,
52 : int fd,
53 9 : int dial_peer ) {
54 9 : if( FD_UNLIKELY( !ctx->ready ) ) return -1;
55 : /* Start an unverified, unpaired connection. */
56 9 : fd_memset( tls, 0, sizeof(*tls) );
57 9 : tls->fd = fd;
58 9 : fd_tlsrec_conn_init( &tls->conn, &ctx->tls, !dial_peer );
59 : /* Fresh X25519 key share per connection, straight from the kernel. */
60 9 : uchar seed[ 32 ];
61 9 : if( FD_UNLIKELY( !fd_rng_secure( seed, 32UL ) ) ) return -1;
62 9 : fd_chacha_rng_init( ctx->rng, seed, FD_CHACHA_RNG_ALGO_CHACHA20 );
63 9 : fd_memzero_explicit( seed, sizeof(seed) );
64 9 : if( FD_UNLIKELY( !fd_rng_secure( tls->conn.tls.kex_private_key, 32UL ) ) ) return -1;
65 9 : fd_x25519_public( tls->conn.tls.kex_public_key, tls->conn.tls.kex_private_key );
66 9 : fd_tlsrec_sock_init( &tls->sock );
67 9 : return 0;
68 9 : }
69 :
70 : int
71 6 : fd_failover_tls_client_hello( fd_failover_tls_t * tls, uchar * out, ulong * out_sz ) {
72 6 : ulong app_sz = 0UL;
73 6 : return fd_tlsrec_conn_rx( &tls->conn, NULL, out, out_sz, NULL, &app_sz );
74 6 : }
75 :
76 : int
77 6 : fd_failover_tls_accept_flight( fd_failover_tls_t * tls, uchar const * data, ulong sz ) {
78 6 : if( fd_tlsrec_sock_tx_pending( &tls->sock ) || tls->received>FD_FAILOVER_TLS_PREPAIR_MAX ||
79 6 : sz>FD_FAILOVER_TLS_PREPAIR_MAX-tls->received ) return -1;
80 6 : fd_tlsrec_slice_t rx[1];
81 6 : fd_tlsrec_slice_init( rx, (uchar *)data, sz );
82 6 : ulong tx_sz = sizeof(tls->sock.tx_buf);
83 6 : ulong app_sz = 0UL;
84 6 : int rc = fd_tlsrec_conn_rx( &tls->conn, rx, tls->sock.tx_buf, &tx_sz, NULL, &app_sz );
85 6 : tls->sock.tx_sz = tx_sz;
86 6 : tls->received += sz;
87 6 : return rc || !fd_tlsrec_slice_is_empty( rx ) || app_sz ? -1 : 0;
88 6 : }
89 :
90 : void
91 108 : fd_failover_tls_fini( fd_failover_tls_t * tls ) {
92 108 : if( FD_LIKELY( tls->fd!=-1 && fd_tlsrec_conn_is_ready( &tls->conn ) && !tls->conn.tx_closed ) ) {
93 105 : (void)fd_tlsrec_sock_close( &tls->sock, &tls->conn, tls->fd );
94 105 : }
95 108 : fd_tlsrec_conn_fini( &tls->conn );
96 108 : fd_memzero_explicit( &tls->sock, sizeof(tls->sock) );
97 108 : tls->fd = -1;
98 108 : tls->verified = 0;
99 108 : tls->paired = 0;
100 108 : tls->received = 0UL;
101 108 : }
102 :
103 : static int
104 75 : pull( fd_failover_tls_t * tls ) {
105 75 : if( !tls->paired && tls->received>=FD_FAILOVER_TLS_PREPAIR_MAX ) return -1;
106 75 : ulong rx_sz = 0UL;
107 75 : int err = fd_tlsrec_sock_rx( &tls->sock, &tls->conn, tls->fd, &rx_sz );
108 75 : if( !tls->paired ) tls->received += rx_sz;
109 75 : if( FD_LIKELY( !err ) ) return 0;
110 0 : if( err==FD_TLSREC_SOCK_ERR_EOF ) tls->peer_closed = 1;
111 0 : return -1;
112 75 : }
113 :
114 : int
115 12 : fd_failover_tls_handshake( fd_failover_tls_t * tls ) {
116 12 : if( FD_LIKELY( tls->verified ) ) return 1; /* Verification is final for this connection. */
117 : /* A failed handshake stays failed until the channel closes it. */
118 12 : if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) {
119 0 : FD_LOG_DEBUG(( "failover TLS handshake failed: %s", fd_tls_reason_cstr( tls->conn.hs.base.reason ) ));
120 0 : return -1;
121 0 : }
122 12 : if( FD_UNLIKELY( fd_tlsrec_sock_flush( &tls->sock, tls->fd )<0 ) ) return -1;
123 12 : if( FD_LIKELY( !fd_tlsrec_conn_is_ready( &tls->conn ) ) ) {
124 9 : if( FD_UNLIKELY( pull( tls ) ) ) return -1;
125 9 : if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) {
126 0 : FD_LOG_DEBUG(( "failover TLS handshake failed: %s", fd_tls_reason_cstr( tls->conn.hs.base.reason ) ));
127 0 : return -1;
128 0 : }
129 9 : if( FD_LIKELY( !fd_tlsrec_conn_is_ready( &tls->conn ) ) ) return 0; /* Continue on a later poll. */
130 9 : }
131 :
132 : /* Handshake done, the peer signed with some key and agreed on our ALPN.
133 : We keep that key, the channel checks it against HELLO. */
134 :
135 6 : if( !fd_tlsrec_conn_is_server( &tls->conn ) ) {
136 3 : fd_tls_estate_cli_t const * cli = &tls->conn.hs.cli;
137 3 : if( FD_UNLIKELY( cli->server_key_type!=FD_TLS_KEY_ED25519 ||
138 3 : cli->server_pubkey_len!=32UL || !cli->alpn_negotiated ) ) return -1;
139 3 : fd_memcpy( tls->peer_pubkey, cli->server_pubkey, 32UL );
140 3 : } else {
141 3 : fd_tls_estate_srv_t const * srv = &tls->conn.hs.srv;
142 3 : fd_memcpy( tls->peer_pubkey, srv->client_pubkey, 32UL );
143 3 : }
144 : /* TLS verified the peer key, HELLO must still authorize the member. */
145 6 : tls->verified = 1;
146 6 : return 1;
147 6 : }
148 :
149 : long
150 : fd_failover_tls_read( fd_failover_tls_t * tls,
151 : void * buf,
152 69 : ulong sz ) {
153 69 : if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) ) ) return -1L;
154 69 : if( FD_LIKELY( !fd_tlsrec_sock_rx_avail( &tls->sock ) ) ) {
155 66 : if( FD_UNLIKELY( fd_tlsrec_sock_flush( &tls->sock, tls->fd )<0 ) ) return -1L;
156 66 : if( FD_UNLIKELY( pull( tls ) ) ) return -1L;
157 66 : }
158 69 : return (long)fd_tlsrec_sock_rx_pop( &tls->sock, buf, sz );
159 69 : }
160 :
161 : long
162 : fd_failover_tls_write( fd_failover_tls_t * tls,
163 : void const * buf,
164 30 : ulong sz ) {
165 30 : if( FD_UNLIKELY( fd_tlsrec_conn_is_failed( &tls->conn ) || tls->conn.tx_closed ) ) return -1L;
166 30 : int rc = fd_tlsrec_sock_flush( &tls->sock, tls->fd );
167 30 : if( FD_UNLIKELY( rc ) ) return rc<0 ? -1L : 0L; /* Earlier output is still parked. */
168 30 : ulong consumed = 0UL;
169 30 : if( FD_UNLIKELY( fd_tlsrec_sock_tx( &tls->sock, &tls->conn, tls->fd, buf, sz, &consumed ) ) ) return -1L;
170 30 : return (long)consumed;
171 30 : }
|