VC Specification

Before diving into this section, understanding VC (Verifiable Credential) necessitates familiarity with the DID concept. Therefore, please ensure you are acquainted with the relevant chapters and content about DID.

Note: the VCs mentioned here are assumed to be Private VCs by default. The main difference between Private and Public VCs is in the construction of the user data Merkle tree; the leaf nodes in Public VCs are not salted, and Public VCs are intended for public display purposes, hence they are not elaborated on extensively here.

Overview

Before exploring VC, let's briefly discuss the concept of credentials. Credentials are common in our daily lives. In the physical world, they can be a driver's license to prove driving skills, a diploma to certify educational level, or an ID card to verify legal identity in a country. However, in the digital world, it's challenging to represent these physical credentials, leading to a gap in the convenience that real-world certificates offer.

To address this, the W3C proposed a standard for credentials in the digital realm that are secure, privacy-respecting, and machine-verifiable, known as Verifiable Credentials.

In the privacy identity world built by zCloak, these data are referred to as self-carried data, meaning only the user themselves actually possess the VC, fully transferring data ownership to the user. For the Web3 context, VCs are adapted to blockchain application scenarios based on W3C standards. In simple terms, VCs can be understood as various cards in the blockchain world. A VC could be a POAP (Proof of Attendance Protocol), an NFT (Non-Fungible Token), or even just a simple card. However, each card follows a set of digital signatures that validate the data as endorsed by the issuer and verify its authenticity. To align with the Ethereum ecosystem, the cryptographic applications used in VCs are also adapted for Ethereum compatibility.

VC Architecture

Continuing with the W3C standard's interpretation of VC (Verifiable Credentials), our VCs at zCloak also adopt a similar structure. The basic structure of a VC as per our implementation is depicted in the following Figure 1.

From the basic structure outlined above, we can see that a VC (Verifiable Credential) primarily comprises three parts:

  1. VC Metadata: This section includes the basic information of the VC, such as:

    1. Context (context): The standards or protocols that the VC adheres to.

    2. Issuer (issuer): The address of the entity that issues the VC.

    3. Type (type): The type or category of the VC.

    4. Issuance Date (issuanceDate): The date when the VC was issued.

  2. VC Claim: This part expresses the specific claims made by the VC. The content here is often presented in a name-value format. The structure of this part is typically represented using the Credential Subject structure, detailing the specific attributes or claims about the credential subject (the individual or entity the VC pertains to).

  3. Proof: The proof section is critical as it transforms an ordinary credential into a verifiable one. The Proof mainly contains the digital signature of the VC issuer. The signature algorithm is aligned with Ethereum rules, utilizing the ECDSA (Elliptic Curve Digital Signature Algorithm) for generation. This ensures the integrity and authenticity of the VC, allowing for secure verification.

VC Core Concepts

Let's briefly introduce some of the core fields in VC (Verifiable Credentials), with adaptations specific to Web3 use cases, as compared to the standard W3C model.ctype (Credential Type): In the original W3C VC standard, this is represented by the type field, but in our adaptation, we have transformed it into the ctype field. There are two main differences from the W3C standard:

  1. Emphasis on Type Entity: We emphasize the structured content of the credential. For each distinct ctype, there is a unique ctypehash derived from hashing the necessary fields of the ctype structure. Since ctype is a global concept, the complete ctype structure can be retrieved using the ctypehash.

  2. Publisher Endorsement: We introduce the concept of publisher endorsement in the issuance of ctype. Each publisher is required to sign the ctype hash before issuing it, using the ECDSA (Elliptic Curve Digital Signature Algorithm) which is consistent with Ethereum's algorithm. In our VCs, the type field is replaced by ctype, with its value being the ctypehash.

These adaptations make the VC more suitable for Web3 scenarios, ensuring that the credential is not only verifiable but also carries a structured and endorsed type that is recognizable and consistent within the decentralized ecosystem.

credentialSubject:This is the main subject part of the credential. It expresses one or more subjects' claims in a key-value pair format. For example:

{
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
}

digest:This serves as the unique identifier of the VC, corresponding to the id field in the W3C VC standard. Unlike a typical UUID, the calculation of digest is more practical and unique. The digest considers the credential subject, holder DID, ctype, expiration date, and issuance date. When generating the proof signature for the VC, the digest is a core component of the signature message.

credentialSubjectHashes:These are the salted hash values of the leaf nodes in the Merkle tree built from the user data in the credential subject. For instance:

{
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
    "credentialSubjectHashes": [
        "0x9c9a50d930a46ef4fa884de415e250200ee8aec0524c9fded3c5e9c499ffc987",
        "0x982225bf5561e4e35e71472b09f4eac29f2fa227383df6764011cb3bf5d94789",
        "0x9083230a121ae6e136050ccf21a9a479aa812ad65318a68325f2e839e1cd6336"
    ],
}

credentialSubjectNonceMap:This maps the encoded user data to its nonce. The nonce is used for salting user data to generate leaf nodes in the construction of the Merkle tree.

{
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
    "credentialSubjectHashes": [
        "0x9c9a50d930a46ef4fa884de415e250200ee8aec0524c9fded3c5e9c499ffc987",
        "0x982225bf5561e4e35e71472b09f4eac29f2fa227383df6764011cb3bf5d94789",
        "0x9083230a121ae6e136050ccf21a9a479aa812ad65318a68325f2e839e1cd6336"
    ],
    "credentialSubjectNonceMap": {
        "0x8e60375533557ac1f3235750024e04c194c4b7732b81a6a92d033a8167b85763": "0xb5744ef1a613765da5960e4aae74ff4672e4d71c9bd404f0d5a9b5c1aa89ec35",
        "0x2d4e0ae8a576732fa15b0bdb0c7d7c36284657a18fdae95ee32439383dfe720b": "0x9920b9dd7d4165b655f3a03e57df58905a33e1a1257a7595fbc60c54eceb6365",
        "0x121af31876dce595e939b017837a144c953fe633f132f99e36e0f66a486dd134": "0x2d0b24fa725bc2d1fd27b891bc9a00f9a6f715125af3ff90e960e64d56a26fac"
    }
}

Hasher:This cryptographic hash method includes an array of two hash methods. The first hash method is used to encode credentialSubject data and build the VC Merkle tree, while the second is used for computing the hash for the VC digest.

{
    "hasher": [
        "RescuePrimeOptimized",
        "Keccak256"
    ],
}

Verifiable Data Registry (VDR): Similar to the VDR design in the DID system, this is used for backing up encrypted VCs. In the zCloak VC system, the VDR is designed as a point-to-point asymmetric encrypted message system. This message channel is primarily used for transmitting VCs and VPs, but it also supports the transfer of other data types.

VC Merkle Tree:The Merkle tree for VC fields is built using the data in the credentialSubject to highlight the VC's verifiability and tamper-proof nature.To further illustrate the construction of the VC Merkle Tree, let's use the example VC mentioned earlier:

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1"
    ],
    "version": "2",
    "ctype": "0xca9b3d79ca4326d0c05caf60d9b6a0a405bacc2fabf61c230f3f2cc0addd4593",
    "issuanceDate": 1700790481800,
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
    "issuer": [
        "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc"
    ],
    "holder": "did:zk:0xE5b8641d32a434BF3B5E6Ea6AFfdA1B56c558eea",
    "hasher": [
        "RescuePrimeOptimized",
        "Keccak256"
    ],
    "digest": "0xad8c4ad09320db11d6b5bd2266ab93a9b92c7fff39c6821e951017b1a31f9001",
    "proof": [
        {
            "type": "EcdsaSecp256k1SignatureEip191",
            "created": 1700790481841,
            "verificationMethod": "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc#key-0",
            "proofPurpose": "assertionMethod",
            "proofValue": "z9kmgzHe2fhSH5NFTeFRNfTfKr3bLYkXvpxJhYkqTfWWStUwPrRUhU7Kr2UiZ12LyHeGkF2bjEYYs23BAFesDU52zb"
        }
    ],
    "credentialSubjectHashes": [
        "0x9c9a50d930a46ef4fa884de415e250200ee8aec0524c9fded3c5e9c499ffc987",
        "0x982225bf5561e4e35e71472b09f4eac29f2fa227383df6764011cb3bf5d94789",
        "0x9083230a121ae6e136050ccf21a9a479aa812ad65318a68325f2e839e1cd6336"
    ],
    "credentialSubjectNonceMap": {
        "0x8e60375533557ac1f3235750024e04c194c4b7732b81a6a92d033a8167b85763": "0xb5744ef1a613765da5960e4aae74ff4672e4d71c9bd404f0d5a9b5c1aa89ec35",
        "0x2d4e0ae8a576732fa15b0bdb0c7d7c36284657a18fdae95ee32439383dfe720b": "0x9920b9dd7d4165b655f3a03e57df58905a33e1a1257a7595fbc60c54eceb6365",
        "0x121af31876dce595e939b017837a144c953fe633f132f99e36e0f66a486dd134": "0x2d0b24fa725bc2d1fd27b891bc9a00f9a6f715125af3ff90e960e64d56a26fac"
    }
}

Based on the description of the credentialSubject, credentialSubjectNonceMap, and credentialSubjectHashes fields, let's look at the rules for constructing the Merkle tree:

  1. Extracting Values from credentialSubject: Take each value from the credentialSubject, and use the order of fields in credentialSubject (from front to back) to determine the left-to-right order for building the Merkle tree.

  2. Encoding and Mapping: Encode each extracted value into HexString format. These encoded values correspond to each key in credentialSubjectNonceMap.

  3. Creating Leaf Nodes with Nonces and Hashing: For each encoded data, find its corresponding nonce (randomly generated and mapped in credentialSubjectNonceMap). Concatenate the encoded data with its nonce and hash this concatenated value using the first hash method specified in Hasher. The result of this hashing process forms the leaf nodes of the Merkle tree.

  4. Building the Tree: Use the same hashing method as in the previous step to construct the other nodes, combining them to form a complete Merkle tree.

Following these rules, we can construct the Merkle tree as shown in the figure below (Figure 2):

From the process of constructing the Merkle tree as described, we can understand that the Merkle tree is a crucial structure in a VC (Verifiable Credential). This is reflected at the field level as roothash, which is the root hash of the tree. The roothash provides a snapshot that represents the data behind each leaf node. In the context of the zCloak system, this roothash plays a significant role in the computation of the digest field of the VC.

The calculation of the digest field is multifaceted. It includes not only the roothash but also incorporates the holder's DID, the expiration date, ctypeHash, and the issuance date. This comprehensive inclusion gives the digest field a multi-dimensional meaning, reflecting various aspects of the VC such as its origin, validity, and content. Therefore, the digest serves as a unique identifier for the VC.

presentations:The concept of VP (Verifiable Presentation) is explained by the W3C as "data derived from one or more verifiable credentials, issued by one or more issuers and shared with a verifier." Simply put, it's a structure for presenting credentials, designed to aggregate and present multiple verifiable credentials. The typical use case of a VP is in its verifiable form, where it's utilized to collect multiple verifiable credentials. I'll briefly explain its workflow with the help of an illustration (Figure 3):

Imagine a scenario as depicted in the illustration. A Holder, who is a graduate of a university, receives a graduation certificate VC from the university (the Issuer) on their graduation day. This certificate contains all relevant graduation information about the Holder. One day, the university invites its alumni to attend a post-season basketball game. The Verifier in this scenario is the university's security guard. When the Holder presents their graduation certificate VC in the form of a VP to the Verifier, the Verifier, upon verifying that the VC is a genuine graduation certificate issued by the university, allows the Holder to enter.

From this description, it's evident that VP is specifically used for presenting VCs. Under the W3C standards, two types of VP are defined: VP and VP_SelectiveDisclosure. For enhanced privacy protection, we propose a third type: VP_Digest.

  • VP: This type presents the entire VC to the Verifier, meaning all information in the Holder's VC is disclosed.

  • VP_SelectiveDisclosure: This type allows for selective disclosure, revealing only parts of the credentialSubject information in the VC to the Verifier.

  • VP_Digest: This type presents only the VC's digest to the Verifier. Combined with checking the ctype, it verifies that the Holder possesses a certain type of VC without disclosing the VC's content.

Each of these presentation types serves different purposes and offers varying levels of privacy and information disclosure, making the system flexible and adaptable to various verification needs.

Concrete Lifecycle

In this section, you'll learn about the usage lifecycle of VCs (Verifiable Credentials) within the VC system. There are two primary modes of VC usage: the request mode and the issue mode. Despite their differences, both modes typically follow a common trajectory in their usage:

  1. Issuing One or More VCs: The initial step involves the creation and issuance of one or more VCs by an issuer. These VCs contain specific claims about the credential subject (the holder).

  2. Storing VCs in a VC Repository: Once issued, these VCs are stored in a VC repository, which could be a digital wallet or another form of secure storage. This repository ensures the safekeeping and accessibility of the VCs for future use.

  3. Combining VCs into a VP for Verification: When needed, the holder combines one or more VCs into a VP (Verifiable Presentation). This VP is then provided to a verifier. The VP can be tailored to the specific verification requirements, such as including full disclosure, selective disclosure, or just the digest of the VCs, depending on the level of privacy and information needed.

  4. Verification of the Received VP by the Verifier: The final step involves the verifier checking the received VP. This verification process confirms the authenticity and validity of the claims made in the VCs contained within the VP.

To provide a detailed explanation of the entire lifecycle, let's delve into these two different usage modes:

Request Mode

In the Request Mode, the user (Holder) assumes the role of a Claimer, initiating a request for a VC (Verifiable Credential). The Attester decides whether to issue the VC to the Claimer, using the assertionMethod in their DID for issuance. The Verifier is responsible for verifying the correctness of the VP (Verifiable Presentation) sent by the Claimer.

Request Mode Workflow:

  1. Claimer Submits Request: The Claimer fills in personal data and sends a request to the Attester.

  2. Attester's Decision: Upon receiving the request, the Attester reviews the Claimer's data, decides whether to issue the VC, and then sends an affirmative response with the VC or a rejection response.

  3. Claimer Prepares VP: The Claimer, upon receiving the VC, combines it into a VP and sends it to the Verifier.

  4. Verifier Validates VP: The Verifier checks the VP and communicates the results to the Claimer.

This mode can be applied in DAO (Decentralized Autonomous Organization) governance, where users submit their blockchain addresses and names to DAO administrators. The administrators verify the identities and issue appropriate permission VCs to qualified DAO members. These VCs can then serve as entry requirements for participating in DAO activities.

Issue Mode

In the Issue Mode, the user's role as a Holder remains the same, but they passively receive the VC and construct a VP. The Issuer fills in the VC Claim, sends the VC directly to the Holder without requiring a request, and the Verifier checks the VP sent by the Holder and provides feedback on the validation.

Issue Mode Workflow:

  1. Issuer Creates VC: The Issuer fills in the VC Claim, constructs the VC, and sends it to the Holder.

  2. Holder Receives and Prepares VP: The Holder receives the VC, creates a VP, and sends it to the Verifier.

  3. Verifier Validates VP: The Verifier checks the VP and informs the Holder of the results.

Compared to the Request Mode, the Issue Mode is more straightforward, directly delivering the VC to the Holder. It is well-suited for issuing POAPs, membership cards, and can be applied in scenarios like proof of attendance for DAO-governed events. The VC can also be used for benefits in future events, such as attendance discounts.

VC Application

zkID Card

The zkID Card is an upper-level application of VC, which can be prepared and issued through the zkID Card Center. In the Card Center platform, each Card is essentially a VC. In the Card Maker, each template created is a structure containing ctype, along with other setting fields for the template. The purpose of designing this application is to facilitate users to better use and understand our product. In this system, the template corresponds to ctype, and the card corresponds to VC. This approach simplifies the user experience by providing a clear, visual representation of VCs and their types.

zkSBT

zkSBT is a post-operation based on VC, working in conjunction with our zkID Wallet to perform zero-knowledge proof validation locally at the user's end. The result of this validation is used to generate a corresponding SBT (Soulbound Token). Let's delve into an example to understand this process better:

Alice undergoes a KYC process on a platform. The KYC provider issues her a VC containing her KYC information. She then plans to participate in an adult-only DeFi activity, which requires an adult zkSBT as an entry threshold. Using her zkID Wallet, Alice generates a zero-knowledge proof (zkp), which calculates necessary fields from her KYC VC, such as age. After completing the zero-knowledge proof process, the zkID Wallet produces outputs related to the zkVM and a verdict on whether she is over 18. Alice then runs a zero-knowledge proof validation program on her local machine. Once verified, the application automatically mints a zkSBT for Alice, indicating she is an adult. With this zkSBT, Alice can happily participate in the DeFi activity.

All computations and certifications related to zkp in the above process are performed locally on the user's device. The zero-knowledge proof program is also embedded in the user's local wallet. Managing this process is entirely under the user's control, similar to managing a wallet, and is executed locally, ensuring privacy and security.

VC Example

  1. Private VC

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1"
    ],
    "version": "2",
    "ctype": "0xca9b3d79ca4326d0c05caf60d9b6a0a405bacc2fabf61c230f3f2cc0addd4593",
    "issuanceDate": 1700790481800,
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
    "issuer": [
        "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc"
    ],
    "holder": "did:zk:0xE5b8641d32a434BF3B5E6Ea6AFfdA1B56c558eea",
    "hasher": [
        "RescuePrimeOptimized",
        "Keccak256"
    ],
    "digest": "0xad8c4ad09320db11d6b5bd2266ab93a9b92c7fff39c6821e951017b1a31f9001",
    "proof": [
        {
            "type": "EcdsaSecp256k1SignatureEip191",
            "created": 1700790481841,
            "verificationMethod": "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc#key-0",
            "proofPurpose": "assertionMethod",
            "proofValue": "z9kmgzHe2fhSH5NFTeFRNfTfKr3bLYkXvpxJhYkqTfWWStUwPrRUhU7Kr2UiZ12LyHeGkF2bjEYYs23BAFesDU52zb"
        }
    ],
    "credentialSubjectHashes": [
        "0x9c9a50d930a46ef4fa884de415e250200ee8aec0524c9fded3c5e9c499ffc987",
        "0x982225bf5561e4e35e71472b09f4eac29f2fa227383df6764011cb3bf5d94789",
        "0x9083230a121ae6e136050ccf21a9a479aa812ad65318a68325f2e839e1cd6336"
    ],
    "credentialSubjectNonceMap": {
        "0x8e60375533557ac1f3235750024e04c194c4b7732b81a6a92d033a8167b85763": "0xb5744ef1a613765da5960e4aae74ff4672e4d71c9bd404f0d5a9b5c1aa89ec35",
        "0x2d4e0ae8a576732fa15b0bdb0c7d7c36284657a18fdae95ee32439383dfe720b": "0x9920b9dd7d4165b655f3a03e57df58905a33e1a1257a7595fbc60c54eceb6365",
        "0x121af31876dce595e939b017837a144c953fe633f132f99e36e0f66a486dd134": "0x2d0b24fa725bc2d1fd27b891bc9a00f9a6f715125af3ff90e960e64d56a26fac"
    }
}
  1. Public VC

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1"
    ],
    "version": "2",
    "ctype": "0xca9b3d79ca4326d0c05caf60d9b6a0a405bacc2fabf61c230f3f2cc0addd4593",
    "issuanceDate": 1700637199861,
    "credentialSubject": {
        "name": "vss-claimer",
        "age": 23,
        "company_name": "Test"
    },
    "issuer": [
        "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc"
    ],
    "holder": "did:zk:0xE5b8641d32a434BF3B5E6Ea6AFfdA1B56c558eea",
    "hasher": [
        "Keccak256",
        "Keccak256"
    ],
    "digest": "0x736d5b904fa700d9ab58d215b45b1e512fce56b1279fc647f48d414509463d47",
    "proof": [
        {
            "type": "EcdsaSecp256k1SignatureEip191",
            "created": 1700637199899,
            "verificationMethod": "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc#key-0",
            "proofPurpose": "assertionMethod",
            "proofValue": "z4GsH1Kzn5iZrP6s42dPHtub4mFS6Ze9ZTRSdv5ZGuSE9zDmUKD4hDtJZwcKsraV7UncMYQTC3hPDjzzDT2yFgQiqM"
        }
    ]
}
  1. VP

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1"
    ],
    "version": "1",
    "type": [
        "VP"
    ],
    "verifiableCredential": [
        {
            "@context": [
                "https://www.w3.org/2018/credentials/v1"
            ],
            "version": "2",
            "ctype": "0xca9b3d79ca4326d0c05caf60d9b6a0a405bacc2fabf61c230f3f2cc0addd4593",
            "issuanceDate": 1700790481800,
            "credentialSubject": {
                "name": "vss-claimer",
                "age": 23,
                "company_name": "Test"
            },
            "issuer": [
                "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc"
            ],
            "holder": "did:zk:0xE5b8641d32a434BF3B5E6Ea6AFfdA1B56c558eea",
            "hasher": [
                "RescuePrimeOptimized",
                "Keccak256"
            ],
            "digest": "0xad8c4ad09320db11d6b5bd2266ab93a9b92c7fff39c6821e951017b1a31f9001",
            "proof": [
                {
                    "type": "EcdsaSecp256k1SignatureEip191",
                    "created": 1700790481841,
                    "verificationMethod": "did:zk:0xB16FEfFaED0630F4C580a58ae0349C68609A6fDc#key-0",
                    "proofPurpose": "assertionMethod",
                    "proofValue": "z9kmgzHe2fhSH5NFTeFRNfTfKr3bLYkXvpxJhYkqTfWWStUwPrRUhU7Kr2UiZ12LyHeGkF2bjEYYs23BAFesDU52zb"
                }
            ],
            "credentialSubjectHashes": [
                "0x9c9a50d930a46ef4fa884de415e250200ee8aec0524c9fded3c5e9c499ffc987",
                "0x982225bf5561e4e35e71472b09f4eac29f2fa227383df6764011cb3bf5d94789",
                "0x9083230a121ae6e136050ccf21a9a479aa812ad65318a68325f2e839e1cd6336"
            ],
            "credentialSubjectNonceMap": {
                "0x8e60375533557ac1f3235750024e04c194c4b7732b81a6a92d033a8167b85763": "0xb5744ef1a613765da5960e4aae74ff4672e4d71c9bd404f0d5a9b5c1aa89ec35",
                "0x2d4e0ae8a576732fa15b0bdb0c7d7c36284657a18fdae95ee32439383dfe720b": "0x9920b9dd7d4165b655f3a03e57df58905a33e1a1257a7595fbc60c54eceb6365",
                "0x121af31876dce595e939b017837a144c953fe633f132f99e36e0f66a486dd134": "0x2d0b24fa725bc2d1fd27b891bc9a00f9a6f715125af3ff90e960e64d56a26fac"
            }
        }
    ],
    "id": "0xc7d3fc7516c46e96f1b3a2e06b5b82f8ad381188809a874c607bb1f27c4787f3",
    "proof": {
        "type": "EcdsaSecp256k1SignatureEip191",
        "created": 1700790916674,
        "verificationMethod": "did:zk:0xE5b8641d32a434BF3B5E6Ea6AFfdA1B56c558eea",
        "proofPurpose": "controller",
        "proofValue": "zNm9H3ZQxabMnqiddNVamqph9avfkNGv5MQuQBBBAKbo4phgqA1sUSPupgFLhBbCAZDzXJFf99snEYFFDj2nXoF9ZJ",
        "challenge": "0xb457f6bf088fa4bce7019f9395a31285d7aadb2517eb1b34c38586f419a17539"
    },
    "hasher": [
        "Keccak256"
    ]
}

Last updated