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

LayerConstruction
Key exchangeFresh X25519 key pair per channel; all-zero shared secret is rejected.
Key derivationHKDF-SHA256 with channel ID salt, transcript hash, and directional labels.
FingerprintSHA-256 of the dApp pairing fields, reduced modulo 10,000.
PayloadJSON-only MessagePack, bounded to 64 KiB and 64 nesting levels.
AEADChaCha20-Poly1305 with a 12-byte nonce of eight zero bytes plus the uint32 sequence.
Additional dataProtocol 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.