Skip to content

OP_RETURN output poisons duplicate detector and rejects the next transaction #610

Description

@BitHighlander

Target

BitHighlander/keepkey-firmware, firmware 7.14.3 Bitcoin-only + Dice audit. The affected signing code is shared with the full product.

Finding

Signing a transaction whose final output is OP_RETURN poisons the duplicate-transaction detector. The next legitimate transaction with a normal payment output is refused with the on-device warning WARNING: Duplicate Transaction! and the protocol returns (Failure_ActionCancelled, "Transaction cancelled").

This is fail-safe (refusal, not an unauthorized signature), but every OP_RETURN-terminated THORChain/Maya-style transaction can arm the denial until power cycle.

Root cause

STAGE_REQUEST_3_OUTPUT calls txin_dgst_final() once for every output. A normal confirmed address output calls txin_dgst_save_and_reset(), but compile_output() returns early for PAYTOOPRETURN without resetting. When OP_RETURN is the last output, the SHA-256 context is left finalized. The next transaction hashes inputs into that finalized context, producing a different digest while amount/address can match the previous payment output—the exact predicate that triggers the warning.

Reproduction / regression evidence

The new Docker Bitcoin-only product suite performs, in one emulator session:

  1. sign a payment + OP_RETURN transaction;
  2. sign a legitimate payment transaction;
  3. observe review codes [ConfirmOutput, Other] and cancellation on the second signing.

Control: the same payment transaction signs twice when no OP_RETURN transaction precedes it.

JUnit result: test_msg_bitcoin_only_variant.py::TestBitcoinOnlyVariant::test_op_return_does_not_poison_the_duplicate_detector is the sole failure after the other Bitcoin-only boundary defects were corrected (252 passed, 415 variant/version skips).

Expected fix

Finalize the input digest once, after input collection and before output review begins—not once per output. Preserve the existing save/reset after the confirmed payment output. Gate with the cross-transaction regression above plus native and full-product signing suites.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions