### zk age verification og source

a simplified system where alice can prove she was born before a certain date without revealing her actual birthdate – or phrased differently:

- prove some trusted third party signed a birth certificate
- and the signature is valid for the known public key
- and the birthdate on the certificate is on/before some requirement

### issuing a claim

some signing authority (the hospital, a government, etc) will need to sign a hashed representation of alice’s identity (`subject`

), attesting to her birthdate – so we’ll construct a standardized payload

```
// Constructs a payload for the issuer to sign, claiming that
// the subject was born on the given date.
fn construct_claim_payload(
subject: Field,
birthyear: Field,
birthmonth : Field,
birthday : Field,
) -> [u8; 32] {
let h1 = std::hash::pedersen([subject, birthyear, birthmonth, birthday])[0];
let h2 = h1.to_be_bytes(32);
let mut result = [0; 32];
for i in 0..32 {
result[i] = h2[i];
}
result
}
```

we use `std::hash::pedersen`

to convert alice’s data into a unique 32-byte hash, which is signed by the trusted third party, and stored off chain however alice chooses – this hash will be used as a hidden input in proofs that alice generates later

the primary advantage of pedersen is its simplicity and efficiency, making it an attractive option in circuits

a small test can make it easy to construct & output claim payloads (essentially what the signer would call in prod)

```
#[test]
fn test_construct_claim_payload() {
let want = [0x0b, 0xb7, 0xda, 0x0f, 0x91, 0x33, 0x82, 0x0c, 0xee, 0x8a, 0x9c, 0xe7, 0xda, 0x04, 0x1e, 0x22, 0x6a, 0x19, 0x12, 0xbe, 0xb5, 0x2b, 0x96, 0xa1, 0x41, 0x3e, 0xb7, 0x41, 0x15, 0xeb, 0x7a, 0xd7];
let got = construct_claim_payload(1234567890, 2003, 1, 2);
assert(want == got);
std::println(got);
}
```

### checking a claim & proving things about it

alice wants to generate a proof that her birthdate is before some date

that relationship can be defined through a set of assertions

```
// Applies basic constraints such that the subject's birthdate is
// never after the required birthdate.
fn check_claim(
required_birthyear: Field,
required_birthmonth: Field,
required_birthday: Field,
subject_birthyear: Field,
subject_birthmonth: Field,
subject_birthday: Field,
) {
assert(required_birthyear as u16 >= subject_birthyear as u16);
if (required_birthyear == subject_birthyear) {
assert(required_birthmonth as u8 >= subject_birthmonth as u8);
if (required_birthmonth == subject_birthmonth) {
assert(required_birthday as u8 >= subject_birthday as u8);
}
}
}
```

which is then the first constraint enforced in the circuit’s entrypoint

```
fn main(
//
// public inputs:
// the things that verifiers will provide as a challenge to alice
//
required_birthyear: pub Field,
required_birthmonth: pub Field,
required_birthday: pub Field,
issuer_public_key_x: pub [u8; 32],
issuer_public_key_y: pub [u8; 32],
subject: pub Field,
//
// private inputs:
// the knowledge that alice wants to keep secret, but still
// prove computation over (i.e. the challenge)
//
issuer_signature: [u8; 64],
subject_birthyear: Field,
subject_birthmonth: Field,
subject_birthday: Field,
) -> pub bool {
check_claim(
required_birthyear,
required_birthmonth,
required_birthday,
subject_birthyear,
subject_birthmonth,
subject_birthday,
);
let claim_payload = construct_claim_payload(
subject,
subject_birthyear,
subject_birthmonth,
subject_birthday,
);
let valid = std::ecdsa_secp256k1::verify_signature(
issuer_public_key_x,
issuer_public_key_y,
issuer_signature,
claim_payload,
);
assert(valid == true);
valid
}
```

with this, alice can prove she knows some signature, that covers some payload that has certain attributes

so its not *zero* knowledge, but it’s also not a ton knowledge

NOTE: at the time of writing, signing within noir tests isn’t well supported, so i just computed in go separately

```
#[test]
fn test_valid_signature() {
// Claim
let subject = 1234567890;
let subject_birthyear = 2003;
let subject_birthmonth = 1;
let subject_birthday = 2;
// Signature covering claim
// This public key corresponds to private key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 (1st one generated by anvil)
let issuer_public_key_x: [u8; 32] = [0x83,0x18,0x53,0x5B,0x54,0x10,0x5D,0x4A,0x7A,0xAE,0x60,0xC0,0x8F,0xC4,0x5F,0x96,0x87,0x18,0x1B,0x4F,0xDF,0xC6,0x25,0xBD,0x1A,0x75,0x3F,0xA7,0x39,0x7F,0xED,0x75];
let issuer_public_key_y: [u8; 32] = [0x35,0x47,0xF1,0x1C,0xA8,0x69,0x66,0x46,0xF2,0xF3,0xAC,0xB0,0x8E,0x31,0x01,0x6A,0xFA,0xC2,0x3E,0x63,0x0C,0x5D,0x11,0xF5,0x9F,0x61,0xFE,0xF5,0x7B,0x0D,0x2A,0xA5];
let issuer_signature: [u8; 64] = [0x88, 0xbb, 0x8d, 0x8e, 0x4d, 0x5f, 0x7e, 0x0a, 0x85, 0x3b, 0x5e, 0x4c, 0xda, 0xf3, 0x92, 0x24, 0x4d, 0x46, 0xf2, 0x2a, 0xdc, 0x0f, 0x4c, 0x28, 0x52, 0x7a, 0x28, 0xac, 0xf0, 0xa6, 0x2f, 0x3b, 0x1e, 0xf9, 0xfe, 0xbd, 0x3a, 0xde, 0xea, 0xed, 0x27, 0x6a, 0x32, 0x87, 0xe5, 0xdb, 0xf4, 0x32, 0x7a, 0x9c, 0x20, 0xce, 0xed, 0x40, 0x3e, 0xdb, 0xa5, 0x8d, 0xbd, 0xef, 0x01, 0xe6, 0x9b, 0xc6];
let is_valid = main(
2003, // required_birthyear
1, // required_birthmonth
3, // required_birthday
issuer_public_key_x,
issuer_public_key_y,
subject,
issuer_signature,
subject_birthyear,
subject_birthmonth,
subject_birthday,
);
assert(is_valid == true);
}
```

*try it out*

install the noir toolchain if you haven't yet:
curl -L https://noirup.org/install | bash noirup --nightlythen checkout and run the example:

git clone https://github.com/sambarnes/noir-by-example.git cd noir-by-example/circuits/gadgets/zk-age-verification nargo test --show-output

...