Once a Holder received a verifiable credential he can use it to prove himself to someone else.
Credential verification protocol implemented using Zero-knowledge protocol.
During the credential verification process, the Holder generates digital proof which the Verifier can validate using published Issuer keys which he used during the issuance of the credential, which Holder used to generate the proof.
Mobile SDK can generate an unlimited number of proof in this way.
There are two roles in the connection establishing process: Verifier and Prover.
- The Verifier is the party that ask Holder to provide some information about him. Verity SDK can be used as an Verifier.
- The Prover is the party that generates proofs based on the previously sored credentials. Mobile SDK represents the Holder party. Bellow in this document we will explain which steps need to be taken in order to generate a Proof for received request on the Prover side using Mobile SDK.
NOTE: library should be initialized before using proofs API. See initialization documentation
NOTE: there must be established connection between Prover and Verifier. See connections document
NOTE: there must be at least on credential stored in the Holder's wallet. See credentials document
To complete this section read through the following sections:
Proof Request is a request sent by one of the communication sides to another one in order to get information about it.
There are three types of data that can be requested in a Proof Request:
Requested Attributes are attributes for which a sender (Verifier) wants to know their exact values.
In other words, the resulting Proof will contain revealed values for requested attributes and math evidence of their correctness.
Requested Attribute Groups are similar to Requested Attributes. The only difference is that they apply an additional restriction for listed attributes: the same credential must be used for their proving. In other words, the resulting Proof will contain revealed values for each requested attribute and common math evidence (referring to a single credential) of their correctness.
Requested Predicates are conditions that the sender wants to ensure that receiver has a credential containing attribute which can resolve this condition but without revealing an actual value of this attribute.
In other words, the resulting Proof will contain math evidence of the fact that Prover has a credential resolving condition in the requested predicate, but an exact attribute value will not be revealed.
Predicates can be requested only over fields which has integer representation in a credential (e.x. predicate can be requested for credential field age
with has value 20
but not twenty
)
There are 4 types of supported predicates
>=
- greater or equal>
- greater<=
- less or equal<
- less
It may happen that User does not have a credential that can be used for filling a requested attribute, but sometimes Verifier may also indicate accepting of attribute values filled by the User.
Predicates cannot be self-attested. They can be only proved based on the existing credential.
An attibute is considered as missing if a User does not have a credential that can be used for filling a requested attribute and Verifier didn't allow its self-attesting. This Proof Request cannot be fulfilled by a User, so he can only reject it.
Generating a Proof that will contain only part of requested attributes/predicates will cause an error on the Verifier side (the Proof will not be accepted).
SDK function to retrieve credentials for a Proof Request will indicate Self-Attested and Missing attributes.
NOTE: Credentials do not specify units of measurement. There is a possible situation when the requested predicate, in theory, is resolvable but no credentials are returned by a library. Example: credentials contains
height
with value2
(implies maters) but requested predicate containsp_value
as170
(implies centimeters). For this case, it will be impossible to fulfill the proof request.
NOTE: Proof may contain only part of credential information (e.g. only one of the multiple credential fields was revealed in the proof. The rest fields are not included in the proof because were not requested).
NOTE: Proof can be built using multiple different credentials (e.g. different credentials were used for filling of requested attributes or predicates).
Aries Present Proof protocol consists of several messages exchange:
- Verifier sends
Presentation Request
message to Prover - Prover handles
Presentation Request
message and sendsPresentation
message to Verifier
In order to handle a Proof Request
a Prover (client) need to take the following steps:
- Download and Parse Proof Offer Request message received from the Pairwise Cloud Agent.
- Create Proof state object using parsed Proof Request message
2.1. Serialize Proof state object and save serialized representation
2.2. Update message (connected to Proof Request) status on the Agent as reviewed - Retrieve and select credentials which will be used for Proof Request filling
- Generate and Share Proof
4.1. Deserialize associated Connection state object
4.2. Deserialize Proof state object
4.3. Generate and Send Proof message
4.4. Serialize Proof state object and save serialized representation - Reject Proof Request
5.1. Deserialize associated Connection state object
5.2. Deserialize Proof state object
5.3. Send Proof Reject message
5.4. Serialize Proof state object and save serialized representation
As we mentioned before in the Storage document SDK does not save state objects, so the application must care about it.
It is up to the developer regarding what data to store in the application.
On of the possible formats may match the following structure:
{
"pwDid" - string, // reference to the connection from which proof request was received
"serialized" - string, // serialized representation of SDK Proof state object
"threadId" - string, // Identifier of present ptoof protocol.
// This is needed to match the following messages related to the protocol instance.
// metadata to show on the UI
"title": string, // proof request title
"request_attributes": json // list of attributes requested for proof
"requested_predicates": json // list of predicates requested for proof
// optionally
"connectionName": string // name of the connection from which Crdential Offer was received
"connectionLogo": string // logo of the connection from which Crdential Offer was received
"timestamp": int // optional, time of sharing a proof (it can be shown on the UI)
"status" - string, // proof status (pending / shared)
}
Later in this document, we will show how to get each of these fields.
-
Download pending messages (see messages documentation for messages downloading information). Pending messages with
proof request
type should be used. Extract Proof Request JSON string from the downloaded message (value ofdecryptedPayload
field).{ "@id":"d7f98364-2995-413d-8d20-ee1c817e1dd2", "@type":"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/request-presentation", "comment":"Basic Info", "request_presentations~attach":[ { "@id":"libindy-request-presentation-0", "data":{ "base64":"eyJuYW1lIjoicHJvb2ZfZnJvbV9hbGljZSIsIm5vbl9yZXZva2VkIjpudWxsLCJub25jZSI6IjkyNDAzNDI1OTA1OTQ2MzEzNTQwODk2NyIsInJlcXVlc3RlZF9hdHRyaWJ1dGVzIjp7ImF0dHJpYnV0ZV8wIjp7Im5hbWUiOiJNZW1iZXJJRCJ9fSwicmVxdWVzdGVkX3ByZWRpY2F0ZXMiOnt9LCJ2ZXIiOiIxLjAiLCJ2ZXJzaW9uIjoiMS4wIn0=" }, "mime-type":"application/json" } ], "~thread": { // (Optional) maybe omitted "thid": string, } }
The following data can be taken from the Credential Offer to fill the application Credential object we defined before:
~thread.thid
or else@id
- Identifier of present proof protocol. This value is needed to determine the next protocol message after offer acceptance.
-
Fetch Proof Request attachment content because it contains requested attributes and predicates:
[appDelegate.sdkApi extractAttachedMessage: proofRequest completion:^(NSError *error, NSString *attachedMessage) { // ... }];
int proofHandle = UtilsApi.vcxExtractAttachedMessage(proofRequest).get();
{ "nonce": "220867029780621153091790", // unique nonce "name": "Basic Info", // name of proof request (can be used on UI instead of `comment` field) "version": "0.1", // version of Proof Request "requested_attributes": { // requested attributes "attribute_1_unique_referent": { // unique identifier of first requested attribute "name": "Number", // name of requested field `Numeber` "restrictions": { // restriction to credentials which can be used for filling of attributes ... } }, "attribute_2_unique_referent": { // unique identifier of second requested attribute "name": "First Name", // name of requested field `First Name` "restrictions": { // restriction to credentials which can be used for filling of attributes ... } } }, "requested_predicates": { // requested predicates "predicate_1_unique_referent": { // unique identifier of first requested predicate "name": "Age", // name of requested field `Age` "p_type": ">=", // type of predicate (supported `>, >=, <, <=`) "p_value": 20, // value of predicate "restrictions": { // restriction to credentials which can be used for filling of attributes ... } } } }
The following data can be taken from the Credential Offer to fill the application Credential object we defined before:
-
name
- title of proof request -
request_attributes
- list of attributes requested for proof -
requested_predicates
- list of predicates requested for proofIn order to fill
pwDid
field you need to takepairwiseDID
value from the downloaded message.
-
Create Credential state object
[appDelegate.sdkApi proofCreateWithRequest: sourceId withProofRequest: message completion:^(NSError *error, vcx_proof_handle_t credentailHandle) { // ... }];
int proofHandle = DisclosedProofApi.proofCreateWithRequest(sourceId, message).get();
sourceId
- random stringmessage
- message received on step 1
-
Serialize Proof state object
[appDelegate.sdkApi proofSerialize: proofHandle completion:^(NSError *error, NSString *proof_request) { // ... }];
String serializedProof = DisclosedProofApi.proofSerialize(proofHandle).get();
-
Save
serialized
sdk credential state object into your application connection object. -
The
status
of the credential is considered aspending
at this step. -
Every time in the future you want to perform some operations using the created Proof object you need firstly to fetch Proof object from the storage and next deserialize SDK object from its serialized representation (receive a new handle).
-
Update status of correspondent message on the Agent as reviewed. See messages documentation for message update information.
Before responding to a Proof Request you need to select credentials that will be used.
There are two ways of showing a Proof Request to a User:
- Show requested attributes/predicates filling with credential values that will be used. In this case steps, below need to be done before showing a Proof Request.
- Show only requested attributes/predicates without values and automatically select credentials after User accepts. In this case steps, below can be done before showing a Proof Request, as after it is accepted by a User.
-
Deserialize Proof state object associated with Proof Request
[appDelegate.sdkApi proofDeserialize:serializedProof completion:^(NSError *error, NSInteger proofHandle) { // ... }];
int proofHandle = ProofApi.proofDeserialize(serializedProof).get();
-
Retrieve credentials which can be used to fill each requested attribute/predicate in the Proof Request
[appDelegate.sdkApi proofDeserialize: serializedProof completion:^(NSError *error, vcx_proof_handle_t proofHandle) { // ... [appDelegate.sdkApi proofRetrieveCredentials: proofHandle withCompletion:^(NSError *error, NSString *retrievedCreds) { // ... }]; }];
int proofHandle = DisclosedProofApi.proofDeserialize(serializedProof).get(); String retrievedCreds = DisclosedProofApi.proofRetrieveCredentials(proofHandle).get();
Result value will contain JSON object with credentials available for filling of each requested attribute / predicate in the Proof Request:
{ // requested attributes "attributes": { // unique identifier of requested attribute in the proof request "attribute_or_predicate_referent_in_proof_request": { // list of credentials which can be used for filling of attribute referenced by `attribute_or_predicate_referent_in_proof_request` "credentials": [ { // credential inforamtion "cred_info":{ "attrs":{ // all attributes in the credential }, "referent": string, // id of credential in the wallet "cred_def_id": string, // credential definition id "schema_id": string // id of schema }, // attributes requested in the proof request with correponding value taken from [cred_info][attrs] "requested_attributes":{ "attribute_name": string } } ], "missing": bool, // indicates if the attribute is missing (there is no credentials and self-attesting disallowed) "self_attest_allowed": bool, // indicates if the attribute can be self-attested (any value passed) "name": string // attribute name in the proof request } }, // requested predicate "predicates": { // unique identifier of requested predicate in the proof request "predicate_or_predicate_referent_in_proof_request": { // list of credentials which can be used for filling of predicate referenced by `predicate_or_predicate_referent_in_proof_request` "credentials": [ { // credential inforamtion "cred_info":{ "attrs":{ // all attributes in the credential }, "referent": string, // id of credential in the wallet "cred_def_id": string, // credential definition id "schema_id": string // id of schema }, // attributes requested in the proof request with correponding value taken from [cred_info][attrs] "requested_attributes":{ "attribute_name": string } } ], "missing": bool, // indicates if the attribute is missing (there is no credentials and self-attesting disallowed) "name": string, // attribute name in the proof request "p_type": string, // predicate type in the proof request "p_value": number // predicate value in the proof request } } }
{ "attributes": { // identifier of first requested attribute in the proof request "attribute_1_unique_referent": { // credentials in the wallet which can be used for filling of attribute referenced by `attribute_1_unique_referent` "credentials": [ { // credential information "cred_info": { "attrs": { "Number": "12345", "First Name": "Alice", "Last Name": "Andersen", "Age": "22" }, "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, // attributes requested in the proof request with correponding value taken from [cred_info][attrs] "requested_attributes":{ "Number": "12345" } } ], "missing": false, "self_attest_allowed": true, "name": "Number", }, // identifier of second requested attribute in the proof request "attribute_2_unique_referent": { "credentials": [ // two credentials which can be used for filling of attribute referenced by `attribute_2_unique_referent` { "cred_info": { // credential information "attrs": { "Number": "12345", "First Name": "Alice", "Last Name": "Andersen", "Age": "22" }, "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, // attributes requested in the proof request "requested_attributes":{ "First Name": "12345" } }, { "cred_info": { "attrs": { "number": "433", "firstName": "alice", "lastName": "andersen", "age": "22" }, "referent": "de23y421-0aaf-3256-1233-32hk1264934f", "cred_def_id": "C3SFPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, // attributes requested in the proof request "requested_attributes":{ "First Name": "alice" } } ], "missing": false, "self_attest_allowed": true, "name": "First Name", } }, "predicates": { // identifier of first requested predicate in the proof request "predicate_1_unique_referent": { "credentials": [ // only one credential which can be used for filling of predicate referenced by `predicate_1_unique_referent` { "cred_info": { // credential information "attrs": { "Number": "12345", "First Name": "Alice Andersen", "Last Name": "Andersen", "Age": "22" }, "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, // attributes requested in the proof request "requested_attributes":{ "Age": "22" } } ], "missing": false, "name": "Age", "p_type": ">=", "p_value": 20 } } }
There can be several credentials that can be used for filling of requested attribute/predicate. At the next step, you will need to select a single credential for each requested attribute/predicate.
If some attribute/predicate is indicated as
missing
Proof Request can be only rejected.There is no guaranty regarding the order of returned credentials. If you want to select the freshest credential as a default user selection you need to store an additional timestamp to credential id on the application side and sort credentials for requested attributes based on their timestamps.
-
Select credentials which will be used proving of each requested attribute / predicate.
In other words, you need to select a singlecredential info
entry for each requested attribute / predicate and build a new object having the following structure:{ "attrs": { // identifier of first requested attribute in the proof request "attribute_1_unique_referent": { "credential": { // credential selected from `retrievedCreds` "cred_info": { "attrs": { "Number": "12345", "First Name": "Alice", "Last Name": "Andersen", "Age": "22" }, "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, "requested_attributes":{ "Number": "12345" } } }, // identifier of second requested attribute in the proof request "attribute_2_unique_referent": { "credential": { // credential selected from `retrievedCreds` "cred_info": { "attrs": { "Number": "12345", "First Name": "Alice", "Last Name": "Andersen", "Age": "22" }, "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, "requested_attributes":{ "First Name": "12345" } } }, // identifier of first requested predicate in the proof request "predicate_1_unique_referent": { "credential": { // credential selected from `retrievedCreds` "cred_info": { "attrs": { "Number": "12345", "First Name": "Alice", "Last Name": "Andersen", "Age": "22" }, "cred_def_id": "R9kuPrDFDVKNRL1oFpRxg2:3:CL:33627:tag1", "referent": "79f68e11-0aaf-4891-8277-1f90f5b05670", "schema_id": "R9kuPrDFDVKNRL1oFpRxg2:2:DEMO-Transcript:1.0" }, "requested_attributes":{ "Age": "22" } } } } }
Pseudocod for selecting of the first possible credential:
selected_credentials = { 'attrs': {} } for attribute_referent in retrievedCreds['attributes']: selected_credentials['attrs'][attribute_referent] = { 'credential': retrievedCreds['attributes'][attribute_referent]['credentials'][0] } for predicate_referent in retrievedCreds['predicates']: selected_credentials['attrs'][predicate_referent] = { 'credential': retrievedCreds['predicates'][predicate_referent]['credentials'][0] }
-
Fill in missing attributes which can be self attested by user (according to Proof Request).
In this case JSON object with user-provided inputs should be constructed:
{ "attribute_referent_in_proof_request": "Custom value", ... }
NOTE: In case self-attested attributes are not required, empty JSON array should be used:
{}
-
If user does not have a credential to fill in missing attributes, and these attributes cannot be self attested (according to Proof Request) you will not be able to generate Proof. So you can only reject received Proof Request.
In case user is able to fulfill received proof request, following steps should be performed to share proof.
-
Deserialize Connection state object associated with received Prof Request message
[appDelegate.sdkApi connectionDeserialize:serializedConnection completion:^(NSError *error, NSInteger connectionHandle) { // ... }];
int connectionHandle = ConnectionApi.connectionDeserialize(serializedConnection).get();
-
Deserialize Proof state object associated with Proof Request
[appDelegate.sdkApi proofDeserialize:serializedProof completion:^(NSError *error, NSInteger proofHandle) { // ... }];
int proofHandle = DisclosedProofApi.proofDeserialize(serializedProof).get();
-
Generate Proof using selected credentials and self attested attributes
[appDelegate.sdkApi proofGenerate: proofHandle withSelectedCredentials:selectedCredentials withSelfAttestedAttrs:selfAttestedAttributes completion:^(NSError *error) { // ... }];
DisclosedProofApi.proofGenerate(proofHandle, selectedCredentials, selfAttestedAttributes).get();
-
Send
Proof
message to Verifier[appDelegate.sdkApi proofSend: proofHandle withConnectionHandle:connectionHandle completion:^(NSError *error) { // ... }];
DisclosedProofApi.proofSend(proofHandle, connectionHandle).get();
-
Serialize Proof state object
[appDelegate.sdkApi proofSerialize:proofHandle completion:^(NSError *error, NSString *state)) { // ... }];
String serializedProof = DisclosedProofApi.proofSerialize(proofHandle).get();
-
The
status
of the proof is considered asshared
at this step. -
You also can save the
timestamp
of proof sharing. -
Update a related record in the storage with the latest value.
-
Deserialize Connection state object associated with received Prof Request message
[appDelegate.sdkApi connectionDeserialize:serializedConnection completion:^(NSError *error, NSInteger connectionHandle) { // ... }];
int connectionHandle = ConnectionApi.connectionDeserialize(serializedConnection).get();
-
Deserialize Proof state object associated with Proof Request
[appDelegate.sdkApi proofDeserialize:serializedProof completion:^(NSError *error, NSInteger proofHandle) { // ... }];
int proofHandle = DisclosedProofApi.proofDeserialize(serializedProof).get();
-
Send
Reject Proof Request
message to Verifier[appDelegate.sdkApi proofDeclinePresentationRequest: proofHandle withConnectionHandle:connectionHandle withReason:(NSString *)@"Rejection reason" withProposal:(NSString *)nil withCompletion:^(NSError *error) { // ... }];
DisclosedProofApi.proofDeclineRequest(proofHandle, connectionHandle, "Rejection reason", null).get();
-
Serialize Proof state object
[appDelegate.sdkApi proofSerialize:proofHandle completion:^(NSError *error, NSString *state)) { // ... }];
String serializedProof = DisclosedProofApi.proofSerialize(proofHandle).get();
-
Update a related record in the storage with the latest value of the serialized Proof state object.
This section shows different formats for Proof Requests that can be sent to credential holder.
-
Request single attribute:
"requested_attributes": { "attribute_1": { "name": "Name" }, }
-
Request single attribute restricted by Issuer:
"requested_attributes": { "attribute_1": { "name": "Name", "restrictions": {"issuer_did": "ANM2cmvMVoWpk6r3pG5FAZ"} }, }
-
Request single attribute restricted by list of Issuer's:
"requested_attributes": { "attribute_1": { "name": "Name", "restrictions": {"issuer_did": {"$in": ["ANM2cmvMVoWpk6r3pG5FAZ", "BNM2cmvMVoWpk6r3pG5FAZ"]}} }, }
-
Request single attribute restricted by Credential Definition:
"requested_attributes": { "attribute_1": { "name": "Name", "restrictions": {|cred_def_id": "ANM2cmvMVoWpk6r3pG5FAZ:3:CL:1:1"} }, }
-
Request single attribute that can be self attested:
"requested_attributes": { "attribute_1": { "name": "Name" }, } // or "requested_attributes": { "attribute_1": { "name": "Name", "self_attest_allowed": true }, }
-
Request single attribute that cannot be self attested:
"requested_attributes": { "attribute_1": { "name": "Name" "self_attest_allowed": false }, } // or set any restriction "requested_attributes": { "attribute_1": { "name": "Name" "restrictions": {"issuer_did": "ANM2cmvMVoWpk6r3pG5FAZ"} }, }
-
Request couple attributes which can be filled from different credentials:
"requested_attributes": { "attribute_1": { "name": "Name" }, "attribute_2": { "name": "Surname" }, }
-
Request couple attributes which must be filled from the same credential:
"requested_attributes": { "attribute_1": { "names": ["Name", "Surname"] }, }
-
Request couple attributes which must be filled from the same credential and restricted by issuer:
"requested_attributes": { "attribute_1": { "names": ["Name", "Surname"], "restrictions": {"issuer_did": "ANM2cmvMVoWpk6r3pG5FAZ"} }, }
-
Request predicates:
// Less or equal "requested_predicates": { "predicate_1": { "name": "Age", "p_type": "<=", "p_value": 30 }, } // Less "requested_predicates": { "predicate_1": { "name": "Age", "p_type": "<", "p_value": 30 }, } // Greater "requested_predicates": { "predicate_1": { "name": "Age", "p_type": ">", "p_value": 30 }, } // Greater or equal "requested_predicates": { "predicate_1": { "name": "Age", "p_type": ">=", "p_value": 30 }, }
-
Request predicates restricted by an issuer:
"requested_predicates": { "predicate_1": { "name": "Age", "p_type": ">=", "p_value": 30, "restrictions": {"issuer_did": "ANM2cmvMVoWpk6r3pG5FAZ"} }, }
Now your application is able to fulfill proof requests using previously received credentials. You are ready to read how to reply to secure questions.