Your Secrets are Safe with Me
Building a secret messaging application on Oasis
Secret messaging as a service
In order to highlight some of the new features of the Oasis Dashboard — the ability to deploy and track usage of confidential smart contracts — this blog post outlines how developers like you can build and deploy a simple confidential messaging application on our dashboard.
Messaging apps are ubiquitous with consumer and business applications alike. Messaging apps make up 7 of the top 20 apps on the Apple App Store and 6 of the top 20 apps in Google Play; messaging is integrated in business applications like Slack, e-commerce platforms like Intercom, and more. Yet what most of these messaging apps fail to ensure is a promise of privacy and ownership for every user of the application.
Enter Oasis, which seeks to combine the decentralization of blockchain and the privacy and confidentiality guarantees of secure computing technology. Combining the two gives users an opportunity to build and use messaging applications where control of data in the app remains in the hands of the user and messages are by design private — even from the app developer or Oasis.
Why messaging as a service?
Similar to software as a service, messaging as a service provides a clean separation of concerns. We want to give developers the ability to use this service either for standalone messaging applications or for integrating it within richer applications, such as games, to provide bulletin boards or facilitate in-app communication for all participants. Since the contract supporting this service is open-source, developers can clone and enhance the contract in ways that appeal to their customers.
Why secret messaging?
Consider requirements from face-to-face communication. As a user, there are some messages that I simply don’t want in the hands of anyone other than my intended recipients. That includes simple messages to family members, to our communication with service providers like doctors, auditors, investment advisors, due-diligence partners, or even friends in an online game. A broadcast message is like pasting a yellow sticky note on a public bulletin board — anyone who wants to, can find it and access it. A directed message, on the other hand, is private communication that no one, including the provider of the service should have access to. This is a natural segue into the next question, Why Oasis?
Why Oasis?
Oasis is a privacy-first cloud computing platform that provides not only integrity, as other blockchain platforms do, but also confidentiality in compute and storage, and privacy in the application layer. A contract running on Oasis is akin to a group of functions, with state, like traditional serverless cloud technologies, but with integrity, confidentiality, and privacy bolted in. The confidentiality starts with signed transactions, and journeys through hardware-based secure enclaves, to encrypted, auditable storage on the blockchain.
The confidentiality and secure computing technologies are requisite to building a truly secret messaging service — ensuring private messages are always encrypted, both in transit and in storage, and are never decrypted outside of secure enclaves giving us an unprecedented level of secrecy.
How to build a secret messaging contract
To build the secret messaging smart contract, I’ve used Rust. For a tutorial on how to build Rust smart contracts on Oasis check out Developing Rust Smart Contracts. Here are high-level requirements for the contract:
Identify users
- Support wallet address based identification
A user can broadcast a message up to a fixed maximum length
- All users get to see broadcast messages
- Support retrieving the last k broadcast messages
A user can send a message up to a fixed maximum length to another user
- They are considered friends after the first message is sent
- The message sent is seen only by the intended recipient as part of her interaction with the sender and in sequence
- The sender sees all of his exchanges with the recipient in sequence
- Support retrieving the last k messages exchanged
Support searching over friends
- A user can see all of her friends
I will walk through some of the aspects of supporting this functionality that provides a glimpse of service internals. The method send is used to send a directed message from one user to another. Let’s look at how we maintain the friends list in the following code fragment, redacted to focus on relevant bits. You can access the full secret messaging repository on github.
/// Function to send a message to a specific recipient
fn send(&mut self, to: Address, message: String) -> bool {
let sender = sender();
let owner = Address::from(H256::from(read(&CREATOR_KEY.into()))); ... // they are now friends
update_friend(&owner, &sender, &to);
update_friend(&owner, &to, &sender); true
}
The method update_friend is reproduced here in full,
/// Update the friends map
/// For a given person, updates the friends list with a friend
/// Note: For efficiency we don’t store and retrieve a hash map
/// but instead store a hashset of friends for each person
fn update_friend(owner: &Address, me: &Address, friend: &Address) {
let key = generate_friends_map_key(owner, me);
let bytes = get_bytes(&key).unwrap();
let mut friends_map = FriendsMap::default();
if !bytes.is_empty() {
friends_map = from_value(helpers::from_bytes(bytes)).unwrap();
}
friends_map.friends.insert(hex::encode(friend));
let json_bytes =
helpers::to_bytes(to_value(friends_map).unwrap());
set_bytes(&key, &json_bytes).unwrap();
}
Storage requires a key to store a piece of data that becomes a part of contract state. We use the Rust tiny_keccak crate for key generation. Friends of a user are stored as a hashset. We retrieve the current set of friends, insert the new one, convert the updated set into a byte stream and stash it in storage as you can see in the above implementation. The state of the contract is stored for the life of the contract. By creating an instance of the contract without expiry, everything stored is always in storage. An enhancement to this contract will be automatic message expiry amongst other things. We invite you to enhance this contract as you like and would love to hear of interesting extensions.
Once you have the contract sources you can test it using the instructions for using contract-kit. Once you bring up contract-kit, you can use truffle to compile and test the non-confidential transactions in the test. For confidential transactions please set MNEMONIC in truffle-config.js to the mnemonic for your wallet on devnet and run the following on our devnet,
truffle test --network oasis test/test-message-board.js
You should see this snippet in your console,
✓ oasis: deploys a message board contract (5384ms)
✓ oasis: char limit should be 280 (2045ms)
✓ oasis: simple message broadcast (5445ms)
✓ oasis: simple message get (2352ms)
✓ oasis: multiple message broadcast (21735ms)
✓ oasis: get message by index (12883ms)
✓ oasis: request more messages than there are (2478ms)
✓ oasis: simple p2p send (15019ms)
✓ oasis: simple p2p message get (2297ms)
Friends of 0xfF8606e0f08EA93803aeEC76b63EF47Ef76dAe27: {"friends":["049c7e346c15eec2d95f13901a2e2a07d54b4354","7defed5db6acfb9deeaf0ba9a92ef99c1650f7f3"]}
Friends of 0x049c7e346c15eeC2D95f13901A2e2a07d54b4354: {"friends":["ff8606e0f08ea93803aeec76b63ef47ef76dae27"]}
✓ oasis: get friends test (5141ms)
[ '049c7e346c15eec2d95f13901a2e2a07d54b4354',
'7defed5db6acfb9deeaf0ba9a92ef99c1650f7f3' ]
✓ oasis: get friends as a space separated string test (2576ms)
The message is longer than the 280 character limit. Please shorten and re-send.
✓ oasis: broadcast over 280 characters (5356ms)
✓ oasis: multiple p2p message send (21529ms)
✓ oasis: p2p get message by index (12910ms)
The contract is available for use on our dashboard. In the next section we will describe how you can instantiate the contract using our dashboard and use it in your application.
Using secret messaging from our dashboard
To use secret messaging as a service, you should use the Oasis Dashboard. (You can learn more about the Dashboard and how to get set up in this simple tutorial).To get started, log into the Oasis Dashboard.
To ensure a more seamless developer experience on the Oasis network, the Dashboard is designed to provide auto-funding and soft wallets. This helps avoid having to setup MetaMask or constantly update your wallet with Dev tokens any time you make a transaction (a challenging pain point of many blockchain protocols). When you log in to the Oasis Dashboard you’ll automatically be funded with up to to 1DEV per day with an automatic top-off every 24 hours. This way, you don’t have to install or use MetaMask. You get a soft-wallet when you sign in for the first time and then funding to deploy and run contracts happens automatically under the hood. This is among the steps we are taking to make it a seamless developer experience to build contracts on the Oasis network.
Once you log in, select a new project and then pick the MessageBoard contract from the list of pre-created deployable contracts. This one is what we described in the earlier section. Once you pick the MessageBoard for deployment, you will be presented with a card for deployment parameters. Fill in the instantiation arguments for the contract as shown in Figure 1, and hit Deploy. You should see your deployment in a new card with the contract address as shown in Figure 2. You are now good to use the contract address, by copying it from the card by clicking on it, in your application.
Here is a snippet of code in javascript that you can use to create an instance of the deployed contract using the contract address,
let instance = new web3c.oasis.Contract(MessageBoard.abi,
‘<contract-address-from-dashboard>’, {
from: <my-wallet-address>
});
You can now use instance for transactions such as,
var str = “Did you read my monograph on Tapanuli Fever?”;
var receipt = await instance.methods.send(<alice-wallet-address>,
str).send();
assert.equal(receipt.status, true);
and,
const message = await instance.methods.get_message_by_index(<my-wallet-address>, <alice-wallet-address>, 0).call();
assert.strictEqual(message, ‘“Did you read my monograph on Tapanuli Fever?”’);
You can test the dashboard deployment using test/test-dashboard.js, in the oasis-messaging git repository, after setting MNEMONIC in truffle-config.js to the mnemonic for your wallet on devnet and running the following,
truffle test --network oasis test/test-dashboard.js
You should see,
Contract: MessageBoard
Contract address: <your-contract-address-from-the-dashboard-deployment>
✓ oasis: deploys a message board contract
✓ oasis: multiple p2p message send (20379ms)
✓ oasis: p2p get message by index (10283ms)
3 passing (31s)
Conclusion
We hope you enjoyed this simple tutorial. This is just one example of the type of functionality you can create and deploy on the Oasis network. We’re excited to see what you build next! Share your latest contracts with us at feedback@oasislabs.com.
Resources
Acknowledgments: Thanks to contributions from Nick, Armani, David, Will, Naheed, Anne, and Andrew.