docs.waku.org/docs/guides/js-waku/message-encryption.md

240 lines
9.5 KiB
Markdown
Raw Normal View History

2023-12-06 05:54:28 +01:00
---
2023-12-06 12:44:14 +01:00
title: Encrypt, Decrypt, and Sign Your Messages
2023-12-06 05:54:28 +01:00
hide_table_of_contents: true
---
2023-12-06 12:44:14 +01:00
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.
2023-12-06 05:54:28 +01:00
:::info
2023-12-07 13:08:42 +01:00
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.
2023-12-06 05:54:28 +01:00
:::
## Installation
Install the required packages for integrating `@waku/message-encryption` using your preferred package manager:
2023-12-06 05:54:28 +01:00
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
<Tabs groupId="package-manager">
<TabItem value="npm" label="NPM">
```shell
2023-12-12 16:53:38 +01:00
npm install @waku/message-encryption @waku/utils
2023-12-06 05:54:28 +01:00
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```shell
2023-12-12 16:53:38 +01:00
yarn add @waku/message-encryption @waku/utils
2023-12-06 05:54:28 +01:00
```
</TabItem>
</Tabs>
## Symmetric encryption
2023-12-06 12:44:14 +01:00
`Symmetric` encryption uses a single, shared key for message encryption and decryption. Use the `generateSymmetricKey()` function to generate a random symmetric key:
2023-12-06 05:54:28 +01:00
```js
import { generateSymmetricKey } from "@waku/message-encryption";
// Generate a random symmetric key
2023-12-12 16:53:38 +01:00
const symmetricKey = generateSymmetricKey();
2023-12-06 05:54:28 +01:00
```
2023-12-06 12:44:14 +01:00
To send encrypted messages, create a `Symmetric` message `encoder` and send the message as usual:
2023-12-06 05:54:28 +01:00
2023-12-08 14:35:38 +01:00
```js title="Sender client"
2023-12-06 05:54:28 +01:00
import { createEncoder } from "@waku/message-encryption/symmetric";
// Create a symmetric message encoder
const encoder = createEncoder({
contentTopic: contentTopic, // message content topic
2023-12-12 16:53:38 +01:00
symKey: symmetricKey, // symmetric key for encrypting messages
2023-12-06 05:54:28 +01:00
});
// 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:
2023-12-08 14:35:38 +01:00
```js title="Receiver client"
2023-12-06 05:54:28 +01:00
import { createDecoder } from "@waku/message-encryption/symmetric";
// Create a symmetric message decoder
2023-12-12 16:53:38 +01:00
const decoder = createDecoder(contentTopic, symmetricKey);
2023-12-06 05:54:28 +01:00
// Receive messages from a Filter subscription
await subscription.subscribe([decoder], callback);
// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```
2023-12-07 13:08:42 +01:00
:::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.
:::
2023-12-06 05:54:28 +01:00
## ECIES encryption
2023-12-07 13:08:42 +01:00
`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:
2023-12-06 05:54:28 +01:00
```js
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
2023-12-07 13:08:42 +01:00
// Generate a random ECDSA private key, keep secure
2023-12-06 05:54:28 +01:00
const privateKey = generatePrivateKey();
// Generate a public key from the private key, provide to the sender
const publicKey = getPublicKey(privateKey);
```
2023-12-06 12:44:14 +01:00
To send encrypted messages, create an `ECIES` message `encoder` with the public key and send the message as usual:
2023-12-06 05:54:28 +01:00
2023-12-08 14:35:38 +01:00
```js title="Sender client"
2023-12-06 05:54:28 +01:00
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 });
```
2023-12-06 12:44:14 +01:00
To decrypt the messages you receive, create an `ECIES` message `decoder` with the private key and process the messages as usual:
2023-12-06 05:54:28 +01:00
2023-12-08 14:35:38 +01:00
```js title="Receiver client"
2023-12-06 05:54:28 +01:00
import { createDecoder } from "@waku/message-encryption/ecies";
// Create an ECIES message decoder
const decoder = createDecoder(contentTopic, privateKey);
// Receive messages from a Filter subscription
await subscription.subscribe([decoder], callback);
// Retrieve messages from Store peers
await node.store.queryWithOrderedCallback([decoder], callback);
```
2023-12-07 13:08:42 +01:00
:::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.
:::
2023-12-06 12:44:14 +01:00
## 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.
2023-12-07 13:08:42 +01:00
:::info
2023-12-08 13:42:01 +01:00
Signing messages is only possible when encrypted, but if your application does not require encryption, you can generate a symmetric key through hardcoded or deterministic methods using information available to all users.
2023-12-07 13:08:42 +01:00
:::
2023-12-06 12:44:14 +01:00
The `sigPrivKey` option allows the `Symmetric` and `ECIES` message `encoders` to sign the message before encryption using an `ECDSA` private key:
2023-12-08 14:35:38 +01:00
```js title="Alice (sender) client"
2023-12-08 13:42:01 +01:00
import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
2023-12-06 12:44:14 +01:00
import { createEncoder as createSymmetricEncoder } from "@waku/message-encryption/symmetric";
import { createEncoder as createECIESEncoder } from "@waku/message-encryption/ecies";
2023-12-07 13:08:42 +01:00
// Generate a random ECDSA private key for signing messages
// ECIES encryption and message signing both use ECDSA keys
2023-12-08 13:42:01 +01:00
// For this example, we'll call the sender of the message Alice
2023-12-12 16:53:38 +01:00
const alicePrivateKey = generatePrivateKey();
const alicePublicKey = getPublicKey(alicePrivateKey);
2023-12-06 12:44:14 +01:00
// Create a symmetric encoder that signs messages
const symmetricEncoder = createSymmetricEncoder({
2023-12-06 15:21:57 +01:00
contentTopic: contentTopic, // message content topic
2023-12-12 16:53:38 +01:00
symKey: symmetricKey, // symmetric key for encrypting messages
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
2023-12-06 12:44:14 +01:00
});
// Create an ECIES encoder that signs messages
const ECIESEncoder = createECIESEncoder({
2023-12-06 15:21:57 +01:00
contentTopic: contentTopic, // message content topic
publicKey: publicKey, // ECIES public key for encrypting messages
2023-12-12 16:53:38 +01:00
sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
2023-12-06 12:44:14 +01:00
});
2023-12-06 14:22:06 +01:00
// Send and receive your messages as usual with Light Push and Filter
await subscription.subscribe([symmetricEncoder], callback);
2023-12-06 15:21:57 +01:00
await node.lightPush.send(symmetricEncoder, { payload });
2023-12-06 14:22:06 +01:00
await subscription.subscribe([ECIESEncoder], callback);
2023-12-06 15:21:57 +01:00
await node.lightPush.send(ECIESEncoder, { payload });
2023-12-06 14:22:06 +01:00
```
2024-01-11 10:30:57 +01:00
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:
<!-- or use the `verifySignature()` function -->
<!-- if (wakuMessage.verifySignature(alicePublicKey)) { -->
2023-12-06 14:22:06 +01:00
2023-12-08 14:35:38 +01:00
```js title="Bob (receiver) client"
2023-12-08 13:42:01 +01:00
import { generatePrivateKey } from "@waku/message-encryption";
import { createEncoder } from "@waku/message-encryption/symmetric";
2024-01-11 10:30:57 +01:00
import { equals } from "uint8arrays/equals";
2023-12-06 14:22:06 +01:00
// Generate a random private key for signing messages
2023-12-08 13:42:01 +01:00
// For this example, we'll call the receiver of the message Bob
2023-12-12 16:53:38 +01:00
const bobPrivateKey = generatePrivateKey();
2023-12-06 14:22:06 +01:00
// Create an encoder that signs messages
const encoder = createEncoder({
2023-12-06 15:21:57 +01:00
contentTopic: contentTopic,
2023-12-12 16:53:38 +01:00
symKey: symmetricKey,
sigPrivKey: bobPrivateKey,
2023-12-06 14:22:06 +01:00
});
// Modify the callback function to verify message signature
const callback = (wakuMessage) => {
// Extract the message signature and public key of the signature
2023-12-12 16:53:38 +01:00
// You can compare the signaturePublicKey with Alice public key
2023-12-06 14:22:06 +01:00
const signature = wakuMessage.signature;
const signaturePublicKey = wakuMessage.signaturePublicKey;
2023-12-12 16:53:38 +01:00
// Verify the message was actually signed and sent by Alice
2023-12-08 13:42:01 +01:00
// Alice's public key can be gotten from broadcasting or out-of-band methods
2024-01-11 10:30:57 +01:00
if (equals(signaturePublicKey, alicePublicKey)) {
2023-12-08 13:42:01 +01:00
console.log("This message was signed by Alice");
2023-12-06 14:22:06 +01:00
} else {
2023-12-08 13:42:01 +01:00
console.log("This message was NOT signed by Alice");
2023-12-06 14:22:06 +01:00
}
};
2023-12-08 13:42:01 +01:00
await subscription.subscribe([encoder], callback);
2023-12-06 12:44:14 +01:00
```
2023-12-08 14:35:38 +01:00
## Storing encryption keys
2023-12-06 15:21:57 +01:00
2024-01-09 16:55:03 +01:00
We used randomly generated keys for encryption and message signing in the provided examples, but real-world applications require consistent keys among client restarts. Have a look at the [Key Pair Handling](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm/src/key_pair_handling) example, which demonstrates the secure storage and retrieval of key information from local storage using [Subtle Crypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto).
2023-12-08 14:28:05 +01:00
2024-01-09 16:55:03 +01:00
If you need a simple way to store your keys in hexadecimal format across your application, you can use the [@waku/utils](https://www.npmjs.com/package/@waku/utils) package:
2023-12-06 15:21:57 +01:00
```js
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
// Generate random symmetric and private keys
2023-12-12 16:53:38 +01:00
const symmetricKey = generateSymmetricKey();
2023-12-06 15:21:57 +01:00
const privateKey = generatePrivateKey();
2023-12-08 14:35:38 +01:00
// Store the keys in hexadecimal format
2023-12-12 16:53:38 +01:00
const symmetricKeyHex = bytesToHex(symmetricKey);
2023-12-06 15:21:57 +01:00
const privateKeyHex = bytesToHex(privateKey);
// Restore the keys from hexadecimal format
2023-12-12 16:53:38 +01:00
const restoredSymmetricKey = hexToBytes(symmetricKeyHex);
2023-12-06 15:21:57 +01:00
const restoredPrivateKey = hexToBytes(privateKeyHex);
```
2023-12-06 05:54:28 +01:00
:::tip Congratulations!
2024-01-11 10:30:57 +01:00
You have successfully encrypted, decrypted, and signed your messages using `Symmetric` and `ECIES` encryption methods. Have a look at the [eth-pm](https://github.com/waku-org/js-waku-examples/tree/master/examples/eth-pm) example for a working demo.
:::
<!-- [flush-notes](https://github.com/waku-org/js-waku-examples/tree/master/examples/flush-notes) and -->