Specification version: 0.2.0
Author: Nazar Mokrynskyi
License: Detox Chat specification (this document) is hereby placed in the public domain
This document is a textual specification of Detox Chat protocol. The goal of this document is to give enough guidance to permit a complete and correct implementation.
Refer to the design document if you need a high level overview.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.
- Contact: node in Detox network that implements Detox Chat application protocol
- Contact request: Initial connection of new contact
Detox Chat works on top of Detox network, make yourself familiar with Detox network first as this document will not cover it.
Detox Chat is only one possible protocol that works on top of Detox network.
In order to identify that remote contact wants to communicate using this particular protocol, its application
parameter during connection should start with detox-chat-v0
(this may change in future incompatible versions of the specification).
After detox-chat-v0
there might be some extensions, that are beyond core Detox protocol and are not required for implementation, but may be supported by some implementations.
Following commands MUST be supported by all implementations:
Command name | Numeric value |
---|---|
SECRET | 1 |
SECRET_RECEIVED | 2 |
NICKNAME | 3 |
TEXT_MESSAGE | 4 |
TEXT_MESSAGE_RECEIVED | 5 |
SECRET
- used for sending secretSECRET_RECEIVED
- used to confirm that secret was receivedNICKNAME
- used to send own nicknameTEXT_MESSAGE
- used to send text messagesTEXT_MESSAGE_RECEIVED
- used to confirm that text message was received- Command with numeric value
0
is reserved for future use. - Commands with numeric values
6..63
are reserved for future use. - Commands with numeric values
64...255
are translated into additional commands from range0..191
and can be used for custom extensions
In order to add someone to contacts and possibly to make a contact request, it is necessary to know contact's ID.
There are 2 types of ID: plain ID and ID with secret. Plain ID is just Ed25519 long term public key of a node. ID with secret is the plain ID with some secret appended to it.
Secrets are used as anti-spam system, user can create any number of such secrets for different purposes, so that during contact request it will be clear which secret was used. Such secrets are only used during contacts requests, all future connections use secrets from last successful secrets exchange.
For convenience of distribution of such IDs in text form, following encoding procedure is used:
- take 32-byte Ed25519 long term public key of a node
- optionally append a secret up to 32-bytes long
- compute Blake2b-256 hash of above, take first 2 bytes from that hash and append to above
- base58-encode all of the above using dictionary
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
(as used in Bitcoin and some other projects)
Upon decoding, hash is recomputed and last 2 bytes are compared in order to make sure, ID was not misprinted.
Contact request is an initial connection to a contact, meaning no previous secrets exchange took place.
Contact request is only possible when secret is used. If no secret was specified, node MUST reject contact request. If contact was previously added using plain ID, first connection without secret MUST be accepted, since contact is already added and no contact request needs to be sent.
After first successful connection happened, any subsequent connections use secrets from last secrets exchange. Connections with secrets that use incorrect secrets MUST be rejected and in specific cases SHOULD notify user that they or their contact was compromised (see "Partial post-compromise security and compromise detection" section below).
Once user is connection to other contact, the first thing that happens is secrets exchange, commands other than SECRET
and SECRET_RECEIVED
MUST be rejected.
There are 2 secrets with each contact: local and remote. Local secret is generated by local node, remote secret is generated by remote contact.
During secrets exchange each contact generates a new, previously unused local secret and sends it to the other contact using command SECRET
and contents as follows:
- 32 bytes - newly generated local secret
Once SECRET
with new remote secret is received, node MUST replace previous remote secret with new one immediately and send SECRET_RECEIVED
command in response with no contents to confirm that secret was indeed received.
Node MUST NOT accept known previously used remote secret (it is up to implementation how many previously used secrets are stored for this purpose).
After both SECRET
and SECRET_RECEIVED
commands were received, secrets exchange is considered finished and other commands are allowed to be used.
When new local secret is sent using SECRET
command, both this newly generated and previous local secrets should be kept until SECRET_RECEIVED
is received, and only then old secret should be removed.
If SECRET
was sent, but no SECRET_RECEIVED
was received, both previous valid secret and unconfirmed should be allowed for next connection.
The fact that new secrets are exchanged during each connection enables following useful features:
- if contact tries to contact us using outdated secret, it means that contact was likely compromised and someone have stolen its keys; by knowing when outdated secret and next after it were generated, user will know approximate time frame in which contact compromise happened
- if after compromise at least one secure conversation took place, compromise detection will be possible AND future conversations with such contact will still be secure
- if an attacker managed to initiate connection faster than contact, then one next valid connection it will still be clear that compromise happened and approximately when
- if incoming connection from contact uses unknown secret, it means that this node was likely compromised AND conversation between an attacker and your contact already happened
These features require implementations to keep track of old secrets (both local and remote) and when they were generated, however, how much secrets are kept and for how long is up to implementation.
Nickname is sent using command NICKNAME
and contents as follows:
- up to 65535 bytes nickname string (though, large nicknames are highly discouraged, as they will take a lot of time to send)
Nickname is typically sent after secrets exchange and after nickname change to currently connected contacts.
Text messages are sent using TEXT_MESSAGE
command and contents as follows:
- 8 bytes - signed 64-bit float (double) in bid-endian format, date as Unix timestamp (in milliseconds) when message was sent
- 8 bytes - signed 64-bit float (double) in bid-endian format, date as Unix timestamp (in milliseconds) when message was written
- up to 65519 bytes - text message itself
Once text message is received, COMMAND_TEXT_MESSAGE_RECEIVED
command is sent in response with contents as follows:
- 8 bytes - signed 64-bit float (double) in bid-endian format, date as Unix timestamp (in milliseconds) when message was sent
NOTE: COMMAND_TEXT_MESSAGE_RECEIVED
means that message was received, there is no indication that it was ever read.
Text messages SHOULD be treated as Markdown markup (GFM flavor), but:
- with images being rendered as links (because otherwise they are loaded unconditionally and may lead to de-anonymization)
- without raw HTML support, all raw HTML will be rendered as text
Since Markdown is a human-readable format, lighter clients may render it as simple text.
Incoming message MUST NOT be shown to user if its sent date is older than of last received message or if its written date is not newer than of last received message.
Detox Chat protocol is inspired by Tox.