<commit_before>Message protocol
----------------

- Most algorithms are described from the view of the _sender_.
  Modifications (reversing keys) apply for recipient.
- Provides confidentiality.
- Provides authentication.
- Provides integrity.
- Provides future security and forward secrecy.
- Provides deniability.

### 1. Header Encryption

```
RecipientIdentityPub == Long term public key of recipient (from keyserver UID Message). curve25519
RecipientIdentityPriv == Private key corresponding to ECDH-RecipientIdentityPub. curve25519

SenderHeaderPub == One-time use only public key generated by Sender. curve25519
SenderHeaderPriv == Private key corresponding to ECDH-SenderHeaderPub. curve25519
```

`HeaderKey == Symmetric` key calculated by (internal to NaCL):

1. By Sender:
   `HeaderKey = curve25519.ecdh( SenderHeaderPriv , RecipientIdentityPub)`
2. By Recipient:
   `HeaderKey = curve25519.ecdh( RecipientIdentityPriv, SenderHeaderPub)`

Encrypt header by:
```
  ciphersuite: must be "CURVE25519 XSALSA20 POLY1305" (NaCL)
  nonce = 32byte random // unique for each message
  SenderHeaderPub, SenderHeaderPriv == GenerateDHKeypair() // unique for each message
  EncryptedHeader = NaCL.SEAL(Nonce, RecipientIdentityPub, SenderHeaderPriv, Header)
  HeaderPacket = Nonce | Length(EncryptedHeader) | EncryptedHeader
```

After header encryption, the sender deletes SenderHeaderPriv (it will never be
used again).

The recipient tries to decrypt the header with all available
RecipientIdentityPriv and caches the successful result, if any.


### 2. Root Key Agreement

Sender has:
- SenderIdentityPub, SenderIdentityPriv == Long term public key of sender (keyserver UID Message)

For each new session or session refresh, sender creates:
- SenderSessionPub, SenderSessionPriv == One Time use only keypair for session creation.

Sender fetches from keyserver:
- RecipientIdentityPub == Long term public key of recipient (from keyserver
  UID Message). curve25519.
- RecipientKeyInitPub == Single use public key of recipient from KeyInit
  repository. curve25519.

Sender calculates:

```
  // if this is a fresh session, previous_root_key is null.
  // Otherwise roll over previous root key.
  t1 = DH.ecdh( SenderIdentityPriv, RecipientKeyInitPub )  // e.g. curve25519.ScalarMult
  t2 = DH.ecdh( SenderSessionPriv, RecipientKeyInitPub )
  t3 = DH.ecdh( SenderSessionPriv, RecipientIdentityPub )

  root_key =  KDF( t1 | t2 | t3 | HASH(previous_root_key) )
```

**Important** : SenderIdentityPriv and RecipientIdentityPub must never be in the same DH!


#### 2.1 Encrypted Header contents

Sender constructs header to send to recipient (JSON encoding):
- Ciphersuite: String. Ciphersuite to use. Defines DH, KDF, Symmetric, Hash.
  Keys must match this. Governs content of header and body, does not affect
  header encryption (which is always NaCL).
- RecipientPubHash: `SHA512(RecipientIdentityPub)`
- RecipientTempHash:
  `SHA512(RecipientKeyInitPub) || SHA512(RecipientSessionPub)`
  `SHA512(RecipientSessionPub)`: If previous SenderSessionPub from other party
  has been received.
- SenderIdentity: Identity string of sender.
- SenderSessionPub: SenderSessionPub (type `KeyEntry`, see
  [keyserver](keyserver.md))
- SenderIdentityPubHash: `SHA512(SenderIdentityPub)`
- SenderIdentityPub: Duplicate from SenderUID for easy parsing
  (type `KeyEntry`, see [keyserver](keyserver.md)
- NextSenderSessionPub: New SenderSessionPub to refresh the session
  (type `KeyEntry`, see [keyserver](keyserver.md)).
  Optional. Should be given by both parties after sending/receiving a defined
  maximum number of messages with the previous keypair.
- NextRecipientSessionPubSeen: Currently known NextSenderSessionPub of the other
  party (type `KeyEntry`, see [keyserver](keyserver.md)).
  Session refresh happens when both parties have created new
  NextSenderSessionPub and one party sees its own NextSenderSessionPub reflected
  in NextRecipientSessionPubSeen.
- NymAddress: Address to receive future messages at.
- MaxDelay: Maximum delay that sender requests from mix (obfuscated by
  `rand(1,2*MaxDelay)*MaxDelay`. Constant per session). Can be used in retention
  calculation.
- SenderSessionCount: Total number of messages sent in session before this
  SenderSessionPub was used.
- SenderMessageCount: Total number of messages sent with this SenderSessionPub.
  Can be used in retention.
- SenderUID: Complete UID Message (including SenderIdentityPub).
- SenderLastKeychainHash: Last entry known to sender from keyserver hashchain.
- Status: 2=Error, 1=Reset. 0=OK. Status of session, see below "invalid header".
  Error tries to start a session with the given remote SenderSessionPub. Reset
  deletes all keys, including root_key, and tries to start a new session with
  public key material.
- Padding: Random padding to bring encrypted header to fixed size.

Sender encrypts header (see 1. Header encryption).

<commit_msg>doc: fix typo<commit_after>Message protocol
----------------

- Most algorithms are described from the view of the _sender_.
  Modifications (reversing keys) apply for recipient.
- Provides confidentiality.
- Provides authentication.
- Provides integrity.
- Provides future security and forward secrecy.
- Provides deniability.

### 1. Header Encryption

```
RecipientIdentityPub == Long term public key of recipient (from keyserver UID Message). curve25519
RecipientIdentityPriv == Private key corresponding to ECDH-RecipientIdentityPub. curve25519

SenderHeaderPub == One-time use only public key generated by Sender. curve25519
SenderHeaderPriv == Private key corresponding to ECDH-SenderHeaderPub. curve25519
```

`HeaderKey == Symmetric` key calculated by (internal to NaCL):

1. By Sender:
   `HeaderKey = curve25519.ecdh( SenderHeaderPriv , RecipientIdentityPub)`
2. By Recipient:
   `HeaderKey = curve25519.ecdh( RecipientIdentityPriv, SenderHeaderPub)`

Encrypt header by:
```
  ciphersuite: must be "CURVE25519 XSALSA20 POLY1305" (NaCL)
  nonce = 32byte random // unique for each message
  SenderHeaderPub, SenderHeaderPriv == GenerateDHKeypair() // unique for each message
  EncryptedHeader = NaCL.SEAL(Nonce, RecipientIdentityPub, SenderHeaderPriv, Header)
  HeaderPacket = Nonce | Length(EncryptedHeader) | EncryptedHeader
```

After header encryption, the sender deletes SenderHeaderPriv (it will never be
used again).

The recipient tries to decrypt the header with all available
RecipientIdentityPriv and caches the successful result, if any.


### 2. Root Key Agreement

Sender has:
- SenderIdentityPub, SenderIdentityPriv == Long term public key of sender (keyserver UID Message)

For each new session or session refresh, sender creates:
- SenderSessionPub, SenderSessionPriv == One Time use only keypair for session creation.

Sender fetches from keyserver:
- RecipientIdentityPub == Long term public key of recipient (from keyserver
  UID Message). curve25519.
- RecipientKeyInitPub == Single use public key of recipient from KeyInit
  repository. curve25519.

Sender calculates:

```
  // if this is a fresh session, previous_root_key is null.
  // Otherwise roll over previous root key.
  t1 = DH.ecdh( SenderIdentityPriv, RecipientKeyInitPub )  // e.g. curve25519.ScalarMult
  t2 = DH.ecdh( SenderSessionPriv, RecipientKeyInitPub )
  t3 = DH.ecdh( SenderSessionPriv, RecipientIdentityPub )

  root_key =  KDF( t1 | t2 | t3 | HASH(previous_root_key) )
```

**Important** : SenderIdentityPriv and RecipientIdentityPub must never be in the same DH!


#### 2.1 Encrypted Header contents

Sender constructs header to send to recipient (JSON encoding):
- Ciphersuite: String. Ciphersuite to use. Defines DH, KDF, Symmetric, Hash.
  Keys must match this. Governs content of header and body, does not affect
  header encryption (which is always NaCL).
- RecipientPubHash: `SHA512(RecipientIdentityPub)`
- RecipientTempHash:
  `SHA512(RecipientKeyInitPub) || SHA512(RecipientSessionPub)`
  `SHA512(RecipientSessionPub)`: If previous SenderSessionPub from other party
  has been received.
- SenderIdentity: Identity string of sender.
- SenderSessionPub: SenderSessionPub (type `KeyEntry`, see
  [keyserver](keyserver.md))
- SenderIdentityPubHash: `SHA512(SenderIdentityPub)`
- SenderIdentityPub: Duplicate from SenderUID for easy parsing
  (type `KeyEntry`, see [keyserver](keyserver.md)
- NextSenderSessionPub: New SenderSessionPub to refresh the session
  (type `KeyEntry`, see [keyserver](keyserver.md)).
  Optional. Should be given by both parties after sending/receiving a defined
  maximum number of messages with the previous keypair.
- NextRecipientSessionPubSeen: Currently known NextSenderSessionPub of the other
  party (type `KeyEntry`, see [keyserver](keyserver.md)).
  Session refresh happens when both parties have created new
  NextSenderSessionPub and one party sees its own NextSenderSessionPub reflected
  in NextRecipientSessionPubSeen.
- NymAddress: Address to receive future messages at.
- MaxDelay: Maximum delay that sender requests from mix (obfuscated by
  `rand(1,2*MaxDelay)*MaxDelay`. Constant per session). Can be used in retention
  calculation.
- SenderSessionCount: Total number of messages sent in session before this
  SenderSessionPub was used.
- SenderMessageCount: Total number of messages sent with this SenderSessionPub.
  Can be used in retention.
- SenderUID: Complete UID Message (including SenderIdentityPub).
- SenderLastKeychainHash: Last entry known to sender from keyserver hashchain.
- Status: 2=Error, 1=Reset, 0=OK. Status of session, see below "invalid header".
  Error tries to start a session with the given remote SenderSessionPub. Reset
  deletes all keys, including root_key, and tries to start a new session with
  public key material.
- Padding: Random padding to bring encrypted header to fixed size.

Sender encrypts header (see 1. Header encryption).


{'fraction_non_alphanumeric': 0.07418307286381165, 'fraction_numerical': 0.018469656992084433, 'mean_word_length': 4.901197604790419, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3897542', 'n_tokens_mistral': 2941, 'n_tokens_neox': 2745, 'n_words': 1074}