Commit Graph
2 Commits
Author SHA1 Message Date
Satoshi Qazi Muhammed 3c5970b711 Accept the mobile wallet's message signature in identity auth
verifyWalletSignature checked a signature only through @pezkuwi/util-crypto,
whose sr25519 uses Pezkuwi's `bizinikiwi` signing context. The browser
extension signs in that context. The Pezkuwi Wallet mobile app does not, for
a message: it picks the signing chain from the address prefix, prefix 42
falls back to Polkadot, and Polkadot gets the standard `substrate` context.
Transactions are unaffected, since their payload carries the genesis hash.

Measured on 2026-10-01 on dks.news, which verifies the same way: every
sign-in from the app was refused with 401, and the captured signature
verifies in the substrate context and in no other. This verifier has the
same shape and its tests sign only with @pezkuwi/keyring, so the mobile path
was never exercised here -- withdrawal and payment confirmations, identity
and citizenship signed from the app would be refused the same way.

The wallet is the released, store-tested build and is not changed for this.
The verifier now also accepts an sr25519 signature in the substrate context,
via @scure/sr25519, the upstream that @pezkuwi/scure-sr25519 is forked from
with only the context string changed. The proof is unchanged: the account's
own key over a canonical, single-use challenge.

Tests: a substrate-context signature passes; the same signature claimed for
another address, or over a different message, is refused. With the fallback
disabled the first fails (26/27); restored, 27/27.
2026-10-01 17:03:18 +04:00
Satoshi Qazi Muhammed 25ed29edcb security(p2p): freeze trade/offer fund-routing columns + atomic release (2 verify-found bypasses) + tests
A bounded adversarial re-verification of the new fund-custody auth found two real
bypasses that the fix rounds missed; both fixed here, plus executable proof.

- BYPASS #1 (CRITICAL, external): p2p_fiat_trades/p2p_fiat_offers kept USING(true)
  anon UPDATE, and confirm-payment / admin_resolve_dispute read the fund DESTINATION
  (buyer_id), amount and token straight from those rows. An anon attacker could
  `UPDATE p2p_fiat_trades SET buyer_id=<attacker>` so the honest seller's correctly-
  signed release paid the attacker. Fix: BEFORE UPDATE triggers freeze the fund-routing
  columns (offer_id, seller_id, buyer_id, crypto_amount, fiat_amount, price_per_unit,
  escrow_locked_amount on trades; seller_id, token, amount_crypto on offers) for any
  non-service-role writer — trigger-level, independent of RLS/grants; status/proof/chat
  stay open. (Corrects the earlier triage that dismissed these tables as non-custodial.)
- BYPASS #2 (double-release TOCTOU): confirm-payment checked status then released
  non-atomically, so two concurrent differently-nonced valid requests could both release
  and drain other buyers' escrow. Fix: atomic compare-and-swap of payment_sent->completed
  BEFORE the escrow move (loser gets 409; revert on release failure).
- Hardening: REVOKE EXECUTE on the dead legacy resolve_p2p_dispute mover from
  PUBLIC/anon/authenticated.
- Proof: 34 executable tests (Deno) — identity-auth unit tests (sr25519 sign/verify,
  challenge money-param binding, citizen/visa ownership, nonce single-use+concurrency,
  freshness) + per-handler authorization-flow tests (bad-sig/cross-id/wrong-state/replay,
  CAS double-release). All pass.
2026-07-25 02:44:02 -07:00