# Vault backend docs ## Flow/Sequence diagrams ### Types of keys in the system ![Screenshot 2023-11-29 at 10.49.02](https://hackmd.io/_uploads/SyHVD9NSa.png) #### "Main chest" - keys a user has - "master key" = password derived deterministic key - "auth key pair" = derived from the master key, public part is stored on the server, private part is derived each time (never saved) - "encryption\share\main key pair" = random key pair created on account creation (stored encrypted with master key on the server, returned after the challenge proving that they are legit) - "BoS"/"box of secrets" = "master key" and "auth key pair" #### "item" keys - "Item Key" generated when uploaded - every user with access to that item, has an "item box" which is an "item key" sealed with the users's private key part of their "main key pair" ### Sign up (assuming you have a sign up link) 1. email with user details (name and email) welcomes to organisation 2. open the link, api confirms it's valid and sends MFA (TOTP) setup details 3. randomly create "encryption key pair" 4. ask user for password 5. derive "auth key pair" and "master key" from that user password (derived each time it's used not stored) 6. encrypt the "encryption key pair" with the "master key" 7. Also, generate a random "recovery phrase" which effectively acts like a password and the previous steps (5 and 6) are repeated with this to create the "recoveryChest" 8. send encrypted content and the public part of the key "encrption set" alongside a TOTP code to verify the user has set it up per details in step 2 9. On success, API sets tokens in HTTP cookies (add details) ```typescript export interface EncryptionSet { sharePublicKey: string; authPublicKey: string; mainChest: Chest; recoveryChest: Chest; } export interface Chest { encryptedData: EncryptedBoxOfSecrets; salt: Salt; hashParams: HashParams; } ``` ```json "encryptionSet": { "sharePublicKey": "2fx7VjzvazPQiyhx8E/tXLFsuV1WxTfW/NZaD8M3XlI=", "authPublicKey": "2fx7VjzvazPQiyhx8E/tXLFsuV1WxTfW/NZaD8M3XlI=", "mainChest": { "encryptedData": { "message": "0/FAWBAwTn+2MJxgBgUvG6rLEU9oQ6Vwqbqt12sHefSGcj5Y/2OpK6QnOrw17S4EKa6zO8U8uWUUy85eU3leTyrJvDiW0janchPLEQM/uy7s0oeqvZRoTRvbjqromBvsyymB3ifV65YArNnUUx8iE9WRpIRDbD2qTmhezY0f91g1PFt3wmT8ipgdftvKyL1cNLFOPcZgbk0dPGQgUYLt/ro9znAN7QeUwb7JQdHoVnwulSQTHcGKY3vCS9dRhWsHM990yQt001rP7jzDg9U/y292r13RCRIttIWk6glQo5nQLiW045a2B/cgO9VEQo5fg69y0BFcgzNUaGlBifT6qWpir9O1DyfcqnHRQyQrmYPuAeNNVjhS1wVFftYhkd92vgOxL01ZFHANkd4he1ezzLxH9P1qMgcyTXQrOGzcGo3FTiDrO21DWCi5L1j4h5qilwPiWms9W37qh2ludSDpVGA=", "version": 1, "nonce": "BUFaGAqsoszNQQeMcC+wnMs3bycFfmxZ" }, "salt": "string", "hashParams": { "alg": 0, "ops": 0, "mem": 0 } } } ``` ### Login - user has password - derive deterministic key (using a password hashing alg) (this is slow, deliberately preventing brute force) - user requests a challenge from the API (which endpoint) - API creates a challenge (long random string) for Alice to sign (which has an expiry time 1min?) - Do they expire successfully? Volunerable? - Post challenge you get the "main chest", inside has salt and encrypted box of secrets - auth key pair is derived on the frontend (in parallel to the challenge) - Alice signs the challenge with her private authKey and sends to the API - API verifies this with the public part of the authKey - API sets a cookie - frontend generates a "session key", post auth, and uses the "session key" to encrypt the decrypted BoS. This encrypted "box of secrets" is stored in localstorage so that the "session" can be persisted on browser refresh, new tab etc. The "session key" is then split with half of it stored alongside the encrypted "box of secrets", and the other half is stored on the server. The encrypted box of secrets can only then be decrypted when the browser is also still authed with the API ### Change Password (todo) ### At Recovery Time (provisional) - regen new auth key pair - prove to the server your ID, sign a challenge (this is) - unlock the chest with the "recovery phraise" and re-encrypt with this new password - create a NEW recovery key, and encrypt the chest with this. Send both chests to the API ## Items - item is defined as anything that traditionally could be on a filesystem. Imagine anything that's possible on S3! - An item can be shared internally and externally (though at the time of writing directories cannot) - An item **has** to "live" in a directory (with the exception of a root directory) - At the time of writing, items are of type: - file - note - directory - Future item types could be: - credential - message - ... or a blob of anything you want to define! - ### What makes up an item - metadata - json blob things like name, size, type, compression alg, etc - content (optional, unset when directory is the Type) - array of bytes ### What happens when you upload an item 1. Assuming you have the "content" bytes (could be a file, a typed string, anything in the browser).... 2. A random symetric key is generated; an "Item Key" 3. A random asymetric key is generated; an "Item KeyPair" These form an "Item KeySet" 4. Encrypt the Metadata with the "Item Key". 5. If there is content, encrypt the Content with the "Item Key". This results in an EncryptedContent blob, Header (which is required to decrypt the blob. See [here](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream), 6. Encrypt/Seal the "Item KeySet" with the User's MainKeyPairPublicKey. This becomes the "EncryptedItemKeySet" which is a SealedBox 7. A request it sent to the API for a new item: Type, ParentDirectory, EncryptedMetadata, ItemKeyPair's PublicKey, "EncryptedItemKeySet" (specific to that user) - If it's a note: Send EncryptedContent and Header in this request. The API puts the EncryptedContent into S3. This means it's just one request to create/save a note, allowing efficient autosave etc rather than requesting numerous S3 links. - If it's a directory: No content - If it's a file: Send the Header to the API, the API returns a presigned S3 url 8. Upload the content to S3 if required 9. S3 triggers an event, the API marks the item as uploaded. The API creates some additional metadata (such as created time) and encrypts this with the ItemKeyPair's PublicKey (a Sealed Box) ## Item like concepts - Contacts are not sharable so are not "items", but are similar to an "item" in shape, but lack a directory in S3. ## Item sharing (internally, to your org) 1. From the API, Alice gets Bob's public key and an "EncryptedItemKeySet" (see step 6) and decrypts that with her "MainKeyPairPrivateKey". 2. Create a [shared key pair](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) for the Alice and Bob. Bob can use his private key and Alice's public key to also derive this key. 3. - Encrypt/Seal the "Item KeySet" with the Recipients's "MainKeyPairPublicKey", this becomes the "EncryptedItemKeySet" which is a SealedBox (see section describing it) - Encrypt the "Share Metadata" (which is a JSON blob containing eg. A share message from Alice to Bob) with the shared key pair. Bob can derive this key to read the metadata. 4. Alice sends: ```typescript userId: string; encryptedMessage: EncryptedMessage; encryptedItemKeySet: EncryptedItemKeySet; ``` to the API # TODO (Document) - Orgs - users, permissions - Orgs - signup - Orgs - leaving us - Groups - Contacts (in-progress) - External Sharing (in-progress) - Password Changing - Update Account Recovery - Audit - _Types_ of keys etc At this point - profit # TODO (High level thoughts) - Rolling keys - SSO - Big Files - Quantum Proofing ## monorepo of all documentation ### frontend - link? ### api ### api docs https://api-docs.siip-vault.com/ # cryptography We use [Libsodium](https://libsodium.gitbook.io/doc/) #### Sealed Boxes A [sealed box](https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes) is an encrypted blob of arbritary data, that is "sealed" with a public key. The private part is needed to decrypt. This is used a lot for wrapping symmetric keys and sharing. The result is a base64 string. #### Password Hashing [Link](https://libsodium.gitbook.io/doc/password_hashing) #### Files [Link](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream)