Merge pull request #133 from LedgerHQ/fix_security_and_display_issues
Display Validator address, add bound to withdrawal derivation index, check deposit contract addresss.
This commit is contained in:
@@ -56,10 +56,11 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI,
|
||||
queryContractUI->msgLength = msgLength;
|
||||
}
|
||||
|
||||
int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init) {
|
||||
eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
|
||||
ethPluginInitContract_t *init) {
|
||||
uint8_t i;
|
||||
const uint8_t **selectors;
|
||||
dataContext.tokenContext.pluginAvailable = 0;
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
// Handle hardcoded plugin list
|
||||
PRINTF("Selector %.*H\n", 4, init->selector);
|
||||
for (i = 0;; i++) {
|
||||
@@ -74,7 +75,7 @@ int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *i
|
||||
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
|
||||
((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
|
||||
strcpy(dataContext.tokenContext.pluginName, INTERNAL_ETH_PLUGINS[i].alias);
|
||||
dataContext.tokenContext.pluginAvailable = 1;
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
contractAddress = NULL;
|
||||
break;
|
||||
}
|
||||
@@ -94,23 +95,22 @@ int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *i
|
||||
} else {
|
||||
PRINTF("Trying alias %s\n", dataContext.tokenContext.pluginName);
|
||||
}
|
||||
int status = eth_plugin_call(contractAddress, ETH_PLUGIN_INIT_CONTRACT, (void *) init);
|
||||
if (!status) {
|
||||
return 0;
|
||||
}
|
||||
if (status == ETH_PLUGIN_RESULT_OK) {
|
||||
break;
|
||||
}
|
||||
if (status == ETH_PLUGIN_RESULT_OK_ALIAS) {
|
||||
eth_plugin_result_t status =
|
||||
eth_plugin_call(contractAddress, ETH_PLUGIN_INIT_CONTRACT, (void *) init);
|
||||
if (status <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
return status;
|
||||
} else if (status == ETH_PLUGIN_RESULT_OK_ALIAS) {
|
||||
contractAddress = NULL;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
PRINTF("eth_plugin_init ok %s\n", dataContext.tokenContext.pluginName);
|
||||
dataContext.tokenContext.pluginAvailable = 1;
|
||||
return 1;
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
return ETH_PLUGIN_RESULT_OK;
|
||||
}
|
||||
|
||||
int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
eth_plugin_result_t eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
ethPluginSharedRW_t pluginRW;
|
||||
ethPluginSharedRO_t pluginRO;
|
||||
char tmp[PLUGIN_ID_LENGTH];
|
||||
@@ -122,9 +122,9 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
pluginRO.txContent = &tmpContent.txContent;
|
||||
|
||||
if (contractAddress == NULL) {
|
||||
if (!dataContext.tokenContext.pluginAvailable) {
|
||||
if (dataContext.tokenContext.pluginStatus <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
PRINTF("Cached plugin call but no plugin available\n");
|
||||
return 0;
|
||||
return dataContext.tokenContext.pluginStatus;
|
||||
}
|
||||
alias = dataContext.tokenContext.pluginName;
|
||||
} else {
|
||||
@@ -176,7 +176,7 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unknown plugin method %d\n", method);
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
|
||||
// Perform the call
|
||||
@@ -222,8 +222,10 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
break;
|
||||
case ETH_PLUGIN_RESULT_OK_ALIAS:
|
||||
break;
|
||||
case ETH_PLUGIN_RESULT_ERROR:
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
default:
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_PROVIDE_PARAMETER:
|
||||
@@ -231,8 +233,10 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
case ETH_PLUGIN_RESULT_OK:
|
||||
case ETH_PLUGIN_RESULT_FALLBACK:
|
||||
break;
|
||||
case ETH_PLUGIN_RESULT_ERROR:
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
default:
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_FINALIZE:
|
||||
@@ -240,8 +244,10 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
case ETH_PLUGIN_RESULT_OK:
|
||||
case ETH_PLUGIN_RESULT_FALLBACK:
|
||||
break;
|
||||
case ETH_PLUGIN_RESULT_ERROR:
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
default:
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_PROVIDE_TOKEN:
|
||||
@@ -249,23 +255,25 @@ int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter) {
|
||||
case ETH_PLUGIN_RESULT_OK:
|
||||
case ETH_PLUGIN_RESULT_FALLBACK:
|
||||
break;
|
||||
case ETH_PLUGIN_RESULT_ERROR:
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
default:
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_ID:
|
||||
if (((ethQueryContractID_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
|
||||
return 0;
|
||||
if (((ethQueryContractID_t *) parameter)->result <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_UI:
|
||||
if (((ethQueryContractUI_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
|
||||
return 0;
|
||||
if (((ethQueryContractUI_t *) parameter)->result <= ETH_PLUGIN_RESULT_OK) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ETH_PLUGIN_RESULT_OK;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI,
|
||||
char *msg,
|
||||
uint32_t msgLength);
|
||||
|
||||
int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init);
|
||||
eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
|
||||
ethPluginInitContract_t *init);
|
||||
// NULL for cached address, or base contract address
|
||||
int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter);
|
||||
eth_plugin_result_t eth_plugin_call(uint8_t *contractAddress, int method, void *parameter);
|
||||
int compound_plugin_call(uint8_t *contractAddress, int method, void *parameter);
|
||||
|
||||
void plugin_ui_start(void);
|
||||
|
||||
@@ -21,11 +21,16 @@ typedef enum {
|
||||
} eth_plugin_msg_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
// Unsuccesful return values
|
||||
ETH_PLUGIN_RESULT_ERROR = 0x00,
|
||||
ETH_PLUGIN_RESULT_OK = 0x01,
|
||||
ETH_PLUGIN_RESULT_OK_ALIAS = 0x02,
|
||||
ETH_PLUGIN_RESULT_FALLBACK = 0x03
|
||||
ETH_PLUGIN_RESULT_UNAVAILABLE = 0x01,
|
||||
ETH_PLUGIN_RESULT_UNSUCCESSFUL = 0x02, // Used for comparison
|
||||
|
||||
// Successful return values
|
||||
ETH_PLUGIN_RESULT_SUCCESSFUL = 0x03, // Used for comparison
|
||||
ETH_PLUGIN_RESULT_OK = 0x04,
|
||||
ETH_PLUGIN_RESULT_OK_ALIAS = 0x05,
|
||||
ETH_PLUGIN_RESULT_FALLBACK = 0x06
|
||||
|
||||
} eth_plugin_result_t;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ typedef enum starkQuantumType_e {
|
||||
|
||||
typedef struct tokenContext_t {
|
||||
char pluginName[PLUGIN_ID_LENGTH];
|
||||
uint8_t pluginAvailable;
|
||||
uint8_t pluginStatus;
|
||||
|
||||
uint8_t data[32];
|
||||
uint8_t fieldIndex;
|
||||
|
||||
@@ -39,7 +39,7 @@ void handleSign(uint8_t p1,
|
||||
dataLength -= 4;
|
||||
}
|
||||
dataPresent = false;
|
||||
dataContext.tokenContext.pluginAvailable = 0;
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
|
||||
// EIP 2718: TransactionType might be present before the TransactionPayload.
|
||||
uint8_t txType = *workBuffer;
|
||||
|
||||
@@ -48,18 +48,21 @@ customStatus_e customProcessor(txContext_t *context) {
|
||||
PRINTF("Missing function selector\n");
|
||||
return CUSTOM_FAULT;
|
||||
}
|
||||
dataContext.tokenContext.pluginAvailable = 0;
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
// If contract debugging mode is activated, do not go through the plugin activation
|
||||
// as they wouldn't be displayed if the plugin consumes all data but fallbacks
|
||||
if (!N_storage.contractDetails) {
|
||||
eth_plugin_prepare_init(&pluginInit,
|
||||
context->workBuffer,
|
||||
context->currentFieldLength);
|
||||
dataContext.tokenContext.pluginAvailable =
|
||||
dataContext.tokenContext.pluginStatus =
|
||||
eth_plugin_perform_init(tmpContent.txContent.destination, &pluginInit);
|
||||
}
|
||||
PRINTF("pluginAvailable %d\n", dataContext.tokenContext.pluginAvailable);
|
||||
if (dataContext.tokenContext.pluginAvailable) {
|
||||
PRINTF("pluginstatus %d\n", dataContext.tokenContext.pluginStatus);
|
||||
eth_plugin_result_t status = dataContext.tokenContext.pluginStatus;
|
||||
if (status == ETH_PLUGIN_RESULT_ERROR) {
|
||||
return CUSTOM_FAULT;
|
||||
} else if (status >= ETH_PLUGIN_RESULT_SUCCESSFUL) {
|
||||
dataContext.tokenContext.fieldIndex = 0;
|
||||
dataContext.tokenContext.fieldOffset = 0;
|
||||
copyTxData(context, NULL, 4);
|
||||
@@ -83,7 +86,8 @@ customStatus_e customProcessor(txContext_t *context) {
|
||||
dataContext.tokenContext.fieldOffset = 0;
|
||||
blockSize = 4;
|
||||
} else {
|
||||
if (!N_storage.contractDetails && !dataContext.tokenContext.pluginAvailable) {
|
||||
if (!N_storage.contractDetails &&
|
||||
dataContext.tokenContext.pluginStatus <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) {
|
||||
return CUSTOM_NOT_HANDLED;
|
||||
}
|
||||
blockSize = 32 - (dataContext.tokenContext.fieldOffset % 32);
|
||||
@@ -112,7 +116,7 @@ customStatus_e customProcessor(txContext_t *context) {
|
||||
|
||||
if (copySize == blockSize) {
|
||||
// Can process or display
|
||||
if (dataContext.tokenContext.pluginAvailable) {
|
||||
if (dataContext.tokenContext.pluginStatus >= ETH_PLUGIN_RESULT_SUCCESSFUL) {
|
||||
ethPluginProvideParameter_t pluginProvideParameter;
|
||||
eth_plugin_prepare_provide_parameter(&pluginProvideParameter,
|
||||
dataContext.tokenContext.data,
|
||||
@@ -275,7 +279,7 @@ void finalizeParsing(bool direct) {
|
||||
32);
|
||||
|
||||
// Finalize the plugin handling
|
||||
if (dataContext.tokenContext.pluginAvailable) {
|
||||
if (dataContext.tokenContext.pluginStatus >= ETH_PLUGIN_RESULT_SUCCESSFUL) {
|
||||
genericUI = false;
|
||||
eth_plugin_prepare_finalize(&pluginFinalize);
|
||||
if (!eth_plugin_call(NULL, ETH_PLUGIN_FINALIZE, (void *) &pluginFinalize)) {
|
||||
|
||||
@@ -20,17 +20,54 @@ void getEth2PublicKey(uint32_t *bip32Path, uint8_t bip32PathLength, uint8_t *out
|
||||
#define ETH2_WITHDRAWAL_CREDENTIALS_LENGTH 0x20
|
||||
#define ETH2_SIGNATURE_LENGTH 0x60
|
||||
|
||||
static const uint8_t deposit_contract_address[] = {0x00, 0x00, 0x00, 0x00, 0x21, 0x9a, 0xb5,
|
||||
0x40, 0x35, 0x6c, 0xbb, 0x83, 0x9c, 0xbe,
|
||||
0x05, 0x30, 0x3d, 0x77, 0x05, 0xfa};
|
||||
|
||||
// Highest index for withdrawal derivation path.
|
||||
#define INDEX_MAX 65536 // 2 ^ 16 : arbitrary value to protect from path attacks.
|
||||
|
||||
typedef struct eth2_deposit_parameters_t {
|
||||
uint8_t valid;
|
||||
char deposit_address[ETH2_DEPOSIT_PUBKEY_LENGTH];
|
||||
} eth2_deposit_parameters_t;
|
||||
|
||||
static void to_lowercase(char *str, unsigned char size) {
|
||||
for (unsigned char i = 0; i < size && str[i] != 0; i++) {
|
||||
if (str[i] >= 'A' && str[i] <= 'Z') {
|
||||
str[i] += 'a' - 'A';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fills the `out` buffer with the lowercase string representation of the pubkey passed in as binary
|
||||
// format by `in`. Does not check the size, so expects `out` to be big enough to hold the string
|
||||
// representation. Returns the length of string (counting the null terminating character).
|
||||
static int getEthDisplayableAddress(char *out, uint8_t *in, cx_sha3_t *sha3) {
|
||||
out[0] = '0';
|
||||
out[1] = 'x';
|
||||
getEthAddressStringFromBinary(in, (uint8_t *) out + 2, sha3, chainConfig);
|
||||
|
||||
uint8_t destinationLen = strlen(out) + 1; // Adding one to account for \0.
|
||||
|
||||
return destinationLen;
|
||||
}
|
||||
|
||||
void eth2_plugin_call(int message, void *parameters) {
|
||||
switch (message) {
|
||||
case ETH_PLUGIN_INIT_CONTRACT: {
|
||||
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
|
||||
eth2_deposit_parameters_t *context = (eth2_deposit_parameters_t *) msg->pluginContext;
|
||||
context->valid = 1;
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
if (memcmp(deposit_contract_address,
|
||||
msg->pluginSharedRO->txContent->destination,
|
||||
sizeof(deposit_contract_address)) != 0) {
|
||||
PRINTF("eth2plugin: failed to check deposit contract\n");
|
||||
context->valid = 0;
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
} else {
|
||||
context->valid = 1;
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ETH_PLUGIN_PROVIDE_PARAMETER: {
|
||||
@@ -83,9 +120,34 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
case 4 + (32 * 3): // deposit data root
|
||||
case 4 + (32 * 5): // deposit pubkey
|
||||
case 4 + (32 * 6):
|
||||
case 4 + (32 * 5): // deposit pubkey 1
|
||||
{
|
||||
// Copy the first 32 bytes.
|
||||
memcpy(context->deposit_address,
|
||||
msg->parameter,
|
||||
sizeof(context->deposit_address));
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
break;
|
||||
}
|
||||
case 4 + (32 * 6): // deposit pubkey 2
|
||||
{
|
||||
// Copy the last 16 bytes.
|
||||
memcpy(context->deposit_address + 32,
|
||||
msg->parameter,
|
||||
sizeof(context->deposit_address) - 32);
|
||||
|
||||
// Use a temporary buffer to store the string representation.
|
||||
char tmp[ETH2_DEPOSIT_PUBKEY_LENGTH];
|
||||
getEthDisplayableAddress(tmp,
|
||||
(uint8_t *) context->deposit_address,
|
||||
msg->pluginSharedRW->sha3);
|
||||
|
||||
// Copy back the string to the global variable.
|
||||
strcpy(context->deposit_address, tmp);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
break;
|
||||
}
|
||||
case 4 + (32 * 3): // deposit data root
|
||||
case 4 + (32 * 10): // signature
|
||||
case 4 + (32 * 11):
|
||||
case 4 + (32 * 12):
|
||||
@@ -98,6 +160,14 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
uint32_t withdrawalKeyPath[4];
|
||||
withdrawalKeyPath[0] = WITHDRAWAL_KEY_PATH_1;
|
||||
withdrawalKeyPath[1] = WITHDRAWAL_KEY_PATH_2;
|
||||
if (eth2WithdrawalIndex > INDEX_MAX) {
|
||||
PRINTF("eth2 plugin: withdrawal index is too big\n");
|
||||
PRINTF("Got %u which is higher than INDEX_MAX (%u)\n",
|
||||
eth2WithdrawalIndex,
|
||||
INDEX_MAX);
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
context->valid = 0;
|
||||
}
|
||||
withdrawalKeyPath[2] = eth2WithdrawalIndex;
|
||||
withdrawalKeyPath[3] = WITHDRAWAL_KEY_PATH_4;
|
||||
getEth2PublicKey(withdrawalKeyPath, 4, tmp);
|
||||
@@ -108,9 +178,11 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
PRINTF("eth2 plugin invalid withdrawal credentials\n");
|
||||
PRINTF("Got %.*H\n", 32, msg->parameter);
|
||||
PRINTF("Expected %.*H\n", 32, tmp);
|
||||
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
||||
context->valid = 0;
|
||||
} else {
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
}
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
|
||||
default:
|
||||
@@ -124,7 +196,7 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
eth2_deposit_parameters_t *context = (eth2_deposit_parameters_t *) msg->pluginContext;
|
||||
PRINTF("eth2 plugin finalize\n");
|
||||
if (context->valid) {
|
||||
msg->numScreens = 1;
|
||||
msg->numScreens = 2;
|
||||
msg->uiType = ETH_UI_TYPE_GENERIC;
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} else {
|
||||
@@ -141,9 +213,9 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_UI: {
|
||||
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
|
||||
// eth2_deposit_parameters_t *context = (eth2_deposit_parameters_t*)msg->pluginContext;
|
||||
eth2_deposit_parameters_t *context = (eth2_deposit_parameters_t *) msg->pluginContext;
|
||||
switch (msg->screenIndex) {
|
||||
case 0: {
|
||||
case 0: { // Amount screen
|
||||
uint8_t decimals = WEI_TO_ETHER;
|
||||
uint8_t *ticker = (uint8_t *) PIC(chainConfig->coinName);
|
||||
strcpy(msg->title, "Amount");
|
||||
@@ -155,6 +227,11 @@ void eth2_plugin_call(int message, void *parameters) {
|
||||
100);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
} break;
|
||||
case 1: { // Deposit pubkey screen
|
||||
strcpy(msg->title, "Validator");
|
||||
strcpy(msg->msg, context->deposit_address);
|
||||
msg->result = ETH_PLUGIN_RESULT_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user