From 3bd6b76916f5466dc7f07775038c98d5598554e1 Mon Sep 17 00:00:00 2001 From: Franck Royer Date: Fri, 11 Jun 2021 15:33:29 +1000 Subject: [PATCH] Publish public key to waku network --- examples/eth-dm/package.json | 4 +- examples/eth-dm/src/AddressInput.tsx | 9 ++-- examples/eth-dm/src/App.tsx | 74 +++++++++++++++++++++++++--- examples/eth-dm/src/crypto.test.ts | 24 +++++---- examples/eth-dm/src/crypto.ts | 53 ++++++++++++++------ examples/eth-dm/tsconfig.json | 10 +--- examples/eth-dm/waffle.json | 1 - 7 files changed, 128 insertions(+), 47 deletions(-) diff --git a/examples/eth-dm/package.json b/examples/eth-dm/package.json index a479fe4009..5825c14513 100644 --- a/examples/eth-dm/package.json +++ b/examples/eth-dm/package.json @@ -54,6 +54,8 @@ }, "devDependencies": { "@ethereum-waffle/jest": "^3.2.2", - "ethereum-waffle": "^3.3.0" + "@ethersproject/shims": "^5.3.0", + "ethereum-waffle": "^3.3.0", + "react-native-get-random-values": "^1.7.0" } } diff --git a/examples/eth-dm/src/AddressInput.tsx b/examples/eth-dm/src/AddressInput.tsx index e7c81176aa..775ff218f8 100644 --- a/examples/eth-dm/src/AddressInput.tsx +++ b/examples/eth-dm/src/AddressInput.tsx @@ -1,20 +1,19 @@ import { useState } from 'react'; - export interface Props { sendMessage: (message: string) => void; } function MessageInput(props: Props) { - const [inputText, setInputText] = useState(""); + const [inputText, setInputText] = useState(''); const onChange = (event: React.ChangeEvent) => { setInputText(event.target.value); }; - const onKeyDown = (event: { key: string; }) => { - if (event.key === "Enter") { + const onKeyDown = (event: { key: string }) => { + if (event.key === 'Enter') { props.sendMessage(inputText); - setInputText(""); + setInputText(''); } }; diff --git a/examples/eth-dm/src/App.tsx b/examples/eth-dm/src/App.tsx index e42fe9f168..500be91e77 100644 --- a/examples/eth-dm/src/App.tsx +++ b/examples/eth-dm/src/App.tsx @@ -1,26 +1,81 @@ +import 'react-native-get-random-values'; + +import '@ethersproject/shims'; + import React, { 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, +} from './crypto'; + +const ContentTopic = '/eth-dm/1/public-key/json'; declare let window: any; function App() { const [waku, setWaku] = useState(); - // const [provider, setProvider] = useState(new ethers.providers.Web3Provider(window.ethereum)); + const [provider, setProvider] = useState(); + const [ethDmKeyPair, setEthDmKeyPair] = useState(); useEffect(() => { - if (!waku) { - initWaku().then((wakuNode) => { + if (provider) return; + const _provider = new ethers.providers.Web3Provider(window.ethereum); + setProvider(_provider); + }, [provider]); + + useEffect(() => { + if (waku) return; + initWaku() + .then((wakuNode) => { setWaku(wakuNode); - }).catch(e => { + }) + .catch((e) => { console.error('Failed to initiate Waku', e); }); - } }, [waku]); + useEffect(() => { + if (ethDmKeyPair) return; + if (!provider) return; + + generateEthDmKeyPair(provider.getSigner()) + .then((keyPair) => { + setEthDmKeyPair(keyPair); + }) + .catch((e) => { + console.error('Failed to generate Key Pair', e); + }); + }, [ethDmKeyPair, provider]); + + const onClick = () => { + if (!ethDmKeyPair) return; + if (!provider) return; + if (!waku) return; + + createPublicKeyMessage(provider.getSigner(), ethDmKeyPair.publicKey) + .then((msg) => { + const wakuMsg = createWakuMessage(msg); + waku.relay.send(wakuMsg).catch((e) => { + console.error('Failed to send Public Key Message'); + }); + }) + .catch((e) => { + console.error('Failed to creat Eth-Dm Publication message', e); + }); + }; + return ( -
-
+
+
+
); @@ -49,3 +104,8 @@ function getNodes() { return getStatusFleetNodes(Environment.Prod); } } + +function createWakuMessage(ethDmMsg: PublicKeyMessage): WakuMessage { + const payload = Buffer.from(JSON.stringify(ethDmMsg)); + return WakuMessage.fromBytes(payload, ContentTopic); +} diff --git a/examples/eth-dm/src/crypto.test.ts b/examples/eth-dm/src/crypto.test.ts index f69c324d3d..82528f8a1e 100644 --- a/examples/eth-dm/src/crypto.test.ts +++ b/examples/eth-dm/src/crypto.test.ts @@ -1,24 +1,30 @@ +import 'react-native-get-random-values'; + +import '@ethersproject/shims'; + +import { ethers } from 'ethers'; + import { - createEthDmPublicationMessage, + createPublicKeyMessage, generateEthDmKeyPair, - verifyEthDmPublicKey + verifyEthDmPublicKey, } from './crypto'; -import { MockProvider } from "ethereum-waffle"; +import { MockProvider } from 'ethereum-waffle'; import { waffleJest } from '@ethereum-waffle/jest'; expect.extend(waffleJest); test('Signature of Eth-DM key is verifiable', async () => { - console.log("get wallet") + console.log('get wallet'); const [wallet] = new MockProvider().getWallets(); - console.log("Generate Keys") + console.log('Generate Keys'); const ethDmKeys = await generateEthDmKeyPair(wallet); - console.log("Create EthDm message") - const ethDmMsg = await createEthDmPublicationMessage(wallet, ethDmKeys.publicKey); + console.log('Create EthDm message'); + const ethDmMsg = await createPublicKeyMessage(wallet, ethDmKeys.publicKey); - console.log("Verify EthDm message") - const res = verifyEthDmPublicKey(ethDmMsg) + console.log('Verify EthDm message'); + const res = verifyEthDmPublicKey(ethDmMsg); expect(res).toBe(true); }); diff --git a/examples/eth-dm/src/crypto.ts b/examples/eth-dm/src/crypto.ts index 3f75d5e2b9..c967bbf0de 100644 --- a/examples/eth-dm/src/crypto.ts +++ b/examples/eth-dm/src/crypto.ts @@ -1,17 +1,30 @@ +import 'react-native-get-random-values'; + +import '@ethersproject/shims'; + import * as EthCrypto from 'eth-crypto'; import { toUtf8Bytes } from '@ethersproject/strings'; import { ethers } from 'ethers'; import { Signer } from '@ethersproject/abstract-signer'; -const Salt = EthCrypto.hash.keccak256("Salt for Eth-Dm, do not share a signature of this message or other could decrypt your messages"); +const Salt = + 'Salt for Eth-Dm, do not share a signature of this message or others could decrypt your messages'; + +export interface KeyPair { + privateKey: string; + publicKey: string; + address: string; +} /** - * Use the signature of the Salt (keccak256 hash of the sentence "Salt for eth-dm..." as + * Use the signature of the Salt ("Salt for eth-dm...") as * the entropy for the EthCrypto keypair. Note that the entropy is hashed with keccak256 * to make the private key. */ -export async function generateEthDmKeyPair(web3Signer: Signer) { - const signature = await web3Signer.signMessage(Salt) +export async function generateEthDmKeyPair( + web3Signer: Signer +): Promise { + const signature = await web3Signer.signMessage(Salt); const entropy = Buffer.from(toUtf8Bytes(signature)); const keys = EthCrypto.createIdentity(entropy); return keys; @@ -20,7 +33,7 @@ export async function generateEthDmKeyPair(web3Signer: Signer) { /** * Message used to communicate the Eth-Dm public key linked to a given Ethereum account */ -export interface EthDmPublicationMessage { +export interface PublicKeyMessage { ethDmPublicKey: string; ethAddress: string; sig: string; @@ -31,21 +44,28 @@ export interface EthDmPublicationMessage { * users know to use this Eth-DM public key to encrypt messages destinated to the * Web3 account holder (ie, Ethereum Address holder). */ -export async function createEthDmPublicationMessage(web3Signer: Signer, ethDmPublicKey: string): Promise { +export async function createPublicKeyMessage( + web3Signer: Signer, + ethDmPublicKey: string +): Promise { const ethAddress = await web3Signer.getAddress(); - const sig = await web3Signer.signMessage(formatEthDmPublicKeyForSig(ethDmPublicKey)) + const sig = await web3Signer.signMessage( + formatPublicKeyForSignature(ethDmPublicKey) + ); return { ethDmPublicKey, ethAddress, sig }; } /** * Verifies that the EthDm Public Key was signed by the holder of the given Ethereum address. */ -export function verifyEthDmPublicKey(msg: EthDmPublicationMessage): boolean { +export function verifyEthDmPublicKey(msg: PublicKeyMessage): boolean { try { - const sigAddress = ethers.utils.verifyMessage(formatEthDmPublicKeyForSig(msg.ethDmPublicKey), msg.sig); - return sigAddress == msg.ethAddress; - } - catch (e) { + const sigAddress = ethers.utils.verifyMessage( + formatPublicKeyForSignature(msg.ethDmPublicKey), + msg.sig + ); + return sigAddress === msg.ethAddress; + } catch (e) { return false; } } @@ -57,8 +77,9 @@ export function verifyEthDmPublicKey(msg: EthDmPublicationMessage): boolean { * The usage of the object helps ensure the signature is only used in an Eth-DM * context. */ -function formatEthDmPublicKeyForSig(ethDmPublicKey: string): string { - return EthCrypto.hash.keccak256(JSON.stringify({ - ethDmPublicKey - })) +function formatPublicKeyForSignature(ethDmPublicKey: string): string { + const txt = JSON.stringify({ + ethDmPublicKey, + }); + return txt; } diff --git a/examples/eth-dm/tsconfig.json b/examples/eth-dm/tsconfig.json index a273b0cfc0..9d379a3c4a 100644 --- a/examples/eth-dm/tsconfig.json +++ b/examples/eth-dm/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -20,7 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ] + "include": ["src"] } diff --git a/examples/eth-dm/waffle.json b/examples/eth-dm/waffle.json index a658e1b11d..86c580f80b 100644 --- a/examples/eth-dm/waffle.json +++ b/examples/eth-dm/waffle.json @@ -4,4 +4,3 @@ "sourceDirectory": "./contracts", "outputDirectory": "./contracts_build" } -