Verifiable Data Sharing 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 Type | Purpose | Direction |
|---|---|---|
| discover-features/2.0/queries | Query Supported Features | Verifier → Holder |
| discover-features/2.0/disclose | Disclose Supported Features | Holder → Verifier |
| vdsp/1.0/query-data | Request Verifiable Credentials | Verifier → Holder |
| vdsp/1.0/data-response | Share Verifiable Presentation | Holder → Verifier |
| vdsp/1.0/data-processing-result | Send Processing Result | Verifier → Holder |
| report-problem/2.0/problem-report | Report Errors or Warnings | Any → Any |
For more details on the DIDComm protocols, see the VDSP protocol document.
Supported Query Languages
| Query Language | Support | Description |
|---|---|---|
| DCQL | Fully supported | Digital 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_vdspPrerequisite
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.
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.