Holochain Forum

Asynchronous Private Messaging

Problem

Agents want to communicate privately with each other even when one party is offline. But node-to-node messaging requires both the sender and recipient to be online.

Solution

Encrypt private messages using the recipient’s public key and publish them to the DHT.

Implementation

Holochain has built-in encryption and decryption functions. Use these to protect public messages when you publish them to the DHT.

Warnings

  • No encryption scheme is safe forever, even currently recommended ones. Consider your DHT’s expected lifetime, actors with quantum computers, and how that could affect the lives of your users.
  • Every entry shows who authored it, leaving a trail of metadata that lets third parties build a profile of social connections.

Related patterns

This pattern can be used with the Mailbox pattern to create something almost as secure as private node-to-node messaging, albeit with the above warnings.

4 Likes

I’m actually trying to implement this but I’m having trouble encrypting the payload with the recipient’s public key. It seems that Holochain only offers encryption with my own private key. Should I see other libraries or am I missing something?

I haven’t checked recently, but since @ddd-mtl recently mentioned this in his post about SnapMail, with requests for Holochain features too, it seems like a good bet that this feature is still unimplemented

3 Likes

Thanks @Connoropolous, I’ll check it out! :slight_smile:

@LuchoTurtle sorry for letting this conversation languish. It does indeed look like hdk::encrypt decrypts a message for the agent’s own use only. (And I suspect there’s a typo; should probably say “agent’s public key”, because otherwise anyone would be able to decrypt it :sweat_smile:) I think this function is probably in there for low-level stuff like the DeepKey hApp. I’m hoping for a more robust set of encryption/decryption functions in a future HDK – functions that let you encrypt to someone else’s public key.

@pauldaoust It’s okay Paul, thanks for responding!

Yeah, the name of the function is a wee bit confusing and doesn’t really serve any high-level purpose, as you said hehe. I’ve circumvented this problem by generating a public/private key pair for each agent and posting the public one to be retrieved by whoever wants to send the message to that agent.

It was a bit of a pain since most crypto packages in Rust have trouble compiling to WASM so yeah :sweat_smile:

Hi @LuchoTurtle!
Would it be possible for yout to share the crypto packages you used for your project?
We also want to implement this asynchronous messaging feature in our app and would love to know about your experience in already working on this concept! :smiley:

Hey there @tats_sato!

I had a few problems compiling the rust-crypto package to WASM so I resorted to rust-crypto-wasm for that. Two weeks ago I made a pull request to fix a compilation error on Mac devices so if I’d recommend ya to use that PR link in your cargo.toml file :slight_smile:.

With that sorted, you should achieve a decent public/private key cryptography by using this piece of code. It uses rust-crypto but since rust-crypto-wasm is a fork, it’s basically the same thing.

However, this snippet of code won’t get you far because it uses the rand crate in Rust and I couldn’t manage to compile it to WASM as much as I tried to. Therefore, you might want to remove the instances that use the rand package. I’ve used snippets of the user’s own address to create the public and secret key since the user’s address is unique but you can try to find your own ways to create randomness (as long as you manage to compile to WASM).

After that, you’re pretty much sorted!

I’ve had users to create a public key and a secret key on the init function and everytime I want to encrypt something, I use the code I sent you to encrypt it with the recipient’s public key and the recipient user can decrypt it with their own secret key! :smiley:

An important note though: Those encrypt and decrypt functions I linked you work with an array of bytes. When you encrypt a String, for example, the result will be an array of bytes. I used the base64::encode function from the base64 crate to encode this byte array so the recipient could easily decode it without running into deserialization problems with array of bytes.

I hope this helps, mate! :smiley:

Is there any possible way to mitigate just that?

Imagine an Instagram on Holo that says “You can’t view the followers of a private account; however, you can visit HoloScan (the equivalent of EtherScan for Holo) to bypass that”!

One possible solution might be to encrypt the whole DHT with a ‘secret’ hard-written in the DNA code of the application. Then with some cross-publishing, we might be able to achieve an almost undecipherable DHT. Cross-publishing is just a fancy way of saying “If you want to post/commit something to the DHT, don’t do it yourself. Ask some random online node/peer to do it for you. And in turn post entries that other random nodes ask you to commit with your signatures. All communication happening via node-to-node messaging.”
An edge case for cross-publishing might be when only you are online! However, even with all these apparatus in place, the app developers who had access to the ‘secret’ can still decipher the DHT! That’s like saying “Nobody can view the followers of your private account (DISCLAIMER: except Mark Zuckerberg of course)!!!”

@artbrock once posted this on Twitter:

I was wondering if there’s any way to achieve the same for Holo applications as well.

And how exactly does an app do that? The zome names should still be viewable. So are the public function names, and entry types and links too I guess… It would still be obvious as to which app’s DNA it is.

@The-A-Man your concerns echo some of my own. I think Holochain’s primary use case is to help groups make sense of each other so they can make wise decisions – essentially a digital augmentation of the social reputation metadata that we couldn’t maintain once groups grew past 150 people. In other words, it’s supposed to help people in big groups make sense of each other. This requires some sort of persistent identity.

Privacy and anonymity are two separate concerns. While Holochain does have a lot of great privacy features built in (not every piece of data has to be public, separate apps have their own private encrypted networks that can’t be discovered by other apps), it isn’t well optimised for anonymity. Given a large enough pool of people who wish to remain anonymous, your ‘P2P tumbler’ approach might work quite well.

Another approach might be throwaway keys, but then you’d have to create some way of discouraging Sybils, because unlike blockchain, every keypair belongs to a validator and Holochain doesn’t have any built-in economic incentives to keep validators honest.

I think Eric and Art have other ideas for anonymity up their sleeves, but I don’ tknow what they are.

Re:

No way to even know about what apps are running unless app announces itself

And how exactly does an app do that?

To clarify, one app’s network doesn’t know about any other app’s network. Their traffic goes past each other on the internet, encrypted, so there’s no way for third parties to detect the existence of a network. Unless it announces itself in a public place (e.g., a hApp store) nobody knows about an app except its participants. BUT – on one participant’s device, their running instances of different apps can know about each other if the participant has set up bridges between them.

Hope that helps; let me know if you have any more questions!

2 Likes

Wouldn’t it be great if Holochain had two separate functions for committing an entry: “commit” and “commit_and_sign”, where the first one (and its variants: commit_and_link, …) would simply commit an entry to the DHT anonymously. For use-cases where you wanna prove that it was you who said/did something, you use commit_and_sign. Anonymity by design (by default)! It would really widen the scope of apps that can be built with Holo(chain).

As far as I know, I don’t think the signature enforcement serves any protocol level function. Correct me if I’m wrong though…

I have heard the possibility of turning that feature on and off for certain apps – what it would look like is a setting in the DNA called “header publishing”, and it’d be either on or off. Without header publishing there are no signatures. If you had an app that needed some sort of signing but you didn’t want provenance trails, you could include signatures in the entries themselves using the API’s sign function.

The signed journals (source chains) are not necessary for the gossip protocol, but signatures are. That is, the DHT’s address space uses public keys to identify nodes, and DHT messages need to be signed by the node that broadcasts them. Unsigned messages, or messages with an invalid signature, are rejected by the recipient.

And at the Holochain level, which is one level higher than the gossip level, that’s where signed source chains are integral to the protocol. That’s related to the whole ‘network of agents building up understanding of each other’s reputation’ design principle.

2 Likes

@pauldaoust ur cool af dude :+1:

3 Likes

It’s a fact! PERIOD!

ha ha, thanks folks :hugs:

anyone talking about decentralized oracles yet?