A credential is made from header, body, an array of signatures and attachments. This design allows a credential to be endorsed by multiple signatures from different issuers, or using different verification stacks. Credentials are primarily stored and presented to holders in JSON format, for readability, while other formats like YAML, XML can also be supported. Note the formats of storing and showing the credential are not related to how the digest (i.e., the hash value) is computed, and will not affect zero-knowledge proof generation. The digest algorithm is determined by the verification stack and it can evolve without upgrading the main protocol.
Header contains the common fields that all every credential must have.
For example, to create a credential of the loyalty points, that a Galxe.com user has earned in the Galxe space, assuming that we have registered the context “Galxe.com loyalty points” on-chain and the context ID is 0xdeadbeef....
, when the user’s Galxe ID is 123123
, Here is an example header. Note that Its type ID is 2, meaning that it is using the primitive credential type containing just one scalar value.
The body part stores all claim values, corresponding to the claim types declared in the credential type. Below is an example of credential body and its type definition. Note that the status
claim value stores not only the hash value, but also the original value. This is because that the claim declaration specify that the hash algorithm is user-defined ( c
), storing both values is mandatory by protocol’s specification.
The signature part of a credential is an array of pairs of metadata and signature, allowing one credential to have multiple signatures. Although the format of a signature is specified by the verification stack used by the issuer, all signatures shares a common metadata structure.
Signed signature metadata
UINT64_MAX
if the credential never expires.Identity commitment will be used as the key to the power of creating a link from an identity to another one, e.g., from a twitter handle to an EVM address. At verification, a holder must generate a proof that he is the actual owner of the credential, by showing that he knows the secrets behind the commitment, and output a number representing the pseudonymous identity the verifier will see. Thus, the verifier is assured that the verification originates from the holder and confirms the holder’s identity as the entity communicating with the verifier in that session.
Note that issuer_id
is not part of the signed metadata. In our design, the issuer_id
is intentionally omitted. This approach prevents the embedding of trust relationships between the issuer and public keys within the credential, because the verification of a public key trustworthiness can only occur externally to the zero-knowledge proof circuit, as it involves stateful checks beyond cryptographic validation. This simplification eliminates the redundancy of including an issuer_id
for zk proof generation. Furthermore, this flexibility enhances the system’s scalability and the fluidity of trust relationships, thereby maintaining the simplicity of the credential’s structure while accommodating a dynamic network of trust.
Unsigned signature metadata
In the unsigned signature part, there are three mandatory fields: issuer_id
, chain_id
and public_key
. They do not require explicit signing, because the issuer’s signature inherently implies the public key value, and issuer_id and chain_id should not be signed and fixed.
The “attachments” section houses values that are external to the main body of the credential. It means that these values are not authenticated (or signed) by the issuer by default. However, issuers have the freedom to add an signature attachment that signs all the other attached fields, using a preferred signature algorithm. The protocol do have any specification for this but may introduce one in the future.
Issuers have the flexibility to introduce any arbitrary fields to this section. For instance, an issuer could provide a passport credential accompanied by a PNG photo. In such a case, issuer can place the hash of the photo in the credential body, while the actual binary data of the image would reside in the attachments.
Credential holders can generate zero-knowledge proofs to selectively disclose specific pieces of information on credentials. Regardless of the chosen verification stack, every proof consists of two main components: a proof and some public inputs. The structure and representation of a proof is varying by the verification stack. There are two types of public inputs: intrinsic public signals and user-defined signals. Intrinsic signals must be the first 8 or 9 signals (depending on if it is revocable) of the public input array. The protocol mandates the sequence of intrinsic signals as described below:
internal_nullifier
and external_nullifier
. When using the babyzk verification stack, this value is computed as poseidon(internal_nullifier, external_nullifier)
.msg.sender
, or indirectly be authenticated through an EIP-712 signature.prop<248, c, 1>
. Here, the least significant bit designates equality, while its [249..1] bits convey the value that underwent comparison.A credential is made from header, body, an array of signatures and attachments. This design allows a credential to be endorsed by multiple signatures from different issuers, or using different verification stacks. Credentials are primarily stored and presented to holders in JSON format, for readability, while other formats like YAML, XML can also be supported. Note the formats of storing and showing the credential are not related to how the digest (i.e., the hash value) is computed, and will not affect zero-knowledge proof generation. The digest algorithm is determined by the verification stack and it can evolve without upgrading the main protocol.
Header contains the common fields that all every credential must have.
For example, to create a credential of the loyalty points, that a Galxe.com user has earned in the Galxe space, assuming that we have registered the context “Galxe.com loyalty points” on-chain and the context ID is 0xdeadbeef....
, when the user’s Galxe ID is 123123
, Here is an example header. Note that Its type ID is 2, meaning that it is using the primitive credential type containing just one scalar value.
The body part stores all claim values, corresponding to the claim types declared in the credential type. Below is an example of credential body and its type definition. Note that the status
claim value stores not only the hash value, but also the original value. This is because that the claim declaration specify that the hash algorithm is user-defined ( c
), storing both values is mandatory by protocol’s specification.
The signature part of a credential is an array of pairs of metadata and signature, allowing one credential to have multiple signatures. Although the format of a signature is specified by the verification stack used by the issuer, all signatures shares a common metadata structure.
Signed signature metadata
UINT64_MAX
if the credential never expires.Identity commitment will be used as the key to the power of creating a link from an identity to another one, e.g., from a twitter handle to an EVM address. At verification, a holder must generate a proof that he is the actual owner of the credential, by showing that he knows the secrets behind the commitment, and output a number representing the pseudonymous identity the verifier will see. Thus, the verifier is assured that the verification originates from the holder and confirms the holder’s identity as the entity communicating with the verifier in that session.
Note that issuer_id
is not part of the signed metadata. In our design, the issuer_id
is intentionally omitted. This approach prevents the embedding of trust relationships between the issuer and public keys within the credential, because the verification of a public key trustworthiness can only occur externally to the zero-knowledge proof circuit, as it involves stateful checks beyond cryptographic validation. This simplification eliminates the redundancy of including an issuer_id
for zk proof generation. Furthermore, this flexibility enhances the system’s scalability and the fluidity of trust relationships, thereby maintaining the simplicity of the credential’s structure while accommodating a dynamic network of trust.
Unsigned signature metadata
In the unsigned signature part, there are three mandatory fields: issuer_id
, chain_id
and public_key
. They do not require explicit signing, because the issuer’s signature inherently implies the public key value, and issuer_id and chain_id should not be signed and fixed.
The “attachments” section houses values that are external to the main body of the credential. It means that these values are not authenticated (or signed) by the issuer by default. However, issuers have the freedom to add an signature attachment that signs all the other attached fields, using a preferred signature algorithm. The protocol do have any specification for this but may introduce one in the future.
Issuers have the flexibility to introduce any arbitrary fields to this section. For instance, an issuer could provide a passport credential accompanied by a PNG photo. In such a case, issuer can place the hash of the photo in the credential body, while the actual binary data of the image would reside in the attachments.
Credential holders can generate zero-knowledge proofs to selectively disclose specific pieces of information on credentials. Regardless of the chosen verification stack, every proof consists of two main components: a proof and some public inputs. The structure and representation of a proof is varying by the verification stack. There are two types of public inputs: intrinsic public signals and user-defined signals. Intrinsic signals must be the first 8 or 9 signals (depending on if it is revocable) of the public input array. The protocol mandates the sequence of intrinsic signals as described below:
internal_nullifier
and external_nullifier
. When using the babyzk verification stack, this value is computed as poseidon(internal_nullifier, external_nullifier)
.msg.sender
, or indirectly be authenticated through an EIP-712 signature.prop<248, c, 1>
. Here, the least significant bit designates equality, while its [249..1] bits convey the value that underwent comparison.