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