VC

Before embarking on a magic zkID Login Journey, we still need to acquire some new knowledge about VCs, starting with the concept:

What are Verifiable Credentials

In the physical world, a credential consists of:

  • Owner: Information related to identifying the owner of the credential

  • Attester: Information related to the issuing authority (for example, a city government, national agency, or certification company)

  • Credential Type: Information related to the data structure of a claim

  • Credential Content: The data in a credential, which contains the personal information of the owner.

A verifiable credential (VC) can represent the same information as a physical credential represents. The addition of technologies, such as digital signatures, makes verifiable credentials tamper-proof and more trustworthy than their physical counterparts.

Claim(s) are assertions made about a subject. They are created by claimers without being attested yet. Once the Claim is successfully attested, the claim and some auxiliary attestation information will be packed into a Verifiable Credential and be sent back to the claimer.

Holders of verifiable credentials can generate verifiable presentations and then share these verifiable presentations with verifiers to prove they possess verifiable credentials with certain characteristics.

Key roles of VC

Note that there are several organizations and communities aiming to define the terminology and the different roles and processes in the Self-Sovereign Identity Ecosystem. Our technology is compatible with a wide range of Verifiable Credential structure types.

Core Data Model

cType

Verifiable credentials MUST have a type property.

A credential type ('cType' for short) defines the structure of a claim. You can think of it as the data model for your claim.

CTYPE is a JSON description of a data structure. It contains a list of key value pairs, where each value is of a defined type.

A Simple Example Of cType

   const base: BaseCType = {
      title: 'Test',
      description: 'Test',
      type: 'object',
      properties: {
        name: {
          type: 'string'
        },
        age: {
          type: 'integer'
        },
        level: {
          type: 'integer'
        }
      },
      required: ['name', 'age']
    };

The example above forms a base cType, which can only contain three kinds of info, with the type 'string', 'integer' and 'integer' respectively:

  • name (string)

  • age (integer)

  • level (integer)

The attester can create different types of cType according to their own need. Depending on the properties defined by the attester, the generation process will make different cTypes. However, such a cType JSON (like 'A Simple Example Of cType') is too large to be stored on the chain, so we perform the Keccak256 hash algorithm to get a hexadecimal string (start with '0x'), which is called cTypeHash (the actual data stored on-chain.)

In a word, each cTypeHash is bound to a unique cType as shown above, this kind of structure can help us standardize the verifiable credential structure.

RawCredential

RawCredential is holder's origin statements about a subject (usually themselves) with some other information. A subject is a thing about which statements can be made. Statements are expressed using subject-property-value relationships. The statesments are called credentialSubject.

RawCredential contains the following six sections :

  • ctype: the structure of a Credential;

  • credentialSubject: holder's origin claim about the subject;

  • credentialSubjectNonceMap: each statement is bound to a unique and random UUID(a.k.a nonce), starting with '0x';

  • credentialSubjectHashes: each statement can be hashed with its own UUID(nonce), and generate a hash result starting with '0x';

  • holder: holder's DidUrl;

  • hasher: the hash function used in this RawCredential, the first HashType is the Merkle Tree's hash function, the second is used at the other situations;

export interface RawCredential {
  ctype: HexString;
  credentialSubject: CredentialSubject;
  credentialSubjectNonceMap: Record<HexString, HexString>;
  credentialSubjectHashes: HexString[];
  holder: DidUrl;
  hasher: [HashType, HashType];
}

When a Claimer(Holder) creates a new RawCredential for a cType, then the RawCredential will be associated with the Claimer’s identity (by filling the field holder with Claimer's DID).

To get an Attestation from an Attester, the RawCredential should involve some cryptographic methods. For example, we generate claimHashes from contents via Recursive-length prefix (RLP) serialization and some hash function(picked by the holder) and get random Nonces via uuid.v4() and finally use some hash function to get credentialSubjectHashes.

Finally, we could obtain a rootHash, which is generated from a claimHashTree.

DigestHash

DigestHash is the unique identifier of a VC, it can be calculated(using Hash Function) via four data:

  • rootHash: the calculated rootHash of Holder's statements;

  • holder: the Holder's DidUrl;

  • expirationDate: the expiration date of the Credential

  • ctype: the structure of a claim;

export type DigestPayload = {
  // rootHash of credential subject
  rootHash: HexString;
  
  // the holder of vc
  holder: DidUrl;
  
  // expiration date
  expirationDate?: number;
  
  // ctype hash
  ctype: HexString;
};
// calculate the DigestHash via picked hash function
export function calcDigest(
  payload: DigestPayload,
  hashType: HashType = DEFAULT_DIGEST_HASH_TYPE
): DigestResult

The DigestHash is what makes an attested Credential unique and immutable. With these cryptography, we could verify the credential's validity easily. Changing (or tampering with) an attested claim will always result in a different claimHash, so in such cases a verification will always fail.

DigestHash can be regarded as an identifier of VC. For the reason that, even if a user request for two credential with the same content and the same cType, the user will get two credential with different DigestHash -- due to the existence of UUID.

In the SDK, we wrapped the verification method inside, which could help the developers to check whether a credential is valid.

Verify Credential

export async function vcVerify(
  vc: VerifiableCredential,
  resolverOrDidDocument?: DidResolver | DidDocument
): Promise<boolean>

Last updated