world_id_primitives/
lib.rs

1//! This crate contains the raw base types (without implementation) for the World ID Protocol.
2//!
3//! It implements basic primitives such as field elements, proofs, the format of requests and responses, etc.
4//!
5//! Importantly, this crate keeps dependencies to a minimum and does not implement any logic beyond serialization and deserialization.
6#![deny(
7    clippy::all,
8    clippy::pedantic,
9    clippy::nursery,
10    missing_docs,
11    dead_code
12)]
13#![allow(clippy::option_if_let_else)]
14
15use alloy_primitives::Keccak256;
16
17pub mod serde_utils;
18use ark_babyjubjub::Fq;
19use ark_ff::{AdditiveGroup, Field, PrimeField, UniformRand};
20use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
21use ruint::aliases::{U160, U256};
22use serde::{de::Error as _, ser::Error as _, Deserialize, Deserializer, Serialize, Serializer};
23use std::{
24    fmt,
25    io::{Cursor, Read, Write},
26    ops::{Deref, DerefMut},
27    str::FromStr,
28};
29
30/// Contains types related to the Authenticator.
31pub mod authenticator;
32
33/// Contains the global configuration for interacting with the World ID Protocol.
34mod config;
35pub use config::Config;
36
37/// Contains the raw circuit input types for the World ID Protocol.
38///
39/// These types are used to prepare the inputs for the Groth16 circuits.
40pub mod circuit_inputs;
41
42/// SAFE-style sponge utilities and helpers.
43pub mod sponge;
44
45/// Base definition of a "Credential" in the World ID Protocol.
46pub mod credential;
47pub use credential::{Credential, CredentialVersion};
48
49/// Contains base types for operations with Merkle trees.
50pub mod merkle;
51
52/// Contains types specifically related to the OPRF services.
53pub mod oprf;
54
55/// Contains the quintessential proof type.
56pub mod proof;
57pub use proof::WorldIdProof;
58
59/// Contains types specifically related to relying parties.
60pub mod rp;
61
62/// The scalar field used in the World ID Protocol.
63///
64/// This is the scalar field of the `BabyJubJub` curve.
65pub type ScalarField = ark_babyjubjub::Fr;
66
67/// The depth of the Merkle tree used in the World ID Protocol for the `WorldIDRegistry` contract.
68pub const TREE_DEPTH: usize = 30;
69
70/// Represents a field element of the base field (`Fq`) in the World ID Protocol.
71///
72/// The World ID Protocol uses the `BabyJubJub` curve throughout. Note the
73/// base field of `BabyJubJub` is the scalar field of the BN254 curve.
74///
75/// This wrapper ensures consistent serialization and deserialization of field elements, where
76/// string-based serialization is done with hex encoding and binary serialization is done with byte vectors.
77#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
78pub struct FieldElement(Fq);
79
80impl FieldElement {
81    /// The additive identity of the field.
82    pub const ZERO: Self = Self(Fq::ZERO);
83    /// The multiplicative identity of the field.
84    pub const ONE: Self = Self(Fq::ONE);
85
86    /// Serializes the field element into a byte vector.
87    ///
88    /// # Errors
89    /// Will return an error if the serialization unexpectedly fails.
90    pub fn serialize_as_bytes<W: Write>(&self, writer: &mut W) -> Result<(), PrimitiveError> {
91        self.0
92            .serialize_compressed(writer)
93            .map_err(|e| PrimitiveError::Serialization(e.to_string()))
94    }
95
96    /// Deserializes a field element from a byte vector.
97    ///
98    /// # Errors
99    /// Will return an error if the provided input is not a valid field element (e.g. not on the curve).
100    pub fn deserialize_from_bytes<R: Read>(bytes: &mut R) -> Result<Self, PrimitiveError> {
101        let field_element = Fq::deserialize_compressed(bytes)
102            .map_err(|e| PrimitiveError::Deserialization(e.to_string()))?;
103        Ok(Self(field_element))
104    }
105
106    /// Deserializes a field element from a big-endian byte slice.
107    #[must_use]
108    pub fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
109        let field_element = Fq::from_be_bytes_mod_order(bytes);
110        Self(field_element)
111    }
112
113    /// Takes arbitrary raw bytes, hashes them with a byte-friendly gas-efficient hash function
114    /// and reduces it to a field element.
115    ///
116    #[must_use]
117    pub fn from_arbitrary_raw_bytes(bytes: &[u8]) -> Self {
118        let mut hasher = Keccak256::new();
119        hasher.update(bytes);
120        let output: [u8; 32] = hasher.finalize().into();
121
122        let n = U256::from_be_bytes(output);
123        // Shift right one byte to make it fit in the field
124        let n: U256 = n >> 8;
125
126        let field_element = Fq::from_bigint(n.into());
127
128        match field_element {
129            Some(element) => Self(element),
130            None => unreachable!(
131                "due to the byte reduction, the value is guaranteed to be within the field"
132            ),
133        }
134
135        // FIXME: add unit tests
136    }
137
138    /// Generates a random field element using the system's CSPRNG.
139    #[must_use]
140    pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
141        let field_element = Fq::rand(rng);
142        Self(field_element)
143    }
144}
145
146impl Deref for FieldElement {
147    type Target = Fq;
148    fn deref(&self) -> &Self::Target {
149        &self.0
150    }
151}
152
153impl DerefMut for FieldElement {
154    fn deref_mut(&mut self) -> &mut Self::Target {
155        &mut self.0
156    }
157}
158
159impl FromStr for FieldElement {
160    type Err = PrimitiveError;
161
162    fn from_str(s: &str) -> Result<Self, Self::Err> {
163        let s = s.trim_start_matches("0x");
164        let u256 = U256::from_str_radix(s, 16).map_err(|_| {
165            PrimitiveError::Deserialization("not a valid hex-encoded number".to_string())
166        })?;
167        u256.try_into()
168    }
169}
170
171impl fmt::Display for FieldElement {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        let u256: U256 = (*self).into();
174        write!(f, "{u256:#066x}")
175    }
176}
177
178impl From<Fq> for FieldElement {
179    fn from(value: Fq) -> Self {
180        Self(value)
181    }
182}
183
184impl TryFrom<U256> for FieldElement {
185    type Error = PrimitiveError;
186    fn try_from(value: U256) -> Result<Self, Self::Error> {
187        Ok(Self(
188            value.try_into().map_err(|_| PrimitiveError::NotInField)?,
189        ))
190    }
191}
192
193// safe because U160 is guaranteed to be less than the field modulus.
194impl From<U160> for FieldElement {
195    fn from(value: U160) -> Self {
196        // convert U160 to U256 to reuse existing implementations
197        let u256 = U256::from(value);
198        let big_int = ark_ff::BigInt(u256.into_limbs());
199        Self(ark_babyjubjub::Fq::new(big_int))
200    }
201}
202
203impl From<FieldElement> for U256 {
204    fn from(value: FieldElement) -> Self {
205        <Self as From<Fq>>::from(value.0)
206    }
207}
208
209impl From<u64> for FieldElement {
210    fn from(value: u64) -> Self {
211        Self(Fq::from(value))
212    }
213}
214
215impl From<u128> for FieldElement {
216    fn from(value: u128) -> Self {
217        Self(Fq::from(value))
218    }
219}
220
221impl Serialize for FieldElement {
222    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
223    where
224        S: Serializer,
225    {
226        if serializer.is_human_readable() {
227            serializer.serialize_str(&self.to_string())
228        } else {
229            let mut writer = Vec::new();
230            self.serialize_compressed(&mut writer)
231                .map_err(S::Error::custom)?;
232            serializer.serialize_bytes(&writer)
233        }
234    }
235}
236
237impl<'de> Deserialize<'de> for FieldElement {
238    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239    where
240        D: Deserializer<'de>,
241    {
242        if deserializer.is_human_readable() {
243            let s = String::deserialize(deserializer)?;
244            Self::from_str(&s).map_err(D::Error::custom)
245        } else {
246            let bytes = Vec::<u8>::deserialize(deserializer)?;
247            Self::deserialize_from_bytes(&mut Cursor::new(bytes)).map_err(D::Error::custom)
248        }
249    }
250}
251
252/// Generic errors that may occur with basic serialization and deserialization.
253#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
254pub enum PrimitiveError {
255    /// Error that occurs when serializing a value. Generally not expected.
256    #[error("Serialization error: {0}")]
257    Serialization(String),
258    /// Error that occurs when deserializing a value. This can happen often when not providing valid inputs.
259    #[error("Deserialization error: {0}")]
260    Deserialization(String),
261    /// Number is equal or larger than the target field modulus.
262    #[error("Provided value is not in the field")]
263    NotInField,
264    /// Index is out of bounds.
265    #[error("Provided index is out of bounds")]
266    OutOfBounds,
267    /// Invalid input provided (e.g., incorrect length, format, etc.)
268    #[error("Invalid input at {attribute}: {reason}")]
269    InvalidInput {
270        /// The attribute that is invalid
271        attribute: String,
272        /// The reason the input is invalid
273        reason: String,
274    },
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use ruint::uint;
281
282    #[test]
283    fn test_field_element_encoding() {
284        let root = FieldElement::try_from(uint!(
285            0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
286        ))
287        .unwrap();
288
289        assert_eq!(
290            serde_json::to_string(&root).unwrap(),
291            "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
292        );
293
294        assert_eq!(
295            root.to_string(),
296            "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
297        );
298
299        let fe = FieldElement::ONE;
300        assert_eq!(
301            serde_json::to_string(&fe).unwrap(),
302            "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
303        );
304
305        let md = FieldElement::ZERO;
306        assert_eq!(
307            serde_json::to_string(&md).unwrap(),
308            "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
309        );
310
311        assert_eq!(*FieldElement::ONE, Fq::ONE);
312    }
313
314    #[test]
315    fn test_field_element_decoding() {
316        let root = FieldElement::try_from(uint!(
317            0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
318        ))
319        .unwrap();
320
321        assert_eq!(
322            serde_json::from_str::<FieldElement>(
323                "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
324            )
325            .unwrap(),
326            root
327        );
328
329        assert_eq!(
330            FieldElement::from_str(
331                "0x0000000000000000000000000000000000000000000000000000000000000001"
332            )
333            .unwrap(),
334            FieldElement::ONE
335        );
336    }
337
338    #[test]
339    fn test_field_element_binary_encoding_roundtrip() {
340        let root = FieldElement::try_from(uint!(
341            0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
342        ))
343        .unwrap();
344
345        let mut buffer = Vec::new();
346        ciborium::into_writer(&root, &mut buffer).unwrap();
347
348        let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
349
350        assert_eq!(root, decoded);
351    }
352
353    #[test]
354    fn test_field_element_binary_encoding_format() {
355        let root = FieldElement::try_from(uint!(
356            0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
357        ))
358        .unwrap();
359
360        // Serialize to CBOR (binary format)
361        let mut buffer = Vec::new();
362        ciborium::into_writer(&root, &mut buffer).unwrap();
363
364        assert_eq!(buffer.len(), 34); // CBOR header (2 bytes) + field element (32 bytes)
365        assert_eq!(buffer[0], 0x58); // CBOR byte string, 1-byte length follows
366        assert_eq!(buffer[1], 0x20); // Length = 32 bytes
367
368        let field_bytes = &buffer[2..];
369        assert_eq!(field_bytes.len(), 32);
370
371        let expected_le_bytes =
372            hex::decode("c224d31e05d194f1b7ac32eaa4dbfce39a43a3f050cf422f21ac917bce23d211")
373                .unwrap();
374        assert_eq!(field_bytes, expected_le_bytes.as_slice());
375    }
376}