Serval Project, December 2017
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.
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, the secret key is derived from the Serval Signing ID secret key.
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 is negligible, even if billions of identities are generated.
Since July 2016, Serval DNA generates Serval IDs by deriving them from the 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 will be replaced by the 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.
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.
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 or a 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.
The Rhizome Secret is a secret key, separate from the SID 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.
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.
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, so once an identity is unlocked, it remains visible until explicitly locked.
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; a string containing 64 uppercase hexadecimal digits - the
identity
field is the Signing Id; a string containing 64 uppercase hexadecimal digits - the
did
field is the string DID; omitted if the identity has no DID - the
name
field is the string Name; omitted if the identity has no name
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
The table columns are:
heading | content |
---|---|
sid |
the SID, a string of 64 uppercase hex digits |
identity |
the Signing ID, a string of 64 uppercase hex digits |
did |
the optional DID (telephone number); null if none is assigned |
name |
the optional string Name; null if none is assigned |
Creates a new identity with a random SID. 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 -- 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 of the new identity; empty or absent to indicate no DID, otherwise must be valid
-
name (optional) = the 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. Returns 201 Created if an identity is created; the JSON result describes the identity that was created.
Return the details of an existing unlocked identity with a given SID. The following [query parameters][] are recognised:
- pin (optional) = a passphrase to unlock the identity prior to querying it; see identity unlocking
If there is no unlocked identity with the given SID, this request returns 404 Not Found. Otherwise it returns 200 OK and the JSON result describes the identity.
Removes an existing unlocked identity with a given SID. The following [query parameters][] are recognised:
- pin (optional) = a passphrase to unlock the identity prior to deleting it; see identity unlocking
If there is no unlocked identity with the given SID, this request returns 404 Not Found. Otherwise it returns 200 OK and the JSON result describes the identity that was removed.
Sets and/or clears the DID and/or Name of the unlocked identity that has the given SID. The following [query parameters][] are recognised:
-
pin (optional) = a passphrase to unlock the identity prior to deleting it; see identity unlocking
-
did (optional) = the DID to assign to the identity; empty to clear the DID, otherwise must be valid
-
name (optional) = the 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. If there is no unlocked identity with the given SID, this request returns 404 Not Found.
Locks an existing identity with a given SID.
If there is no unlocked identity with the given SID, this request returns 404 Not Found. Otherwise it returns 200 OK and the JSON result describes the identity that was locked.
Copyright 2015 Serval Project Inc.
Copyright 2016-2018 Flinders University
Available under the Creative Commons Attribution 4.0 International licence.