Line data Source code
1 : #define _GNU_SOURCE
2 : #include "../tiles.h"
3 :
4 : #include "generated/fd_sign_tile_seccomp.h"
5 :
6 : #include "../keyguard/fd_keyguard.h"
7 : #include "../keyguard/fd_keyload.h"
8 : #include "../keyguard/fd_keyswitch.h"
9 : #include "../../ballet/base58/fd_base58.h"
10 :
11 : #include <errno.h>
12 : #include <sys/mman.h>
13 :
14 0 : #define MAX_IN (32UL)
15 :
16 : /* fd_sign_in_ctx_t is a context object for each in (producer) mcache
17 : connected to the sign tile. */
18 :
19 : typedef struct {
20 : ulong seq;
21 : fd_frag_meta_t * mcache;
22 : uchar * data;
23 : } fd_sign_out_ctx_t;
24 :
25 : typedef struct {
26 : uchar _data[ FD_KEYGUARD_SIGN_REQ_MTU ];
27 :
28 : /* Pre-staged with the public key base58 encoded, followed by "-" in the first bytes */
29 : ulong public_key_base58_sz;
30 : uchar concat[ FD_BASE58_ENCODED_32_SZ+1UL+9UL ];
31 :
32 : uchar event_concat[ 18UL+32UL ];
33 :
34 : int in_role[ MAX_IN ];
35 : uchar * in_data[ MAX_IN ];
36 : ushort in_mtu [ MAX_IN ];
37 :
38 : fd_sign_out_ctx_t out[ MAX_IN ];
39 :
40 : fd_sha512_t sha512 [ 1 ];
41 :
42 : fd_keyswitch_t * keyswitch;
43 :
44 : uchar * public_key;
45 : uchar * private_key;
46 : } fd_sign_ctx_t;
47 :
48 : FD_FN_CONST static inline ulong
49 3 : scratch_align( void ) {
50 3 : return alignof( fd_sign_ctx_t );
51 3 : }
52 :
53 : FD_FN_PURE static inline ulong
54 3 : scratch_footprint( fd_topo_tile_t const * tile ) {
55 3 : (void)tile;
56 3 : ulong l = FD_LAYOUT_INIT;
57 3 : l = FD_LAYOUT_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
58 3 : return FD_LAYOUT_FINI( l, scratch_align() );
59 3 : }
60 :
61 : static void FD_FN_SENSITIVE
62 0 : derive_fields( fd_sign_ctx_t * ctx ) {
63 0 : uchar check_public_key[ 32 ];
64 0 : fd_ed25519_public_from_private( check_public_key, ctx->private_key, ctx->sha512 );
65 0 : if( FD_UNLIKELY( memcmp( check_public_key, ctx->public_key, 32UL ) ) )
66 0 : FD_LOG_EMERG(( "The public key in the identity key file does not match the public key derived from the private key. "
67 0 : "Firedancer will not use the key pair to sign as it might leak the private key." ));
68 :
69 0 : fd_base58_encode_32( ctx->public_key, &ctx->public_key_base58_sz, (char *)ctx->concat );
70 0 : ctx->concat[ ctx->public_key_base58_sz ] = '-';
71 :
72 0 : memcpy( ctx->event_concat, "FD_METRICS_REPORT-", 18UL );
73 0 : }
74 :
75 : static void FD_FN_SENSITIVE
76 0 : during_housekeeping_sensitive( fd_sign_ctx_t * ctx ) {
77 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
78 0 : memcpy( ctx->private_key, ctx->keyswitch->bytes, 32UL );
79 0 : explicit_bzero( ctx->keyswitch->bytes, 32UL );
80 0 : FD_COMPILER_MFENCE();
81 0 : memcpy( ctx->public_key, ctx->keyswitch->bytes+32UL, 32UL );
82 :
83 0 : derive_fields( ctx );
84 0 : fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
85 0 : }
86 0 : }
87 :
88 : static inline void
89 0 : during_housekeeping( fd_sign_ctx_t * ctx ) {
90 0 : during_housekeeping_sensitive( ctx );
91 0 : }
92 :
93 : /* during_frag is called between pairs for sequence number checks, as
94 : we are reading incoming frags. We don't actually need to copy the
95 : fragment here, see fd_dedup.c for why we do this.*/
96 :
97 : static void FD_FN_SENSITIVE
98 : during_frag_sensitive( void * _ctx,
99 : ulong in_idx,
100 : ulong seq,
101 : ulong sig,
102 : ulong chunk,
103 0 : ulong sz ) {
104 0 : (void)seq;
105 0 : (void)sig;
106 0 : (void)chunk;
107 :
108 0 : fd_sign_ctx_t * ctx = (fd_sign_ctx_t *)_ctx;
109 0 : FD_TEST( in_idx<MAX_IN );
110 :
111 0 : int role = ctx->in_role[ in_idx ];
112 0 : uint mtu = ctx->in_mtu [ in_idx ];
113 :
114 0 : if( sz>mtu ) {
115 0 : FD_LOG_EMERG(( "oversz signing request (role=%d sz=%lu mtu=%u)", role, sz, mtu ));
116 0 : }
117 0 : fd_memcpy( ctx->_data, ctx->in_data[ in_idx ], sz );
118 0 : }
119 :
120 :
121 : static void
122 : during_frag( void * _ctx,
123 : ulong in_idx,
124 : ulong seq,
125 : ulong sig,
126 : ulong chunk,
127 : ulong sz,
128 0 : ulong ctl FD_PARAM_UNUSED ) {
129 0 : during_frag_sensitive( _ctx, in_idx, seq, sig, chunk, sz );
130 0 : }
131 :
132 : static void FD_FN_SENSITIVE
133 : after_frag_sensitive( void * _ctx,
134 : ulong in_idx,
135 : ulong seq,
136 : ulong sig,
137 : ulong sz,
138 : ulong tsorig,
139 : ulong tspub,
140 0 : fd_stem_context_t * stem ) {
141 0 : (void)seq;
142 0 : (void)tsorig;
143 0 : (void)tspub;
144 0 : (void)stem;
145 :
146 0 : fd_sign_ctx_t * ctx = (fd_sign_ctx_t *)_ctx;
147 :
148 0 : int sign_type = (int)(uint)sig;
149 :
150 0 : FD_TEST( in_idx<MAX_IN );
151 :
152 0 : int role = ctx->in_role[ in_idx ];
153 :
154 0 : fd_keyguard_authority_t authority = {0};
155 0 : memcpy( authority.identity_pubkey, ctx->public_key, 32 );
156 :
157 0 : if( FD_UNLIKELY( !fd_keyguard_payload_authorize( &authority, ctx->_data, sz, role, sign_type ) ) ) {
158 0 : FD_LOG_EMERG(( "fd_keyguard_payload_authorize failed (role=%d sign_type=%d)", role, sign_type ));
159 0 : }
160 :
161 0 : switch( sign_type ) {
162 0 : case FD_KEYGUARD_SIGN_TYPE_ED25519: {
163 0 : fd_ed25519_sign( ctx->out[ in_idx ].data, ctx->_data, sz, ctx->public_key, ctx->private_key, ctx->sha512 );
164 0 : break;
165 0 : }
166 0 : case FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519: {
167 0 : uchar hash[ 32 ];
168 0 : fd_sha256_hash( ctx->_data, sz, hash );
169 0 : fd_ed25519_sign( ctx->out[ in_idx ].data, hash, 32UL, ctx->public_key, ctx->private_key, ctx->sha512 );
170 0 : break;
171 0 : }
172 0 : case FD_KEYGUARD_SIGN_TYPE_PUBKEY_CONCAT_ED25519: {
173 0 : memcpy( ctx->concat+ctx->public_key_base58_sz+1UL, ctx->_data, 9UL );
174 0 : fd_ed25519_sign( ctx->out[ in_idx ].data, ctx->concat, ctx->public_key_base58_sz+1UL+9UL, ctx->public_key, ctx->private_key, ctx->sha512 );
175 0 : break;
176 0 : }
177 0 : case FD_KEYGUARD_SIGN_TYPE_FD_METRICS_REPORT_CONCAT_ED25519: {
178 0 : memcpy( ctx->event_concat+18UL, ctx->_data, 32UL );
179 0 : fd_ed25519_sign( ctx->out[ in_idx ].data, ctx->event_concat, 18UL+32UL, ctx->public_key, ctx->private_key, ctx->sha512 );
180 0 : break;
181 0 : }
182 0 : default:
183 0 : FD_LOG_EMERG(( "invalid sign type: %d", sign_type ));
184 0 : }
185 :
186 0 : fd_mcache_publish( ctx->out[ in_idx ].mcache, 128UL, ctx->out[ in_idx ].seq, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL );
187 0 : ctx->out[ in_idx ].seq = fd_seq_inc( ctx->out[ in_idx ].seq, 1UL );
188 0 : }
189 :
190 : static void
191 : after_frag( void * _ctx,
192 : ulong in_idx,
193 : ulong seq,
194 : ulong sig,
195 : ulong sz,
196 : ulong tsorig,
197 : ulong tspub,
198 0 : fd_stem_context_t * stem ) {
199 0 : after_frag_sensitive( _ctx, in_idx, seq, sig, sz, tsorig, tspub, stem );
200 0 : }
201 :
202 : static void FD_FN_SENSITIVE
203 : privileged_init_sensitive( fd_topo_t * topo,
204 0 : fd_topo_tile_t * tile ) {
205 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
206 :
207 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
208 0 : fd_sign_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
209 :
210 0 : uchar * identity_key = fd_keyload_load( tile->sign.identity_key_path, /* pubkey only: */ 0 );
211 0 : ctx->private_key = identity_key;
212 0 : ctx->public_key = identity_key + 32UL;
213 :
214 : /* The stack can be taken over and reorganized by under AddressSanitizer,
215 : which causes this code to fail. */
216 : #if FD_HAS_ASAN
217 : FD_LOG_WARNING(( "!!! SECURITY WARNING !!! YOU ARE RUNNING THE SIGNING TILE "
218 : "WITH ADDRESS SANITIZER ENABLED. THIS CAN LEAK SENSITIVE "
219 : "DATA INCLUDING YOUR PRIVATE KEYS INTO CORE DUMPS IF THIS "
220 : "PROCESS ABORTS. IT IS HIGHLY ADVISED TO NOT TO RUN IN THIS "
221 : "MODE IN PRODUCTION!" ));
222 : #else
223 : /* Prevent the stack from showing up in core dumps just in case the
224 : private key somehow ends up in there. */
225 0 : FD_TEST( fd_tile_stack0() );
226 0 : FD_TEST( fd_tile_stack_sz() );
227 0 : if( FD_UNLIKELY( madvise( (void*)fd_tile_stack0(), fd_tile_stack_sz(), MADV_DONTDUMP ) ) )
228 0 : FD_LOG_ERR(( "madvise failed (%i-%s)", errno, fd_io_strerror( errno ) ));
229 0 : #endif
230 0 : }
231 :
232 : static void
233 : privileged_init( fd_topo_t * topo,
234 0 : fd_topo_tile_t * tile ) {
235 0 : privileged_init_sensitive( topo, tile );
236 0 : }
237 :
238 : static void FD_FN_SENSITIVE
239 : unprivileged_init_sensitive( fd_topo_t * topo,
240 0 : fd_topo_tile_t * tile ) {
241 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
242 :
243 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
244 0 : fd_sign_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
245 0 : FD_TEST( fd_sha512_join( fd_sha512_new( ctx->sha512 ) ) );
246 :
247 0 : FD_TEST( tile->in_cnt<=MAX_IN );
248 0 : FD_TEST( tile->in_cnt==tile->out_cnt );
249 :
250 0 : ctx->keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->keyswitch_obj_id ) );
251 0 : derive_fields( ctx );
252 :
253 0 : for( ulong i=0UL; i<MAX_IN; i++ ) ctx->in_role[ i ] = -1;
254 :
255 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
256 0 : fd_topo_link_t * in_link = &topo->links[ tile->in_link_id[ i ] ];
257 0 : fd_topo_link_t * out_link = &topo->links[ tile->out_link_id[ i ] ];
258 :
259 0 : if( in_link->mtu > FD_KEYGUARD_SIGN_REQ_MTU ) FD_LOG_CRIT(( "oversz link[%lu].mtu=%lu", i, in_link->mtu ));
260 0 : ctx->in_data[ i ] = in_link->dcache;
261 0 : ctx->in_mtu [ i ] = (ushort)in_link->mtu;
262 :
263 0 : ctx->out[ i ].mcache = out_link->mcache;
264 0 : ctx->out[ i ].data = out_link->dcache;
265 0 : ctx->out[ i ].seq = 0UL;
266 :
267 0 : if( !strcmp( in_link->name, "shred_sign" ) ) {
268 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_LEADER;
269 0 : FD_TEST( !strcmp( out_link->name, "sign_shred" ) );
270 0 : FD_TEST( in_link->mtu==32UL );
271 0 : FD_TEST( out_link->mtu==64UL );
272 0 : } else if ( !strcmp( in_link->name, "gossip_sign" ) ) {
273 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_GOSSIP;
274 0 : FD_TEST( !strcmp( out_link->name, "sign_gossip" ) );
275 0 : FD_TEST( in_link->mtu==2048UL );
276 0 : FD_TEST( out_link->mtu==64UL );
277 0 : } else if ( !strcmp( in_link->name, "repair_sign")) {
278 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_REPAIR;
279 0 : FD_TEST( !strcmp( out_link->name, "sign_repair" ) );
280 0 : FD_TEST( in_link->mtu==2048UL );
281 0 : FD_TEST( out_link->mtu==64UL );
282 0 : } else if ( !strcmp(in_link->name, "voter_sign" ) ) {
283 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_VOTER;
284 0 : FD_TEST( !strcmp( out_link->name, "sign_voter" ) );
285 0 : FD_TEST( in_link->mtu==FD_TXN_MTU );
286 0 : FD_TEST( out_link->mtu==64UL );
287 0 : } else if( !strcmp(in_link->name, "bundle_sign" ) ) {
288 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_BUNDLE;
289 0 : FD_TEST( !strcmp( out_link->name, "sign_bundle" ) );
290 0 : FD_TEST( in_link->mtu==9UL );
291 0 : FD_TEST( out_link->mtu==64UL );
292 0 : } else if( !strcmp(in_link->name, "event_sign" ) ) {
293 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_EVENT;
294 0 : FD_TEST( !strcmp( out_link->name, "sign_event" ) );
295 0 : FD_TEST( in_link->mtu==32UL );
296 0 : FD_TEST( out_link->mtu==64UL );
297 0 : } else if( !strcmp(in_link->name, "pack_sign" ) ) {
298 0 : ctx->in_role[ i ] = FD_KEYGUARD_ROLE_BUNDLE_CRANK;
299 0 : FD_TEST( !strcmp( out_link->name, "sign_pack" ) );
300 0 : FD_TEST( in_link->mtu==1232UL );
301 0 : FD_TEST( out_link->mtu==64UL );
302 0 : } else {
303 0 : FD_LOG_CRIT(( "unexpected link %s", in_link->name ));
304 0 : }
305 0 : }
306 :
307 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
308 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
309 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
310 0 : }
311 :
312 : static void
313 : unprivileged_init( fd_topo_t * topo,
314 0 : fd_topo_tile_t * tile ) {
315 0 : unprivileged_init_sensitive( topo, tile );
316 0 : }
317 :
318 : static ulong
319 : populate_allowed_seccomp( fd_topo_t const * topo,
320 : fd_topo_tile_t const * tile,
321 : ulong out_cnt,
322 0 : struct sock_filter * out ) {
323 0 : (void)topo;
324 0 : (void)tile;
325 :
326 0 : populate_sock_filter_policy_fd_sign_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
327 0 : return sock_filter_policy_fd_sign_tile_instr_cnt;
328 0 : }
329 :
330 : static ulong
331 : populate_allowed_fds( fd_topo_t const * topo,
332 : fd_topo_tile_t const * tile,
333 : ulong out_fds_cnt,
334 0 : int * out_fds ) {
335 0 : (void)topo;
336 0 : (void)tile;
337 :
338 0 : if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
339 :
340 0 : ulong out_cnt = 0;
341 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
342 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
343 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
344 0 : return out_cnt;
345 0 : }
346 :
347 0 : #define STEM_BURST (1UL)
348 :
349 : /* See explanation in fd_pack */
350 0 : #define STEM_LAZY (128L*3000L)
351 :
352 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_sign_ctx_t
353 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_sign_ctx_t)
354 :
355 0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
356 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
357 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
358 :
359 : #include "../../disco/stem/fd_stem.c"
360 :
361 : fd_topo_run_tile_t fd_tile_sign = {
362 : .name = "sign",
363 : .populate_allowed_seccomp = populate_allowed_seccomp,
364 : .populate_allowed_fds = populate_allowed_fds,
365 : .scratch_align = scratch_align,
366 : .scratch_footprint = scratch_footprint,
367 : .privileged_init = privileged_init,
368 : .unprivileged_init = unprivileged_init,
369 : .run = stem_run,
370 : };
|