From 75352abcacbd9dca234fdf6eb434bca1d7322b32 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Mon, 30 Aug 2021 20:41:18 +1000 Subject: [PATCH 1/9] Promote dedicated symmetric key generation API Using the private key API for symmetric key is confusing. --- CHANGELOG.md | 1 + README.md | 4 ++-- guides/encrypt-messages-version-1.md | 4 ++-- src/index.ts | 6 +++++- src/lib/waku_message/index.spec.ts | 8 ++++++-- src/lib/waku_message/symmetric/browser.ts | 6 +----- src/lib/waku_message/symmetric/index.ts | 4 ---- src/lib/waku_message/symmetric/node.ts | 6 +----- src/lib/waku_message/version_1.ts | 19 +++++++++++++------ src/lib/waku_store/index.spec.ts | 8 ++++++-- 10 files changed, 37 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d87a9b9682..e3f12b8a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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. +- Renamed and promote the usage of `generateSymmetricKey()` to generate random symmetric keys. ### Fixed - Buffer concat error when using symmetric encryption in the browser. diff --git a/README.md b/README.md index 30dddbd372..9e2463d4ee 100644 --- a/README.md +++ b/README.md @@ -138,14 +138,14 @@ A quick note on the cryptographic libraries used as it is a not a straightforwar Asymmetric private keys and symmetric keys are expected to be 32 bytes arrays. ```ts -import { generatePrivateKey, getPublicKey } from 'js-waku'; +import { generatePrivateKey, generateSymmetricKey, getPublicKey } from 'js-waku'; // Asymmetric const privateKey = generatePrivateKey(); const publicKey = getPublicKey(privateKey); // Symmetric -const symKey = generatePrivateKey(); +const symKey = generateSymmetricKey(); ``` #### Encrypt Waku Messages diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index 01a5edf289..8f00634aea 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -62,9 +62,9 @@ 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'; +import { generateSymmetricKey } from 'js-waku'; -const key = generatePrivateKey(); +const key = generateSymmetricKey(); ``` ### Encrypt Message diff --git a/src/index.ts b/src/index.ts index ac71817bdf..69c667b160 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,11 @@ export { Waku, DefaultPubSubTopic } from './lib/waku'; export { WakuMessage } from './lib/waku_message'; -export { generatePrivateKey, getPublicKey } from './lib/waku_message/version_1'; +export { + generatePrivateKey, + generateSymmetricKey, + getPublicKey, +} from './lib/waku_message/version_1'; export { WakuLightPush, diff --git a/src/lib/waku_message/index.spec.ts b/src/lib/waku_message/index.spec.ts index 7cb6a6ae79..9795c045e7 100644 --- a/src/lib/waku_message/index.spec.ts +++ b/src/lib/waku_message/index.spec.ts @@ -14,7 +14,11 @@ import { delay } from '../delay'; import { hexToBuf } from '../utils'; import { Waku } from '../waku'; -import { generatePrivateKey, getPublicKey } from './version_1'; +import { + generatePrivateKey, + generateSymmetricKey, + getPublicKey, +} from './version_1'; import { WakuMessage } from './index'; @@ -122,7 +126,7 @@ describe('Waku Message: Node only', function () { payload: Buffer.from(messageText, 'utf-8').toString('hex'), }; - const symKey = generatePrivateKey(); + const symKey = generateSymmetricKey(); waku.relay.addDecryptionKey(symKey); diff --git a/src/lib/waku_message/symmetric/browser.ts b/src/lib/waku_message/symmetric/browser.ts index 4f092f74da..bb1dd3222a 100644 --- a/src/lib/waku_message/symmetric/browser.ts +++ b/src/lib/waku_message/symmetric/browser.ts @@ -1,4 +1,4 @@ -import { IvSize, SymmetricKeySize } from './index'; +import { IvSize } from './index'; declare global { interface Window { @@ -44,10 +44,6 @@ export async function decrypt( .then(Buffer.from); } -export function generateKeyForSymmetricEnc(): Buffer { - return crypto.getRandomValues(Buffer.alloc(SymmetricKeySize)); -} - export function generateIv(): Uint8Array { const iv = new Uint8Array(IvSize); crypto.getRandomValues(iv); diff --git a/src/lib/waku_message/symmetric/index.ts b/src/lib/waku_message/symmetric/index.ts index 76a0b2faa1..58a69acd93 100644 --- a/src/lib/waku_message/symmetric/index.ts +++ b/src/lib/waku_message/symmetric/index.ts @@ -15,10 +15,6 @@ export interface Symmetric { * Proceed with symmetric decryption of `cipherText` value. */ decrypt: (iv: Buffer, key: Buffer, cipherText: Buffer) => Promise; - /** - * Generate a new private key for Symmetric encryption purposes. - */ - generateKeyForSymmetricEnc: () => Buffer; /** * Generate an Initialization Vector (iv) for for Symmetric encryption purposes. */ diff --git a/src/lib/waku_message/symmetric/node.ts b/src/lib/waku_message/symmetric/node.ts index 2ea07f4b5b..8ad95a0cf2 100644 --- a/src/lib/waku_message/symmetric/node.ts +++ b/src/lib/waku_message/symmetric/node.ts @@ -1,6 +1,6 @@ import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; -import { IvSize, SymmetricKeySize, TagSize } from './index'; +import { IvSize, TagSize } from './index'; const Algorithm = 'aes-256-gcm'; @@ -31,10 +31,6 @@ export async function decrypt( return Buffer.concat([a, b]); } -export function generateKeyForSymmetricEnc(): Buffer { - return randomBytes(SymmetricKeySize); -} - export function generateIv(): Buffer { return randomBytes(IvSize); } diff --git a/src/lib/waku_message/version_1.ts b/src/lib/waku_message/version_1.ts index 95fa2922b5..5795d87bce 100644 --- a/src/lib/waku_message/version_1.ts +++ b/src/lib/waku_message/version_1.ts @@ -7,7 +7,7 @@ import * as secp256k1 from 'secp256k1'; import { hexToBuf } from '../utils'; -import { IvSize, symmetric } from './symmetric'; +import { IvSize, symmetric, SymmetricKeySize } from './symmetric'; const FlagsLength = 1; const FlagMask = 3; // 0011 @@ -15,6 +15,8 @@ const IsSignedMask = 4; // 0100 const PaddingTarget = 256; const SignatureLength = 65; +export const PrivateKeySize = 32; + /** * Encode the payload pre-encryption. * @@ -172,14 +174,19 @@ export async function decryptSymmetric( } /** - * Generate a new key. Can be used as a private key for Asymmetric encryption - * or a key for symmetric encryption. + * Generate a new private key to be used for asymmetric encryption. * - * If using Asymmetric encryption, use {@link getPublicKey} to get the - * corresponding Public Key. + * Use {@link getPublicKey} to get the corresponding Public Key. */ export function generatePrivateKey(): Uint8Array { - return randomBytes(32); + return randomBytes(PrivateKeySize); +} + +/** + * Generate a new symmetric key to be used for symmetric encryption. + */ +export function generateSymmetricKey(): Uint8Array { + return randomBytes(SymmetricKeySize); } /** diff --git a/src/lib/waku_store/index.spec.ts b/src/lib/waku_store/index.spec.ts index 03fab4b0cc..0424de60ce 100644 --- a/src/lib/waku_store/index.spec.ts +++ b/src/lib/waku_store/index.spec.ts @@ -13,7 +13,11 @@ import { import { delay } from '../delay'; import { Waku } from '../waku'; import { WakuMessage } from '../waku_message'; -import { generatePrivateKey, getPublicKey } from '../waku_message/version_1'; +import { + generatePrivateKey, + generateSymmetricKey, + getPublicKey, +} from '../waku_message/version_1'; import { Direction } from './history_rpc'; @@ -160,7 +164,7 @@ describe('Waku Store', () => { 'This message is not for and I must not be able to read it'; const privateKey = generatePrivateKey(); - const symKey = generatePrivateKey(); + const symKey = generateSymmetricKey(); const publicKey = getPublicKey(privateKey); const [ From 931a414a3c06a85174038f344eff7c9dedd186f3 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 1 Sep 2021 12:48:51 +1000 Subject: [PATCH 2/9] Allow passing decryption keys in hex string format --- CHANGELOG.md | 1 + src/lib/waku_relay/index.ts | 11 +++++++---- src/lib/waku_store/index.ts | 15 ++++++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f12b8a2d..9d24360ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,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. +- Allow passing decryption keys in hex string format. ### 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/src/lib/waku_relay/index.ts b/src/lib/waku_relay/index.ts index 5844e4f211..440640ee99 100644 --- a/src/lib/waku_relay/index.ts +++ b/src/lib/waku_relay/index.ts @@ -17,6 +17,7 @@ import { InMessage } from 'libp2p-interfaces/src/pubsub'; import { SignaturePolicy } from 'libp2p-interfaces/src/pubsub/signature-policy'; import PeerId from 'peer-id'; +import { hexToBuf } from '../utils'; import { CreateOptions, DefaultPubSubTopic } from '../waku'; import { WakuMessage } from '../waku_message'; @@ -128,17 +129,19 @@ export class WakuRelay extends Gossipsub { * of messages received on the given content topic. This can either be a * private key for asymmetric encryption or a symmetric key. Waku relay will * attempt to decrypt messages using both methods. + * Strings must be in hex format. */ - addDecryptionKey(privateKey: Uint8Array): void { - this.decryptionKeys.add(privateKey); + addDecryptionKey(key: Uint8Array | string): void { + this.decryptionKeys.add(hexToBuf(key)); } /** * Delete a decryption key to attempt decryption of messages received on the * given content topic. + * Strings must be in hex format. */ - deleteDecryptionKey(privateKey: Uint8Array): void { - this.decryptionKeys.delete(privateKey); + deleteDecryptionKey(key: Uint8Array | string): void { + this.decryptionKeys.delete(hexToBuf(key)); } /** diff --git a/src/lib/waku_store/index.ts b/src/lib/waku_store/index.ts index 43b93aa0cc..40fadb83e5 100644 --- a/src/lib/waku_store/index.ts +++ b/src/lib/waku_store/index.ts @@ -8,6 +8,7 @@ import PeerId from 'peer-id'; import { HistoryResponse_Error } from '../../proto/waku/v2/store'; import { getPeersForProtocol, selectRandomPeer } from '../select_peer'; +import { hexToBuf } from '../utils'; import { DefaultPubSubTopic } from '../waku'; import { WakuMessage } from '../waku_message'; @@ -43,7 +44,7 @@ export interface QueryOptions { pageSize?: number; timeFilter?: TimeFilter; callback?: (messages: WakuMessage[]) => void; - decryptionKeys?: Uint8Array[]; + decryptionKeys?: Array; } /** @@ -114,6 +115,13 @@ export class WakuStore { const connection = this.libp2p.connectionManager.get(peer.id); if (!connection) throw 'Failed to get a connection to the peer'; + const decryptionKeys: Uint8Array[] = []; + if (opts.decryptionKeys) { + opts.decryptionKeys.forEach((key) => { + decryptionKeys.push(hexToBuf(key)); + }); + } + const messages: WakuMessage[] = []; let cursor = undefined; while (true) { @@ -154,10 +162,7 @@ export class WakuStore { const pageMessages: WakuMessage[] = []; await Promise.all( response.messages.map(async (protoMsg) => { - const msg = await WakuMessage.decodeProto( - protoMsg, - opts.decryptionKeys - ); + const msg = await WakuMessage.decodeProto(protoMsg, decryptionKeys); if (msg) { messages.push(msg); From f4cae60ef8f14b80925ba427137962f2a4543f7e Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 1 Sep 2021 12:35:55 +1000 Subject: [PATCH 3/9] Fix documentation --- src/lib/waku_relay/index.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/lib/waku_relay/index.ts b/src/lib/waku_relay/index.ts index 440640ee99..4a4b0f7b32 100644 --- a/src/lib/waku_relay/index.ts +++ b/src/lib/waku_relay/index.ts @@ -65,9 +65,6 @@ export class WakuRelay extends Gossipsub { heartbeat: RelayHeartbeat; pubSubTopic: string; - /** - * Decryption private keys to use to attempt decryption of incoming messages. - */ public decryptionKeys: Set; /** @@ -125,10 +122,10 @@ export class WakuRelay extends Gossipsub { } /** - * Register a decryption private key or symmetric key to attempt decryption - * of messages received on the given content topic. This can either be a - * private key for asymmetric encryption or a symmetric key. Waku relay will - * attempt to decrypt messages using both methods. + * Register a decryption key to attempt decryption of received messages. + * This can either be a private key for asymmetric encryption or a symmetric + * key. `WakuRelay` will attempt to decrypt messages using both methods. + * * Strings must be in hex format. */ addDecryptionKey(key: Uint8Array | string): void { @@ -136,8 +133,9 @@ export class WakuRelay extends Gossipsub { } /** - * Delete a decryption key to attempt decryption of messages received on the - * given content topic. + * Delete a decryption key that was used to attempt decryption of received + * messages. + * * Strings must be in hex format. */ deleteDecryptionKey(key: Uint8Array | string): void { From 911ce5bab759dc9c76e26f9082cba534cdca0a25 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 1 Sep 2021 13:55:46 +1000 Subject: [PATCH 4/9] Allow passing decryption keys to `WakuStore` instance To avoid having to pass them at every `queryHistory` call. --- CHANGELOG.md | 1 + src/lib/waku_store/index.spec.ts | 4 +++- src/lib/waku_store/index.ts | 27 ++++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d24360ca7..ed2f4974ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Examples (eth-pm): Encrypt Public Key Messages using symmetric encryption. - Guides: Encrypt messages using Waku Message Version 1. - Allow passing decryption keys in hex string format. +- Allow passing decryption keys to `WakuStore` instance to avoid having to pass them at every `queryHistory` call. ### 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/src/lib/waku_store/index.spec.ts b/src/lib/waku_store/index.spec.ts index 0424de60ce..b11daf570f 100644 --- a/src/lib/waku_store/index.spec.ts +++ b/src/lib/waku_store/index.spec.ts @@ -236,9 +236,11 @@ describe('Waku Store', () => { storePeers = waku2.store.peers; } + waku2.store.addDecryptionKey(symKey); + dbg('Retrieve messages from store'); const messages = await waku2.store.queryHistory([], { - decryptionKeys: [privateKey, symKey], + decryptionKeys: [privateKey], }); expect(messages?.length).eq(3); diff --git a/src/lib/waku_store/index.ts b/src/lib/waku_store/index.ts index 40fadb83e5..ed3d7ca513 100644 --- a/src/lib/waku_store/index.ts +++ b/src/lib/waku_store/index.ts @@ -52,6 +52,7 @@ export interface QueryOptions { */ export class WakuStore { pubSubTopic: string; + public decryptionKeys: Set; constructor(public libp2p: Libp2p, options?: CreateOptions) { if (options?.pubSubTopic) { @@ -59,6 +60,8 @@ export class WakuStore { } else { this.pubSubTopic = DefaultPubSubTopic; } + + this.decryptionKeys = new Set(); } /** @@ -115,7 +118,7 @@ export class WakuStore { const connection = this.libp2p.connectionManager.get(peer.id); if (!connection) throw 'Failed to get a connection to the peer'; - const decryptionKeys: Uint8Array[] = []; + const decryptionKeys = Array.from(this.decryptionKeys.values()); if (opts.decryptionKeys) { opts.decryptionKeys.forEach((key) => { decryptionKeys.push(hexToBuf(key)); @@ -198,6 +201,28 @@ export class WakuStore { } } + /** + * Register a decryption key to attempt decryption of messages received in any + * subsequent [[queryHistory]] call. This can either be a private key for + * asymmetric encryption or a symmetric key. [[WakuStore]] will attempt to + * decrypt messages using both methods. + * + * Strings must be in hex format. + */ + addDecryptionKey(key: Uint8Array | string): void { + this.decryptionKeys.add(hexToBuf(key)); + } + + /** + * Delete a decryption key that was used to attempt decryption of messages + * received in subsequent [[queryHistory]] calls. + * + * Strings must be in hex format. + */ + deleteDecryptionKey(key: Uint8Array | string): void { + this.decryptionKeys.delete(hexToBuf(key)); + } + /** * Returns known peers from the address book (`libp2p.peerStore`) that support * store protocol. Waku may or may not be currently connected to these peers. From cfb97b6bde8f0057fc8f74f37f9633c53d3e2d54 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 1 Sep 2021 15:02:28 +1000 Subject: [PATCH 5/9] Allow passing decryption keys to `Waku` instance Keys can be set at creation or at run time. They are passed to `WakuStore` and `WakuRelay` instances. --- CHANGELOG.md | 1 + src/lib/waku.spec.ts | 77 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/waku.ts | 26 +++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2f4974ac..9f352b81d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Guides: Encrypt messages using Waku Message Version 1. - Allow passing decryption keys in hex string format. - Allow passing decryption keys to `WakuStore` instance to avoid having to pass them at every `queryHistory` call. +- Allow passing decryption keys to `Waku` instance to avoid having to pass them to both `WakuRelay` and `WakuStore`. ### 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/src/lib/waku.spec.ts b/src/lib/waku.spec.ts index 797700bf3d..528d7649df 100644 --- a/src/lib/waku.spec.ts +++ b/src/lib/waku.spec.ts @@ -12,6 +12,10 @@ import { } from '../test_utils/'; import { Waku } from './waku'; +import { WakuMessage } from './waku_message'; +import { generateSymmetricKey } from './waku_message/version_1'; + +const TestContentTopic = '/test/1/waku/utf8'; describe('Waku Dial', function () { let waku: Waku; @@ -130,3 +134,76 @@ describe('Waku Dial', function () { }); }); }); + +describe('Decryption Keys', () => { + afterEach(function () { + if (this.currentTest?.state === 'failed') { + console.log(`Test failed, log file name is ${makeLogFileName(this)}`); + } + }); + + let waku1: Waku; + let waku2: Waku; + beforeEach(async function () { + [waku1, waku2] = await Promise.all([ + Waku.create({ staticNoiseKey: NOISE_KEY_1 }), + Waku.create({ + staticNoiseKey: NOISE_KEY_2, + libp2p: { addresses: { listen: ['/ip4/0.0.0.0/tcp/0/ws'] } }, + }), + ]); + + waku1.addPeerToAddressBook(waku2.libp2p.peerId, waku2.libp2p.multiaddrs); + + await Promise.all([ + new Promise((resolve) => + waku1.libp2p.pubsub.once('pubsub:subscription-change', () => + resolve(null) + ) + ), + new Promise((resolve) => + waku2.libp2p.pubsub.once('pubsub:subscription-change', () => + resolve(null) + ) + ), + ]); + }); + + afterEach(async function () { + this.timeout(5000); + await waku1.stop(); + await waku2.stop(); + }); + + it('Used by Waku Relay', async function () { + this.timeout(10000); + + const symKey = generateSymmetricKey(); + + waku2.addDecryptionKey(symKey); + + const messageText = 'Message is encrypted'; + const messageTimestamp = new Date('1995-12-17T03:24:00'); + const message = await WakuMessage.fromUtf8String( + messageText, + TestContentTopic, + { + timestamp: messageTimestamp, + symKey, + } + ); + + const receivedMsgPromise: Promise = new Promise((resolve) => { + waku2.relay.addObserver(resolve); + }); + + await waku1.relay.send(message); + + const receivedMsg = await receivedMsgPromise; + + expect(receivedMsg.contentTopic).to.eq(message.contentTopic); + expect(receivedMsg.version).to.eq(message.version); + expect(receivedMsg.payloadAsUtf8).to.eq(messageText); + expect(receivedMsg.timestamp?.valueOf()).to.eq(messageTimestamp.valueOf()); + }); +}); diff --git a/src/lib/waku.ts b/src/lib/waku.ts index 9d7d3e9cd5..729ef77066 100644 --- a/src/lib/waku.ts +++ b/src/lib/waku.ts @@ -93,6 +93,7 @@ export interface CreateOptions { * {@link CreateOptions.libp2p}. */ bootstrap?: boolean | string[] | (() => string[] | Promise); + decryptionKeys?: Array; } export class Waku { @@ -133,6 +134,8 @@ export class Waku { libp2p.connectionManager.on('peer:disconnect', (connection: Connection) => { this.stopKeepAlive(connection.remotePeer); }); + + options?.decryptionKeys?.forEach(this.addDecryptionKey); } /** @@ -270,6 +273,29 @@ export class Waku { return this.libp2p.stop(); } + /** + * Register a decryption key to attempt decryption of messages received via + * [[WakuRelay]] and [[WakuStore]]. This can either be a private key for + * asymmetric encryption or a symmetric key. + * + * Strings must be in hex format. + */ + addDecryptionKey(key: Uint8Array | string): void { + this.relay.addDecryptionKey(key); + this.store.addDecryptionKey(key); + } + + /** + * Delete a decryption key that was used to attempt decryption of messages + * received via [[WakuRelay]] or [[WakuStore]]. + * + * Strings must be in hex format. + */ + deleteDecryptionKey(key: Uint8Array | string): void { + this.relay.deleteDecryptionKey(key); + this.store.deleteDecryptionKey(key); + } + /** * Return the local multiaddr with peer id on which libp2p is listening. * @throws if libp2p is not listening on localhost From 50fb533e442623168a9f13fea0d04e68da35e310 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Wed, 1 Sep 2021 15:49:46 +1000 Subject: [PATCH 6/9] test: Increase timeouts + add debug logs --- src/lib/waku_relay/index.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/waku_relay/index.spec.ts b/src/lib/waku_relay/index.spec.ts index b073310bc8..c03a187540 100644 --- a/src/lib/waku_relay/index.spec.ts +++ b/src/lib/waku_relay/index.spec.ts @@ -29,6 +29,9 @@ describe('Waku Relay', () => { let waku1: Waku; let waku2: Waku; beforeEach(async function () { + this.timeout(10000); + + log('Starting JS Waku instances'); [waku1, waku2] = await Promise.all([ Waku.create({ staticNoiseKey: NOISE_KEY_1 }), Waku.create({ @@ -36,9 +39,10 @@ describe('Waku Relay', () => { libp2p: { addresses: { listen: ['/ip4/0.0.0.0/tcp/0/ws'] } }, }), ]); - + log("Instances started, adding waku2 to waku1's address book"); waku1.addPeerToAddressBook(waku2.libp2p.peerId, waku2.libp2p.multiaddrs); + log('Wait for mutual pubsub subscription'); await Promise.all([ new Promise((resolve) => waku1.libp2p.pubsub.once('pubsub:subscription-change', () => @@ -51,6 +55,7 @@ describe('Waku Relay', () => { ) ), ]); + log('before each hook done'); }); afterEach(async function () { @@ -60,11 +65,13 @@ describe('Waku Relay', () => { }); it('Subscribe', async function () { + log('Getting subscribers'); const subscribers1 = waku1.libp2p.pubsub.getSubscribers(DefaultPubSubTopic); const subscribers2 = waku2.libp2p.pubsub.getSubscribers(DefaultPubSubTopic); + log('Asserting mutual subscription'); expect(subscribers1).to.contain(waku2.libp2p.peerId.toB58String()); expect(subscribers2).to.contain(waku1.libp2p.peerId.toB58String()); }); From 75f605a676f1d93d61fa294332830c7c93267dbb Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 2 Sep 2021 13:41:44 +1000 Subject: [PATCH 7/9] Improve error text To make it clearer what the issue is. --- src/lib/waku_store/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/waku_store/index.ts b/src/lib/waku_store/index.ts index ed3d7ca513..1b2286186f 100644 --- a/src/lib/waku_store/index.ts +++ b/src/lib/waku_store/index.ts @@ -108,13 +108,15 @@ export class WakuStore { let peer; if (opts.peerId) { peer = this.libp2p.peerStore.get(opts.peerId); - if (!peer) throw 'Peer is unknown'; + if (!peer) + throw `Failed to retrieve connection details for provided peer in peer store: ${opts.peerId.toB58String()}`; } else { peer = this.randomPeer; + if (!peer) + throw 'Failed to find known peer that registers waku store protocol'; } - if (!peer) throw 'No peer available'; if (!peer.protocols.includes(StoreCodec)) - throw 'Peer does not register waku store protocol'; + throw `Peer does not register waku store protocol: ${peer.id.toB58String()}`; const connection = this.libp2p.connectionManager.get(peer.id); if (!connection) throw 'Failed to get a connection to the peer'; From 87fd5ac17196cf75cfc3dcfeac6221a099442753 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 2 Sep 2021 13:48:28 +1000 Subject: [PATCH 8/9] Update guide to pass decryption keys to the `Waku` instance Make the guide and API simpler as there is no need to learn/use both `WakuStore` and `WakuRelay` APIs to decrypt messages. --- guides/encrypt-messages-version-1.md | 114 ++++++++------------------- 1 file changed, 35 insertions(+), 79 deletions(-) diff --git a/guides/encrypt-messages-version-1.md b/guides/encrypt-messages-version-1.md index 8f00634aea..d9fcc6176c 100644 --- a/guides/encrypt-messages-version-1.md +++ b/guides/encrypt-messages-version-1.md @@ -28,7 +28,7 @@ If key recovery is important for your dApp, then check out 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? +## Which encryption method should I use? Whether you should use symmetric or asymmetric encryption depends on your use case. @@ -59,12 +59,12 @@ When Alice sends an encrypted message for Bob, only Bob can decrypt it. ### Generate Key To use symmetric encryption, you first need to generate a key. -You can simply use `generatePrivateKey` for secure key generation: +Use `generateSymmetricKey` for secure key generation: ```js import { generateSymmetricKey } from 'js-waku'; -const key = generateSymmetricKey(); +const symmetricKey = generateSymmetricKey(); ``` ### Encrypt Message @@ -80,7 +80,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: symmetricKey }); ``` @@ -92,56 +92,34 @@ 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. +To decrypt messages, +whether they are received over Waku Relay or using Waku Store, +add the symmetric key as a decryption key to your Waku instance. ```js -waku.relay.addDecryptionKey(key); +waku.addDecryptionKey(symmetricKey); +``` +Alternatively, you can pass the key when creating the instance: + +```js +import { Waku } from 'js-waku'; + +const waku = Waku.create({ decryptionKeys: [symmetricKey] }); ``` -`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. +It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption. 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: [key] -}); - -// Using callback syntax -waku.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: +To use asymmetric encryption, you first need to generate a private key and calculate the corresponding public key. +Use `generatePrivateKey` for secure key generation: ```js import { generatePrivateKey, getPublicKey } from 'js-waku'; @@ -181,52 +159,30 @@ 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. +To decrypt messages, +whether they are received over Waku Relay or using Waku Store, +add the private key as a decryption key to your Waku instance. ```js -waku.relay.addDecryptionKey(privateKey); +waku.addDecryptionKey(privateKey); +``` +Alternatively, you can pass the key when creating the instance: + +```js +import { Waku } from 'js-waku'; + +const waku = Waku.create({ decryptionKeys: [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. +It will attempt to decrypt any message it receives using the key, for both symmetric and asymmetric encryption. 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), @@ -251,16 +207,16 @@ If a message was not successfully decrypted, then it will be dropped from the re 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], { - decryptionKeys: [privateKey] -}); +import { Waku } from 'js-waku'; + +const waku = Waku.create({ decryptionKeys: [privateKey] }); + +const messages = await waku.store.queryHistory([contentTopic]); 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]); From 3c0884a96ea1b2cefd47f52186b904e3892d493e Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Thu, 2 Sep 2021 15:27:39 +1000 Subject: [PATCH 9/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f352b81d1..5d69a3d8a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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. - Renamed and promote the usage of `generateSymmetricKey()` to generate random symmetric keys. +- Improved errors thrown by `WakuStore.queryHistory`. ### Fixed - Buffer concat error when using symmetric encryption in the browser.