Security whitepaper

Skiff uses the latest in privacy, cryptography, and decentralization to keep your work and personal data private and truly owned by you. We explain how Skiff's platform guarantees your privacy at a technical level below.Last updated February 2023
Full whitepaper
Talk at BSidesSF

Threat model


End-to-end encryption of all sensitive information

The contents and certain metadata (including title, time created, and last modified date) associated with every document created or uploaded by a user is only visible to the user or shared collaborators.


Resistant to man-in-the-middle attacks

Even without a fully trusted communication channel between the server and clients, our model will never reveal email subject, email content, or document title, contents, and other information.


Resistant to user abuse attacks

We are particularly sensitive to user abuse as a threat vector. One user abuse attack includes sharing unwanted information or documents with a particular target user.


Resistant to impersonation attacks

Our model must be resistant to impersonation of any of the parties (server or clients). It should also inhibit impersonation of other users


Makes phishing difficult

Skiff seeks to prevent adversaries from compromising user accounts even if their password is compromised, including using 2FA, and and/or hardware tokens.


Uncompromisingly usable

Building a usable, responsive, and intuitive product with these security properties is critical to ensuring that users do not switch to less-secure alternatives due to poor usability.

System design

Overview and encryption protocols

Public-key authenticated encryption allows us to securely and privately share access to end-to-end encrypted documents in our security model. Under this schema, each user is issued a long-term public signing key and a medium to long term public key for encryption. Each public key is associated with a corresponding private key generated using Curve25519. We use tweetnacl-js as our encryption library for both asymmetric public-key authenticated encryption ( and secret-key authenticated encryption using xsalsa20-poly1305 (tweetnacl.secretbox). Both algorithms ensure both confidentiality and authenticity of encrypted data (AEAD). While a user’s encryption and signing public keys can be freely shared and used to securely send or verify information, all private keys - which can decrypt information or generate signatures - must be kept private. We discuss how this is done in the following section

Login, creating accounts, and private key storage

For a new user Alice, our account creation and login system is designed to:
  1. Keep Alice’s private keys safe
  2. Ensure sensitive information - such as Alice’s password and private keys - are never sent beyond her browser window, and
  3. Resist brute-force and dictionary attacks.
Account creation occurs according to the following process:
  1. Bob enters his email and generates a secure password (more than n digits, upper/lower case letters, numbers, and special characters). Random encryption and signing keypairs are generated (tweetnacl.js) for Bob’s account in-browser.
  2. On the browser, we run Argon2id to derive a symmetric key from Bob’s password. Using HKDF, this symmetric key is used to generate one symmetric key for login (using the Secure Remote Password protocol), and another symmetric key for encrypting Bob’s secrets (i.e. private keys). This second key is called Bob’s password_derived_secret.
  3. We use Bob’s password_derived_secret to encrypt sensitive data associated with Bob’s account; this encrypted data is called Bob’s encrypted_user_data. This includes Bob’s private keys but not his password. This encrypted information is stored by our server but can only be decrypted with Bob’s password_derived_ secret. The next time Bob logs in, Bob downloads his encrypted_user_data from the server, then decrypts the encrypted_user_data in-browser by using the password_derived_secret. The password_ derived_secret never leaves the browser.
In this system, Bob’s public keys are publicly visible and shareable with other users, while his private keys are encrypted end-to-end. His password and password_derived_ secret are never stored, not even as encrypted data. Bob’s password_derived_secret and password are also never sent over any network, even as encrypted data.
Snippet of Secure Remote Password protocol implementation.
We use the secure remote password (SRP) protocol to authenticate user login. After Bob is authenticated using SRP, the server sends Bob’s encrypted_user_data as well as a signed JSON Web Token (JWT) to indicate that Bob has properly logged in within a certain amount of time. In our security model, the time-limited JWT is generally used for “read-only” operations, including downloading encrypted documents. This is discussed in further detail below.

Account recovery and password changes

When users forget their password, it’s convenient for them to have a way to recover their account and reset their password. This is achieved using a recovery key (a symmetric key similar to the password_derived_secret). A user can enable account recovery in their settings, which generates a recovery key. The recovery key is used to encrypt the user’s private data (i.e. private keys); this encrypted user data is stored by Skiff but inaccessible. When a user requests account recovery, their identity is first verified through email. The server sends an email to the user containing a random eight-character alphanumeric passcode which they can use to prove access to their email account. After this step, the user enters their email, recovery key, and a new password. The client hashes the recovery key, which is sent to the server. The server checks if the hash matches the stored recovery key hash, and that the client retains the correct email code. If both match, the server sends the encrypted user data to the client, which then decrypts it with their recovery key. Note that storing a hash of the recovery key is not like storing a hash of a user’s password; while a password may be predictable and reused, the recovery key is a randomly generated symmetric key. From this point, the process is similar to choosing a new password when an account is created - a password_ derived_secret is derived from the new password, which is then used to encrypt the user’s private data, and the encrypted user data is uploaded to the server, replacing the previous data encrypted with the old password_derived_secret. In future implementations, account recovery can be accomplished using n-of-m Shamir Secret Sharing to maintain endto-end encryption while increasing usability. For example, using 2-of-3 secret sharing, one secret share could be stored in the user’s device, another stored by Skiff’s server, and another provided for “paper” storage. In this model, usability may be significantly higher for many use cases.

Skiff Mail


Skiff Mail extends the Skiff Workspace to enable privacy-first and end-to-end encrypted communication. This section breaks down different cases for sending and receiving messages on Skiff Mail, including to and from external email addresses.

Sending from Skiff Mail to Skiff email

Sending an email from one Skiff address to another is very similar to sharing a Skiff document with another user. Consider the scenario where Alice wants to send an email email_a to Bob, where both Alice and Bob are Skiff users (say [email protected] and [email protected]). When Alice clicks “send” in her Skiff Mail client, Alice’s Skiff client generates a random symmetric key sym_key_email_a to encrypt the email subject and content corresponding to sym_key_email_a. This symmetric key sym_key_email_a is subsequently encrypted with Alice’s and Bob’s encryption public keys for future access, as with a document shared on Skiff’s collaboration platform. Now, as in Skiff’s collaborative workspace, the receiver can log in and decrypt the email’s symmetric key to gain access to the sent message.Under this model, any email sent and received via Skiff Mail is completely end-to-end encrypted and private to Alice, Bob, and any other recipients. No third party (not even Skiff) ever sees or processes the message content.

Sending from Skiff Mail to an External email

When Alice sends an email email_b from her Skiff Mail client to an external email address, Alice’s Skiff client will still generates a symmetric key sym_key_email_b and encrypt this key with Alice’s public key for future access. However, in order to send the mail to an external, unencrypted email address, Alice’s client also encrypts this email with the public key of a decryption service that temporarily processes this mail for external receipt.To send the email email_b externally to Charlie (say charlie@, Alice’s Skiff client now encrypts email_b with the public key of the decryption service pubkey_decrypt. This decryption service ephemerally decrypts the message email_b, composes outgoing MIMEs needed to send email_b to Charlie at [email protected], and sends email_b message to the external email address. Now, Charlie - a non-Skiff recipient - can read Alice’s sent mail. The key used by the decryption service to send this message externally is discarded and deleted, leaving no way for a third party (not even Skiff) to access any sensitive information inside email_b in the future. As in the Skiff-to-Skiff mail case, Skiff does not store an unencrypted copy of this externally sent mail.

Email receiving - External to Skiff Mail

Now, when loading her Skiff inbox, Alice can decrypt the encrypted copy of email email_d in order to read the message. The encryption service discards and deletes its initial randomly generated copy of sym_key_email_d , now ensuring that every message inside Alice’s inbox remains end-to-end encrypted and completely private to her.
This encryption step - taking the unencrypted, external email email_b and transforming it into an encrypted message on Skiff Mail - ensures that Skiff never stores copies of users’ unencrypted mail, even when sent by an external service.

Skiff Pages and Drive


Every document is associated with a short-term symmetric session_key as well as an asymmetric “hierarchical” keypair. The session_key is used to encrypt all document contents and metadata that are stored on the server. In order to support real-time collaboration and simple sharing mechanisms, all collaborators - say Alice and Bob - have access to the same session_key. However, because Alice and Bob have different asymmetric key pairs, they each have unique encrypted copies of the same session key (encrypted with public_key_Alice and public_key_Bob, respectively). Using this symmetric session_key, we can outline processes for creating, editing, and sharing documents. In the following section, we share more about how a document’s hierarchical key can be used for constructing a scalable filesystem out of many thousands of documents.

Real-time collaboration

Real-time collaboration among shared users on a document is end-to-end encrypted using the document’s session key. On Skiff, conflict resolution is performed using a CRDT, which allows each collaborator to maintain an in-browser copy of the document and perform change resolution as live document updates are received from other users. To initiate a collaboration session using the CRDT, two shared users open the document and create a WebSocket connections to a shared WebSocket “room” allowing messages to be passed to other participants. They include the unique identifier of the document for collaboration. At this point, both users begin broadcasting end-to-end encrypted updates to the CRDT through this WebSocket connection to other users listening for collaboration updates. Each user decrypts the CRDT updates (using the document symmetric key) and applies these updates to their local copies of the document data structure. After applying updates, all collaborative users arrive at a final version of the shared document.
Code snippet of Skiff custom provider for real-time collaboration.

Link sharing a document

Link sharing is a critical usability feature for modern collaboration. Unlike all link sharing schemes in use by collaborative products today, Skiff’s link sharing mechanism maintains end-to-end encryption such that not even Skiff can gain access to a document’s URL. In this section, we discuss “end-to-end encrypted links." These links enable sharing single pages, embedded files, and entire recursive subtrees of documents, such as wikis, blogs, or websites. Skiff’s end-to-end encrypted links store information in the URL that remains private to the client (using a URL fragment) and employ an authentication technique extremely similar to the user login method documented above. In order to generate a sharable link for a document, d2, Alice:
  1. Generates a random key link_key_d2, and encrypts session_key_d2 with link_key_d2, and encrypts the link_key with session_key_d2. It is critical that Alice encrypts the link key with the session key so she can recover the document link when redownloading the document (for example, after logging out and logging back in).
  2. Using link_key_d2 as a “password," Alice generates a salt and verifier used for Secure Remote Password. As in user account creation, the salt, verifier, and encrypted keys are stored by the server. However, it is impossible for the server to decrypt the link_key or the session_ key.
Alice now has access to a URL link https:///#link_key that can be securely transmitted to another individual, either through an end-to-end encrypted messenger or another communication channel. Note: As noted above, Alice encrypts link_key_d2 with session_key_d2 in addition to encrypting session_key_d2 with link_key_d2 in order to recover the full link URL when accessing the document. In order for Bob to access d2 given the link URL, Bob:
  1. Parses the URL to extract link_key and docID
  2. Performs the SRP login process to request the salt and send a proof back to the server, which responds with the encrypted session_key_d2
  3. Bob then decrypts session_key_d2 with link_key_d2. At this point, Bob can download and read document d2.
  4. If Alice has enabled editing for the link, Bob sends a copy of session_key_d2 encrypted with Bob’s public key. The server stores this key and a new permission entry on d2 for Bob to access in the future.
In the future, in order for Alice to access the link, Alice uses the version of link_key encrypted with the session_key to reconstruct the link URL.

Skiff Calendar


Skiff Calendar is an end-to-end encrypted, privacy-first, and easy-to-use calendar product that integrates deeply with Skiff Mail, Skiff Pages, and Skiff Drive. Given how much sensitive information we store in our calendars, inboxes, and online accounts, having a product that keeps this information private to you is a necessity.

Calendar events

The fundamental data unit for Skiff Calendar is a calendar, which has its own Curve25519 public private keypair, like a user or a document. This keypair is used to encrypt sensitive information for the calendar, such as all private event information (title, location, notes, and external recipients). Similar to sharing a document with a user, a file with a user, or sending an email to a user, sharing a calendar entails sharing the calendar’s private key. With this private key comes the ability to decrypt events on the calendar, including all event information. Calendar, Mail, Pages, and Drive use many of the same primitives around sharing encryption keys with users via public private keypairs. In the Skiff Calendar context, sharing calendars is quite similar to sharing documents, where multiple users can have access to a calendar via multiple shared copies of the calendar’s private key. A user’s personal preferences regarding an event - such as the event color, or event notifications - are also kept endto-end encrypted with users’ personal keypairs. Given this model, all sensitive information, and even user preferences, are all kept end-to-end encrypted and private to users

Calendar invites - Skiff to Skiff

Sending a calendar invite to another Skiff user is quite simple: The event in question is duplicated and encrypted with the public key belonging to the receiving user’s primary calendar (which may just be the user’s primary public encryption key).Sending a calendar invite to another Skiff user can be considered similar to sending an end-to-end encrypted email to that user, wherein the subject, contents, and all attachments are end-to-end encrypted and no one else can ever see the content.

Calendar invites - External

Because digital calendars communicate using the iCalendar (ICS) format and email messaging, external calendar invites function similarly to sending external emails on Skiff. When an external user is invited, say at a Gmail address, the inviting user creates an ICS file containing the calendar event details and sends the message to the external user. In addition to the title, location, and event details, the list of external users is kept end-to-end encrypted and not stored by Skiff. Instead, the ICS invite and event details are sent as if they are an external email. For event updates, cancellations, and more, users perform a similar process of sending external emails from their Skiff Mail client applications. When an ICS invite is received by a Skiff user, parsing the ICS file also occurs client-side - keeping received event details private and end-to-end encrypted as well. This is similar to email receipt in Skiff Mail, where users perform decryption of email content once logged in.


Skiff’s security model is designed to enable end-to-end encrypted, scalable, and decentralized collaboration and communication agnostic to document type. In this model, all sensitive information - from document titles to email content - is kept private to creators and intended recipients and never shared with a third party. Today, privacy is often neglected by the communication and collaboration products trusted by billions of people. Through innovation, community, and new security technology, we at Skiff hope to build a new, user-centered, and end-to-end encrypted ecosystem. New technology is constantly helping unlock this transformation, from crypto wallets (which provide public keys to millions of users) to client-side search indexing algorithms.The model presented in this paper is the beginning of a platform to build even more private, performant, and userfriendly consumer software.