feat(nufi-tron): fix support keystone-nexus tx
What changed, and why it matters
This commit adds support for a new Tron transaction format used by the NuFi/Keystone Nexus wallet integration. It changes how the firmware parses, validates, and signs certain Tron QR-code transactions, and it adjusts how unknown TRC-20 token amounts are converted for display. There is no direct evidence in the commit that this fixes a security vulnerability; it reads as a feature/compatability update.
Review the implementations of `tron_parse_keystone`, `tron_check_keystone`, and `tron_sign_keystone` (not shown in this diff) to ensure they perform equivalent or stronger validation than the existing `tron_*_sign_request` functions. Also verify that the decimal divider change for unknown TRC-20 tokens does not mislead users about transaction amounts, and consider removing or guarding the debug printf that leaks sensitive key metadata.
Security signals we found
New code path gated by `urType == 7` routes Tron transactions to different parse/check/sign functions (`tron_parse_keystone`, `tron_check_keystone`, `tron_sign_keystone`) whose implementations are not shown in this diff.
Unknown TRC-20 token display divider changed from 10^18 to 10^6, which could affect how token amounts are presented to the user.
Debug printf added that logs master fingerprint and extended public key during transaction check.
No explicit bounds checks or input validation changes are visible in the provided diff.
Evidence from the diff
The patch extends Tron transaction handling in two layers. In Rust (wrapped_tron.rs), it adds a fee_limit field to WrappedTron, populates it from raw.fee_limit, and changes the default decimal divider for unknown TRC-20 tokens from 18 to 6 decimals while renaming the label to Unknown-TRC20. In the C UI layer (gui_trx.c), it branches transaction parse/check/sign paths based on urType == 7, routing those requests to new tron_*_keystone functions instead of the standard tron_*_sign_request functions. Debug printf statements for UR type, master fingerprint, and xpub are also added.
Changed components
rust/apps/tron/src/transaction/wrapped_tron.rssrc/ui/gui_chain/multi/web3/gui_trx.cTron transaction parsing/signing flowNuFi/Keystone Nexus Tron integrationInspect captured patch +35 / −6
diff --git a/rust/apps/tron/src/transaction/wrapped_tron.rs b/rust/apps/tron/src/transaction/wrapped_tron.rs
index 6fc2278..2b7b639 100644
--- a/rust/apps/tron/src/transaction/wrapped_tron.rs
+++ b/rust/apps/tron/src/transaction/wrapped_tron.rs
@@ -36,6 +36,7 @@ pub struct WrappedTron {
pub(crate) value: String,
pub(crate) token_short_name: Option<String>,
pub(crate) divider: f64,
+ pub(crate) fee_limit: u64,
}
#[macro_export]
@@ -70,9 +71,11 @@ impl WrappedTron {
value: "0".to_string(),
divider: DIVIDER,
token_short_name: None,
+ fee_limit: 0,
};
if let Some(raw) = &instance.tron_tx.raw_data {
+ instance.fee_limit = raw.fee_limit as u64;
if let Some(contract) = raw.contract.get(0) {
use crate::pb::protocol::transaction::contract::ContractType;
let c_type = ContractType::from_i32(contract.r#type)
@@ -125,8 +128,8 @@ impl WrappedTron {
instance.token = token_info.1.to_string();
instance.divider = token_info.2;
} else {
- instance.token = "TRC20".to_string();
- instance.divider = 10u64.pow(18) as f64;
+ instance.token = "Unknown-TRC20".to_string();
+ instance.divider = 10u64.pow(6) as f64;
}
}
}
@@ -389,6 +392,11 @@ impl WrappedTron {
} else {
Self::generate_trc20_tx(tx)
}?;
+ let fee_limit = if let Some(raw) = &tron_tx.raw_data {
+ raw.fee_limit as u64
+ } else {
+ 0
+ };
Ok(Self {
hd_path: content.hd_path,
extended_pubkey: context.extended_public_key.to_string(),
@@ -401,6 +409,7 @@ impl WrappedTron {
value: tx.value.to_string(),
divider,
token_short_name,
+ fee_limit,
})
}
_ => Err(TronError::InvalidRawTxCryptoBytes(
diff --git a/src/ui/gui_chain/multi/web3/gui_trx.c b/src/ui/gui_chain/multi/web3/gui_trx.c
index ccd31d9..0c1efbf 100644
--- a/src/ui/gui_chain/multi/web3/gui_trx.c
+++ b/src/ui/gui_chain/multi/web3/gui_trx.c
@@ -30,10 +30,17 @@ void GuiSetTrxUrData(URParseResult *urResult, URParseMultiResult *urMultiResult,
void *GuiGetTrxData(void)
{
CHECK_FREE_PARSE_RESULT(g_parseResult);
+ uint8_t mfp[4];
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
-
+ QRCodeType urType = g_isMulti ? g_urMultiResult->ur_type : g_urResult->ur_type;
+ char *trxXpub = GetCurrentAccountPublicKey(XPUB_TYPE_TRX);
do {
- PtrT_TransactionParseResult_DisplayTron parseResult = tron_parse_sign_request(data);
+ PtrT_TransactionParseResult_DisplayTron parseResult = NULL;
+ if( urType == 7) {
+ parseResult = tron_parse_keystone(data, urType, mfp, sizeof(mfp), trxXpub);
+ }else{
+ parseResult = tron_parse_sign_request(data);
+ }
CHECK_CHAIN_BREAK(parseResult);
g_parseResult = (void *)parseResult;
@@ -47,7 +54,13 @@ PtrT_TransactionCheckResult GuiGetTrxCheckResult(void)
uint8_t mfp[4];
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
char *trxXpub = GetCurrentAccountPublicKey(XPUB_TYPE_TRX);
+ QRCodeType urType = g_isMulti ? g_urMultiResult->ur_type : g_urResult->ur_type;
+ printf("GuiGetTrxCheckResult, urType: %d\n", urType);
GetMasterFingerPrint(mfp);
+ printf("Trx check sign request, mfp: %02x%02x%02x%02x, xpub: %s\n", mfp[0], mfp[1], mfp[2], mfp[3], trxXpub);
+ if( urType == 7) {
+ return tron_check_keystone(data, urType, mfp, sizeof(mfp), trxXpub);
+ }
return tron_check_sign_request(data, trxXpub, mfp, sizeof(mfp));
}
@@ -113,6 +126,9 @@ UREncodeResult *GuiGetTrxSignQrCodeData(void)
UREncodeResult *encodeResult = NULL;
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
+ QRCodeType urType = g_isMulti ? g_urMultiResult->ur_type : g_urResult->ur_type;
+ uint8_t mfp[4];
+ GetMasterFingerPrint(mfp);
uint8_t seed[64];
do {
@@ -120,8 +136,12 @@ UREncodeResult *GuiGetTrxSignQrCodeData(void)
if (ret != 0) {
break;
}
-
- encodeResult = tron_sign_request(data, seed, GetCurrentAccountSeedLen());
+ if( urType == 7) {
+ encodeResult = tron_sign_keystone(data, urType, mfp, sizeof(mfp), GetCurrentAccountPublicKey(XPUB_TYPE_TRX),
+ SOFTWARE_VERSION, seed, GetCurrentAccountSeedLen());
+ } else {
+ encodeResult = tron_sign_request(data, seed, GetCurrentAccountSeedLen());
+ }
CHECK_CHAIN_BREAK(encodeResult);
} while (0);
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.