1use ark_bn254::Fr;
6use ark_ec::{AffineRepr, CurveGroup};
7use ark_ff::{PrimeField, Zero};
8use eddsa_babyjubjub::EdDSAPublicKey;
9use taceo_oprf::core::{dlog_equality::DLogEqualityProof, oprf::BlindingFactor};
10use world_id_primitives::{
11 FieldElement,
12 authenticator::{AuthenticatorPublicKeySet, MAX_AUTHENTICATOR_KEYS},
13 circuit_inputs::{NullifierProofCircuitInput, QueryProofCircuitInput},
14 merkle::MerkleInclusionProof,
15};
16
17type BaseField = ark_babyjubjub::Fq;
18type Affine = ark_babyjubjub::EdwardsAffine;
19
20#[derive(Debug, thiserror::Error)]
21pub enum ProofInputError {
23 #[error("The specified Merkle tree depth is invalid (expected: {expected}, got: {is}).")]
25 InvalidMerkleTreeDepth {
26 expected: usize,
28 is: BaseField,
30 },
31 #[error("The set of authenticator public keys is invalid.")]
33 InvalidAuthenticatorPublicKeySet,
34 #[error("The provided Merkle tree inclusion proof is invalid.")]
36 InvalidMerkleTreeInclusionProof,
37 #[error("The signature over the nonce and RP ID is invalid.")]
39 InvalidQuerySignature,
40 #[error("The provided blinding factor is invalid.")]
42 InvalidBlindingFactor,
43 #[error(
45 "The provided credential has expired (expires_at: {expires_at}, check_timestamp: {current_timestamp})."
46 )]
47 CredentialExpired {
48 current_timestamp: u64,
50 expires_at: u64,
52 },
53 #[error(
55 "The provided credential has a genesis issued at date that is too old (genesis_issued_at: {genesis_issued_at}, check_timestamp: {genesis_issued_at_min})."
56 )]
57 CredentialGenesisExpired {
58 genesis_issued_at_min: u64,
60 genesis_issued_at: u64,
62 },
63 #[error("The value '{name}' is out of bounds (got: {is}, limit: {limit}).")]
65 ValueOutOfBounds {
66 name: &'static str,
68 is: BaseField,
70 limit: BaseField,
72 },
73 #[error("The credential signature is invalid for the given issuer public key.")]
75 InvalidCredentialSignature,
76 #[error(
78 "The provided point '{name}' is not a valid point in the prime-order subgroup of the BabyJubJub curve."
79 )]
80 InvalidBabyJubJubPoint {
81 name: &'static str,
83 },
84 #[error("The provided OPRF DlogEquality proof is invalid.")]
86 InvalidOprfProof,
87 #[error("The provided unblinded OPRF response point is invalid.")]
89 InvalidOprfResponse,
90 #[error(
92 "The provided session ID commitment is invalid for the given id and session id randomness."
93 )]
94 InvalidSessionId,
95}
96
97pub fn check_query_input_validity<const TREE_DEPTH: usize>(
105 inputs: &QueryProofCircuitInput<TREE_DEPTH>,
106) -> Result<Affine, ProofInputError> {
107 if inputs.depth != BaseField::new((TREE_DEPTH as u64).into()) {
109 return Err(ProofInputError::InvalidMerkleTreeDepth {
110 expected: TREE_DEPTH,
111 is: inputs.depth,
112 });
113 }
114 let idx_u64 = u64::try_from(FieldElement::from(inputs.mt_index)).map_err(|_| {
117 ProofInputError::ValueOutOfBounds {
118 name: "Merkle tree index",
119 is: inputs.mt_index,
120 limit: BaseField::new((1u64 << TREE_DEPTH).into()),
121 }
122 })?;
123 if idx_u64 >= (1u64 << TREE_DEPTH) {
124 return Err(ProofInputError::ValueOutOfBounds {
125 name: "Merkle tree index",
126 is: inputs.mt_index,
127 limit: BaseField::new((1u64 << TREE_DEPTH).into()),
128 });
129 }
130
131 let pk_set = AuthenticatorPublicKeySet::new(
133 inputs
134 .pk
135 .iter()
136 .map(|&x| EdDSAPublicKey { pk: x })
137 .collect(),
138 )
139 .map_err(|_| ProofInputError::InvalidAuthenticatorPublicKeySet)?;
140 let pk_set_hash = pk_set.leaf_hash();
141 let merkle_tree_inclusion_proof = MerkleInclusionProof::new(
142 FieldElement::from(inputs.merkle_root),
143 idx_u64,
144 inputs.siblings.map(FieldElement::from),
145 );
146 if !merkle_tree_inclusion_proof.is_valid(FieldElement::from(pk_set_hash)) {
147 return Err(ProofInputError::InvalidMerkleTreeInclusionProof);
148 }
149
150 let pk_index_usize = usize::try_from(FieldElement::from(inputs.pk_index)).map_err(|_| {
152 ProofInputError::ValueOutOfBounds {
153 name: "Authenticator PubKey index",
154 is: inputs.pk_index,
155 limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
156 }
157 })?;
158 let pk = pk_set
159 .get(pk_index_usize)
160 .ok_or_else(|| ProofInputError::ValueOutOfBounds {
161 name: "Authenticator PubKey index",
162 is: inputs.pk_index,
163 limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
164 })?;
165
166 if !inputs.r.is_on_curve() || !inputs.r.is_in_correct_subgroup_assuming_on_curve() {
167 return Err(ProofInputError::InvalidBabyJubJubPoint {
168 name: "Query Signature R",
169 });
170 }
171 if !pk.pk.is_on_curve() || !pk.pk.is_in_correct_subgroup_assuming_on_curve() {
172 return Err(ProofInputError::InvalidBabyJubJubPoint {
173 name: "Authenticator Public Key",
174 });
175 }
176
177 let _rp_id_u64 = u64::try_from(FieldElement::from(inputs.rp_id)).map_err(|_| {
178 ProofInputError::ValueOutOfBounds {
179 name: "RP Id",
180 is: inputs.pk_index,
181 limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
182 }
183 })?;
184 let query = world_id_primitives::authenticator::oprf_query_digest(
185 idx_u64,
186 FieldElement::from(inputs.action),
187 FieldElement::from(inputs.rp_id),
188 );
189 let signature = eddsa_babyjubjub::EdDSASignature {
190 r: inputs.r,
191 s: inputs.s,
192 };
193
194 if !pk.verify(*query, &signature) {
195 return Err(ProofInputError::InvalidQuerySignature);
196 }
197
198 let blinding_factor = BlindingFactor::from_scalar(inputs.beta)
199 .map_err(|_| ProofInputError::InvalidBlindingFactor)?;
200 let query_point = taceo_oprf::core::oprf::client::blind_query(*query, blinding_factor);
201
202 Ok(query_point.blinded_query())
203}
204
205#[expect(
213 clippy::too_many_lines,
214 reason = "necessary checks for input validity should be in one function"
215)]
216pub fn check_nullifier_input_validity<const TREE_DEPTH: usize>(
217 inputs: &NullifierProofCircuitInput<TREE_DEPTH>,
218) -> Result<FieldElement, ProofInputError> {
219 let blinded_query = check_query_input_validity(&inputs.query_input)?;
221
222 let current_timestamp_u64 = u64::try_from(FieldElement::from(inputs.current_timestamp))
225 .map_err(|_| ProofInputError::ValueOutOfBounds {
226 name: "current timestamp",
227 is: inputs.current_timestamp,
228 limit: BaseField::new(u64::MAX.into()),
229 })?;
230 let credential_expires_at_u64 = u64::try_from(FieldElement::from(inputs.cred_expires_at))
231 .map_err(|_| ProofInputError::ValueOutOfBounds {
232 name: "credential expiry timestamp",
233 is: inputs.current_timestamp,
234 limit: BaseField::new(u64::MAX.into()),
235 })?;
236 if credential_expires_at_u64 <= current_timestamp_u64 {
238 return Err(ProofInputError::CredentialExpired {
239 current_timestamp: current_timestamp_u64,
240 expires_at: credential_expires_at_u64,
241 });
242 }
243 let genesis_issued_at_u64 = u64::try_from(FieldElement::from(inputs.cred_genesis_issued_at))
245 .map_err(|_| ProofInputError::ValueOutOfBounds {
246 name: "credential genesis issued at",
247 is: inputs.cred_genesis_issued_at,
248 limit: BaseField::new(u64::MAX.into()),
249 })?;
250 let genesis_issued_at_min_u64 =
251 u64::try_from(FieldElement::from(inputs.cred_genesis_issued_at_min)).map_err(|_| {
252 ProofInputError::ValueOutOfBounds {
253 name: "credential genesis issued at minimum bound",
254 is: inputs.cred_genesis_issued_at_min,
255 limit: BaseField::new(u64::MAX.into()),
256 }
257 })?;
258 if genesis_issued_at_min_u64 > genesis_issued_at_u64 {
259 return Err(ProofInputError::CredentialGenesisExpired {
260 genesis_issued_at_min: genesis_issued_at_min_u64,
261 genesis_issued_at: genesis_issued_at_u64,
262 });
263 }
264
265 let blinded_subject = sub(
266 FieldElement::from(inputs.query_input.mt_index),
267 FieldElement::from(inputs.cred_sub_blinding_factor),
268 );
269
270 let cred_hash = hash_credential(
271 FieldElement::from(inputs.issuer_schema_id),
272 blinded_subject,
273 FieldElement::from(inputs.cred_genesis_issued_at),
274 FieldElement::from(inputs.cred_expires_at),
275 FieldElement::from(inputs.cred_hashes[0]),
276 FieldElement::from(inputs.cred_hashes[1]),
277 FieldElement::from(inputs.cred_id),
278 );
279 let pk = EdDSAPublicKey { pk: inputs.cred_pk };
280
281 let signature = eddsa_babyjubjub::EdDSASignature {
282 r: inputs.cred_r,
283 s: inputs.cred_s,
284 };
285
286 if !inputs.cred_r.is_on_curve() || !inputs.cred_r.is_in_correct_subgroup_assuming_on_curve() {
287 return Err(ProofInputError::InvalidBabyJubJubPoint {
288 name: "Credential Signature R",
289 });
290 }
291 if !pk.pk.is_on_curve() || !pk.pk.is_in_correct_subgroup_assuming_on_curve() {
292 return Err(ProofInputError::InvalidBabyJubJubPoint {
293 name: "Credential Public Key",
294 });
295 }
296
297 if !pk.verify(*cred_hash, &signature) {
298 return Err(ProofInputError::InvalidCredentialSignature);
299 }
300
301 if !inputs.oprf_pk.is_on_curve() || !inputs.oprf_pk.is_in_correct_subgroup_assuming_on_curve() {
303 return Err(ProofInputError::InvalidBabyJubJubPoint {
304 name: "OPRF Public Key",
305 });
306 }
307 if !inputs.oprf_response_blinded.is_on_curve()
308 || !inputs
309 .oprf_response_blinded
310 .is_in_correct_subgroup_assuming_on_curve()
311 {
312 return Err(ProofInputError::InvalidBabyJubJubPoint {
313 name: "OPRF Blinded Response",
314 });
315 }
316
317 let dlog_proof = DLogEqualityProof {
319 e: inputs.dlog_e,
320 s: inputs.dlog_s,
321 };
322 dlog_proof
323 .verify(
324 inputs.oprf_pk,
325 blinded_query,
326 inputs.oprf_response_blinded,
327 Affine::generator(),
328 )
329 .map_err(|_| ProofInputError::InvalidOprfProof)?;
330
331 if !inputs.oprf_response.is_on_curve()
333 || !inputs
334 .oprf_response
335 .is_in_correct_subgroup_assuming_on_curve()
336 {
337 return Err(ProofInputError::InvalidBabyJubJubPoint {
338 name: "OPRF Unblinded Response",
339 });
340 }
341 let expected_blinded_response = (inputs.oprf_response * inputs.query_input.beta).into_affine();
342 if expected_blinded_response != inputs.oprf_response_blinded {
343 return Err(ProofInputError::InvalidOprfResponse);
344 }
345
346 if !inputs.id_commitment.is_zero() {
348 let expected_commitment = session_id_commitment(
349 FieldElement::from(inputs.query_input.mt_index),
350 FieldElement::from(inputs.id_commitment_r),
351 );
352 if expected_commitment != FieldElement::from(inputs.id_commitment) {
353 return Err(ProofInputError::InvalidSessionId);
354 }
355 }
356
357 let nullfier = oprf_finalize_hash(
359 *world_id_primitives::authenticator::oprf_query_digest(
360 #[expect(
361 clippy::missing_panics_doc,
362 reason = "checked in check_query_input_validity"
363 )]
364 u64::try_from(FieldElement::from(inputs.query_input.mt_index)).unwrap(),
365 FieldElement::from(inputs.query_input.action),
366 FieldElement::from(inputs.query_input.rp_id),
367 ),
368 inputs.oprf_response,
369 );
370
371 Ok(nullfier)
372}
373
374fn sub(leaf_index: FieldElement, blinding_factor: FieldElement) -> FieldElement {
378 let sub_ds = Fr::from_be_bytes_mod_order(b"H_CS(id, r)");
379 let mut input = [sub_ds, *leaf_index, *blinding_factor];
380 poseidon2::bn254::t3::permutation_in_place(&mut input);
381 input[1].into()
382}
383fn oprf_finalize_hash(query: BaseField, oprf_response: Affine) -> FieldElement {
385 let finalize_ds = Fr::from_be_bytes_mod_order(super::OPRF_PROOF_DS);
386 let mut input = [finalize_ds, query, oprf_response.x, oprf_response.y];
387 poseidon2::bn254::t4::permutation_in_place(&mut input);
388 input[1].into()
389}
390
391fn session_id_commitment(user_id: FieldElement, commitment_rand: FieldElement) -> FieldElement {
393 let sub_ds = Fr::from_be_bytes_mod_order(b"H(id, r)");
394 let mut input = [sub_ds, *user_id, *commitment_rand];
395 poseidon2::bn254::t3::permutation_in_place(&mut input);
396 input[1].into()
397}
398
399fn hash_credential(
401 issuer_schema_id: FieldElement,
402 sub: FieldElement,
403 genesis_issued_at: FieldElement,
404 expires_at: FieldElement,
405 claims_hash: FieldElement,
406 associated_data_hash: FieldElement,
407 id: FieldElement,
408) -> FieldElement {
409 let cred_ds = Fr::from_be_bytes_mod_order(b"POSEIDON2+EDDSA-BJJ");
410 let mut input = [
411 cred_ds,
412 *issuer_schema_id,
413 *sub,
414 *genesis_issued_at,
415 *expires_at,
416 *claims_hash,
417 *associated_data_hash,
418 *id,
419 ];
420 poseidon2::bn254::t8::permutation_in_place(&mut input);
421 input[1].into()
422}
423
424#[cfg(test)]
425mod tests {
426 use ark_ec::twisted_edwards::Affine;
427 use std::str::FromStr;
428 use world_id_primitives::circuit_inputs::{NullifierProofCircuitInput, QueryProofCircuitInput};
429
430 use crate::proof::errors::{check_nullifier_input_validity, check_query_input_validity};
431
432 fn get_valid_query_proof_input() -> QueryProofCircuitInput<30> {
434 QueryProofCircuitInput {
435 pk: [Affine {
436 x: ark_babyjubjub::Fq::from_str(
437 "19037598474602150174935475944965340829216795940473064039209388058233204431288",
438 ).unwrap(),
439 y: ark_babyjubjub::Fq::from_str(
440 "3549932221586364715003722955756497910920276078443163728621283280434115857197",
441 ).unwrap(),
442 },
443 Affine::zero(),
444 Affine::zero(),
445 Affine::zero(),
446 Affine::zero(),
447 Affine::zero(),
448 Affine::zero(),
449 ],
450 pk_index: ark_bn254::Fr::from(0u64),
451 s: ark_babyjubjub::Fr::from_str(
452 "2692248185200295468055279425612708965310378163906753799023551825366269352327",
453 ).unwrap(),
454 r: Affine {
455 x: ark_babyjubjub::Fq::from_str(
456 "14689596469778385278298478829656243946283084496217945909620117398922933730711",
457 ).unwrap(),
458 y: ark_babyjubjub::Fq::from_str(
459 "4424830738973486800075394160997493242162871494907432163152597205147606706197",
460 ).unwrap(),
461 },
462 merkle_root: ark_bn254::Fr::from_str("4959814736111706042728533661656003495359474679272202023690954858781105690707").unwrap(),
463 depth: ark_babyjubjub::Fq::from(30u64),
464 mt_index: ark_bn254::Fr::from(1u64),
465 siblings: [
466 ark_bn254::Fr::from_str("0").unwrap(),
467 ark_bn254::Fr::from_str("15621590199821056450610068202457788725601603091791048810523422053872049975191").unwrap(),
468 ark_bn254::Fr::from_str("15180302612178352054084191513289999058431498575847349863917170755410077436260").unwrap(),
469 ark_bn254::Fr::from_str("20846426933296943402289409165716903143674406371782261099735847433924593192150").unwrap(),
470 ark_bn254::Fr::from_str("19570709311100149041770094415303300085749902031216638721752284824736726831172").unwrap(),
471 ark_bn254::Fr::from_str("11737142173000203701607979434185548337265641794352013537668027209469132654026").unwrap(),
472 ark_bn254::Fr::from_str("11865865012735342650993929214218361747705569437250152833912362711743119784159").unwrap(),
473 ark_bn254::Fr::from_str("1493463551715988755902230605042557878234810673525086316376178495918903796315").unwrap(),
474 ark_bn254::Fr::from_str("18746103596419850001763894956142528089435746267438407061601783590659355049966").unwrap(),
475 ark_bn254::Fr::from_str("21234194473503024590374857258930930634542887619436018385581872843343250130100").unwrap(),
476 ark_bn254::Fr::from_str("14681119568252857310414189897145410009875739166689283501408363922419813627484").unwrap(),
477 ark_bn254::Fr::from_str("13243470632183094581890559006623686685113540193867211988709619438324105679244").unwrap(),
478 ark_bn254::Fr::from_str("19463898140191333844443019106944343282402694318119383727674782613189581590092").unwrap(),
479 ark_bn254::Fr::from_str("10565902370220049529800497209344287504121041033501189980624875736992201671117").unwrap(),
480 ark_bn254::Fr::from_str("5560307625408070902174028041423028597194394554482880015024167821933869023078").unwrap(),
481 ark_bn254::Fr::from_str("20576730574720116265513866548855226316241518026808984067485384181494744706390").unwrap(),
482 ark_bn254::Fr::from_str("11166760821615661136366651998133963805984915741187325490784169611245269155689").unwrap(),
483 ark_bn254::Fr::from_str("13692603500396323648417392244466291089928913430742736835590182936663435788822").unwrap(),
484 ark_bn254::Fr::from_str("11129674755567463025028188404867541558752927519269975708924528737249823830641").unwrap(),
485 ark_bn254::Fr::from_str("6673535049007525806710184801639542254440636510496168661971704157154828514023").unwrap(),
486 ark_bn254::Fr::from_str("7958154589163466663626421142270206662020519181323839780192984613274682930816").unwrap(),
487 ark_bn254::Fr::from_str("3739156991379607404516753076057250171966250101655747790592556040569841550790").unwrap(),
488 ark_bn254::Fr::from_str("1334107297020502384420211493664486465203492095766400031330900935069700302301").unwrap(),
489 ark_bn254::Fr::from_str("20357028769054354174264046872903423695314313082869184437966002491602414517674").unwrap(),
490 ark_bn254::Fr::from_str("19392290367394672558538719012722289280213395590510602524366987685302929990731").unwrap(),
491 ark_bn254::Fr::from_str("7360502715619830055199267117332475946442427205382059394111067387016428818088").unwrap(),
492 ark_bn254::Fr::from_str("9629177338475347225553791169746168712988898028547587350296027054067573957412").unwrap(),
493 ark_bn254::Fr::from_str("21877160135037839571797468541807904053886800340144060811298025652177410263004").unwrap(),
494 ark_bn254::Fr::from_str("7105691694342706282901391345307729036900705570482804586768449537652208350743").unwrap(),
495 ark_bn254::Fr::from_str("15888057581779748293164452094398990053773731478520540058125130669204703869637").unwrap(),
496 ],
497 beta: ark_babyjubjub::Fr::from_str("1277277022932719396321614946989807194659268059729440522321681213750340643042").unwrap(),
498 rp_id: ark_bn254::Fr::from_str("14631649082411674499").unwrap(),
499 action: ark_bn254::Fr::from_str("8982441576518976929447725179565370305223105654688049122733783421407497941726").unwrap(),
500 nonce: ark_bn254::Fr::from_str("8530676162050357218814694371816107906694725175836943927290214963954696613748").unwrap(),
501 }
502 }
503
504 #[test]
505 fn test_valid_query_proof_input() {
506 let inputs = get_valid_query_proof_input();
507 let _ = check_query_input_validity(&inputs).unwrap();
508 }
509
510 #[test]
511 fn test_invalid_query_proof_input() {
512 let inputs = get_valid_query_proof_input();
513 {
514 let mut inputs = inputs.clone();
515 inputs.depth = ark_babyjubjub::Fq::from(29u64); assert!(matches!(
517 check_query_input_validity(&inputs).unwrap_err(),
518 super::ProofInputError::InvalidMerkleTreeDepth { .. }
519 ));
520 }
521 {
522 let mut inputs = inputs.clone();
523 inputs.mt_index = ark_bn254::Fr::from(1073741824u64);
525 assert!(matches!(
526 check_query_input_validity(&inputs).unwrap_err(),
527 super::ProofInputError::ValueOutOfBounds {
528 name: "Merkle tree index",
529 ..
530 }
531 ));
532 }
533 {
534 let mut inputs = inputs.clone();
535 inputs.merkle_root = ark_bn254::Fr::from(12345u64);
536 assert!(matches!(
537 check_query_input_validity(&inputs).unwrap_err(),
538 super::ProofInputError::InvalidMerkleTreeInclusionProof
539 ));
540 }
541 {
542 let mut inputs = inputs.clone();
543 inputs.pk_index = ark_bn254::Fr::from(7u64); assert!(matches!(
545 check_query_input_validity(&inputs).unwrap_err(),
546 super::ProofInputError::ValueOutOfBounds {
547 name: "Authenticator PubKey index",
548 ..
549 }
550 ));
551 }
552 {
553 let mut inputs = inputs.clone();
554 inputs.r = Affine {
555 x: ark_babyjubjub::Fq::from(1u64),
556 y: ark_babyjubjub::Fq::from(2u64),
557 };
558 assert!(matches!(
559 check_query_input_validity(&inputs).unwrap_err(),
560 super::ProofInputError::InvalidBabyJubJubPoint {
561 name: "Query Signature R"
562 }
563 ));
564 }
565 {
566 let mut inputs = inputs.clone();
567 inputs.pk[0] = Affine {
568 x: ark_babyjubjub::Fq::from(1u64),
569 y: ark_babyjubjub::Fq::from(2u64),
570 };
571
572 let pk_set = world_id_primitives::authenticator::AuthenticatorPublicKeySet::new(
574 inputs
575 .pk
576 .iter()
577 .map(|&x| eddsa_babyjubjub::EdDSAPublicKey { pk: x })
578 .collect(),
579 )
580 .unwrap();
581 let mut current = pk_set.leaf_hash();
582 let idx =
583 u64::try_from(world_id_primitives::FieldElement::from(inputs.mt_index)).unwrap();
584 for (i, sibling) in inputs.siblings.iter().enumerate() {
585 let sibling_fr = *world_id_primitives::FieldElement::from(*sibling);
586 if (idx >> i) & 1 == 0 {
587 let mut state = poseidon2::bn254::t2::permutation(&[current, sibling_fr]);
588 state[0] += current;
589 current = state[0];
590 } else {
591 let mut state = poseidon2::bn254::t2::permutation(&[sibling_fr, current]);
592 state[0] += sibling_fr;
593 current = state[0];
594 }
595 }
596 inputs.merkle_root = current;
597
598 assert!(matches!(
599 check_query_input_validity(&inputs).unwrap_err(),
600 super::ProofInputError::InvalidBabyJubJubPoint {
601 name: "Authenticator Public Key"
602 }
603 ));
604 }
605 {
606 let mut inputs = inputs.clone();
607 inputs.action = ark_bn254::Fr::from(12345u64);
608 assert!(matches!(
609 check_query_input_validity(&inputs).unwrap_err(),
610 super::ProofInputError::InvalidQuerySignature
611 ));
612 }
613 }
614
615 fn get_valid_nullifier_proof_input() -> NullifierProofCircuitInput<30> {
616 NullifierProofCircuitInput {
617 query_input: get_valid_query_proof_input(),
618 issuer_schema_id: ark_bn254::Fr::from(1u64),
619 cred_pk: Affine {
620 x: ark_babyjubjub::Fq::from_str(
621 "15406775215557320288232407896017344573719706795510112309920214099347968981892",
622 )
623 .unwrap(),
624 y: ark_babyjubjub::Fq::from_str(
625 "486388649729314270871358770861421181497883381447163109744630700259216042819",
626 )
627 .unwrap(),
628 },
629 cred_hashes: [
630 ark_bn254::Fr::from_str(
631 "14272087287699568472569351444185311392108883722570788958733484799744115401870",
632 )
633 .unwrap(),
634 ark_bn254::Fr::from_str("0").unwrap(),
635 ],
636 cred_genesis_issued_at: ark_bn254::Fr::from(1770125923u64),
637 cred_expires_at: ark_bn254::Fr::from(1770125983u64),
638 cred_s: ark_babyjubjub::Fr::from_str(
639 "1213918488111680600555111454085490191981091366153388773926786471247948539005",
640 )
641 .unwrap(),
642 cred_r: Affine {
643 x: ark_babyjubjub::Fq::from_str(
644 "15844586803954862856390946258558419582000810449135704981677693963391564067969",
645 )
646 .unwrap(),
647 y: ark_babyjubjub::Fq::from_str(
648 "592710378120172403096018676235519447487818389124797234601458948988041235710",
649 )
650 .unwrap(),
651 },
652 current_timestamp: ark_bn254::Fr::from(1770125908u64),
653 cred_genesis_issued_at_min: ark_bn254::Fr::from(0u64),
654 cred_sub_blinding_factor: ark_bn254::Fr::from_str(
655 "12170146734368267085913078854954627576787934009906407554611507307540342380837",
656 )
657 .unwrap(),
658 cred_id: ark_bn254::Fr::from(3198767490419873482u64),
659 id_commitment_r: ark_bn254::Fr::from_str(
660 "11722352184830287916674945948108962396487445899741105828127518108056503126019",
661 )
662 .unwrap(),
663 id_commitment: ark_bn254::Fr::from(0u64),
664 dlog_e: ark_bn254::Fr::from_str(
665 "20738873297635092620048980552264360096607713029337408079647701591795211132447",
666 )
667 .unwrap(),
668 dlog_s: ark_babyjubjub::Fr::from_str(
669 "409914485496464180245985942628922659137136006706846380135829705769429965654",
670 )
671 .unwrap(),
672 oprf_pk: Affine {
673 x: ark_babyjubjub::Fq::from_str(
674 "2124016492737602714904869498047199181102594928943726277329982080254326092458",
675 )
676 .unwrap(),
677 y: ark_babyjubjub::Fq::from_str(
678 "13296886400185574560491768605341786437896334271868835545571935419923854148448",
679 )
680 .unwrap(),
681 },
682 oprf_response_blinded: Affine {
683 x: ark_babyjubjub::Fq::from_str(
684 "186021305824089989598292966483056363224488147240980559441958002546059602483",
685 )
686 .unwrap(),
687 y: ark_babyjubjub::Fq::from_str(
688 "16813058203546508924422863380215026034284821141284206571184467783067057954778",
689 )
690 .unwrap(),
691 },
692 oprf_response: Affine {
693 x: ark_babyjubjub::Fq::from_str(
694 "10209445202057032226639052993170591937356545068582397532992536070677055126187",
695 )
696 .unwrap(),
697 y: ark_babyjubjub::Fq::from_str(
698 "21877375411477040679486668720099554257785799784699842830375906922948306109699",
699 )
700 .unwrap(),
701 },
702 signal_hash: ark_bn254::Fr::from_str(
703 "37938388892362834151584770384290207919364301626797345218722464515205243407",
704 )
705 .unwrap(),
706 }
707 }
708
709 #[test]
710 fn test_valid_nullifier_proof_input() {
711 let inputs = get_valid_nullifier_proof_input();
712 let _ = check_nullifier_input_validity(&inputs).unwrap();
713 }
714
715 #[test]
716 fn test_invalid_nullifier_proof_input() {
717 let inputs = get_valid_nullifier_proof_input();
718 {
719 let mut inputs = inputs.clone();
720 inputs.current_timestamp =
721 ark_babyjubjub::Fq::from_str("123465723894591324701234982134000070").unwrap(); assert!(matches!(
723 check_nullifier_input_validity(&inputs).unwrap_err(),
724 super::ProofInputError::ValueOutOfBounds {
725 name: "current timestamp",
726 ..
727 }
728 ));
729 }
730 {
731 let mut inputs = inputs.clone();
732 inputs.current_timestamp = inputs.cred_expires_at;
733 assert!(matches!(
734 check_nullifier_input_validity(&inputs).unwrap_err(),
735 super::ProofInputError::CredentialExpired { .. }
736 ));
737 }
738 {
739 let mut inputs = inputs.clone();
740 inputs.cred_genesis_issued_at_min = ark_bn254::Fr::from(1770125924u64);
742 assert!(matches!(
743 check_nullifier_input_validity(&inputs).unwrap_err(),
744 super::ProofInputError::CredentialGenesisExpired { .. }
745 ));
746 }
747 {
748 let mut inputs = inputs.clone();
749 inputs.cred_r = Affine {
750 x: ark_babyjubjub::Fq::from(1u64),
751 y: ark_babyjubjub::Fq::from(2u64),
752 };
753 assert!(matches!(
754 check_nullifier_input_validity(&inputs).unwrap_err(),
755 super::ProofInputError::InvalidBabyJubJubPoint {
756 name: "Credential Signature R"
757 }
758 ));
759 }
760 {
761 let mut inputs = inputs.clone();
762 inputs.cred_pk = Affine {
763 x: ark_babyjubjub::Fq::from(1u64),
764 y: ark_babyjubjub::Fq::from(2u64),
765 };
766 assert!(matches!(
767 check_nullifier_input_validity(&inputs).unwrap_err(),
768 super::ProofInputError::InvalidBabyJubJubPoint {
769 name: "Credential Public Key"
770 }
771 ));
772 }
773 {
774 let mut inputs = inputs.clone();
775 inputs.cred_s = ark_babyjubjub::Fr::from(12345u64);
776 assert!(matches!(
777 check_nullifier_input_validity(&inputs).unwrap_err(),
778 super::ProofInputError::InvalidCredentialSignature
779 ));
780 }
781 {
782 let mut inputs = inputs.clone();
783 inputs.oprf_pk = Affine {
784 x: ark_babyjubjub::Fq::from(1u64),
785 y: ark_babyjubjub::Fq::from(2u64),
786 };
787 assert!(matches!(
788 check_nullifier_input_validity(&inputs).unwrap_err(),
789 super::ProofInputError::InvalidBabyJubJubPoint {
790 name: "OPRF Public Key"
791 }
792 ));
793 }
794 {
795 let mut inputs = inputs.clone();
796 inputs.oprf_response_blinded = Affine {
797 x: ark_babyjubjub::Fq::from(1u64),
798 y: ark_babyjubjub::Fq::from(2u64),
799 };
800 assert!(matches!(
801 check_nullifier_input_validity(&inputs).unwrap_err(),
802 super::ProofInputError::InvalidBabyJubJubPoint {
803 name: "OPRF Blinded Response"
804 }
805 ));
806 }
807 {
808 let mut inputs = inputs.clone();
809 inputs.dlog_s = ark_babyjubjub::Fr::from(12345u64);
810 assert!(matches!(
811 check_nullifier_input_validity(&inputs).unwrap_err(),
812 super::ProofInputError::InvalidOprfProof
813 ));
814 }
815 {
816 let mut inputs = inputs.clone();
817 inputs.oprf_response = Affine {
818 x: ark_babyjubjub::Fq::from(1u64),
819 y: ark_babyjubjub::Fq::from(2u64),
820 };
821 assert!(matches!(
822 check_nullifier_input_validity(&inputs).unwrap_err(),
823 super::ProofInputError::InvalidBabyJubJubPoint {
824 name: "OPRF Unblinded Response"
825 }
826 ));
827 }
828 {
829 let mut inputs = inputs.clone();
830 use ark_ec::AffineRepr;
832 inputs.oprf_response = ark_babyjubjub::EdwardsAffine::generator();
833 assert!(matches!(
834 check_nullifier_input_validity(&inputs).unwrap_err(),
835 super::ProofInputError::InvalidOprfResponse
836 ));
837 }
838 {
839 let mut inputs = inputs.clone();
840 inputs.id_commitment = ark_bn254::Fr::from(12345u64);
841 assert!(matches!(
842 check_nullifier_input_validity(&inputs).unwrap_err(),
843 super::ProofInputError::InvalidSessionId
844 ));
845 }
846 }
847}