Skip to content

Commit 2a095bf

Browse files
Merge pull request from GHSA-7hmg-5627-mv35
Fix finalizeParsing()
2 parents a647c36 + 9735116 commit 2a095bf

File tree

6 files changed

+137
-110
lines changed

6 files changed

+137
-110
lines changed

src/eth_plugin_handler.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,4 @@ eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
3131
// NULL for cached address, or base contract address
3232
eth_plugin_result_t eth_plugin_call(int method, void *parameter);
3333

34-
void plugin_ui_start(void);
35-
3634
#endif // _ETH_PLUGIN_HANDLER_H_

src/eth_plugin_ui.c

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/shared_context.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ typedef enum {
175175

176176
#define NETWORK_STRING_MAX_SIZE 16
177177

178-
typedef struct txStringProperties_t {
178+
typedef struct txStringProperties_s {
179179
char fullAddress[43];
180180
char fullAmount[79]; // 2^256 is 78 digits long
181181
char maxFee[50];
@@ -190,7 +190,7 @@ typedef struct txStringProperties_t {
190190
#endif
191191
#define SHARED_CTX_FIELD_2_SIZE 40
192192

193-
typedef struct strDataTmp_t {
193+
typedef struct strDataTmp_s {
194194
char tmp[SHARED_CTX_FIELD_1_SIZE];
195195
char tmp2[SHARED_CTX_FIELD_2_SIZE];
196196
} strDataTmp_t;

src_common/uint256.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,20 @@ bool tostring256(const uint256_t *const number,
230230
UPPER(LOWER(base)) = 0;
231231
LOWER(LOWER(base)) = baseParam;
232232
uint32_t offset = 0;
233-
if ((baseParam < 2) || (baseParam > 16)) {
233+
if ((outLength == 0) || (baseParam < 2) || (baseParam > 16)) {
234234
return false;
235235
}
236236
do {
237-
if (offset > (outLength - 1)) {
238-
return false;
239-
}
240237
divmod256(&rDiv, &base, &rDiv, &rMod);
241238
out[offset++] = HEXDIGITS[(uint8_t) LOWER(LOWER(rMod))];
242-
} while (!zero256(&rDiv));
239+
} while (!zero256(&rDiv) && (offset < outLength));
243240

244-
if (offset > (outLength - 1)) {
241+
if (offset == outLength) { // destination buffer too small
242+
if (outLength > 3) {
243+
strcpy(out, "...");
244+
} else {
245+
out[0] = '\0';
246+
}
245247
return false;
246248
}
247249

src_features/signTx/logic_signTx.c

Lines changed: 123 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "ethUtils.h"
1010
#include "common_ui.h"
1111
#include "ui_callbacks.h"
12+
#include <ctype.h>
1213

1314
#define ERR_SILENT_MODE_CHECK_FAILED 0x6001
1415

@@ -170,26 +171,6 @@ customStatus_e customProcessor(txContext_t *context) {
170171
return CUSTOM_NOT_HANDLED;
171172
}
172173

173-
void to_uppercase(char *str, unsigned char size) {
174-
for (unsigned char i = 0; i < size && str[i] != 0; i++) {
175-
str[i] = str[i] >= 'a' ? str[i] - ('a' - 'A') : str[i];
176-
}
177-
}
178-
179-
void compareOrCopy(char *preapproved_string, size_t size, char *parsed_string, bool silent_mode) {
180-
if (silent_mode) {
181-
/* ETH address are not fundamentally case sensitive but might
182-
have some for checksum purpose, so let's get rid of these diffs */
183-
to_uppercase(preapproved_string, strlen(preapproved_string));
184-
to_uppercase(parsed_string, strlen(parsed_string));
185-
if (memcmp(preapproved_string, parsed_string, strlen(preapproved_string))) {
186-
THROW(ERR_SILENT_MODE_CHECK_FAILED);
187-
}
188-
} else {
189-
strlcpy(preapproved_string, parsed_string, size);
190-
}
191-
}
192-
193174
void reportFinalizeError(bool direct) {
194175
reset_app_context();
195176
if (direct) {
@@ -200,20 +181,20 @@ void reportFinalizeError(bool direct) {
200181
}
201182
}
202183

203-
// Convert `BEgasPrice` and `BEgasLimit` to Uint256 and then stores the multiplication of both in
204-
// `output`.
205-
static void computeFees(txInt256_t *BEgasPrice, txInt256_t *BEgasLimit, uint256_t *output) {
206-
uint256_t gasPrice = {0};
207-
uint256_t gasLimit = {0};
208-
209-
PRINTF("Gas price %.*H\n", BEgasPrice->length, BEgasPrice->value);
210-
PRINTF("Gas limit %.*H\n", BEgasLimit->length, BEgasLimit->value);
211-
convertUint256BE(BEgasPrice->value, BEgasPrice->length, &gasPrice);
212-
convertUint256BE(BEgasLimit->value, BEgasLimit->length, &gasLimit);
213-
mul256(&gasPrice, &gasLimit, output);
184+
static void address_to_string(uint8_t *in,
185+
size_t in_len,
186+
char *out,
187+
size_t out_len,
188+
cx_sha3_t *sha3,
189+
uint64_t chainId) {
190+
if (in_len != 0) {
191+
getEthDisplayableAddress(in, out, out_len, sha3, chainId);
192+
} else {
193+
strlcpy(out, "Contract", out_len);
194+
}
214195
}
215196

216-
static void feesToString(uint256_t *rawFee, char *displayBuffer, uint32_t displayBufferSize) {
197+
static void raw_fee_to_string(uint256_t *rawFee, char *displayBuffer, uint32_t displayBufferSize) {
217198
const char *feeTicker = get_network_ticker();
218199
uint8_t tickerOffset = 0;
219200
uint32_t i;
@@ -255,32 +236,40 @@ static void feesToString(uint256_t *rawFee, char *displayBuffer, uint32_t displa
255236
}
256237

257238
// Compute the fees, transform it to a string, prepend a ticker to it and copy everything to
258-
// `displayBuffer`.
259-
void prepareAndCopyFees(txInt256_t *BEGasPrice,
260-
txInt256_t *BEGasLimit,
261-
char *displayBuffer,
262-
uint32_t displayBufferSize) {
239+
// `displayBuffer` output
240+
static void max_transaction_fee_to_string(const txInt256_t *BEGasPrice,
241+
const txInt256_t *BEGasLimit,
242+
char *displayBuffer,
243+
uint32_t displayBufferSize) {
244+
// Use temporary variables to convert values to uint256_t
245+
uint256_t gasPrice = {0};
246+
uint256_t gasLimit = {0};
247+
// Use temporary variable to store the result of the operation in uint256_t
263248
uint256_t rawFee = {0};
264-
computeFees(BEGasPrice, BEGasLimit, &rawFee);
265-
feesToString(&rawFee, displayBuffer, displayBufferSize);
249+
250+
PRINTF("Gas price %.*H\n", BEGasPrice->length, BEGasPrice->value);
251+
PRINTF("Gas limit %.*H\n", BEGasLimit->length, BEGasLimit->value);
252+
convertUint256BE(BEGasPrice->value, BEGasPrice->length, &gasPrice);
253+
convertUint256BE(BEGasLimit->value, BEGasLimit->length, &gasLimit);
254+
mul256(&gasPrice, &gasLimit, &rawFee);
255+
raw_fee_to_string(&rawFee, displayBuffer, displayBufferSize);
266256
}
267257

268-
void prepareFeeDisplay() {
269-
prepareAndCopyFees(&tmpContent.txContent.gasprice,
270-
&tmpContent.txContent.startgas,
271-
strings.common.maxFee,
272-
sizeof(strings.common.maxFee));
258+
static void nonce_to_string(const txInt256_t *nonce, char *out, size_t out_size) {
259+
uint256_t nonce_uint256;
260+
convertUint256BE(nonce->value, nonce->length, &nonce_uint256);
261+
tostring256(&nonce_uint256, 10, out, out_size);
273262
}
274263

275-
void prepareNetworkDisplay() {
264+
static void get_network_as_string(char *out, size_t out_size) {
276265
const char *name = get_network_name();
277266
if (name == NULL) {
278267
// No network name found so simply copy the chain ID as the network name.
279268
uint64_t chain_id = get_chain_id();
280-
u64_to_string(chain_id, strings.common.network_name, sizeof(strings.common.network_name));
269+
u64_to_string(chain_id, out, out_size);
281270
} else {
282271
// Network name found, simply copy it.
283-
strlcpy(strings.common.network_name, name, sizeof(strings.common.network_name));
272+
strlcpy(out, name, out_size);
284273
}
285274
}
286275

@@ -305,12 +294,27 @@ static void get_public_key(uint8_t *out, uint8_t outLength) {
305294
getEthAddressFromKey(&publicKey, out, &global_sha3);
306295
}
307296

297+
/* Local implmentation of strncasecmp, workaround of the segfaulting base implem
298+
* Remove once strncasecmp is fixed
299+
*/
300+
static int strcasecmp_workaround(const char *str1, const char *str2) {
301+
unsigned char c1, c2;
302+
do {
303+
c1 = *str1++;
304+
c2 = *str2++;
305+
if (toupper(c1) != toupper(c2)) {
306+
return toupper(c1) - toupper(c2);
307+
}
308+
} while (c1 != '\0');
309+
return 0;
310+
}
311+
308312
void finalizeParsing(bool direct) {
309313
char displayBuffer[50];
310314
uint8_t decimals = WEI_TO_ETHER;
311315
const char *ticker = get_network_ticker();
312316
ethPluginFinalize_t pluginFinalize;
313-
bool genericUI = true;
317+
bool use_standard_UI = true;
314318

315319
// Verify the chain
316320
if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) {
@@ -335,7 +339,6 @@ void finalizeParsing(bool direct) {
335339

336340
// Finalize the plugin handling
337341
if (dataContext.tokenContext.pluginStatus >= ETH_PLUGIN_RESULT_SUCCESSFUL) {
338-
genericUI = false;
339342
eth_plugin_prepare_finalize(&pluginFinalize);
340343

341344
uint8_t msg_sender[ADDRESS_LENGTH] = {0};
@@ -381,14 +384,17 @@ void finalizeParsing(bool direct) {
381384
// Handle the right interface
382385
switch (pluginFinalize.uiType) {
383386
case ETH_UI_TYPE_GENERIC:
387+
// Use the dedicated ETH plugin UI
388+
use_standard_UI = false;
384389
tmpContent.txContent.dataPresent = false;
385390
// Add the number of screens + the number of additional screens to get the total
386391
// number of screens needed.
387392
dataContext.tokenContext.pluginUiMaxItems =
388393
pluginFinalize.numScreens + pluginProvideInfo.additionalScreens;
389394
break;
390395
case ETH_UI_TYPE_AMOUNT_ADDRESS:
391-
genericUI = true;
396+
// Use the standard ETH UI as this plugin uses the amount/address UI
397+
use_standard_UI = true;
392398
tmpContent.txContent.dataPresent = false;
393399
if ((pluginFinalize.amount == NULL) || (pluginFinalize.address == NULL)) {
394400
PRINTF("Incorrect amount/address set by plugin\n");
@@ -413,11 +419,15 @@ void finalizeParsing(bool direct) {
413419
return;
414420
}
415421
}
416-
} else {
417-
genericUI = true;
418422
}
419423
}
420424

425+
// User has just validated a swap but ETH received apdus about a non standard plugin / contract
426+
if (called_from_swap && !use_standard_UI) {
427+
PRINTF("ERR_SILENT_MODE_CHECK_FAILED, called_from_swap\n");
428+
THROW(ERR_SILENT_MODE_CHECK_FAILED);
429+
}
430+
421431
if (tmpContent.txContent.dataPresent && !N_storage.dataAllowed) {
422432
reportFinalizeError(direct);
423433
ui_warning_contract_data();
@@ -426,66 +436,94 @@ void finalizeParsing(bool direct) {
426436
}
427437
}
428438

429-
// Prepare destination address to display
430-
if (genericUI) {
431-
if (tmpContent.txContent.destinationLength != 0) {
432-
getEthDisplayableAddress(tmpContent.txContent.destination,
433-
displayBuffer,
434-
sizeof(displayBuffer),
435-
&global_sha3,
436-
chainConfig->chainId);
437-
compareOrCopy(strings.common.fullAddress,
438-
sizeof(strings.common.fullAddress),
439+
// Prepare destination address and amount to display
440+
if (use_standard_UI) {
441+
// Format the address in a temporary buffer, if in swap case compare it with validated
442+
// address, else commit it
443+
address_to_string(tmpContent.txContent.destination,
444+
tmpContent.txContent.destinationLength,
439445
displayBuffer,
440-
called_from_swap);
446+
sizeof(displayBuffer),
447+
&global_sha3,
448+
chainConfig->chainId);
449+
if (called_from_swap) {
450+
// Ensure the values are the same that the ones that have been previously validated
451+
if (strcasecmp_workaround(strings.common.fullAddress, displayBuffer) != 0) {
452+
PRINTF("ERR_SILENT_MODE_CHECK_FAILED, address check failed\n");
453+
THROW(ERR_SILENT_MODE_CHECK_FAILED);
454+
}
441455
} else {
442-
strcpy(strings.common.fullAddress, "Contract");
456+
strlcpy(strings.common.fullAddress, displayBuffer, sizeof(strings.common.fullAddress));
443457
}
444-
}
458+
PRINTF("Address displayed: %s\n", strings.common.fullAddress);
445459

446-
// Prepare amount to display
447-
if (genericUI) {
460+
// Format the amount in a temporary buffer, if in swap case compare it with validated
461+
// amount, else commit it
448462
amountToString(tmpContent.txContent.value.value,
449463
tmpContent.txContent.value.length,
450464
decimals,
451465
ticker,
452466
displayBuffer,
453467
sizeof(displayBuffer));
454-
compareOrCopy(strings.common.fullAmount,
455-
sizeof(strings.common.fullAmount),
456-
displayBuffer,
457-
called_from_swap);
468+
if (called_from_swap) {
469+
// Ensure the values are the same that the ones that have been previously validated
470+
if (strcmp(strings.common.fullAmount, displayBuffer) != 0) {
471+
PRINTF("ERR_SILENT_MODE_CHECK_FAILED, amount check failed\n");
472+
THROW(ERR_SILENT_MODE_CHECK_FAILED);
473+
}
474+
} else {
475+
strlcpy(strings.common.fullAmount, displayBuffer, sizeof(strings.common.fullAmount));
476+
}
477+
PRINTF("Amount displayed: %s\n", strings.common.fullAmount);
458478
}
459479

460-
// Prepare nonce to display
461-
uint256_t nonce;
462-
convertUint256BE(tmpContent.txContent.nonce.value, tmpContent.txContent.nonce.length, &nonce);
463-
tostring256(&nonce, 10, displayBuffer, sizeof(displayBuffer));
464-
strlcpy(strings.common.nonce, displayBuffer, sizeof(strings.common.nonce));
480+
// Compute the max fee in a temporary buffer, if in swap case compare it with validated max fee,
481+
// else commit it
482+
max_transaction_fee_to_string(&tmpContent.txContent.gasprice,
483+
&tmpContent.txContent.startgas,
484+
displayBuffer,
485+
sizeof(displayBuffer));
486+
if (called_from_swap) {
487+
// Ensure the values are the same that the ones that have been previously validated
488+
if (strcmp(strings.common.maxFee, displayBuffer) != 0) {
489+
PRINTF("ERR_SILENT_MODE_CHECK_FAILED, fees check failed\n");
490+
THROW(ERR_SILENT_MODE_CHECK_FAILED);
491+
}
492+
} else {
493+
strlcpy(strings.common.maxFee, displayBuffer, sizeof(strings.common.maxFee));
494+
}
465495

466-
// Compute maximum fee
467-
prepareFeeDisplay();
468496
PRINTF("Fees displayed: %s\n", strings.common.maxFee);
469497

498+
// Prepare nonce to display
499+
nonce_to_string(&tmpContent.txContent.nonce,
500+
strings.common.nonce,
501+
sizeof(strings.common.nonce));
502+
PRINTF("Nonce: %s\n", strings.common.nonce);
503+
470504
// Prepare chainID field
471-
prepareNetworkDisplay();
505+
get_network_as_string(strings.common.network_name, sizeof(strings.common.network_name));
472506
PRINTF("Network: %s\n", strings.common.network_name);
473507

474-
bool no_consent;
508+
bool no_consent_check;
475509

476-
no_consent = called_from_swap;
510+
// If called from swap, the user as already validated a standard transaction
511+
// We have already checked the fields of this transaction above
512+
no_consent_check = called_from_swap && use_standard_UI;
477513

478514
#ifdef NO_CONSENT
479-
no_consent = true;
515+
no_consent_check = true;
480516
#endif // NO_CONSENT
481517

482-
if (no_consent) {
518+
if (no_consent_check) {
483519
io_seproxyhal_touch_tx_ok(NULL);
484520
} else {
485-
if (genericUI) {
521+
if (use_standard_UI) {
486522
ux_approve_tx(false);
487523
} else {
488-
plugin_ui_start();
524+
dataContext.tokenContext.pluginUiState = PLUGIN_UI_OUTSIDE;
525+
dataContext.tokenContext.pluginUiCurrentItem = 0;
526+
ux_approve_tx(true);
489527
}
490528
}
491529
}

0 commit comments

Comments
 (0)