Skip to content

chore: init otp-display-grant plan#84

Draft
lwin-kyaw wants to merge 1 commit into
mainfrom
feat/otp-display-grant
Draft

chore: init otp-display-grant plan#84
lwin-kyaw wants to merge 1 commit into
mainfrom
feat/otp-display-grant

Conversation

@lwin-kyaw

@lwin-kyaw lwin-kyaw commented Jun 23, 2026

Copy link
Copy Markdown

PR Title

chore: document OTP display grant plan for untrusted connections

Description

  • ADDED:

    • Adds a design proposal for an optional requireOtpDisplayGrant flow for untrusted dapp-wallet connections.
    • Documents the current session-hijack risk where an attacker can scan the dapp QR code, front-run their own handshake-offer, and complete a session if the user enters an OTP matching the attacker-controlled offer.
    • Proposes delaying OTP display on the wallet until the dapp explicitly grants OTP display for the accepted handshake offer.
    • Includes the proposed compatibility model, protocol additions, strict-flow sequence, and a Mermaid sequence diagram.
  • CHANGED:

Issue

In the current untrusted flow, the wallet displays the OTP before the dapp has accepted a specific handshake-offer. In a same-room scenario, an attacker can scan the same QR code, front-run the real wallet with an attacker-controlled offer, and potentially cause the dapp to bind to the attacker session if the user enters an OTP matching that offer.

Solution

The proposed strict flow adds an opt-in requireOtpDisplayGrant mode. When enabled, the wallet generates the OTP but does not display it immediately. The wallet sends a handshake-offer indicating OTP display grant support, and the dapp sends an encrypted otp-display-grant only to the accepted provisional session channel. The wallet displays the OTP only after receiving that grant.

This means a front-running attacker may still cause a timeout or denial of service, but the real wallet will not reveal an OTP for a session the dapp did not accept, preventing the practical OTP-copy session hijack.

New Strict Flow

  1. Dapp creates SessionRequest with capabilities.otpDisplayGrant = true.
  2. Wallet scans the request.
  3. Wallet generates OTP and deadline but does not emit display_otp.
  4. Wallet sends handshake-offer with otpDisplayGrantRequired: true.
  5. Dapp receives the offer.
  6. If requireOtpDisplayGrant is true and the offer lacks otpDisplayGrantRequired, dapp rejects.
  7. Dapp creates a provisional in-memory session from the offer public key and channel.
  8. Dapp subscribes to the proposed session channel.
  9. Dapp sends encrypted otp-display-grant to the wallet's proposed session channel.
  10. Wallet receives the grant and only then emits display_otp.
  11. User enters OTP into the dapp.
  12. Dapp verifies OTP.
  13. Dapp sends final handshake-ack.
  14. Both clients persist and finalize the session.
sequenceDiagram
	participant U as User
	participant D as Dapp Client
	participant R as Relay
	participant W as Wallet Client

	U->>D: Start untrusted connect
	D->>D: Create SessionRequest<br/>requireOtpDisplayGrant=true
	D-->>U: Show QR code
	U->>W: Scan QR code
	W->>W: Generate OTP<br/>do not display yet
	W->>R: handshake-offer<br/>otpDisplayGrantRequired=true
	R->>D: handshake-offer
	D->>D: Validate strict offer support
	D->>R: otp-display-grant
	R->>W: otp-display-grant
	W-->>U: Display OTP
	U->>D: Enter OTP
	D->>D: Verify OTP
	D->>R: handshake-ack
	R->>W: handshake-ack
	D->>D: Persist session
	W->>W: Persist session
Loading

Checklist

  • Tests are included if applicable
  • Any added code is fully documented

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant