world_id_primitives/
lib.rs1#![deny(
7 clippy::all,
8 clippy::pedantic,
9 clippy::nursery,
10 missing_docs,
11 dead_code
12)]
13
14use ark_babyjubjub::Fq;
15use ark_ff::{AdditiveGroup, Field, PrimeField};
16use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
17use ruint::aliases::U256;
18use serde::{de::Error as _, ser::Error as _, Deserialize, Deserializer, Serialize, Serializer};
19use std::{
20 fmt,
21 io::{Cursor, Read, Write},
22 ops::{Deref, DerefMut},
23 str::FromStr,
24};
25
26pub mod authenticator;
28
29mod config;
31pub use config::Config;
32
33pub mod circuit_inputs;
37
38pub mod credential;
40pub use credential::{Credential, CredentialVersion};
41
42pub mod merkle;
44
45pub mod oprf;
47
48pub mod proof;
50pub use proof::WorldIdProof;
51
52pub mod rp;
54
55pub type ScalarField = ark_babyjubjub::Fr;
59
60pub const TREE_DEPTH: usize = 30;
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
71pub struct FieldElement(Fq);
72
73impl FieldElement {
74 pub const ZERO: Self = Self(Fq::ZERO);
76 pub const ONE: Self = Self(Fq::ONE);
78
79 pub fn serialize_as_bytes<W: Write>(&self, writer: &mut W) -> Result<(), PrimitiveError> {
84 self.0
85 .serialize_compressed(writer)
86 .map_err(|e| PrimitiveError::Serialization(e.to_string()))
87 }
88
89 pub fn deserialize_from_bytes<R: Read>(bytes: &mut R) -> Result<Self, PrimitiveError> {
94 let field_element = Fq::deserialize_compressed(bytes)
95 .map_err(|e| PrimitiveError::Deserialization(e.to_string()))?;
96 Ok(Self(field_element))
97 }
98
99 #[must_use]
101 pub fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
102 let field_element = Fq::from_be_bytes_mod_order(bytes);
103 Self(field_element)
104 }
105}
106
107impl Deref for FieldElement {
108 type Target = Fq;
109 fn deref(&self) -> &Self::Target {
110 &self.0
111 }
112}
113
114impl DerefMut for FieldElement {
115 fn deref_mut(&mut self) -> &mut Self::Target {
116 &mut self.0
117 }
118}
119
120impl FromStr for FieldElement {
121 type Err = PrimitiveError;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 let s = s.trim_start_matches("0x");
125 let u256 = U256::from_str_radix(s, 16).map_err(|_| {
126 PrimitiveError::Deserialization("not a valid hex-encoded number".to_string())
127 })?;
128 u256.try_into()
129 }
130}
131
132impl fmt::Display for FieldElement {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 let u256: U256 = (*self).into();
135 write!(f, "{u256:#066x}")
136 }
137}
138
139impl From<Fq> for FieldElement {
140 fn from(value: Fq) -> Self {
141 Self(value)
142 }
143}
144
145impl TryFrom<U256> for FieldElement {
146 type Error = PrimitiveError;
147 fn try_from(value: U256) -> Result<Self, Self::Error> {
148 Ok(Self(
149 value.try_into().map_err(|_| PrimitiveError::NotInField)?,
150 ))
151 }
152}
153
154impl From<FieldElement> for U256 {
155 fn from(value: FieldElement) -> Self {
156 <Self as From<Fq>>::from(value.0)
157 }
158}
159
160impl From<u64> for FieldElement {
161 fn from(value: u64) -> Self {
162 Self(Fq::from(value))
163 }
164}
165
166impl From<u128> for FieldElement {
167 fn from(value: u128) -> Self {
168 Self(Fq::from(value))
169 }
170}
171
172impl Serialize for FieldElement {
173 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174 where
175 S: Serializer,
176 {
177 if serializer.is_human_readable() {
178 serializer.serialize_str(&self.to_string())
179 } else {
180 let mut writer = Vec::new();
181 self.serialize_compressed(&mut writer)
182 .map_err(S::Error::custom)?;
183 serializer.serialize_bytes(&writer)
184 }
185 }
186}
187
188impl<'de> Deserialize<'de> for FieldElement {
189 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
190 where
191 D: Deserializer<'de>,
192 {
193 if deserializer.is_human_readable() {
194 let s = String::deserialize(deserializer)?;
195 Self::from_str(&s).map_err(D::Error::custom)
196 } else {
197 let bytes = Vec::<u8>::deserialize(deserializer)?;
198 Self::deserialize_from_bytes(&mut Cursor::new(bytes)).map_err(D::Error::custom)
199 }
200 }
201}
202
203#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
205pub enum PrimitiveError {
206 #[error("Serialization error: {0}")]
208 Serialization(String),
209 #[error("Deserialization error: {0}")]
211 Deserialization(String),
212 #[error("Provided value is not in the field")]
214 NotInField,
215 #[error("Provided index is out of bounds")]
217 OutOfBounds,
218 #[error("Invalid input at {attribute}: {reason}")]
220 InvalidInput {
221 attribute: String,
223 reason: String,
225 },
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use ruint::uint;
232
233 #[test]
234 fn test_field_element_encoding() {
235 let root = FieldElement::try_from(uint!(
236 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
237 ))
238 .unwrap();
239
240 assert_eq!(
241 serde_json::to_string(&root).unwrap(),
242 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
243 );
244
245 assert_eq!(
246 root.to_string(),
247 "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
248 );
249
250 let fe = FieldElement::ONE;
251 assert_eq!(
252 serde_json::to_string(&fe).unwrap(),
253 "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
254 );
255
256 let md = FieldElement::ZERO;
257 assert_eq!(
258 serde_json::to_string(&md).unwrap(),
259 "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
260 );
261
262 assert_eq!(*FieldElement::ONE, Fq::ONE);
263 }
264
265 #[test]
266 fn test_field_element_decoding() {
267 let root = FieldElement::try_from(uint!(
268 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
269 ))
270 .unwrap();
271
272 assert_eq!(
273 serde_json::from_str::<FieldElement>(
274 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
275 )
276 .unwrap(),
277 root
278 );
279
280 assert_eq!(
281 FieldElement::from_str(
282 "0x0000000000000000000000000000000000000000000000000000000000000001"
283 )
284 .unwrap(),
285 FieldElement::ONE
286 );
287 }
288
289 #[test]
290 fn test_field_element_binary_encoding_roundtrip() {
291 let root = FieldElement::try_from(uint!(
292 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
293 ))
294 .unwrap();
295
296 let mut buffer = Vec::new();
297 ciborium::into_writer(&root, &mut buffer).unwrap();
298
299 let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
300
301 assert_eq!(root, decoded);
302 }
303
304 #[test]
305 fn test_field_element_binary_encoding_format() {
306 let root = FieldElement::try_from(uint!(
307 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
308 ))
309 .unwrap();
310
311 let mut buffer = Vec::new();
313 ciborium::into_writer(&root, &mut buffer).unwrap();
314
315 assert_eq!(buffer.len(), 34); assert_eq!(buffer[0], 0x58); assert_eq!(buffer[1], 0x20); let field_bytes = &buffer[2..];
320 assert_eq!(field_bytes.len(), 32);
321
322 let expected_le_bytes =
323 hex::decode("c224d31e05d194f1b7ac32eaa4dbfce39a43a3f050cf422f21ac917bce23d211")
324 .unwrap();
325 assert_eq!(field_bytes, expected_le_bytes.as_slice());
326 }
327}