# 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](https://w3c-ccg.github.io/ld-proofs/#proof-chains): > 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](https://github.com/w3c-ccg/ld-proofs/issues/26#issuecomment-821855653), [2](https://github.com/digitalbazaar/jsonld-signatures/issues/110#issuecomment-824700691)]. This document attempts to address this issue by defining a new Linked Data Proof type, as per the received suggestion(s)[[1](https://github.com/digitalbazaar/jsonld-signatures/issues/110#issuecomment-825011812), [2](https://github.com/w3c-ccg/ld-proofs/issues/26#issuecomment-825923877)]. ## 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: <p align="center"> <img height="500" width="200" src="https://i.imgur.com/2icJFoh.png"> </p> *Proof chain with N proof nodes.* 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](https://w3c-ccg.github.io/ld-proofs/#dfn-proof-type) that is attached to a linked data document. ```json= { "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](https://w3c-ccg.github.io/ld-proofs/#dfn-created) used to timestamp the creation of the new proof. The string value of an [ISO8601](https://www.iso.org/standard/40874.html) combined date and time string generated by the Proof Algorithm. ```json= { "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](https://w3c-ccg.github.io/ld-proofs/#dfn-domain) for which this signature should be verifiable. These properties were selected because they are all [required by the Linked Data Proofs specification](https://w3c-ccg.github.io/ld-proofs/#linked-data-proof-overview), 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. ```json= { "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](https://github.com/w3c-ccg/ld-proofs/issues/26#issuecomment-830029583). 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](https://w3c-ccg.github.io/ld-cryptosuite-registry/). In the current design, the `ChainedProof2021` proof type does not enforce the usage of a specific [cryptographic suite](https://w3c-ccg.github.io/ld-proofs/#dfn-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](https://w3c-ccg.github.io/lds-ed25519-2018/), [JsonWebSignature2020](https://w3c-ccg.github.io/lds-jws2020/#json-web-signature-2020), etc.) in combination with this proof type. ```json= { "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](https://w3c-ccg.github.io/ld-proofs/#dfn-proofpurpose)). Allowed values are [defined here](https://w3c-ccg.github.io/ld-proofs/#proof-purpose). ```json= { "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`. ```json= { "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*](https://w3c-ccg.github.io/ld-proofs/#proof-algorithm) and [*Proof Verification*](https://w3c-ccg.github.io/ld-proofs/#proof-verification-algorithm) algorithms are required. #### 2.2.1 Proof Algorithm As stated in section [11.1](https://w3c-ccg.github.io/ld-proofs/#proof-algorithm) 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. 6. 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`. 7. 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: ```json= { "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: ```json= { "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: ```json= { "@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