Skip to main content

Verifiable Credentials (VCs)

Verifiable Credentials are tamper-evident digital credentials that can be cryptographically verified without contacting the issuer. They’re the digital equivalent of physical credentials like driver’s licenses, diplomas, or identity cards.

What is a Verifiable Credential?

A Verifiable Credential is a digital statement made by an issuer about a subject (usually the credential holder) that includes:
  • Claims: Information about the subject (name, date of birth, degree, etc.)
  • Metadata: Issuer, issue date, expiration, credential type
  • Proof: Cryptographic signature that proves authenticity
Think of a VC like a digitally-signed diploma: it contains information about you, who issued it, and a cryptographic proof that it hasn’t been tampered with.

VC Structure

A standard Verifiable Credential follows the W3C data model:
{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://www.w3.org/2018/credentials/examples/v1"
  ],
  "id": "http://example.edu/credentials/3732",
  "type": ["VerifiableCredential", "UniversityDegreeCredential"],
  "issuer": "did:alyra:UniversityPublicKey...",
  "issuanceDate": "2024-01-01T00:00:00Z",
  "expirationDate": "2029-01-01T00:00:00Z",
  "credentialSubject": {
    "id": "did:alyra:StudentPublicKey...",
    "degree": {
      "type": "BachelorDegree",
      "name": "Bachelor of Science in Computer Science"
    },
    "gpa": "3.8"
  },
  "proof": {
    "type": "Dilithium",
    "created": "2024-01-01T00:00:00Z",
    "proofPurpose": "assertionMethod",
    "verificationMethod": "did:alyra:UniversityPublicKey...#keys-1",
    "signature": "eyJhbGciOiJEaWxpdGhpdW0yIiwiYjY0IjpmYWxzZSwiY3JpdCI..."
  }
}

VC Components

Defines the meaning of terms used in the credential. Points to JSON-LD context files.
"@context": [
  "https://www.w3.org/2018/credentials/v1",
  "https://example.com/contexts/education/v1"
]
Unique identifier for this specific credential instance.
"id": "https://university.edu/credentials/12345"
Specifies what kind of credential this is. Always includes VerifiableCredential.
"type": ["VerifiableCredential", "DriverLicense"]
DID of the organization that issued the credential.
"issuer": "did:alyra:DMVPublicKey..."
When the credential was issued (ISO 8601 format).
"issuanceDate": "2024-01-15T10:30:00Z"
The actual claims about the subject. This is the main content of the credential.
"credentialSubject": {
  "id": "did:alyra:HolderDID...",
  "givenName": "Alice",
  "familyName": "Smith",
  "birthDate": "1990-01-01"
}
Cryptographic proof that makes the credential verifiable.
"proof": {
  "type": "Dilithium",
  "created": "2024-01-15T10:30:00Z",
  "verificationMethod": "did:alyra:IssuerDID...#keys-1",
  "signature": "base64_encoded_signature..."
}

VC Lifecycle

Issuance Process

In Sphyre, credential issuance follows these steps:
1

User Requests Credential

User fills out a credential request form in Sphyre ALV
const request = {
  schemaId: "national-id-v1",
  templateId: "gov-national-id",
  claims: {
    fullName: "Alice Smith",
    dateOfBirth: "1990-01-01",
    nationality: "USA"
  },
  holderDid: "did:alyra:UserPublicKey..."
};
2

Issuer Reviews Request

Request appears in issuer dashboard (Sphyre Issuers)
3

Issuer Approves & Signs

Issuer approves and Fortro Engine creates the VC
let credential = VerifiableCredential {
    context: vec![VC_CONTEXT.to_string()],
    id: generate_credential_id(),
    types: vec!["VerifiableCredential".into(), schema.name.clone()],
    issuer: issuer_did.clone(),
    issuance_date: Utc::now(),
    credential_subject: claims,
    proof: sign_credential(&credential_data, &issuer_private_key)
};
4

Store on IPFS

Credential is uploaded to IPFS for decentralized storage
let ipfs_hash = ipfs_client.add_json(&credential).await?;
5

Anchor on Blockchain

IPFS hash is anchored on Ethereum for immutability
let tx = contract.anchor_credential(
    ipfs_hash,
    credential.id,
    issuer_did
).send().await?;
6

Deliver to Holder

Credential is sent to user’s wallet

Verification Process

Verifiers can check credential authenticity without contacting the issuer:
1

Receive Presentation

Verifier receives a Verifiable Presentation containing the VC
2

Extract Credential

Parse the VC and extract the proof
3

Resolve Issuer DID

Get issuer’s public key from their DID
const didDocument = await resolveDID(credential.issuer);
const publicKey = didDocument.verificationMethod[0].publicKeyBase64;
4

Verify Signature

Check cryptographic signature using issuer’s public key
const isValid = verifySignature(
  credential,
  credential.proof.signature,
  publicKey
);
5

Check Blockchain Anchor

Verify the credential hash exists on blockchain
const onChain = await contract.verifyAnchor(ipfsHash);
6

Check Revocation Status

Ensure credential hasn’t been revoked
const isRevoked = await contract.isRevoked(credential.id);
7

Validate Expiration

Check if credential is still valid
const isExpired = new Date() > new Date(credential.expirationDate);

Credential Types in Sphyre

Sphyre supports various credential schemas:

National ID

Government-issued identity credentials with full name, DOB, nationality

Driver's License

Driving credentials with license class, restrictions, expiration

Student ID

Educational institution credentials with student number, program

Employee Badge

Employment credentials with job title, department, access level

Professional License

Professional certifications with license number, specialty, board

Health Insurance

Insurance credentials with policy number, coverage type, provider

Selective Disclosure

VCs support selective disclosure - sharing only specific claims:

Example: Age Verification

Instead of showing full ID:
// Full credential contains:
{
  "name": "Alice Smith",
  "dateOfBirth": "1990-01-01",
  "address": "123 Main St",
  "idNumber": "ABC123456"
}

// Selective disclosure - only share age proof:
{
  "isOver21": true  // Proven via ZKP without revealing exact birthdate
}

Verifiable Presentations

To share credentials, holders create Verifiable Presentations (VPs):
{
  "@context": ["https://www.w3.org/2018/credentials/v1"],
  "type": "VerifiablePresentation",
  "verifiableCredential": [{
    // Full VC or selected claims
  }],
  "holder": "did:alyra:HolderPublicKey...",
  "proof": {
    "type": "Dilithium",
    "created": "2024-01-15T12:00:00Z",
    "challenge": "random-challenge-from-verifier",
    "domain": "verifier.sphyre.tech",
    "proofPurpose": "authentication",
    "verificationMethod": "did:alyra:HolderPublicKey...#keys-1",
    "signature": "holder_signature..."
  }
}
Key Points:
  • Holder signs the presentation (not just the credential)
  • Includes challenge from verifier to prevent replay attacks
  • Can contain multiple credentials
  • Proves holder controls the DID

Revocation

Issuers can revoke credentials if needed:

Revocation Methods

Credential contains a reference to a revocation list
"credentialStatus": {
  "id": "https://issuer.com/status/1#94567",
  "type": "RevocationList2020Status",
  "revocationListIndex": "94567",
  "revocationListCredential": "https://issuer.com/status/1"
}

Storage & Privacy

IPFS Storage

Content-Addressed

Files identified by cryptographic hash, ensuring data integrity

Decentralized

No single point of failure, distributed across IPFS network

Immutable

Content cannot be modified; changes create new hash

Optional Encryption

Credentials can be encrypted before upload for privacy

Privacy Considerations

VCs stored on IPFS are public by default. For sensitive data, use encryption or store only hashes.
Privacy Strategies:
  1. Encryption: Encrypt credential before IPFS upload
  2. Hashed Claims: Store only hashes of sensitive data
  3. Zero-Knowledge Proofs: Prove claims without revealing data
  4. Pairwise Credentials: Issue different credentials for different relationships

Schema Management

Schemas define the structure of credentials:
{
  "id": "national-id-v1",
  "name": "National ID",
  "version": "1.0",
  "description": "Government-issued national identification",
  "fields": [
    {
      "name": "fullName",
      "type": "string",
      "required": true,
      "description": "Legal full name"
    },
    {
      "name": "dateOfBirth",
      "type": "date",
      "required": true,
      "description": "Date of birth (YYYY-MM-DD)"
    },
    {
      "name": "nationality",
      "type": "string",
      "required": true,
      "description": "Country of citizenship"
    },
    {
      "name": "idNumber",
      "type": "string",
      "required": true,
      "description": "Unique identification number"
    }
  ],
  "issuer": "did:alyra:GovernmentDID..."
}

Best Practices

Always set appropriate expiration dates. Credentials shouldn’t be valid forever.
"expirationDate": "2029-12-31T23:59:59Z"
Issue specific credentials for specific purposes. Don’t create “super credentials” with all user data.
Include only necessary claims. Less data = better privacy.
Version your schemas and credentials for backward compatibility.
"version": "1.0",
"schemaVersion": "2.1"
Use JSON-LD contexts for semantic interoperability across systems.

VC vs Traditional Credentials

FeatureTraditionalVerifiable Credential
FormatPaper/PDFJSON-LD
VerificationContact issuerCryptographic proof
TamperingEasy to forgeCryptographically impossible
SharingPhotocopySelective disclosure
RevocationDifficultInstant via blockchain
PrivacyFull disclosureMinimal disclosure
PortabilityPhysical/emailDigital wallet
SpeedDays/weeksInstant

Advanced Features

Composite Credentials

Combine multiple credentials for complex proofs:
{
  "type": "VerifiablePresentation",
  "verifiableCredential": [
    { /* University degree */ },
    { /* Professional license */ },
    { /* Employment verification */ }
  ]
}

Derived Credentials

Create new credentials based on existing ones:
// Original: Full birthdate credential
// Derived: "Over 21" credential
const derivedCredential = deriveCredential(
  originalCredential,
  ["isOver21"],  // Only this claim
  zkProof        // Zero-knowledge proof
);

Delegated Credentials

Issue credentials on behalf of another issuer:
{
  "issuer": "did:alyra:RegionalOffice...",
  "delegatedFrom": "did:alyra:NationalAuthority...",
  "proof": {
    "type": "DelegatedProof",
    "delegationCredential": { /* Proof of delegation */ }
  }
}

Code Examples

Creating a Credential (Backend)

use serde_json::json;
use chrono::Utc;

async fn issue_credential(
    issuer_did: &str,
    holder_did: &str,
    claims: HashMap<String, String>,
    schema_id: &str
) -> Result<VerifiableCredential> {
    // Create credential structure
    let credential = json!({
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        "type": ["VerifiableCredential", schema_id],
        "issuer": issuer_did,
        "issuanceDate": Utc::now().to_rfc3339(),
        "credentialSubject": {
            "id": holder_did,
            "claims": claims
        }
    });
    
    // Sign credential
    let signature = sign_with_dilithium(&credential, issuer_private_key)?;
    
    // Add proof
    credential["proof"] = json!({
        "type": "Dilithium",
        "created": Utc::now().to_rfc3339(),
        "verificationMethod": format!("{}#keys-1", issuer_did),
        "signature": base64::encode(signature)
    });
    
    Ok(credential)
}

Verifying a Credential (Frontend)

async function verifyCredential(credential: VerifiableCredential) {
  // 1. Resolve issuer DID
  const issuerDID = credential.issuer;
  const didDocument = await resolveDID(issuerDID);
  
  // 2. Get public key
  const publicKey = didDocument.verificationMethod[0].publicKeyBase64;
  
  // 3. Verify signature
  const credentialWithoutProof = { ...credential };
  delete credentialWithoutProof.proof;
  
  const isValidSignature = await verifyDilithiumSignature(
    JSON.stringify(credentialWithoutProof),
    credential.proof.signature,
    publicKey
  );
  
  // 4. Check expiration
  const isExpired = new Date() > new Date(credential.expirationDate);
  
  // 5. Check revocation
  const isRevoked = await checkRevocationStatus(credential.id);
  
  return isValidSignature && !isExpired && !isRevoked;
}

Resources

Next Steps

1

Issue Your First Credential

2

Learn About ZKP

Explore Zero-Knowledge Proofs for privacy
3

API Integration

4

Build an Issuer