Line data Source code
1 : #include "fd_keyguard.h"
2 : #include "fd_keyguard_client.h"
3 : #include "../bundle/fd_bundle_crank_constants.h"
4 : #include "../../waltz/tls/fd_tls.h"
5 :
6 : struct fd_keyguard_sign_req {
7 : fd_keyguard_authority_t * authority;
8 : };
9 :
10 : typedef struct fd_keyguard_sign_req fd_keyguard_sign_req_t;
11 :
12 : static int
13 : fd_keyguard_authorize_vote_txn( fd_keyguard_authority_t const * authority,
14 : uchar const * data,
15 : ulong sz,
16 0 : int sign_type ) {
17 : /* FIXME Add vote transaction authorization here */
18 0 : (void)authority; (void)data; (void)sz; (void)sign_type;
19 0 : return 1;
20 0 : }
21 :
22 : static int
23 : fd_keyguard_authorize_gossip( fd_keyguard_authority_t const * authority,
24 : uchar const * data,
25 : ulong sz,
26 0 : int sign_type ) {
27 : /* FIXME Add gossip message authorization here */
28 0 : (void)authority; (void)data; (void)sz; (void)sign_type;
29 0 : return 1;
30 0 : }
31 :
32 : static int
33 : fd_keyguard_authorize_bundle_crank_txn( fd_keyguard_authority_t const * authority,
34 : uchar const * data,
35 : ulong sz,
36 0 : int sign_type ) {
37 0 : static const uchar disc1[ 8 ] = { FD_BUNDLE_CRANK_DISC_INIT_TIP_DISTR };
38 0 : static const uchar disc2[ 8 ] = { FD_BUNDLE_CRANK_DISC_CHANGE_TIP_RCV };
39 0 : static const uchar disc3[ 8 ] = { FD_BUNDLE_CRANK_DISC_CHANGE_BLK_BLD };
40 :
41 0 : if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
42 :
43 0 : (void)authority;
44 : /* TODO: we can check a lot more bytes */
45 0 : switch( sz ) {
46 0 : case (FD_BUNDLE_CRANK_2_SZ-65UL):
47 0 : return fd_memeq( data+FD_BUNDLE_CRANK_2_IX1_DISC_OFF-65UL, disc2, 8UL ) &&
48 0 : fd_memeq( data+FD_BUNDLE_CRANK_2_IX2_DISC_OFF-65UL, disc3, 8UL );
49 0 : case (FD_BUNDLE_CRANK_3_SZ-65UL):
50 0 : return fd_memeq( data+FD_BUNDLE_CRANK_3_IX1_DISC_OFF-65UL, disc1, 8UL ) &&
51 0 : fd_memeq( data+FD_BUNDLE_CRANK_3_IX2_DISC_OFF-65UL, disc2, 8UL ) &&
52 0 : fd_memeq( data+FD_BUNDLE_CRANK_3_IX3_DISC_OFF-65UL, disc3, 8UL );
53 0 : default:
54 0 : return 0;
55 0 : }
56 0 : }
57 :
58 : static int
59 : fd_keyguard_authorize_ping( fd_keyguard_authority_t const * authority,
60 : uchar const * data,
61 : ulong sz,
62 0 : int sign_type ) {
63 0 : (void)authority;
64 0 : if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
65 0 : if( sz != 32 ) return 0;
66 0 : if( 0!=memcmp( data, "SOLANA_PING_PONG", 16 ) ) return 0;
67 0 : return 1;
68 0 : }
69 :
70 : static int
71 : fd_keyguard_authorize_pong( fd_keyguard_authority_t const * authority,
72 : uchar const * data,
73 : ulong sz,
74 0 : int sign_type ) {
75 0 : (void)authority;
76 0 : if( sign_type != FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 ) return 0;
77 0 : if( sz != 48 ) return 0;
78 0 : if( 0!=memcmp( data, "SOLANA_PING_PONG", 16 ) ) return 0;
79 0 : return 1;
80 0 : }
81 :
82 : static int
83 : fd_keyguard_authorize_gossip_prune( fd_keyguard_authority_t const * authority,
84 : uchar const * data,
85 : ulong sz,
86 0 : int sign_type ) {
87 0 : if( FD_UNLIKELY( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) ) return 0;
88 : /* Prune messages always begin with the node's pubkey */
89 0 : if( sz<40UL ) return 0;
90 0 : if( 0!=memcmp( authority->identity_pubkey, data, 32 ) ) return 0;
91 0 : return 1;
92 0 : }
93 :
94 : static int
95 : fd_keyguard_authorize_repair( fd_keyguard_authority_t const * authority,
96 : uchar const * data,
97 : ulong sz,
98 0 : int sign_type ) {
99 :
100 0 : if( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) return 0;
101 0 : if( sz<80 ) return 0;
102 :
103 0 : uint discriminant = fd_uint_load_4( data );
104 0 : uchar const * sender = data+4;
105 :
106 0 : if( discriminant< 8 ) return 0; /* window_index is min ID */
107 0 : if( discriminant>11 ) return 0; /* ancestor_hashes is max ID */
108 :
109 0 : if( 0!=memcmp( authority->identity_pubkey, sender, 32 ) ) return 0;
110 :
111 0 : return 1;
112 0 : }
113 :
114 : static int
115 : fd_keyguard_authorize_tls_cv( fd_keyguard_authority_t const * authority FD_PARAM_UNUSED,
116 : uchar const * data,
117 : ulong sz,
118 0 : int sign_type ) {
119 0 : if( FD_UNLIKELY( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) ) return 0;
120 0 : if( FD_UNLIKELY( sz != 130 ) ) return 0;
121 :
122 : /* validate client prefix against fd_tls */
123 0 : return fd_memeq( fd_tls13_cli_sign_prefix, data, sizeof(fd_tls13_cli_sign_prefix) );
124 0 : }
125 :
126 : int
127 : fd_keyguard_payload_authorize( fd_keyguard_authority_t const * authority,
128 : uchar const * data,
129 : ulong sz,
130 : int role,
131 0 : int sign_type ) {
132 :
133 0 : if( sz > FD_KEYGUARD_SIGN_REQ_MTU ) {
134 0 : FD_LOG_WARNING(( "oversz signing request (role=%d sz=%lu)", role, sz ));
135 0 : return 0;
136 0 : }
137 :
138 : /* Identify payload type */
139 :
140 0 : ulong payload_mask = fd_keyguard_payload_match( data, sz, sign_type );
141 0 : int match_cnt = fd_ulong_popcnt( payload_mask );
142 0 : if( FD_UNLIKELY( payload_mask==0UL ) ) {
143 0 : FD_LOG_WARNING(( "unrecognized payload type (role=%#x)", (uint)role ));
144 0 : }
145 :
146 0 : int is_ambiguous = match_cnt != 1;
147 :
148 : /* We know that gossip, gossip prune, and repair messages are
149 : ambiguous, so allow mismatches here. */
150 0 : int is_gossip_repair =
151 0 : 0==( payload_mask &
152 0 : (~( FD_KEYGUARD_PAYLOAD_GOSSIP |
153 0 : FD_KEYGUARD_PAYLOAD_REPAIR |
154 0 : FD_KEYGUARD_PAYLOAD_PRUNE ) ) );
155 : /* Also allow ambiguities between shred and gossip ping messages
156 : until shred sign type is fixed... */
157 0 : int is_shred_ping =
158 0 : 0==( payload_mask &
159 0 : (~( FD_KEYGUARD_PAYLOAD_SHRED |
160 0 : FD_KEYGUARD_PAYLOAD_PING ) ) );
161 :
162 0 : if( FD_UNLIKELY( is_ambiguous && !is_gossip_repair && !is_shred_ping ) ) {
163 0 : FD_LOG_WARNING(( "ambiguous payload type (role=%#x mask=%#lx)", (uint)role, payload_mask ));
164 0 : }
165 :
166 : /* Authorize each role */
167 :
168 0 : switch( role ) {
169 :
170 0 : case FD_KEYGUARD_ROLE_SEND: {
171 0 : int txn_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_TXN )) &&
172 0 : fd_keyguard_authorize_vote_txn( authority, data, sz, sign_type );
173 0 : int tls_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_TLS_CV )) &&
174 0 : fd_keyguard_authorize_tls_cv( authority, data, sz, sign_type );
175 0 : if( FD_UNLIKELY( !txn_ok && !tls_ok ) ) {
176 0 : FD_LOG_WARNING(( "unauthorized payload type for send (mask=%#lx)", payload_mask ));
177 0 : return 0;
178 0 : }
179 0 : return 1;
180 0 : }
181 :
182 0 : case FD_KEYGUARD_ROLE_GOSSIP: {
183 0 : int ping_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_PING )) &&
184 0 : fd_keyguard_authorize_ping( authority, data, sz, sign_type );
185 0 : int pong_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_PONG )) &&
186 0 : fd_keyguard_authorize_pong( authority, data, sz, sign_type );
187 0 : int prune_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_PRUNE )) &&
188 0 : fd_keyguard_authorize_gossip_prune( authority, data, sz, sign_type );
189 0 : int gossip_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_GOSSIP )) &&
190 0 : fd_keyguard_authorize_gossip( authority, data, sz, sign_type );
191 0 : if( FD_UNLIKELY( !ping_ok && !pong_ok && !prune_ok && !gossip_ok ) ) {
192 0 : FD_LOG_WARNING(( "unauthorized payload type for gossip (mask=%#lx)", payload_mask ));
193 0 : return 0;
194 0 : }
195 0 : return 1;
196 0 : }
197 :
198 0 : case FD_KEYGUARD_ROLE_REPAIR: {
199 0 : int ping_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_PING )) &&
200 0 : fd_keyguard_authorize_ping( authority, data, sz, sign_type );
201 0 : int pong_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_PONG )) &&
202 0 : fd_keyguard_authorize_pong( authority, data, sz, sign_type );
203 0 : int repair_ok = (!!( payload_mask & FD_KEYGUARD_PAYLOAD_REPAIR )) &&
204 0 : fd_keyguard_authorize_repair( authority, data, sz, sign_type );
205 0 : if( FD_UNLIKELY( !ping_ok && !pong_ok && !repair_ok ) ) {
206 0 : FD_LOG_WARNING(( "unauthorized payload type for repair (mask=%#lx)", payload_mask ));
207 0 : return 0;
208 0 : }
209 0 : return 1;
210 0 : }
211 :
212 0 : case FD_KEYGUARD_ROLE_LEADER:
213 0 : if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_SHRED ) ) {
214 0 : FD_LOG_WARNING(( "unauthorized payload type for leader (mask=%#lx)", payload_mask ));
215 0 : return 0;
216 0 : }
217 : /* no further restrictions on shred */
218 0 : return 1;
219 :
220 0 : case FD_KEYGUARD_ROLE_BUNDLE:
221 0 : if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_BUNDLE ) ) {
222 0 : FD_LOG_WARNING(( "unauthorized payload type for bundle (mask=%#lx)", payload_mask ));
223 0 : return 0;
224 0 : }
225 : /* no further restrictions on bundle */
226 0 : return 1;
227 :
228 0 : case FD_KEYGUARD_ROLE_EVENT:
229 0 : if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_EVENT ) ) {
230 0 : FD_LOG_WARNING(( "unauthorized payload type for event (mask=%#lx)", payload_mask ));
231 0 : return 0;
232 0 : }
233 : /* no further restrictions on event */
234 0 : return 1;
235 :
236 0 : case FD_KEYGUARD_ROLE_BUNDLE_CRANK:
237 0 : if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_TXN ) ) {
238 0 : FD_LOG_WARNING(( "unauthorized payload type for event (mask=%#lx)", payload_mask ));
239 0 : return 0;
240 0 : }
241 0 : return fd_keyguard_authorize_bundle_crank_txn( authority, data, sz, sign_type );
242 :
243 0 : default:
244 0 : FD_LOG_WARNING(( "unsupported role=%#x", (uint)role ));
245 0 : return 0;
246 0 : }
247 0 : }
|