# Private identity verification with ZK proof
Zero-knowledge proofs (ZKPs) are a type of cryptographic protocol that enables one party to prove to another party that a statement is true, without revealing any additional information about the statement beyond its truth. ZKPs have many potential applications, including in the fields of identity verification, voting systems, and financial transactions.
One use case for ZKPs is private identity verification. Imagine a scenario where a person wants to prove that they are over the age of 18 without revealing their exact date of birth. With ZKPs, this is possible.
To create a ZKP for this use case, we can represent the statement "I am over 18 years old" as a mathematical equation, and use a ZKP system like zk-SNARKs or zk-STARKs to generate a proof that this equation is true. The proof can then be provided to a verifier, who can confirm that the statement is true without learning the exact date of birth of the person making the claim.
Let's take a closer look at how this works. Suppose Alice wants to prove that she is over the age of 18 to Bob, without revealing her exact date of birth. Alice can use a ZKP system to generate a proof that she is over 18, without revealing any additional information about her age.
To generate a zero-knowledge proof that you are above 18 years old, you would need a proof system, a statement to prove, a program, inputs to the program, a prover and a verifier.
Lets see what those are and how they play together.
## A proof system
You would need to select a proof system that supports zero-knowledge proofs, such as zk-SNARKs or zk-STARKs. There are several libraries available that support zero-knowledge proofs, such as libsnark, bellman, and circom.
## A statement to prove
In this case, the statement would be "I am above 18 years old." This statement needs to be expressed in a way that can be verified by the proof system.
## A program
You would need to create a program that represents the statement you want to prove. This program would be used to generate the zero-knowledge proof. The program needs to be expressed in a format that is compatible with the proof system you have selected.
In our example a program in the language of a zero-knowledge proof library that could represent the proof that you are above 18 years old would be something like:
```javascript=
function age_proof(birth_date, current_date) {
let age = current_date.year - birth_date.year;
if (current_date.month < birth_date.month || (current_date.month == birth_date.month && current_date.day < birth_date.day)) {
age--;
}
return age;
}
function statement_proof(birth_date, current_date) {
let age = age_proof(birth_date, current_date);
return age > 18;
}
```
This program should be compiled into a circuit or program that is compatible with a zero-knowledge proof library. The inputs, birth date and current date, could be provided to the prover to generate the zero-knowledge proof. The verifier would then verify the proof without revealing any additional information beyond what is required to verify the statement that the user is above 18 years old.
### Compiling a program (zk-STARKs)
Compiling a program into a format that is compatible with the zk-STARKs proof system is a complex process that typically involves several steps. Here's a high-level overview of the process:
1. **Choose a programming language:**
The first step is to choose a programming language that is compatible with the zk-STARKs proof system being used. Some popular languages include JavaScript, Python, and Rust.
2. **Write the program:**
Once a language has been chosen, the next step is to write the program that will be compiled into a format that is compatible with the zk-STARKs proof system.
3. **Convert the program into arithmetic circuits:**
The program must be converted into a series of arithmetic circuits, which are essentially mathematical representations of the program's logic. This is typically done using a compiler or transpiler, which converts the program's code into a series of equations that can be solved by a mathematical solver.
4. **Optimize the arithmetic circuits:**
The circuits generated in the previous step are typically large and complex, and may require optimization to reduce their size and complexity. This can be done using various techniques, such as multi-linear mapping, low-degree testing, and polynomial interpolation.
5. **Generate the universal and personal setup:**
Before the program can be used to generate proofs, a universal setup and a personal setup must be generated. The universal setup is a set of parameters that are used to generate proofs for any instance of the program, while the personal setup is specific to the inputs of the instance being proven.
3. **Generate the proof:**
Once the setup has been generated, the program can be used to generate a proof using the zk-STARKs proof system.
Note that this is just a high-level overview of the process, and the actual implementation may be more complex depending on the specific zk-STARKs proof system being used.
## Inputs
You would need to provide inputs to the circuit or program that represent your age or other information required to prove the statement. In this case, you would need to provide your birth date or some other proof of age.
## A prover
You would need a prover that can generate the zero-knowledge proof. The prover takes the inputs and runs them through the circuit or program to generate the proof.
A prover in the context of proving that you are above 18 years old using the program I provided earlier would be something like:
``` javascript=
// Assume birth_date and current_date are already defined
// Step 1: Compile the program into a circuit or program that is compatible with the proof system
let compiled_program = compile_program(statement_proof);
// Step 2: Generate a witness for the inputs
let witness = {
"birth_date": birth_date,
"current_date": current_date
};
// Step 3: Generate a proof using the witness and the compiled program
let proof = generate_proof(compiled_program, witness);
// Step 4: Send the proof to the verifier for verification
send_proof_to_verifier(proof);
```
In this example, we assume that `birth_date` and `current_date` have already been defined with valid values. We compile the statement_proof function into a program that is compatible with the proof system we are using. Then we generate a witness for the inputs by creating a JSON object that maps the input variable names to their values.
Next, we generate a proof using the witness and the compiled program. This generates a zero-knowledge proof that the statement is true without revealing any additional information beyond what is required to verify the statement.
Finally, we send the proof to the verifier for verification. The verifier checks the proof and returns either true or false to indicate whether the proof is valid or not. If the proof is valid, then we can conclude that the user is above 18 years old without revealing any additional information about their age or birth date.
### Generate proof implementation
The implementation of the `generate_proof` function can vary depending on the specific zero-knowledge proof system being used.
#### zk-SNARKs proof system
```javascript=
function generate_proof(compiled_program, witness) {
// Step 1: Generate a proving key from the compiled program
let proving_key = generate_proving_key(compiled_program);
// Step 2: Generate a proof using the proving key and the witness
let proof = generate_snark_proof(compiled_program, proving_key, witness);
// Step 3: Return the proof
return proof;
}
```
In this example, we first generate a proving key from the compiled program using the `generate_proving_key` function. The proving key is used to generate the proof, and is a key component of the zk-SNARKs proof system.
Next, we use the `generate_snark_proof` function to generate the proof itself, using the compiled program, the proving key, and the witness.
Finally, we return the proof, which can be sent to the verifier for verification.
Note that this is just an example implementation, and the actual implementation will depend on the specific zero-knowledge proof system being used.
#### zk-STARKs proof system
```javascript=
function generate_proof(compiled_program, witness) {
// Step 1: Generate a hash of the compiled program
let program_hash = hash(compiled_program);
// Step 2: Generate a proof using the program hash and the witness
let proof = generate_stark_proof(program_hash, witness);
// Step 3: Return the proof
return proof;
}
```
In this example, we assume that the program has already been compiled into a format that is compatible with the zk-STARKs proof system. We generate a hash of the compiled program using a cryptographic hash function.
Next, we use the `generate_stark_proof` function to generate the proof, using the program hash and the witness as inputs. The proof generated by the zk-STARKs proof system is typically much larger than the proof generated by zk-SNARKs, but it does not require a trusted setup.
Finally, we return the proof, which can be sent to the verifier for verification.
Note that this is just an example implementation, and the actual implementation will depend on the specific zero-knowledge proof system being used.
:::info
Notice the difference between zk-SNARKs and zk-STARKs in the parameters for the proof generation function.
:::
## A verifier
You would need a verifier that can verify the zero-knowledge proof. The verifier takes the proof and the circuit or program and verifies that the proof is valid without revealing any additional information beyond what is required to verify the statement.
Here's an example of a verifier in the context of verifying a proof that someone is above 18 years old using the program and proof generated earlier:
```javascript=
// Step 1: Compile the program into a circuit or program that is compatible with the proof system
let compiled_program = compile_program(statement_proof);
// Step 2: Receive the proof from the prover
let proof = receive_proof_from_prover();
// Step 3: Verify the proof
let is_valid = verify_proof(compiled_program, proof);
// Step 4: Return the result of the verification to the prover
return is_valid;
```
In this example, we assume that the compiled program has already been generated using the `statement_proof` function, and that the proof has been received from the prover. We then use the `verify_proof` function to check the proof's validity using the compiled program and the proof.
If the proof is valid, the verifier returns true to indicate that the statement is true without revealing any additional information beyond what is required to verify the statement. If the proof is invalid, the verifier returns false, indicating that the statement is false, or that the proof is invalid.
The verifier does not learn any additional information beyond what is required to verify the statement, which in this case is only whether the user is above 18 years old.
## Summary
This process demonstrates how ZKPs can be used to verify a person's age without revealing their exact date of birth. By converting a statement into a mathematical equation and generating a proof of its truth without revealing any additional information, ZKPs enable privacy-preserving identity verification.
ZKPs have the potential to revolutionize many aspects of our lives, from identity verification to financial transactions. By providing a way to prove a statement's truth without revealing additional information, ZKPs can enable secure and private interactions between individuals and organizations. The use case of private identity verification is just one example of how ZKPs can be used to protect privacy while still enabling trusted interactions.