Skip to content

Port the WebAuthn-PRF fee-payer derivation to mobile (C2/C3) — the mobile mainnet blocker #682

Description

@Miracle656

Background

The 2026-08-05 external security assessment raised C2 and C3:

  • C2 — the fee-payer key is derivable from the credential id, which is public
  • C3 — the derived secret is persisted in plaintext local storage

Both were fixed on web by deriving the fee-payer seed from a WebAuthn PRF extension output and holding it in memory / sessionStorage, cleared on lock. See docs/adr/0003-fee-payer-key-from-webauthn-prf.md.

The mobile port was never done. frontend/mobile still uses legacy fee-payer derivation, so C2 and C3 are live there.

docs/MAINNET_READINESS.md records the consequence:

Mobile — ❌ Not mainnet-safe. C2/C3 PRF fix never ported to mobile; still legacy fee-payer derivation.
Launch web-only on mainnet; mobile waits for the PRF port.

So this single issue is what keeps the mobile app off mainnet. It is the highest-value security work outstanding in the repo.

What to build

Bring mobile to the same derivation the web wallet uses, or — if the platform genuinely cannot — a documented equivalent with the same properties.

Key files

  • docs/adr/0003-fee-payer-key-from-webauthn-prf.md — the design to match
  • frontend/mobile/ — passkey registration/assertion and fee-payer derivation
  • sdk/src/ — the shared derivation, if it can be lifted rather than duplicated

The hard part, stated up front

PRF support on native passkey APIs is not guaranteed the way it is in browsers, and differs across iOS and Android and across authenticators. Establishing what is actually available on device is part of this issue, not a preliminary.

That means the first deliverable is a findings note, not code: which platforms expose PRF (or an equivalent HMAC secret), at which OS versions, and what the fallback must be when unavailable.

A fallback that quietly restores C2/C3 is not acceptable. If a device cannot derive securely, the wallet should say so and refuse mainnet rather than pretend.

Acceptance criteria

  • A short findings note in docs/ recording PRF (or equivalent) availability on iOS and Android, with the OS/authenticator versions tested — including the negative results
  • Mobile derives the fee-payer seed from the PRF output, not from the credential id
  • The derived secret is never written to persistent storage; a test asserts it is absent after lock
  • Behaviour on a device without PRF is explicit and documented — degrade or refuse, never silently fall back to legacy derivation
  • A test asserts that the credential id alone is not sufficient to derive the fee-payer key (this is C2 stated as an executable check)
  • Web and mobile derive the same key for the same passkey, verified against a fixture — otherwise a user's fee payer differs per device and funds land somewhere they cannot spend from
  • docs/MAINNET_READINESS.md updated: the mobile row moves off "not mainnet-safe", or states precisely what still blocks it

Notes

  • The cross-platform key-agreement criterion is the one most likely to be missed and the most expensive to discover later.
  • frontend/mobile uses plain npm, not --legacy-peer-deps; the latter corrupts the lockfile. Tests run via jest-expo. The Maestro e2e job is pre-existing-red on main — not your fault, do not chase it.
  • Related but separate: M3 (on-curve pubkey validation compiled out of production) is the other mainnet security blocker and is tracked on its own.

Drips Wave · Complexity: Advanced · 200 points


Required: Before submitting, join the contributor Telegram so your work can be tracked and counted toward the Stellar Wave: https://t.me/+fxHXq8f1SwlkZDBk

Activity

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

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions