JLINC DID Method Specification
version 1.0 - October 12, 2018
Abstract
JLINC is a protocol for sharing data protected by an agreement on the terms under which the data is being shared.
This document specifies methods for creating and editing Decentralized IDs (DIDs) suitable for use with the JLINC protocol. It conforms to the requirements specified in the DID specification currently published by the W3C Credentials Community Group.
Copyright Notice
© JLINCLabs 2018
Table of Contents
- Notation and Conventions
- Definitions
- Overview
- Method Name
- Format
- Operations
6.1. Register
6.2. Resolve
6.2.1 Resolve Root
6.3. Supersede
6.4. Revoke
6.5. History
1. Notation and Conventions
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
2. Definitions
- JLINC - the JLINC protocol.
- JSON-LD - the JSON based serialization format defined at https://www.w3.org/TR/json-ld/.
- Base64 - the URL-safe variant of Base64 encoding defined in RFC 4648 as base64url without padding.
- JWT - a JSON Web Token, as defined in RFC 7519.
3. Overview
This specification defines the JLINC methods for creating, resolving and modifying DID documents. It conforms to the requirements specified in the DID specification currently published by the W3C Credentials Community Group.
4. Method Name
The namestring that shall identify the JLINC DID method is: jlinc.
A DID that uses this method MUST begin with the following prefix: did:jlinc. Per the DID specification, this string MUST be in lowercase.
5. Format
The JLINC DID has the following ABNF format:
jlinc-did = "did:jlinc:" id-string
id-string = 1* idchar
idchar = ALPHA / DIGIT / "-" / "_"
The idchar consists of the characters in the BASE64 UrlSafe character set defined in RFC 4648 as base64url without padding.
Example:
did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI
6. Operations
As a prerequisite the JLINC DID system publishes a master curve25519 public key for the system at the root of the service (https://did.jlinc.org/
) as well as in other venues. It may change from time to time.
6.1. Register (create)
POST to /register
with a JSON payload exemplified as follows:
{
"did": {
"@context": "https://w3id.org/did/v1",
"id": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI",
"created": "2018-10-13T17:00:00Z",
"publicKey": [{
"id": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI#signing",
"type": "ed25519",
"owner": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI",
"publicKeyBase64": "0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI"
},
{
"id": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI#encrypting",
"type": "curve25519",
"owner": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI",
"publicKeyBase64": "w4NUG7YTyEHnu25LetDETmr1eju4OQymYnjpwbQNkCo"
}]
},
"secret": {
"cyphertext": "yOXRmKr7vZO2kI90Wzce3vrVXRQgIoRc",
"nonce": "_qhHMFWMNTueliszyMJMqXBaKpgPXlyw"
},
"signature": "xCNUhhxGpdMNCu5H5ym8uspP1qMAzJa5edQBzlskGPIHlDyqJoD6D1BfDMTaGHKQS7kp"
}
The did
value MUST be the actual DID document that the registrant wishes to have recorded. It must be a valid DID document according to the DID specification and at a minimum it MUST contain:
- the URL for the generic DID context as specified in the DID specification
- a DID subject with the value
did:jlinc:
followed by a globally unique value, preferably the ed25519 signing public key in Base64 format that is also listed in the publicKey array; - a
created
timestamp in RFC 3339 format - the system will check the timestamp to be sure it is within reasonable bounds of the current time, allowing for network delays and small server time variations; - a publicKey array with at least two keys. One key MUST be an ed25519 signing public key identified by the DID owner followed by “#signing”. The other key MUST be a curve25519 encrypting public key identified by the DID owner followed by “#encrypting”. The encrypting key MAY be derived from the signing key by for example the libsodium function
crypto_sign_ed25519_pk_to_curve25519
or it MAY be independently generated.
The secret
is a random 32 byte value, encrypted using the NACL public key authenticated encryption method with the system master public key and the encrypting private key of the registrant associated with the encrypting public key above, and a nonce. This secret must be kept confidential and safe by the registrant, as it is required to perform any further operations on this DID, including revoking or superseding it.
The signature
is generated by first concatenating the id
value, a dot (.
), and the created
timestamp from the DID. A SHA256 value is then generated from the concatenated string, and signed using the private key that can be validated by the public key encoded in the DID subject (0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI
in the example above). Finally the signature is Base64 encoded.
If the DID has already been registered, the system will return an HTTP 409 status, and a JSON body of
{"error": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI already exists" }
.
If any of the system checks fail, an HTTP 400 status will be returned along with a JSON body containing the error description(s) under an error
key.
Otherwise, the system will return an HTTP 200 status and a JSON body containing
the new DID’s id
and a challenge
string. Example:
{
"id": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI",
"challenge": "8354eaccf3a8ba6bebfe009e13e4e92956143cb3342afb906d99d28724bd3df9"
}
At this point the new DID is not yet active and will not show up in any resolve requests.
The registrant must now construct a JSON document with the DID id
in question and a signature over a SHA256 hash of the challenge string with the private key that can be validated by the signing public key registered by the DID subject, in Base64 encoding. Example:
{
"id": "did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI",
"signature": "BdtJT_d7nTsc_oMrns9W7BNyPIN1uV3UaRv4im6qmVmhxP5rKx9JbFiq6-pkqAbcn"
}
A JWT is then constructed with this JSON document as the payload (i.e. “claims set”). The JWT MUST be secured with the SHA256 HMAC method (‘HS256’), and the HMAC secret key MUST be the value that was sent encoded in the system’s master public key in the initial request.
This JWT must then be POSTed to the /confirm
endpoint under a challengeResponse
key. Example:
{
"challengeResponse":"JWTString"
}
If the JWT verifies and the signature contained within also verifies, an HTTP status 201 is returned with a body containing a JSON document with the id
value of the newly activated DID.
6.2 Resolve (read)
GET /{DID}
DID MUST be the full did including the did:jlinc:"
prefix. Otherwise an HTTP status 400 will be returned with a JSON body of {"error":"cannot parse request"}
.
If the requested DID does not exist, an HTTP status 404 will be returned
with a JSON body of {"status":"not found"}
.
If the requested DID has been revoked an HTTP status 410 will be returned
with a JSON body of {"status":"revoked"}
.
If the requested DID has been superseded an HTTP status 303 will be returned, with a Location header giving the current resolve address for the entity in question. If there exists a chain of superseded DIDs only the location of most current one will be returned.
The JSON body will contain {"supersededBy": *new URL* }
.
If the request can be successfully fulfilled an HTTP status 200 and a Content-Type of “ld+json” will be returned, and the body will consist of the complete DID record.
6.2.1 Resolve Root (read)
GET /root/{DID}
A service provider may wish to record the original DID of a new client as the root DID, and be able to retrieve the current record for that entity by that original DID, even if it has been superseded.
This endpoint behaves the same way as Resolve, except that it will always return the most current record for that root rather than redirecting to it as with Resolve.
6.3 Supersede (update)
JLINC DIDs cannot be edited, only replaced. If the client wishes to replace an existing DID, either because the private key has been compromised or because the client wishes to edit the DID information, a new DID request MUST be created and POSTed to the /supersede
endpoint.
The procedure is exactly the same as register
with the exception that instead of secret
the request MUST contain a key called supersedes
with the superseded DID as its value.
The client MUST then confirm the request to the confirmSupersede
endpoint using the same confirm procedure and same secret for the JWT HMAC as was used to create the original DID.
6.4 Revoke (delete)
If a client wishes to revoke a DID rather than just superseding it, making it invalid for any further use, it MUST create a JWT with the payload consisting of a JSON document with the key of id
and value of the complete DID being revoked. Example:
{"id":"did:jlinc:0fil1CNmwie8TevnTJckrvqsk9nyvo8U_3YRLeagAhI"}
The JWT MUST be HMAC’d with the secret that was used to register the DID in question, and POSTed to the revoke
endpoint under a revokeRequest
key. Example:
{
"revokeRequest":"JWTString"
}
If there is a failure an HTTP status of 400 will be returned along with a JSON body containing the error description(s) under an error
key.
Otherwise an HTTP status of 200 will be returned with a JSON body containing the revoked DID under a revoked
key.
This action is permanent and cannot be undone.
6.5 History
GET /history/{DID}
As with resolve
, the DID MUST be the full did including the did:jlinc:"
prefix.
Querying the history of a DID, if the DID is resolvable, returns a JSON object containing an array of related DIDs, each being a supersession of the previous one in the chain. The last DID in the chain MUST either be the currently valid one, or a final revoked version.
Example:
{
"history": [
{
"did": {...},
"superseded": "2018-10-23T17:00:00Z"
},
{
"did": {...},
"superseded": "2018-12-25T17:00:00Z"
},
{
"did": {...},
"valid": "2018-12-25T17:00:05Z"
}
]
}
Querying history on any DID in chain will return the whole chain.