zecwallet
zecwallet copied to clipboard
Privacy leak: wormhole service links transactions to user's PC and mobile IP address
Following up on a discussion in the Zecwallet UX - Replace Zecwallet UI with a React-based one grant proposal:
The connection between the Browser and Mobile App is done over the wormhole protocol. This is already built, and is currently how the App connects to Zecwallet. The connection uses TLS and the data inside is further encrypted end-to-end (with libsodium's symmetric encryption), so the wormhole service can't see the data either (But can see metadata like the IP address).
(where the wormhole protocol implementation is here.)
I'm worried about the privacy implications of the metadata and traffic patterns, because the centralized wormhole server can see the message timing, size and IP address of all PC-to-mobile communication.
For example, I guess that it's very easy to identify "transaction send" events just by the message sizes, in both the current architecture (where the mobile app is a UI frontend) and in the envisioned "web app" architecture (where the mobile app is a key store). The actual transactions are readily observable (on the peer-to-peer network, and at the lightwalletd service), and linkable by the timing. Hence:
The operator of the "wormhole" service, or anyone monitoring it, gets to know the IP address of both the PC and mobile phone of every sender of every transaction that uses this system.
That's bad.
A couple of ideas for improvements:
- Establish a direct TCP connection between the PC and mobile, when possible. For example, consider how Magic Wormhole falls back to its Transit Relay only if necessary:
The file-transfer commands also use a “Transit Relay”, which is another simple server that glues together two inbound TCP connections and transfers data on each to the other. The wormhole send file mode shares the IP addresses of each client with the other (inside the encrypted message), and both clients first attempt to connect directly. If this fails, they fall back to using the transit relay.
- Use Tor or I2P for the PC-to-mobile link
-
The companion app protocol already attempts a direct connection first. It falls back to the wormhole connection if a direct connection cannot be made (The fallback explicitly opt-in). Details
-
The PC and mobile both pad messages exchanged in either direction, so all messages are exactly the same size. The wormhole server can't deduce sends (or any other type of message)
-
Can you explain more precisely how the direct connection mode works? Is it attempted just when you're on the same subnet, or will it also work if at least one side accepts incoming connection (no NAT or firewall block, or better yet, using means to traverse these such as STUN)?
-
Great news about the padding. How large is it (transactions may get large)? Where is it documented?
@tromer Direct Connection mode will work if the IP address + port on the LAN is accessible, otherwise it will not. It's somewhat "dumb" and does not try to do anything fancy. By default, if your device and zecwallet are on a different LAN, it will fail. The wormhole service must be enabled by the user.
A better design to solve issues mentioned by @tromer would be to decentralize the wormhole service, and have many of them to choose from. This could be via round robin DNS and also randomly choosing a server on startup (similar to Electrum) and also giving the option to use a custom wormhole server. This breaks the invariant that any one wormhole server would have all or most of the traffic. We have looked into doing this in Hush but haven't gotten around to it: https://github.com/MyHush/SilentDragonWormhole/issues/2
@tromer I was also happy to hear about the padding (as I maintain a fork of zecwallet) and wanted to know for myself the answer to your padding question. The code is here:
https://github.com/ZcashFoundation/zecwallet/blob/master/src/websockets.cpp#L404
It rounds all message sizes up to the next multiple of 256 bytes. Since many messages are small, this works well for those, but I believe some messages which are very large, such as "send a zxtn to these 20 zaddrs" would create a very large message which leaks some metadata. It's possible that 256 efficiently uses bandwidth but is not optimal for metadata leakage.
@tromer @adityapk00 I did a simple metadata leakage analysis on this padding issue and confirmed that it does leak metadata about message types. Specifically, the message size (even after padding) of a z_sendmany
is unique, and can be told apart from a getBalance
request/response and from the much smaller getInfo
request/responses.
The core issue is that the message padding of 256 was a good idea, but the value of 256 is too small to effectively make message types indistinguishable. TLDR: The solution is to use padding of 16KB, implemented here in Hush SilentDragon:
https://github.com/MyHush/SilentDragon/commit/6f230c90af76d6c1c3ec8064ae03d13d42d600c1
I observed at least 4 different message type sizes, even after padding, within a few minutes of operation. These 4 buckets uniquely or almost-uniquely identify message types. A padding of 512 would increase privacy but would still give metadata, since some message types are very small (under 512 bytes) while z_sendmany/getBalance responses are just over 11KB. Using any padding amount less than 12KB will still leak metadata. I chose to pad to 16KB to take into account future changes to messages and as a buffer since I did not calculate the absolute largest a message could be. If Zecwallet/SilentDragon supported sending to many zaddrs at once in the future, those messages could be larger than 16KB and then leak metadata. So the best padding will always depend on the codebase and the largest message sizes.
@tromer @adityapk00 ignoring this bug for 1.5 years is a dis-service to the privacy of Zcash users, and indeed, is one of the many reasons why I cannot recommend anybody use ZEC mainnet, since simple-to-fix and important privacy issues go unfixed for egregiously long periods of time.