Security
WalletPair assumes an active attacker can control the relay. The relay can observe connection metadata, CAIP-2 chain suffixes, timing, and ciphertext sizes, and can drop, delay, replay, reorder, or inject frames. It cannot decrypt or forge an accepted encrypted frame.
Cryptographic stack
| Layer | Construction |
|---|---|
| Key exchange | Fresh X25519 key pair per channel; all-zero shared secret is rejected. |
| Key derivation | HKDF-SHA256 with channel ID salt, transcript hash, and directional labels. |
| Fingerprint | SHA-256 of the dApp pairing fields, reduced modulo 10,000. |
| Payload | JSON-only MessagePack, bounded to 64 KiB and 64 nesting levels. |
| AEAD | ChaCha20-Poly1305 with a 12-byte nonce of eight zero bytes plus the uint32 sequence. |
| Additional data | Protocol label, channel ID, transcript hash, direction, sequence, and CAIP-2 chain ID. |
Pairing assurance and limitations
The wallet authenticates the dApp key scanned from QR when the user compares the matching four digits. The code is a short human check, not cryptographic-strength authentication: a replacement committed before learning it has a 1/10,000 chance per attempt, while an attacker that learns it first can search for a collision offline. The code does not authenticate the relay or an already compromised dApp page.
The dApp does not authenticate a wallet identity. It pins the first eligible non-self joiner; that joiner can deny service but cannot impersonate the QR-authenticated dApp to the wallet or decrypt the wallet's frames.
Replay protection and persistence
Each direction has its own sequence. A sender reserves and persists the next value before encrypting; a receiver records it only after all parsing, AEAD, and MessagePack checks succeed. Counters cannot reset while traffic keys are reused. If state cannot be recovered safely, the channel is abandoned.
Formal verification
The ProVerif model covers an active relay attacker, including an attacker becoming the dApp's first joiner. Under idealized primitives it proves collision-free wallet binding of the five dApp fingerprint fields, injective dApp-to-wallet message correspondence including the public CAIP-2 suffix, and secrecy of wallet data sent immediately after pairing. It does not prove parser bounds, all-zero rejection, the four-digit probability bound, or persistent counter behavior.