Skip to content

Instantly share code, notes, and snippets.

@Semisol
Created April 29, 2024 22:51
Show Gist options
  • Save Semisol/bc0b36915d9898e03f150e1a8cb25c00 to your computer and use it in GitHub Desktop.
Save Semisol/bc0b36915d9898e03f150e1a8cb25c00 to your computer and use it in GitHub Desktop.

NIP-XX

Event Encryption

Goals

  • Plausible deniability: There should be no case where can there be a cryptographic proof that a certain party has sent an event to another in a non-forgable way.
  • Relay privacy: The sender and recipient's real public keys can be fully protected from relays if the implementation chooses to do so.
  • Forward secrecy: The compromise of any or both parties' private keys should not lead to previous communications being compromised.
  • Efficiency: The protocol should use as little resources as possible in terms of (en/de)cryption and signing/verification.
  • Filtering-compatible: The protocol should be optimized to allow relays and clients to filter attacks out without impacting privacy.

Prekeys

To allow for non-interactive initialization of the protocol, a user MUST publish pre-generated keys (prekeys). This is done with a kind 10373 event.

{
    "kind": 10373,
    "tags": [],
    "content": <event>,
    "created_at": ...
}

The content MUST be a serialized event of kind 20373 (key authorization) signed by the prekey, with the content of the key authorization event being the public key of the author of the prekey event.

Initialization

An encrypted channel must be initialized before use. This is done with a channel information event (kind 373) being sent from the initiator to the target. This allows clients and relays to employ public key based filtering.

Clients MAY establish channels to never use them for privacy reasons.

An initialization event has the following format:

{
    "pubkey": <initiator>,
    "kind": 373,
    "tags": [
        ["p", <target>]
    ],
    "content": <encrypted data>,
    ...
}

The content should be the encrypted initialization data with the initiator and target's keys.

The initialization data is as follows:

{
    "channel_key": <initiator channel pubkey>,
    "channel_key_proof": <initiator channel key proof>,
    "prekey": <public prekey used>
}

The initiator channel key should be a public/private keypair for this channel only.

The initiator channel key proof should be a key authorization event (kind 20373) signed by the initiator channel key with the content being the hex pubkey of the initiator.

Calculating channel keys

The shared secret ss should be calculated as the unhashed x only point of ECDH between the initiator channel key and the used target prekey.

From this two keys can be calculated:

  • initiator private key: HMAC-SHA256(key=ss, message="initiator key")
  • target private key: HMAC-SHA256(key=ss, message="target key")

Encrypted event

Events can be exchanged over the channel using a kind 374 event. Clients MUST query for encrypted events sent by the opposite role, and send outgoing encrypted events with their own role in the channel.

An encrypted event is defined as follows:

{
    "kind": 374,
    "pubkey": <initiator/target public key>,
    "tags": [],
    "content": <base64 encoded encrypted inner event>
}

The inner event should be encrypted to the public key itself. The inner event must not have a signature or public key.

Control messages

A control message is defined with a kind 20374 event sent in a channel.

Currently, control messages do not have any tags or content. The first empty control message is regarded as a channel acknowledgement signaling it has been successfully established.

Re-initialization

A channel SHOULD be re-initialized by clients every so often in a way that does not leak metadata. Re-initialization is the same as initialization.

An initialization event can be delivered over the encrypted channel, in the same format as a public one. Clients MAY publicly broadcast an initialization event if they do not get a channel acknowledgement within a reasonable timeframe with or without user confirmation after sending it in the channel.

@arthurfranca
Copy link

initiator private key: HMAC-SHA256(key=ss, message="initiator key") [...] Clients MUST query for encrypted events sent by the opposite role

Is message="initiator key" a pubkey? Cause if it is a privkey, the other role won't be able to query by author (other role's pubkey that is the pair of that other role's hmac result)

@arthurfranca
Copy link

Doesn't the lack of a seal with the sender's main pubkey and sig make the receiver unsure if they are really talking to the right person after the channel has been established? The sender could had forwarded their channel privkey to someone else or to the public without the penalty of revealing their main private key.

I guess it is a feature not a bug ("Plausible deniability") but well for this reason people using it should avoid writing secret info even though it is encrypted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment