serval-dna/doc/REST-API-Keyring.md
Andrew Bettison 98ec1c9608 Redesign the Keyring REST API (fixes #132)
The new API follows REST rules by using the proper request verbs:
POST, PUT, PATCH and DELETE, instead of just GET.

The legacy GET-only API is still supported for backward compatibility,
but not longer tested or documented.

Add a new query-single-identity operation.

Implement the lock-single-identity operation, which until now had been
documented but not yet implemented.  Whenever a single identity is
locked (released), any other unlocked identities with the same PIN are
flagged to indicate that the PIN is not "fully" unlocked, so that the
next time the PIN is entered, the slot decryption is re-tried for
non-loaded identities, and the locked identity will be unlocked again.

Update the 'keyring' and 'keyringrestful' test scripts:
- refactored to reduce curl command-line clutter in test cases
- now tests the redesigned request verbs and paths
- added a test for GET /restful/keyring/SID
- added a test for PUT /restful/keyring/SID/lock
2018-03-19 18:06:23 +10:30

292 lines
12 KiB
Markdown

Keyring REST API
================
[Serval Project][], December 2017
Introduction
------------
The [Serval Mesh network][] is based on [cryptographic identities][] that can
easily be created by any node at any time. Each [Serval DNA][] daemon that
runs on a node in the network stores its own identities in the [Keyring][], an
encrypted store protected by passwords, and gives applications access to the
Keyring via the **Keyring REST API** described in this document. Using this
API, client applications can add, remove, unlock, lock, query, and modify
identities in the keyring.
Basic concepts
--------------
### Serval ID
Every identity in the [Serval mesh network][] is represented by its **Serval
ID**, (usually abbreviated to [SID][], and formerly known as “Subscriber ID”),
which is a unique 256-bit public key in the [Curve25519][] *crypto-box* key
space. The SID is used:
* as the network address in the [Serval Mesh network][]
* to encrypt [MDP][] messages
* to identify the senders, recipients and authors of [Rhizome bundles][]
* to identify the parties in a [MeshMS conversation][]
The Serval ID public key is derived from the *Serval ID secret key*, which is
generated when the identity is first created, and stored in the keyring. The
original implementation of [Serval DNA][] generated the *Serval ID secret key*
at random, but since the introduction of [combined IDs](#combined-ids), the
secret key is derived from the [Serval Signing ID](#serval-signing-id) secret
key.
### Serval Signing ID
Every identity in the [Serval mesh network][] has a **Serval Signing ID**,
which is a unique 256-bit public key in the [Curve25519][] *crypto-sign* key
space that is generated by choosing a *secret signing key* at random when the
the identity is first created. The Signing ID is used:
* to prevent forgery of [Serval Mesh network][] routing messages
* to authenticate non-encrypted [MDP][] messages
The large size of the key space means that, as long as the secret key choice
is truly random, the [probability of two identities having the same
ID][birthday paradox] is negligible, even if billions of identities are
generated.
### Combined IDs
Since July 2016, [Serval DNA][] generates [Serval ID](#serval-id)s by deriving
them from the [Serval Signing ID](#serval-signing-id), so when creating a new
identity, only a single secret key is generated at random instead of two. This
change was made possible by replacing the [NaCl][] cryptographic library with
[libsodium][], which provides a primitive to map keys from the *crypto-sign*
key space to the *crypto-box* key space (but not vice versa, which is
mathematically impossible).
These related keys are called *combined IDs*.
Eventually, the [Serval ID](#serval-id) will be replaced by the [Serval Signing
ID](#serval-signing-id) throughout the Serval software and protocols, but will
still be called “Serval ID”. The encryption key (in the *crypto-box* key
space) will be derived from the signing key on the fly whenever needed, so there
will only be one kind of Serval ID, which will be a great simplification.
### DID
The **DID** ([Dialled Identity][]) is a telephone number, represented as a
string of between 5 and 31 ASCII characters from the set `123456789#0*`. It is
used by the [DNA][] protocol to allow [Serval mesh network][] users to discover
each other by telephone number; the first step in establishing a mesh voice
call.
### Name
The **Name** is a short, unstructured string between 1 and 63 bytes in length,
assigned by a human user to an identity. It is used to represent the identity
to human users, as it is more recognisable than a hexadecimal [SID](#serval-id)
or a [DID](#did) (telephone number).
Serval DNA does not interpret the name, merely stores it, so the name may use
any encoding on which all clients agree, such as ASCII or [UTF-8][]. Since it
is intended for human consumption, it is recommended that it contain only
printable characters, that it contain no carriage-motion characters (eg, TAB
U+0009 or LF U+0010), and that it not start or end with white space, but Serval
DNA does not enforce any such rules. The only restriction enforced by Serval
DNA is that it contain no zero bytes.
### Rhizome Secret
The *Rhizome Secret* is a secret key, separate from the [SID](#serval-id)
secret, that is generated randomly for each new identity, and stored in the
keyring as part of the identity. The Rhizome Secret is used to securely encode
the [Bundle Secret][] of a bundle into its [manifest][], in the form of the
[Bundle Key][], thus relieving [Rhizome][] applications of the burden of having
to store and protect Bundle Secrets themselves.
### PIN
When an identity is created, it can optionally be given a PIN (passphrase). If
the PIN is *empty* then the identity is permanently unlocked (visible).
Identities with a non-empty PIN are stored encrypted in the keyring file.
Inspection of the keyring file will not reveal their presence unless the
correct PIN is supplied, because all unused entries in the keyring file are
filled with pseudo-random content that is indistinguishable from encrypted
identities.
If a PIN is lost and forgotten, then the identity (identities) it unlocks will
remain locked and unusable forever. There is no “master PIN” or back-door.
### Identity unlocking
All Keyring requests can supply a passphrase using the optional **pin**
parameter, which unlocks all keyring identities protected by that passphrase,
prior to performing the request. Serval DNA caches every PIN it receives until
the PIN is revoked using the [lock request](#get-restful-keyring-lock), so once
an identity is unlocked, it remains visible until explicitly locked.
Keyring REST API common features
--------------------------------
### Keyring JSON result
All Keyring requests relating to a single identity that do not produce a
special response content for the outcome, return the following augmented [JSON
result][] object as the HTTP response content:
{
"http_status_code": ...,
"http_status_message": "...",
"identity": {
"sid": "<hex64>",
"identity": "<hex64>",
"did": "...",
"name": "..."
}
}
* the `sid` field is the [SID](#serval-id); a string containing 64 uppercase
hexadecimal digits
* the `identity` field is the [Signing Id](#serval-signing-id); a string
containing 64 uppercase hexadecimal digits
* the `did` field is the string [DID](#did); omitted if the identity has no DID
* the `name` field is the string [Name](#name); omitted if the identity has no
name
Keyring REST API operations
---------------------------
### GET /restful/keyring/identities.json
Returns a list of all currently unlocked identities, one identity per row in
[JSON table][] format. The following parameters are recognised:
* **pin** (optional) = a passphrase to unlock identities prior to listing;
see [identity unlocking](#identity-unlocking)
The table columns are:
| heading | content |
|:---------- |:------------------------------------------------------------------------- |
| `sid` | the [SID](#serval-id), a string of 64 uppercase hex digits |
| `identity` | the [Signing ID](#serval-signing-id), a string of 64 uppercase hex digits |
| `did` | the optional [DID](#did) (telephone number); `null` if none is assigned |
| `name` | the optional string [Name](#name); `null` if none is assigned |
### POST /restful/keyring/add
Creates a new identity with a random [SID](#serval-id). This request does not
accept parameters in the request body (eg, using a [Content-Type][] of
[multipart/form-data][]), but does accept the following [query parameters][] in
the *path*:
* **pin** (optional) = the passphrase for the new identity; if present, then
the new identity is protected by the given passphrase; see [identity
unlocking](#identity-unlocking) -- note that the newly created identity is
already unlocked when this request returns, because the passphrase has been
added to the PIN cache
* **did** (optional) = the [DID](#did) of the new identity; empty or absent
to indicate no DID, otherwise must be valid
* **name** (optional) = the [Name](#name) of the new identity; empty or
absent to specify no name, otherwise must be valid
If any parameter contains an invalid value then the request returns [400 Bad
Request][400]. Returns [201 Created][201] if an identity is created; the [JSON
result](#keyring-json-result) describes the identity that was created.
### GET /restful/keyring/SID
Return the details of an existing unlocked identity with a given
[SID](#serval-id). The following [query parameters][] are recognised:
* **pin** (optional) = a passphrase to unlock the identity prior to querying
it; see [identity unlocking](#identity-unlocking)
If there is no unlocked identity with the given SID, this request returns [404
Not Found][404]. Otherwise it returns [200 OK][200] and the [JSON
result](#keyring-json-result) describes the identity.
### DELETE /restful/keyring/SID
Removes an existing unlocked identity with a given [SID](#serval-id). The
following [query parameters][] are recognised:
* **pin** (optional) = a passphrase to unlock the identity prior to deleting
it; see [identity unlocking](#identity-unlocking)
If there is no unlocked identity with the given SID, this request returns [404
Not Found][404]. Otherwise it returns [200 OK][200] and the [JSON
result](#keyring-json-result) describes the identity that was removed.
### PATCH /restful/keyring/SID
Sets and/or clears the [DID](#did) and/or [Name](#name) of the unlocked
identity that has the given [SID](#serval-id). The following [query
parameters][] are recognised:
* **pin** (optional) = a passphrase to unlock the identity prior to deleting
it; see [identity unlocking](#identity-unlocking)
* **did** (optional) = the [DID](#did) to assign to the identity; empty to
clear the DID, otherwise must be valid
* **name** (optional) = the [Name](#name) to assign to the identity; empty to
clear the name, otherwise must be valid
If a parameter is missing, then the corresponding field of the identity is left
unchanged. If a parameter is set to an empty string, then the corresponding
field of the identity is erased.
If any parameter contains an invalid value then the request returns [400 Bad
Request][400]. If there is no unlocked identity with the given SID, this
request returns [404 Not Found][404].
### PUT /restful/keyring/SID/lock
Locks an existing identity with a given [SID](#serval-id).
If there is no unlocked identity with the given SID, this request returns [404
Not Found][404]. Otherwise it returns [200 OK][200] and the [JSON
result](#keyring-json-result) describes the identity that was locked.
-----
**Copyright 2015 Serval Project Inc.**
**Copyright 2016-2018 Flinders University**
![CC-BY-4.0](./cc-by-4.0.png)
Available under the [Creative Commons Attribution 4.0 International licence][CC BY 4.0].
[Serval Project]: http://www.servalproject.org/
[CC BY 4.0]: ../LICENSE-DOCUMENTATION.md
[Serval Mesh network]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:mesh_network
[Serval DNA]: ../README.md
[REST-API]: ./REST-API.md
[Keyring]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:keyring
[cryptographic identities]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:security_framework
[Curve25519]: https://en.wikipedia.org/wiki/Curve25519
[libsodium]: https://libsodium.org/
[NaCl]: https://nacl.cr.yp.to/
[SID]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:sid
[birthday paradox]: http://en.wikipedia.org/wiki/Birthday_problem
[Dialled Identity]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:did
[DNA]: http://developer.servalproject.org/dokuwiki/doku.php?id=content:tech:dna
[MDP]: ./Mesh-Datagram-Protocol.md
[Rhizome]: ./REST-API-Rhizome.md
[Rhizome bundles]: ./REST-API-Rhizome.md#bundle
[manifest]: ./REST-API-Rhizome.md#manifest
[Bundle Secret]: ./REST-API-Rhizome.md#bundle-secret
[Bundle Key]: ./REST-API-Rhizome.md#bundle-key
[MeshMS conversation]: ./REST-API-MeshMS.md#conversation
[JSON result]: ./REST-API.md#json-result
[JSON table]: ./REST-API.md#json-table
[UTF-8]: https://en.wikipedia.org/wiki/UTF-8
[200]: ./REST-API.md#200-ok
[201]: ./REST-API.md#201-created
[202]: ./REST-API.md#202-accepted
[400]: ./REST-API.md#400-bad-request
[404]: ./REST-API.md#404-not-found
[419]: ./REST-API.md#419-authentication-timeout
[422]: ./REST-API.md#422-unprocessable-entity
[423]: ./REST-API.md#423-locked
[500]: ./REST-API.md#500-server-error