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 -->