mirror of https://github.com/status-im/specs.git
174 lines
11 KiB
Markdown
174 lines
11 KiB
Markdown
<!-- TODO: Remove stuff already captured elsewhere -->
|
|
|
|
## Table of Contents
|
|
|
|
- [Terminology](#terminology)
|
|
- [Basic Assumption](#basic-assumption)
|
|
- [Protocol Overview](#protocol-overview)
|
|
- [Whisper adapter](#whisper-adapter)
|
|
- [Whisper node configuration](#whisper-node-configuration)
|
|
- [Keys management](#keys-management)
|
|
- [Encryption algorithms](#encryption-algorithms)
|
|
- [Topic](#topic)
|
|
- [Message encryption](#message-encryption)
|
|
- [Perfect Forward Secrecy (PFS)](#perfect-forward-secrecy-pfs)
|
|
- [Device syncing](#device-syncing)
|
|
- [One-to-one messages](#one-to-one-messages)
|
|
- [Sending](#sending)
|
|
- [Sending using PFS](#sending-using-pfs)
|
|
- [Receiving](#receiving)
|
|
- [Public messages](#public-messages)
|
|
- [Sending](#sending-1)
|
|
- [Receiving](#receiving-1)
|
|
- [Group messages](#group-messages)
|
|
- [Offline messages](#offline-messages)
|
|
- [Anonymity concerns](#anonymity-concerns)
|
|
- [Whisper V6 extensions (or Status Whisper Node)](#whisper-v6-extensions-or-status-whisper-node)
|
|
- [New RPC methods](#new-rpc-methods)
|
|
|
|
# Terminology
|
|
|
|
* *Client*: a Whisper node implementing the protocol
|
|
* *Whisper node*: an Ethereum node with Whisper V6 enabled (in the case of geth, it's `--shh` option)
|
|
* *Status Whisper node*: an Ethereum node with Whisper V6 enabled and additional Whisper extensions described in [Whisper V6 extensions (or Status Whisper Node)](#whisper-v6-extensions-or-status-whisper-node)
|
|
* *Whisper network*: a group of Whisper nodes connected together through the internet connection and forming a graph
|
|
* *MailServer*: an Ethereum node with Whisper V6 enabled and a mail server registered capable of storing and providing offline messages
|
|
* *Message*: decrypted Whisper message
|
|
* *Envelope*: encrypted message with some metadata like topic and TTL sent between Whisper nodes; a symmetric or asymmetric key is needed to decrypt it and read the payload
|
|
* *Offline message*: an expired envelope stored by a Whisper node permanently
|
|
|
|
# Basic Assumption
|
|
|
|
This protocol assumes the following:
|
|
1. There MUST be an Ethereum node that is capable of discovering peers and implements Whisper V6 specification.
|
|
2. Participants of a given Whisper network in order to communicate with each other MUST accept messages with lowered PoW value. More in (Whisper node configuration)(#whisper-node-configuration).
|
|
3. Time MUST be synced between all nodes participating in the given network (this is intrinsic requirement of the Whisper specification as well). A clock drift between two peers larger than 20 seconds MAY result in discarding incoming messages.
|
|
|
|
# Perfect Forward Secrecy (PFS)
|
|
|
|
Additionally to encrypting messages on the Whisper level, the protocol supports PFS specification.
|
|
|
|
A message payload is first encrypted following the PFS specification and then it is encrypted once again following the Whisper specification and this protocol.
|
|
|
|
As not all messages are encrypted with PFS, a following strategy MAY be used:
|
|
1. First, message is decrypted on the Whisper level
|
|
2. Try to decrypt the message payload using PFS algorithm
|
|
2.1. If successful, pass the decrypted value to (3)
|
|
2.2. If failed, pass the unchanged payload to (3)
|
|
3. Decode the payload as described in [Payload](#payload) section
|
|
|
|
TODO: link to a separate document (currently in the PR).
|
|
|
|
[PFS in Status.im docs](https://status.im/research/pfs.html)
|
|
|
|
# Device syncing
|
|
|
|
TODO: link to a separate document.
|
|
|
|
# One-to-one messages
|
|
|
|
One-to-one messages are also known as private messages. These are the messages sent between two participants of the conversation.
|
|
|
|
## Sending
|
|
|
|
Sending a message is fairly easy and relies on the Whisper RPC API, however, some preparation is needed:
|
|
|
|
1. Obtain a public key of the recipient of the message
|
|
2. Add your private key to Whisper using [`shh_addPrivateKey`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_addprivatekey) and save the result as `sigKeyID`
|
|
3. Call [`shh_post`(https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_post) with the following settings:
|
|
1. `pubKey` MUST be a hex-encoded public key of the message recipient
|
|
2. `sig` MUST be set to `sigKeyID`
|
|
3. `ttl` MUST be at least `10` (it is in seconds)
|
|
4. `topic` MUST be set accordingly to [Topic](#topic) section and hex-encoded
|
|
5. `payload` MUST be a hex-encoded string
|
|
6. `powTime` MAY be arbitrary but should be enough to perform proof-of-work
|
|
7. `powTarget` MUST be equal or lower than `0.002`.
|
|
|
|
Note: these instructions are for the Whisper V6 RPC API. If you use Whisper service directly or Go `shhclient`, the parameters might have different types.
|
|
|
|
Learn more following [Whisper V6 RPC API](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API).
|
|
|
|
### Sending using PFS
|
|
|
|
When one decides to use PFS, the flow is the same but the payload MUST be additionally encrypted following the [PFS specification](#pfs) before being hex-encoded and passed to `shh_post`.
|
|
|
|
## Receiving
|
|
|
|
Receiving private messages depends on Whisper filters idea. Upon receiving, messages are first matched by a topic and then trying to be decrypted using user's private key.
|
|
|
|
1. Add your private key to Whisper using [`shh_addPrivateKey`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_addprivatekey) and save the result as `sigKeyID`
|
|
2. Call [`shh_subscribe`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_subscribe) with criteria:
|
|
1. `minPow` MUST be at least `0.002`
|
|
2. `topics` MUST be list of hex-encoded topics you expect messages to receive from (follow [Topic](#topic) section)
|
|
3. `allowP2P` MUST be set to `true` if offline messages are supported, otherwise can be `false`.
|
|
|
|
Alternative method is to use [`shh_newMessageFilter`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_newmessagefilter) which takes the same criteria object and then periodically calling [`shh_getFilterMessages`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_getfiltermessages) method.
|
|
|
|
Learn more following [Whisper V6 RPC API](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API).
|
|
|
|
# Public messages
|
|
|
|
Public messages are encrypted with a symmetric key which is publicly known so anyone can participate in the conversation.
|
|
|
|
The fact that anyone can participate makes the public chats vulnerable to spam attacks. Also, there are no moderators of these chats.
|
|
|
|
## Sending
|
|
|
|
1. Calculate a symmetric key using [`shh_generateSymKeyFromPassword`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_generatesymkeyfrompassword) passing a public chat name as a string and save the result to `symKeyID`
|
|
2. Call [`shh_post`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_post) with the following settings:
|
|
1. `symKeyID` MUST be set to `symKeyID`
|
|
2. `sig` MUST be set to `sigKeyID`
|
|
3. `ttl` MUST be at least `10` (it is in seconds)
|
|
4. `topic` MUST be set accordingly to [Topic](#topic) section and hex-encoded,
|
|
5. `payload` MUST be a hex-encoded string,
|
|
6. `powTime` MAY be arbitrary but should be enough to perform proof-of-work
|
|
7. `powTarget` MUST be equal or lower than `0.002`.
|
|
|
|
Learn more following [Whisper V6 RPC API](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API).
|
|
|
|
## Receiving
|
|
|
|
Receiving public messages depends on Whisper filters idea. Upon receiving, messages are first matched by a topic and then trying to be decrypted using a symmetric key.
|
|
|
|
1. Calculate a symmetric key using [`shh_generateSymKeyFromPassword`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_generatesymkeyfrompassword) passing public chat name as a string and save the result to `symKeyID`
|
|
2. Call [`shh_subscribe`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_subscribe) with criteria:
|
|
1. `minPow` MUST be at least `0.002`
|
|
2. `topics` MUST be list of hex-encoded topics you expect messages to receive from (follow [Topic](#topic) section)
|
|
3. `allowP2P` MUST be set to `true` if offline messages are supported, otherwise can be `false`.
|
|
|
|
Alternative method is to use [`shh_newMessageFilter`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_newmessagefilter) which takes the same criteria object and then periodically calling [`shh_getFilterMessages`](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_getfiltermessages) method.
|
|
|
|
Learn more following [Whisper V6 RPC API](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API).
|
|
|
|
# Group messages
|
|
|
|
TODO: describe how to send a group message starting from adding a key in Whisper etc.
|
|
|
|
# Offline messages
|
|
|
|
In the case of mobile clients which are often offline, there is a strong need to have an ability to download offline messages. By offline messages, we mean messages sent into the Whisper network and expired before being collected by the recipient. A message stays in the Whisper network for a duration specified as `TTL` (time-to-live) property.
|
|
|
|
A Whisper client needs to register a mail server instance which will be used by [geth's Whisper service](https://github.com/ethereum/go-ethereum/blob/v1.8.23/whisper/whisperv6/whisper.go#L209-L213).
|
|
|
|
`MailServer` is an interface with two methods:
|
|
* `Archive(env *Envelope)`
|
|
* `DeliverMail(whisperPeer *Peer, request *Envelope)`
|
|
|
|
If a mail server is registered for a given Whisper client, it will save all incoming messages on a local disk (this is the simplest implementation, it can store the messages wherever it wants, also using technologies like swarm and IPFS) in the background.
|
|
|
|
Notice that each node is meant to be independent and SHOULD keep a copy of all historic messages. High Availability (HA) can be achieved by having multiple nodes in different locations. Additionally, each node is free to store messages in a way which provides storage HA as well.
|
|
|
|
Saved messages are delivered to a requester (another Whisper peer) asynchronously as a response to `p2pMessageCode` message code. This is not exposed as a JSON-RPC method in `shh` namespace but it's exposed in status-go as `shhext_requestMessages` and blocking `shh_requestMessagesSync`. Read more about [Whisper V6 extensions](#whisper-v6-extensions-or-status-whisper-node).
|
|
|
|
In order to receive historic messages from a filter, p2p messages MUST be allowed when creating the filter. Receiving p2p messages is implemented in [geth's Whisper V6 implementation](https://github.com/ethereum/go-ethereum/blob/v1.8.23/whisper/whisperv6/whisper.go#L739-L751).
|
|
|
|
## Anonymity concerns
|
|
|
|
In order to use a mail server, a given node needs to connect to it directly, i.e. add the mail server as its peer and mark it as trusted. This means that the mail server is able to send direct p2p messages to the node instead of broadcasting them. Effectively, it knows which topics the node is interested in, when it is online as well as many metadata like IP address.
|
|
|
|
|
|
<!-- TODO: Document Discovery topic -->
|
|
<!-- TODO: Document topic hashing for 1:1 chat -->
|
|
<!-- TODO: Document topic hashing for private group chat -->
|
|
<!-- TODO: Document topic hashing for public chats -->
|