From 19f13b80fa95fde6856c7714f9fa3e8c619921c7 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Thu, 5 May 2022 11:08:34 -0400 Subject: [PATCH] chore: add docs for encoding/decoding messages (#236) --- README.md | 40 ++-------- docs/encoding.md | 197 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 33 deletions(-) create mode 100644 docs/encoding.md diff --git a/README.md b/README.md index 606665d7..c5a9f5bb 100644 --- a/README.md +++ b/README.md @@ -52,46 +52,20 @@ make dynamic-library ## Tutorials and documentation - [Receive and send messages using Waku Relay](docs/relay.md) - [Send messages using Waku Lightpush](docs/lightpush.md) +- [Encoding and decoding Waku Messages](docs/encoding.md) - [C Bindings](library/README.md) ## Examples Examples of usage of go-waku as a library can be found in the `examples/` folder: -- [**basic2**](../examples/basic2) - demonstrates how to send and receive messages -- [**chat2**](../examples/chat2) - simple chat client using waku relay / lightpush + filter / store protocol to send/receive messages and retrieve message history -- [**filter2**](../examples/filter2) - demonstrates how to use filter protocol -- [**c-bindings**](../examples/c-bindings) - simple program to demonstrate how to consume the go-waku library via C FFI -- [**waku-csharp**](../examples/csharp) - C# console application that uses the go-waku library via FFI -- [**android-kotlin**](../examples/android-kotlin) - android app that uses a .jar generated by gomobile using kotlin +- [**basic2**](tree/master/examples/basic2) - demonstrates how to send and receive messages +- [**chat2**](tree/master/examples/chat2) - simple chat client using waku relay / lightpush + filter / store protocol to send/receive messages and retrieve message history +- [**filter2**](tree/master/examples/filter2) - demonstrates how to use filter protocol +- [**c-bindings**](tree/master/examples/c-bindings) - simple program to demonstrate how to consume the go-waku library via C FFI +- [**waku-csharp**](tree/master/examples/csharp) - C# console application that uses the go-waku library via FFI +- [**android-kotlin**](tree/master/examples/android-kotlin) - android app that uses a .jar generated by gomobile using kotlin - -## Waku Protocol Support - -- ✔: Supported -- 🚧: Implementation in progress -- ⛔: Support is not planned - -| Spec | Implementation Status | -| ---- | -------------- | -|[7/WAKU-DATA](https://rfc.vac.dev/spec/7)|✔| -|[10/WAKU2](https://rfc.vac.dev/spec/10)|🚧| -|[11/WAKU2-RELAY](https://rfc.vac.dev/spec/11)|✔| -|[12/WAKU2-FILTER](https://rfc.vac.dev/spec/12)|✔| -|[13/WAKU2-STORE](https://rfc.vac.dev/spec/13)|✔| -|[14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14)|✔| -|[15/WAKU2-BRIDGE](https://rfc.vac.dev/spec/15)|⛔| -|[16/WAKU2-RPC](https://rfc.vac.dev/spec/16)|✔| -|[17/WAKU2-RLNRELAY](https://rfc.vac.dev/spec/17)|| -|[18/WAKU2-SWAP](https://rfc.vac.dev/spec/18)|🚧| -|[21/WAKU2-FTSTORE](https://rfc.vac.dev/spec/21)|✔| -|[22/TOY-CHAT](https://rfc.vac.dev/spec/22)|✔| -|[23/TOPICS](https://rfc.vac.dev/spec/22)|✔| -|[25/LIBP2P-DNS-DISCOVERY](https://rfc.vac.dev/spec/25)|🚧| -|[26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26)|✔| -|[27/WAKU2-PEERS](https://rfc.vac.dev/spec/27)|✔| -|[29/WAKU2-CONFIG](https://rfc.vac.dev/spec/29)|🚧| - ## Contribution Thank you for considering to help out with the source code! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! diff --git a/docs/encoding.md b/docs/encoding.md new file mode 100644 index 00000000..7821bde0 --- /dev/null +++ b/docs/encoding.md @@ -0,0 +1,197 @@ +Encoding and decoding Waku Messages +=== + +The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption. The encryption comes with several handy design requirements: confidentiality, authenticity and integrity. You can find more details about Waku Message Payload Encryption in 26/WAKU-PAYLOAD. + +## What data is encrypted +With Waku Message Version 1, the entire payload is encrypted. + +Which means that the only discriminating data available in clear text is the content topic and timestamp (if present). Hence, if Alice expects to receive messages under a given content topic, she needs to try to decrypt all messages received on said content topic. + +This needs to be kept in mind for scalability and forward secrecy concerns: + +- If there is high traffic on a given content topic then all clients need to process and attempt decryption of all messages with said content topic; +- If a content topic is only used by a given (group of) user(s) then it is possible to deduce some information about said user(s) communications such as sent time and frequency of messages. + +## Key management +By using Waku Message Version 1, you will need to provide a way to your users to generate and store keys in a secure manner. Storing, backing up and recovering key is out of the scope of this guide. + +## Which encryption method should I use? +Whether you should use symmetric or asymmetric encryption depends on your use case. + +Symmetric encryption is done using a single key to encrypt and decrypt. + +Which means that if Alice knows the symmetric key `K` and uses it to encrypt a message, she can also use `K` to decrypt any message encrypted with `K`, even if she is not the sender. + +Group chats is a possible use case for symmetric encryption: All participants can use an out-of-band method to agree on a `K`. Participants can then use `K` to encrypt and decrypt messages within the group chat. Participants MUST keep `K` secret to ensure that no external party can decrypt the group chat messages. + +Asymmetric encryption is done using a key pair: the public key is used to encrypt messages, the matching private key is used to decrypt messages. + +For Alice to encrypt a message for Bob, she needs to know Bob’s Public Key `K`. Bob can then use his private key `k` to decrypt the message. As long as Bob keep his private key `k` secret, then he, and only he, can decrypt messages encrypted with `K`. + +Private 1:1 messaging is a possible use case for asymmetric encryption: When Alice sends an encrypted message for Bob, only Bob can decrypt it. + +## Symmetric Encryption + +### Encrypt a Message +To encrypt a message, assign the 32 byte symmetric key to a `KeyInfo`, and set its `Kind` to `node.Symmetric`. `node.EncodeWakuMessage` can then be used to encrypt the payload of a WakuMessage (that uses version `1`). + +```go +package main + +import ( + "fmt" + + "github.com/status-im/go-waku/waku/v2/utils" + "github.com/status-im/go-waku/waku/v2/node" + "github.com/status-im/go-waku/waku/v2/protocol/pb" +) + +func main(){ + symKey := []byte{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32} + + keyInfo := &node.KeyInfo{ + Kind: node.Symmetric, + SymKey: symKey, + // PrivKey: Set a privkey if the message requires a signature + } + + msg := &pb.WakuMessage{ + Payload: []byte("Hello World"), + Version: 1, + ContentTopic: "/test/1/test/proto", + Timestamp: utils.GetUnixEpoch(), + } + + if err := node.EncodeWakuMessage(msg, keyInfo); err != nil { + fmt.Println("Error encrypting the message payload: ", err) + } +} +``` +The `WakuMessage` payload will be encrypted, and can be later broadcasted via Relay or Lightpush. It's also possible to not have to rely on a `WakuMessage` by creating instead a `node.Payload`, setting the payload in the `Data` attribute and the key in `Key`, and then use `payload.Encode(1)` to encrypt your message, receiving a `byte[]` as a result. + +The message can also be signed with a private key (`*ecdsa.PrivKey`) by setting it into the `PrivKey` attribute of the `KeyInfo` instance. + +### Decrypting a Message +To decrypt a message, regardless of the protocol on which it was received, assign the 32 byte symmetric key to a `KeyInfo`, and set its `Kind` to `node.Symmetric`. + +```go +package main + +import ( + "fmt" + + "github.com/status-im/go-waku/waku/v2/utils" + "github.com/status-im/go-waku/waku/v2/node" + "github.com/status-im/go-waku/waku/v2/protocol/pb" +) + +func main(){ + symKey := []byte{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32} + + keyInfo := &node.KeyInfo{ + Kind: node.Symmetric, + SymKey: symKey, + } + + // This message could have arrived via any of these protocols: relay, filter, store + msg := &pb.WakuMessage{ + Payload: ..., // Some encrypted payload + Version: 1, + ... + } + + if err := node.DecodeWakuMessage(msg, keyInfo); err != nil { + fmt.Println("Error decrypting the message payload: ", err) + } + + fmt.Println(msg.Payload) +} +``` + +The `WakuMessage` payload will be decrypted and available in plain text. Messages that can't be decrypted will return an `error`. + +> To have access to the message author's public key and signature (if available) of the message, `DecodePayload(msg, keyInfo)` can be used instead. It will return a `DecodedPayload` instance containing `PubKey` and `Signature` attributes, and it's not a destructive operation, so the `WakuMessage` protobuffer will not be modified. + + + +## Asymmetric Encryption + +### Encrypt a Message +To encrypt a message, assign an `*ecdsa.PublicKey` to a `KeyInfo`, and set its `Kind` to `node.Asymmetric`. `node.EncodeWakuMessage` can then be used to encrypt the payload of a WakuMessage (that uses version `1`). + +```go +package main + +import ( + "fmt" + + "github.com/status-im/go-waku/waku/v2/utils" + "github.com/status-im/go-waku/waku/v2/node" + "github.com/status-im/go-waku/waku/v2/protocol/pb" + "github.com/ethereum/go-ethereum/crypto" +) + +func main(){ + + + keyInfo := &node.KeyInfo{ + Kind: node.Asymmetric, + PubKey: ..., // The public key to encrypt the messages with + // PrivKey: Set a privkey if the message requires a signature + } + + msg := &pb.WakuMessage{ + Payload: []byte("Hello World"), + Version: 1, + ContentTopic: "/test/1/test/proto", + Timestamp: utils.GetUnixEpoch(), + } + + if err := node.EncodeWakuMessage(msg, keyInfo); err != nil { + fmt.Println("Error encrypting the message payload: ", err) + } +} +``` +The `WakuMessage` payload will be encrypted, and can be later broadcasted via Relay or Lightpush. It's also possible to not have to rely on a `WakuMessage` by creating instead a `node.Payload`, setting the payload in the `Data` attribute and the key in `Key`, and then use `payload.Encode(1)` to encrypt your message, receiving a `byte[]` as a result. + +The message can also be signed with a private key (`*ecdsa.PrivKey`) by setting it into the `PrivKey` attribute of the `KeyInfo` instance. + +### Decrypting a Message +To decrypt a message that was encrypted with your public key, regardless of the protocol on which it was received, assign the `*ecdsa.PrivateKey` to `KeyInfo`, and set its `Kind` to `node.Asymmetric`. + +```go +package main + +import ( + "fmt" + + "github.com/status-im/go-waku/waku/v2/utils" + "github.com/status-im/go-waku/waku/v2/node" + "github.com/status-im/go-waku/waku/v2/protocol/pb" +) + +func main(){ + keyInfo := &node.KeyInfo{ + Kind: node.Symmetric, + PrivKey: ..., // Your private key + } + + // This message could have arrived via any of these protocols: relay, filter, store + msg := &pb.WakuMessage{ + Payload: ..., // Some encrypted payload + Version: 1, + ... + } + + if err := node.DecodeWakuMessage(msg, keyInfo); err != nil { + fmt.Println("Error decrypting the message payload: ", err) + } + + fmt.Println(msg.Payload) +} +``` + +The `WakuMessage` payload will be decrypted and available in plain text. Messages that can't be decrypted will return an `error`. + +> To have access to the message author's public key and signature (if available) of the message, `DecodePayload(msg, keyInfo)` can be used instead. It will return a `DecodedPayload` instance containing `PubKey` and `Signature` attributes, and it's not a destructive operation, so the `WakuMessage` protobuffer will not be modified.