# Encryption using the Signal Protocol
<style> .ui-infobar, #doc.markdown-body { max-width: 2000px; } </style>
###### tags: `Kizuna-architecture`
## Summary of Meeting Notes
* Encryption should be done both in sync and async so that holoport owners don't get to see messages
* Goal should be not sending plaintext messsages between client and holoport
* Key storage
* DHT: more resilient, less secure
* Source Chain: less resilient, more secure
* resiliencey means being able to handle operations when holoports are offline
* Key request (from DHT)
* problem of key draining
* problem of simultaneous key pulls
* clearly a problem even with randomization
* would be fixed if we ask agents for keys (sync)
* deliver key via call_remote
* Cryptographic calculations are not safe in wasm
* holoport owners may get access to private keys
* implement as close to the APIs
* Relevant resources
* maybe libsodium api can offer more than hdk (e.g. lair function that does DHE in the background)
* https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
* https://github.com/holochain/holochain/blob/develop/crates/hdk3/src/x_salsa20_poly1305/x_25519_x_salsa20_poly1305_encrypt.rs
#### 2/09/2021 Questions and Comments
- Plain text should never be sent from client to HoloPort
- We dont want to store private keys in source chain - not compliant w/ CAL, also a bit risky
- There has to be at least 1 key stored persistently in client that can decrypt all encrypted entries in source chain. (And this is not the private counterpart of AgentPubKey because that is for signing)
- If every private entry is encrypted with this key, then this key is weakest point of the encryption scheme we will have.
- If we will store this in one local device of the agent, how do we ensure that multi-session is possible?
- OR, we could derive a private/public key from AgentPrivateKey which means that you don't have to store that key persistently because as long as you have the right seed, you will always generate the same private key.
- Art has said that Chaperone needs to also derive some sort of encryption key so that it can be used for encrypting entries for an agent in the wormhole (what is wormhole?? does this mean the private key leaves local device?)
- **We may need some form of persistent private key storage in the client when using Holo**
- How do we reconcile the difference between crypto implementation for native Holochain user and Holo hosted user?
- Holochain has Lair, but Holo does not. So, I think we have to match the native holochain user with Holo use case?
- Where should we generate dynamic keys that can work both for Holo and Holochain
- Lair
- Will not work with Holo (since lair is replaced by Chaperone in Holo)
- Client (on our own)
- The burden of implementing key infrastructure will be upon us
- Each project will have different implementation (may introduce lots of security vulnerabilities)
- We can do basically anything we want to if we do this.
- Chaperone
- No idea how this will work yet. Maybe chaperone will expose APIs that we can use and get the private key exposed outside of the iframe?
- Will chaperone have key generation APIs and will we be able to access those dynamically generated keys so that we can persitently store them?
- Do we have access to secrets on the browser or will it be completely contained in chaperone?
- A no to this answer would mean that we have to introduce something like tweetnacl into Kizuna.
### External libraries
- https://github.com/signalapp/libsignal-protocol-javascript
- https://www.npmjs.com/package/tweetnacl
## Entry Relationship Diagram
```mermaid
graph LR
subgraph encryption zome
subgraph agents
alice
end
subgraph public keys in DHT and source chain
X3DHKeys
onetime_key_1
onetime_key_2
onetime_key_...
alice-->X3DHKeys
alice-->onetime_key_1
alice-->onetime_key_2
alice-->onetime_key_...
end
subgraph private keys in LAIR
X3DHKeys_pr
onetime_key_1_pr
onetime_key_2_pr
onetime_key_..._pr
X3DHKeys.->|can fetch|X3DHKeys_pr
onetime_key_1.->|can fetch|onetime_key_1_pr
onetime_key_2.->|can fetch|onetime_key_2_pr
onetime_key_... .->|can fetch|onetime_key_..._pr
end
end
```
## P2P Encryption using the x_salsa20_poly1305
```mermaid
sequenceDiagram
participant ACD as Alice_Conversation_CELL
participant DHT as DHT
participant BCD as Bobby_Conversation_CELL
ACD-->>ACD: create an x25519 Identity Keypair using
```
## Functions (using x_salsa_poly_1305)
### `init`
```rust=
fn
```
## Functions
### `encrypt_message`
- Alice sends a message to offline Bobby
```rust=
fn encrypt_message(message: MessageParameter) -> ExternResult<EncryptedMessage>
pub struct AssociatedData {
sender_identity_key: X25519PubKey,
receiver_identity_key: X25519PubKey,
sender_ephemeral_key: X25519PubKey,
receiver_onetime_key: X25519PubKey
}
#[hdk_entry(id = "encryptedmessage", visibility = "public")]
pub struct EncryptedMessage {
id: Option<String>,
encrypted_data: Vec<Bytes>,
associated_data: AssociatedData
}
```
```mermaid
sequenceDiagram
participant ACD as Alice_Conversation_CELL
participant BCD as Bobby_Conversation_CELL
participant DHT as DHT
par init
rect rgba(0,0,255,.1)
ACD-->>ACD: call publish_prekeys
end
and
rect rgba(0,0,255,.1)
BCD-->>BCD: call publish_prekeys
end
end
rect rgba(255, 0, 0, .5)
ACD-->>ACD: call `get_prekey_bundle`
end
ACD-->>ACD: call `agree_on_secret` -> shared_master
Note over ACD: use shared secret to derive two symmetric keys : root key and sending chain key
# ratchet step
ACD-->>ACD: call `create_x25519_keypair` for initial ratchet keypair
ACD-->>ACD: run ratchet_chain <br> (generated_public_key, bob_onetime_key) -> ratchet_key
ACD-->>ACD: run root_chain (shared_master, ratchet_key) -> (root_key, sending_key)
ACD-->>ACD: run sending_chain <br> (sending_key) -> (chain_key, message_key)
ACD-->>ACD: encrypt
```
### `publish_prekeys`
```rust=
fn publish_prekeys() -> ExternResult<bool>
```
```rust=
// two entry types = two validation rules
// downside: getting two entries every time a session is being established
// what data type for keys is native to rsm/rust
#[hdk_entry(id = "x3dhkeys", visibility = "public")]
pub struct X3DHKeys = {
identity_key: X25519PubKey, // once
signed_prekey: X25519PubKey, // replaced weekly/monthly together with prekey sig
prekey_signature: X25519PubKey, // replaced weekly/monthly together with prekey
}
#[hdk_entry(id = "x3dhonetimekeys", visibility = "public")]
pub struct X3DHOnetimeKeys = {
onetime_prekeys: Vec<X25519PubKey> // replaced once available keys are depleted/low
}
```
```rust=
// `hdk3::x_salsa20_poly1305::create_x25519_keypair::create_x25519_keypair`
pub fn create_x25519_keypair() -> HdkResult<X25519PubKey>
// `hdk3::host_fn::sign::sign`
fn sign(key: AgentPubKey, data: SerializedBytes) -> HdkResult<Signature>
// `hdk3::prelude::prelude::signature::Signature`
pub struct Signature(pub Vec<u8>);
```
```mermaid
sequenceDiagram
participant BCD as Bobby_Conversation_CELL
participant DHT as DHT
BCD-->>BCD: call `create_x25519_keypair` for identity key
Note over BCD: can we use the AgentPubKey for the identity key?
BCD-->>BCD: call `create_x25519_keypair` for prekey
BCD-->>BCD: sign prekey
BCD-->>BCD: create X3DHKeys entry
BCD-->>DHT: commit X3DHKeys entry in DHT
DHT-->>BCD: return header address
BCD-->>DHT: link Agent to entry with tag 'x3dhkeys'
loop until required number of onetime keys is met
BCD-->>BCD: call`create_x25519_keypair`to generate onetime keys
end
BCD-->>BCD: create X3DHOnetimeKeys entry
BCD-->>DHT: commit X3DHOnetimeKeys entry in DHT
DHT-->>BCD: return header address
BCD-->>DHT: link Agent to entry with tag 'x3dhonetimekeys'
```
### `replace_prekey`
```rust=
fn replace_prekey() -> ExternResult<bool>
```
```mermaid
sequenceDiagram
participant BCD as Bobby_Conversation_CELL
participant DHT as DHT
BCD-->>BCD: construct hash address of X3DHKeys entry in DHT
BCD-->>DHT: get X3DHKeys entry from DHT
DHT-->>BCD: return X3DHKeys entry
BCD-->>BCD: call`create_x25519_keypair`to generate prekey
BCD-->>DHT: update X3DHKeys entry in DHT with newly generated prekey
```
### `replenish_onetime_keys`
```rust=
fn replenish_onetime_keys() -> ExternResult<bool>
```
```mermaid
sequenceDiagram
participant BCD as Bobby_Conversation_CELL
participant DHT as DHT
BCD-->>BCD: construct hash address of X3DHOnetimeKeys entry in DHT
BCD-->>DHT: get X3DHOnetimeKeys entry from DHT
DHT-->>BCD: return X3DHOnetimeKeys entry
loop until required number of onetime keys is met
BCD-->>BCD: call`create_x25519_keypair`to generate onetime keys
end
BCD-->>DHT: update X3DHOnetimeKeys entry in DHT with newly generated keys
```
### `get_prekey_bundle`
```rust=
pub struct PrekeyBundle {
X3DHKeys: X3DHKeys,
OnetimeKey: String
}
fn get_prekey_bundle() -> ExternResult<PrekeyBundle>
```
```mermaid
sequenceDiagram
participant ACD as Alice_Conversation_CELL
participant BCD as Bobby_Conversation_CELL
participant DHT as DHT
ACD-->>DHT: get_links from receiver Agent with tag 'x3dhkeys'
DHT-->>ACD: return links
ACD-->>DHT: get link target
DHT-->>ACD: return X3DHKeys entry
ACD-->>DHT: get_links from receiver Agent with tag 'x3dhonetimekeys'
DHT-->>ACD: return links
ACD-->>DHT: get link target
DHT-->>ACD: return X3DHOnetimeKeys entry
ACD-->>ACD: select a onetime prekey at random
Note over ACD: what selection mechanism would be good?
ACD-->>DHT: update_entry to remove selected onetime prekey
Note over DHT: exposes social graph
ACD-->>ACD: return PrekeyBundle
```
### `agree_on_secret`
```mermaid
sequenceDiagram
participant ACD as Alice_Conversation_CELL
participant DHT as DHT
ACD-->>ACD: verify prekey_signature[bobby]
opt verification failed
ACD-->>ACD: return error
end
ACD-->>ACD: DH(identity_key[alice], signed_prekey[bobby]) -> DH1
ACD-->>ACD: call`create_x25519_keypair`to generate an ephemeral key
ACD-->>ACD: DH(ephemeral_key[alice], identity_key[bobby]) -> DH2
ACD-->>ACD: DH(ephemeral_key[alice], signed_prekey[bobby]) -> DH3
ACD-->>ACD: DH(ephemeral_key[alice], onetime_key[bobby]) -> DH4
ACD-->>ACD: concatenate all DH outputs (DH1||DH2||DH3||DH4) -> shared key
ACD-->>ACD: encode both identity keys as byte strings
ACD-->>ACD: contatenate encoded byte strings -> AD
ACD-->>ACD: return shared key
```
### `ratchet_chain`
```rust=
pub struct RatchetInput {
public_key_receiver: String //from receiver
public_key_sender: String //own, used to retrieve private key from lair
}
fn ratchet(input: RatchetInput) -> ExternResult<Key> {
// Diffie-Hellman
}
```
### `root_chain`
```rust=
pub struct RootInput {
private_key_shared: String
dh_output: String
}
pub struct RootPair {
chain_key: String,
root_key: String
}
fn ratchet(input: RootInput) -> ExternResult<RootPair> {
// KDF
}
```
### `sending_chain`
```rust=
pub struct SendingPair{
chain_key: String,
message_key: String
}
fn sending_chain(chain_key: Key) -> ExternResult<SendingPair> {
// KDF
}
```
### `receiving_chain`
### `AEAD`
### `KDF`
### `Encryption`
```mermaid
sequenceDiagram
participant SecMem as Client_Secure_Memory
participant Client as Alice_Client
participant Chap as Alice_Chaperone
participant ACC as Alice_Conversation_Cell
participant DHT as DHT
rect rgba(255, 0, 0, 0.3)
Note over Client: Key Generation
Note over SecMem: implement storage to secure memory
Note over Chap: Will the chaperone have any role in key generation and storage?
Client-->>Client: generate registrationID <br> (libsignal)
Client-->>SecMem: store registrationID
Client-->>Client: generate identityKeyPair <br> (libsignal)
Client-->>SecMem: store identityKeyPair
Client-->>Client: generate prekey and signed prekey <br> (libsignal)
Client-->>DHT: register prekeys and signed prekey
Note over Client: implement a Store for storing and fetching keys
end
rect rgba(0, 255, 0, 0.2)
Note over Client: Session Building
Client-->>Client: Build a session <br> (libsignal)
Client-->>DHT: fetch prekey bundle
DHT-->>Client: return prekey bundle
Client-->>Client: process prekeys <br> (libsignal)
end
rect rgba(0, 0, 255, 0.2)
Note over Client: encrypt a message
Client-->>Client: encrypt message payload using Session <br> (libsignal)
Client-->>ACC: send_message containing the ciphertext as payload
Note over Client: we can't encrypt other data that the backend needs to process (e.g. receiver) -> attack vector during transmission to backend?
ACC-->>Client: return message bundle
end
rect rgba(255, 0, 255, 0.2)
Note over Client: Decryption
DHT-->>Client: receive message via signal or getters
Client-->>Client: find or build matching session with sender
Client-->>Client: decrypt message payload <br> (libsignal)
end
```