Skip to content

[rehearsal 1b] Per-chain clear/blind-sign: TRON, Solana v0, TON, Mayachain, THORChain affiliate#297

Open
BitHighlander wants to merge 14 commits into
pr/rehearsal-3from
pr/rehearsal-1b
Open

[rehearsal 1b] Per-chain clear/blind-sign: TRON, Solana v0, TON, Mayachain, THORChain affiliate#297
BitHighlander wants to merge 14 commits into
pr/rehearsal-3from
pr/rehearsal-1b

Conversation

@BitHighlander

Copy link
Copy Markdown
Owner

Dress-rehearsal PR 4/5 (stacks on #296). Applies the metadata/blind-sign gating to each non-EVM family. No thortx (owned by PR1a-#294). Local kkemu + firmware-unit green (52 per-chain tests).

BitHighlander and others added 14 commits July 7, 2026 19:46
fsm_msgTonSignTx displayed to_address and amount from deprecated proto
fields that are NOT included in the raw_tx bytes being signed. A malicious
host could show one transfer on screen while getting a different transaction
signed — identical CVE pattern to TronSignTx.

Replace with a single blind-sign prompt showing only the raw_tx byte count.

(cherry picked from commit cf288b9)
fsm_msgTronSignTx displayed to_address and amount from deprecated proto fields
that are NOT included in the raw_data bytes being signed. A malicious host
could show one transfer on screen while getting a different transaction signed.

Replace with a single blind-sign prompt that shows only the raw_data byte count,
which is the actual data committed to by the signature.

(cherry picked from commit 42f19cb)
TIP-712 signing sends only pre-computed hashes — the device cannot
reconstruct or verify the original typed-data struct. Typed-data
signatures can authorise token approvals and permits.

Add an explicit acknowledgement screen before showing the raw hashes
so users understand they are signing unverifiable data.

(cherry picked from commit 2fbd1ea)
For SOL_INSTR_TOKEN_TRANSFER_CHECKED the instruction data encodes the
authoritative decimal count in byte 9 (parsed into pi->extra_u8). The
host-supplied SolanaTokenInfo.decimals is metadata only and could be
forged to misrepresent the displayed amount. Split the fallthrough case
so TRANSFER_CHECKED always uses pi->extra_u8 for decimal scaling while
still displaying the host-supplied symbol as a label.

(cherry picked from commit 2690731)
…_data

The device signs sha256(raw_data) but displayed nothing about it — every
TRON transaction was a 'TRON Blind Sign / Sign N-byte transaction?' screen.
Pioneer-powered swaps from TRC-20 (USDT) were fully blind.

Parse raw_data on-device (a minimal fail-closed protobuf reader over the
exact signed bytes) and clear-sign the two payloads the Vault builds:

- TransferContract: 'Send <amt> TRX to T...?' + memo (THORChain swap memos
  ride in raw_data.data) + fee_limit
- TriggerSmartContract carrying transfer(address,uint256): token contract,
  recipient, amount in base units, fee_limit, memo

Owner address must match the derived key (parsed from signed bytes).
Anything not fully understood — extra contracts, unknown fields,
Permission_id, TRC-10 value, non-transfer selectors, dirty ABI words —
stays on the blind path, now gated behind AdvancedMode like Solana's
opaque transactions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit fa31202)
…ting

pb_read_varint accepted a 10-byte varint whose final byte's payload had
bits set above bit 0 (shift=63 + 7 bits > 64), silently discarding the
overflow via the left-shift. A malformed key, length, amount, or
fee_limit varint using more precision than 64 bits allows could parse
as if it were well-formed instead of falling to TRON_TX_UNVERIFIED.

Reject outright when the 10th byte's payload exceeds 1. Added tests for
overlong key, length-delimited length, amount, and fee_limit varints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 991db99)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 8af7248)
…enom handling

Swap-memo clear-signing (thorchain.c + mayachain.c parseConfirmMemo,
deliberate near-copies kept textually parallel):

- Replace the strtok tokenizer with a manual ':' split that PRESERVES
  empty fields. strtok collapses consecutive delimiters, so an empty
  field (e.g. the limit in "=:ETH.ETH:0xdest::kk:75") silently shifted
  later fields left and the affiliate would have been displayed as the
  limit on a security screen.
- NEW 4th swap confirm screen "Affiliate fee <bps> bps to <affiliate>"
  whenever the affiliate field is non-empty (fee shows "0" when absent).
  Previously memo fields 5-6 (affiliate + fee bps) were silently
  dropped, so users never saw affiliate fee skimming.
- SWAP/ADD/WITHDRAW intent detection, confirm titles/strings,
  "self"/"none" defaults, ADD's optional pool screen and WITHDRAW's
  required basis-points field keep their existing behavior. Memos whose
  second field has no chain.asset '.' pair now consistently fall back to
  raw-memo display.

MAYA hardening to THOR parity:

- Add mayachain_isValidDenom (copy of thorchain_isValidDenom) and
  validate the MsgSend denom (default "cacao" when absent/empty) in
  fsm_msgMayachainMsgAck BEFORE any display, failing with
  Failure_SyntaxError "Invalid denom". Previously arbitrary denom bytes
  flowed unvalidated into the UI and the signing JSON (snprintf'd into
  the amount object in mayachain.c).
- Replace the raw sprintf into denom_str[71] with a bounded snprintf,
  and show the validated/defaulted denom on the final sign screen.
- MsgDeposit display (both chains): widen asset_str 21 -> 64 and
  amount_str 32 -> 96. Long-form THOR assets (~50 chars, e.g.
  ETH.USDT-0XDAC17...) were truncated at 19 chars, and bn_format zeroes
  its output on overflow so the old 32-byte amount buffer would have
  rendered long-asset deposits as an empty amount.

Tests (make xunit green: 200 firmware + 2 board + 4 crypto):

- New parseConfirmMemo suites for both chains driven end-to-end through
  the real confirm() loop: tests preload ButtonAck + DebugLinkDecision
  tiny-message frames on the emulator UDP port and assert the exact
  number of confirm screens consumed, proving the affiliate screen
  exists, empty fields don't shift, and legacy ADD/WITHDRAW screen
  counts are unchanged.
- Re-enable unittests/firmware/mayachain.cpp (stale 2-arg
  mayachain_signTxUpdateMsgSend call since 50164a2); pass "cacao" to
  match the historical JSON. The recorded signature vector turned out to
  be cryptographically invalid for the fixture's key/JSON (the file was
  never in the unit build, see 28c74a0, so it was never validated);
  replaced with a vector recomputed independently via python-ecdsa
  (RFC6979/secp256k1, low-s) that the firmware output matches exactly.
- Add mayachain_isValidDenom validation vectors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit ca7c569)
… honest wording for unspecified affiliate fee

thorchain_parseConfirmMemo/mayachain_parseConfirmMemo received size as a
raw byte count from the BTC OP_RETURN caller (transaction.c passes
op_return_data.size, no NUL), but copied the memo with strlcpy(dst, src,
size), which writes only size-1 bytes — silently dropping the memo's
final character. An affiliate fee of '75' bps displayed as '7'; a
one-character affiliate vanished entirely. Copy the bytes exactly
(bounded memcpy into the zeroed buffer) instead.

Also: an empty fee field with an affiliate present displayed 'Affiliate
fee 0 bps' — but THORName/MAYAName registration lets the network apply
the affiliate's registered default bps when the memo field is empty, so
'0' asserted a fact the device cannot know. Say 'unspecified' instead.

Regression tests pass raw non-NUL-terminated bytes and assert the exact
screen count (the dropped 1-char affiliate showed 3 screens, not 4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 6e6cbcb)
…-by-one at full buffer capacity

mayachain_signTxUpdateMsgSend still wrote denom into the signed JSON with
an unescaped %s — the FSM caller validates first, but the exported
signer helper itself had no defense, unlike thorchain's hardened path.
Add the same default+validate+escaped-write sequence directly in the
signer function so it stays safe if called directly or reused later.

Also: the memo copy fix (previous commit) still dropped the last byte
when size == sizeof(memoBuf) exactly — the documented <=256 contract
allows a full 256-byte raw memo, and copyLen was clamped to
sizeof(memoBuf)-1 regardless. Widen memoBuf by one byte so the full
256-byte capacity can be copied verbatim while a terminator byte is
still guaranteed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit b7d7c31)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 3d9be49)
…haped messages

Pioneer-powered Solana swaps reach the device as versioned (v0)
serialized messages and were always blind-signed:

- solana_parseVersionedTx forced every v0 message OPAQUE. Now a v0
  message parses like a legacy one; only instructions that actually
  reference address-lookup-table accounts (indices beyond the static
  list, unresolvable on-device) force the tx opaque. An attached but
  unreferenced lookup table does not block verification.
- Memo instruction bodies (THORChain swap intents '=:ETH.ETH:...') were
  detected but hidden ('Memo attached'). Printable memos are now shown.
- SolanaSignMessage payloads that parse as a fully-verified Solana tx
  (from byte 0) are clear-signed per instruction with the SignTx rules
  (signer check + per-instruction confirms) instead of hex-blob blind
  signing — this is the exact path wallet integrations use for v0 swap
  txs. Unverifiable payloads keep the AdvancedMode gate + hex preview.
- Over-limit instruction counts now walk the section structurally, so
  trailing-data and lookup-section checks still apply (truncated bodies
  are malformed rather than opaque).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit cc2d11f)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 4d69d6d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant