LCOV - code coverage report
Current view: top level - disco/keyguard - fd_sign_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 167 399 41.9 %
Date: 2026-11-12 04:25:03 Functions: 11 42 26.2 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "../tiles.h"
       3             : 
       4             : #include <linux/futex.h>
       5             : #include "generated/fd_sign_tile_seccomp.h"
       6             : 
       7             : #include "../keyguard/fd_keyguard.h"
       8             : #include "../keyguard/fd_keyguard_bls.h"
       9             : #include "../keyguard/fd_keyload.h"
      10             : #include "../keyguard/fd_keyswitch.h"
      11             : #include "../../discof/admin/fd_adminctl.h"
      12             : #include "../../ballet/base58/fd_base58.h"
      13             : #include "../metrics/fd_metrics.h"
      14             : #include "../../ballet/bls/fd_bls.h"
      15             : 
      16             : #include "../../util/hist/fd_histf.h"
      17             : 
      18             : #include <errno.h>
      19             : #include <sys/mman.h>
      20             : 
      21           0 : #define MAX_IN (32UL)
      22             : 
      23          48 : #define FD_KEYGUARD_BLS_KEY_MAX (FD_KEYGUARD_AUTH_VOTERS_MAX+1UL) /* identity plus authorized voters */
      24             : 
      25             : FD_STATIC_ASSERT( sizeof(fd_keyguard_bls_key_t)*FD_KEYGUARD_BLS_KEY_MAX<=4096UL, bls_keys_fit_protected_page );
      26             : 
      27             : /* fd_sign_in_ctx_t is a context object for each in (producer) mcache
      28             :    connected to the sign tile. */
      29             : 
      30             : struct fd_sign_out_ctx {
      31             :   fd_wksp_t * out_mem;
      32             :   ulong       out_chunk0;
      33             :   ulong       out_wmark;
      34             :   ulong       out_chunk;
      35             : };
      36             : typedef struct fd_sign_out_ctx fd_sign_out_ctx_t;
      37             : 
      38             : struct fd_sign_in_ctx {
      39             :   int              role;
      40             :   fd_wksp_t *      mem;
      41             :   ulong            chunk0;
      42             :   ulong            wmark;
      43             :   ulong            mtu;
      44             : };
      45             : typedef struct fd_sign_in_ctx fd_sign_in_ctx_t;
      46             : 
      47             : typedef struct {
      48             :   uchar             _data[ FD_KEYGUARD_SIGN_REQ_MTU ];
      49             : 
      50             :   /* Pre-staged with the public key base58 encoded, followed by "-" in the first bytes */
      51             :   ulong public_key_base58_sz;
      52             :   uchar concat[ FD_BASE58_ENCODED_32_SZ+1UL+9UL ];
      53             : 
      54             :   fd_sign_in_ctx_t  in[ MAX_IN ];
      55             :   fd_sign_out_ctx_t out[ MAX_IN ];
      56             : 
      57             :   fd_sha512_t       sha512 [ 1 ];
      58             : 
      59             :   fd_keyswitch_t *  keyswitch;
      60             : 
      61             :   fd_keyswitch_t *  av_keyswitch; /* authorized voters */
      62             : 
      63             :   uchar *           public_key;
      64             :   uchar *           private_key;
      65             : 
      66             :   uchar const *     failover_junk_key;     /* the junk key that failover installs after it hands away its staked identity, NULL if none */
      67             :   uchar *           failover_key;          /* the staked identity failover hands off */
      68             : 
      69             :   fd_keyguard_bls_key_t * bls_keys;
      70             : 
      71             :   uchar tip_payment_program     [32];
      72             :   uchar tip_distribution_program[32];
      73             : 
      74             :   ulong             authorized_voters_cnt;
      75             :   uchar             authorized_voter_pubkeys[ FD_KEYGUARD_AUTH_VOTERS_MAX ][ 32UL ];
      76             :   uchar          (* authorized_voter_private_keys)[ 32UL ];
      77             : 
      78             :   fd_histf_t        sign_duration[1];
      79             : } fd_sign_ctx_t;
      80             : 
      81             : FD_FN_CONST static inline ulong
      82           0 : scratch_align( void ) {
      83           0 :   return alignof( fd_sign_ctx_t );
      84           0 : }
      85             : 
      86             : FD_FN_PURE static inline ulong
      87           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
      88           0 :   (void)tile;
      89           0 :   ulong l = FD_LAYOUT_INIT;
      90           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
      91           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
      92           0 : }
      93             : 
      94             : static void FD_FN_SENSITIVE
      95         432 : derive_fields( fd_sign_ctx_t * ctx ) {
      96         432 :   uchar check_public_key[ 32 ];
      97         432 :   fd_ed25519_public_from_private( check_public_key, ctx->private_key, ctx->sha512 );
      98         432 :   if( FD_UNLIKELY( memcmp( check_public_key, ctx->public_key, 32UL ) ) )
      99           0 :     FD_LOG_EMERG(( "The public key in the identity key file does not match the public key derived from the private key. "
     100         432 :                    "Firedancer will not use the key pair to sign as it might leak the private key." ));
     101             : 
     102         432 :   fd_base58_encode_32( ctx->public_key, &ctx->public_key_base58_sz, (char *)ctx->concat );
     103         432 :   ctx->concat[ ctx->public_key_base58_sz ] = '-';
     104             : 
     105         432 :   fd_keyguard_bls_key_derive( &ctx->bls_keys[ 0 ], ctx->public_key, ctx->private_key, ctx->sha512 );
     106         432 : }
     107             : 
     108             : /* A failover switch passes only a public key, we copy the junk key or
     109             :    the failover identity into the identity key. */
     110             : 
     111             : static void FD_FN_SENSITIVE
     112          66 : failover_select( fd_sign_ctx_t * ctx ) {
     113          66 :   uchar const * selected = NULL;
     114          66 :   if(      fd_memeq( ctx->keyswitch->bytes, ctx->failover_junk_key+32UL, 32UL ) ) selected = ctx->failover_junk_key;
     115          39 :   else if( fd_memeq( ctx->keyswitch->bytes, ctx->failover_key+32UL,      32UL ) ) selected = ctx->failover_key;
     116          66 :   if( FD_UNLIKELY( !selected ) ) FD_LOG_ERR(( "identity switch selects a key that is neither the junk key nor the failover identity" ));
     117          63 :   fd_memzero_explicit( ctx->keyswitch->bytes, 64UL );
     118          63 :   fd_memcpy( ctx->private_key, selected,       32UL );
     119          63 :   fd_memcpy( ctx->public_key,  selected+32UL,  32UL );
     120          63 :   derive_fields( ctx );
     121          63 :   fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
     122          63 : }
     123             : 
     124             : static int
     125             : failover_identity_allowed( fd_sign_ctx_t const * ctx,
     126           9 :                            uchar const *         pubkey ) {
     127           9 :   for( ulong i=0UL; i<ctx->authorized_voters_cnt; i++ ) {
     128           0 :     if( fd_memeq( pubkey, ctx->authorized_voter_pubkeys[ i ], 32UL ) ) return 0;
     129           0 :   }
     130           9 :   return 1;
     131           9 : }
     132             : 
     133             : /* Handles the failover keyswitch params (prepare, check, pubkey select)
     134             :    and returns 1 if fully handled.  Returns 0 for a normal keypair switch. */
     135             : 
     136             : static int FD_FN_SENSITIVE
     137         138 : failover_keyswitch( fd_sign_ctx_t * ctx ) {
     138         138 :   if( FD_LIKELY( fd_keyswitch_state_query( ctx->keyswitch )!=FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) return 1;
     139         102 :   ulong param = fd_keyswitch_param_query( ctx->keyswitch );
     140         102 :   if( FD_UNLIKELY( param==FD_KEYSWITCH_PARAM_IDENTITY_PREPARE ) ) {
     141           9 :     int allowed = failover_identity_allowed( ctx, ctx->keyswitch->bytes+32UL );
     142           9 :     if( FD_LIKELY( allowed ) ) fd_memcpy( ctx->failover_key, ctx->keyswitch->bytes, 64UL );
     143           9 :     fd_memzero_explicit( ctx->keyswitch->bytes, 64UL );
     144           9 :     ctx->keyswitch->result = allowed ? FD_ADMINCTL_RESULT_SUCCESS : FD_SET_IDENTITY_RESULT_AUTHORIZED_VOTER;
     145           9 :     fd_keyswitch_state( ctx->keyswitch, allowed ? FD_KEYSWITCH_STATE_COMPLETED : FD_KEYSWITCH_STATE_FAILED );
     146           9 :     return 1;
     147           9 :   }
     148          93 :   if( FD_UNLIKELY( param==FD_KEYSWITCH_PARAM_IDENTITY_CHECK ) ) {
     149           0 :     int allowed = failover_identity_allowed( ctx, ctx->keyswitch->bytes );
     150           0 :     fd_memzero_explicit( ctx->keyswitch->bytes, 64UL );
     151           0 :     ctx->keyswitch->result = allowed ? FD_ADMINCTL_RESULT_SUCCESS : FD_SET_IDENTITY_RESULT_AUTHORIZED_VOTER;
     152           0 :     fd_keyswitch_state( ctx->keyswitch, allowed ? FD_KEYSWITCH_STATE_COMPLETED : FD_KEYSWITCH_STATE_FAILED );
     153           0 :     return 1;
     154           0 :   }
     155          93 :   if( FD_UNLIKELY( param==FD_KEYSWITCH_PARAM_IDENTITY_PUBKEY ) ) {
     156          66 :     failover_select( ctx );
     157          66 :     return 1;
     158          66 :   }
     159          27 :   FD_TEST( param==FD_KEYSWITCH_PARAM_IDENTITY_KEYPAIR );
     160          27 :   fd_memcpy( ctx->failover_key, ctx->keyswitch->bytes, 64UL );
     161          27 :   return 0;
     162          27 : }
     163             : 
     164             : static void FD_FN_SENSITIVE
     165         234 : during_housekeeping_sensitive( fd_sign_ctx_t * ctx ) {
     166         234 :   int identity_handled = ctx->failover_junk_key && failover_keyswitch( ctx );
     167             : 
     168         234 :   if( FD_UNLIKELY( !identity_handled && fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
     169          36 :     memcpy( ctx->private_key, ctx->keyswitch->bytes, 32UL );
     170          36 :     fd_memzero_explicit( ctx->keyswitch->bytes, 32UL );
     171          36 :     FD_COMPILER_MFENCE();
     172          36 :     memcpy( ctx->public_key, ctx->keyswitch->bytes+32UL, 32UL );
     173             : 
     174          36 :     derive_fields( ctx );
     175          36 :     fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
     176          36 :   }
     177             : 
     178             :   /* firedancer only */
     179             : 
     180         234 :   if( FD_UNLIKELY( ctx->av_keyswitch && fd_keyswitch_state_query( ctx->av_keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
     181         123 :     ulong param = fd_keyswitch_param_query( ctx->av_keyswitch );
     182         123 :     if( FD_LIKELY( param==FD_KEYSWITCH_PARAM_AV_ADD ) ) {
     183          75 :       if( FD_UNLIKELY( ctx->authorized_voters_cnt==FD_KEYGUARD_AUTH_VOTERS_MAX ) ) {
     184           0 :         FD_LOG_WARNING(( "keyswitch failed: maximum number of authorized voters reached" ));
     185           0 :         fd_memzero_explicit( ctx->av_keyswitch->bytes, 64UL );
     186           0 :         ctx->av_keyswitch->result = FD_ADD_AUTHORIZED_VOTER_RESULT_MAX_AUTH_VOTERS;
     187           0 :         fd_keyswitch_state( ctx->av_keyswitch, FD_KEYSWITCH_STATE_FAILED );
     188           0 :         return;
     189           0 :       }
     190          84 :       for( ulong i=0UL; i<ctx->authorized_voters_cnt; i++ ) {
     191           9 :         if( FD_UNLIKELY( !memcmp( ctx->authorized_voter_pubkeys[ i ], ctx->av_keyswitch->bytes+32UL, 32UL ) ) ) {
     192           0 :           FD_BASE58_ENCODE_32_BYTES( ctx->authorized_voter_pubkeys[ i ], pubkey_b58 );
     193           0 :           FD_LOG_WARNING(( "keyswitch failed: authorized voter key duplicate (%s)", pubkey_b58 ));
     194           0 :           fd_memzero_explicit( ctx->av_keyswitch->bytes, 64UL );
     195           0 :           ctx->av_keyswitch->result = FD_ADD_AUTHORIZED_VOTER_RESULT_DUPLICATE_AUTH_VOTER;
     196           0 :           fd_keyswitch_state( ctx->av_keyswitch, FD_KEYSWITCH_STATE_FAILED );
     197           0 :           return;
     198           0 :         }
     199           9 :       }
     200             : 
     201          75 :       memcpy( ctx->authorized_voter_private_keys[ ctx->authorized_voters_cnt ], ctx->av_keyswitch->bytes, 32UL );
     202          75 :       fd_memzero_explicit( ctx->av_keyswitch->bytes, 32UL );
     203          75 :       FD_COMPILER_MFENCE();
     204          75 :       memcpy( ctx->authorized_voter_pubkeys[ ctx->authorized_voters_cnt ], ctx->av_keyswitch->bytes + 32UL, 32UL );
     205          75 :       fd_keyguard_bls_key_derive( &ctx->bls_keys[ ctx->authorized_voters_cnt+1UL ],
     206          75 :                                   ctx->authorized_voter_pubkeys[ ctx->authorized_voters_cnt ],
     207          75 :                                   ctx->authorized_voter_private_keys[ ctx->authorized_voters_cnt ],
     208          75 :                                   ctx->sha512 );
     209          75 :       ctx->authorized_voters_cnt++;
     210          75 :     } else if( FD_LIKELY( param==FD_KEYSWITCH_PARAM_AV_CLEAR ) ) {
     211          48 :       fd_memzero_explicit( ctx->authorized_voter_private_keys, 32UL*FD_KEYGUARD_AUTH_VOTERS_MAX                            );
     212          48 :       fd_memzero_explicit( ctx->authorized_voter_pubkeys,      sizeof( ctx->authorized_voter_pubkeys                     ) );
     213          48 :       fd_memzero_explicit( ctx->bls_keys+1,                    sizeof(fd_keyguard_bls_key_t)*(FD_KEYGUARD_BLS_KEY_MAX-1UL) );
     214          48 :       ctx->authorized_voters_cnt = 0UL;
     215          48 :     } else {
     216           0 :       FD_LOG_CRIT(( "keyswitch: unexpected authorized voter operation %lu", param ));
     217           0 :     }
     218         123 :     fd_keyswitch_state( ctx->av_keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
     219         123 :   }
     220         234 : }
     221             : 
     222             : static inline void
     223         234 : during_housekeeping( fd_sign_ctx_t * ctx ) {
     224         234 :   during_housekeeping_sensitive( ctx );
     225         234 : }
     226             : 
     227             : static inline void
     228           0 : metrics_write( fd_sign_ctx_t * ctx ) {
     229           0 :   FD_MHIST_COPY( SIGN, SIGN_DURATION_NANOS, ctx->sign_duration );
     230           0 : }
     231             : 
     232             : /* during_frag is called between pairs for sequence number checks, as
     233             :    we are reading incoming frags.  We don't actually need to copy the
     234             :    fragment here, see fd_dedup.c for why we do this.*/
     235             : 
     236             : static void FD_FN_SENSITIVE
     237             : during_frag_sensitive( void * _ctx,
     238             :                        ulong  in_idx,
     239             :                        ulong  seq,
     240             :                        ulong  sig,
     241             :                        ulong  chunk,
     242         654 :                        ulong  sz ) {
     243         654 :   (void)seq;
     244         654 :   (void)sig;
     245             : 
     246         654 :   fd_sign_ctx_t * ctx = (fd_sign_ctx_t *)_ctx;
     247         654 :   FD_TEST( in_idx<MAX_IN );
     248             : 
     249         654 :   int   role = ctx->in[ in_idx ].role;
     250         654 :   ulong mtu  = ctx->in[ in_idx ].mtu;
     251             : 
     252         654 :   if( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>mtu ) {
     253           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 ));
     254           0 :   }
     255             : 
     256         654 :   void * src = fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk );
     257         654 :   fd_memcpy( ctx->_data, src, sz );
     258         654 : }
     259             : 
     260             : 
     261             : static void
     262             : during_frag( void * _ctx,
     263             :              ulong  in_idx,
     264             :              ulong  seq,
     265             :              ulong  sig,
     266             :              ulong  chunk,
     267             :              ulong  sz,
     268         654 :              ulong  ctl FD_PARAM_UNUSED ) {
     269         654 :   during_frag_sensitive( _ctx, in_idx, seq, sig, chunk, sz );
     270         654 : }
     271             : 
     272             : /* The failover identity signs the admission proofs and the TLS
     273             :    handshakes of the failover channel. */
     274             : 
     275             : static void FD_FN_SENSITIVE
     276             : failover_sign( fd_sign_ctx_t * ctx,
     277             :                uchar *         dst,
     278          81 :                ulong           sz ) {
     279          81 :   fd_ed25519_sign( dst, ctx->_data, sz, ctx->failover_key+32UL, ctx->failover_key, ctx->sha512 );
     280          81 : }
     281             : 
     282             : static void FD_FN_SENSITIVE
     283             : after_frag_sensitive( void *              _ctx,
     284             :                       ulong               in_idx,
     285             :                       ulong               seq,
     286             :                       ulong               sig,
     287             :                       ulong               sz,
     288             :                       ulong               tsorig,
     289             :                       ulong               tspub,
     290         681 :                       fd_stem_context_t * stem ) {
     291         681 :   (void)seq;
     292         681 :   (void)tspub;
     293             : 
     294         681 :   fd_sign_ctx_t * ctx = (fd_sign_ctx_t *)_ctx;
     295             : 
     296             :   /* The lower 32 bits are used to specify the sign type.
     297             : 
     298             :      If the frag is coming from the repair tile, then the upper 32 bits
     299             :      contain the repair tile nonce to identify the request.
     300             : 
     301             :      If the frag is coming from the send tile, then the upper 32 bits
     302             :      contain the index of the authorized voter that needs to sign the
     303             :      vote transaction.  The least significant bit of the upper 32 is
     304             :      used to indicate if a second signature is needed.  The next 4 least
     305             :      significant bits are used to encode the index of the authorized
     306             :      voter that a signature is needed from.
     307             : 
     308             :      BLS requests from the votor tile encode the upper 32 bits the same
     309             :      way, except that a set least significant bit selects the authorized
     310             :      voter's key in place of the identity's instead of adding a second
     311             :      signature. */
     312         681 :   int sign_type         = (int)(uint)(sig);
     313         681 :   int needs_second_sign = ctx->in[ in_idx ].role==FD_KEYGUARD_ROLE_TXSEND && ((sig>>32) & 1UL);
     314             : 
     315         681 :   FD_TEST( in_idx<MAX_IN );
     316             : 
     317         681 :   int role = ctx->in[ in_idx ].role;
     318             : 
     319         681 :   fd_keyguard_authority_t authority = {0};
     320         681 :   memcpy( authority.identity_pubkey,          ctx->public_key,               32UL );
     321         681 :   memcpy( authority.tip_payment_program,      ctx->tip_payment_program,      32UL );
     322         681 :   memcpy( authority.tip_distribution_program, ctx->tip_distribution_program, 32UL );
     323             : 
     324         681 :   if( FD_UNLIKELY( !fd_keyguard_payload_authorize( &authority, ctx->_data, sz, role, sign_type ) ) ) {
     325          18 :     FD_LOG_EMERG(( "fd_keyguard_payload_authorize failed (role=%d sign_type=%d)", role, sign_type ));
     326          18 :   }
     327             : 
     328         663 :   long sign_duration = -fd_tickcount();
     329             : 
     330         663 :   uchar * dst    = fd_chunk_to_laddr( ctx->out[ in_idx ].out_mem, ctx->out[ in_idx ].out_chunk );
     331         663 :   ulong   out_sz = 64UL;
     332             : 
     333         663 :   switch( sign_type ) {
     334         120 :   case FD_KEYGUARD_SIGN_TYPE_ED25519: {
     335         120 :     if( FD_UNLIKELY( role==FD_KEYGUARD_ROLE_FAILOV ) ) { failover_sign( ctx, dst, sz ); break; }
     336          39 :     fd_ed25519_sign( dst, ctx->_data, sz, ctx->public_key, ctx->private_key, ctx->sha512 );
     337          39 :     if( needs_second_sign ) {
     338           0 :       ulong authority_idx = (sig >> 33) & 0xFUL;
     339           0 :       if( FD_UNLIKELY( authority_idx>=ctx->authorized_voters_cnt ) ) {
     340           0 :         FD_LOG_CRIT(( "invalid sign request from in_idx=%lu: authority_idx=%lu out of range (authorized_voters_cnt=%lu)", in_idx, authority_idx, ctx->authorized_voters_cnt ));
     341           0 :       }
     342           0 :       fd_ed25519_sign( dst+64UL, ctx->_data, sz, ctx->authorized_voter_pubkeys[ authority_idx ], ctx->authorized_voter_private_keys[ authority_idx ], ctx->sha512 );
     343           0 :       out_sz = 128UL;
     344           0 :     }
     345          39 :     break;
     346          39 :   }
     347          39 :   case FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519: {
     348           0 :     uchar hash[ 32 ];
     349           0 :     fd_sha256_hash( ctx->_data, sz, hash );
     350           0 :     fd_ed25519_sign( dst, hash, 32UL, ctx->public_key, ctx->private_key, ctx->sha512 );
     351           0 :     break;
     352          39 :   }
     353           0 :   case FD_KEYGUARD_SIGN_TYPE_PUBKEY_CONCAT_ED25519: {
     354           0 :     memcpy( ctx->concat+ctx->public_key_base58_sz+1UL, ctx->_data, 9UL );
     355           0 :     fd_ed25519_sign( dst, ctx->concat, ctx->public_key_base58_sz+1UL+9UL, ctx->public_key, ctx->private_key, ctx->sha512 );
     356           0 :     break;
     357          39 :   }
     358         393 :   case FD_KEYGUARD_SIGN_TYPE_BLS: {
     359         393 :     ulong key_idx = 0UL;
     360         393 :     if( (sig>>32) & 1UL ) {
     361         198 :       ulong authority_idx = (sig >> 33) & 0xFUL;
     362         198 :       if( FD_UNLIKELY( authority_idx>=ctx->authorized_voters_cnt ) ) {
     363           3 :         FD_LOG_CRIT(( "invalid sign request from in_idx=%lu: authority_idx=%lu out of range (authorized_voters_cnt=%lu)", in_idx, authority_idx, ctx->authorized_voters_cnt ));
     364           3 :       }
     365         195 :       key_idx = authority_idx+1UL;
     366         195 :     }
     367             : 
     368         390 :     fd_bls_sig_t bls_sig[1];
     369         390 :     fd_bls_sec_sign( &ctx->bls_keys[ key_idx ].secret_key, ctx->_data, sz, bls_sig );
     370         390 :     fd_bls_sig_ser( bls_sig, dst );
     371         390 :     out_sz = FD_BLS_SIG_SZ;
     372         390 :     break;
     373         393 :   }
     374         150 :   case FD_KEYGUARD_SIGN_TYPE_BLS_PUBKEY: {
     375         150 :     if( FD_UNLIKELY( sig!=FD_KEYGUARD_SIGN_TYPE_BLS_PUBKEY ) ) {
     376           3 :       FD_LOG_EMERG(( "invalid BLS public key request (sig=%lu)", sig ));
     377           3 :     }
     378         147 :     ulong authority_idx = FD_LOAD( ulong, ctx->_data );
     379         147 :     if( FD_UNLIKELY( authority_idx!=ULONG_MAX && authority_idx>=ctx->authorized_voters_cnt ) ) {
     380           3 :       FD_LOG_CRIT(( "invalid BLS public key request from in_idx=%lu: authority_idx=%lu out of range (authorized_voters_cnt=%lu)", in_idx, authority_idx, ctx->authorized_voters_cnt ));
     381           3 :     }
     382         144 :     ulong key_idx = authority_idx==ULONG_MAX ? 0UL : authority_idx+1UL;
     383         144 :     memcpy( dst, ctx->bls_keys[ key_idx ].public_key, FD_BLS_PUB_COMPRESSED_SZ );
     384         144 :     out_sz = FD_BLS_PUB_COMPRESSED_SZ;
     385         144 :     break;
     386         147 :   }
     387           0 :   default:
     388           0 :     FD_LOG_EMERG(( "invalid sign type: %d", sign_type ));
     389         663 :   }
     390             : 
     391         654 :   if( FD_LIKELY( sign_type!=FD_KEYGUARD_SIGN_TYPE_BLS_PUBKEY ) ) {
     392         510 :     sign_duration += fd_tickcount();
     393         510 :     fd_histf_sample( ctx->sign_duration, (ulong)sign_duration );
     394         510 :   }
     395             : 
     396         654 :   fd_stem_publish( stem, in_idx, sig, ctx->out[ in_idx ].out_chunk, out_sz, 0UL, tsorig, 0UL );
     397         654 :   ctx->out[ in_idx ].out_chunk = fd_dcache_compact_next( ctx->out[ in_idx ].out_chunk, out_sz, ctx->out[ in_idx ].out_chunk0, ctx->out[ in_idx ].out_wmark );
     398         654 : }
     399             : 
     400             : static void
     401             : after_frag( void *              _ctx,
     402             :             ulong               in_idx,
     403             :             ulong               seq,
     404             :             ulong               sig,
     405             :             ulong               sz,
     406             :             ulong               tsorig,
     407             :             ulong               tspub,
     408         681 :             fd_stem_context_t * stem ) {
     409         681 :   after_frag_sensitive( _ctx, in_idx, seq, sig, sz, tsorig, tspub, stem );
     410         681 : }
     411             : 
     412             : static void FD_FN_SENSITIVE
     413             : load_keys( fd_sign_ctx_t *        ctx,
     414             :            fd_topo_t const *      topo,
     415           0 :            fd_topo_tile_t const * tile ) {
     416           0 :   ctx->failover_junk_key = NULL;
     417           0 :   ctx->failover_key      = NULL;
     418           0 :   int standby = !strcmp( tile->sign.identity_key_path, FD_KEYLOAD_FAILOVER_JUNK );
     419           0 :   if( FD_UNLIKELY( tile->sign.failover_enabled ) ) {
     420           0 :     ulong failov_idx = fd_topo_find_tile( topo, "failov", 0UL );
     421           0 :     FD_TEST( failov_idx!=ULONG_MAX );
     422           0 :     ctx->failover_junk_key = fd_keyload_load( FD_KEYLOAD_FAILOVER_JUNK, /* pubkey only: */ 0 );
     423           0 :     ctx->failover_key      = fd_keyload_mprotect_wr( fd_keyload_load( topo->tiles[ failov_idx ].failov.target_key_path, /* pubkey only: */ 0 ), /* public_key_only: */ 0 );
     424           0 :     if( FD_UNLIKELY( fd_memeq( ctx->failover_junk_key+32UL, ctx->failover_key+32UL, 32UL ) ) ) {
     425           0 :       FD_LOG_ERR(( "the failover junk key must differ from the [paths.identity_key] key" ));
     426           0 :     }
     427             : 
     428           0 :     fd_sha512_t sha512[ 1 ];
     429           0 :     FD_TEST( fd_sha512_join( fd_sha512_new( sha512 ) ) );
     430           0 :     uchar check_public_key[ 32 ];
     431           0 :     fd_ed25519_public_from_private( check_public_key, ctx->failover_key, sha512 );
     432           0 :     fd_memzero_explicit( sha512, sizeof(sha512) );
     433           0 :     if( FD_UNLIKELY( !fd_memeq( check_public_key, ctx->failover_key+32UL, 32UL ) ) ) {
     434           0 :       FD_LOG_EMERG(( "The public key in the failover identity key file does not match the public key derived from the private key. "
     435           0 :                      "Firedancer will not use the key pair to sign as it might leak the private key." ));
     436           0 :     }
     437           0 :   } else {
     438           0 :     FD_TEST( !standby );
     439           0 :   }
     440             : 
     441           0 :   uchar * identity_key;
     442           0 :   if( FD_UNLIKELY( standby ) ) {
     443           0 :     identity_key = fd_keyload_alloc_protected_pages( 1UL, 2UL );
     444           0 :     fd_memcpy( identity_key, ctx->failover_junk_key, 64UL );
     445           0 :   } else {
     446           0 :     identity_key = fd_keyload_mprotect_wr( fd_keyload_load( tile->sign.identity_key_path, /* pubkey only: */ 0 ), /* public_key_only: */ 0 );
     447           0 :   }
     448           0 :   ctx->private_key = identity_key;
     449           0 :   ctx->public_key  = identity_key + 32UL;
     450             : 
     451           0 :   ctx->bls_keys                      = fd_keyload_alloc_protected_pages( 1UL, 2UL );
     452           0 :   ctx->authorized_voter_private_keys = fd_keyload_alloc_protected_pages( 1UL, 2UL );
     453             : 
     454           0 :   FD_TEST( tile->sign.authorized_voter_paths_cnt<FD_KEYGUARD_BLS_KEY_MAX );
     455           0 :   ctx->authorized_voters_cnt = tile->sign.authorized_voter_paths_cnt;
     456           0 :   for( ulong i=0UL; i<tile->sign.authorized_voter_paths_cnt; i++ ) {
     457           0 :     uchar const * authorized_voter_key = fd_keyload_load( tile->sign.authorized_voter_paths[ i ], /* pubkey only: */ 0 );
     458           0 :     memcpy( ctx->authorized_voter_private_keys[ i ], authorized_voter_key, 32UL );
     459           0 :     memcpy( ctx->authorized_voter_pubkeys[ i ], authorized_voter_key + 32UL, 32UL );
     460           0 :     fd_keyload_unload( authorized_voter_key, /* public_key_only: */ 0 );
     461           0 :   }
     462           0 : }
     463             : 
     464             : static void FD_FN_SENSITIVE
     465             : privileged_init_sensitive( fd_topo_t const *      topo,
     466           0 :                            fd_topo_tile_t const * tile ) {
     467           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     468           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     469           0 :   fd_sign_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
     470             : 
     471           0 :   load_keys( ctx, topo, tile );
     472             : 
     473             :   /* The stack can be taken over and reorganized by under AddressSanitizer,
     474             :      which causes this code to fail.  */
     475             : #if FD_HAS_ASAN
     476             :   FD_LOG_WARNING(( "!!! SECURITY WARNING !!! YOU ARE RUNNING THE SIGNING TILE "
     477             :                    "WITH ADDRESS SANITIZER ENABLED. THIS CAN LEAK SENSITIVE "
     478             :                    "DATA INCLUDING YOUR PRIVATE KEYS INTO CORE DUMPS IF THIS "
     479             :                    "PROCESS ABORTS. IT IS HIGHLY ADVISED TO NOT TO RUN IN THIS "
     480             :                    "MODE IN PRODUCTION!" ));
     481             : #else
     482             :   /* Prevent the stack from showing up in core dumps just in case the
     483             :      private key somehow ends up in there. */
     484           0 :   FD_TEST( fd_tile_stack0() );
     485           0 :   FD_TEST( fd_tile_stack_sz() );
     486           0 :   if( FD_UNLIKELY( madvise( (void*)fd_tile_stack0(), fd_tile_stack_sz(), MADV_DONTDUMP ) ) )
     487           0 :     FD_LOG_ERR(( "madvise failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     488           0 : #endif
     489           0 : }
     490             : 
     491             : static void
     492             : privileged_init( fd_topo_t const *      topo,
     493           0 :                  fd_topo_tile_t const * tile ) {
     494           0 :   privileged_init_sensitive( topo, tile );
     495           0 : }
     496             : 
     497             : static void FD_FN_SENSITIVE
     498             : unprivileged_init_sensitive( fd_topo_t const *      topo,
     499           0 :                              fd_topo_tile_t const * tile ) {
     500           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     501             : 
     502           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     503           0 :   fd_sign_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_sign_ctx_t ), sizeof( fd_sign_ctx_t ) );
     504           0 :   FD_TEST( fd_sha512_join( fd_sha512_new( ctx->sha512 ) ) );
     505             : 
     506           0 :   FD_TEST( tile->in_cnt<=MAX_IN );
     507           0 :   FD_TEST( tile->in_cnt==tile->out_cnt );
     508             : 
     509           0 :   fd_histf_join( fd_metrics_histf_new_ns( ctx->sign_duration, FD_MHIST_EDGES_NS( SIGN, SIGN_DURATION_NANOS ) ) );
     510             : 
     511           0 :   ctx->keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id ) );
     512           0 :   derive_fields( ctx );
     513           0 :   for( ulong i=0UL; i<ctx->authorized_voters_cnt; i++ ) {
     514           0 :     fd_keyguard_bls_key_derive( &ctx->bls_keys[ i+1UL ],
     515           0 :                                 ctx->authorized_voter_pubkeys[ i ],
     516           0 :                                 ctx->authorized_voter_private_keys[ i ],
     517           0 :                                 ctx->sha512 );
     518           0 :   }
     519             : 
     520           0 :   if( FD_LIKELY( tile->av_keyswitch_obj_id!=ULONG_MAX ) ) {
     521           0 :     ctx->av_keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->av_keyswitch_obj_id ) );
     522           0 :     FD_TEST( ctx->av_keyswitch );
     523           0 :   } else {
     524           0 :     ctx->av_keyswitch = NULL;
     525           0 :   }
     526             : 
     527           0 :   memcpy( ctx->tip_payment_program,      tile->sign.bundle.tip_payment_program_addr,      32UL );
     528           0 :   memcpy( ctx->tip_distribution_program, tile->sign.bundle.tip_distribution_program_addr, 32UL );
     529             : 
     530           0 :   for( ulong i=0UL; i<MAX_IN; i++ ) ctx->in[ i ].role = -1;
     531             : 
     532           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     533           0 :     fd_topo_link_t const * in_link = &topo->links[ tile->in_link_id[ i ] ];
     534           0 :     fd_topo_link_t const * out_link = &topo->links[ tile->out_link_id[ i ] ];
     535             : 
     536           0 :     if( in_link->mtu > FD_KEYGUARD_SIGN_REQ_MTU ) FD_LOG_CRIT(( "oversz link[%lu].mtu=%lu", i, in_link->mtu ));
     537           0 :     ctx->in[ i ].mem    = fd_wksp_containing( in_link->dcache );
     538           0 :     ctx->in[ i ].mtu    = in_link->mtu;
     539           0 :     ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, in_link->dcache );
     540           0 :     ctx->in[ i ].wmark  = fd_dcache_compact_wmark( ctx->in[ i ].mem, in_link->dcache, in_link->mtu );
     541             : 
     542           0 :     ctx->out[ i ].out_mem    = fd_wksp_containing( out_link->dcache );
     543           0 :     ctx->out[ i ].out_chunk0 = fd_dcache_compact_chunk0( ctx->out[ i ].out_mem, out_link->dcache );
     544           0 :     ctx->out[ i ].out_wmark  = fd_dcache_compact_wmark( ctx->out[ i ].out_mem, out_link->dcache, out_link->mtu );
     545           0 :     ctx->out[ i ].out_chunk  = ctx->out[ i ].out_chunk0;
     546             : 
     547           0 :     if( !strcmp( in_link->name, "shred_sign" ) ) {
     548           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_LEADER;
     549           0 :       FD_TEST( !strcmp( out_link->name, "sign_shred" ) );
     550           0 :       FD_TEST( in_link->mtu==32UL );
     551           0 :       FD_TEST( out_link->mtu==64UL );
     552           0 :     } else if ( !strcmp( in_link->name, "gossip_sign" ) ) {
     553           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_GOSSIP;
     554           0 :       FD_TEST( !strcmp( out_link->name, "sign_gossip" ) );
     555           0 :       FD_TEST( in_link->mtu==2048UL );
     556           0 :       FD_TEST( out_link->mtu==64UL );
     557           0 :     } else if ( !strcmp( in_link->name, "repair_sign" ) ) {
     558           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_REPAIR;
     559           0 :       FD_TEST( !strcmp( out_link->name, "sign_repair" ) );
     560           0 :       FD_TEST( in_link->mtu==124UL ); // FD_REPAIR_SIG_SER_MAX
     561           0 :       FD_TEST( out_link->mtu==64UL );
     562           0 :     } else if( !strcmp(in_link->name, "bundle_sign" ) ) {
     563           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_BUNDLE;
     564           0 :       FD_TEST( !strcmp( out_link->name, "sign_bundle" ) );
     565           0 :       FD_TEST( in_link->mtu==9UL );
     566           0 :       FD_TEST( out_link->mtu==64UL );
     567           0 :     } else if( !strcmp(in_link->name, "event_sign" ) ) {
     568           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_EVENT;
     569           0 :       FD_TEST( !strcmp( out_link->name, "sign_event" ) );
     570           0 :       FD_TEST( in_link->mtu==317UL );
     571           0 :       FD_TEST( out_link->mtu==64UL );
     572           0 :     } else if( !strcmp(in_link->name, "pack_sign" ) ) {
     573           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_BUNDLE_CRANK;
     574           0 :       FD_TEST( !strcmp( out_link->name, "sign_pack" ) );
     575           0 :       FD_TEST( in_link->mtu==1232UL );
     576           0 :       FD_TEST( out_link->mtu==64UL );
     577           0 :     } else if( !strcmp(in_link->name, "rserve_sign" ) ) {
     578           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_RSERVE;
     579           0 :       FD_TEST( !strcmp( out_link->name, "sign_rserve" ) );
     580           0 :       FD_TEST( in_link->mtu==32UL );
     581           0 :       FD_TEST( out_link->mtu==64UL );
     582           0 :     } else if( !strcmp(in_link->name, "votor_sign" ) ) {
     583           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_VOTOR;
     584           0 :       FD_TEST( !strcmp( out_link->name, "sign_votor" ) );
     585           0 :       FD_TEST( in_link->mtu==FD_KEYGUARD_SIGN_REQ_MTU );
     586           0 :       FD_TEST( out_link->mtu==FD_BLS_SIG_SZ );
     587           0 :     } else if( !strcmp(in_link->name, "failov_sign" ) ) {
     588           0 :       ctx->in[ i ].role = FD_KEYGUARD_ROLE_FAILOV;
     589           0 :       FD_TEST( !strcmp( out_link->name, "sign_failov" ) );
     590           0 :       FD_TEST( in_link->mtu==130UL );
     591           0 :       FD_TEST( out_link->mtu==64UL );
     592           0 :       FD_TEST( ctx->failover_junk_key && ctx->failover_key );
     593           0 :     } else {
     594           0 :       FD_LOG_CRIT(( "unexpected link %s", in_link->name ));
     595           0 :     }
     596           0 :   }
     597             : 
     598           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
     599           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     600           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     601           0 : }
     602             : 
     603             : static void
     604             : unprivileged_init( fd_topo_t const *      topo,
     605           0 :                    fd_topo_tile_t const * tile ) {
     606           0 :   unprivileged_init_sensitive( topo, tile );
     607           0 : }
     608             : 
     609             : static ulong
     610             : populate_allowed_seccomp( fd_topo_t const *      topo,
     611             :                           fd_topo_tile_t const * tile,
     612             :                           ulong                  out_cnt,
     613           0 :                           struct sock_filter *   out ) {
     614           0 :   (void)topo;
     615           0 :   (void)tile;
     616             : 
     617           0 :   populate_sock_filter_policy_fd_sign_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
     618           0 :   return sock_filter_policy_fd_sign_tile_instr_cnt;
     619           0 : }
     620             : 
     621             : static ulong
     622             : populate_allowed_fds( fd_topo_t const *      topo,
     623             :                       fd_topo_tile_t const * tile,
     624             :                       ulong                  out_fds_cnt,
     625           0 :                       int *                  out_fds ) {
     626           0 :   (void)topo;
     627           0 :   (void)tile;
     628             : 
     629           0 :   if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     630             : 
     631           0 :   ulong out_cnt = 0;
     632           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     633           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     634           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     635           0 :   return out_cnt;
     636           0 : }
     637             : 
     638           0 : #define STEM_BURST (1UL)
     639             : 
     640             : /* See explanation in fd_pack */
     641           0 : #define STEM_LAZY  (128L*3000L)
     642             : 
     643           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_sign_ctx_t
     644           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_sign_ctx_t)
     645             : 
     646           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
     647           0 : #define STEM_CALLBACK_METRICS_WRITE       metrics_write
     648           0 : #define STEM_CALLBACK_DURING_FRAG         during_frag
     649           0 : #define STEM_CALLBACK_AFTER_FRAG          after_frag
     650             : 
     651             : #include "../../disco/stem/fd_stem.c"
     652             : 
     653             : fd_topo_run_tile_t fd_tile_sign = {
     654             :   .name                     = "sign",
     655             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     656             :   .populate_allowed_fds     = populate_allowed_fds,
     657             :   .scratch_align            = scratch_align,
     658             :   .scratch_footprint        = scratch_footprint,
     659             :   .privileged_init          = privileged_init,
     660             :   .unprivileged_init        = unprivileged_init,
     661             :   .run                      = stem_run,
     662             : };

Generated by: LCOV version 1.14