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.