diff --git a/examples/eth-dm/package.json b/examples/eth-dm/package.json index 5825c14513..5e03d6e1bb 100644 --- a/examples/eth-dm/package.json +++ b/examples/eth-dm/package.json @@ -56,6 +56,7 @@ "@ethereum-waffle/jest": "^3.2.2", "@ethersproject/shims": "^5.3.0", "ethereum-waffle": "^3.3.0", + "npm-run-all": "^4.1.5", "react-native-get-random-values": "^1.7.0" } } diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index 64a985871b..c6d81b64a9 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -2,20 +2,22 @@ import 'react-native-get-random-values'; import '@ethersproject/shims'; -import React, { useEffect, useState } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; import './App.css'; import { Environment, getStatusFleetNodes, Waku, WakuMessage } from 'js-waku'; import { ethers } from 'ethers'; import { Web3Provider } from '@ethersproject/providers'; import { createPublicKeyMessage, - PublicKeyMessage, generateEthDmKeyPair, KeyPair, validatePublicKeyMessage, } from './crypto'; +import * as EthCrypto from 'eth-crypto'; +import { DirectMessage, PublicKeyMessage } from './messages'; const PublicKeyContentTopic = '/eth-dm/1/public-key/json'; +const DirectMessageContentTopic = '/eth-dm/1/direct-message/json'; declare let window: any; @@ -24,6 +26,7 @@ function App() { const [provider, setProvider] = useState(); const [ethDmKeyPair, setEthDmKeyPair] = useState(); const [publicKeyMsg, setPublicKeyMsg] = useState(); + const [publicKeys, setPublicKeys] = useState>(new Map()); useEffect(() => { if (provider) return; @@ -60,9 +63,14 @@ function App() { }); }; + const observerPublicKeyMessage = handlePublicKeyMessage.bind( + {}, + setPublicKeys + ); + useEffect(() => { if (!waku) return; - waku.relay.addObserver(handlePublicKeyMessage, [PublicKeyContentTopic]); + waku.relay.addObserver(observerPublicKeyMessage, [PublicKeyContentTopic]); }); const broadcastPublicKey = () => { @@ -71,7 +79,7 @@ function App() { if (!waku) return; if (publicKeyMsg) { - const wakuMsg = createWakuMessage(publicKeyMsg); + const wakuMsg = encodePublicKeyWakuMessage(publicKeyMsg); waku.relay.send(wakuMsg).catch((e) => { console.error('Failed to send Public Key Message'); }); @@ -79,7 +87,7 @@ function App() { createPublicKeyMessage(provider.getSigner(), ethDmKeyPair.publicKey) .then((msg) => { setPublicKeyMsg(msg); - const wakuMsg = createWakuMessage(msg); + const wakuMsg = encodePublicKeyWakuMessage(msg); waku.relay.send(wakuMsg).catch((e) => { console.error('Failed to send Public Key Message'); }); @@ -90,6 +98,17 @@ function App() { } }; + const sendDummyMessage = () => { + publicKeys.forEach(async (publicKey, address) => { + const msg = await encodeEncryptedWakuMessage( + 'Here is a secret message', + publicKey, + address + ); + await waku?.relay.send(msg); + }); + }; + return (
@@ -99,6 +118,12 @@ function App() { +
); @@ -128,26 +153,48 @@ function getNodes() { } } -function createWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { +function encodePublicKeyWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { const payload = encode(ethDmMsg); return WakuMessage.fromBytes(payload, PublicKeyContentTopic); } -function handlePublicKeyMessage(msg: WakuMessage) { - if (msg.payload) { - const publicKeyMsg = decode(msg.payload); - console.log('publicKeyMsg', publicKeyMsg); - const res = validatePublicKeyMessage(publicKeyMsg); - console.log(`Public Key Message Received, valid: ${res}`, publicKeyMsg); - } +async function encodeEncryptedWakuMessage( + message: string, + publicKey: string, + address: string +): Promise { + const encryptedMsg = await EthCrypto.encryptWithPublicKey(publicKey, message); + + const directMsg: DirectMessage = { + toAddress: address, + encMessage: encryptedMsg, + }; + + const payload = encode(directMsg); + return WakuMessage.fromBytes(payload, DirectMessageContentTopic); } -function encode(msg: PublicKeyMessage): Buffer { +function handlePublicKeyMessage( + setter: Dispatch>>, + msg: WakuMessage +) { + if (!msg.payload) return; + const publicKeyMsg: PublicKeyMessage = decode(msg.payload); + const res = validatePublicKeyMessage(publicKeyMsg); + console.log(`Public Key Message Received, valid: ${res}`, publicKeyMsg); + + setter((prevPks: Map) => { + prevPks.set(publicKeyMsg.ethAddress, publicKeyMsg.ethDmPublicKey); + return new Map(prevPks); + }); +} + +function encode(msg: T): Buffer { const jsonStr = JSON.stringify(msg); return Buffer.from(jsonStr, 'utf-8'); } -function decode(bytes: Uint8Array): PublicKeyMessage { +function decode(bytes: Uint8Array): T { const buf = Buffer.from(bytes); const str = buf.toString('utf-8'); return JSON.parse(str); diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index f9eb9a55ba..315fde89fb 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -6,6 +6,7 @@ import * as EthCrypto from 'eth-crypto'; import { toUtf8Bytes } from '@ethersproject/strings'; import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; +import { PublicKeyMessage } from './messages'; const Salt = 'Salt for Eth-Dm, do not share a signature of this message or others could decrypt your messages'; @@ -30,15 +31,6 @@ export async function generateEthDmKeyPair( return keys; } -/** - * Message used to communicate the Eth-Dm public key linked to a given Ethereum account - */ -export interface PublicKeyMessage { - ethDmPublicKey: string; - ethAddress: string; - sig: string; -} - /** * Sign the Eth-DM public key with Web3. This can then be published to let other * users know to use this Eth-DM public key to encrypt messages destinated to the diff --git a/examples/eth-dm/src/messages.ts b/examples/eth-dm/src/messages.ts new file mode 100644 index 0000000000..05d771387b --- /dev/null +++ b/examples/eth-dm/src/messages.ts @@ -0,0 +1,18 @@ +import * as EthCrypto from 'eth-crypto'; + +/** + * Message used to communicate the Eth-Dm public key linked to a given Ethereum account + */ +export interface PublicKeyMessage { + ethDmPublicKey: string; + ethAddress: string; + sig: string; +} + +/** + * Direct Encrypted Message used for private communication over the Waku network. + */ +export interface DirectMessage { + toAddress: string; + encMessage: EthCrypto.Encrypted; +}