Northwire's encryption scheme and security design

Northwire tries it's best to protect your private data despite Misfin, like SMTP, does not support end-to-end encryption. Northwire does this through several layers of encryption, keyed hashes as indices and constantly rotating the keys.

Encryption and the chain of keys

Northwire encrypts your messages as soon as it receives them and can decide which mailbox it belongs to. However, encrypting everything with a single key is dengerous. If that key ever gets stolen, then the entire database is at risk of being decryptable by outsiders.

ascii diagram of encrypting mails with a single key
         ┌────────────┐
key ───> │  messages  │
         └────────────┘

         ┌─xxxxxxxxxx─┐
key ───> │  messages  │ Also in danger of being leaked.
 ^       └─xxxxxxxxxx─┘ And a major pain to re-encryt all messages
 |
stolen

Insead, based on the assumption that binray explots are much harder then finding some way to dump the database (and easier to detect due to the large attempts needed and failed attempt often leads to a crash), Northwire uses rotating server keys, a key² key (key encrypting other keys) to make leaks much harder.

ascii diagram of Northwire's encryption chain
Generates new keys
    ┌────────┐
    │  KMS   │ ───┐
    └────────┘    │
      │           │
      │   ┌───────v──────┐
      │   │ Key rotation │
      │   │   Worker     │
      │   └──────────────┘
      │                 │
      │           │     │
      │           │     │ Update
      v root key  │     │ stored key²
     ┌─────────┐  │     v                     ┌──────────┐
     │ Server  ├──┼──> key² ─────> key ─────> │ messages │
     └─────────┘  │                           └──────────┘
                  │
           Serer  │ Database

This way the server process only needs to hold onto a cached copy of key². The actual message encryption key can be stored in the database, temporarly decrypted in-memory to decrypt messages and quickly discarded to ensure attackers cannot find them lingering around. Since re-encryption now only means re-encryption several keys, the worker can afford very frequent refreshes. Every 10 minutes or so, the KMS (key managmetn service), issues a new root key, the worker rotates the keys in databse and wipes old (with margin for error) key²s from database to ensure old root key leaks cannot threaten the security of the messages.

This leaves the active root key critical and the obvious target of attacks. Which Northwire mitigats by mprotect-ing the root key with PROT_NONE and only un-seal it for the split second during the decrypton of key². Reading outside of that window means a detectable server carsh.

All in all, a database dump means little to attackers beisdes able to find indices and table relations. While a bug in the server, even leading to ACE (Arbitrary Code Execution) would need the attcker figuring out how to

Which is quite a very tall bar.

Hashing the indices

Instead of storing raw indexes like `user@example.com` to indicate who the message belongs to. Northwire uses BLAKE2b("user@example.com", <sever-owned-string>) as indices, without the server string, anyone reading the database can only see that a specific hash owns a specific number of messages.

Example of hashed indices
hash (index)│ encrypted message
────────────┼───────────────────
0baf8ee6751 │ xxxxxxxxxxxxxxxxxx
dd2e5357a68 │ xxxxxxxxxxxxxxxxxx
0baf8ee6751 │ xxxxxxxxxxxxxxxxxx
0baf8ee6751 │ xxxxxxxxxxxxxxxxxx
0baf8ee6751 │ xxxxxxxxxxxxxxxxxx
18618a15bcd │ xxxxxxxxxxxxxxxxxx

canary

Of course, sometimes you need both the index and the actual value. For cases that this works with (requiring databse design, so we don't depend on them directly in queries), Northwire encrypts the vlaues with the method described in the previous section and decrypts it on the host with approprate hygene.

Self sandboxing

But what if there's a bug in one of the dependencies or the core logic of Northwire itself? This is where sandboxing comes in. Self sandboxing is a form of sandboxing that is applied by the process itself instead of by a outside program (like it is with AppAromor, SELinux or SystemD).

Most server applications (including Northwire) runs in 2 phases. Initialiation and the main serving loop. Initialization requires more functionality that the OS provides -- creating threads for the thread pool, loading certificates, seeding randomness, you name it and initalizing mostly runs on trusted data - service configration, key files, stdin to name a few. But once in the main loop, most of the OS features are unneded and it starts accepting untrusted input. Why would a mail server ever need to call setuid? To write to anywhere but it's own scratch directory? Or why would it want to fork if it runs on a thread pool and it's already initialized?

Typical server startup and serving phase
Program start
    │
    v
 Initializtion
    │<───────────────── enables sandboxing
    v                    (cannot setuid, ioctl, chdir, fork, exec,
 Main logic loop<────┐    chroot, creat UNIX sockets, settimeofday
    └────────────────┘    etc..)

It doesn't. With enough understnading of how the program works, it is possible to specsify what is normal behaivour after initilization and only allow the expected sysetem calls and file paths to be accessed. The program doing otherwise indicates a lack of understanding or real malicious behaivour. In either case, the program should be stopped. Northwire does this using minijail (a high level library wrapped around Seccomp and Landlock on Linux).

Due to the serving loop needing less varities of OS features, self sandboxing after initlazation provides stricter coverage compared to external sandboxes, which cannot distingush between initialization and the service loop. Letting the broder initalization's capablity set through.

On a breach

Given the security machinisms, a database breach/dump if ever happened (which is hard in the first place and is obvious) is less likely to be disastrous. Keys gets rotated and there's limited ways to store and move data outside of the intended use.

A data breach would lead to leaking data relation but never the actual content. Likewise a root key leak would resolve itself. Not saying it is impossible. But Northwire is designed to take blows and survive with your privace intact.





This page is rendered from Gemini Gemtext to HTML. We recommened to get a proper Gemini client for the best experience.