# Encryption Primitives ## Key Wrapping (x25519-hkdf-a256kw) How a symmetric content key gets wrapped to a recipient's X25519 public key. This is the core crypto operation behind both direct encryption and grant creation. ```mermaid flowchart LR subgraph Wrap ["wrap_key()"] direction TB EphKey["Generate ephemeral
X25519 keypair"] --> ECDH RecipPub["Recipient's
X25519 public key"] --> ECDH ECDH["X25519 ECDH
shared secret"] --> HKDF HKDF["HKDF-SHA256
info = 'opake-v1-x25519-hkdf-a256kw-{did}'"] --> KEK KEK["256-bit key
encryption key"] --> AESKW ContentKey["Content key K
(AES-256)"] --> AESKW AESKW["AES-256-KW"] --> Ciphertext end Ciphertext["wrappedKey.ciphertext:
[32B ephemeral pubkey ‖ 40B wrapped key]"] style Wrap fill:#1a1a2e,color:#eee style Ciphertext fill:#16213e,color:#eee ``` ## Keyring Key Wrapping (AES-256-KW) How a content key gets wrapped under a keyring's group key. Symmetric wrap — no ECDH, no ephemeral keys. ```mermaid flowchart LR subgraph Wrap ["wrap_content_key_for_keyring()"] direction TB GK["Group key GK
(AES-256)"] --> KEK["AES-256-KW
(RFC 3394)"] ContentKey["Content key K
(AES-256)"] --> KEK end KEK --> Wrapped["40 bytes
(32B key + 8B integrity)"] style Wrap fill:#1a1a2e,color:#eee style Wrapped fill:#16213e,color:#eee ``` The group key itself is wrapped to each member's X25519 public key using the asymmetric wrapping scheme above. ## Content Encryption (AES-256-GCM) ```mermaid flowchart LR subgraph Encrypt ["encrypt_blob()"] direction TB K["Content key K"] --> GCM Nonce["Random 12-byte nonce"] --> GCM Plaintext["File bytes"] --> GCM GCM["AES-256-GCM"] end GCM --> Ciphertext["Ciphertext + auth tag"] GCM --> StoredNonce["Nonce stored in
document record"] style Encrypt fill:#1a1a2e,color:#eee ```