Commit 075edd3b authored by Laura Kalbag's avatar Laura Kalbag
Browse files

Merge branch 'master' of source.ind.ie:indienet/docs

parents 54c995e0 64a6f7d0
......@@ -3,7 +3,7 @@ title = "Design"
weight = "10"
+++
The Indienet Engine is a loosely-coupled ActivityPub server implementation for single-person web apps in JavaScript (Node.js) that handles the following features via REST and WebSocket APIs and a realtime RethinkDB database (see [technology stack](../technology-stack)):
The Indienet Engine is a loosely-coupled ActivityPub server implementation for single-person web apps in JavaScript (Node.js) that handles the following features via REST and WebSocket APIs and [message persistence](/engine/technology-stack/database/) (see [technology stack](../technology-stack)):
* [ ] Authentication
* [ ] Server-to-server interactions
......
......@@ -23,6 +23,11 @@ Just like any web app, this means that we must trust the host not to:
While these issues are blatantly apparent for web clients, they are also faced by native apps today in a world of App Stores and automatic updates.
## Spikes
* Documentation: [/spikes/security](/spikes/security)
* Source: https://source.ind.ie/indienet/spikes/security
## Resources
* [End-To-End Web Crypto: A Broken Security Model](https://www.indolering.com/e2e-web-crypto/)
......
......@@ -5,9 +5,9 @@ weight = "30"
The requirements for authentication are affected by the following requirements and aspects of the Indienet:
* Indienet sites/apps are personal sites, there is only a single owner that uses each site/app. (We do not have the concept of users and we do not need usernames.)
* Indienet sites/apps are federated personal web sites/apps, there is only a single owner that uses each site/app. (We do not have the concept of users and we do not need usernames.)
* Private messages must be end-to-end encrypted (see [security](/engine/security))
* Private messages must be end-to-end encrypted (see [/engine/security](/engine/security) and [/spikes/security](/spikes/security))
* Private messages must be accessible at any time in the future from the server from any authenticated client.
......@@ -17,24 +17,56 @@ As such, the current plans for authentication are:
1. On first use, a private and public key keypair is generated on the client.
2. The private key is symmetrically encrypted (e.g., via [Triplesec]( https://keybase.io/triplesec/)) using a strong password (we will recommend the use of a password manager) and sent to the server, along with the public key.
2. The private key is symmetrically encrypted via a key generated using a strong password (we will recommend the use of a password manager). The encrypted private key is then sent to the server, along with the public key.
3. A copy of the private key is kept on the client but can be re-requested from the server at any time and from any client. (The private key is useless unless it is decrypted with the strong password set in Step 2.)
3. An unencrypted copy of the private key is kept on the client – e.g., as an unextractable key via the WebCrypto API – (so the site owner doesn’t have to keep entering their password) but the encrypted private key can also be re-requested from the server at any time and from any client. (The private key is useless unless it is decrypted with the strong password set in Step 2.)
4. The public key is also served at a well-known location and is used by other clients to encrypt private messages for the owner of the instance.
4. The public key is also served at a well-known location on the server and is used by other clients to encrypt private messages for the owner of the instance.
(We are currently spiking this out. See [/spikes/security](/spikes/security))
## Client authentication
Client authentication for the REST and WebSocket APIs will use JWT with publickey authentication.
## Private Key Retrieval
1. If the client doesn’t have a copy of the private key, it requests it from the server.
2. The client decrypts the private key using the owner’s strong password.
2. The client decrypts the private key using the symmetric key generated from the owner’s strong password.
3. It uses the decrypted private key to authenticate using public key authentication (see below).
## Future thoughts:
* Would using a Service Worker to handle cryptographic functions in the browser have security advantages? (Keep an eye on [browser compatibility](https://caniuse.com/#search=service%20worker) – once all evergreen browsers support this, let’s take a look.)
## General resources
* [OpenCrypto](https://github.com/safebash/OpenCrypto): OpenCrypto is a Cryptographic JavaScript library built on top of [WebCrypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)
* [Web Crypto Live Table](https://diafygi.github.io/webcrypto-examples/)
* [WebKit: Update on Web Cryptography](https://webkit.org/blog/7790/update-on-web-cryptography/): “When developing with pure JavaScript crypto libraries, secret or private keys are often stored in the global JavaScript execution context. It is extremely vulnerable as keys are exposed to any JavaScript resources being loaded and therefore allows XSS attackers be able to steal the keys. WebCrypto API instead protects the secret or private keys by storing them completely outside of the JavaScript execution context.”
* [Storing Cryptographic Keys in Persistent Browser Storage](https://pomcor.com/2017/06/02/keys-in-browser/)
* [WebCrypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)
* [WebCrypto examples](https://github.com/diafygi/webcrypto-examples)
* [Cryptobench.js](https://github.com/mnasyrov/cryptobench-js)
* An older article (2013) by Alex Maccaw on [end-to-end encryption in web apps](https://blog.alexmaccaw.com/end-to-end-encryption-in-js-web-apps).
## Public Key Authentication
Note: we should keep in mind how Mastodon uses public-key authentication for message verification (see https://web-payments.org/vocabs/security#publicKey)
### Resources
* For a general guide on application of cryptography for developers, see the book [Serious Cryptography: A Practical Introduction to Modern Encryption](https://nostarch.com/seriouscrypto)
* [feathers-authentication-publickey](https://github.com/amaurymartiny/feathers-authentication-publickey): “Public Key authentication strategy for feathers-authentication using Passport” ([Example.](https://github.com/amaurymartiny/feathers-authentication-publickey/tree/master/example))
* [passport-publickey](https://github.com/timfpark/passport-publickey): “Passport strategy for authenticating using a public/private key pair to sign a nonce challenge.”
......
......@@ -3,10 +3,22 @@ title = "Database"
weight = "70"
+++
**Note:** this decision is under review.
**Note:** this decision is under review. Options:
## LevelDB
**Note:** What can we learn from our experience with [Heartbeat Node](https://source.ind.ie/project/heartbeat-node) - e.g., regarding the use of LevelDB for this purpose? (e.g., see the [implementation of the StreamWeaver class](https://source.ind.ie/project/heartbeat-node/blob/master/StreamWeaver.coffee)).
## Secure Scuttlebutt
[Secure Scuttlebutt](https://github.com/ssbc/secure-scuttlebutt) is built on LevelDB, specifically for p2p messaging. Would the higher-level abstraction aid or limit us?
Related:
* [Kappa architecture](http://milinda.pathirage.org/kappa-architecture.com/)
* [Scuttlebot](https://github.com/ssbc/scuttlebot)
## RethinkDB
RethinkDB is a realtime database that has a changefeeds feature that could simplify our production footprint/deployment requirements, server-side code dramatically and enable us to keep an event-driven workflow throughout.
......
+++
title = "Security"
weight = "20"
+++
## Goals
Security goals for Indienet are:
1. Implement end-to-end encrypted private messages between federated personal web sites (with the same level of protection as PGP in email).
2. Enable people to access their federated personal web site, and their entire list of end-to-end encrypted private messages, from any browser/device using a master password. If they haven’t authenticated on a certain device before, they will have to enter their master password the first time only.
3. Research and use the latest cryptography knowledge and best practices whenever possible.
## General notes
1. Please keep all spikes in separate projects in the /spikes/authentication subgroup and push to the source repo regularly as you’re working on them.
2. Please create issues for the various tasks you need to perform for each spike in the source repo and update and close the issues as you go.
3. Please document findings and questions on the notes sections of the spikes on this page as you work.
4. JavaScript should be in [JavaScript Standard Style](https://github.com/feross/standard).
## Source
* https://source.ind.ie/indienet/spikes/security
## Spike 1: OpenCrypto in browser: key generation, persistence, & retrieval
![Authentication flow](/images/spikes/authentication.png)
### Spike goals
1. Explore the design; does it work? Do we encounter any pain points or obvious flaws?
2. Explore the WebCrypto API-based OpenCrypto library. Is it fit for purpose?
### Spike tasks
Please keep the Web interface as simple as possible. Plain HTML.
Please also keep the server (Node) as simple as possible. Plain [Express](https://expressjs.com).
1. **Create a public key and an *unextractable* private key**
This will result in:
* The an *unextractable* unencrypted private key (`unencryptedPrivateKey`)
* The public key (`publicKey`)
2. **Persist the unextractable `unencryptedPrivateKey` in IndexedDB** (see [level-browserify](https://www.npmjs.com/package/level-browserify) for a simpler LevelDB interface for IndexedDB).
This means that:
* The private key material is not accessible from JavaScript (we – or a malicious future script – can still use the key but can’t extract it)
* The owner of the site doesn’t have to keep entering their password (they will have to – once – the first time they use a different browser or a different device to access their personal federated web site)
3. **Create an ephemeral symmetric key from a master password** that the site owner chooses (`passwordKey`) and use it to .
4. **Encrypt the `unencryptedPrivateKey`** with the `passwordKey` to get the `encryptedPrivateKey`
5. **Transfer `publicKey` and `encryptedPrivateKey` to the server** and store them there. You can store them on the file system.
6. **Make `publicKey` accessible from a route on the server** (just use plain Express on the server):
`GET /public-key` → returns `publicKey`
7. **Make `encryptedPrivateKey` accessible from a route on the server:**
`GET /private-key` → returns `encryptedPrivateKey`
This means:
* `publicKey` is accessible to any other instance at a well-known location (it will also be advertised within the actor object in ActivityPub, but don’t worry about that right now)
* `encryptedPrivateKey` can be downloaded to any client that the owner uses in the future. When the owner provides the password they used in Step #2, we can recreate the symmetric key and decrypt `encryptedPrivateKey` to obtain `privateKey` (which we persist, as in Step 1, on the new client).
Note: at this point, the server has been set up/configured for the first time. It has been claimed by the owner. From here on, to authenticate the owner, we will use publickey authentication (see Spike 3).
### Notes
* None yet.
---
## Spike 2: Re-implement Spike 1 using libsodium
Use [libsodium](https://download.libsodium.org/doc/) to implement Spike 1 (please keep all spikes separate and checked into source control; don’t overwrite Spike 1).
### Questions
1. Which implementations should we spike (and why?)
* [libsodium.js](https://github.com/jedisct1/libsodium.js)
* [js-nacl](https://github.com/tonyg/js-nacl)
* [Natrium](https://github.com/wilhelmmatilainen/natrium)
* [Natrium Browser](https://github.com/wilhelmmatilainen/natrium-browser)
2. What are the differences in workflow between Spike 1 and Spike 2? Which is easier to work with?
3. What are the security implications of using a libsodium port (which gives us better ciphers) instead of the WebCrypto API (which gives us unextractable keys)? (This is a question we should post to some friendly neighbourhood cryptographers in the community.)
### Notes
* None yet.
---
## Spike 3: Publickey authentication
To authenticate with the server for the REST and WebSocket APIs, we will be using JWT with a publickey authentication scheme, implemented within Passport.
For publickey authentication, we will:
1. Receive a `nonce` from the server
2. Sign the `nonce` with our `unencryptedPrivateKey` to get a `signedNonce`
3. Transfer the `signedNonce` to the server
4. On the server, verify the signature using our `publicKey`
5. If the signature is verified, the server will return the JWT, which we will persist on the client and use in subsequent requests. (We should import the JWT using the WebCrypto API as an unextractable key.)
For this spike, please explore two versions, in order:
1. Version 1: Vanilla Express and Passport:
* [Express](https://expressjs.com)
* [Passport](http://www.passportjs.org)
* Test: does [passport-publickey](https://github.com/timfpark/passport-publickey) do what we want, or can we build what we want on top of it, or do we need to look into implementing our own publickey authentication scheme on top of Passport
* Research: are there any other publickey authentication schemes implmented in JavaScript. Quickly test them out and report back if there are.
2. Version 2: [FeathersJS](https://feathersjs.com)
### Notes
* None yet.
---
## Spike 4: End-to-end encrypted private messages
Mock a separate, second node (`node2`) that has a:
* private key (`privateKeyNode2`)
* public key (`publicKeyNode2`)
* a message it wants to send to our node (`messageNode2`)
The goal is for this second node is to send us a private message, encrypted with our public key, that we will decrypt and read using our private key (as created in Spikes 1 & 2; please keep the spikes separate).
Continue to mock `node2` to:
1. Create a secret symmetric session key (`secretSessionKeyNode2`)
2. Encrypt `messageNode2` with `secretSessionKeyNode2` to get `encryptedMessageNode2`
3. Encrypt `secretSessionKeyNode2` with our `publicKey` to get `encryptedSecretSessionKeyNode2` (`node2` encrypts `secretSessionKeyNode2` with our public key since the message is being sent to us from `node2` and we should be the only ones able to decrypt it, using our `privateKey`)
**We then assume that node2 communicates, via ActivityPub, `encyptedMessageNode2` and `encryptedSecretSessionKeyNode2` to us.** Please don’t mock separate nodes or networking between them for this spike.
Then, on our node:
1. Use `privateKey` to decrypt `encryptedSecretSessionKeyNode2` and get `secretSessionKeyNode2`
2. Use `secretSessionKeyNode2` to decrypt `encryptedMessageNode2` and get `messageNode2`
3. Display `messageNode2`
### Notes
* None yet.
---
## Related resources
For more resources, references, etc., see: [/engine/technology-stack/authentication](/engine/technology-stack/authentication)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment