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
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
26/// Contains types related to the Authenticator.
27pub mod authenticator;
28
29/// Contains the global configuration for interacting with the World ID Protocol.
30mod config;
31pub use config::Config;
32
33/// Contains the raw circuit input types for the World ID Protocol.
34///
35/// These types are used to prepare the inputs for the Groth16 circuits.
36pub mod circuit_inputs;
37
38/// Base definition of a "Credential" in the World ID Protocol.
39pub mod credential;
40pub use credential::{Credential, CredentialVersion};
41
42/// Contains base types for operations with Merkle trees.
43pub mod merkle;
44
45/// Contains types specifically related to the OPRF services.
46pub mod oprf;
47
48/// Contains the quintessential proof type.
49pub mod proof;
50pub use proof::WorldIdProof;
51
52/// Contains types specifically related to relying parties.
53pub mod rp;
54
55/// The scalar field used in the World ID Protocol.
56///
57/// This is the scalar field of the `BabyJubJub` curve.
58pub type ScalarField = ark_babyjubjub::Fr;
59
60/// The depth of the Merkle tree used in the World ID Protocol for the `AccountRegistry` contract.
61pub const TREE_DEPTH: usize = 30;
62
63/// Represents a field element of the base field (`Fq`) in the World ID Protocol.
64///
65/// The World ID Protocol uses the `BabyJubJub` curve throughout. Note the
66/// base field of `BabyJubJub` is the scalar field of the BN254 curve.
67///
68/// This wrapper ensures consistent serialization and deserialization of field elements, where
69/// string-based serialization is done with hex encoding and binary serialization is done with byte vectors.
70#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
71pub struct FieldElement(Fq);
72
73impl FieldElement {
74    /// The additive identity of the field.
75    pub const ZERO: Self = Self(Fq::ZERO);
76    /// The multiplicative identity of the field.
77    pub const ONE: Self = Self(Fq::ONE);
78
79    /// Serializes the field element into a byte vector.
80    ///
81    /// # Errors
82    /// Will return an error if the serialization unexpectedly fails.
83    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    /// Deserializes a field element from a byte vector.
90    ///
91    /// # Errors
92    /// Will return an error if the provided input is not a valid field element (e.g. not on the curve).
93    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    /// Deserializes a field element from a big-endian byte slice.
100    #[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/// Generic errors that may occur with basic serialization and deserialization.
204#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
205pub enum PrimitiveError {
206    /// Error that occurs when serializing a value. Generally not expected.
207    #[error("Serialization error: {0}")]
208    Serialization(String),
209    /// Error that occurs when deserializing a value. This can happen often when not providing valid inputs.
210    #[error("Deserialization error: {0}")]
211    Deserialization(String),
212    /// Number is equal or larger than the target field modulus.
213    #[error("Provided value is not in the field")]
214    NotInField,
215    /// Index is out of bounds.
216    #[error("Provided index is out of bounds")]
217    OutOfBounds,
218    /// Invalid input provided (e.g., incorrect length, format, etc.)
219    #[error("Invalid input at {attribute}: {reason}")]
220    InvalidInput {
221        /// The attribute that is invalid
222        attribute: String,
223        /// The reason the input is invalid
224        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        // Serialize to CBOR (binary format)
312        let mut buffer = Vec::new();
313        ciborium::into_writer(&root, &mut buffer).unwrap();
314
315        assert_eq!(buffer.len(), 34); // CBOR header (2 bytes) + field element (32 bytes)
316        assert_eq!(buffer[0], 0x58); // CBOR byte string, 1-byte length follows
317        assert_eq!(buffer[1], 0x20); // Length = 32 bytes
318
319        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}