Merge pull request #266 from status-im/rename-eth-pm
Rename Eth-DM to Eth-PM
14
.github/workflows/deploy-gh-pages.yml
vendored
@ -49,20 +49,20 @@ jobs:
|
|||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./examples/web-chat/build
|
publish_dir: ./examples/web-chat/build
|
||||||
|
|
||||||
- name: "[eth-dm] install using npm i"
|
- name: "[eth-pm] install using npm i"
|
||||||
run: npm install
|
run: npm install
|
||||||
working-directory: examples/eth-dm
|
working-directory: examples/eth-pm
|
||||||
|
|
||||||
- name: "[eth-dm] build"
|
- name: "[eth-pm] build"
|
||||||
run: npm run build
|
run: npm run build
|
||||||
working-directory: examples/eth-dm
|
working-directory: examples/eth-pm
|
||||||
|
|
||||||
- name: "[eth-dm] Deploy on gh pages to /eth-dm"
|
- name: "[eth-pm] Deploy on gh pages to /eth-pm"
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./examples/eth-dm/build
|
publish_dir: ./examples/eth-pm/build
|
||||||
destination_dir: eth-dm
|
destination_dir: eth-pm
|
||||||
|
|
||||||
- name: "[eth-pm-wallet] install using npm i"
|
- name: "[eth-pm-wallet] install using npm i"
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
2
.github/workflows/examples-ci.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
examples_build_and_test:
|
examples_build_and_test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
example: [ web-chat, eth-dm, eth-pm-wallet-encryption, min-react-js-chat, store-reactjs-chat ]
|
example: [ web-chat, eth-pm, eth-pm-wallet-encryption, min-react-js-chat, store-reactjs-chat ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
- Renamed `discover.getStatusFleetNodes` to `discovery.getBootstrapNodes`;
|
- Renamed `discover.getStatusFleetNodes` to `discovery.getBootstrapNodes`;
|
||||||
Changed the API to allow retrieval of bootstrap nodes from other sources.
|
Changed the API to allow retrieval of bootstrap nodes from other sources.
|
||||||
|
- Examples: Renamed `eth-dm` to `eth-pm`: "Direct Message" can lead to confusion with "Direct Connection" that
|
||||||
|
refers to low latency network connections.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Examples (cli-chat): The focus of this library is Web environment;
|
- Examples (cli-chat): The focus of this library is Web environment;
|
||||||
|
|||||||
@ -17,8 +17,8 @@ import WifiIcon from '@material-ui/icons/Wifi';
|
|||||||
import BroadcastPublicKey from './BroadcastPublicKey';
|
import BroadcastPublicKey from './BroadcastPublicKey';
|
||||||
import Messaging from './messaging/Messaging';
|
import Messaging from './messaging/Messaging';
|
||||||
import {
|
import {
|
||||||
DirectMessageContentTopic,
|
PrivateMessageContentTopic,
|
||||||
handleDirectMessage,
|
handlePrivateMessage,
|
||||||
handlePublicKeyMessage,
|
handlePublicKeyMessage,
|
||||||
initWaku,
|
initWaku,
|
||||||
PublicKeyContentTopic,
|
PublicKeyContentTopic,
|
||||||
@ -122,20 +122,22 @@ function App() {
|
|||||||
if (!address) return;
|
if (!address) return;
|
||||||
if (!provider?.provider?.request) return;
|
if (!provider?.provider?.request) return;
|
||||||
|
|
||||||
const observerDirectMessage = handleDirectMessage.bind(
|
const observerPrivateMessage = handlePrivateMessage.bind(
|
||||||
{},
|
{},
|
||||||
setMessages,
|
setMessages,
|
||||||
address,
|
address,
|
||||||
provider.provider.request
|
provider.provider.request
|
||||||
);
|
);
|
||||||
|
|
||||||
waku.relay.addObserver(observerDirectMessage, [DirectMessageContentTopic]);
|
waku.relay.addObserver(observerPrivateMessage, [
|
||||||
|
PrivateMessageContentTopic,
|
||||||
|
]);
|
||||||
|
|
||||||
return function cleanUp() {
|
return function cleanUp() {
|
||||||
if (!waku) return;
|
if (!waku) return;
|
||||||
if (!observerDirectMessage) return;
|
if (!observerPrivateMessage) return;
|
||||||
waku.relay.deleteObserver(observerDirectMessage, [
|
waku.relay.deleteObserver(observerPrivateMessage, [
|
||||||
DirectMessageContentTopic,
|
PrivateMessageContentTopic,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
}, [waku, address, provider?.provider?.request]);
|
}, [waku, address, provider?.provider?.request]);
|
||||||
@ -178,7 +180,7 @@ function App() {
|
|||||||
light push
|
light push
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h6" className={classes.title}>
|
<Typography variant="h6" className={classes.title}>
|
||||||
Ethereum Direct Message
|
Ethereum Private Message with Wallet Encryption
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{addressDisplay}</Typography>
|
<Typography>{addressDisplay}</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import { hexToBuf, equalByteArrays, bufToHex } from 'js-waku/lib/utils';
|
|||||||
import * as sigUtil from 'eth-sig-util';
|
import * as sigUtil from 'eth-sig-util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign the Eth-DM public key with Web3. This can then be published to let other
|
* Sign the encryption public key with Web3. This can then be published to let other
|
||||||
* users know to use this Eth-DM public key to encrypt messages for the
|
* users know to use this encryption public key to encrypt messages for the
|
||||||
* Ethereum Address holder.
|
* Ethereum Address holder.
|
||||||
*/
|
*/
|
||||||
export async function createPublicKeyMessage(
|
export async function createPublicKeyMessage(
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import {
|
|||||||
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
|
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
|
||||||
import { Waku, WakuMessage } from 'js-waku';
|
import { Waku, WakuMessage } from 'js-waku';
|
||||||
import { bufToHex, hexToBuf } from 'js-waku/lib/utils';
|
import { bufToHex, hexToBuf } from 'js-waku/lib/utils';
|
||||||
import { DirectMessage } from './wire';
|
import { PrivateMessage } from './wire';
|
||||||
import { DirectMessageContentTopic } from '../waku';
|
import { PrivateMessageContentTopic } from '../waku';
|
||||||
import * as sigUtil from 'eth-sig-util';
|
import * as sigUtil from 'eth-sig-util';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
@ -110,12 +110,12 @@ async function encodeEncryptedWakuMessage(
|
|||||||
publicKey: Uint8Array,
|
publicKey: Uint8Array,
|
||||||
address: string
|
address: string
|
||||||
): Promise<WakuMessage> {
|
): Promise<WakuMessage> {
|
||||||
const directMsg = new DirectMessage({
|
const privateMessage = new PrivateMessage({
|
||||||
toAddress: hexToBuf(address),
|
toAddress: hexToBuf(address),
|
||||||
message: message,
|
message: message,
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = directMsg.encode();
|
const payload = privateMessage.encode();
|
||||||
|
|
||||||
const encObj = sigUtil.encrypt(
|
const encObj = sigUtil.encrypt(
|
||||||
Buffer.from(publicKey).toString('base64'),
|
Buffer.from(publicKey).toString('base64'),
|
||||||
@ -124,7 +124,7 @@ async function encodeEncryptedWakuMessage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const encryptedPayload = Buffer.from(JSON.stringify(encObj), 'utf8');
|
const encryptedPayload = Buffer.from(JSON.stringify(encObj), 'utf8');
|
||||||
return WakuMessage.fromBytes(encryptedPayload, DirectMessageContentTopic);
|
return WakuMessage.fromBytes(encryptedPayload, PrivateMessageContentTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessage(
|
function sendMessage(
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const Root = protobuf.Root,
|
|||||||
Field = protobuf.Field;
|
Field = protobuf.Field;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message used to communicate the Eth-Dm public key linked to a given Ethereum account
|
* Message used to communicate the encryption public key linked to a given Ethereum account
|
||||||
*/
|
*/
|
||||||
export class PublicKeyMessage {
|
export class PublicKeyMessage {
|
||||||
private static Type = new Type('PublicKeyMessage')
|
private static Type = new Type('PublicKeyMessage')
|
||||||
@ -59,36 +59,36 @@ export class PublicKeyMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DirectMessagePayload {
|
export interface PrivateMessagePayload {
|
||||||
toAddress: Uint8Array;
|
toAddress: Uint8Array;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct Encrypted Message used for private communication over the Waku network.
|
* Encrypted Message used for private communication over the Waku network.
|
||||||
*/
|
*/
|
||||||
export class DirectMessage {
|
export class PrivateMessage {
|
||||||
private static Type = new Type('DirectMessage')
|
private static Type = new Type('PrivateMessage')
|
||||||
.add(new Field('toAddress', 1, 'bytes'))
|
.add(new Field('toAddress', 1, 'bytes'))
|
||||||
.add(new Field('message', 2, 'string'));
|
.add(new Field('message', 2, 'string'));
|
||||||
private static Root = new Root().define('messages').add(DirectMessage.Type);
|
private static Root = new Root().define('messages').add(PrivateMessage.Type);
|
||||||
|
|
||||||
constructor(public payload: DirectMessagePayload) {}
|
constructor(public payload: PrivateMessagePayload) {}
|
||||||
|
|
||||||
public encode(): Uint8Array {
|
public encode(): Uint8Array {
|
||||||
const message = DirectMessage.Type.create(this.payload);
|
const message = PrivateMessage.Type.create(this.payload);
|
||||||
return DirectMessage.Type.encode(message).finish();
|
return PrivateMessage.Type.encode(message).finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decode(bytes: Uint8Array | Buffer): DirectMessage | undefined {
|
public static decode(bytes: Uint8Array | Buffer): PrivateMessage | undefined {
|
||||||
const payload = DirectMessage.Type.decode(
|
const payload = PrivateMessage.Type.decode(
|
||||||
bytes
|
bytes
|
||||||
) as unknown as DirectMessagePayload;
|
) as unknown as PrivateMessagePayload;
|
||||||
if (!payload.toAddress || !payload.message) {
|
if (!payload.toAddress || !payload.message) {
|
||||||
console.log('Field missing on decoded Direct Message', payload);
|
console.log('Field missing on decoded PrivateMessage', payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new DirectMessage(payload);
|
return new PrivateMessage(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
get toAddress(): Uint8Array {
|
get toAddress(): Uint8Array {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { Waku, WakuMessage } from 'js-waku';
|
import { Waku, WakuMessage } from 'js-waku';
|
||||||
import { DirectMessage, PublicKeyMessage } from './messaging/wire';
|
import { PrivateMessage, PublicKeyMessage } from './messaging/wire';
|
||||||
import { validatePublicKeyMessage } from './crypto';
|
import { validatePublicKeyMessage } from './crypto';
|
||||||
import { Message } from './messaging/Messages';
|
import { Message } from './messaging/Messages';
|
||||||
import { bufToHex, equalByteArrays } from 'js-waku/lib/utils';
|
import { bufToHex, equalByteArrays } from 'js-waku/lib/utils';
|
||||||
|
|
||||||
export const PublicKeyContentTopic =
|
export const PublicKeyContentTopic =
|
||||||
'/eth-pm-wallet/1/encryption-public-key/proto';
|
'/eth-pm-wallet/1/encryption-public-key/proto';
|
||||||
export const DirectMessageContentTopic =
|
export const PrivateMessageContentTopic =
|
||||||
'/eth-pm-wallet/1/direct-message/proto';
|
'/eth-pm-wallet/1/private-message/proto';
|
||||||
|
|
||||||
export async function initWaku(): Promise<Waku> {
|
export async function initWaku(): Promise<Waku> {
|
||||||
const waku = await Waku.create({ bootstrap: true });
|
const waku = await Waku.create({ bootstrap: true });
|
||||||
@ -52,7 +52,7 @@ export function handlePublicKeyMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleDirectMessage(
|
export async function handlePrivateMessage(
|
||||||
setter: Dispatch<SetStateAction<Message[]>>,
|
setter: Dispatch<SetStateAction<Message[]>>,
|
||||||
address: string,
|
address: string,
|
||||||
providerRequest: (request: {
|
providerRequest: (request: {
|
||||||
@ -61,7 +61,7 @@ export async function handleDirectMessage(
|
|||||||
}) => Promise<any>,
|
}) => Promise<any>,
|
||||||
wakuMsg: WakuMessage
|
wakuMsg: WakuMessage
|
||||||
) {
|
) {
|
||||||
console.log('Direct Message received:', wakuMsg);
|
console.log('Private Message received:', wakuMsg);
|
||||||
if (!wakuMsg.payload) return;
|
if (!wakuMsg.payload) return;
|
||||||
|
|
||||||
const decryptedPayload = await providerRequest({
|
const decryptedPayload = await providerRequest({
|
||||||
@ -70,22 +70,22 @@ export async function handleDirectMessage(
|
|||||||
}).catch((error) => console.log(error.message));
|
}).catch((error) => console.log(error.message));
|
||||||
|
|
||||||
console.log('Decrypted Payload:', decryptedPayload);
|
console.log('Decrypted Payload:', decryptedPayload);
|
||||||
const directMessage = DirectMessage.decode(
|
const privateMessage = PrivateMessage.decode(
|
||||||
Buffer.from(decryptedPayload, 'hex')
|
Buffer.from(decryptedPayload, 'hex')
|
||||||
);
|
);
|
||||||
if (!directMessage) {
|
if (!privateMessage) {
|
||||||
console.log('Failed to decode Direct Message');
|
console.log('Failed to decode Private Message');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!equalByteArrays(directMessage.toAddress, address)) return;
|
if (!equalByteArrays(privateMessage.toAddress, address)) return;
|
||||||
|
|
||||||
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
|
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
|
||||||
|
|
||||||
console.log('Message decrypted:', directMessage.message);
|
console.log('Message decrypted:', privateMessage.message);
|
||||||
setter((prevMsgs: Message[]) => {
|
setter((prevMsgs: Message[]) => {
|
||||||
const copy = prevMsgs.slice();
|
const copy = prevMsgs.slice();
|
||||||
copy.push({
|
copy.push({
|
||||||
text: directMessage.message,
|
text: privateMessage.message,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
});
|
});
|
||||||
return copy;
|
return copy;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Ethereum Direct Message Web App
|
# Ethereum Private Message Web App
|
||||||
|
|
||||||
**Demonstrates**:
|
**Demonstrates**:
|
||||||
|
|
||||||
@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
A PoC implementation of [20/ETH-DM](https://rfc.vac.dev/spec/20/).
|
A PoC implementation of [20/ETH-DM](https://rfc.vac.dev/spec/20/).
|
||||||
|
|
||||||
Ethereum Direct Message, or Eth-DM, is a protocol that allows sending encrypted message to a recipient,
|
Ethereum Private Message, or Eth-PM, is a protocol that allows sending encrypted message to a recipient,
|
||||||
only knowing their Ethereum Address.
|
only knowing their Ethereum Address.
|
||||||
|
|
||||||
This is protocol has been created to demonstrated how encryption and signature could be added to messages
|
This protocol has been created to demonstrated how encryption and signature could be added to message
|
||||||
sent over the Waku v2 network.
|
sent over the Waku v2 network.
|
||||||
|
|
||||||
The `main` branch's HEAD is deployed on GitHub Pages at https://status-im.github.io/js-waku/eth-dm/.
|
The `main` branch's HEAD is deployed on GitHub Pages at https://status-im.github.io/js-waku/eth-pm/.
|
||||||
|
|
||||||
To run a development version locally, do:
|
To run a development version locally, do:
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ To run a development version locally, do:
|
|||||||
git clone https://github.com/status-im/js-waku/ ; cd js-waku
|
git clone https://github.com/status-im/js-waku/ ; cd js-waku
|
||||||
npm install # Install dependencies for js-waku
|
npm install # Install dependencies for js-waku
|
||||||
npm run build # Build js-waku
|
npm run build # Build js-waku
|
||||||
cd examples/eth-dm
|
cd examples/eth-pm
|
||||||
npm install # Install dependencies for the web app
|
npm install # Install dependencies for the web app
|
||||||
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku/eth-dm
|
npm run start # Start development server to serve the web app on http://localhost:3000/js-waku/eth-pm
|
||||||
```
|
```
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "eth-dm",
|
"name": "eth-pm",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "eth-dm",
|
"name": "eth-pm",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "/js-waku/eth-dm",
|
"homepage": "/js-waku/eth-pm",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^4.11.4",
|
"@material-ui/core": "^4.11.4",
|
||||||
"@material-ui/icons": "^4.11.2",
|
"@material-ui/icons": "^4.11.2",
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
@ -19,8 +19,8 @@ import WifiIcon from '@material-ui/icons/Wifi';
|
|||||||
import BroadcastPublicKey from './BroadcastPublicKey';
|
import BroadcastPublicKey from './BroadcastPublicKey';
|
||||||
import Messaging from './messaging/Messaging';
|
import Messaging from './messaging/Messaging';
|
||||||
import {
|
import {
|
||||||
DirectMessageContentTopic,
|
PrivateMessageContentTopic,
|
||||||
handleDirectMessage,
|
handlePrivateMessage,
|
||||||
handlePublicKeyMessage,
|
handlePublicKeyMessage,
|
||||||
initWaku,
|
initWaku,
|
||||||
PublicKeyContentTopic,
|
PublicKeyContentTopic,
|
||||||
@ -137,19 +137,21 @@ function App() {
|
|||||||
if (!EncryptionKeyPair) return;
|
if (!EncryptionKeyPair) return;
|
||||||
if (!address) return;
|
if (!address) return;
|
||||||
|
|
||||||
const observerDirectMessage = handleDirectMessage.bind(
|
const observerPrivateMessage = handlePrivateMessage.bind(
|
||||||
{},
|
{},
|
||||||
setMessages,
|
setMessages,
|
||||||
address
|
address
|
||||||
);
|
);
|
||||||
|
|
||||||
waku.relay.addObserver(observerDirectMessage, [DirectMessageContentTopic]);
|
waku.relay.addObserver(observerPrivateMessage, [
|
||||||
|
PrivateMessageContentTopic,
|
||||||
|
]);
|
||||||
|
|
||||||
return function cleanUp() {
|
return function cleanUp() {
|
||||||
if (!waku) return;
|
if (!waku) return;
|
||||||
if (!observerDirectMessage) return;
|
if (!observerPrivateMessage) return;
|
||||||
waku.relay.deleteObserver(observerDirectMessage, [
|
waku.relay.deleteObserver(observerPrivateMessage, [
|
||||||
DirectMessageContentTopic,
|
PrivateMessageContentTopic,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
}, [waku, address, EncryptionKeyPair]);
|
}, [waku, address, EncryptionKeyPair]);
|
||||||
@ -192,7 +194,7 @@ function App() {
|
|||||||
light push
|
light push
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h6" className={classes.title}>
|
<Typography variant="h6" className={classes.title}>
|
||||||
Ethereum Direct Message
|
Ethereum Private Message
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{addressDisplay}</Typography>
|
<Typography>{addressDisplay}</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
@ -12,9 +12,7 @@ export interface KeyPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the signature of the Salt ("Salt for eth-dm...") as
|
* Generate new encryption keypair.
|
||||||
* the entropy for the EthCrypto keypair. Note that the entropy is hashed with keccak256
|
|
||||||
* to make the private key.
|
|
||||||
*/
|
*/
|
||||||
export async function generateEncryptionKeyPair(): Promise<KeyPair> {
|
export async function generateEncryptionKeyPair(): Promise<KeyPair> {
|
||||||
const privateKey = generatePrivateKey();
|
const privateKey = generatePrivateKey();
|
||||||
@ -23,8 +21,8 @@ export async function generateEncryptionKeyPair(): Promise<KeyPair> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign the Eth-DM public key with Web3. This can then be published to let other
|
* Sign the encryption public key with Web3. This can then be published to let other
|
||||||
* users know to use this Eth-DM public key to encrypt messages for the
|
* users know to use this public key to encrypt messages for the
|
||||||
* Ethereum Address holder.
|
* Ethereum Address holder.
|
||||||
*/
|
*/
|
||||||
export async function createPublicKeyMessage(
|
export async function createPublicKeyMessage(
|
||||||
@ -62,10 +60,10 @@ export function validatePublicKeyMessage(msg: PublicKeyMessage): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare Eth-Dm Public key to be signed for publication.
|
* Prepare encryption public key to be signed for publication.
|
||||||
* The public key is set in on Object `{ encryptionPublicKey: string; }`, converted
|
* The public key is set in on Object `{ encryptionPublicKey: string; }`, converted
|
||||||
* to JSON and then hashed with Keccak256.
|
* to JSON and then hashed with Keccak256.
|
||||||
* The usage of the object helps ensure the signature is only used in an Eth-DM
|
* The usage of the object helps ensure the signature is only used in an Eth-PM
|
||||||
* context.
|
* context.
|
||||||
*/
|
*/
|
||||||
function formatPublicKeyForSignature(encryptionPublicKey: Uint8Array): string {
|
function formatPublicKeyForSignature(encryptionPublicKey: Uint8Array): string {
|
||||||
@ -69,7 +69,7 @@ function getWrapKey(keyMaterial: CryptoKey, salt: Uint8Array) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt Eth-DM KeyPair using provided password
|
* Encrypt encryption KeyPair using provided password.
|
||||||
*/
|
*/
|
||||||
async function encryptKey(encryptionKeyPair: KeyPair, password: string) {
|
async function encryptKey(encryptionKeyPair: KeyPair, password: string) {
|
||||||
const keyMaterial = await getKeyMaterial(password);
|
const keyMaterial = await getKeyMaterial(password);
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@ -9,8 +9,8 @@ import {
|
|||||||
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
|
import React, { ChangeEvent, useState, KeyboardEvent } from 'react';
|
||||||
import { Waku, WakuMessage } from 'js-waku';
|
import { Waku, WakuMessage } from 'js-waku';
|
||||||
import { hexToBuf } from 'js-waku/lib/utils';
|
import { hexToBuf } from 'js-waku/lib/utils';
|
||||||
import { DirectMessage } from './wire';
|
import { PrivateMessage } from './wire';
|
||||||
import { DirectMessageContentTopic } from '../waku';
|
import { PrivateMessageContentTopic } from '../waku';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
formControl: {
|
formControl: {
|
||||||
@ -109,13 +109,13 @@ async function encodeEncryptedWakuMessage(
|
|||||||
publicKey: Uint8Array,
|
publicKey: Uint8Array,
|
||||||
address: string
|
address: string
|
||||||
): Promise<WakuMessage> {
|
): Promise<WakuMessage> {
|
||||||
const directMsg = new DirectMessage({
|
const privateMessage = new PrivateMessage({
|
||||||
toAddress: hexToBuf(address),
|
toAddress: hexToBuf(address),
|
||||||
message: message,
|
message: message,
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = directMsg.encode();
|
const payload = privateMessage.encode();
|
||||||
return WakuMessage.fromBytes(payload, DirectMessageContentTopic, {
|
return WakuMessage.fromBytes(payload, PrivateMessageContentTopic, {
|
||||||
encPublicKey: publicKey,
|
encPublicKey: publicKey,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -11,7 +11,7 @@ const Root = protobuf.Root,
|
|||||||
Field = protobuf.Field;
|
Field = protobuf.Field;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message used to communicate the Eth-Dm public key linked to a given Ethereum account
|
* Message used to communicate the encryption public key linked to a given Ethereum account
|
||||||
*/
|
*/
|
||||||
export class PublicKeyMessage {
|
export class PublicKeyMessage {
|
||||||
private static Type = new Type('PublicKeyMessage')
|
private static Type = new Type('PublicKeyMessage')
|
||||||
@ -59,36 +59,36 @@ export class PublicKeyMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DirectMessagePayload {
|
export interface PrivateMessagePayload {
|
||||||
toAddress: Uint8Array;
|
toAddress: Uint8Array;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct Encrypted Message used for private communication over the Waku network.
|
* Encrypted Message used for private communication over the Waku network.
|
||||||
*/
|
*/
|
||||||
export class DirectMessage {
|
export class PrivateMessage {
|
||||||
private static Type = new Type('DirectMessage')
|
private static Type = new Type('PrivateMessage')
|
||||||
.add(new Field('toAddress', 1, 'bytes'))
|
.add(new Field('toAddress', 1, 'bytes'))
|
||||||
.add(new Field('message', 2, 'string'));
|
.add(new Field('message', 2, 'string'));
|
||||||
private static Root = new Root().define('messages').add(DirectMessage.Type);
|
private static Root = new Root().define('messages').add(PrivateMessage.Type);
|
||||||
|
|
||||||
constructor(public payload: DirectMessagePayload) {}
|
constructor(public payload: PrivateMessagePayload) {}
|
||||||
|
|
||||||
public encode(): Uint8Array {
|
public encode(): Uint8Array {
|
||||||
const message = DirectMessage.Type.create(this.payload);
|
const message = PrivateMessage.Type.create(this.payload);
|
||||||
return DirectMessage.Type.encode(message).finish();
|
return PrivateMessage.Type.encode(message).finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decode(bytes: Uint8Array | Buffer): DirectMessage | undefined {
|
public static decode(bytes: Uint8Array | Buffer): PrivateMessage | undefined {
|
||||||
const payload = DirectMessage.Type.decode(
|
const payload = PrivateMessage.Type.decode(
|
||||||
bytes
|
bytes
|
||||||
) as unknown as DirectMessagePayload;
|
) as unknown as PrivateMessagePayload;
|
||||||
if (!payload.toAddress || !payload.message) {
|
if (!payload.toAddress || !payload.message) {
|
||||||
console.log('Field missing on decoded Direct Message', payload);
|
console.log('Field missing on decoded Private Message', payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new DirectMessage(payload);
|
return new PrivateMessage(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
get toAddress(): Uint8Array {
|
get toAddress(): Uint8Array {
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { Waku, WakuMessage } from 'js-waku';
|
import { Waku, WakuMessage } from 'js-waku';
|
||||||
import { DirectMessage, PublicKeyMessage } from './messaging/wire';
|
import { PrivateMessage, PublicKeyMessage } from './messaging/wire';
|
||||||
import { validatePublicKeyMessage } from './crypto';
|
import { validatePublicKeyMessage } from './crypto';
|
||||||
import { Message } from './messaging/Messages';
|
import { Message } from './messaging/Messages';
|
||||||
import { bufToHex, equalByteArrays } from 'js-waku/lib/utils';
|
import { bufToHex, equalByteArrays } from 'js-waku/lib/utils';
|
||||||
|
|
||||||
export const PublicKeyContentTopic = '/eth-dm/1/public-key/proto';
|
export const PublicKeyContentTopic = '/eth-pm/1/public-key/proto';
|
||||||
export const DirectMessageContentTopic = '/eth-dm/1/direct-message/proto';
|
export const PrivateMessageContentTopic = '/eth-pm/1/private-message/proto';
|
||||||
|
|
||||||
export async function initWaku(): Promise<Waku> {
|
export async function initWaku(): Promise<Waku> {
|
||||||
const waku = await Waku.create({ bootstrap: true });
|
const waku = await Waku.create({ bootstrap: true });
|
||||||
@ -50,27 +50,27 @@ export function handlePublicKeyMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleDirectMessage(
|
export async function handlePrivateMessage(
|
||||||
setter: Dispatch<SetStateAction<Message[]>>,
|
setter: Dispatch<SetStateAction<Message[]>>,
|
||||||
address: string,
|
address: string,
|
||||||
wakuMsg: WakuMessage
|
wakuMsg: WakuMessage
|
||||||
) {
|
) {
|
||||||
console.log('Direct Message received:', wakuMsg);
|
console.log('Private Message received:', wakuMsg);
|
||||||
if (!wakuMsg.payload) return;
|
if (!wakuMsg.payload) return;
|
||||||
const directMessage = DirectMessage.decode(wakuMsg.payload);
|
const privateMessage = PrivateMessage.decode(wakuMsg.payload);
|
||||||
if (!directMessage) {
|
if (!privateMessage) {
|
||||||
console.log('Failed to decode Direct Message');
|
console.log('Failed to decode Private Message');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!equalByteArrays(directMessage.toAddress, address)) return;
|
if (!equalByteArrays(privateMessage.toAddress, address)) return;
|
||||||
|
|
||||||
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
|
const timestamp = wakuMsg.timestamp ? wakuMsg.timestamp : new Date();
|
||||||
|
|
||||||
console.log('Message decrypted:', directMessage.message);
|
console.log('Message decrypted:', privateMessage.message);
|
||||||
setter((prevMsgs: Message[]) => {
|
setter((prevMsgs: Message[]) => {
|
||||||
const copy = prevMsgs.slice();
|
const copy = prevMsgs.slice();
|
||||||
copy.push({
|
copy.push({
|
||||||
text: directMessage.message,
|
text: privateMessage.message,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
});
|
});
|
||||||
return copy;
|
return copy;
|
||||||
@ -3,5 +3,5 @@
|
|||||||
Here is the list of the code examples and the features they demonstrate:
|
Here is the list of the code examples and the features they demonstrate:
|
||||||
|
|
||||||
- [Web Chat App](web-chat): Group chat, React/TypeScript, Relay, Store.
|
- [Web Chat App](web-chat): Group chat, React/TypeScript, Relay, Store.
|
||||||
- [Ethereum Direct Message Web App](eth-dm): Private Messaging, React/TypeScript, Light Push, Signature with Web3, Asymmetric Encryption.
|
- [Ethereum Private Message Web App](eth-pm): Private Messaging, React/TypeScript, Light Push, Signature with Web3, Asymmetric Encryption.
|
||||||
- [Minimal ReactJS Chat App](min-react-js-chat): Group chat, React/JavaScript, Relay, Protobuf using `protons`.
|
- [Minimal ReactJS Chat App](min-react-js-chat): Group chat, React/JavaScript, Relay, Protobuf using `protons`.
|
||||||
|
|||||||