# Group Messaging with Encryption
### Libraries and Functions
#### random
- used to generate the encrypting/decrypting key
```rust
try_from_random
```
#### x_salsa20_poly1305
##### box (symmetric encryption)
- used for encrypting/decrypting messages
```rust
x_salsa20_poly1305_encrypt
x_salsa20_poly1305_decrypt
```
##### secretbox (assymetric encryption)
- used for encrypting group encryption keys
```rust
create_x25519_keypair
x_25519_x_salsa20_poly1305_encrypt
x_25519_x_salsa20_poly1305_decrypt
```
### Structures
```rust
pub struct IdentityKey {
agent: AgentPubKey,
key: X25519PubKey,
}
pub struct MessageEntry {
author: AgentPubKey,
receiver: Vec<AgentPubKey>,
group_uuid: String,
payload: String,
time_sent: Timestamp,
time_received: Option<Timestamp>
}
pub struct EncryptedMessageEntry {
session_id: u32,
data: XSalsa20Poly1305EncryptedData
}
pub struct GroupKey {
session_id: u32,
key: XSalsa20Poly1305KeyRef
}
pub struct EncryptedGroupKey {
session_id: u32,
key: XSalsa20Poly1305EncryptedData
}
```
- **session_id**
- determines changes in members which necessitates the change of the group encryption key
### Stages
1. Key creation
- uses the TryFromRandom function to generate a series of bytes for the KeyRef
- needs to be shifted to LAIR according to the docs
- done during init()
- done when a new key is needed when a member joins or leaves the group
2. Key distribution
- group encryption keys are encrypted using `box` encryption
- sender: group moderator/creator
- recipient: individual members
- encrypted key entries are committed to the DHT and linked to individual member's AgentPubKeys
- done when a member is added to the group
- a new encrypted group key entry (using the new member's pubkey) is committed to the DHT
- done when a member is removed from the group
- a new key is generated
- new encrypted group key entries to correspond to all members
- will be moved to an offchain ephemeral storage to minimize the encryption processing of keys per member
3. Encryption and Decryption
- messages are encrypted/decrypted by the `secretbox` function
- messages are matched to corresponding keys based on a session id
### Functions
#### init()
- run when cloning and initiating the group DNA
- creates an x25519 keypair (public and private) for the agent
- commits an `IdentityKey` entry in the DHT (public entry)
- links the `IdentityKey` entry to the agent's pubkey
```mermaid
sequenceDiagram
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
ACC-->>ACC: call `init`
ACC-->>ACC: call create_x25519_keypair
ACC-->>DHT: create_entry IdentityKey containing the Public Encrypting Key
ACC-->>DHT: create_link from agent's AgentPubKey to IdentityKey
```
#### create_key() -> ExternResult\<()>
```rust=
pub struct create_key_input {
session_id: u32,
agent: Vec<AgentPubKey>
}
```
- creates a group encryption key using TryFromRandom
- creates a GroupKey structure
- use 0 as the session_id for the initial group
- add the generated group encryption key
- encrypts the GroupKey structure using own x25519 key
```mermaid
sequenceDiagram
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
ACC-->>ACC: use TryFromRandom to generate encryption key
ACC-->>ACC: get_links for GroupKey with group as base and "latest_group_key" as tag
alt no link found
ACC-->>ACC: use 0 as session_id
else link found
ACC-->>ACC: get entry
ACC-->>ACC: add 1 to latest session id
end
ACC-->>ACC: create a GroupKey structure
ACC-->>DHT: get member list
DHT-->>ACC: return member list
loop for member in list
ACC-->>ACC: encrypt entry using member's x25519 key
ACC-->>DHT: create EncryptedGroupKey entry in DHT
ACC-->>DHT: create link from each Agent PubKey to EncryptedGroupKey using their respective Agent PubKey as tags
end
ACC-->>ACC: encrypt entry using own x25519 key
ACC-->>DHT: create EntryptedGroupKey entry in DHT
ACC-->>DHT: delete link from previous latest key
ACC-->>DHT: create link with Group entry as base, previous EncryptedGroupKey as target, and session_id as tag
ACC-->>DHT: create link with Group entry as base, new EncryptedGroupKey as target, and "latest_group_key" as tag
```
#### add_member(agent: AgentPubKey) -> ExternResult\<()>
```mermaid
sequenceDiagram
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
participant BCC as Bobby_Conversation_Cell
Note over ACC: through what mechanism?
ACC-->>BCC: ask new member to clone Group DNA
BCC-->>BCC: clone Group DNA
BCC-->>BCC: calls `init` to create x25519 key
Note over BCC: do we have return values? how can we signal creation success (either remote signal or call_remote)
BCC-->>ACC: return Ok
ACC-->>ACC: call `create_key`
```
#### remove_member(agent: AgentPubKey) -> ExternResult\<()>
```mermaid
sequenceDiagram
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
participant BCC as Bobby_Conversation_Cell
ACC-->>ACC: call `remove_member` (updates member list)
ACC-->>ACC: call `create_key`
```
#### get_agent_key(agent: AgentPubKey) -> ExternResult\<IdentityKey>
```rust
pub struct IdentityKey {
agent: AgentPubKey,
key: X25519PubKey,
}
```
```mermaid
sequenceDiagram
participant fn as caller_fn
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
fn-->>ACC: call `get_agent_key` with an agent's PubKey as input
ACC-->>DHT: get_links on AgentPubKey with tag "identity_key"
DHT-->>ACC: return links
ACC-->>DHT: get_details on link target
DHT-->>ACC: return details
ACC-->>ACC: convert details to IdentityKey entry
ACC-->>fn: return IdentityKey
```
#### encrypt_key(input: EncryptInput) -> ExternResult\<XSalsa20Poly1305EncryptedData>
```rust=
pub struct EncryptInput {
recipient: AgentPubKey,
data: XSalsa20Poly1305Data,
}
```
```mermaid
sequenceDiagram
participant fn as caller
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
fn-->>ACC: call `encrypt_key`
ACC-->>DHT: call `get_agent_key` with the recipient AgentPubKey as input
DHT-->>ACC: return IdentityKey
note over ACC: can we do a query here instead?
ACC-->>DHT: call `get_agent_key` with the own AgentPubKey as input
DHT-->>ACC: return IdentityKey
ACC-->>ACC: call `x_25519_x_salsa20_poly1305_encrypt` with the returned X25519PubKeys
ACC-->>fn: return encrypted data
```
#### decrypt_key(input: DecryptInput) -> ExternResult\<XSalsa20Poly1305Data>
```rust=
pub struct EncryptInput {
sender: AgentPubKey,
data: XSalsa20Poly1305Data,
}
```
```mermaid
sequenceDiagram
participant fn as caller
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
fn-->>ACC: call `decrypt`
ACC-->>DHT: call `get_agent_key` with the sender AgentPubKey as input
DHT-->>ACC: return IdentityKey
note over ACC: can we do a query here instead?
ACC-->>DHT: call `get_agent_key` with the own AgentPubKey as input
DHT-->>ACC: return IdentityKey
ACC-->>ACC: call `x_25519_x_salsa20_poly1305_decrypt` with the returned X25519PubKeys
ACC-->>fn: return decrypted data
```
#### send_message
```rust=
pub struct EncryptedMessageEntry {
session_id: u32,
data: XSalsa20Poly1305EncryptedData
}
```
```mermaid
sequenceDiagram
participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
AUI-->>ACC: call `send_message`
ACC-->>ACC: construct Message Entry
ACC-->>ACC: get latest session_id
ACC-->>ACC: get latest encryption key
ACC-->>ACC: decrypt key using own x25519 key
ACC-->>ACC: call x_salsa20_poly1305_encrypt()
ACC-->>ACC: construct EncryptedMessage Entry adding the session_id
ACC-->>DHT: commit EncryptedMessage Entry to chain
ACC-->>AUI: return Message Entry
```
### get_messages (general)
```rust=
pub struct MessageEntry {
author: AgentPubKey,
receiver: Vec<AgentPubKey>,
payload: Payload,
time_sent: Timestamp,
reply_to: Option<EntryHash>,
}
pub struct EncryptedMessageEntry {
session_id: u32,
data: XSalsa20Poly1305EncryptedData
}
```
```mermaid
sequenceDiagram
participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
AUI-->>ACC: call `get_fn`
ACC-->>DHT: gets encrypted messages
DHT-->>ACC: return encrypted messages
ACC-->>DHT: get all encryption keys linked to own PubKey
loop for encryption_keys in get results
ACC-->>ACC: decrypt key
ACC-->>ACC: add key to a hashmap with session_id as keys
end
loop for encrypted_message in get results
ACC-->>ACC: check for filtering conditions (author, receiver, hash)
alt message should be returned
ACC-->>ACC: get session_id from encrypted message entry
ACC-->>ACC: get corresponding encryption key from hashmap
ACC-->>ACC: call `decrypt()` on encrypted_message using key
else message should not be returned
ACC-->>ACC: continue with loop
end
end
```
### Security Analysis and Comparison to the Signal Protocol
```mermaid
sequenceDiagram
participant UI as UI
participant WASM as WASM
participant Lair as Lair
participant Chain as Chain
participant WASM2 as WASM2
participant Chain2 as Chain2
UI-->>WASM: plaintext
WASM-->>Lair: plaintext
Lair-->>Lair: encrypt
Lair-->>WASM: encrypted
WASM-->>Chain: encrypted
WASM-->>WASM2: encrypted + encryption via `call_remote`
WASM2-->>Chain2: encryped
```
| | | Signal | Holochain | Comment |
|----------- |----------------------------------- |-------------------- |-------------------- |------------------------------------------------ |
| | Confidentiality | :heavy_check_mark: | :heavy_check_mark: | |
| | Integrity | :heavy_check_mark: | :x: | Need MACs or signatures. |
| | Authentication | :heavy_check_mark: | partial | |
| | Participant Consistency | :heavy_check_mark: | partial | Need MACs or signatures. |
| | Destination Validation | :heavy_check_mark: | partial | Need MACs or signatures. |
| | Forward Secrecy | :heavy_check_mark: | :x: | Single encrypting key for all messages |
| | Backward Secrecy/Self-Healing/PCS | :heavy_check_mark: | :x: | Single encrypting key for all messages |
| | Anonymity Preserving | | :x: | Encryption PubKeys are linked to AgentPubKeys |
| | Speaker Consistency | :heavy_check_mark: | | |
| | Causality Preserving | :heavy_check_mark: | | |
| | Global Transcript | | | |
| | Message Unlinkability | :heavy_check_mark: | :x: | Author and recipient agent keys are plaintext. |
| | Message Repudiation | :heavy_check_mark: | :x: | Author and recipient agent keys are plaintext. |
| | Participant Repudiation | :heavy_check_mark: | :x: | Author and recipient agent keys are plaintext. |
| Usability | Out-of-Order Resilient | :heavy_check_mark: | :heavy_check_mark: | Single encrypting key for all messages |
| Usability | Dropped Message Resilient | :heavy_check_mark: | :heavy_check_mark: | Single encrypting key for all messages |
| Usability | Asynchronicity | :heavy_check_mark: | partial | |
| Usability | Multi-Device Support | partial | partial | |
| Usability | No Additional Service | | TBD | No need for a key server aside from DHT. |
| Group | Computational Equality | :heavy_check_mark: | NA | |
| Group | Trust Equality | :heavy_check_mark: | NA | |
| Group | Subgroup Messaging | :heavy_check_mark: | NA | |
| Group | Contractable | :heavy_check_mark: | NA | |
| Group | Expandable | :heavy_check_mark: | NA | |
### Things to keep in mind
- security is multi-layered
- validation rules
- capability grants
- membrane proof
- encryption via call_remote
### Issues
- single point of failure (encrypting keys)