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 : }
|