A credential comprises one or more claims made by the issuer of a credential about the subject. For example; a driving license containing the licensee's name, SSN, date of birth, etc is a credential where such data and details are claims made by the license issuer about the licensee which is the subject. A credential is called a verifiable credential when its authenticity can be cryptographically checked by anyone because the credential contains a cryptographic signature by the issuer, and the issuer's public key is well known. To convince a service provider, the verifier, of the authenticity of a verifiable credential, the credential along with the signature is shared with the service provider who can then verify the signature.
However, the above approach may be harmful to the user’s privacy. The entity in possession of the credential being the holder, has to share the credential in its entirety with the verifier, which may reveal a lot more information than what was needed. As an example, the holder may have wanted to share his name and date of birth from his license, not the rest of the credential data like his SSN, all while still being able to convince the verifier that he does own a verifiable credential containing his name and date of birth. This desired property is called selective disclosure where the holder can choose to disclose only parts of the credential, rather than the entire credential.
Another problem to solve is linkability. Say the holder is able to achieve selective disclosure where it only reveals part of the credential data to the verifier (the part that is sufficient for the verifier to provide service to the holder), the holder would not want the verifier to link his previous interactions with him. This desired property is called unlinkability. There are cases however when the holder is willing to be linked across several of his interactions but that should be voluntary and we do support that.
In some cases, even the above two properties might not be sufficient, for example when the holder wants to reveal part of the data -
- If a holder wants to prove he is over 18 or above 65 for availing a service, he doesn't need to show his date of birth; just being able to prove those conditions, called predicates is sufficient for the verifier.
- For proving that he/she is not a resident of a city/state, the holder should not need to share his exact address.
- For availing of social welfare, when the holder's salary is below a certain amount, the holder should not need to reveal his salary from his bank statement, but show an anonymous credential to convince the verifier of the same.
- Similarly, an investor should be able to convince a verifier that the total value of his assets is greater than some amount without revealing the actual values of his assets.
One of the solutions to solve the above problem is called anonymous credentials, sometimes called Attribute-Based Credentials (ABC). The main idea in anonymous credentials is that rather than considering the credential data as arbitrary bytes which are then signed by the issuer, anonymous credentials adds "structure" to the credential. Each claim in the credential is treated as an attribute and then these attributes are signed in a specific way by the issuer to create a signature. These attributes and the signature now form anonymous credentials. For example, if a credential has 3 claims made by the issuer about the holder including, name, city, and SSN, these 3 claims correspond to 3 attributes, one for each claim. Because of the "structure" in this credential, the holder can prove to a verifier, using zero-knowledge proof, that he has this credential from the issuer without revealing any of the attributes. To be precise, the holder can disclose these attributes exist and have been signed by an official issuer, without disclosing the attributes themselves or the signature.
"The holder can also reveal one or more attributes as part of the zero-knowledge proof from this anonymous credential, thus achieving selective disclosure."
“One or more zero-knowledge proofs created from the same anonymous credential cannot be linked together thus giving us unlinkability. "
To achieve both of these properties, we use a signature scheme called BBS+. A useful property of BBS+ (and similar) signatures is that when using several BBS+ signatures, attributes common to the signatures can be proven equal without revealing them. For example, if you have a driving license credential and passport, both with a BBS+ signature, you can prove that your SSN attribute is the same in both your driving license and passport without revealing your SSN. This property allows linking many credentials together and is helpful in cases when you need to prove that all credentials are about the same subject (or subject id).
Because credentials are essentially attestations made by an authority, i.e. the issuer, meaning they act as evidence or proof of something, it should be possible for the authority to permanently or temporarily renounce the attestations. These revoked credentials signal that the credential should not be accepted by any verifier. This kind of signaling requires the verifier to check the unique ID against a public list of all revoked credentials. However, revealing such a unique ID goes against our unlinkability requirement, as the unique ID of the credential ties all zero-knowledge proofs created from that credential. Therefore, we need a system in which the holder can prove that the membership of their credential ID is in a list in-zero knowledge:
- The verifier is assured the holder’s credential ID is not in the public list of all revoked credentials
- The verifier will not have access to the credential ID
- This system will ensure the holder is unlinkable.
For this zero-knowledge membership, we use an accumulator which is a set-like database but with a very small size and allows such zero-knowledge membership checks. Accumulators also enable other use cases where such a membership or non-membership check is required.
Say that you have a bank details credential that contains your account number as one of the attributes. To convince a regulator that your account is not in a "suspicious-accounts" list, you can prove to the regulator that your account is not in the accumulator which contains all suspicious accounts. We use a pairing-based accumulator called VB accumulator.
Some other common requirements with anonymous credentials is being able to prove conditions or predicates about the attributes like proving total income from a bank statement credential is less than the limit. Here the total income might not be a single attribute but composed of several attributes with one attribute per income source. Therefore, the attributes need to be added together before comparing to the limit amount, some cases may need more complex operations on one or more attributes.
These capabilities are not possible with BBS+ alone, and we therefore use zk-SNARK which can be used to prove arbitrary conditions on the attributes. The zk-SNARK we use is the same as used in Zcash but adapted to be linked with the BBS+ signature.
Anonymous credentials offer great privacy but too much privacy can sometimes be crippling for regulators or law enforcement. Consider an exchange as a verifier that allows customers to make transactions after they prove possession of a government ID, but does not ask for any other information. At any point, a regulator or law enforcement may want to know details of who made certain transactions, but the only information the verifier has and is able to provide, is that someone made the transactions with a government ID.
This isn’t sufficient for the regulator or law enforcement, traceability is needed here. However, not everyone in this chain needs traceability, certain authorities are the ones in need of it. We solve this problem by a technique called verifiable encryption.
Using the example above, a customer who possesses a government ID should be required to encrypt their SSN for the regulator without revealing the SSN itself. Authorities can decrypt the SSN and track who the government ID belongs to, while the customer is assured the exchange is not able to track them, only the authorities who need to trace them. Note that verifiable encryption is not mandatory with Dock credentials, it’s opt-in.
- The core cryptographic library that implements the signature schemes and proof protocols is implemented in Rust.
- The library contains different primitives in separate crates. The notable ones are BBS+ signatures, accumulators, and the composite proof system.
- The composite proof system allows us to combine these primitives like proving knowledge of 2 BBS+ signatures with proof of accumulator membership.
Since a large part of this code needs to run in a Javascript environment like a browser or a react native app, we built a WASM wrapper over the Rust code. The wrapper is quite thin and mostly does deserialization/serialization logic. It intentionally lacks abstractions like classes and is just some modules with free-floating functions. Finally, there is a Typescript library that uses the WASM wrapper and provides various abstractions like creating signatures, proofs, accumulators.
As verification of proofs and accumulator membership/non-membership requires publicly available authenticated storage, we deployed the ability to post BBS+ and accumulator public keys on our chain and also to create, update and remove accumulators. The changes have been live on the mainnet for some time and corresponding modules and tests are added in SDK for BBS+, accumulators, and composite proof by fetching objects from the chain.
For a more detailed explanation of the primitives, see the explainer in the GitHub repo. The usage with examples is described here. It is broken down into the following sections:
The composite proof subsection shows several examples like selective disclosure, creating proofs over multiple BBS+ signatures, BBS+ signature with accumulator membership, getting blind BBS+ signature, meaning even the issuer is not aware of certain attributes, and opt-in linkability where the holder chooses to be linkable across several proofs of his credential, but only at a certain verifier.
We are exploring options for predicates like range proofs, verifiable encryption, and support for arbitrary computation on credential data in general, but we have PoCs for showing that they work with BBS+ signatures.
This PoC shows the usage of BBS+ and LegoGroth16, which is an adaptation of Groth16, the zk-SNARK used in Zcash. The PoC shows how to:
- Prove that some attribute of a BBS+ signature satisfies some bound that can be used to prove that age is less than or greater than some number
- Prove that the sum of certain attributes satisfy a certain bound which can be used to prove that the sum of liabilities is less than some number.
- Prove that sum of certain attributes of 1 credential is less than the sum of certain attributes of another credential, which can be used to prove that the sum of liabilities is less than the sum of assets.
This PoC shows verifiable encryption based on SAVER. We show in this test, that a user can verifiably encrypt any of his attributes for a third party, and convince the third party that the encryption has been correctly done.
We have turned some of the above mentioned PoCs into production ready code, specifically verifiable encryption and range proof. Please check this blog post for more information. We have published the Rust code for verifiable encryption and corresponding Typescript wrapper. Look at this Readme section to learn more about these.