|
| 1 | +#include "fd_gui_config_parse.h" |
| 2 | + |
| 3 | +#include "../../ballet/utf8/fd_utf8.h" |
| 4 | + |
| 5 | +int |
| 6 | +fd_gui_config_validator_info_validate( uchar const * data, ulong sz, cJSON ** out_json, fd_pubkey_t * out_pubkey ) { |
| 7 | + /* |
| 8 | + pub struct ConfigKeys { |
| 9 | + #[cfg_attr(feature = "serde", serde(with = "short_vec"))] |
| 10 | + pub keys: Vec<(Pubkey, bool)>, |
| 11 | + } |
| 12 | +
|
| 13 | + The memory layout of a ConfigProgram account is a bincode serialized |
| 14 | + ConfigKeys followed immediately by a stringified json object |
| 15 | + containing the desired info. |
| 16 | +
|
| 17 | + The short_vec serialization format is a 1-3 bytes size field (where |
| 18 | + the highest bit in a given byte is a continuation bit) followed by |
| 19 | + serialized elements in the vector (in this case, each element is a |
| 20 | + 32byte pubkey followed by a 1byte bool. For our simple parser, we |
| 21 | + only need to consider vectors smaller than 128 elements. |
| 22 | +
|
| 23 | + The JSON schema for a validator info object is the following |
| 24 | +
|
| 25 | + { |
| 26 | + "name": "<validator name>", |
| 27 | + "website": "<website url>", |
| 28 | + "details": "<validator details>", |
| 29 | + "iconUrl": "<icon url>" |
| 30 | + } |
| 31 | +
|
| 32 | + Since accounts are at most 10MB, we should be safely within cJSON's |
| 33 | + allocator limits. |
| 34 | +*/ |
| 35 | + ulong i = 0UL; |
| 36 | + |
| 37 | +#define CHECK( cond ) do { \ |
| 38 | + if( FD_UNLIKELY( !(cond) ) ) { \ |
| 39 | + return 0; \ |
| 40 | + } \ |
| 41 | + } while( 0 ) |
| 42 | + |
| 43 | + /* CHECK that it is safe to read at least n more bytes assuming i is |
| 44 | + the current location. n is untrusted and could trigger overflow, so |
| 45 | + don't do i+n<=payload_sz */ |
| 46 | +#define CHECK_LEFT( n ) CHECK( (n)<=(sz-i) ) |
| 47 | + |
| 48 | + CHECK_LEFT( 1UL ); uchar ck_sz = data[ i ]; i++; |
| 49 | + if( FD_UNLIKELY( ck_sz!=2 ) ) return 0; |
| 50 | + |
| 51 | + struct __attribute__((packed, aligned(1))) config_keys { |
| 52 | + fd_pubkey_t pubkey; |
| 53 | + uchar is_signer; |
| 54 | + }; |
| 55 | + |
| 56 | + struct config_keys * data_config_keys = (struct config_keys *)(data + i); |
| 57 | + CHECK_LEFT( (sizeof(fd_pubkey_t) + 1UL)*ck_sz ); i += (sizeof(fd_pubkey_t) + 1UL)*ck_sz; |
| 58 | + CHECK_LEFT( FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_MAX_SZ ); |
| 59 | + |
| 60 | + /* First entry should be Va1idator1nfo111111111111111111111111111111 */ |
| 61 | + uchar expected[ 32UL ] = { 0x07, 0x51, 0x97, 0x01, 0x74, 0x48, 0xf2, 0xac, 0x5d, 0xc2, 0x3c, 0x9e, 0xbc, 0x7a, 0xc7, 0x8c, 0x0a, 0x27, 0x25, 0x7a, 0xc6, 0x14, 0x45, 0x8d, 0xe0, 0xa4, 0xf1, 0x6f, 0x80, 0x00, 0x00, 0x00 }; |
| 62 | + if( FD_UNLIKELY( memcmp( data_config_keys[0].pubkey.uc, expected, sizeof(fd_pubkey_t) ) || data_config_keys[0].is_signer ) ) return 0; |
| 63 | + |
| 64 | + CHECK_LEFT( sizeof(ulong) ); ulong json_str_sz = *(ulong *)(data+i); i += sizeof(ulong); |
| 65 | + |
| 66 | + CHECK_LEFT( json_str_sz ); |
| 67 | + cJSON * json = cJSON_ParseWithLengthOpts( (char *)(data+i), json_str_sz, NULL, 0 ); |
| 68 | + if( FD_UNLIKELY( !json ) ) return 0; |
| 69 | + |
| 70 | +#undef CHECK |
| 71 | +#undef CHECK_LEFT |
| 72 | + |
| 73 | + *out_json = json; |
| 74 | + fd_memcpy( out_pubkey->uc, data_config_keys[1].pubkey.uc, sizeof(fd_pubkey_t) ); |
| 75 | + return 1; |
| 76 | +} |
| 77 | + |
| 78 | +void |
| 79 | +fd_gui_config_validator_info_parse( cJSON * json, fd_gui_config_parse_info_t * node_info ) { |
| 80 | + const cJSON * name = cJSON_GetObjectItemCaseSensitive( json, "name" ); |
| 81 | + /* cJSON guarantees name->valuestring is NULL terminated */ |
| 82 | + int missing_name = !cJSON_IsString( name ) |
| 83 | + || strlen(name->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ |
| 84 | + || !fd_cstr_printf_check( node_info->name, strlen(name->valuestring)+1UL, NULL, "%s", name->valuestring ) |
| 85 | + || !fd_utf8_verify( node_info->name, strlen(node_info->name) ); |
| 86 | + if( FD_UNLIKELY( missing_name ) ) node_info->name[ 0 ] = '\0'; |
| 87 | + |
| 88 | + const cJSON * website = cJSON_GetObjectItemCaseSensitive( json, "website" ); |
| 89 | + int missing_website = !cJSON_IsString( website ) |
| 90 | + || strlen(website->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_WEBSITE_SZ |
| 91 | + || !fd_cstr_printf_check( node_info->website, strlen(website->valuestring)+1UL, NULL, "%s", website->valuestring ) |
| 92 | + || !fd_utf8_verify( node_info->website, strlen(node_info->website) ); |
| 93 | + if( FD_UNLIKELY( missing_website ) ) node_info->website[ 0 ] = '\0'; |
| 94 | + |
| 95 | + const cJSON * details = cJSON_GetObjectItemCaseSensitive( json, "details" ); |
| 96 | + int missing_details = !cJSON_IsString( details ) |
| 97 | + || strlen(details->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_DETAILS_SZ |
| 98 | + || !fd_cstr_printf_check( node_info->details, strlen(details->valuestring)+1UL, NULL, "%s", details->valuestring ) |
| 99 | + || !fd_utf8_verify( node_info->details, strlen(node_info->details) ); |
| 100 | + if( FD_UNLIKELY( missing_details ) ) node_info->details[ 0 ] = '\0'; |
| 101 | + |
| 102 | + const cJSON * icon_uri = cJSON_GetObjectItemCaseSensitive( json, "iconUrl" ); |
| 103 | + int missing_icon_uri = !cJSON_IsString( icon_uri ) |
| 104 | + || strlen(icon_uri->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ |
| 105 | + || !fd_cstr_printf_check( node_info->icon_uri, strlen(icon_uri->valuestring)+1UL, NULL, "%s", icon_uri->valuestring ) |
| 106 | + || !fd_utf8_verify( node_info->icon_uri, strlen(node_info->icon_uri) ); |
| 107 | + if( FD_UNLIKELY( missing_icon_uri ) ) node_info->icon_uri[ 0 ] = '\0'; |
| 108 | + |
| 109 | + const cJSON * keybase_username = cJSON_GetObjectItemCaseSensitive( json, "keybase_username" ); |
| 110 | + int missing_keybase_username = !cJSON_IsString( keybase_username ) |
| 111 | + || strlen(keybase_username->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ |
| 112 | + || !fd_cstr_printf_check( node_info->keybase_username, strlen(keybase_username->valuestring)+1UL, NULL, "%s", keybase_username->valuestring ) |
| 113 | + || !fd_utf8_verify( node_info->keybase_username, strlen(node_info->keybase_username) ); |
| 114 | + if( FD_UNLIKELY( missing_keybase_username ) ) node_info->keybase_username[ 0 ] = '\0'; |
| 115 | +} |
0 commit comments