Verifiable Data Sharing Protocol

Affinidi TDK - VDSP library facilitates secure, interoperable, verifiable credential exchange between Holders and Verifiers using the DIDComm v2.1 protocol.

VDSP seamlessly integrates with Affinidi Messaging and other DIDComm-based mediators to implement DIDComm protocols for sharing verifiable credentials.

The VDSP provides the following main capabilities:

  • VdspVerifier - Represents the requesting party (Verifier) that verifies one or more credentials, such as identity documents, educational certificates, or professional qualifications, as part of a workflow like background screening for employees.

  • VdspHolder - Represents the user or entity (Holder) that possesses the credentials required by the verifiers. They have complete control over their data and how it is shared.

It simplifies the data sharing, including credential queries and cryptographic operations such as signature verifications.

Key Features

  • Implements DIDComm v2.1 protocol for secure, end-to-end encrypted communication.
  • Support for feature discovery between verifiers and holders.
  • Supports DCQL for credential querying and filtering.
  • Cryptographically verifies the signature of verifiable presentations and credentials.
  • Built-in support for proof context (challenge and domain) to prevent replay attacks.
  • Supports data integrity proof suites (ECDSA and EdDSA) for signing presentations.
  • Problem reporting mechanism for error handling.

DIDComm Protocols

VDSP utilises DIDComm protocols to define higher-level protocols, which facilitate the exchange of credential data from feature discovery, data request, and processing the shared credential.

Message TypePurposeDirection
discover-features/2.0/queriesQuery Supported FeaturesVerifier → Holder
discover-features/2.0/discloseDisclose Supported FeaturesHolder → Verifier
vdsp/1.0/query-dataRequest Verifiable CredentialsVerifier → Holder
vdsp/1.0/data-responseShare Verifiable PresentationHolder → Verifier
vdsp/1.0/data-processing-resultSend Processing ResultVerifier → Holder
report-problem/2.0/problem-reportReport Errors or WarningsAny → Any

For more details on the DIDComm protocols, see the VDSP protocol document.

Supported Query Languages

Query LanguageSupportDescription
DCQLFully supportedDigital Credential Query Language for querying credentials from the holder’s digital wallet.

Installation

Run the following command to add the package to your Dart/Flutter project:

dart pub add affinidi_tdk_vdsp

Prerequisite

You must have a running and accessible DIDComm mediator instance before proceeding. The mediator provides the messaging layer for secure communication between Verifiers and Holders.

If you don’t have a mediator yet, see deployment options.

Initialise Verifier and Holder

Set up DID Managers and Mediator

Both the verifier and holder need DID managers and a connection to a DIDComm mediator.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart'; import 'package:ssi/ssi.dart';
Example
// Resolve the mediator's DID document final mediatorDid = 'did:web:...'; // Your mediator's DID final mediatorDidDocument = await UniversalDIDResolver.defaultResolver.resolveDid( mediatorDid, ); // Set up verifier's DID manager final verifierKeyStore = InMemoryKeyStore(); final verifierWallet = PersistentWallet(verifierKeyStore); final verifierDidManager = DidKeyManager( wallet: verifierWallet, store: InMemoryDidStore(), ); final verifierKeyId = 'verifier-key-1'; await verifierWallet.generateKey( keyId: verifierKeyId, keyType: KeyType.p256, ); await verifierDidManager.addVerificationMethod(verifierKeyId); // Set up holder's DID manager final holderKeyStore = InMemoryKeyStore(); final holderWallet = PersistentWallet(holderKeyStore); final holderDidManager = DidKeyManager( wallet: holderWallet, store: InMemoryDidStore(), ); final holderKeyId = 'holder-key-1'; await holderWallet.generateKey( keyId: holderKeyId, keyType: KeyType.p256, ); await holderDidManager.addVerificationMethod(holderKeyId);

Initialise VDSP Verifier

Create a VDSP verifier for requesting credentials from holders.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
final vdspVerifier = await VdspVerifier.init( mediatorDidDocument: mediatorDidDocument, didManager: verifierDidManager, );

Initialise VDSP Holder

Create a VDSP holder for sharing credentials with verifiers.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
final vdspHolder = await VdspHolder.init( mediatorDidDocument: mediatorDidDocument, didManager: holderDidManager, featureDisclosures: FeatureDiscoveryHelper.vdspHolderDisclosures, );

Verifier Methods

Manage the verifier’s interactions with holders.

Query Holder Features

Sends a feature query to discover what features the holder supports before requesting credentials.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Verifier queries holder's features await vdspVerifier.queryHolderFeatures( holderDid: (await holderDidManager.getDidDocument()).id, featureQueries: FeatureDiscoveryHelper.getFeatureQueriesByDisclosures( FeatureDiscoveryHelper.vdspHolderDisclosures, ), );

Query Holder Data

Requests verifiable credentials from a holder using DCQL query.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart'; import 'package:dcql/dcql.dart'; import 'package:uuid/uuid.dart';
Example
// Define what credentials the verifier needs // In this example, verifier requests holder's email address final dcqlQuery = DcqlCredentialQuery( credentials: [ DcqlCredential( id: const Uuid().v4(), format: CredentialFormat.ldpVc, claims: [ DcqlClaim( path: ['credentialSubject', 'email'], ), ], ), ], ); // Send the query to the holder await vdspVerifier.queryHolderData( holderDid: holderDid, dcqlQuery: dcqlQuery, operation: 'registerAgent', // Optional operation identifier proofContext: VdspQueryDataProofContext( challenge: 'unique-challenge-string', domain: 'verifier.example.com', ), comment: 'Please share your email credential', );

Send Data Processing Result

Sends a processing result message to the holder after receiving and verifying credentials.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Send processing result to holder await vdspVerifier.sendDataProcessingResult( holderDid: holderDid, result: { 'success': true, 'message': 'Credentials verified successfully', }, );

Listen for Incoming Messages (Verifier)

Listens for incoming messages from holders, including disclose messages, data responses, and problem reports.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
vdspVerifier.listenForIncomingMessages( onDiscloseMessage: (message) async { print('Holder supports: ${message.body}'); final body = DiscloseBody.fromJson(message.body!); // Check if holder supports required features // Proceed with data query if features are supported }, onDataResponse: ({ required VdspDataResponseMessage message, required bool presentationAndCredentialsAreValid, VerifiablePresentation? verifiablePresentation, required VerificationResult presentationVerificationResult, required List<VerificationResult> credentialVerificationResults, }) async { print('Verification result: $presentationAndCredentialsAreValid'); if (presentationAndCredentialsAreValid) { // Extract claims from the verified presentation final email = verifiablePresentation ?.verifiableCredential .first .credentialSubject .first['email']; print('Verified email: $email'); // Send processing result to holder await vdspVerifier.sendDataProcessingResult( holderDid: message.from!, result: { 'success': true, 'email': email, }, ); } else { print('Verification failed:'); print('Presentation: ${presentationVerificationResult.errors}'); print('Credentials: ${credentialVerificationResults.map((r) => r.errors)}'); } }, onProblemReport: (message) { print('Problem reported: ${message.body}'); }, );

Holder Methods

Manage the holder’s interactions with verifiers.

Get Disclosures

Returns supported feature disclosures for a query message received from a verifier.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Get disclosures based on the query message final disclosures = vdspHolder.getDisclosures(queryMessage: message); print('Supported disclosures: $disclosures');

Disclose Features

Sends a disclose message in response to a feature query from a verifier.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Respond to feature query with supported disclosures final disclosures = vdspHolder.getDisclosures(queryMessage: message); await vdspHolder.disclose( queryMessage: message, disclosures: disclosures, );

Filter Verifiable Credentials

Filters stored verifiable credentials based on a DCQL query received from a verifier.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Filter credentials based on the verifier's query final queryResult = await vdspHolder.filterVerifiableCredentials( requestMessage: message, verifiableCredentials: holderVerifiableCredentials, // Holder's stored credentials ); print('Matching credentials: ${queryResult.verifiableCredentials.length}');

Share Data

Creates and sends a verifiable presentation in response to a data request from a verifier.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Filter credentials based on the query final queryResult = await vdspHolder.filterVerifiableCredentials( requestMessage: message, verifiableCredentials: holderVerifiableCredentials, ); // Create a signer for the verifiable presentation final holderSigner = await holderDidManager.getSigner( holderDidManager.assertionMethod.first, ); // Share the filtered credentials as a verifiable presentation await vdspHolder.shareData( requestMessage: message, verifiableCredentials: queryResult.verifiableCredentials, verifiablePresentationSigner: holderSigner, verifiablePresentationProofSuite: DataIntegrityProofSuite.ecdsa_jcs_2019, operation: 'registerAgent', comment: 'Here is my email credential', );

Listen for Incoming Messages (Holder)

Listens for incoming messages from verifiers, including feature queries, data requests, processing results, and problem reports.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
vdspHolder.listenForIncomingMessages( onFeatureQuery: (message) async { // Handle feature query from verifier final disclosures = vdspHolder.getDisclosures(queryMessage: message); await vdspHolder.disclose( queryMessage: message, disclosures: disclosures, ); }, onDataRequest: (message) async { // Filter credentials based on the query final queryResult = await vdspHolder.filterVerifiableCredentials( requestMessage: message, verifiableCredentials: holderVerifiableCredentials, ); // Create a signer for the verifiable presentation final holderSigner = await holderDidManager.getSigner( holderDidManager.assertionMethod.first, ); // Share the filtered credentials await vdspHolder.shareData( requestMessage: message, verifiableCredentials: queryResult.verifiableCredentials, verifiablePresentationSigner: holderSigner, verifiablePresentationProofSuite: DataIntegrityProofSuite.ecdsa_jcs_2019, operation: 'registerAgent', comment: 'Here is my credential', ); }, onDataProcessingResult: (message) async { print('Processing result: ${message.body}'); }, onProblemReport: (message) { print('Problem reported: ${message.body}'); }, );

Complete Workflow Example

The following example provides a quick code overview of how a verifier discovers the holder’s supported features, listens to the response, and sends a credential query to request the holder’s data.

Import
import 'package:affinidi_tdk_vdsp/affinidi_tdk_vdsp.dart';
Example
// Verifier queries holder's features await vdspVerifier.queryHolderFeatures( holderDid: (await holderDidManager.getDidDocument()).id, featureQueries: FeatureDiscoveryHelper.getFeatureQueriesByDisclosures( FeatureDiscoveryHelper.vdspHolderDisclosures, ), ); // Verifier listens for disclose messages vdspVerifier.listenForIncomingMessages( onDiscloseMessage: (message) async { print('Holder supports: ${message.body}'); final body = DiscloseBody.fromJson(message.body!); // Check if holder supports required features // Proceed with data query if features are supported }, onDataResponse: ({ required VdspDataResponseMessage message, required bool presentationAndCredentialsAreValid, VerifiablePresentation? verifiablePresentation, required VerificationResult presentationVerificationResult, required List<VerificationResult> credentialVerificationResults, }) async { print('Verification result: \$presentationAndCredentialsAreValid'); if (presentationAndCredentialsAreValid) { // Extract claims from the verified presentation final email = verifiablePresentation ?.verifiableCredential .first .credentialSubject .first['email']; print('Verified email: \$email'); // Send processing result to holder await vdspVerifier.sendDataProcessingResult( holderDid: message.from!, result: { 'success': true, 'email': email, }, ); } else { print('Verification failed:'); print('Presentation: \${presentationVerificationResult.errors}'); print('Credentials: \${credentialVerificationResults.map((r) => r.errors)}'); } }, onProblemReport: (message) { print('Problem reported: \${message.body}'); }, ); // Holder listens for feature queries and responds vdspHolder.listenForIncomingMessages( onFeatureQuery: (message) async { final disclosures = vdspHolder.getDisclosures(queryMessage: message); await vdspHolder.disclose( queryMessage: message, disclosures: disclosures, ); }, onDataRequest: (message) async { // Filter credentials based on the query final queryResult = await vdspHolder.filterVerifiableCredentials( requestMessage: message, verifiableCredentials: holderVerifiableCredentials, // Holder's credentials ); // Create a signer for the verifiable presentation final holderSigner = await holderDidManager.getSigner( holderDidManager.assertionMethod.first, ); // Share the filtered credentials as a verifiable presentation await vdspHolder.shareData( requestMessage: message, verifiableCredentials: queryResult.verifiableCredentials, verifiablePresentationSigner: holderSigner, verifiablePresentationProofSuite: DataIntegrityProofSuite.ecdsa_jcs_2019, operation: 'registerAgent', comment: 'Here is my email credential', ); }, ); // Define what credentials the verifier needs // In this example, verifier requests for holder's email address final dcqlQuery = DcqlCredentialQuery( credentials: [ DcqlCredential( id: const Uuid().v4(), format: CredentialFormat.ldpVc, claims: [ DcqlClaim( path: ['credentialSubject', 'email'], ), ], ), ], ); // Send the query to the holder await vdspVerifier.queryHolderData( holderDid: holderDid, dcqlQuery: dcqlQuery, operation: 'registerAgent', // Optional operation identifier proofContext: VdspQueryDataProofContext( challenge: 'unique-challenge-string', domain: 'verifier.example.com', ), comment: 'Please share your email credential', );

For a complete runnable example demonstrating the full workflow, see the example on GitHub that shows:

  • Feature discovery between verifier and holder.
  • DCQL-based credential query for the holder.
  • Verifiable presentation creation and sharing.
  • Automatic verification of presentations and credentials.
  • Handling processing result.