LCOV - code coverage report
Current view: top level - disco/bundle - fd_bundle_auth.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 4 141 2.8 %
Date: 2025-08-05 05:04:49 Functions: 1 8 12.5 %

          Line data    Source code
       1             : #include "fd_bundle_auth.h"
       2             : #include "proto/auth.pb.h"
       3             : #include "../../ballet/base58/fd_base58.h"
       4             : #include "../../ballet/nanopb/pb_decode.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 and keeps them refreshed. */
      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 :   auth_GenerateAuthChallengeRequest req = {0};
      45           0 :   req.role = auth_Role_VALIDATOR;
      46           0 :   memcpy( req.pubkey.bytes, auther->pubkey, 32 );
      47           0 :   req.pubkey.size = 32;
      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 :       &auth_GenerateAuthChallengeRequest_msg, &req,
      55           0 :       NULL, 0
      56           0 :   );
      57           0 :   if( FD_UNLIKELY( !request ) ) return;
      58             : 
      59           0 :   auther->state      = FD_BUNDLE_AUTH_STATE_WAIT_CHALLENGE;
      60           0 :   auther->needs_poll = 0;
      61           0 :   fd_grpc_client_deadline_set(
      62           0 :       request,
      63           0 :       FD_GRPC_DEADLINE_RX_END,
      64           0 :       fd_log_wallclock()+FD_BUNDLE_AUTH_REQUEST_TIMEOUT );
      65             : 
      66           0 :   char key_cstr[ FD_BASE58_ENCODED_32_SZ ];
      67           0 :   fd_base58_encode_32( auther->pubkey, NULL, key_cstr );
      68           0 :   FD_LOG_INFO(( "Requesting bundle auth challenge (identity=%s)", key_cstr ));
      69           0 : }
      70             : 
      71             : int
      72             : fd_bundle_auther_handle_challenge_resp(
      73             :     fd_bundle_auther_t * auther,
      74             :     void const *         data,
      75             :     ulong                data_sz
      76           0 : ) {
      77           0 :   auther->needs_poll = 1;
      78             : 
      79           0 :   pb_istream_t istream = pb_istream_from_buffer( data, data_sz );
      80           0 :   auth_GenerateAuthChallengeResponse resp = auth_GenerateAuthChallengeResponse_init_default;
      81           0 :   int decode_ok = pb_decode( &istream, &auth_GenerateAuthChallengeResponse_msg, &resp );
      82           0 :   if( FD_UNLIKELY( !decode_ok ) ) {
      83           0 :     FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthChallengeResponse) failed" ));
      84           0 :     goto fail;
      85           0 :   }
      86           0 :   if( FD_UNLIKELY( resp.challenge.size!=9UL ) ) {
      87           0 :     FD_LOG_WARNING(( "Unexpected auth.GenerateAuthChallengeResponse challenge size: %u bytes", resp.challenge.size ));
      88           0 :     goto fail;
      89           0 :   }
      90           0 :   memcpy( auther->challenge, resp.challenge.bytes, 9UL );
      91             : 
      92           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_TOKENS;
      93           0 :   FD_LOG_DEBUG(( "Got auth challenge" ));
      94           0 :   return 1;
      95             : 
      96           0 : fail:
      97           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
      98           0 :   return 0;
      99           0 : }
     100             : 
     101             : static void
     102             : fd_bundle_auther_req_tokens( fd_bundle_auther_t *   auther,
     103             :                              fd_grpc_client_t *     client,
     104           0 :                              fd_keyguard_client_t * keyguard ) {
     105           0 :   if( FD_UNLIKELY( fd_grpc_client_request_is_blocked( client ) ) ) return;
     106             : 
     107           0 :   auth_GenerateAuthTokensRequest req = {0};
     108             : 
     109             :   /* Format challenge string as '{base58(pubkey)}-{challenge}' */
     110           0 :   ulong enc_len;
     111           0 :   char * p = (char *)req.challenge.bytes;
     112           0 :   fd_base58_encode_32( auther->pubkey, &enc_len, p );
     113           0 :   p += enc_len;
     114           0 :   p = fd_cstr_append_char( p, '-' );
     115           0 :   p = fd_cstr_append_text( p, auther->challenge, 9UL );
     116           0 :   req.challenge.size = (uint)( (ulong)p - (ulong)req.challenge.bytes );
     117             : 
     118           0 :   memcpy( req.client_pubkey.bytes, auther->pubkey, 32UL );
     119           0 :   req.client_pubkey.size = 32UL;
     120             : 
     121           0 :   fd_keyguard_client_sign( keyguard, req.signed_challenge.bytes, (uchar const *)auther->challenge, 9UL, FD_KEYGUARD_SIGN_TYPE_PUBKEY_CONCAT_ED25519 );
     122           0 :   req.signed_challenge.size = 64UL;
     123             : 
     124           0 :   static char const path[] = "/auth.AuthService/GenerateAuthTokens";
     125           0 :   fd_grpc_h2_stream_t * request = fd_grpc_client_request_start(
     126           0 :       client,
     127           0 :       path, sizeof(path)-1,
     128           0 :       FD_BUNDLE_CLIENT_REQ_Auth_GenerateAuthTokens,
     129           0 :       &auth_GenerateAuthTokensRequest_msg, &req,
     130           0 :       NULL, 0
     131           0 :   );
     132           0 :   if( FD_UNLIKELY( !request ) ) return;
     133             : 
     134           0 :   auther->state      = FD_BUNDLE_AUTH_STATE_WAIT_TOKENS;
     135           0 :   auther->needs_poll = 0;
     136           0 :   fd_grpc_client_deadline_set(
     137           0 :       request,
     138           0 :       FD_GRPC_DEADLINE_RX_END,
     139           0 :       fd_log_wallclock()+FD_BUNDLE_AUTH_REQUEST_TIMEOUT );
     140             : 
     141           0 :   FD_LOG_DEBUG(( "Requesting bundle auth tokens" ));
     142           0 : }
     143             : 
     144             : int
     145             : fd_bundle_auther_handle_tokens_resp(
     146             :     fd_bundle_auther_t * auther,
     147             :     void const *         data,
     148             :     ulong                data_sz
     149           0 : ) {
     150           0 :   pb_istream_t istream = pb_istream_from_buffer( data, data_sz );
     151           0 :   auth_GenerateAuthTokensResponse resp = auth_GenerateAuthTokensResponse_init_default;
     152           0 :   int decode_ok = pb_decode( &istream, &auth_GenerateAuthTokensResponse_msg, &resp );
     153           0 :   if( FD_UNLIKELY( !decode_ok ) ) {
     154           0 :     FD_LOG_WARNING(( "Protobuf decode of (auth.GenerateAuthTokensResponse) failed" ));
     155           0 :     goto fail;
     156           0 :   }
     157           0 :   if( FD_UNLIKELY( !resp.has_access_token || resp.access_token.value.size==0 ) ) {
     158           0 :     FD_LOG_WARNING(( "auth.GenerateAuthTokensResponse: missing access_token" ));
     159           0 :     goto fail;
     160           0 :   }
     161           0 :   if( FD_UNLIKELY( resp.access_token.value.size > sizeof(auther->access_token) ) ) {
     162           0 :     FD_LOG_WARNING(( "auth.GenerateAuthTokensResponse: oversz access_token: %u bytes", resp.access_token.value.size ));
     163           0 :     goto fail;
     164           0 :   }
     165             : 
     166           0 :   fd_memcpy( auther->access_token, resp.access_token.value.bytes, resp.access_token.value.size );
     167           0 :   auther->access_token_sz = (ushort)resp.access_token.value.size;
     168           0 :   auther->state = FD_BUNDLE_AUTH_STATE_DONE_WAIT;
     169           0 :   FD_LOG_DEBUG(( "Got auth tokens" ));
     170           0 :   return 1;
     171             : 
     172           0 : fail:
     173           0 :   auther->state = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
     174           0 :   auther->needs_poll = 1;
     175           0 :   return 0;
     176           0 : }
     177             : 
     178             : void
     179             : fd_bundle_auther_poll( fd_bundle_auther_t *   auther,
     180             :                        fd_grpc_client_t *     client,
     181           0 :                        fd_keyguard_client_t * keyguard ) {
     182           0 :   switch( auther->state ) {
     183           0 :   case FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE:
     184           0 :     fd_bundle_auther_req_challenge( auther, client );
     185           0 :     break;
     186           0 :   case FD_BUNDLE_AUTH_STATE_REQ_TOKENS:
     187           0 :     fd_bundle_auther_req_tokens( auther, client, keyguard );
     188           0 :     break;
     189           0 :   default:
     190           0 :     break;
     191           0 :   }
     192           0 : }
     193             : 
     194             : void
     195           3 : fd_bundle_auther_reset( fd_bundle_auther_t * auther ) {
     196           3 :   auther->state      = FD_BUNDLE_AUTH_STATE_REQ_CHALLENGE;
     197           3 :   auther->needs_poll = 1;
     198           3 : }

Generated by: LCOV version 1.14