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.

3 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: