/ docs / build / javascript / message-encryption.mdx
message-encryption.mdx
  1  ---
  2  title: Encrypt, Decrypt, and Sign Your Messages
  3  hide_table_of_contents: true
  4  ---
  5  
  6  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.
  7  
  8  :::info
  9  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.
 10  :::
 11  
 12  ## Installation
 13  
 14  Install the required packages for integrating `@waku/message-encryption` using your preferred package manager:
 15  
 16  ```mdx-code-block
 17  import Tabs from '@theme/Tabs';
 18  import TabItem from '@theme/TabItem';
 19  ```
 20  
 21  <Tabs groupId="package-manager">
 22  <TabItem value="npm" label="NPM">
 23  
 24  ```shell
 25  npm install @waku/message-encryption @waku/utils
 26  ```
 27  
 28  </TabItem>
 29  <TabItem value="yarn" label="Yarn">
 30  
 31  ```shell
 32  yarn add @waku/message-encryption @waku/utils
 33  ```
 34  
 35  </TabItem>
 36  </Tabs>
 37  
 38  ## Symmetric encryption
 39  
 40  `Symmetric` encryption uses a single, shared key for message encryption and decryption. Use the `generateSymmetricKey()` function to generate a random symmetric key:
 41  
 42  ```js
 43  import { generateSymmetricKey } from "@waku/message-encryption";
 44  
 45  // Generate a random symmetric key
 46  const symmetricKey = generateSymmetricKey();
 47  ```
 48  
 49  To send encrypted messages, create a `Symmetric` message `encoder` and send the message as usual:
 50  
 51  ```js title="Sender client"
 52  import { createEncoder } from "@waku/message-encryption/symmetric";
 53  
 54  // Create a symmetric message encoder
 55  const encoder = createEncoder({
 56    contentTopic: contentTopic, // message content topic
 57    symKey: symmetricKey, // symmetric key for encrypting messages
 58  });
 59  
 60  // Send the message using Light Push
 61  await node.lightPush.send(encoder, { payload });
 62  ```
 63  
 64  To decrypt the messages you receive, create a symmetric message `decoder` and process the messages as usual:
 65  
 66  ```js title="Receiver client"
 67  import { createDecoder } from "@waku/message-encryption/symmetric";
 68  
 69  // Create a symmetric message decoder
 70  const decoder = createDecoder(contentTopic, symmetricKey);
 71  
 72  // Receive messages from a Filter subscription
 73  await subscription.subscribe([decoder], callback);
 74  
 75  // Retrieve messages from Store peers
 76  await node.store.queryWithOrderedCallback([decoder], callback);
 77  ```
 78  
 79  :::tip
 80  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.
 81  :::
 82  
 83  ## ECIES encryption
 84  
 85  `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:
 86  
 87  ```js
 88  import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
 89  
 90  // Generate a random ECDSA private key, keep secure
 91  const privateKey = generatePrivateKey();
 92  
 93  // Generate a public key from the private key, provide to the sender
 94  const publicKey = getPublicKey(privateKey);
 95  ```
 96  
 97  To send encrypted messages, create an `ECIES` message `encoder` with the public key and send the message as usual:
 98  
 99  ```js title="Sender client"
100  import { createEncoder } from "@waku/message-encryption/ecies";
101  
102  // Create an ECIES message encoder
103  const encoder = createEncoder({
104    contentTopic: contentTopic, // message content topic
105    publicKey: publicKey, // ECIES public key for encrypting messages
106  });
107  
108  // Send the message using Light Push
109  await node.lightPush.send(encoder, { payload });
110  ```
111  
112  To decrypt the messages you receive, create an `ECIES` message `decoder` with the private key and process the messages as usual:
113  
114  ```js title="Receiver client"
115  import { createDecoder } from "@waku/message-encryption/ecies";
116  
117  // Create an ECIES message decoder
118  const decoder = createDecoder(contentTopic, privateKey);
119  
120  // Receive messages from a Filter subscription
121  await subscription.subscribe([decoder], callback);
122  
123  // Retrieve messages from Store peers
124  await node.store.queryWithOrderedCallback([decoder], callback);
125  ```
126  
127  :::tip
128  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.
129  :::
130  
131  ## Signing encrypted messages
132  
133  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.
134  
135  :::info
136  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.
137  :::
138  
139  The `sigPrivKey` parameter allows the `Symmetric` and `ECIES` message `encoders` to sign the message before encryption using an `ECDSA` private key:
140  
141  ```js title="Alice (sender) client"
142  import { generatePrivateKey, getPublicKey } from "@waku/message-encryption";
143  import { createEncoder as createSymmetricEncoder } from "@waku/message-encryption/symmetric";
144  import { createEncoder as createECIESEncoder } from "@waku/message-encryption/ecies";
145  
146  // Generate a random ECDSA private key for signing messages
147  // ECIES encryption and message signing both use ECDSA keys
148  // For this example, we'll call the sender of the message Alice
149  const alicePrivateKey = generatePrivateKey();
150  const alicePublicKey = getPublicKey(alicePrivateKey);
151  
152  // Create a symmetric encoder that signs messages
153  const symmetricEncoder = createSymmetricEncoder({
154    contentTopic: contentTopic, // message content topic
155    symKey: symmetricKey, // symmetric key for encrypting messages
156    sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
157  });
158  
159  // Create an ECIES encoder that signs messages
160  const ECIESEncoder = createECIESEncoder({
161    contentTopic: contentTopic, // message content topic
162    publicKey: publicKey, // ECIES public key for encrypting messages
163    sigPrivKey: alicePrivateKey, // private key for signing messages before encryption
164  });
165  
166  // Send and receive your messages as usual with Light Push and Filter
167  await subscription.subscribe([symmetricEncoder], callback);
168  await node.lightPush.send(symmetricEncoder, { payload });
169  
170  await subscription.subscribe([ECIESEncoder], callback);
171  await node.lightPush.send(ECIESEncoder, { payload });
172  ```
173  
174  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 or use the `verifySignature()` function to verify the message origin:
175  
176  ```js title="Bob (receiver) client"
177  import { generatePrivateKey } from "@waku/message-encryption";
178  import { createEncoder } from "@waku/message-encryption/symmetric";
179  
180  // Generate a random private key for signing messages
181  // For this example, we'll call the receiver of the message Bob
182  const bobPrivateKey = generatePrivateKey();
183  
184  // Create an encoder that signs messages
185  const encoder = createEncoder({
186    contentTopic: contentTopic,
187    symKey: symmetricKey,
188    sigPrivKey: bobPrivateKey,
189  });
190  
191  // Modify the callback function to verify message signature
192  const callback = (wakuMessage) => {
193    // Extract the message signature and public key of the signature
194    // You can compare the signaturePublicKey with Alice public key
195    const signature = wakuMessage.signature;
196    const signaturePublicKey = wakuMessage.signaturePublicKey;
197  
198    // Verify the message was actually signed and sent by Alice
199    // Alice's public key can be gotten from broadcasting or out-of-band methods
200    if (wakuMessage.verifySignature(alicePublicKey)) {
201      console.log("This message was signed by Alice");
202    } else {
203      console.log("This message was NOT signed by Alice");
204    }
205  };
206  
207  await subscription.subscribe([encoder], callback);
208  ```
209  
210  ## Storing encryption keys
211  
212  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).
213  
214  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:
215  
216  ```js
217  import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
218  
219  // Generate random symmetric and private keys
220  const symmetricKey = generateSymmetricKey();
221  const privateKey = generatePrivateKey();
222  
223  // Store the keys in hexadecimal format
224  const symmetricKeyHex = bytesToHex(symmetricKey);
225  const privateKeyHex = bytesToHex(privateKey);
226  
227  // Restore the keys from hexadecimal format
228  const restoredSymmetricKey = hexToBytes(symmetricKeyHex);
229  const restoredPrivateKey = hexToBytes(privateKeyHex);
230  ```
231  
232  :::tip Congratulations!
233  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.
234  :::
235  
236  <!-- [flush-notes](https://github.com/waku-org/js-waku-examples/tree/master/examples/flush-notes) and -->