LCOV - code coverage report
Current view: top level - disco/bundle - fd_bundle_auth.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 181 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 8 0.0 %

          Line data    Source code
       1             : #include "fd_bundle_auth.h"
       2             : #include "../../ballet/base58/fd_base58.h"
       3             : #include "../../ballet/pb/fd_pb_encode.h"
       4             : #include "../../ballet/pb/fd_pb_tokenize.h"
       5             : #include "../../disco/keyguard/fd_keyguard.h"
       6             : #include "../../disco/keyguard/fd_keyguard_client.h"
       7             : 
       8           0 : #define FD_BUNDLE_AUTH_REQUEST_TIMEOUT ((long)5e9) /* 5 seconds */
       9             : 
      10             : /* fd_bundle_auther_t generates auth tokens. */
      11             : 
      12             : /* FIXME consider rewriting this with coroutines or light threads */
      13             : 
      14             : fd_bundle_auther_t *
      15           0 : fd_bundle_auther_init( fd_bundle_auther_t * auther ) {
      16           0 :   *auther = (fd_bundle_auther_t) {
      17           0 :     .state      = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE,
      18           0 :     .needs_poll = 1
      19           0 :   };
      20           0 :   return auther;
      21           0 : }
      22             : 
      23             : void
      24           0 : fd_bundle_auther_handle_request_fail( fd_bundle_auther_t * auther ) {
      25           0 :   switch( auther->state ) {
      26           0 :   case FD_BUNDLE_AUTH_STATE_WAIT_CHALLENGE:
      27           0 :     FD_LOG_DEBUG(( "Request for auth challenge failed, retrying" ));
      28           0 :     auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
      29           0 :     auther->needs_poll = 1;
      30           0 :     break;
      31           0 :   case FD_BUNDLE_AUTH_STATE_WAIT_TOKENS:
      32           0 :     FD_LOG_DEBUG(( "Request for auth tokens failed" ));
      33           0 :     auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
      34           0 :     auther->needs_poll = 1;
      35           0 :     break;
      36           0 :   }
      37           0 : }
      38             : 
      39             : static void
      40             : fd_bundle_auther_req_challenge( fd_bundle_auther_t *   auther,
      41           0 :                                 fd_grpc_client_t *     client ) {
      42           0 :   if( FD_UNLIKELY( fd_grpc_client_request_is_blocked( client ) ) ) return;
      43             : 
      44           0 :   uchar req[ 64 ];
      45           0 :   fd_pb_encoder_t enc[1]; fd_pb_encoder_init( enc, req, sizeof(req) );
      46           0 :   FD_TEST( fd_pb_push_uint32( enc, 1U, 2U ) ); /* role = VALIDATOR */
      47           0 :   FD_TEST( fd_pb_push_bytes( enc, 2U, auther->pubkey, 32UL ) ); /* pubkey */
      48             : 
      49           0 :   static char const path[] = "/auth.AuthService/GenerateAuthChallenge";
      50           0 :   fd_grpc_h2_stream_t * request = fd_grpc_client_request_start(
      51           0 :       client,
      52           0 :       path, sizeof(path)-1,
      53           0 :       FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthChallenge,
      54           0 :       req, fd_pb_encoder_out_sz( enc ),
      55           0 :       NULL, 0,
      56           0 :       0 /* is_streaming */
      57           0 :   );
      58           0 :   if( FD_UNLIKELY( !request ) ) return;
      59             : 
      60           0 :   auther->state      = FD_BUNDLE_AUTH_STATE_WAIT_CHALLENGE;
      61           0 :   auther->needs_poll = 0;
      62           0 :   fd_grpc_client_deadline_set(
      63           0 :       request,
      64           0 :       FD_GRPC_DEADLINE_RX_END,
      65           0 :       fd_log_wallclock()+FD_BUNDLE_AUTH_REQUEST_TIMEOUT );
      66             : 
      67           0 :   char key_cstr[ FD_BASE58_ENCODED_32_SZ ];
      68           0 :   fd_base58_encode_32( auther->pubkey, NULL, key_cstr );
      69           0 :   FD_LOG_INFO(( "Requesting bundle auth challenge (identity=%s)", key_cstr ));
      70           0 : }
      71             : 
      72             : int
      73             : fd_bundle_auther_handle_challenge_resp(
      74             :     fd_bundle_auther_t * auther,
      75             :     void const *         data,
      76             :     ulong                data_sz
      77           0 : ) {
      78           0 :   auther->needs_poll = 1;
      79             : 
      80           0 :   fd_pb_inbuf_t in[1]; fd_pb_inbuf_init( in, data, data_sz );
      81           0 :   int found_challenge = 0;
      82           0 :   while( fd_pb_inbuf_sz( in ) ) {
      83           0 :     fd_pb_tlv_t tlv[1];
      84           0 :     if( FD_UNLIKELY( !fd_pb_tlv_read( in, tlv ) ) ) {
      85           0 :       FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthChallengeResponse) failed" ));
      86           0 :       goto fail;
      87           0 :     }
      88           0 :     if( tlv->field_id==1U ) { /* challenge */
      89           0 :       uchar const * chal = fd_pb_tlv_bytes( in, tlv );
      90           0 :       if( FD_UNLIKELY( !chal ) ) {
      91           0 :         FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthChallengeResponse) failed" ));
      92           0 :         goto fail;
      93           0 :       }
      94           0 :       if( FD_UNLIKELY( tlv->len!=9UL ) ) {
      95           0 :         FD_LOG_WARNING(( "Unexpected auth.GenerateAuthChallengeResponse challenge size: %lu bytes", tlv->len ));
      96           0 :         goto fail;
      97           0 :       }
      98           0 :       memcpy( auther->challenge, chal, 9UL );
      99           0 :       found_challenge = 1;
     100           0 :     }
     101           0 :     if( FD_UNLIKELY( !fd_pb_tlv_skip( in, tlv ) ) ) {
     102           0 :       FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthChallengeResponse) failed" ));
     103           0 :       goto fail;
     104           0 :     }
     105           0 :   }
     106           0 :   if( FD_UNLIKELY( !found_challenge ) ) {
     107           0 :     FD_LOG_WARNING(( "auth.GenerateAuthChallengeResponse: missing challenge" ));
     108           0 :     goto fail;
     109           0 :   }
     110             : 
     111           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_TOKENS;
     112           0 :   FD_LOG_DEBUG(( "Got auth challenge" ));
     113           0 :   return 1;
     114             : 
     115           0 : fail:
     116           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
     117           0 :   return 0;
     118           0 : }
     119             : 
     120             : static void
     121             : fd_bundle_auther_req_tokens( fd_bundle_auther_t *   auther,
     122             :                              fd_grpc_client_t *     client,
     123           0 :                              fd_keyguard_client_t * keyguard ) {
     124           0 :   if( FD_UNLIKELY( fd_grpc_client_request_is_blocked( client ) ) ) return;
     125             : 
     126             :   /* Format challenge string as '{base58(pubkey)}-{challenge}' */
     127           0 :   char  chal[ FD_BASE58_ENCODED_32_SZ+1UL+9UL ];
     128           0 :   ulong enc_len;
     129           0 :   char * p = chal;
     130           0 :   fd_base58_encode_32( auther->pubkey, &enc_len, p );
     131           0 :   p += enc_len;
     132           0 :   p = fd_cstr_append_char( p, '-' );
     133           0 :   p = fd_cstr_append_text( p, auther->challenge, 9UL );
     134           0 :   ulong chal_sz = (ulong)p - (ulong)chal;
     135             : 
     136           0 :   uchar signed_chal[ 64 ];
     137           0 :   fd_keyguard_client_sign( keyguard, signed_chal, (uchar const *)auther->challenge, 9UL, FD_KEYGUARD_SIGN_TYPE_PUBKEY_CONCAT_ED25519 );
     138             : 
     139           0 :   uchar req[ 256 ];
     140           0 :   fd_pb_encoder_t enc[1]; fd_pb_encoder_init( enc, req, sizeof(req) );
     141           0 :   FD_TEST( fd_pb_push_bytes( enc, 1U, chal,           chal_sz ) ); /* challenge */
     142           0 :   FD_TEST( fd_pb_push_bytes( enc, 2U, auther->pubkey, 32UL    ) ); /* client_pubkey */
     143           0 :   FD_TEST( fd_pb_push_bytes( enc, 3U, signed_chal,    64UL    ) ); /* signed_challenge */
     144             : 
     145           0 :   static char const path[] = "/auth.AuthService/GenerateAuthTokens";
     146           0 :   fd_grpc_h2_stream_t * request = fd_grpc_client_request_start(
     147           0 :       client,
     148           0 :       path, sizeof(path)-1,
     149           0 :       FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthTokens,
     150           0 :       req, fd_pb_encoder_out_sz( enc ),
     151           0 :       NULL, 0,
     152           0 :       0 /* is_streaming */
     153           0 :   );
     154           0 :   if( FD_UNLIKELY( !request ) ) return;
     155             : 
     156           0 :   auther->state      = FD_BUNDLE_AUTH_STATE_WAIT_TOKENS;
     157           0 :   auther->needs_poll = 0;
     158           0 :   fd_grpc_client_deadline_set(
     159           0 :       request,
     160           0 :       FD_GRPC_DEADLINE_RX_END,
     161           0 :       fd_log_wallclock()+FD_BUNDLE_AUTH_REQUEST_TIMEOUT );
     162             : 
     163           0 :   FD_LOG_DEBUG(( "Requesting bundle auth tokens" ));
     164           0 : }
     165             : 
     166             : int
     167             : fd_bundle_auther_handle_tokens_resp(
     168             :     fd_bundle_auther_t * auther,
     169             :     void const *         data,
     170             :     ulong                data_sz
     171           0 : ) {
     172             :   /* GenerateAuthTokensResponse.access_token (Token) .value (bytes) */
     173           0 :   uchar const * tok    = NULL;
     174           0 :   ulong         tok_sz = 0UL;
     175           0 :   fd_pb_inbuf_t in[1]; fd_pb_inbuf_init( in, data, data_sz );
     176           0 :   while( fd_pb_inbuf_sz( in ) ) {
     177           0 :     fd_pb_tlv_t tlv[1];
     178           0 :     if( FD_UNLIKELY( !fd_pb_tlv_read( in, tlv ) ) ) goto decode_fail;
     179           0 :     if( tlv->field_id==1U ) { /* access_token */
     180           0 :       fd_pb_inbuf_t sub[1];
     181           0 :       if( FD_UNLIKELY( !fd_pb_tlv_submsg( in, tlv, sub ) ) ) goto decode_fail;
     182           0 :       while( fd_pb_inbuf_sz( sub ) ) {
     183           0 :         fd_pb_tlv_t tlv2[1];
     184           0 :         if( FD_UNLIKELY( !fd_pb_tlv_read( sub, tlv2 ) ) ) goto decode_fail;
     185           0 :         if( tlv2->field_id==1U ) { /* value */
     186           0 :           tok = fd_pb_tlv_bytes( sub, tlv2 );
     187           0 :           if( FD_UNLIKELY( !tok ) ) goto decode_fail;
     188           0 :           tok_sz = tlv2->len;
     189           0 :         }
     190           0 :         if( FD_UNLIKELY( !fd_pb_tlv_skip( sub, tlv2 ) ) ) goto decode_fail;
     191           0 :       }
     192           0 :     }
     193           0 :     if( FD_UNLIKELY( !fd_pb_tlv_skip( in, tlv ) ) ) goto decode_fail;
     194           0 :   }
     195           0 :   if( FD_UNLIKELY( !tok || !tok_sz ) ) {
     196           0 :     FD_LOG_WARNING(( "auth.GenerateAuthTokensResponse: missing access_token" ));
     197           0 :     goto fail;
     198           0 :   }
     199           0 :   if( FD_UNLIKELY( tok_sz > sizeof(auther->access_token) ) ) {
     200           0 :     FD_LOG_WARNING(( "auth.GenerateAuthTokensResponse: oversz access_token: %lu bytes", tok_sz ));
     201           0 :     goto fail;
     202           0 :   }
     203             : 
     204           0 :   fd_memcpy( auther->access_token, tok, tok_sz );
     205           0 :   auther->access_token_sz = (ushort)tok_sz;
     206           0 :   auther->state = FD_BUNDLE_AUTH_STATE_DONE_WAIT;
     207           0 :   FD_LOG_DEBUG(( "Got auth tokens" ));
     208           0 :   return 1;
     209             : 
     210           0 : decode_fail:
     211           0 :   FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthTokensResponse) failed" ));
     212           0 : fail:
     213           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
     214           0 :   auther->needs_poll = 1;
     215           0 :   return 0;
     216           0 : }
     217             : 
     218             : void
     219             : fd_bundle_auther_poll( fd_bundle_auther_t *   auther,
     220             :                        fd_grpc_client_t *     client,
     221           0 :                        fd_keyguard_client_t * keyguard ) {
     222           0 :   switch( auther->state ) {
     223           0 :   case FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE:
     224           0 :     fd_bundle_auther_req_challenge( auther, client );
     225           0 :     break;
     226           0 :   case FD_BUNDLE_AUTH_STATE_REQ_TOKENS:
     227           0 :     fd_bundle_auther_req_tokens( auther, client, keyguard );
     228           0 :     break;
     229           0 :   default:
     230           0 :     break;
     231           0 :   }
     232           0 : }
     233             : 
     234             : void
     235           0 : fd_bundle_auther_reset( fd_bundle_auther_t * auther ) {
     236           0 :   auther->state      = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
     237           0 :   auther->needs_poll = 1;
     238           0 : }

Generated by: LCOV version 1.14