Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add js-waku encryption guide #145

Merged
merged 13 commits into from
Jan 11, 2024
1 change: 1 addition & 0 deletions docs/guides/js-waku/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Have a look at the quick start guide and comprehensive tutorials to learn how to
| - | - |
| [Send and Receive Messages Using Light Push and Filter](/guides/js-waku/light-send-receive) | Learn how to send and receive messages on light nodes using the [Light Push](/learn/concepts/protocols#light-push) and [Filter](/learn/concepts/protocols#filter) protocols |
| [Retrieve Messages Using Store Protocol](/guides/js-waku/store-retrieve-messages) | Learn how to retrieve and filter historical messages on light nodes using the [Store protocol](/learn/concepts/protocols#store) |
| [Encrypt, Decrypt, and Sign Your Messages](/guides/js-waku/message-encryption) | Learn how to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages |
| [Build React DApps Using @waku/react](/guides/js-waku/use-waku-react) | Learn how to use the [@waku/react](https://www.npmjs.com/package/@waku/react) package seamlessly integrate `@waku/sdk` into a React application |
| [Scaffold DApps Using @waku/create-app](/guides/js-waku/use-waku-create-app) | Learn how to use the [@waku/create-app](https://www.npmjs.com/package/@waku/create-app) package to bootstrap your next `@waku/sdk` project from various example templates |
| [Bootstrap Nodes and Discover Peers](/guides/js-waku/configure-discovery) | Learn how to bootstrap your node using [Static Peers](/learn/concepts/static-peers) and discover peers using [DNS Discovery](/learn/concepts/dns-discovery) |
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/js-waku/light-send-receive.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const callback = (wakuMessage) => {
console.log(messageObj);
};

// Create a filter subscription
// Create a Filter subscription
const subscription = await node.filter.createSubscription();

// Subscribe to content topics and process new messages
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/js-waku/manage-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Manage Your Filter Subscriptions
hide_table_of_contents: true
---

This guide provides detailed steps to manage [Filter](/learn/concepts/protocols#filter) subscriptions and handle node disconnections in your application. Have a look at the [Filter guide](/guides/js-waku/light-send-receive) for receiving messages with the `Light Push` and `Filter` protocol.
This guide provides detailed steps to manage [Filter](/learn/concepts/protocols#filter) subscriptions and handle node disconnections in your application. Have a look at the [Send and Receive Messages Using Light Push and Filter](/guides/js-waku/light-send-receive) guide for using the `Light Push` and `Filter` protocols.

## Overview

Expand Down
233 changes: 233 additions & 0 deletions docs/guides/js-waku/message-encryption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
---
title: Encrypt, Decrypt, and Sign Your Messages
hide_table_of_contents: true
---

This guide provides detailed steps to use the [@waku/message-encryption](https://www.npmjs.com/package/@waku/message-encryption) package to encrypt, decrypt, and sign your messages using [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.

:::info
Waku uses libp2p noise encryption for node-to-node connections. However, no default encryption method is applied to the data sent over the network. This design choice enhances Waku's encryption flexibility, encouraging developers to freely use custom protocols or [Waku message payload encryption](/learn/glossary#waku-message-payload-encryption) methods.
:::

## Installation

Install the required packages for integrating `@waku/message-encryption` using your preferred package manager:

```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```

<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">

```shell
npm install @waku/message-encryption @waku/utils uint8arrays
```

</TabItem>
<TabItem value="yarn" label="Yarn">

```shell
yarn add @waku/message-encryption @waku/utils uint8arrays
```

</TabItem>
</Tabs>

## Symmetric encryption

`Symmetric` encryption uses a single, shared key for message encryption and decryption. Use the `generateSymmetricKey()` function to generate a random symmetric key:

```js
import { generateSymmetricKey } from "@waku/message-encryption";

// Generate a random symmetric key
const symKey = generateSymmetricKey();
```

To send encrypted messages, create a `Symmetric` message `encoder` and send the message as usual:

```js
import { createEncoder } from "@waku/message-encryption/symmetric";

// Create a symmetric message encoder
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
symKey: symKey, // symmetric key for encrypting messages
});

// Send the message using Light Push
await node.lightPush.send(encoder, { payload });
```

To decrypt the messages you receive, create a symmetric message `decoder` and process the messages as usual:
LordGhostX marked this conversation as resolved.
Show resolved Hide resolved

```js
import { createDecoder } from "@waku/message-encryption/symmetric";

// Create a symmetric message decoder
const decoder = createDecoder(contentTopic, symKey);

// Receive messages from a Filter subscription
const subscription = await node.filter.createSubscription();
await subscription.subscribe([decoder], callback);

// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```

:::tip
The symmetric key exchange between users can happen through an [out-of-band method](/learn/glossary#out-of-band). For example, where the key is embedded within the URL shared by a user to access a specific resource.
:::

## ECIES encryption
LordGhostX marked this conversation as resolved.
Show resolved Hide resolved

`ECIES` encryption uses a public key for encryption and a private key for decryption. Use the `generatePrivateKey()` function to generate a random `ECDSA` private key:

```js
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";

// Generate a random ECDSA private key, keep secure
const privateKey = generatePrivateKey();

// Generate a public key from the private key, provide to the sender
const publicKey = getPublicKey(privateKey);
```

To send encrypted messages, create an `ECIES` message `encoder` with the public key and send the message as usual:

```js
import { createEncoder } from "@waku/message-encryption/ecies";

// Create an ECIES message encoder
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
publicKey: publicKey, // ECIES public key for encrypting messages
});

// Send the message using Light Push
await node.lightPush.send(encoder, { payload });
```

To decrypt the messages you receive, create an `ECIES` message `decoder` with the private key and process the messages as usual:

```js
import { createDecoder } from "@waku/message-encryption/ecies";

// Create an ECIES message decoder
const decoder = createDecoder(contentTopic, privateKey);

// Receive messages from a Filter subscription
const subscription = await node.filter.createSubscription();
await subscription.subscribe([decoder], callback);

// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```

:::tip
Users can share their public key through broadcasting or [out-of-band methods](/learn/glossary#out-of-band), such as embedding it in a URL or sending an unencrypted message on another content topic for others to retrieve.
:::

## Signing encrypted messages

Message signing helps in proving the authenticity of received messages. By attaching a signature to a message, you can verify its origin and integrity with absolute certainty.

LordGhostX marked this conversation as resolved.
Show resolved Hide resolved
:::info
Signing messages is only possible when encrypted, but if your app does not require encryption, you can generate a symmetric key through hardcoded or deterministic methods using information available to all users.
:::

The `sigPrivKey` option allows the `Symmetric` and `ECIES` message `encoders` to sign the message before encryption using an `ECDSA` private key:

```js
import { generatePrivateKey } from "@waku/message-encryption";
import { createEncoder as createSymmetricEncoder } from "@waku/message-encryption/symmetric";
import { createEncoder as createECIESEncoder } from "@waku/message-encryption/ecies";

// Generate a random ECDSA private key for signing messages
// ECIES encryption and message signing both use ECDSA keys
const sigPrivKey = generatePrivateKey();
LordGhostX marked this conversation as resolved.
Show resolved Hide resolved

// Create a symmetric encoder that signs messages
const symmetricEncoder = createSymmetricEncoder({
contentTopic: contentTopic, // message content topic
symKey: symKey, // symmetric key for encrypting messages
sigPrivKey: sigPrivKey, // private key for signing messages before encryption
});

// Create an ECIES encoder that signs messages
const ECIESEncoder = createECIESEncoder({
contentTopic: contentTopic, // message content topic
publicKey: publicKey, // ECIES public key for encrypting messages
sigPrivKey: sigPrivKey, // private key for signing messages before encryption
});

// Send and receive your messages as usual with Light Push and Filter
await subscription.subscribe([symmetricEncoder], callback);
await node.lightPush.send(symmetricEncoder, { payload });

await subscription.subscribe([ECIESEncoder], callback);
await node.lightPush.send(ECIESEncoder, { payload });
```

You can extract the `signature` and its public key (`signaturePublicKey`) from the [DecodedMessage](https://js.waku.org/classes/_waku_message_encryption.DecodedMessage.html) and compare it with the expected public key to verify the message origin:

```js
import { equals } from "uint8arrays/equals";

// Generate a random private key for signing messages
const sigPrivKey = generatePrivateKey();

// Generate a public key from the private key for verifying signatures
const sigPubKey = getPublicKey(sigPrivKey);

// Create an encoder that signs messages
const encoder = createEncoder({
contentTopic: contentTopic,
symKey: symKey,
sigPrivKey: sigPrivKey,
});
LordGhostX marked this conversation as resolved.
Show resolved Hide resolved

// Modify the callback function to verify message signature
const callback = (wakuMessage) => {
// Extract the message signature and public key of the signature
const signature = wakuMessage.signature;
const signaturePublicKey = wakuMessage.signaturePublicKey;

// Compare the public key of the message signature with the sender's own
if (equals(signaturePublicKey, sigPubKey)) {
console.log("This message was correctly signed");
} else {
console.log("This message has an incorrect signature");
}
};
```

## Restoring encryption keys
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this is necessary//useful.

However, do feel free to use https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm/src/key_pair_handling as an example on how to save/restore key information from local storage using proper security (Subtle Crypto)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the key_pair_handling example, but I still think the bytesToHex and hexToBytes examples are helpful for some key exporting use cases. Let me know what you think about the update


We used randomly generated keys for encryption and message signing in the provided examples, but real-world applications require consistent keys among clients. You can use the [@waku/utils](https://www.npmjs.com/package/@waku/utils) package to convert keys into a hexadecimal format for uniformity:

```js
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
import { generateSymmetricKey, generatePrivateKey } from "@waku/message-encryption";

// Generate random symmetric and private keys
const symKey = generateSymmetricKey();
const privateKey = generatePrivateKey();
console.log(symKey, privateKey);

// Convert the keys to hexadecimal format
const symKeyHex = bytesToHex(symKey);
const privateKeyHex = bytesToHex(privateKey);
console.log(symKeyHex, privateKeyHex);

// Restore the keys from hexadecimal format
const restoredSymKey = hexToBytes(symKeyHex);
const restoredPrivateKey = hexToBytes(privateKeyHex);
console.log(restoredSymKey, restoredPrivateKey);
```

:::tip Congratulations!
You have successfully encrypted, decrypted, and signed your messages using `symmetric` and `ECIES` encryption methods. Have a look at the [flush-notes](https://github.com/waku-org/js-waku-examples/tree/master/examples/flush-notes) example for a working demo.
:::
2 changes: 1 addition & 1 deletion docs/guides/js-waku/use-waku-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ yarn create vite [PROJECT DIRECTORY] --template react
</TabItem>
</Tabs>

Next, install the required libraries for integrating `@waku/sdk` using your preferred package manager:
Next, install the required packages for integrating `@waku/sdk` using your preferred package manager:

<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
Expand Down
4 changes: 4 additions & 0 deletions docs/learn/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ A node is a device or client that implements Waku [protocols](#protocol) and lev

A node key is a [Secp256k1](https://en.bitcoin.it/wiki/Secp256k1) (64-char hex string) private key for generating the [PeerID](#peer-id), [listening](#transport) addresses, and [discovery](#peer-discovery) addresses of a Waku node.

### Out-of-band

Out-of-band refers to exchanging information through a separate, secure channel distinct from the main communication method to enhance security.

### Payload

The payload field in a [Waku Message](#waku-message) contains the application data, serving as the business logic message transmitted between clients over Waku. Applications can encrypt the payload or employ encryption methods specified in [Waku Message Payload Encryption](#waku-message-payload-encryption).
Expand Down
8 changes: 4 additions & 4 deletions docs/learn/waku-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ title: The Waku Network
hide_table_of_contents: true
---

:::info
The public Waku Network replaces the previous experimental shared routing layer based on a default pubsub topic (`/waku/2/default-waku/proto`). If your project currently uses this or any other shared pubsub topics, we encourage you to migrate to the public Waku Network with built-in DoS protection, with built-in DoS protection, scalability and reasonable bandwidth usage.
:::

The Waku Network is a shared p2p messaging network that is open-access, useful for generalized messaging, privacy-preserving, scalable and accessible even to resource-restricted devices. Some of the most prominent features include:

1. DoS/spam protection with privacy-preserving [Rate-Limiting Nullifiers](https://rfc.vac.dev/spec/64/#rln-rate-limiting).
Expand All @@ -16,6 +12,10 @@ The Waku Network is a shared p2p messaging network that is open-access, useful f

If you want to learn more about the Waku Network, the [WAKU2-NETWORK RFC](https://rfc.vac.dev/spec/64/) provides an in-depth look under the hood.

:::info
The public Waku Network replaces the previous experimental shared routing layer based on a default pubsub topic (`/waku/2/default-waku/proto`). If your project currently uses this or any other shared pubsub topics, we encourage you to migrate to the public Waku Network with built-in DoS protection, with built-in DoS protection, scalability, and reasonable bandwidth usage.
:::

## Why join the Waku network?

1. Applications or projects can build decentralized communication components on this network, gaining from the fault-tolerance of shared infrastructure, the out-of-the-box censorship resistance of a p2p network and the privacy-preservation of Waku protocols.
Expand Down
4 changes: 4 additions & 0 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ const config = {
href: "https://rfc.vac.dev/",
label: "Vac RFCs",
},
{
href: "https://github.com/waku-org/awesome-waku/",
label: "Awesome Waku",
},
],
},
{
Expand Down
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const sidebars = {
items: [
"guides/js-waku/light-send-receive",
"guides/js-waku/store-retrieve-messages",
"guides/js-waku/message-encryption",
"guides/js-waku/use-waku-react",
"guides/js-waku/use-waku-create-app",
"guides/js-waku/configure-discovery",
Expand Down