Encryption - Part 3

  • Part 1 - File encryption
  • Part 2 - Treesystem encryption
  • Part 3 - Per-app encryption
  • Part 4 - Cryptographic details for experts


Per-App Encryption

Every user has their own bucket in S3, which looks like this:

                        ( Alice's S3 Bucket )
                        /         |         \
           /      \          /        \            /     |    \
       (nodes) (within)   (each) (individual)    (app) (go) (here)

We see that Alice is using 3 different apps:

  • Storm4
  • A wallet for her cryptocurrency
  • An address book app

And each app has its own separate container within the user's bucket. The question is:

  • Can her com.crypto.wallet app read from her com.fusion.addressBook app ?
  • If she installs com.fusion.calendar can it read from com.fusion.addressBook ?
  • If she installs com.sketchy.game can it read from her com.crypto.wallet ?!?!?

So there are situations in which this may be desirable. And clearly there are situations in which it most certainly is NOT.

Per-app encryption solves this dilemma:

  • By default, com.foo.appA cannot read the content from com.bar.appB
  • But Alice has full control of her data
  • If she chooses, she can grant permission for com.foo.appA to access com.bar.appB (either read-only or read-write)

This puts Alice in control of her data. After all, it's her data — she owns it. By default, she's protected. But by empowering Alice to make her own choices, we open the door to an app ecosystem full of possibilities:

  • new forms of inter-app communication & collaboration become possible
  • it allows Alice to migrate her data from appA to appB

Per-app encryption is always enabled for co-op users. But if your app uses custom authentication (i.e. your app users are NOT co-op users), then per-app encryption is disabled by default. You can optionally enable it, if you wish.


Encryption Details

In part 1 you saw a record file:

    "fileID": "E21E04505CD4430EBBBB989E92CE90BF",
    "keys": {
        "UID:z55tqmfr9kix1p1gntotqpwkacpuoyno": {
            "perms": "rws",

In this example, only a single user is given permission to read the file. (userID: z55tqm...) So this user will need to use their private key to decrypt the key blob ("eyJ2ZXJ..."), which will reveal the file encryption key. And the file encryption key can then be used to decrypt the corresponding file.

Per-app encryption adds another layer of encryption to this scheme. It starts by the server generating a unique encryption key for each [userID, appID] tuple. This is called the app encryption key. And each of the following app encryption keys would be different:

  • [alice, com.foo.appA]
  • [alice, com.bar.appB]
  • [bob, com.bar.appB]

With per-app encryption, the server adds another layer of encryption by encrypting the uploaded wrapped key ("eyJ2ZXJ...") with the appropriate app encryption key.


Let's break this down by starting at the client. Alice wants to upload a file to the cloud, and the framework automatically goes through the process of encrypting the data:

// For this node, we are only granting read permission to Alice

(cleartext file) (file encryption key)---------->(wrap)
          \         /                              |
      (Threefish-512 TBC)                  (alice's pubKey)
              |                                    |
      (encrypted file)                     (wrapped file key)
                                             (JSON record file)

This creates a JSON record file, which looks like the JSON from above. So there's a key blob ("eyJ2ZXJ..."), which is Base64(Wrap(fileKey, pubKey)).

The client uploads this JSON file to the cloud. And then the server encrypts the key blob again, using the app encryption key. The end result is that any device wishing to decrypt the content will need to:

  • download the JSON file
  • decrypt the key blob using the appropriate app encryption key
  • decrypt the result using their private key
  • this finally reveals the file encryption key
  • which can then be used to decrypt the node's data


Controlling Access

Imagine that Alice just downloaded a new app: gov.city.bikeShare. It prompts her to login. The app is using the ZeroDark co-op login.

Since this is the first time Alice is asking for access to this particular app, the server generates the app encryption key, and gives it to Alice's app. This happens transparently.

Later Alice downloads another app: org.health.exerciseTracker. This app is also using the ZeroDark co-op login. And it has the technical ability to read the history generated by the gov.city.bikeShare app. That is, it can read the data directly from the cloud IF Alice grants it permission to do so.

Alice wants to grant this permission. She trusts the exercise tracker app. And she finds this app-integration especially useful. She taps a button to grant permission, and she is directed to a website.

The website will ask her to grant permission to the app. And will require an additional authorization step:

  • Alice will need to input a time-based one-time PIN. (e.g. using Google Authenticator, Microsoft Authenticator, Authy, Duo, etc)
  • Or Alice will need to verify a PIN sent to her mobile phone using SMS

The particular method of authorization is determined by Alice, who can configure this via the ZeroDark user dashboard.



Per-app encryption builds atop of the zero-knowledge encryption described in parts 1 & 2. It empowers users by giving them control of their data. In addition, it opens up new opportunities for developers, by allowing for new forms of inter-app communication & collaboration.

Next Step