Publish public key to waku network

This commit is contained in:
Franck Royer 2021-06-11 15:33:29 +10:00
parent ee38388326
commit 3bd6b76916
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
7 changed files with 128 additions and 47 deletions

View File

@ -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"
}
}

View File

@ -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<HTMLInputElement>) => {
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('');
}
};

View File

@ -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<Waku>();
// const [provider, setProvider] = useState(new ethers.providers.Web3Provider(window.ethereum));
const [provider, setProvider] = useState<Web3Provider>();
const [ethDmKeyPair, setEthDmKeyPair] = useState<KeyPair>();
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 (
<div className='App'>
<header className='App-header'>
<div className="App">
<header className="App-header">
<button onClick={onClick} disabled={!ethDmKeyPair || !waku}>
Broadcast Eth-DM Public Key
</button>
</header>
</div>
);
@ -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);
}

View File

@ -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);
});

View File

@ -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<KeyPair> {
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<EthDmPublicationMessage> {
export async function createPublicKeyMessage(
web3Signer: Signer,
ethDmPublicKey: string
): Promise<PublicKeyMessage> {
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;
}

View File

@ -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"]
}

View File

@ -4,4 +4,3 @@
"sourceDirectory": "./contracts",
"outputDirectory": "./contracts_build"
}