From 880746a0df4ae2f6465cbc7a23090a2bee142986 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 26 Aug 2021 14:36:37 +1000 Subject: [PATCH 1/7] Add guide to encrypt messages using version 1 --- CHANGELOG.md | 1 + guides/encrypt-messages-version-1.md | 265 +++++++++++++++++++++++++++ guides/menu.md | 1 + 3 files changed, 267 insertions(+) create mode 100644 guides/encrypt-messages-version-1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d6a5ab4f29..d87a9b9682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Examples (eth-pm): Encrypt Public Key Messages using symmetric encryption. +- Guides: Encrypt messages using Waku Message Version 1. ### Changed - **Breaking**: Moved `startTime` and `endTime` for history queries to a `timeFilter` property as both or neither must be passed; passing only one parameter is not supported. diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md new file mode 100644 index 0000000000..8c7863fb81 --- /dev/null +++ b/guides/encrypt-messages-version-1.md @@ -0,0 +1,265 @@ +# Encrypt Messages Using Waku Message Version 1 + +The Waku Message format provides an easy way to encrypt messages using symmetric or asymmetric encryption. +The encryption comes with several handy [design requirements](https://rfc.vac.dev/spec/26/#design-requirements): +confidentiality, authenticity and integrity. + +You can find more details about Waku Message Payload Encryption in [26/WAKU-PAYLOAD](https://rfc.vac.dev/spec/26/). + +## What data is encrypted + +With waku Message version 1, the 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 decrypting of 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 said traffic; +- If a content topic is only used by a give (group of) users then it is possible to deduce some information about their communication such time and frequency of messages. + +## Key management + +By using Waku Message Version 1, you will need to provide a way for your user 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 do I need? + +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 `K` and use 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 determine `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 messaging is a possible use case for aasymmetric encryption: +When Alice sends an encrypted message for Bob, only Bob can decrypt it. + +## Symmetric Encryption + +### Generate Key + +To use symmetric encryption, you first need to generate a key. +You can simply use `generatePrivateKey` for secure key generation: + +```js +import { generatePrivateKey } from 'js-waku'; + +const key = generatePrivateKey(); +``` + +### Encrypt Message + +To encrypt a message with the previously generated key, +pass the key in the `symKey` property to `WakuMessage.fromBytes`. + +Same as clear Waku Messages, +`payload` is your message payload and `contentTopic` is the content topic for your dApp. +See [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md) for details. + +```js +import { WakuMessage } from 'js-waku'; + +const message = await WakuMessage.fromBytes(payload, contentTopic, { + symKey: key, +}); +``` + +The Waku Message can then be sent to the Waku network using [Waku Relay](relay-receive-send-messages.md) or Waku Light Push: + +```js +await waku.lightPush.push(message); +``` + +### Decrypt Messages + +#### Waku Relay + +To decrypt messages received over Waku Relay, add the key as a decryption key to your Waku Relay instance. + +```js +waku.relay.addDecryptionKey(key); +``` + +`waku.relay` will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption. +If the message is successfully decrypted, then the decrypted messages will be passed to the observers you have registered. + +You can call `addDecryptionKey` several times if you are using multiple keys, +symmetric key and asymmetric private keys can be used together. + +Messages that are not successfully decrypted are dropped. + +To learn more about Waku Relay, check out [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md). + +#### Waku Store + +To decrypt messages retrieved via a store query, +pass the `key` to the query in the `decryptionKeys` property. + +`decryptionKeys` accepts an array, allowing you to pass several keys. + +Symmetric keys or asymmetric private keys can be mixed, both decryption methods will be attempted. + +```js +// Using await syntax +const messages = await waku2.store.queryHistory([contentTopic], { + decryptionKeys: [key] +}); + +// Using callback syntax +waku2.store.queryHistory([contentTopic], { + decryptionKeys: [key], + callback: (messages) => { + // Process decrypted messages + } +}); +``` + +Messages that are not successfully decrypted are excluded from the result array. + +## Asymmetric Encryption + +### Generate Key Pair + +To use symmetric encryption, you first need to generate a private key and calculate the corresponding public key. +You can simply use `generatePrivateKey` for secure key generation: + +```js +import { generatePrivateKey, getPublicKey } from 'js-waku'; + +const privateKey = generatePrivateKey(); +const publicKey = getPublicKey(privateKey); +``` + +The private key must be securely stored and remain private. +If leaked then other parties may be able to decrypt the user's messages. + +The public key is unique for a given private key and can always be recovered given the private key, +hence it is not needed to save it as long as as the private key can be recovered. + +### Encrypt Message + +The public key is used to encrypt messages, to do so, +pass the key in the `encPublicKey` property to `WakuMessage.fromBytes`. + +Same as clear Waku Messages, +`payload` is your message payload and `contentTopic` is the content topic for your dApp. +See [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md) for details. + +```js +import { WakuMessage } from 'js-waku'; + +const message = await WakuMessage.fromBytes(payload, contentTopic, { + encPublicKey: publicKey +}); +``` + +The Waku Message can then be sent to the Waku network using [Waku Relay](relay-receive-send-messages.md) or Waku Light Push: + +```js +await waku.lightPush.push(message); +``` + +### Decrypt Messages + +#### Waku Relay + +The private key is needed to decrypt messages. + +For messages received over Waku Relay, add the private key as a decryption key to your Waku Relay instance. + +```js +waku.relay.addDecryptionKey(privateKey); +``` + +`waku.relay` will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption. +If the message is successfully decrypted, then the decrypted messages will be passed to the observers you have registered. + +You can call `addDecryptionKey` several times if you are using multiple keys, +symmetric key and asymmetric private keys can be used together. + +Messages that are not successfully decrypted are dropped. + +To learn more about Waku Relay, check out [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md). + +#### Waku Store + +To decrypt messages retrieved via a store query, +pass the `key` to the query in the `decryptionKeys` property. + +`decryptionKeys` accepts an array, allowing you to pass several keys. + +Symmetric keys or asymmetric private keys can be mixed, both decryption methods will be attempted. + +```js +// Using await syntax +const messages = await waku.store.queryHistory([contentTopic], { + decryptionKeys: [privateKey], +}); + +// Using callback syntax +waku.store.queryHistory([contentTopic], { + decryptionKeys: [privateKey], + callback: (messages) => { + // Process decrypted messages + }, +}); +``` + +Messages that are not successfully decrypted are excluded from the result array. + +## Handling `WakuMessage` instances + +When creating a Waku Message using `WakuMessage.fromBytes` with an encryption key (symmetric or asymmetric), +the payload gets encrypted. +Which means that `wakuMessage.payload` returns an encrypted payload: + +```js +import { WakuMessage } from 'js-waku'; + +const message = await WakuMessage.fromBytes(payload, contentTopic, { + encPublicKey: publicKey +}); + +console.log(message.payload); // This is encrypted +``` + +However, `WakuMessage` instances returned by `WakuRelay` or `WakuStore` are decrypted, +if the correct decryption key is passed as explained previously. + +`WakuRelay` and `WakuStore` never returned messages that are encrypted. +If a message was not succesfully decrypted, then it will be dropped from the results. + +Which means that `WakuMessage` instances returned by `WakuRelay` and `WkauStore` always a clear payload (in regard to Waku Message version 1): + +```js +const messages = await waku.store.queryHistory([contentTopic], { + decryptionKeys: [privateKey] +}); + +if (messages && messages[0]) { + console.log(messages[0].payload); // This payload is decrypted +} + +waku.relay.addDecryptionKey(privateKey); + +waku.relay.addObserver((message) => { + console.log(message.payload); // This payload is decrypted +}, [contentTopic]); +``` diff --git a/guides/menu.md b/guides/menu.md index 4cd0487044..75f7deba30 100644 --- a/guides/menu.md +++ b/guides/menu.md @@ -4,3 +4,4 @@ - [How to Choose a Content Topic](choose-content-topic.md) - [Receive and Send Messages Using Waku Relay With ReactJS](reactjs-relay.md) - [Retrieve Messages Using Waku Store](store-retrieve-messages.md) +- [Encrypt Messages Using Waku Message Version 1](encrypt-messages-version-1.md) From cc84edca9c18d5cacc64b096e5a702dafdae5cc3 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 26 Aug 2021 16:27:17 +1000 Subject: [PATCH 2/7] Rephrase, typos --- guides/encrypt-messages-version-1.md | 35 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index 8c7863fb81..1587261ad2 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -8,19 +8,19 @@ You can find more details about Waku Message Payload Encryption in [26/WAKU-PAYL ## What data is encrypted -With waku Message version 1, the payload 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 decrypting of all messages received on said content topic. +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 said traffic; -- If a content topic is only used by a give (group of) users then it is possible to deduce some information about their communication such time and frequency of messages. +- 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 for your user to generate and store keys in a secure manner. +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. @@ -31,12 +31,12 @@ Whether you should use symmetric or asymmetric encryption depends on your use ca **Symmetric** encryption is done using a single key to encrypt and decrypt. -Which means that if Alice knows `K` and use it to encrypt a message, +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 determine `K`. +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. @@ -48,7 +48,7 @@ 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 messaging is a possible use case for aasymmetric encryption: +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 @@ -77,7 +77,7 @@ See [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md) import { WakuMessage } from 'js-waku'; const message = await WakuMessage.fromBytes(payload, contentTopic, { - symKey: key, + symKey: key }); ``` @@ -118,12 +118,12 @@ Symmetric keys or asymmetric private keys can be mixed, both decryption methods ```js // Using await syntax -const messages = await waku2.store.queryHistory([contentTopic], { +const messages = await waku.store.queryHistory([contentTopic], { decryptionKeys: [key] }); // Using callback syntax -waku2.store.queryHistory([contentTopic], { +waku.store.queryHistory([contentTopic], { decryptionKeys: [key], callback: (messages) => { // Process decrypted messages @@ -155,8 +155,8 @@ hence it is not needed to save it as long as as the private key can be recovered ### Encrypt Message -The public key is used to encrypt messages, to do so, -pass the key in the `encPublicKey` property to `WakuMessage.fromBytes`. +The public key is used to encrypt messages; +to do so, pass it in the `encPublicKey` property to `WakuMessage.fromBytes`. Same as clear Waku Messages, `payload` is your message payload and `contentTopic` is the content topic for your dApp. @@ -240,13 +240,12 @@ const message = await WakuMessage.fromBytes(payload, contentTopic, { console.log(message.payload); // This is encrypted ``` -However, `WakuMessage` instances returned by `WakuRelay` or `WakuStore` are decrypted, -if the correct decryption key is passed as explained previously. +However, `WakuMessage` instances returned by `WakuRelay` or `WakuStore` are always decrypted. -`WakuRelay` and `WakuStore` never returned messages that are encrypted. -If a message was not succesfully decrypted, then it will be dropped from the results. +`WakuRelay` and `WakuStore` never return messages that are encrypted. +If a message was not successfully decrypted, then it will be dropped from the results. -Which means that `WakuMessage` instances returned by `WakuRelay` and `WkauStore` always a clear payload (in regard to Waku Message version 1): +Which means that `WakuMessage` instances returned by `WakuRelay` and `WakuStore` always have a clear payload (in regard to Waku Message version 1): ```js const messages = await waku.store.queryHistory([contentTopic], { From 6e5cbefc4994e28fa47e9d1c67c9f6c4fc4cfd91 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 26 Aug 2021 16:33:06 +1000 Subject: [PATCH 3/7] Mention Eth-PM --- guides/encrypt-messages-version-1.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index 1587261ad2..eb4e7dc22e 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -262,3 +262,18 @@ waku.relay.addObserver((message) => { console.log(message.payload); // This payload is decrypted }, [contentTopic]); ``` + + +## Code Example + +The [Eth-PM](https://github.com/status-im/js-waku/tree/main/examples/eth-pm) Web App example demonstrates both the use of symmetric and asymmetric encryption. + +Asymmetric encryption is used for private messages so that only the intended recipient can read said messages. + +Symmetric encryption is used for the public key messages. +In this instance, the same key is used for all users: the Keccak-256 hash of the content topic (which results in 32 bytes array). +While this does not add functional value, it does demonstrate the usage of symmetric encryption in a web app. + +A live version of Eth-PM can be found at https://status-im.github.io/js-waku/eth-pm/. + +The specifications of the protocol it implements can be found at [20/TOY-ETH-PM](https://rfc.vac.dev/spec/20/). From 659e167c068016ed095b767c447421115eaa782b Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Fri, 27 Aug 2021 14:40:46 +1000 Subject: [PATCH 4/7] Mention CryptoSubtle for key storage/export --- guides/encrypt-messages-version-1.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index eb4e7dc22e..95ac10ad01 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -23,7 +23,10 @@ This needs to be kept in mind for scalability and forward secrecy concerns: 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. - +If key recovery is important for your dApp, then check out +[SubtleCrypto.wrapKey()(https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey) which can be used to securely store or export private keys. + +An example to save and load a key pair in local storage, protected with a password, can be found in [Eth-PM](https://github.com/status-im/js-waku/blob/main/examples/eth-pm/src/key_pair_handling/key_pair_storage.ts). ## Which encryption method do I need? From 4504de55f3aabb76509cc5d4067e874a4c2cfe33 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Fri, 27 Aug 2021 14:59:36 +1000 Subject: [PATCH 5/7] Use more precise terminology --- guides/encrypt-messages-version-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index 95ac10ad01..f496201ece 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -72,7 +72,7 @@ const key = generatePrivateKey(); To encrypt a message with the previously generated key, pass the key in the `symKey` property to `WakuMessage.fromBytes`. -Same as clear Waku Messages, +Same as Waku Messages version 0 (unencrypted), `payload` is your message payload and `contentTopic` is the content topic for your dApp. See [Receive and Send Messages Using Waku Relay](relay-receive-send-messages.md) for details. From e0d199ef14dbeb0e760a037db85641ebaab2259f Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 30 Aug 2021 15:13:03 +1000 Subject: [PATCH 6/7] Add word --- .cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.cspell.json b/.cspell.json index a7f7f5ffdb..82d6d34fc7 100644 --- a/.cspell.json +++ b/.cspell.json @@ -75,6 +75,7 @@ "supercrypto", "transpiled", "typedoc", + "unencrypted", "unmount", "unmounts", "untracked", From c9678aa66571e59a96fa6d09de00c27ee5238aab Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 30 Aug 2021 19:54:36 +1000 Subject: [PATCH 7/7] Fix link --- guides/encrypt-messages-version-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index f496201ece..01a5edf289 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -24,7 +24,7 @@ By using Waku Message Version 1, you will need to provide a way to your users to Storing, backing up and recovering key is out of the scope of this guide. If key recovery is important for your dApp, then check out -[SubtleCrypto.wrapKey()(https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey) which can be used to securely store or export private keys. +[SubtleCrypto.wrapKey()](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey) which can be used to securely store or export private keys. An example to save and load a key pair in local storage, protected with a password, can be found in [Eth-PM](https://github.com/status-im/js-waku/blob/main/examples/eth-pm/src/key_pair_handling/key_pair_storage.ts).