Merge pull request #227 from status-im/179-eth-dm-encrypt

This commit is contained in:
Franck Royer 2021-07-12 17:27:06 +10:00 committed by GitHub
commit b8296648e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 68 additions and 2075 deletions

View File

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Breaking**: `WakuMessage` constructor is now private, `from*` and `decode*` function should be used.
- `WakuMessage` version 1 is partially supported, enabling asymmetrical encryption and signature of messages;
this can be done by passing keys to `WakuMessage.from*` and `WakuMessage.decode*` methods.
- Examples (eth-dm): Use Waku Message version 1 encryption scheme instead of `eth-crypto`.
### Fixed
- Disable `keepAlive` if set to `0`.

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,6 @@
"@types/jest": "^26.0.15",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"eth-crypto": "^1.9.0",
"ethers": "^5.2.0",
"fontsource-roboto": "^4.0.0",
"js-waku": "../../build/main",

View File

@ -124,6 +124,20 @@ function App() {
};
}, [waku, address]);
useEffect(() => {
if (!waku) return;
if (!ethDmKeyPair) return;
waku.relay.addDecryptionPrivateKey(ethDmKeyPair.privateKey);
return function cleanUp() {
if (!waku) return;
if (!ethDmKeyPair) return;
waku.relay.deleteDecryptionPrivateKey(ethDmKeyPair.privateKey);
};
}, [waku, ethDmKeyPair]);
useEffect(() => {
if (!waku) return;
if (!ethDmKeyPair) return;
@ -147,9 +161,11 @@ function App() {
};
}, [waku, address, ethDmKeyPair]);
let peers = 0;
let relayPeers = 0;
let lightPushPeers = 0;
if (waku) {
peers = waku.libp2p.connectionManager.connections.size;
relayPeers = waku.relay.getPeers().size;
lightPushPeers = waku.lightPush.peers.length;
}
let addressDisplay = '';
@ -174,7 +190,7 @@ function App() {
/>
</IconButton>
<Typography className={classes.peers} aria-label="connected-peers">
{peers} peer{peers && peers > 1 ? 's' : ''}
Peers: {relayPeers} relay, {lightPushPeers} light push
</Typography>
<Typography variant="h6" className={classes.title}>
Ethereum Direct Message

View File

@ -40,9 +40,12 @@ export default function BroadcastPublicKey({
setPublicKeyMsg(msg);
encodePublicKeyWakuMessage(msg)
.then((wakuMsg) => {
waku.lightPush.push(wakuMsg).catch((e) => {
console.error('Failed to send Public Key Message', e);
});
waku.lightPush
.push(wakuMsg)
.then((res) => console.log('Public Key Message pushed', res))
.catch((e) => {
console.error('Failed to send Public Key Message', e);
});
})
.catch((e) => {
console.log(

View File

@ -1,14 +1,17 @@
import '@ethersproject/shims';
import * as EthCrypto from 'eth-crypto';
import { ethers } from 'ethers';
import { Signer } from '@ethersproject/abstract-signer';
import { DirectMessage, PublicKeyMessage } from './messaging/wire';
import { PublicKeyMessage } from './messaging/wire';
import { hexToBuf, equalByteArrays, bufToHex } from 'js-waku/lib/utils';
import {
generatePrivateKey,
getPublicKey,
} from 'js-waku/lib/waku_message/version_1';
export interface KeyPair {
privateKey: string;
publicKey: string;
privateKey: Uint8Array;
publicKey: Uint8Array;
}
/**
@ -17,7 +20,9 @@ export interface KeyPair {
* to make the private key.
*/
export async function generateEthDmKeyPair(): Promise<KeyPair> {
return EthCrypto.createIdentity();
const privateKey = generatePrivateKey();
const publicKey = getPublicKey(privateKey);
return { privateKey, publicKey };
}
/**
@ -27,16 +32,15 @@ export async function generateEthDmKeyPair(): Promise<KeyPair> {
*/
export async function createPublicKeyMessage(
web3Signer: Signer,
ethDmPublicKey: string
ethDmPublicKey: Uint8Array
): Promise<PublicKeyMessage> {
const ethAddress = await web3Signer.getAddress();
const bytesEthDmPublicKey = hexToBuf(ethDmPublicKey);
const signature = await web3Signer.signMessage(
formatPublicKeyForSignature(bytesEthDmPublicKey)
formatPublicKeyForSignature(ethDmPublicKey)
);
return new PublicKeyMessage({
ethDmPublicKey: bytesEthDmPublicKey,
ethDmPublicKey: ethDmPublicKey,
ethAddress: hexToBuf(ethAddress),
signature: hexToBuf(signature),
});
@ -72,20 +76,3 @@ function formatPublicKeyForSignature(ethDmPublicKey: Uint8Array): string {
ethDmPublicKey: bufToHex(ethDmPublicKey),
});
}
/**
* Decrypt a Direct Message using the private key.
*/
export function decryptMessage(
privateKey: string,
directMessage: DirectMessage
) {
return EthCrypto.decryptWithPrivateKey(privateKey, directMessage.encMessage);
}
/**
* Encrypt message with given Public Key
*/
export async function encryptMessage(publicKey: string, message: string) {
return await EthCrypto.encryptWithPublicKey(publicKey, message);
}

View File

@ -9,7 +9,6 @@ import {
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
import { Waku, WakuMessage } from 'js-waku';
import { DirectMessage, encode } from './wire';
import { encryptMessage } from '../crypto';
import { DirectMessageContentTopic } from '../waku';
const useStyles = makeStyles((theme) => ({
@ -109,16 +108,15 @@ async function encodeEncryptedWakuMessage(
publicKey: string,
address: string
): Promise<WakuMessage> {
const encryptedMsg = await encryptMessage(publicKey, message);
const directMsg: DirectMessage = {
toAddress: address,
encMessage: encryptedMsg,
message: message,
};
const payload = encode(directMsg);
return WakuMessage.fromBytes(payload, {
contentTopic: DirectMessageContentTopic,
encPublicKey: publicKey,
});
}

View File

@ -1,4 +1,3 @@
import * as EthCrypto from 'eth-crypto';
import * as protobuf from 'protobufjs/light';
export interface PublicKeyMessagePayload {
@ -61,7 +60,7 @@ export class PublicKeyMessage {
*/
export interface DirectMessage {
toAddress: string;
encMessage: EthCrypto.Encrypted;
message: string;
}
export function encode<T>(msg: T): Buffer {

View File

@ -1,7 +1,7 @@
import { Dispatch, SetStateAction } from 'react';
import { getStatusFleetNodes, Waku, WakuMessage } from 'js-waku';
import { decode, DirectMessage, PublicKeyMessage } from './messaging/wire';
import { decryptMessage, validatePublicKeyMessage } from './crypto';
import { validatePublicKeyMessage } from './crypto';
import { Message } from './messaging/Messages';
import { bufToHex, equalByteArrays } from 'js-waku/lib/utils';
@ -58,31 +58,25 @@ export function handlePublicKeyMessage(
export async function handleDirectMessage(
setter: Dispatch<SetStateAction<Message[]>>,
privateKey: string,
privateKey: Uint8Array,
address: string,
wakuMsg: WakuMessage
) {
console.log('Direct Message received:', wakuMsg);
if (!wakuMsg.payload) return;
const directMessage: DirectMessage = decode(wakuMsg.payload);
// Only decrypt messages for us
if (!equalByteArrays(directMessage.toAddress, address)) return;
try {
const text = await decryptMessage(privateKey, directMessage);
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
console.log('Message decrypted:', text);
setter((prevMsgs: Message[]) => {
const copy = prevMsgs.slice();
copy.push({
text: text,
timestamp: timestamp,
});
return copy;
console.log('Message decrypted:', directMessage.message);
setter((prevMsgs: Message[]) => {
const copy = prevMsgs.slice();
copy.push({
text: directMessage.message,
timestamp: timestamp,
});
} catch (e) {
console.log(' Failed to decrypt message', e);
}
return copy;
});
}

View File

@ -1,5 +1,9 @@
export function hexToBuf(str: string): Buffer {
return Buffer.from(str.replace(/^0x/i, ''), 'hex');
export function hexToBuf(hex: string | Buffer | Uint8Array): Buffer {
if (typeof hex === 'string') {
return Buffer.from(hex.replace(/^0x/i, ''), 'hex');
} else {
return Buffer.from(hex);
}
}
export function bufToHex(buf: Uint8Array | Buffer | ArrayBuffer): string {

View File

@ -16,7 +16,7 @@ const dbg = debug('waku:message');
export interface Options {
contentTopic?: string;
timestamp?: Date;
encPublicKey?: Uint8Array;
encPublicKey?: Uint8Array | string;
sigPrivKey?: Uint8Array;
}

View File

@ -42,7 +42,7 @@ export function clearEncode(
const remainder = rawSize % PaddingTarget;
const paddingSize = PaddingTarget - remainder;
const pad = randomBytes(paddingSize);
const pad = Buffer.from(randomBytes(paddingSize));
if (!validateDataIntegrity(pad, paddingSize)) {
throw new Error('failed to generate random padding of size ' + paddingSize);
@ -110,9 +110,9 @@ export function clearDecode(
*/
export async function encryptAsymmetric(
data: Uint8Array | Buffer,
publicKey: Uint8Array | Buffer
publicKey: Uint8Array | Buffer | string
): Promise<Uint8Array> {
return ecies.encrypt(Buffer.from(publicKey), Buffer.from(data));
return ecies.encrypt(hexToBuf(publicKey), Buffer.from(data));
}
export async function decryptAsymmetric(