JLINC Protocol

version 7 - March 26, 2020

Abstract

JLINC is a protocol for sharing data protected by an agreement on the terms under which the data is being shared.

The agreement is known as an Information Sharing Agreement, and can be a reference to a standardized agreement (a Standard Information Sharing Agreement or SISA) or a one-off specialized contract.

The base profile is HTTP-based, but any protocol that affords methods for initiating and responding to data transactions, along with metadata (headers) accompanying those interactions could be adapted.

© JLINCLabs 2020

Table of Contents

  1. Notation and Conventions
  2. Definitions
  3. Overview
  4. Establishing a SISA
  5. SISA Events
  6. Consent Receipts
  7. Audit

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

3. Overview

The JLINC protocol is in essence very simple. It starts with the existence of one or more Standard Information Sharing Agreements (SISAs) for a given industry or activity type. We envision these being hammered out by citizen representative groups together with industry organizations. JLINC Labs has created a basic SISA to kick off the process.

The SISA is chosen and cryptographically signed by the initiating party (a Rights-Holder or Data-Custodian) and offered to the receiving party. The receiving party, if it recognizes and accepts the SISA, counter-signs it and returns it to the initiating party.

Both parties retain a copy of the signed and counter-signed SISA, and may submit it to a Ledger of their choice to facilitate non-repudiation.

Once the SISA has been completed, the parties may use the JLINC protocol to exchange information accompanied by a hash of the SISA, indicating that the exchange is understood to be happening in the context of the SISA. Records of these exchanges (SISA events) are also retained by both parties and may also be submitted to a Ledger.

4. Establishing a SISA

A completed SISA records an agreement between two parties. One of the parties may be a Rights-Holder and the other a Data-Custodian, or in some use cases both parties may be considered to be Data-Custodians.

The heart of the SISA is the agreement itself, which may be the text of the agreement, or a URI pointing to a specific agreement instance at a SISA Terms Resource.

The format of that URI SHOULD be {scheme}{host}{version}{hash-of-the-agreement}.

A JSON-LD document is then created, which MUST include a @context, a version, a jlincId (which SHOULD be a collision resistant nonce), and one of either agreementText or agreementURI. Example:

{
  "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "jlincId": "36ea8b9202bf0fb2e6c908c59394891c",
  "agreementURI": "https://sisa.jlinc.org/v1/3b39160c2b9ae7b2ef81c3311c7924f1c4d4fa9ca47cfe7c840c9852b50d68d5"
}

This JSON-LD document MUST then be encapsulated in a JWT, which we’ll call the Agreement JWT. That Agreement JWT is the byte-for-byte canonical representation of the agreement – the value which is to be hashed and signed. The Agreement JWT SHOULD be secured with the SHA256 HMAC method, using a secret value known to the party offering the SISA, so that offerer can later check that the SISA being exchanged is the identical one that they proposed.

The initiator of the exchange, having chosen a SISA to offer, MUST then create hash of the Agreement JWT above and sign it with their private key.

Another JSON-LD document is created containing

N.B. - if a Rights-Holder is initiating the exchange the corresponding rightsHolder* keys should be used instead of the dataCustodian* keys.

This document is packaged up in a JSON-LD object under the “offeredSisa” key and sent to the other party in the exchange. Example:

{ "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "offeredSisa":
  { "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
    "agreementJwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "dataCustodianSigType": "sha256:ed25519",
    "dataCustodianDid": "did:jlinc:68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianPublicKey": "68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianSig": "t-G1zZOpORau6jrE3wmk9wBEF-B4KRmAAp...",
    "createdAt": "2018-05-25T18:44:14.528Z"
  }
}

The recipient SHOULD take a hash of the JWT value under the “agreement” key and then verify the offering party’s signature with their public key.

Next the recipient SHOULD decode the payload of the JWT value under the “agreement” key and ascertain that the offered Agreement is acceptable. An elaboration of this protocol is under consideration describing methods for negotiating Agreements which may be followed if the offered Agreement is not acceptable.

Assuming the Agreement is acceptable, the recipient MUST encapsulate the object under the “offeredSisa” key in another JWT, secured this time with the recipient’s secret using the SHA256 HMAC method. This we will call the offeredSisa JWT. It is a hash of this object, the offeredSisa JWT, that the recipient will sign to accept the offer. The recipient then creates the accepted JSON-LD object in the same manner as above, but with the “offeredSisaJwt” key’s value being the offeredSisa JWT, and in our example the signer being the Rights-Holder. Example:

{ "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "offeredSisaJwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
  "rightsHolderSigType": "sha256:ed25519",
  "rightsHolderDid": "did:jlinc:DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
  "rightsHolderPublicKey": "DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
  "rightsHolderSig": "uidhE6-GyICSXwh9F8cN0ZKV2mzUJWqWot...",
  "createdAt": "2018-05-25T18:44:15.368Z"
}

Finally, the recipient MUST encapsulate the accepted SISA in another JWT along with its SHA256 hash (as the sisaId), and both save it locally and return a copy of it to the initiator.

{ "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "acceptedSisaJwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
  "sisaId": "KC62PasU6kCEr3VdPPU21u8fZxiIl2QSU5QNF6T-Gl4"
}

A note about these nested JWTs: The JWT as a string is to be understood as the canonical representation of each JSON-LD object, and is always the thing which is hashed to produce an object to be signed, or the id of the final accepted SISA. This avoids hash mismatches due to JSON documents being marshaled slightly differently by various implementations as well as the complications of object canonicalization. Although decoded for reference and validation, the original JWTs MUST be preserved for revalidation if ever necessary.

acceptedSisaJwt -->
  offeredSisaJwt -->
    agreementJwt

The offerer SHOULD check the signature of the acceptor against the hash of the value under the “offeredSisaJwt” key, verify that the inner “agreementJwt” value in the decoded “offeredSisaJwt” has a correct HMAC (thus verifying that what the second party signed was exactly what was offered), and then permanently store the completed SISA, so that both parties have independent copies of it.

A variation on the above uses Ed25519 keys to sign and validate the JWTs.

5. SISA Events

Once a SISA has been completed, the parties that are signatories to it can exchange data and other requests under its terms. That the exchange is taking place in the context of, and under the agreed upon rules of the SISA is indicated by including the sisaId and signatures over it in the exchange. This is known as a SISA event.

This an example of a SISA event:

{ "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "audit": {
    "eventType": "dataEvent",
    "sisaId": "KC62PasU6kCEr3VdPPU21u8fZxiIl2QSU5QNF6T-Gl4",
    "previousId": "XxCndMFazK5_T8lGdw-Kxcr7y7rnNstO3aXJ_Jy2rm0",
    "createdAt": "2018-05-25T18:44:16.249Z",

    "eventId": "ffmpRg_xyZd2YYGciTOR2rdYzztX8-0zXGaf5Ka82Ls",
    "descendedFrom": "null",

    "rightsHolderSigType": "sha256:ed25519",
    "rightsHolderDid": "did:jlinc:DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
    "rightsHolderPublicKey": "DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
    "rightsHolderSig": "uidhE6-GyICSXwh9F8cN0ZKV2mzUJWqWot...",

    "dataCustodianSigType": "sha256:ed25519",
    "dataCustodianDid": "did:jlinc:68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianPublicKey": "68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianSig": "t-G1zZOpORau6jrE3wmk9wBEF-B4KRmAAp..."
  },

  "eventJwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
}

There are two required sections, audit and eventJwt.

The audit section MAY be sent to an audit server for future reference. The eventJwt is private to the parties in the exchange.

Looking at the audit section, we have a eventType - explained below.

Then we have a sisaId - this is a SHA256 hash of the SISA being referenced by this event, see above.

Then we have a eventId - this is a SHA256 hash of a value created by concatenating, with dots (i.e. “.”) the sisaId, the eventJwt, the previousId and the timestamp (createdAt), in that order.
The pattern is a SHA256 hash of sisaId.eventJwt.previousId.createdAt
That gives a unique ID for the event, and is the value signed by the parties to the event.

The previousId is the eventId of the previous event from this initiator to this recipient, and can be used to assure that events are received in the correct order, and well as providing immutability for the history of events in the relationship between the parties. If there is no previous event, the value of previousId should be the string ‘null’.

The createdAt is a timestamp for the moment when the initiator of the event created and signed it, in RFC 3339 format.

Signature data consists of an DID id, a public key, a signature type, and the signature itself, as described in the Establishing a SISA section above. The signature is over the eventId value. The initiator signs the eventId value and sends the whole SISA event including the initiator’s signature data to the recipient. Upon receipt and validation, the recipient signs the same eventId value and appends their signature data, forming a complete SISA event. The recipient MUST return a copy of that final SISA event to the initiator.

There are 3 types of SISA events:

  1. dataEvent
  2. permissionEvent
  3. statusEvent

The dataEvent type indicates that the event is an update, addition or deletion of data for which one of the parties is authoritative.

The permissionEvent type indicates that the event is a change in the permissions for data use that a Rights-Holder is affording under the terms of the SISA.

The statusEvent type indicates that the event is a change in the status of the SISA itself, for example “suspend” or “terminate” the SISA.

The eventJwt value is a JWT created by the initiator of the event by encapsulating the object carrying the parameters of the event. As noted in the SISA section, this JWT is always the thing which is hashed to produce a value for object to be signed. This avoids hash mismatches due to JSON documents being marshaled slightly differently by various implementations, and/or canonicalization being done inconsistently. Although decoded for use, the original JWT MUST be preserved for revalidation if ever necessary.

The format of the event object is implementation dependent.

Once a SISA has been established and SISA events begin to flow, a relationship state between the parties exists. In particular, at any given moment there is a state comprised of the permissions that the Rights-Holder has afforded and/or denied to the Data-Custodian, and the status of the SISA.

Both parties to the SISA SHOULD be able to view a representation of this state whenever desired, which will quite probably change from time to time.

This state, the cumulative result of all the SISA events under a particular SISA at a given point in time, MAY be displayed to end users in the form of a Consent Receipt as defined by the Kantara Consent Receipt Specification or later revisions of the same.

7. Audit

The audit section of a SISA event is designed to contain hashes, signatures, public keys and other opaque values as well as DID id’s, but nothing that can be decoded or decrypted to reveal sensitive data.

Either or both of the parties to a SISA event MAY choose to transmit the audit section of these events to a third-party audit service of their choice. Audit records serve to provide an independent record of SISA agreements and events that could be referenced in the case where a dispute arises over the authenticity of an event history.

A SISA agreement SHOULD be transmitted to an audit service API as a JSON object with a JSON-LD @context and a single key “acceptedSisaJwt”, with a fully completed SISA JWT as the value.

The agent which transmits this record MAY also include an agent signature, which will be over the hash of the acceptedSisaJwt (i.e. the sisaId), and the audit service MAY require and validate such a signature.

Example:

{
  "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "acceptedSisaJwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",

  "agentSigType": "sha256:ed25519",
  "agentDid": "did:jlinc:l9RLhEy6DghCLBoPLhKypYRS4h1Sys_-KqfrldzsF6o",
  "agentPublicKey": "l9RLhEy6DghCLBoPLhKypYRS4h1Sys_-KqfrldzsF6o",
  "agentSig": "t-G1zZOpORau6jrE3wmk9wBEF-B4KRmAAp..."
}

SISA Events: the audit section of a SISA event, along with the @context definition, SHOULD also be transmitted to an audit service API as a JSON object.

Similarly to the SISA agreement audit, the agent which transmits this record MAY also include an agent signature, which will be over the same eventId value as used by the other signatures, and the audit service MAY require and validate such a signature.

Example:

{
  "@context": "https://protocol.jlinc.org/context/jlinc-v7.jsonld",
  "audit": {
    "eventType": "dataEvent",
    "sisaId": "KC62PasU6kCEr3VdPPU21u8fZxiIl2QSU5QNF6T-Gl4",
    "previousId": "XxCndMFazK5_T8lGdw-Kxcr7y7rnNstO3aXJ_Jy2rm0",
    "createdAt": "2018-05-25T18:44:16.249Z",

    "eventId": "ffmpRg_xyZd2YYGciTOR2rdYzztX8-0zXGaf5Ka82Ls",
    "descendedFrom": "null",

    "rightsHolderSigType": "sha256:ed25519",
    "rightsHolderDid": "did:jlinc:DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
    "rightsHolderPublicKey": "DKHFTOEcTYdQ1EADyhT95A9UkMmlkQn8_CtdHm2v_pg",
    "rightsHolderSig": "uidhE6-GyICSXwh9F8cN0ZKV2mzUJWqWot...",

    "dataCustodianSigType": "sha256:ed25519",
    "dataCustodianDid": "did:jlinc:68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianPublicKey": "68C659BSZoQ1NeCJ0OVAdDpcfQJfFTMnwD53z-S5Ips",
    "dataCustodianSig": "t-G1zZOpORau6jrE3wmk9wBEF-B4KRmAAp..."

    "agentSigType": "sha256:ed25519",
    "agentDid": "did:jlinc:l9RLhEy6DghCLBoPLhKypYRS4h1Sys_-KqfrldzsF6o",
    "agentPublicKey": "l9RLhEy6DghCLBoPLhKypYRS4h1Sys_-KqfrldzsF6o",
    "agentSig": "t-G1zZOpORau6jrE3wmk9wBEF-B4KRmAAp..."
  }
}