OpenSourceCheck

OSC-2026-0008 · github.com/KeystoneHQ/keystone3-firmware

info clean-run claude-fable-5 2026-08-04 signed: nostr

Repository
github.com/KeystoneHQ/keystone3-firmware
Commit
31f1fa88a522f492b9ad6260c4c3e316bd209b32
Scope path
rust/apps/bitcoin/src/transactions/
Model
claude-fable-5
Harness
claude-code
Prompts
prompts/tx-authorization-audit-v1.md
Independent runs
1
Transcript sha256
a6e2321d71fa9a0bde02c75e7db61b8a1c4340fbf24d7258d9d0bf6f64bc3a4f
Auditor
TheIcarusWings (theicaruswings)
Signature
nostr-schnorr · npub19tzp8lf3klmqj3dz9mz0qnuvjp7uyy9993gmljmyaxs8phztj7wsnujvq5

Scope. AMENDED 2026-08-04 (original in git history). Transaction-authorization class at HEAD 31f1fa88, examining THE PSBT PATH ONLY: rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs plus shared fee logic in parsed_tx.rs. In scope: change-output verification for single-sig and multisig, cosigner substitution, input-value provenance and the SegWit fee attack, fee computation and large-fee warnings - all on the PSBT path. EXPLICITLY NOT EXAMINED: the legacy raw-transaction (companion-app protobuf) path at transactions/legacy/, which is a separate live dispatch arm (TxData implements TxChecker alongside WrappedPsbt in tx_checker.rs); a later independent audit found a finding there, recorded as OSC-2026-0020. Also not in scope: firmware update/rollback integrity, the C GUI rendering layer, QR/USB transport parsers, entropy (see OSC-2026-0005), and non-Bitcoin chains.

Findings

info OSC-2026-0008-F1 unreviewed

No defect substantiated in scope; change-output handling is correctly independent. Single-sig: the device re-derives the public key from its OWN xpub and rebuilds the expected script per BIP-44/49/84, returning None (not mine) if it does not equal tx_out.script_pubkey - so an attacker-labelled 'change' output is displayed as a payment to a stranger rather than hidden. Multisig: the full m-of-n script is rebuilt from ALL cosigner xpubs held in the stored wallet config (with the PSBT's fingerprint set required to equal the wallet's), closing cosigner substitution. Input values are cross-checked between witness_utxo and non_witness_utxo when both are present, failing with InputValueTampered on mismatch, and a dedicated check cites the Trezor fee-attack advisory by URL. Fee is shown, inputs<outputs is rejected absent ANYONECANPAY, and a large-fee warning fires above 5,000,000 sats or 100 sat/vByte. OBSERVATION, not a finding: non_witness_utxo is not required, so an input supplying only witness_utxo has its amount taken on trust. This is deliberately NOT reported as a vulnerability - for SegWit v0 the amount is committed in the BIP-143 sighash, so a lie yields a signature invalid for the real UTXO and no funds move; the historical Trezor attack required combining signatures across separate sessions with inconsistent declared amounts, which was not constructed against this firmware and would need dynamic testing to settle. Confidence it is not a straightforward vulnerability: high. Confidence it is safe under all multi-session flows: medium.

rust/apps/bitcoin/src/transactions/psbt/wrapped_psbt.rs:1086 (single-sig), :998 (multisig), :497 (get_input_prevout), :591 (check_my_input_value_tampered); rust/apps/bitcoin/src/transactions/parsed_tx.rs:237 (fee thresholds) · CWE-345