What is Zbay
Tue, Dec 29, 2020 •10 min read
Category: Business Stories / Blockchain
Zbay is an attempt to build a secure, decentralized substitute for team chat tools like Slack, Discord, or Keybase.
The goal is to achieve a similar level of usability and reliability to centralized tools, while reducing or outright eliminating risks from phishing attacks, malware attacks, and large-scale data breaches.
Zbay also integrates the privacy-focused cryptocurrency Zcash. Users can send and receive Zcash, register names for users and communities on the Zcash blockchain, and—potentially, in the future—purchase additional services such as persistent encrypted storage.
How does Zbay do it?
The basic starting point
At the moment, Zbay relies on shielded transactions on the Zcash blockchain as a transport. Each message sent between the users translates to a spend of UTXO on the blockchain level. We are using the encrypted memo field of the spend output to convey the message body. Zcash shielded transactions are only readable for someone who has a viewing key for the target addresses of the spend. This principle defines how Zbay translates Zcash primitives into interactions between Zbay users.
All of these details are changing rapidly as we move to using libp2p over Tor for message transport.
Direct messages
The simplest form of interaction is sending a message to someone whose address you know. This means sending a 0-valued transaction with a message encoded into the note field. Only the recipient of the message has a viewing key to this address, which makes it private, similar to sending a PGP-encrypted email to someone who’s key you know—except that in this case the recipient’s address and public key are the same thing. It’s also important to note that, due to Zcash’s design, a 3rd party observing blocks cannot even tell that the interaction between the two addresses took place. This is unlike more popular blockchain networks like Ethereum or Bitcoin where anyone using even a web-based block explorer can trace the complete history of interactions between parties. Privacy at the blockchain layer is a main feature of Zcash that enables ideas like Zbay in its current form.
Private channels
Private channels are slightly more complex than direct messages. Private channels are used for interactions between 3 or more Zbay users. To form a private channel, one of the users creates a fresh Zcash address. They then share the viewing key to this address with the people they invite. Invited users add the viewing key to their wallet, which enables them to decrypt (receive) messages.
Channel invitation builds on top of the “Direct message” functionality, as it’s used to send the viewing key to the invitee.
Public channels
Public channels are open broadcast communication. Everyone can join and read the channels. From an implementation perspective, public and private channels differ very little. Public channels are private channels that everyone knows the viewing key to. The viewing keys to public channels are published to a meta-channel that we call the “Channel of Channels”, which every Zbay instance tracks.
It’s up to the user to decide to join the public channel. When they join it, the viewing key is added to their wallet.
User identity
Each Zbay user creates a username during the first run of the application. Each Zbay client makes sure that the usernames are unique, so that users cannot impersonate one another.
During the first run, Zbay generates a keypair and stores it. The private key of this pair is used to sign all messages sent by that user. We are using a signing scheme that allows for the extraction of public keys from the signature and signed data. This not only prevents message spoofing but also provides another layer of protection against messages being tampered with in-transit.
During the first run Zbay also posts the public key and username of the user to the meta-channel we call “Users Channel”. All Zbay instances track this channel. Thanks to the fact that blockchain ensures everyone sees the same order of transaction, we define the ownership of the username as “It’s owned by the public key that claimed it first”.
Technical stack
Zbay uses the Electron framework. This implies NodeJS for the OS-side of it. For the frontend-side we are using a combination of React + Redux + Material UI. We have recently made a push to move our codebase from Javascript to Typescript.
Zbay supports Linux, macOS and Windows. It is cross-compiled to each of these platforms using Github Actions.
Zbay is open source. You can find the code repository here.
History of the project
The full node phase
The development work on the project initiated in February 2019. This was just a few months after the Sapling Zcash update which changed the protocol in a way that made Zbay feasible. The first steps in the project were very crude. For example, at the time, the Zcash node didn’t implement importing the viewing keys to the wallet. We had to fork the Zcash node repository and roll in the support for this ourselves. For the first few months of the project, we had to maintain the fork and track each new release of Zcash and merge all the upstream changes to our fork. That was a lot of work, but it was worth it to have a working prototype. Eventually, the Zcash project added the features from our fork, and after that we no longer had to touch the codebase of the Zcash node.
The architecture of the first implementation assumed that each Zbay user had a local Zcash node. The Zcash node was started and monitored by the Zbay process. From a theoretical point of view, this is the right way to interact with a blockchain.
This approach worked, but had some serious practical implications. Most notably it takes a lot of time, disk space and CPU usage to get the new user fully synced with blockchain. We’ve put in a lot of work to optimize it. Working in increments we were able to shave off the time needed to onboard new users from a few days to a few hours (it’s hard to give exact figures as those were determined by network, disk and CPU speed). But really there was nothing we could do about requiring over 10 GB of disk space and high CPU usage.
In this era, we did a lot of work around optimization, monitoring external processes, making backups, etc.
The light client phase (current)
In early 2020 we’ve implemented a version that we call Zbay light, which at the moment of writing is the current production version. In this era, we are no longer working with a local Zcash node. Instead, we are using tools from the Zecwallet Lite project. This was a major change and has both good and bad implications:
Disadvantage: we are relying on an external piece of cloud infrastructure (Lightwallet servers) that is operated by a trusted party. This also has implications for security and privacy.
Disadvantage: we’ve lost the ability to receive 0-confirmation messages and display messages before they are included in the block by a miner.
Benefit: it takes just minutes for a new user to be fully synced and operational.
Benefit: it requires little disk space as we no longer need to download the entire history of the blockchain.
The Lightwallet protocol maintains privacy by fetching data about new blocks in pure (encrypted) form and only decrypting it locally, on the same computer Zbay is running on.
However, for efficiency purposes it strips out memo data, which Zbay relies on. Since different users request different memos, this opens up a surface for some deanonymization attack, if someone could coerce the operators of the Lightwallet servers to cooperate.
Cryptographic secrets are not exposed in any way, they are stored in a wallet and the wallet is in charge of all the decrypting.
From an implementation point of view, the switch to “Zbay light” meant a lot of adaptation work. To give some examples, Lightwallet-cli is written in Rust, while Zbay is written in NodeJS. We needed to learn how to efficiently use the code interoperably. Sure, we could have just spawned the subprocess every time we need to run Rust code, but this would result in a whole lot of process forking and would be very inefficient. Therefore, we import Rust components using node-gyp bindings and call Rust functions directly from NodeJS.
Present and future of the project
Challenges ahead
We are very happy with what we’ve accomplished so far, yet we know there is still a long way to go to reach the goal. To address the elephant in the room, as long as Zbay relies on Zcash for message transport it will be as fast as blockchain. And “as fast as blockchain” doesn’t mean “very fast”. As things currently are, the block time on Zcash is 75 seconds. This is nowhere close to the UX of other instant messaging applications. Storing messages on-chain also means they cannot be deleted or forgotten, which presents a serious privacy and security problem if keys are compromised.
“Websocket over Tor” phase
We have recently released a version that offers instant and efficient direct messaging when both parties are online.
Zbay launches a local tor client, which configures it to expose a v3 onion service. This service allows each Zbay to open a private websocket server that other Zbay instances can connect to using its onion address. The connection is direct in the sense of a Websocket layer. But since it goes over Tor, it means that in reality there are 6 tor nodes in between that form a Tor circuit, effectively anonymizing the connection on TCP protocol layer and offering metadata protection against a wide range of attackers.
Thanks to this approach, Direct Messages are now truly instant.
Libp2p over Websocket over Tor
Direct websocket connections are awesome for direct messages but how do we use it for private and public channels? How does it scale if there are hundreds or thousands of users? Shall we create a well-connected network of Websocket connections, effectively opening o(n2) connections? This would not work so well.
Our current development work revolves around using the libp2p library to create a mesh of Websocket-over-Tor connections. So instead of creating the connection directly, we rely on Libp2p library to do it for us. Its DHT implementation is responsible for creating the topology of the Mesh. Thanks to gossip and pubsub protocols (libp2p’s GossipSub), we don’t require everyone to be connected to everyone else. At the moment of writing this article, we are at the stage of load testing the communication logic using hundreds of machines running on AWS and the results look very promising, pointing us to what we should address next.
Persistent channel history
Moving the message part of communication out of Zcash has one more important consequence. When we were using a blockchain to deliver messages, the blockchain was providing us with the immutable record of history of each channel or direct message thread.
With the move to communication over pubsub, we are losing the history, as in pubsub you only receive a message if you’re currently subscribed. Therefore, at the same time we are developing a solution to keep the state of the channel between channel members. Remember that by design we have no central servers that could keep the data while everyone goes offline, so we need a robust system that can keep track of history and reconcile the network partitions which happen when members of the channels go online and offline while others keep on chatting.
For solving our history problem, we are looking into adopting good old git plus some extra cryptography. We are exposing read-only git repositories on Tor’s v3 onion services, so that users can sync their history using git pull. That’s of course just a gist of it, it gets quite a bit more nuanced in details. We considered designing our own protocol, perhaps borrowing from concepts in the peer-to-peer social networking protocol SecureScuttlebutt. But the problem seemed to overlap in significant ways with the use case and functionality offered by git, and git is fast and well-understood, so we decided to start there. The result has been promising!
More private Private Channels
It is important to highlight that with push to Tor and Libp2p we are not breaking ties with Zcash. We are keeping the cryptography model used on blockchain, so for example Private Channels will continue to be encrypted in transit using the same key as they used to be as the note field in Zcash transaction output.
However, for Private Channels we were planning some redesign. The current cryptographic setup has some limitations that we want to shake off:
There is only one shared viewing key used for the channel.
Once someone learns there is no way to make him ‘unlearn it’. So, there is no way to revoke access to a Private channel shall this be required. It's an important missing feature, yet not one that is easy to roll in in the current design.
We would like to offer forward and backward secrecy for Private Channels. If an attacker who is intercepting a team’s encrypted network traffic succeeds at obtaining the private key to a channel, for example through a malware attack or the theft of a device, they should not be able to decrypt old messages that were intercepted in transit, but deleted from the device. Similarly, they lose the ability to decrypt messages immediately or at least soon after the device compromise.
Eventually we also want to protect private channels from leaking metadata to users who are not part of the team. For this, it is not enough to encrypt messages. Ideally, we have to move each private channel to its own mesh network (connecting over Tor, but only to other team members) and we need a secure way to permit new peers to join these networks.
Final thoughts
Zbay is an exciting project. It’s pushing the limits of what is possible using open-source technologies and decentralization. We’ve been working on it for a little over year and half and have made some amazing progress. We have a clear vision of what we want to accomplish and how to do it.
We are also scaling up the team and are looking for developers passionate about Tor, cryptography, and decentralization to lend us a hand.