ChainedProof2021

Note this document is still work in progress and subject to change (based on received feedback and further findings / implementation insights).

1. Introduction

As stated in the Linked Data Proofs specification:

A proof chain is useful when the same data needs to be signed by multiple entities and the order of when the proofs occurred matters, such as in the case of a notary counter-signing a proof that had been created on a document.

Unfortunately, the approach for expressing a "proof chains" described in the Linked Data specification is not suitable for usage with Verifiable Credentials, as pointed out in the following Github Issues [1, 2].

This document attempts to address this issue by defining a new Linked Data Proof type, as per the received suggestion(s)[1, 2].

2. The ChainedProof2021 Proof type

This document defines a new Linked Data Proof type ChainedProof2021, which can be used to describe a signature which is generated / verified based on an existing proof node (i.e. Linked Data Proof), as well as the original signed document (indirectly, via signing the proofValue of the referenced proof).

It should be possible to chain multiple such signatures together, to achieve the structure illustrated below:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The directed arrows illustrate a dependency (in terms of the signed content), i.e. Proof 1 is generated over the normalized contents of the Credential and the Proof 1 options. In case the credential or the signature options (e.g. created) are modified, the signature is invalidated.

Proof 2 (a Chained Proof) is generated over the normalized contents of the Proof 1 node and the Proof 2 options. In case the contents the two proof objects are tampered with in any way (e.g. the created date is tampered with), the signature is invalidated. Furthermore, in this case, if the contents of the Credential are tampered with, Proof 1 will be invalidated, and by extension Proof 2 will be invalidated as well

A proof node of the ChainedProof2021 type is expected to contain a proofValue field (e.g. containing a digital signature), which can be verified using the referenced verificationMethod. This is a common pattern with various Linked Data Proof types.

Unlike a regular Linked Data Proof, the signature associated with the proofValue field is not generated based on the contents of the Linked Data Document, but rather based on the contents of a referenced Linked Data Proof node (also associated with the document). The exact process is defined in section 2.3. Modification To Algorithms.

2.1 Terms

The following section outlines the terms used with a ChainedProof2021 proof type.

2.2.1 Proof Type

This property is required, and MUST be set to ChainedProof2021.

This is a standard property used to identify the type of linked data proof that is attached to a linked data document.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

2.2.2 Created

This property is required.

This is a standard property used to timestamp the creation of the new proof. The string value of an ISO8601 combined date and time string generated by the Proof Algorithm.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

2.2.3 Previous Proof

This property is required.

This is a custom property used to uniquely reference / identify an existing proof node associated with a linked data document. In case the proof node referenced by this value is missing or tampered with, the verification of the proof value associated with the ChainedProof2021 must fail.

The value associated with this property must be an object with the following properties:

  • type - The type of the referenced proof
  • created - The created field of the referenced proof
  • verificationMethod - The verificationMethod (e.g. DID URL) associated with the previous proof
  • proofPurpose - The intended purpose for the generated proof (e.g. asserting control over a verifiable credential, invoking a capability, authentication, etc.)
  • domain - An optional string value restricting the domain for which this signature should be verifiable.

These properties were selected because they are all required by the Linked Data Proofs specification, and can be expected to be present in all Linked Data Proof nodes, regardless of their type. These properties should be enough to uniquely identify / reference any other proofs associated with the document.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

In case the reference is still ambigous (i.e. two or more proof nodes match the provided type, created date, verification method and proof purpose) the signature generation / verification process must fail.

Note the approach described above is based on the assumption that proof nodes do not contain an identifier (i.e. @id property), which prevents us from directly referencing them in the previousProof field. This assumption was also mentioned in the associated issue. In case a more elegant mechanism for uniquely referencing another existing blank node exist, they should be considered.

2.2.4 Chain Tip Signature Suite

This property is required. The associated value MUST be a signature suite defined in the Linked Data Cryptographic Suite Registry.

In the current design, the ChainedProof2021 proof type does not enforce the usage of a specific cryptographic suite for generating the associated proofValue.

Instead, the definition of the signature suite is delegated to the proof type defined in the chainSignatureSuite property. This enables a certain degree of flexibility, i.e. signers can use proof types they support (e.g. Ed25519Signature2018, JsonWebSignature2020, etc.) in combination with this proof type.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

2.2.5 Proof Purpose

This property is required.

The specific intent for the proof, the reason why an entity created it (as defined here). Allowed values are defined here.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

2.2.6 Proof Value

This property is required.

A linked data document featuring a ChainedProof2021 Linked Data Proof MUST contain a proofValue attribute with value defined by the signing algorithm described in the specification of the used chainedProofType.

{ "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainSignatureSuite": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, "proofValue": "eyJhbGc...kvBg" }

In the example above, because the Ed25519Signature2018 signature suite is used to generate the proofValue associated with the "chain tip", the resulting proofValue field contains a detached JWS.

2.2 Modification to algorithms

A ChainedProof2021 proof can be generated only in combination with another existing proof node (referenced via the previousProof value). A Linked Data Proof node which satisfies the constraints expressed in the previousProof property (evaluated based on the rules defined in section 2.3.3.) is required as an additional input. In case the Linked Data Proof node referenced by the previousProof entry is missing, the process must exit with an error.

Because of this special property, modifications to the Proof Generation and Proof Verification algorithms are required.

2.2.1 Proof Algorithm

As stated in section 11.1 of the Linked Data Proofs specification, the expected inputs for this algorithm are the linked data document to be signed (referred to as document), proof options for the proof being generated (referred to as options), as well as a private key, referred to as privateKey.

Because the signature included in a ChainedProof2021 node is not generated over the contents of the document, but rather over the contents of another proof node, the referenced proof node must be passed as an adittional input as well (referred to as previousProof).

  1. Create a copy of the document to be signed, hereafter referred to as output.
  2. Create a value tbs that represents the data to be signed, and set it to the result of running the Create Verify Hash Algorithm, passing the Linked Data Proof identified by the previousProof property as the document argument, the options for the ChainedProof2021 as proof options, as well as the canonicalization and message digest algorithms defined by the selected chainSignatureSuite.
  3. Digitally sign tbs using the privateKey and the digital proof algorithm (defined by the chainSignatureSuite value of the options argument). The resulting string is the proofValue.
  4. Add the previousProof node to output.
  5. Add a proof node to output containing the ChainedProof2021 linked data proof, using the appropriate type (ChainedProof2021) and proofValue values as well as all of the data in the proof options (e.g. created, and if given, any additional proof options such as domain).
  6. Return output as the signed linked data document.

I.e. the signature is generated over the following content:

[Hash proof options 2 || Hash proof 1]

Furthermore, given an example with N chained proofs, the signature of proof N is is generated over the following content:

[Hash proof options N || Hash proof N-1]

Where || denotes concatenation.

2.2.2 Proof Verification Algorithm

This algorithm takes a signed linked data document, signed document and outputs a true or false value based on whether or not the digital proof on signed document was verified.

As stated in the relevant specification section, the expected inputs for this algorithm is the signed linked data document (referred to as signed document) to be verified.

In order to verify a ChainedProof2021 the following steps need to be taken:

  1. Get the public key by dereferencing its URL identifier in the ChainedProof2021 proof node of the default graph of signed document. Confirm that the linked data document that describes the public key specifies its owner and that its owner's URL identifier can be dereferenced to reveal a bi-directional link back to the key. Ensure that the key's owner is a trusted entity before proceeding to the next step.
  2. Let document be a copy of signed document.
  3. Remove any proof nodes from the default graph in document and save it as proof. The proof set is expected to contain at least two entries (i.e. the ChainedProof2021, and the proof node referenced by the previousProof property of the Chained Proof).
  4. Verify the referenced previousProof according to the Proof Verification Algorithm defined by the corresponding Proof Type. In case the referenced signature is not valid, the process must terminate with an error.
  5. Create a value tbv that represents the data to be verified, and set it to the result of running the Create Verify Hash Algorithm, passing the previous proof as the document argument, the options for the ChainedProof2021 as proof options, as well as the canonicalization and message digest algorithms defined by the selected chainSignatureSuite.
  6. Pass the proofValue, tbv, and the public key to the proof algorithm (e.g. JSON Web Proof using RSASSA-PKCS1-v1_5 algorithm). Return the resulting boolean value.

2.2.3 Rules for matching based on previousProof options

Given two inputs:

  • A previousProof entry extracted from a ChaiendProof2021 Linked Data Proof.
  • A set of Linked Data Proofs (e.g. extracted from the proof property of the Signed Linked Data Document)

The process described here must output one Linked Data Proof object (selected based on the previousProof argument), or return an error (in case zero, or more than two Linked Data Proofs match the previousProof).

In order to find the appropriate Linked Data Proof to return, each entry in the proof set is matched against the options in the previousProof object. The type, created, verificationMethod and proofPurpose must match exactly.

In case any optional values (e.g. domain) are present in the previousProof entry, they must also be present in the returned Linked Data Proof.

3. Examples

A proof chain is useful when the same data needs to be signed by multiple entities and the order of the proofs matters, such as in the case of a notary counter-signing a proof that had been created on a document.

Given a Verifiable Credential with the following associated Ed25519SignatureSuite2018 proof:

{ "type": "Ed25519Signature2018", "created": "2016-10-23T05:50:16Z", "verificationMethod": "did:example:employer#keys1", "proofPurpose": "assertionMethod", "jws" : "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..AUQ3AJ23WM5vMOWNtYKuqZBekRAOUibOMH9XuvOd39my1sO-X9R4QyAXLD2ospssLvIuwmQVhJa-F0xMOnkvBg" }

The signature suite defined in this document can be used to generate a chained proof referencing the original signature. The second proof would look as follows:

{ "type": "ChainedProof2021", // The creator of the embedded signature "verificationMethod": "did:example:employee#keys1", // When was the chained proof created "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod", // To avoid ambiguity, the Signature Suite used to created the proofValue is explicitly mentioned. "chainSignatureSuite": "Ed25519Signature2019", // Uniquely identifies the previous link in the chain "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "verificationMethod": "did:example:employer#keys1", "proofPurpose": "assertionMethod", }, "proofValue": "eyJhbGc...kvBg" }

Both proofs can be attached to the verifiable credential as follows:

{ "@context": [ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1" ], "id": "http://example.edu/credentials/1872", "type": ["VerifiableCredential", "EmploymentContractCredential"], "issuer": "did:example:employer", "issuanceDate": "2010-01-01T19:23:24Z", "credentialSubject": { "id": "did:example:employee", "professorAt": { "id": "did:example:university", "name": [{ "value": "Example University", "lang": "en" }] } }, "proof": [ // Employer signature, e.g. university representative { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "verificationMethod": "did:example:employer#key1", "proofPurpose": "assertionMethod", "jws" : "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..AUQ3AJ23WM5vMOWNtYKuqZBekRAOUibOMH9XuvOd39my1sO-X9R4QyAXLD2ospssLvIuwmQVhJa-F0xMOnkvBg" }, { "type": "ChainedProof2021", "verificationMethod": "did:example:employee#keys1", "chainedProofType": "Ed25519Signature2018", "created": "2010-02-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "Ed25519Signature2018", "created": "2010-01-01T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod": "did:example:issuer#keys1" }, // ProofValue is a detached JWS, as defined by the used Ed25519Signature2018 suite. "proofValue": "eyJhbGc...kvBg" }, // A further example for a ChainedProof entry "built" on a previous ChainedProof property { "type": "ChainedProof2021", "verificationMethod": "did:example:hr-department#keys1", "chainedProofType": "RsaSignature2018", "created": "2010-03-01T12:00:00Z", "proofPurpose": "assertionMethod" "previousProof": { "type": "ChainedProof2021", "created": "2010-02-01T12:00:00Z", "verificationMethod": "did:example:employee#keys1", "proofPurpose": "assertionMethod", }, // ProofValue is a detached JWS, as defined by the used RsaSignature2018 suite. "proofValue": "eyJhGbc...Gbg" }] }

3. Conformance

4. Security Considerations