mirror of https://github.com/vacp2p/rfc.git
Update ETH-PM specs (#455)
This commit is contained in:
parent
03e93b4bbc
commit
8f623202aa
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
slug: 20
|
slug: 20
|
||||||
title: 20/TOY-ETH-DM
|
title: 20/TOY-ETH-PM
|
||||||
name: Toy Ethereum Direct Message
|
name: Toy Ethereum Private Message
|
||||||
status: raw
|
status: draft
|
||||||
tags: waku-application
|
tags: waku-application
|
||||||
editor: Franck Royer <franck@status.im>
|
editor: Franck Royer <franck@status.im>
|
||||||
contributors:
|
contributors:
|
||||||
|
@ -10,14 +10,16 @@ contributors:
|
||||||
|
|
||||||
**Content Topics**:
|
**Content Topics**:
|
||||||
|
|
||||||
- Public Key Broadcast: `/eth-dm/1/public-key/json`,
|
- Public Key Broadcast: `/eth-pm/1/public-key/proto`,
|
||||||
- Direct Message: `/eth-dm/1/direct-message/json`.
|
- Private Message: `/eth-pm/1/private-message/proto`.
|
||||||
|
|
||||||
This specification explains the Toy Ethereum Direct Message protocol
|
This specification explains the Toy Ethereum Private Message protocol
|
||||||
which enables a peer to send an encrypted direct message to another peer
|
which enables a peer to send an encrypted message to another peer
|
||||||
using the Waku v2 network, and the peer's Ethereum address.
|
using the Waku v2 network, and the peer's Ethereum address.
|
||||||
|
|
||||||
The main purpose of this specification is to demonstrate how Waku v2 can be used for direct messaging purposes.
|
The main purpose of this specification is to demonstrate how Waku v2 can be used for encrypted messaging purposes,
|
||||||
|
using Ethereum accounts for identity.
|
||||||
|
This protocol caters for Web3 wallets restrictions, allowing it to be implemented only using standard Web3 API.
|
||||||
In the current state, the protocol has privacy and features [limitations](#limitations), has not been audited
|
In the current state, the protocol has privacy and features [limitations](#limitations), has not been audited
|
||||||
and hence is not fit for production usage.
|
and hence is not fit for production usage.
|
||||||
We hope this can be an inspiration for developers wishing to build on top of Waku v2.
|
We hope this can be an inspiration for developers wishing to build on top of Waku v2.
|
||||||
|
@ -33,129 +35,170 @@ Here are the variables used in the protocol and their definition:
|
||||||
|
|
||||||
- `B` is Bob's Ethereum address (or account),
|
- `B` is Bob's Ethereum address (or account),
|
||||||
- `b` is the private key of `B`, and is only known by Bob.
|
- `b` is the private key of `B`, and is only known by Bob.
|
||||||
- `B'` is Bob's Eth-DM Public Key, which has `b'` as private key.
|
- `B'` is Bob's Encryption Public Key, for which `b'` is the private key.
|
||||||
|
- `M` is the private message that Alice sends to Bob.
|
||||||
|
|
||||||
# Design Requirements
|
# Design Requirements
|
||||||
|
|
||||||
The proposed protocol MUST adhere to the following design requirements:
|
The proposed protocol MUST adhere to the following design requirements:
|
||||||
|
|
||||||
1. Alice knows Bob's Ethereum address,
|
1. Alice knows Bob's Ethereum address,
|
||||||
1. Bob is willing to participate to Eth-DM, and publishes `B'`,
|
2. Bob is willing to participate to Eth-PM, and publishes `B'`,
|
||||||
1. Alice wants to send message `M` to Bob,
|
3. Bob's ownership of `B'` MUST be verifiable,
|
||||||
1. Bob SHOULD be able to get `M` using [10/WAKU2](/spec/13),
|
4. Alice wants to send message `M` to Bob,
|
||||||
1. Participants only have access to their Ethereum Wallet via the Web3 API,
|
5. Bob SHOULD be able to get `M` using [10/WAKU2](/spec/13),
|
||||||
1. Carole MUST NOT be able to read `M`'s content even if she is storing it or relaying it,
|
6. Participants only have access to their Ethereum Wallet via the Web3 API,
|
||||||
1. ECDSA Elliptic curve cryptography is used,
|
7. Carole MUST NOT be able to read `M`'s content even if she is storing it or relaying it,
|
||||||
1. [eth-crypto](https://www.npmjs.com/package/eth-crypto),
|
8. [Waku Message Version 1](/spec/26/) Asymmetric Encryption is used for encryption purposes.
|
||||||
which uses [eccrypto](https://www.npmjs.com/package/eccrypto),
|
|
||||||
is used for encryption and decryption purposes.
|
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
Bob's Ethereum Address is present in clear in Direct Messages,
|
|
||||||
meaning that anyone who is monitoring the Waku network know how many messages Bob receives.
|
|
||||||
|
|
||||||
Alice's details are not included in the message's structure,
|
Alice's details are not included in the message's structure,
|
||||||
meaning that there is no programmatic way for Bob to reply to Alice
|
meaning that there is no programmatic way for Bob to reply to Alice
|
||||||
or verify her identity.
|
or verify her identity.
|
||||||
|
|
||||||
|
Private messages are sent on the same content topic for all users.
|
||||||
|
As the recipient data is encrypted, all participants must decrypt all messages which can lead to scalability issues.
|
||||||
|
|
||||||
# The protocol
|
# The protocol
|
||||||
|
|
||||||
## Eth-DM Key Generation
|
## Generate Encryption KeyPair
|
||||||
|
|
||||||
First, Bob needs to generate an Eth-DM keypair.
|
First, Bob needs to generate a keypair for Encryption purposes.
|
||||||
To avoid Bob having to save an additional private key or recovery phrase for Eth-DM purposes,
|
|
||||||
we generate the Eth-DM keypair using Bob's Ethereum account.
|
|
||||||
This will allow Bob to recover his Eth-DM private key as long as he has access to his Ethereum private key.
|
|
||||||
|
|
||||||
|
Bob SHOULD get 32 bytes from a secure random source as Encryption Private Key, `b'`.
|
||||||
|
Then Bob can compute the corresponding SECP-256k1 Public Key, `B'`.
|
||||||
|
|
||||||
To generate his Eth-DM keypair, Bob MUST use his Ethereum private key 'b' to sign the Eth-DM salt message:
|
# Broadcast Encryption Public Key
|
||||||
`Salt for Eth-Dm, do not share a signature of this message or others could decrypt your messages`.
|
|
||||||
|
|
||||||
The resulting signature 's' is then concatenated with itself once and hashed using keccak256.
|
For Alice to encrypt messages for Bob,
|
||||||
The resulting hash is Bob's Eth-DM private key `b'`:
|
Bob SHOULD broadcast his Encryption Public Key `B'`.
|
||||||
|
To prove that the Encryption Public Key `B'` is indeed owned by the owner of Bob's Ethereum Account `B`,
|
||||||
|
Bob MUST sign `B'` using `B`.
|
||||||
|
|
||||||
```
|
## Sign Encryption Public Key
|
||||||
b' = keccak256(s + s)
|
|
||||||
|
To prove ownership of the Encryption Public Key,
|
||||||
|
Bob must sign it using [EIP-712](https://eips.ethereum.org/EIPS/eip-712) v3,
|
||||||
|
meaning calling `eth_signTypedData_v3` on his Wallet's API.
|
||||||
|
|
||||||
|
Note: While v4 also exists,
|
||||||
|
it is not available on all wallets and the features brought by v4 is not needed for the current use case.
|
||||||
|
|
||||||
|
The `TypedData` to be passed to `eth_signTypedData_v3` MUST be as follows, where:
|
||||||
|
|
||||||
|
- `encryptionPublicKey` is Bob's Encryption Public Key, `B'`, in hex format, **without** `0x` prefix.
|
||||||
|
- `bobAddress` is Bob's Ethereum address, corresponding to `B`, in hex format, **with** `0x` prefix.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const typedData = {
|
||||||
|
domain: {
|
||||||
|
chainId: 1,
|
||||||
|
name: 'Ethereum Private Message over Waku',
|
||||||
|
version: '1',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
encryptionPublicKey: encryptionPublicKey,
|
||||||
|
ownerAddress: bobAddress,
|
||||||
|
},
|
||||||
|
primaryType: 'PublishEncryptionPublicKey',
|
||||||
|
types: {
|
||||||
|
EIP712Domain: [
|
||||||
|
{ name: 'name', type: 'string' },
|
||||||
|
{ name: 'version', type: 'string' },
|
||||||
|
{ name: 'chainId', type: 'uint256' },
|
||||||
|
],
|
||||||
|
PublishEncryptionPublicKey: [
|
||||||
|
{ name: 'encryptionPublicKey', type: 'string' },
|
||||||
|
{ name: 'ownerAddress', type: 'string' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The signature process is as per the current Ethereum best practice:
|
## Public Key Message
|
||||||
|
|
||||||
1. Convert the salt message to a byte array using utf-8 encoding,
|
The resulting signature is then included in a `PublicKeyMessage`, where
|
||||||
2. Use [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) Ethereum JSON-RPC command or equivalent.
|
|
||||||
|
|
||||||
# Eth-DM Public Key Broadcast
|
- `encryption_public_key` is Bob's Encryption Public Key `B'`, not compressed,
|
||||||
|
- `eth_address` is Bob's Ethereum Address `B`,
|
||||||
|
- `signature` is the EIP-712 as described above.
|
||||||
|
|
||||||
For Bob to be reachable, he SHOULD broadcast his Eth-DM Public Key `B'`.
|
```protobuf
|
||||||
To prove that he is indeed the owner of his Ethereum account `B`, he MUST sign his Eth-DM Public Key.
|
syntax = "proto3";
|
||||||
|
|
||||||
To do so, Bob MUST format his Public Key to lower case hex (no prefix) in a JSON Object on the property `ethDmPublicKey`, e.g.:
|
message PublicKeyMessage {
|
||||||
|
bytes encryption_public_key = 1;
|
||||||
```json
|
bytes eth_address = 2;
|
||||||
{
|
bytes signature = 3;
|
||||||
"ethDmPublicKey": "abcd...0123"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, Bob MUST sign the stringified JSON using [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign).
|
This MUST be wrapped in a Waku Message version 0, with the Public Key Broadcast content topic.
|
||||||
This results in the `sigEthDmPubKey` signature.
|
Finally, Bob SHOULD publish the message on Waku v2.
|
||||||
|
|
||||||
Bob then creates an Eth-DM Public Key Message containing:
|
## Consideration for a non-interactive/uncoordinated protocol
|
||||||
|
|
||||||
- Bob's Eth-DM Public Key `B'` on property `ethDmPublicKey`,
|
Alice has to get Bob's public Key to send a message to Bob.
|
||||||
- Bob's Ethereum Address `B` on property `ethAddress`,
|
Because an Ethereum Address is part of the hash of the public key's account,
|
||||||
- The signature of Bob's Eth-DM Public Key `sigEthDmPubKey` on property `sig`.
|
it is not enough in itself to deduce Bob's Public Key.
|
||||||
|
|
||||||
In JSON format as follows:
|
This is why the protocol dictates that Bob MUST send his Public Key first,
|
||||||
|
and Alice MUST receive it before she can send him a message.
|
||||||
|
|
||||||
```json
|
Moreover, nim-waku, the reference implementation of [13/WAKU2-STORE](/spec/13/),
|
||||||
{
|
stores messages for a maximum period of 30 days.
|
||||||
"ethDmPublicKey": "abcd...0123",
|
This means that Bob would need to broadcast his public key at least every 30 days to be reachable.
|
||||||
"ethAddress": "0x01234...eF",
|
|
||||||
"sig": "0x2eb...a1b"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, Bob SHOULD publish the message on Waku v2 with the Public Key Broadcast content topic.
|
Below we are reviewing possible solutions to mitigate this "sign up" step.
|
||||||
|
|
||||||
# Send Direct Message
|
### Retrieve the public key from the blockchain
|
||||||
|
|
||||||
Alice MAY monitor the Waku v2 to collect Ethereum Address/Eth-DM Public Key tuples.
|
If Bob has signed at least one transaction with his account then his Public Key can be extracted from the transaction's ECDSA signature.
|
||||||
Alice SHOULD verify that the `sig` property of said message contains a valid signature as constructed above.
|
The challenge with this method is that standard Web3 Wallet API does not allow Alice to specifically retrieve all/any transaction sent by Bob.
|
||||||
|
|
||||||
|
Alice would instead need to use the `eth.getBlock` API to retrieve Ethereum blocks one by one.
|
||||||
|
For each block, she would need to check the `from` value of each transaction until she finds a transaction sent by Bob.
|
||||||
|
|
||||||
|
This process is resource intensive and can be slow when using services such as Infura due to rate limits in place,
|
||||||
|
which makes it inappropriate for a browser or mobile phone environment.
|
||||||
|
|
||||||
|
An alternative would be to either run a backend that can connect directly to an Ethereum node,
|
||||||
|
use a centralized blockchain explorer
|
||||||
|
or use a decentralized indexing service such as [The Graph](https://thegraph.com/).
|
||||||
|
|
||||||
|
Note that these would resolve a UX issue only if a sender wants to proceed with _air drops_.
|
||||||
|
|
||||||
|
Indeed, if Bob does not publish his Public Key in the first place
|
||||||
|
then it can be an indication that he simply does not participate in this protocol and hence will not receive messages.
|
||||||
|
|
||||||
|
However, these solutions would be helpful if the sender wants to proceed with an _air drop_ of messages:
|
||||||
|
Send messages over Waku for users to retrieve later, once they decide to participate in this protocol.
|
||||||
|
Bob may not want to participate first but may decide to participate at a later stage
|
||||||
|
and would like to access previous messages.
|
||||||
|
This could make sense in an NFT offer scenario:
|
||||||
|
Users send offers to any NFT owner,
|
||||||
|
NFT owner may decide at some point to participate in the protocol and retrieve previous offers.
|
||||||
|
|
||||||
|
### Publishing the public in long term storage
|
||||||
|
|
||||||
|
Another improvement would be for Bob not having to re-publish his public key every 30 days or less.
|
||||||
|
Similarly to above, if Bob stops publishing his public key then it may be an indication that he does not participate in the protocol anymore.
|
||||||
|
|
||||||
|
In any case, the protocol could be modified to store the Public Key in a more permanent storage, such as a dedicated smart contract on the blockchain.
|
||||||
|
|
||||||
|
# Send Private Message
|
||||||
|
|
||||||
|
Alice MAY monitor the Waku v2 to collect Ethereum Address and Encryption Public Key tuples.
|
||||||
|
Alice SHOULD verify that the `signature`s of `PublicKeyMessage`s she receives are valid as per EIP-712.
|
||||||
She SHOULD drop any message without a signature or with an invalid signature.
|
She SHOULD drop any message without a signature or with an invalid signature.
|
||||||
|
|
||||||
Using Bob's Eth-DM Public Key, retrieved via [10/WAKU2](/spec/13), Alice MAY now send an encrypted message to Bob.
|
Using Bob's Encryption Public Key, retrieved via [10/WAKU2](/spec/13), Alice MAY now send an encrypted message to Bob.
|
||||||
|
|
||||||
If she wishes to do so, Alice MUST encrypt her message `M` using Bob's Eth-DM Public Key `B'`.
|
If she wishes to do so, Alice MUST encrypt her message `M` using Bob's Encryption Public Key `B'`,
|
||||||
|
as per [26/WAKU-PAYLOAD Asymmetric Encryption specs](/spec/26/#asymmetric).
|
||||||
|
|
||||||
The result of the encryption is as follows
|
Alice SHOULD now publish this message on the Private Message content topic.
|
||||||
(see [eth-crypto's encryptWithPublicKey](https://www.npmjs.com/package/eth-crypto#encryptwithpublickey)):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"iv": "...",
|
|
||||||
"ephemPublicKey": "...",
|
|
||||||
"ciphertext": "...",
|
|
||||||
"mac": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Alice MUST then set this result in a Direct Message's property `encMessage`,
|
|
||||||
with Bob's Ethereum address `B` set to property `toAddress`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"toAddress": "...",
|
|
||||||
"encMessage": {
|
|
||||||
"iv": "...",
|
|
||||||
"ephemPublicKey": "...",
|
|
||||||
"ciphertext": "...",
|
|
||||||
"mac": "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Alice SHOULD now publish this message on the Direct Message content topic.
|
|
||||||
|
|
||||||
# Copyright
|
# Copyright
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ bookMenuLevels: 1
|
||||||
|
|
||||||
- Raw
|
- Raw
|
||||||
- [17/WAKU2-RLNRELAY]({{< relref "/docs/rfcs/17/README.md" >}})
|
- [17/WAKU2-RLNRELAY]({{< relref "/docs/rfcs/17/README.md" >}})
|
||||||
- [20/TOY-ETH-DM]({{< relref "/docs/rfcs/20/README.md" >}})
|
- [20/TOY-ETH-PM]({{< relref "/docs/rfcs/20/README.md" >}})
|
||||||
- [21/WAKU2-FTSTORE]({{< relref "/docs/rfcs/21/README.md" >}})
|
- [21/WAKU2-FTSTORE]({{< relref "/docs/rfcs/21/README.md" >}})
|
||||||
- [24/STATUS-CURATION]({{< relref "/docs/rfcs/24/README.md" >}})
|
- [24/STATUS-CURATION]({{< relref "/docs/rfcs/24/README.md" >}})
|
||||||
- [25/LIBP2P-DNS-DISCOVERY]({{< relref "/docs/rfcs/25/README.md" >}})
|
- [25/LIBP2P-DNS-DISCOVERY]({{< relref "/docs/rfcs/25/README.md" >}})
|
||||||
|
|
Loading…
Reference in New Issue