Skip to content

signMessage silently falls back to mainHd when an sp1 address is selected, producing a signature unrelated to the displayed address #3580

Description

@antho31

Describe the bug

In Settings > Advanced > Sign & verify messages, the address picker offers the wallet's
Silent Payment (sp1...) address. Signing a message with it succeeds and returns a signature,
but that signature is produced with the wallet's mainHd key, which is unrelated to the sp1
address shown in the UI. No error or warning is displayed.

The problem is not that Silent Payment message signing is unsupported. The problem is that it
fails silently and hands the user something that looks like a valid ownership proof.

Root cause

cw_bitcoin/lib/electrum_wallet.dart, signMessage():

Future<String> signMessage(String message, {String? address = null}) async {
  final addressRecord = address != null
      ? walletAddresses.allAddresses.firstWhereOrNull((addr) => addr.address == address)
      : null;
  ...
  final hd = addressRecord != null
      ? _hdFor(record: addressRecord).childKey(Bip32KeyIndex(addressRecord.index))
      : mainHd;
  ...
}

walletAddresses.allAddresses returns only _addresses (List<BitcoinAddressRecord>).
Silent Payment addresses live in a separate list, silentAddresses
(List<BitcoinSilentPaymentAddressRecord>), which is never included in allAddresses.
firstWhereOrNull therefore never matches the sp1 string, addressRecord stays null,
and execution takes the mainHd fallback.

Steps to reproduce

  1. Create or restore a Bitcoin wallet with Silent Payments enabled
  2. Go to Settings > Advanced > Sign & verify messages
  3. Select the wallet's sp1... address
  4. Sign any message
  5. Recover the public key from the signature (BIP137 ECDSA recovery over the
    "Bitcoin Signed Message" magic hash)
  6. The recovered key does not match either public key encoded in the sp1 address
    (bech32m, 66-byte payload: 33 bytes B_scan || 33 bytes B_spend). It matches the
    wallet's mainHd key instead, confirming the fallback path above.

Expected behavior

Signing must never silently use a key unrelated to the address the user selected. Minimal
acceptable fix, either:

  • exclude sp1 entries from the address picker on this screen, or
  • have signMessage raise an explicit error when the selected address has no signable record

Whether an sp1 address should be signable at all, and under which scheme, is a separate design
question. I've written it up in a comment below rather than bundling it here, so this issue can
stay scoped to the silent wrong-key fallback.

Impact

Cake Wallet's message signing cannot currently be used to prove ownership of a Silent Payment
address, for example by a third party service verifying an SP address before sending recurring
payments. The failure is silent, so both the user and the verifier are given false confidence
that ownership was proven.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions