1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//! Circuit input types for the World ID Protocol circuits.
//!
//! This module requires the `circuits` feature and is not available in WASM builds.
use std::collections::HashMap;
use groth16_material::circom::ProofInput;
use ruint::aliases::U256;
use crate::authenticator::MAX_AUTHENTICATOR_KEYS;
type BaseField = ark_babyjubjub::Fq;
type ScalarField = ark_babyjubjub::Fr;
type Affine = ark_babyjubjub::EdwardsAffine;
#[inline]
pub(crate) fn fq_to_u256_vec(f: ark_babyjubjub::Fq) -> Vec<U256> {
vec![f.into()]
}
#[inline]
pub(crate) fn fq_seq_to_u256_vec(fs: &[ark_babyjubjub::Fq]) -> Vec<U256> {
fs.iter().copied().map(Into::into).collect()
}
#[inline]
pub(crate) fn fr_to_u256_vec(f: ark_babyjubjub::Fr) -> Vec<U256> {
vec![f.into()]
}
#[inline]
pub(crate) fn affine_to_u256_vec(p: ark_babyjubjub::EdwardsAffine) -> Vec<U256> {
vec![p.x.into(), p.y.into()]
}
#[inline]
pub(crate) fn affine_seq_to_u256_vec(ps: &[ark_babyjubjub::EdwardsAffine]) -> Vec<U256> {
ps.iter()
.copied()
.flat_map(|p| [p.x.into(), p.y.into()])
.collect()
}
/// The input for the circuit of the OPRF Query Proof `π1`.
///
/// TODO: Rename attribute names to match the `Credential` type.
#[derive(Debug, Clone)]
pub struct QueryProofCircuitInput<const MAX_DEPTH: usize> {
/// The `AuthenticatorPublicKeySet` represented as an array of Affine points.
pub pk: [Affine; MAX_AUTHENTICATOR_KEYS],
/// The index of the public key which will be used to sign the OPRF query from the `AuthenticatorPublicKeySet`.
pub pk_index: BaseField,
/// The `s` part of the signature of the query with the public key at the `pk_index`.
pub s: ScalarField,
/// The `r` part of the signature of the query with the public key at the `pk_index`.
pub r: Affine,
/// The root of the Merkle tree of the `WorldIDRegistry` contract.
pub merkle_root: BaseField,
/// The depth of the Merkle tree of the `WorldIDRegistry` contract.
pub depth: BaseField,
/// The leaf index of the World ID in the Merkle tree of the `WorldIDRegistry` contract.
///
/// In the `MerkleInclusionProof` type, this is the `leaf_index` field.
pub mt_index: BaseField,
/// The siblings of the Merkle proof of the account in the `WorldIDRegistry` contract.
pub siblings: [BaseField; MAX_DEPTH],
/// The (non-inverted) blinding factor of the OPRF query.
pub beta: ScalarField,
/// The ID of the RP requesting the proof as registered in the `RpRegistry` contract.
///
/// TODO: Will require updates once the new `RpRegistry` is launched.
pub rp_id: BaseField,
/// The action for the proof request. See `ProofRequest` for more details.
pub action: BaseField,
/// The nonce of the proof request. See `ProofRequest` for more details.
pub nonce: BaseField,
}
impl<const MAX_DEPTH: usize> ProofInput for QueryProofCircuitInput<MAX_DEPTH> {
fn prepare_input(&self) -> HashMap<String, Vec<U256>> {
let mut map = HashMap::new();
map.insert("pk".to_owned(), affine_seq_to_u256_vec(&self.pk));
map.insert("pk_index".to_owned(), fq_to_u256_vec(self.pk_index));
map.insert("s".to_owned(), fr_to_u256_vec(self.s));
map.insert("r".to_owned(), affine_to_u256_vec(self.r));
map.insert("merkle_root".to_owned(), fq_to_u256_vec(self.merkle_root));
map.insert("depth".to_owned(), fq_to_u256_vec(self.depth));
map.insert("mt_index".to_owned(), fq_to_u256_vec(self.mt_index));
map.insert("siblings".to_owned(), fq_seq_to_u256_vec(&self.siblings));
map.insert("beta".to_owned(), fr_to_u256_vec(self.beta));
map.insert("rp_id".to_owned(), fq_to_u256_vec(self.rp_id));
map.insert("action".to_owned(), fq_to_u256_vec(self.action));
map.insert("nonce".to_owned(), fq_to_u256_vec(self.nonce));
map
}
}
/// The input for the circuit of the Uniqueness Proof `π2` (internally also nullifier proof).
///
/// Externally, the Nullifier Proof is exposed to RPs as a Uniqueness Proof or a Session Proof respectively.
#[derive(Debug, Clone)]
pub struct NullifierProofCircuitInput<const MAX_DEPTH: usize> {
/// The input for the circuit of the OPRF Query Proof from which this nullifier proof is constructed.
pub query_input: QueryProofCircuitInput<MAX_DEPTH>,
// SECTION: Credential Inputs
/// The id as registered in the `CredentialSchemaIssuerRegistry` representing the (issuer, schema) pair.
///
/// This is the `issuer_schema_id` field of the `Credential` type.
pub issuer_schema_id: BaseField,
/// The public key of the issuer of the credential. This is stored in the `Credential` type in the `issuer` field.
pub cred_pk: Affine,
/// A specific commitment to particular claims of the `Credential`. In particular:
/// [`claims_hash`, `associated_data_commitment`]
pub cred_hashes: [BaseField; 2],
/// The `genesis_issued_at` attribute of the `Credential`. See the `Credential` type for more details.
pub cred_genesis_issued_at: BaseField,
/// The `expires_at` attribute of the `Credential`. See the `Credential` type for more details.
pub cred_expires_at: BaseField,
/// The `s` part of the signature of the credential (signed by the issuer)
pub cred_s: ScalarField,
/// The `r` part of the signature of the credential (signed by the issuer)
pub cred_r: Affine,
/// The timestamp from the request.
pub current_timestamp: BaseField,
/// The `genesis_issued_at_min` attribute of the `Credential`. See the `Credential` type for more details.
pub cred_genesis_issued_at_min: BaseField,
/// The `cred_user_id_r` blinding factor used to generate the `sub`.
pub cred_sub_blinding_factor: BaseField,
/// The unique identifier of the `Credential`.
pub cred_id: BaseField,
// SECTION: User Inputs
/// The random commitment for future session proofs generated by the Authenticator.
///
/// TODO: Rename to match new terms and avoid confusion with World ID <3.0's `identity_commitment`.
pub id_commitment_r: BaseField,
/// The identity commitment for future session proofs.
///
/// Is used internally to check for equality against the computed identity commitment.
/// If set to 0, all computed identity commitments will be accepted.
pub id_commitment: BaseField,
// SECTION: OPRF Inputs
/// The `e` part of the `DLog` equality proof (Fiat-Shamir challenge)
pub dlog_e: BaseField,
/// The `s` part of the `DLog` equality proof (proof response)
pub dlog_s: ScalarField,
/// The public key of the OPRF Nodes for the particular RP (this is the `RpNullifierKey`).
pub oprf_pk: Affine,
/// The combined blinded response after aggregating peer commitments from the OPRF nodes.
pub oprf_response_blinded: Affine,
/// The unblinded response from from the OPRF nodes.
pub oprf_response: Affine,
// SECTION: RP Inputs
/// The hashed signal provided by the RP and committed to by the user. See `ProofRequest` for more details.
pub signal_hash: BaseField,
}
impl<const MAX_DEPTH: usize> ProofInput for NullifierProofCircuitInput<MAX_DEPTH> {
fn prepare_input(&self) -> std::collections::HashMap<String, Vec<ruint::aliases::U256>> {
let mut map = self.query_input.prepare_input();
map.insert(
"issuer_schema_id".to_owned(),
fq_to_u256_vec(self.issuer_schema_id),
);
map.insert("cred_pk".to_owned(), affine_to_u256_vec(self.cred_pk));
map.insert(
"cred_hashes".to_owned(),
fq_seq_to_u256_vec(&self.cred_hashes),
);
map.insert(
"cred_genesis_issued_at".to_owned(),
fq_to_u256_vec(self.cred_genesis_issued_at),
);
map.insert(
"cred_genesis_issued_at_min".to_owned(),
fq_to_u256_vec(self.cred_genesis_issued_at_min),
);
map.insert(
"cred_expires_at".to_owned(),
fq_to_u256_vec(self.cred_expires_at),
);
map.insert("cred_id".to_owned(), fq_to_u256_vec(self.cred_id));
map.insert(
"cred_user_id_r".to_owned(),
fq_to_u256_vec(self.cred_sub_blinding_factor),
);
map.insert("cred_s".to_owned(), fr_to_u256_vec(self.cred_s));
map.insert("cred_r".to_owned(), affine_to_u256_vec(self.cred_r));
map.insert(
"id_commitment_r".to_owned(),
fq_to_u256_vec(self.id_commitment_r),
);
map.insert(
"id_commitment".to_owned(),
fq_to_u256_vec(self.id_commitment),
);
map.insert("dlog_e".to_owned(), fq_to_u256_vec(self.dlog_e));
map.insert("dlog_s".to_owned(), fr_to_u256_vec(self.dlog_s));
map.insert("oprf_pk".to_owned(), affine_to_u256_vec(self.oprf_pk));
map.insert(
"oprf_response_blinded".to_owned(),
affine_to_u256_vec(self.oprf_response_blinded),
);
map.insert(
"oprf_response".to_owned(),
affine_to_u256_vec(self.oprf_response),
);
map.insert("signal_hash".to_owned(), fq_to_u256_vec(self.signal_hash));
map.insert(
"current_timestamp".to_owned(),
fq_to_u256_vec(self.current_timestamp),
);
map
}
}