Verifiable presentations

Verifiable Presentations (V1)

This document describes how to create v1 verifiable presentations and how to verify them.

Table of Contents:

Build Statement

The SDK contains a helper to create statements about identities, which can then be proven.

To do so, use the CredentialStatementBuilder, to build a statement:

From ./../examples/nodejs/common/verifiable-credential-statements.ts#3~21

let builder = new CredentialStatementBuilder();

// Add a web3 ID credential statements
builder = builder.forWeb3IdCredentials([ContractAddress.create(123)], (b) => b.addMembership('position', ['engineer']));

// Add an identity credential statement. Alternatively, if the proof produced from the
// statement should be tied to an account, use `builder.forAccountCredentials`.
builder = builder.forIdentityCredentials([0, 1], (b) => {
b.addMinimumAge(18);
b.addEUResidency();
b.revealAttribute('firstName');
});

// Get the complete statement to request a proof of.
const statement = builder.getStatements();

console.log('successfully constructed statement', statement);

Identity/account credential statements

To build a statement against an identity credential, the builder has two different entrypoints, which have an identical function signature, which consists of

  1. A list of identity providers that the identity must be created from
  2. A callback function which should be used to add statements for the credential
// used for proofs which are not tied to a specific account
builder.forIdentityCredentials([0,2], (build) => ...)
// used for proofs tied to an account created from the identity credential.
builder.forAccountCredentials([0,2], (build) => ...

Below are a set of functions accessible for the build object passed in the callback

Minimum Age

There is a helper function for specifying the prover must have some minimum age.

Example: add the statement that the prover must be born at least 18 years old:

    build.addMinimumAge(18);

Eu membership

There are helpers for specifying the country of residency or nationality to be one of the EU member states.

    build.addEUNationality();
build.addEUResidency();

Reveal statement

State that a given attribute should be revealed as part of the proof.

    build.revealAttribute(AttributesKeys.nationality);

Range statement

State that a given attribute should be between 2 given values.

Example: add the statement that the prover must be born between January 1, 1941 and Februar 2, 2005.

    build.addRange(AttributesKeys.dob, 19410101, 20050202);

Note that this type of statement is only allowed for the following attributes:

  • dob (date of birth)
  • idDocIssuedAt
  • idDocExpiresAt

Membership statement

Example: add the statement that the prover's country of residency is France or Spain:

    build.addMembership(AttributesKeys.CountryOfResidency, ['FR', 'ES']);

Note that this type of statement is only allowed for the following attributes:

  • Nationality
  • CountryOfResidency
  • IdDocIssuer
  • IdDocType

Non membership statement

Example: add the statement that the prover's country of residency not Germany nor Portugal:

    build.addNonMembership(AttributesKeys.CountryOfResidency, ['DE', 'PT']);

Note that this type of statement is only allowed for the following attributes:

  • Nationality
  • CountryOfResidency
  • IdDocIssuer
  • IdDocType

Web3 ID credential statements

To build a statement against a Web3 ID, the builder has exposes an entrypoint forWeb3IdCredentials, which has a function signature similar to those used for identity/account statements

  1. A list smart contract addresses the Web3 ID must be created from
  2. A callback function which should be used to add statements for the credential

Reveal statement

State that a given attribute should be revealed as part of the proof.

Example: reveal the education degree of an education ID.

    build.revealAttribute('degree');

Range statement

State that a given attribute should be between 2 given values.

Example: add the statement that the prover must be hired between January 1, 2015 and Februar 2, 2005.

    build.addRange('hired', 20150101, 20050202);

Membership statement

Example: add the statement that the prover's position in a company is either "engineer" or "designer"

    build.addMembership('position', ['engineer', 'designer']);

Non membership statement

Example: add the statement that the prover's position in a company is not "manager":

    build.addNonMembership('position', ['manager']);

JSON representation

The VerifiablePresentationRequestV1, VerifiablePresentationV1, and VerifiableAuditRecord can be represented as JSON by calling the associated .toJSON method (will also be called implicitly with JSON.stringify). Correspondingly, parsing the JSON values can be done with the .fromJSON function exposed for each type.

bigints are used internally in the types described above and need to be handled with something like json-bigint

Example: service serializes presentation request in response to frontend; frontend deserializes and parses the JSON.

const json = JSON.stringify(presentationRequest); // service sends back presentation request to frontend
...
const presentationRequest = VerifiablePresentationRequestV1.fromJSON(JSON.parse(json)); // frontend parses the JSON.

Verifiable Presentation Request (proof request)

To get a verifiable presentation of one or more verifiable credentials owned by a user, the entity requesting the information must first build a verifiable presentation request. In the V1 protocol, this is done in the following sequence:

  1. Make the request context, consisting of a. a unique 32-byte "nonce" b. a "connection ID" which identifies the connection between prover and requester c. a "context string" which describes the context of the proof request, e.g. which store is being accessed d. a set of requested context values, identified by their labels. For now the defaults here are: the block hash of the anchor transaction and the resource ID (i.e. an identifier of the requester, e.g. a url of the website)
  2. Build the statement to be proven by the user

Once this is done, the request must be anchored on chain with a transaction. This can be achieved by calling

const nonce = Uint8Array.from(...) // randomly generated 32-byte value
const connectionID = ... // e.g. a wallet-connect ID
const contextString = 'My compliant web3 wine shop'
const context = VerifiablePresentationRequestV1.createSimpleContext(nonce, connectionID, contextString)

const statement = new CredentialStatementBuilder()...

// a GRPC client connected a node on the network the anchor should be registered on
const grpcClient: ConcordiumGRPCClient = ...;
// the sender of the anchor transaction
const sender: AccountAddress.Type = ...;
// the keys for the account to sign the anchor transaction
const signer: Signer = ...;

// create the presentation request with an on-chain anchor, which can be checked by the owner of the credentials.
const presentationRequest = await VerifiablePresentationRequestV1.createAndAchor(
grpcClient,
sender,
signer,
context,
statement
);

Verifiable Presentation (proof)

Computing a verifiable presentation from a verifiable presentation request is a process of the following sequence for each credential statement in the request:

  1. Identify valid credentials for the statement by looking at the ID qualifier of the statement.
  2. Validate the attributes of the credential in the context of the statement.
  3. Construct a SpecifiedCredentialStatement corresponding to the credential. This is is not the same as the CredentialStatement we built for the VerfiablePresentationRequest previously; here we're working with a specific credential, e.g. from the users wallet.

When this is done for all credential statements in the request, we construct the proof context corresponding to the request context of the request, specifying values for each requested context value in VerifiablePresentationRequestV1.Context.requested.

// specify the resource ID from the connection to the requester of the proof
// the block hash is automatically derived from the request
const contextValues: GivenContext[] = [{label: 'ResourceID', context: ...}];

// The application holding the credentials selects the credentials to use and creates a DIDString from them.
// This will be a combination of the below, i.e. probably not all at once
const selectedCredentialIds: DIDString[] = [
createIdentityDID(...),
createAccountDID(...),
createWeb3IdDID(...),
];

// These are then paired with the statements from the verifiable presentation request to form the statements
// required for the verifiable presentation input:
const statements: SpecifiedCredentialStatement[] = selectedCredentialIds.map((id, i) => ({
id,
statement: presentationRequest.credentialStatements[i].statement
}));

// the inputs for the credential owned by the user, i.e. credential attribute values. For each
// `SpecifiedCredentialStatement`, there should be a corresponding input
const inputs: CommitmentInput[] = [
createIdentityCommitmentInputWithHdWallet(...),
createAccountCommitmentInputWithHdWallet(...),
createWeb3CommitmentInputWithHdWallet(...)
];

const presentation = await VerifiablePresentationV1.createFromAnchor(
grpcClient,
presentationRequest,
statements,
inputs,
contextValues
);

// verify the presentation elsewhere
const result = VerifiablePresentationV1.verifyWithNode(presentation, presentationRequest, grpcClient, network);

Verifiable Audit Record

Services can opt in to create a verifiable audit record from the verifiable presentation request and corresponding verifiable presentation. This exists in a private and public pair. The private should be stored by the application, and the public should be registered on chain.

const uuid: string = ...;
const private = PrivateVerificationAuditRecord.create(uuid, presentationRequest, presentation);
const {
publicRecord,
transactionHash
} = await PrivateVerificationAuditRecord.registerPublicRecord(private, grpcClient, sender, signer);

Generated using TypeDoc