world_id_proof/
nullifier.rs1use ark_ff::PrimeField;
4use eyre::Context;
5use groth16_material::circom::CircomGroth16Material;
6
7use taceo_oprf::{
8 client::{Connector, VerifiableOprfOutput},
9 core::oprf::BlindingFactor,
10};
11
12use world_id_primitives::{
13 FieldElement, ProofRequest, TREE_DEPTH,
14 circuit_inputs::QueryProofCircuitInput,
15 nullifier::Nullifier,
16 oprf::{NullifierOprfRequestAuthV1, OprfModule},
17};
18
19use crate::{
20 AuthenticatorProofInput,
21 proof::{OPRF_PROOF_DS, ProofError, errors},
22};
23
24#[derive(Debug, Clone)]
26pub struct OprfNullifier {
27 pub query_proof_input: QueryProofCircuitInput<TREE_DEPTH>,
29 pub verifiable_oprf_output: VerifiableOprfOutput,
31 pub nullifier: Nullifier,
35}
36
37impl OprfNullifier {
38 pub async fn generate(
59 services: &[String],
60 threshold: usize,
61 query_material: &CircomGroth16Material,
62 authenticator_input: AuthenticatorProofInput,
63 proof_request: &ProofRequest,
64 connector: Connector,
65 ) -> Result<Self, ProofError> {
66 let mut rng = rand::rngs::OsRng;
67
68 let query_blinding_factor = BlindingFactor::rand(&mut rng);
69
70 let siblings: [ark_babyjubjub::Fq; TREE_DEPTH] =
71 authenticator_input.inclusion_proof.siblings.map(|s| *s);
72
73 let action = *proof_request.computed_action(&mut rng);
74 let query_hash = world_id_primitives::authenticator::oprf_query_digest(
75 authenticator_input.inclusion_proof.leaf_index,
76 action.into(),
77 proof_request.rp_id.into(),
78 );
79 let signature = authenticator_input.private_key.sign(*query_hash);
80
81 let query_proof_input = QueryProofCircuitInput::<TREE_DEPTH> {
82 pk: authenticator_input.key_set.as_affine_array(),
83 pk_index: authenticator_input.key_index.into(),
84 s: signature.s,
85 r: signature.r,
86 merkle_root: *authenticator_input.inclusion_proof.root,
87 depth: ark_babyjubjub::Fq::from(TREE_DEPTH as u64),
88 mt_index: authenticator_input.inclusion_proof.leaf_index.into(),
89 siblings,
90 beta: query_blinding_factor.beta(),
91 rp_id: *FieldElement::from(proof_request.rp_id),
92 action,
93 nonce: *proof_request.nonce,
94 };
95 let _ = errors::check_query_input_validity(&query_proof_input)?;
96
97 tracing::debug!("generating query proof");
98 let (proof, public_inputs) = query_material.generate_proof(&query_proof_input, &mut rng)?;
99 query_material.verify_proof(&proof, &public_inputs)?;
100 tracing::debug!("generated query proof");
101
102 let auth = NullifierOprfRequestAuthV1 {
103 proof: proof.into(),
104 action,
105 nonce: *proof_request.nonce,
106 merkle_root: *authenticator_input.inclusion_proof.root,
107 current_time_stamp: proof_request.created_at,
108 expiration_timestamp: proof_request.expires_at,
109 signature: proof_request.signature,
110 rp_id: proof_request.rp_id,
111 };
112
113 tracing::debug!("executing distributed OPRF");
114
115 let service_uris = taceo_oprf::client::to_oprf_uri_many(services, OprfModule::Nullifier)
116 .context("while building service URI for nullifier")?;
117
118 let verifiable_oprf_output = taceo_oprf::client::distributed_oprf(
119 &service_uris,
120 threshold,
121 *query_hash,
122 query_blinding_factor,
123 ark_babyjubjub::Fq::from_be_bytes_mod_order(OPRF_PROOF_DS),
124 auth,
125 connector,
126 )
127 .await?;
128
129 let nullifier: Nullifier = FieldElement::from(verifiable_oprf_output.output).into();
130
131 Ok(Self {
132 query_proof_input,
133 verifiable_oprf_output,
134 nullifier,
135 })
136 }
137}