keystore
========

KeyStore holds *unencrypted* secrets on behalf of users in memory for
a short time (of the order of a SSO session lifespan). User secrets
can be *opened* with a password (used to decrypt the key, which is
stored encrypted in a database), *queried* by presenting a suitable
authentication token, and *closed* (wiped and forgotten).

The database can provide multiple versions of the encrypted key (to
support multiple decryption passwords), in which case we'll try
them all sequentially until one of them decrypts successfully with
the provided password.

In order to query the KeyStore, you need to present a valid SSO
token for the user whose secrets you would like to obtain.

# API

The server exports an API over HTTP/HTTPS. All requests should be made
using the POST method and a Content-Type of *application/json*. The
request body should contain a JSON-encoded object. Responses will be
similarly JSON-encoded.

`/api/open` (*OpenRequest*)

Retrieve the encrypted key for a user, decrypt it with the provided
password, and store it in memory.

OpenRequest is an object with the
following attributes:

* `username`
* `password` to decrypt the user's key with
* `ttl` (seconds) time after which the credentials are automatically
  forgotten

`/api/get` (*GetRequest*) -> *GetResponse*

Retrieve the key for a user. GetRequest must contain the following
attributes:

* `username` whose key you wish to retrieve
* `sso_ticket` with a valid SSO ticket for the *keystore* service

If the request is successfully authenticated, GetResponse will contain
a single attribute *key*.

`/api/close` (*CloseRequest*)

Forget the key for a given user.

# Dovecot integration

The final consumer for user encryption keys is the Dovecot
service. The *dovecot-keylookupd* daemon can read the user public and
private keys from LDAP, and serve the *unencrypted* keys to Dovecot
using its [dict proxy
protocol](https://wiki2.dovecot.org/AuthDatabase/Dict).

TODO: explain the lookup protocol.

# Configuration

The *keystored* daemon loads its configuration from a YAML-encoded
file, */etc/keystore/config.yml* by default. It can contain the
following attributes:

* `sso_public_key_file`: path to the SSO Ed25519 public key
* `sso_service`: SSO service for this application
* `sso_domain`: SSO domain
* `ldap`: LDAP backend configuration
  * `uri`: LDAP server URI
  * `bind_dn`: bind DN (for simple bind, SASL is not supported)
  * `bind_pw`: bind password
  * `bind_pw_file`: bind password (load from this file), in
    alternative to *bind_pw*
  * `query`: Parameters for the LDAP search query
    * `search_base`: base DN for the search
    * `search_filter`: search filter. The filter string may contain a
      literal `%s` token somewhere, that will be replaced with the
      (escaped) username.
    * `scope`: search scope, one of *sub* (default), *one* or *base*
    * `public_key_attr`: attribute that contains the user's public key
    * `private_key_attr`: attribute that contains the user's encrypted
      key(s)
* `http_server`: HTTP server configuration
  * `tls`: contains the server-side TLS configuration:
    * `cert`: path to the server certificate
    * `key`: path to the server's private key
    * `ca`: path to the CA used to validate clients
    * `acl`: specifies TLS-based access controls, a list of entries
      with the following attributes:
      * `path`: regular expression to match the request URL path
      * `cn`: regular expression that must match the CommonName part
        of the subject of the client certificate
  * `max_inflight_requests`: maximum number of in-flight requests to
    allow before server-side throttling kicks in

The *dovecot-keylookupd* daemon uses a similar configuration, read by
default from */etc/keystore/dovecot.yml*:

* `ldap`: LDAP backend configuration, see above
* `keystore`: configures the connection to the keystore service
  * `url`: URL for the keystore service
  * `sharded`: if true, requests to the keystore service will be
    partitioned according to the user's *shard* attribute
  * `tls_config`: client TLS configuration
    * `cert`: path to the client certificate
    * `key`: path to the private key
    * `ca`: path to the CA used to validate the server
* `shard`: shard identifier for the local host. Must be set if
  keystore.sharded is true.