5.8 KiB
NIP-704
More private Encrypted Direct Messages
draft
optional
author:motorina0
This NIP defines a way for two clients to derive and share one-use-only
keys for sending and recieving kind:4
events.
Motivation
The content of Direct Messages
NIP-04 is encrypted, but everyone can see who is chatting with whom. Privacy wise this is far from ideal.
This NIP describes a way to obfuscate DM communications from the "general public", it does not deal with the relay tracking of clients (for that see NIP-705).
Suggestion
For the maximum of privacy the two participants of a Direct Message
exchange SHOULD use a different public key for each kind:4
event.
This means that each participant has to:
- build a
direct-message parent key
from which it will derivekeys-to-send
andkeys-to-receive
(listen for)kind:4
events - share this
direct-message parent key
with its DM peer
Each client has a master
key (denoted with m
). This key can be the profile nsec...
, but it is not mandatory.
Derive the direct-message parent key
A client must generate multiple direct-message parent keys
, one for each peer that it is communicating with. The BIP32 structure to be used is:
m / purpose' / conin_type' / part1' / part2' / ... / part8'
- this NIP defines the
purpose
25709'
(dm
->0x646d
->25709
) for derivingDirect Messages
related keys - nostr
coin_type'
is1237'
(see NIP-06) part1' / part2' / ... / part8'
is the public key hex string (of the peer) split in 8 chunks:- the reason for using the peer's (
Bob
) public key is to always arive at the same value even if prio state is lost - the reason for splitting the public key is that each level of the path can have a max value of 232-1
- the reason for using the peer's (
Example
If Alice wants to build hedm parent key
for Bob then she has to:
- get the public key of `Bob` (in hex). Eg:
3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
- split the public key hex string in 8 chunks: -
- derive the
dm parent key
:m/25709'/1237'/3bf0c63f'/cb934634'/.../aefa459d'
3bf0c63f
, cb934634
, 07af97a5
, e5ee64fa
, 883d107e
, f9e55847
, 2c4eb9aa
, aefa459d
We notate the above derived direct-message parent key
with dmpk
. Then we can define paths of the form dmpk/<action>/index
.
Action Name | Value | Path | Derive keys for |
---|---|---|---|
init | 0 |
dmpk/0/0 |
initialize the direct messages flow |
send | 1 |
dmpk/1/<index> |
sending direct messages |
receive | 2 |
dmpk/2/<index> |
receiving direct messages |
draft : republish |
3 |
dmpk/3/<index> |
sending republish events |
draft : market-order |
4500 |
dmpk/4500/<index> |
sending NIP45 market orders |
The client (creator of the dmpk
) must:
- use a new send key (
dmpk/1/<index>
) for each event it signs. It starts from0
and increments after an event is signed. - create filters for the public keys it expects to receive messages to (
dmpk/2/<index>
). It is recommended to listen for the next10
keys and increment the index once a key is used (see BIP-44 address gap logic).
Exchange the direct-message parent key
If Alice
wants to signal Bob
that she is ready to use this NIP (for more privacy) she must:
- build a JSON data of the form:
{
"pubkey": <32-bytes lowercase hex-encoded public key of Alice (public profile key)>
"dmpk": <32-bytes lowercase hex-encoded direct-message parent key>,
"shared_secret_hash": <32-bytes lowercase hex-encoded sha256 of the shared secret>
"send_index": <integer (optional), the index of the last key used to sign an event>,
"receive_index": <integer (optional), the index of the last key an event was received to>,
}
Note
send_index
andreceive_index
are optional, but they help the client a lot in knowing what the state is. Otherwise (when an account is retored) the client would have to scan the public keys until unused ones are found (similar to BIP-44 address gap logic).
- publish a
Parameterized Replaceable Event
(NIP-33) of the form:
{
...
"pubkey": <32-bytes lowercase hex-encoded "init" public key derived using `dmpk/0/0`>,
"kind": 35709,
"content": <NIP-04 encrypted content of the JSON data>,
"tags:" [
"d": <32-bytes lowercase hex-encoded public key of Bob>,
"p": <32-bytes lowercase hex-encoded public key of Bob>
]
}
If Bob
supports this NIP then he must:
- subscribe to "init" events for him:
{
"kind": 35709,
"#p": [<32-bytes lowercase hex-encoded public key of Bob>]
}
- when an event is received, descrypt the content, verify the
shared_secret_hash
againstAlice's
public key - decide if it wants to communicate with
Alice
. If yes it should publish its ownkind:35709
forAlice
After both Alice
and Bob
have published the kind: 35709
event, they can start to publish and listen to events using the one-use-only
keys.