Skip to main content

Feedback

Is this page helpful?

Version: 5.x

Migration Guide: TrustVC W3C VC Data Model v1.1 to v2.0

This guide provides comprehensive instructions for migrating TrustVC library implementations from W3C VC Data Model v1.1 to v2.0, covering both technical implementation details for developers and practical usage examples for users.

Note: This migration guide is specifically designed for users of the TrustVC library (@trustvc/trustvc packages). If you're using other W3C VC implementations, please refer to their respective documentation.

Overview

The TrustVC library's support for W3C VC Data Model v2.0 introduces significant improvements over v1.1, including:

  • Modern Cryptographic Suites: ECDSA-SD-2023 with selective disclosure capabilities
  • Updated Context URLs: New v2.0 contexts for enhanced interoperability
  • Improved Date Fields: validFrom/validUntil replacing issuanceDate/expirationDate
  • Enhanced Status Lists: BitstringStatusList replacing StatusList2021
  • Multikey Format: Modern key representation for better DID document compatibility

Key Differences Summary

Aspectv1.1 (Legacy)v2.0 (Modern)
Contexthttps://www.w3.org/2018/credentials/v1https://www.w3.org/ns/credentials/v2
Date FieldsissuanceDate, expirationDatevalidFrom, validUntil
CryptosuiteBBS+ (BbsBlsSignature2020)ECDSA-SD-2023 (ecdsa-sd-2023)
Proof TypeBbsBlsSignature2020DataIntegrityProof
Key FormatBLS12-381 G2 (privateKeyBase58)Multikey (secretKeyMultibase)
Status ListStatusList2021CredentialBitstringStatusListCredential
Status EntryStatusList2021EntryBitstringStatusListEntry
Status SubjectStatusList2021BitstringStatusList

Migration Steps

1. Update Dependencies

First, ensure you're using the latest versions of TrustVC packages that support v2.0:

npm install @trustvc/trustvc@latest

2. Key Pair Migration

v1.1 Key Generation (Legacy)

import { issuer } from '@trustvc/trustvc';

const { generateKeyPair, VerificationType } = issuer;

// v1.1 - BLS12-381 G2 Key
const legacyKeyPair = await generateKeyPair({
type: VerificationType.Bls12381G2Key2020,
seedBase58: 'your-seed-here' // optional
});

console.log(legacyKeyPair);
// Output:
// {
// type: "Bls12381G2Key2020",
// privateKeyBase58: "...",
// publicKeyBase58: "...",
// seedBase58: "..."
// }

v2.0 Key Generation (Modern)

import { issuer } from '@trustvc/trustvc';

const { generateKeyPair, CryptoSuite } = issuer;

// v2.0 - ECDSA-SD-2023 with Multikey
const modernKeyPair = await generateKeyPair({
type: CryptoSuite.EcdsaSd2023 // 'ecdsa-sd-2023'
});

console.log(modernKeyPair);
// Output:
// {
// type: "Multikey",
// secretKeyMultibase: "z...",
// publicKeyMultibase: "z..."
// }

Important: After generating new ECDSA-SD-2023 keys, you must update your DID:WEB document to include the new public key. The key format changes from publicKeyBase58 (BLS12-381) to publicKeyMultibase (Multikey).

Sample DID Document Update

{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"
],
"id": "did:web:example.com",
"verificationMethod": [
{
"id": "did:web:example.com#key-1",
"type": "Multikey",
"controller": "did:web:example.com",
"publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}
],
"assertionMethod": ["did:web:example.com#key-1"],
"authentication": ["did:web:example.com#key-1"]
}

3. Credential Issuance Migration

v1.1 Credential Issuance

import { issuer, signW3C } from '@trustvc/trustvc';

const { CryptoSuite } = issuer;

const credentialV1 = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3id.org/security/bbs/v1'
],
type: ['VerifiableCredential'],
issuer: 'did:web:example.com',
issuanceDate: '2024-01-01T00:00:00Z',
expirationDate: '2025-01-01T00:00:00Z',
credentialSubject: {
id: 'did:example:subject',
name: 'John Doe',
degree: 'Bachelor of Science'
}
};

const legacyKeyPair = {
id: 'did:web:example.com#key-1',
controller: 'did:web:example.com',
type: 'Bls12381G2Key2020',
privateKeyBase58: '...',
publicKeyBase58: '...'
};

const { signed: signedV1 } = await signW3C(credentialV1, legacyKeyPair, CryptoSuite.BbsBlsSignature2020);

v2.0 Credential Issuance

import { signW3C } from '@trustvc/trustvc';
import { issuer } from '@trustvc/trustvc';

const { CryptoSuite } = issuer;

const credentialV2 = {
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://w3id.org/security/data-integrity/v2'
],
type: ['VerifiableCredential'],
issuer: 'did:web:example.com',
validFrom: '2024-01-01T00:00:00Z',
validUntil: '2025-01-01T00:00:00Z',
credentialSubject: {
id: 'did:example:subject',
name: 'John Doe',
degree: 'Bachelor of Science'
}
};

const modernKeyPair = {
'@context': 'https://w3id.org/security/multikey/v1',
id: 'did:web:example.com#multikey-1',
controller: 'did:web:example.com',
type: 'Multikey',
secretKeyMultibase: 'z...',
publicKeyMultibase: 'z...'
};

const { signed: signedV2 } = await signW3C(credentialV2, modernKeyPair, CryptoSuite.EcdsaSd2023, {
mandatoryPointers: [
'/credentialSubject/id',
'/credentialSubject/name'
]
});

Mandatory Pointers: In ECDSA-SD-2023, mandatory pointers specify which fields must always be disclosed and cannot be selectively hidden. This is useful for ensuring critical information like the credential subject's ID and name are always visible, while other fields like age or GPA can be selectively disclosed. The pointers use JSON Pointer syntax (RFC 6901) to reference specific fields in the credential.

Default Behavior: If no mandatory pointers are provided, all fields in the credential become selectively disclosable, meaning the holder can choose to hide any field during presentation. However, certain fields like the credential's @context, type, issuer, and validFrom/validUntil are typically always disclosed by default as they are essential for credential verification.

4. Selective Disclosure (Derive) Migration

One of the most powerful features of v2.0 is enhanced selective disclosure using ECDSA-SD-2023. Here's how it works and differs from v1.1:

Key Differences: BBS+ vs ECDSA-SD-2023

Aspectv1.1 (BBS+)v2.0 (ECDSA-SD-2023)
VerificationCan verify original credential directlyMust derive before verification
PerformanceSlower derivation and verificationFaster derivation and verification
Mandatory FieldsLimited control over always-visible fieldsPrecise control via mandatory pointers
Ecosystem SupportMature but limited adoptionGrowing W3C standard support

Creating and Verifying Derived Credential

Step 1: Sign Credential with Selective Disclosure Support
import { issuer, signW3C } from '@trustvc/trustvc';

const { CryptoSuite } = issuer;

const credential = {
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://w3id.org/security/data-integrity/v2'
],
type: ['VerifiableCredential'],
issuer: 'did:web:example.com',
validFrom: '2024-01-01T00:00:00Z',
credentialSubject: {
id: 'did:example:student123',
name: 'Alice Johnson',
studentId: 'STU-2024-001',
degree: 'Bachelor of Computer Science',
gpa: 3.9,
graduationYear: 2024,
dateOfBirth: '1999-05-15'
}
};

// Sign with mandatory pointers (always visible fields)
const { signed: fullCredential } = await signW3C(credential, modernKeyPair, CryptoSuite.EcdsaSd2023, {
mandatoryPointers: [
'/credentialSubject/id',
'/credentialSubject/name',
'/credentialSubject/degree'
]
});
Step 2: Derive Selective Disclosure Credential
import { deriveW3C } from '@trustvc/trustvc';

// Create job application VC (hide sensitive info)
const jobApplicationVC = await deriveW3C(fullCredential, [
'/credentialSubject/graduationYear'
// Reveals: id, name, degree (mandatory) + graduationYear
// Hides: studentId, gpa, dateOfBirth
]);

// Create scholarship application VC (show academic performance)
const scholarshipVC = await deriveW3C(fullCredential, [
'/credentialSubject/gpa',
'/credentialSubject/graduationYear'
// Reveals: id, name, degree (mandatory) + gpa + graduationYear
// Hides: studentId, dateOfBirth
]);
Step 3: Verify the Derived Credential
import { verifyW3CSignature } from '@trustvc/trustvc';

// ❌ WRONG: Cannot verify original credential with ECDSA-SD-2023
// const result = await verifyW3CSignature(fullCredential); // This will fail

// ✅ CORRECT: Verify the derived credential
const jobVerification = await verifyW3CSignature(jobApplicationVC);
const scholarshipVerification = await verifyW3CSignature(scholarshipVC);

console.log('Job Application Valid:', jobVerification.verified);
console.log('Scholarship Application Valid:', scholarshipVerification.verified);

⚠️ Important: Unlike BBS+ credentials, ECDSA-SD-2023 credentials must be derived before verification. You cannot verify the original full credential directly if you want selective disclosure.

Best Practices for Selective Disclosure

  1. Plan Mandatory Pointers Carefully: Include fields that should always be visible (e.g., subject ID, credential type)
  2. Use Meaningful Field Selection: Only reveal fields necessary for the specific use case
  3. Always Derive Before Verification: Remember that ECDSA-SD-2023 requires derivation before verification
  4. Test Different Disclosure Scenarios: Ensure your selective disclosure works for various use cases
  5. Document Your Disclosure Policies: Clearly communicate what fields are mandatory vs. selectable

5. Verification Migration

The TrustVC library provides seamless verification through the verifyDocument function, which offers backward compatibility and works with both v1.1 and v2.0 credentials automatically.

Unified Verification with verifyDocument

import { verifyDocument } from '@trustvc/trustvc';

// ✅ Works with v1.1 BBS+ credentials
const v1ResultFragments = await verifyDocument(signedV1BBSCredential);

// ✅ Works with derived v2.0 ECDSA-SD-2023 credentials
const v2ResultFragments = await verifyDocument(derivedV2ECDSACredential);

// ✅ Works with non-derived v2.0 ECDSA-SD-2023 credentials
const _v2ResultFragments = await verifyDocument(signedV2ECDSACredential);

console.log('v1.1 Verification fragments:', v1ResultFragments);
console.log('v2.0 Derived Verification fragments:', v2ResultFragments);
console.log('v2.0 Verification fragments:', _v2ResultFragments);

Note: The verifyDocument function provides ease of use by automatically handling both derived and non-derived credentials. It returns verification result fragments containing detailed verification information rather than a simple boolean verified property.

6. Credential Status Migration

v1.1 Status List Creation

import { credentialStatus } from '@trustvc/trustvc';

const { createCredentialStatusPayload, StatusList } = credentialStatus;

// Create status list
const statusList = new StatusList({ length: 100000 });
statusList.setStatus(42, true); // Revoke credential at index 42
const encodedList = await statusList.encode();

// Create v1.1 status credential
const statusCredentialV1 = await createCredentialStatusPayload(
{
id: 'https://example.com/status/1',
credentialSubject: {
id: 'https://example.com/status/1#list',
type: 'StatusList2021', // v1.1 subject type
statusPurpose: 'revocation',
encodedList
}
},
legacyKeyPair,
'StatusList2021Credential', // v1.1 credential type
CryptoSuite.BbsBlsSignature2020 // legacy cryptosuite
);

v1.1 Credential with Status List

const credentialWithStatusV1 = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3id.org/security/bbs/v1',
'https://w3id.org/vc/status-list/2021/v1'
],
type: ['VerifiableCredential'],
issuer: 'did:web:example.com',
issuanceDate: '2024-01-01T00:00:00Z',
credentialStatus: {
id: 'https://example.com/status/1#42',
type: 'StatusList2021Entry', // v1.1 status type
statusPurpose: 'revocation',
statusListIndex: '42',
statusListCredential: 'https://example.com/status/1'
},
credentialSubject: {
id: 'did:example:subject',
name: 'John Doe'
}
};

v2.0 Status List Creation

import { credentialStatus } from '@trustvc/trustvc';

const { createCredentialStatusPayload, StatusList } = credentialStatus;

// Same StatusList class works for both versions
const statusList = new StatusList({ length: 100000 });
statusList.setStatus(42, true); // Revoke credential at index 42
const encodedList = await statusList.encode();

// Create v2.0 status credential
const statusCredentialV2 = await createCredentialStatusPayload(
{
id: 'https://example.com/status/1',
credentialSubject: {
id: 'https://example.com/status/1#list',
type: 'BitstringStatusList', // v2.0 subject type
statusPurpose: 'revocation',
encodedList
}
},
modernKeyPair,
'BitstringStatusListCredential', // v2.0 credential type
CryptoSuite.EcdsaSd2023 // modern cryptosuite
);

v2.0 Credential with Status List

const credentialWithStatusV2 = {
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://w3id.org/security/data-integrity/v2'
],
type: ['VerifiableCredential'],
issuer: 'did:web:example.com',
validFrom: '2024-01-01T00:00:00Z',
credentialStatus: {
id: 'https://example.com/status/1#42',
type: 'BitstringStatusListEntry', // v2.0 status type
statusPurpose: 'revocation',
statusListIndex: '42',
statusListCredential: 'https://example.com/status/1'
},
credentialSubject: {
id: 'did:example:subject',
name: 'John Doe'
}
};

7. Document Builder Migration

The DocumentBuilder class helps build and manage W3C v2.0 credentials with credential status features.

Note: The new DocumentBuilder only supports v2.0 credentials with ECDSA-SD-2023 cryptosuite by default.

Basic Document Builder Usage

import { DocumentBuilder } from '@trustvc/trustvc';

// DocumentBuilder automatically uses v2.0 context and ECDSA-SD-2023
const document = new DocumentBuilder({
// Adds a custom vocabulary used to define terms in the `credentialSubject`.
// Users can define their own context if they have domain-specific fields or custom data structures.
});

// Add credential subject
document.credentialSubject({
id: 'did:example:student123',
name: 'Alice Johnson',
studentId: 'STU-2024-001',
degree: 'Bachelor of Computer Science',
gpa: 3.9,
graduationYear: 2024,
dateOfBirth: '1999-05-15'
});

// Set validUntil (v2.0 date fields)
document.expirationDate('2029-01-01T00:00:00Z');

// Add credential status for revocation (v2.0 format) - Verifiable Document
document.credentialStatus({
id: 'https://example.com/status/1#42',
type: 'BitstringStatusListEntry',
statusPurpose: 'revocation',
statusListIndex: '42',
statusListCredential: 'https://example.com/status/1'
});

// Or add credential status for Transferable Records
document.credentialStatus({
chain: 'Ethereum',
chainId: 1,
tokenRegistry: '0x1234567890abcdef...',
rpcProviderUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID',
});

Document Builder with Signing and Selective Disclosure

// ECDSA-SD-2023 key pair
const modernKeyPair = {
'@context': 'https://w3id.org/security/multikey/v1',
id: 'did:web:example.com#multikey-1',
controller: 'did:web:example.com',
type: 'Multikey',
secretKeyMultibase: 'z...',
publicKeyMultibase: 'z...'
};

// Sign with mandatory pointers (always visible fields)
const signedDocument = await document.sign(modernKeyPair, CryptoSuite.EcdsaSd2023, {
mandatoryPointers: [
'/credentialSubject/id',
'/credentialSubject/name',
'/credentialSubject/degree'
]
});
console.log(signedDocument);

Mandatory Pointers: In ECDSA-SD-2023, mandatory pointers specify which fields must always be disclosed and cannot be selectively hidden. This is useful for ensuring critical information like the credential subject's ID and name are always visible, while other fields like age or GPA can be selectively disclosed. The pointers use JSON Pointer syntax (RFC 6901) to reference specific fields in the credential.

Default Behavior: If no mandatory pointers are provided, all fields in the credential become selectively disclosable, meaning the holder can choose to hide any field during presentation. However, certain fields like the credential's @context, type, issuer, and validFrom/validUntil are typically always disclosed by default as they are essential for credential verification.

Advanced Document Builder Features

// Add render method for display templates
document.renderMethod({
id: 'https://university.edu/templates',
type: 'EMBEDDED_RENDERER',
templateName: 'ACADEMIC_TRANSCRIPT'
});

// Set the qrcode method to be used for the document
document.qrCode({
uri: 'https://example.com/qrcode',
type: 'TrustVCQRCode',
});

Built-in Derive Function

The new DocumentBuilder includes a built-in derive function for selective disclosure:

// Use DocumentBuilder's derive function for selective disclosure
const derivedDocument = await document.derive([
'/credentialSubject/graduationYear'
// Reveals: id, name, degree (mandatory) + graduationYear
// Hides: studentId, gpa, dateOfBirth
]);
console.log(derivedDocument);

// Verify the derived credential
// ECDSA-SD-2023 credentials must be derived before verification
const isVerified = await document.verify();
console.log(isVerified); // true or false

Migration Checklist

For Developers

  • Update package dependencies to latest versions
  • Replace BLS12-381 key generation with ECDSA-SD-2023
  • Update context URLs from v1 to v2
  • Change date fields: issuanceDatevalidFrom, expirationDatevalidUntil
  • Update status list types: StatusList2021*BitstringStatusList*
  • Replace BbsBlsSignature2020 with ecdsa-sd-2023 cryptosuite
  • Update key format from privateKeyBase58 to secretKeyMultibase
  • Test selective disclosure functionality
  • Update verification logic (remove explicit suite specification)
  • Update DID documents to use Multikey type for modern keys

For Users

  • Ensure your issuer supports v2.0 credentials
  • Update any hardcoded context URLs in your applications
  • Verify that your verification systems support both v1.1 and v2.0
  • Test credential status checking with new BitstringStatusList format
  • Update any date field parsing logic
  • Consider implementing selective disclosure features

Backward Compatibility

The TrustVC libraries maintain backward compatibility, allowing you to:

  • Verify v1.1 credentials using v2.0 libraries
  • Run both v1.1 and v2.0 credentials in the same application
  • Gradually migrate your credential issuance to v2.0

Common Issues and Solutions

Issue 1: Context Resolution Errors

Problem: Old context URLs not resolving Solution: Update to v2.0 contexts and ensure your document loader supports them

Issue 2: Date Field Validation

Problem: Verification fails due to date field mismatches Solution: Use appropriate date fields for each version (issuanceDate for v1.1, validFrom for v2.0)

Issue 3: Key Format Compatibility

Problem: Legacy keys not working with v2.0 Solution: Generate new ECDSA-SD-2023 keys for v2.0 credentials while maintaining legacy keys for v1.1 support

Issue 4: Status List Type Errors

Problem: Status list verification fails Solution: Ensure status credential type matches the credential version (StatusList2021 for v1.1, BitstringStatusList for v2.0)

Resources

Support

If you encounter issues during migration, please:

  1. Check the Common Issues section
  2. Open an issue on GitHub

Note: This migration guide covers the most common use cases. For advanced scenarios or custom implementations, please refer to the individual package documentation or contact our support team.