1#![cfg_attr(all(),
2doc = ::embed_doc_image::embed_image!("world-id-protocol-parties", "assets/world-id-protocol-parties.png"))]
3#![doc = include_str!("../README.md")]
4#![cfg_attr(not(test), deny(unused_crate_dependencies))]
5#![deny(clippy::all, clippy::nursery, missing_docs, dead_code)]
6#![allow(clippy::option_if_let_else)]
7
8use alloy_primitives::Keccak256;
9use ark_babyjubjub::Fq;
10use ark_ff::{AdditiveGroup, Field, PrimeField, UniformRand};
11use ruint::aliases::{U160, U256};
12use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
13use std::{
14 fmt,
15 ops::{Deref, DerefMut},
16 str::FromStr,
17};
18
19#[cfg(target_arch = "wasm32")]
20use getrandom as _;
21
22pub mod authenticator;
24
25mod key_set;
26pub use key_set::{
27 AuthenticatorPublicKeySet, MAX_AUTHENTICATOR_KEYS, SparseAuthenticatorPubkeysError,
28};
29
30mod config;
32pub use config::{Config, ServiceEndpoint};
33
34pub mod sponge;
36
37pub mod credential;
39pub use credential::{Credential, CredentialVersion};
40
41pub mod merkle;
43
44pub mod api_types;
46
47pub mod oprf;
49
50mod nullifier;
52pub use nullifier::Nullifier;
53
54mod session;
56pub use session::{SessionFeType, SessionFieldElement, SessionId, SessionNullifier};
57
58pub mod proof;
60pub use proof::{OwnershipProof, ZeroKnowledgeProof};
61
62pub mod rp;
64
65pub mod serde_utils;
66
67mod signer;
69pub use signer::Signer;
70
71pub mod request;
73pub use request::{
74 ConstraintExpr, ConstraintKind, ConstraintNode, MAX_CONSTRAINT_NODES, ProofRequest,
75 ProofResponse, ProofType, RequestItem, RequestVersion, ResponseItem, ValidationError,
76};
77
78pub use eddsa_babyjubjub::{EdDSAPrivateKey, EdDSAPublicKey, EdDSASignature};
79pub use taceo_oprf::types::{OprfKeyId, ShareEpoch};
80
81pub type ScalarField = ark_babyjubjub::Fr;
85
86pub const TREE_DEPTH: usize = 30;
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
97pub struct FieldElement(Fq);
98
99impl FieldElement {
100 pub const ZERO: Self = Self(Fq::ZERO);
102 pub const ONE: Self = Self(Fq::ONE);
104
105 #[must_use]
107 pub fn to_be_bytes(&self) -> [u8; 32] {
108 let as_num: U256 = self.to_u256();
109 as_num.to_be_bytes()
110 }
111
112 pub fn from_be_bytes(be_bytes: &[u8; 32]) -> Result<Self, PrimitiveError> {
119 U256::from_be_bytes(*be_bytes).try_into()
120 }
121
122 #[must_use]
134 pub fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
135 let field_element = Fq::from_be_bytes_mod_order(bytes);
136 Self(field_element)
137 }
138
139 #[must_use]
142 pub fn from_arbitrary_raw_bytes(bytes: &[u8]) -> Self {
143 let mut hasher = Keccak256::new();
144 hasher.update(bytes);
145 let output: [u8; 32] = hasher.finalize().into();
146
147 let n = U256::from_be_bytes(output);
148 let n: U256 = n >> 8;
150
151 let field_element = Fq::from_bigint(n.into());
152
153 match field_element {
154 Some(element) => Self(element),
155 None => unreachable!(
156 "due to the byte reduction, the value is guaranteed to be within the field"
157 ),
158 }
159
160 }
162
163 #[must_use]
165 pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
166 let field_element = Fq::rand(rng);
167 Self(field_element)
168 }
169
170 pub fn to_u256(&self) -> U256 {
172 self.0.into()
173 }
174}
175
176impl Deref for FieldElement {
177 type Target = Fq;
178 fn deref(&self) -> &Self::Target {
179 &self.0
180 }
181}
182
183impl DerefMut for FieldElement {
184 fn deref_mut(&mut self) -> &mut Self::Target {
185 &mut self.0
186 }
187}
188
189impl FromStr for FieldElement {
190 type Err = PrimitiveError;
191
192 fn from_str(s: &str) -> Result<Self, Self::Err> {
198 let s = s.trim_start_matches("0x");
199 let bytes = hex::decode(s)
200 .map_err(|e| PrimitiveError::Deserialization(format!("Invalid hex encoding: {e}")))?;
201 let bytes: [u8; 32] = bytes
202 .try_into()
203 .map_err(|_| PrimitiveError::Deserialization("expected 32 bytes".to_string()))?;
204 Self::from_be_bytes(&bytes)
205 }
206}
207
208impl fmt::Display for FieldElement {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 write!(f, "0x{}", hex::encode(self.to_be_bytes()))
211 }
212}
213
214impl From<Fq> for FieldElement {
215 fn from(value: Fq) -> Self {
216 Self(value)
217 }
218}
219
220impl TryFrom<U256> for FieldElement {
221 type Error = PrimitiveError;
222 fn try_from(value: U256) -> Result<Self, Self::Error> {
223 Ok(Self(
224 value.try_into().map_err(|_| PrimitiveError::NotInField)?,
225 ))
226 }
227}
228
229impl From<U160> for FieldElement {
231 fn from(value: U160) -> Self {
232 let u256 = U256::from(value);
234 let big_int = ark_ff::BigInt(u256.into_limbs());
235 Self(ark_babyjubjub::Fq::new(big_int))
236 }
237}
238
239impl From<FieldElement> for U256 {
240 fn from(value: FieldElement) -> Self {
241 <Self as From<Fq>>::from(value.0)
242 }
243}
244
245impl From<u64> for FieldElement {
246 fn from(value: u64) -> Self {
247 Self(Fq::from(value))
248 }
249}
250
251impl From<u128> for FieldElement {
252 fn from(value: u128) -> Self {
253 Self(Fq::from(value))
254 }
255}
256
257impl TryFrom<FieldElement> for u64 {
258 type Error = PrimitiveError;
259 fn try_from(value: FieldElement) -> Result<Self, Self::Error> {
260 let u256 = <U256 as From<Fq>>::from(value.0);
261 u256.try_into().map_err(|_| PrimitiveError::OutOfBounds)
262 }
263}
264
265impl TryFrom<FieldElement> for usize {
266 type Error = PrimitiveError;
267 fn try_from(value: FieldElement) -> Result<Self, Self::Error> {
268 let u256 = <U256 as From<Fq>>::from(value.0);
269 u256.try_into().map_err(|_| PrimitiveError::OutOfBounds)
270 }
271}
272
273impl Serialize for FieldElement {
274 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
275 where
276 S: Serializer,
277 {
278 if serializer.is_human_readable() {
279 serializer.serialize_str(&self.to_string())
280 } else {
281 serializer.serialize_bytes(&self.to_be_bytes())
282 }
283 }
284}
285
286impl<'de> Deserialize<'de> for FieldElement {
287 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
288 where
289 D: Deserializer<'de>,
290 {
291 if deserializer.is_human_readable() {
292 let s = String::deserialize(deserializer)?;
293 Self::from_str(&s).map_err(D::Error::custom)
294 } else {
295 let bytes = Vec::<u8>::deserialize(deserializer)?;
296 let bytes: [u8; 32] = bytes
297 .try_into()
298 .map_err(|_| D::Error::custom("expected 32 bytes"))?;
299 Self::from_be_bytes(&bytes).map_err(D::Error::custom)
300 }
301 }
302}
303
304#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
306pub enum PrimitiveError {
307 #[error("Serialization error: {0}")]
309 Serialization(String),
310 #[error("Deserialization error: {0}")]
312 Deserialization(String),
313 #[error("Provided value is not in the field")]
315 NotInField,
316 #[error("Provided index is out of bounds")]
318 OutOfBounds,
319 #[error("Invalid input at {attribute}: {reason}")]
321 InvalidInput {
322 attribute: String,
324 reason: String,
326 },
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use ruint::uint;
333
334 #[test]
335 fn test_field_element_encoding() {
336 let root = FieldElement::try_from(uint!(
337 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
338 ))
339 .unwrap();
340
341 assert_eq!(
342 serde_json::to_string(&root).unwrap(),
343 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
344 );
345
346 assert_eq!(
347 root.to_string(),
348 "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
349 );
350
351 let fe = FieldElement::ONE;
352 assert_eq!(
353 serde_json::to_string(&fe).unwrap(),
354 "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
355 );
356
357 let md = FieldElement::ZERO;
358 assert_eq!(
359 serde_json::to_string(&md).unwrap(),
360 "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
361 );
362
363 assert_eq!(*FieldElement::ONE, Fq::ONE);
364 }
365
366 #[test]
367 fn test_field_element_decoding() {
368 let root = FieldElement::try_from(uint!(
369 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
370 ))
371 .unwrap();
372
373 assert_eq!(
374 serde_json::from_str::<FieldElement>(
375 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
376 )
377 .unwrap(),
378 root
379 );
380
381 assert_eq!(
382 FieldElement::from_str(
383 "0x0000000000000000000000000000000000000000000000000000000000000001"
384 )
385 .unwrap(),
386 FieldElement::ONE
387 );
388 }
389
390 #[test]
391 fn test_simple_bytes_encoding() {
392 let fe = FieldElement::ONE;
393 let bytes = fe.to_be_bytes();
394 let mut expected = [0u8; 32];
395 expected[31] = 1;
396 assert_eq!(bytes, expected);
397
398 let reversed = FieldElement::from_be_bytes(&bytes).unwrap();
399 assert_eq!(reversed, fe);
400 }
401
402 #[test]
403 fn test_field_element_cbor_encoding_roundtrip() {
404 let root = FieldElement::try_from(uint!(
405 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
406 ))
407 .unwrap();
408
409 let mut buffer = Vec::new();
410 ciborium::into_writer(&root, &mut buffer).unwrap();
411
412 let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
413
414 assert_eq!(root, decoded);
415 }
416
417 #[test]
418 fn test_field_element_binary_encoding_format() {
419 let root = FieldElement::try_from(uint!(
420 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
421 ))
422 .unwrap();
423
424 let mut buffer = Vec::new();
426 ciborium::into_writer(&root, &mut buffer).unwrap();
427
428 assert_eq!(buffer.len(), 34); assert_eq!(buffer[0], 0x58); assert_eq!(buffer[1], 0x20); let field_bytes = &buffer[2..];
433 assert_eq!(field_bytes.len(), 32);
434
435 let expected_be_bytes =
436 hex::decode("11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2")
437 .unwrap();
438 assert_eq!(field_bytes, expected_be_bytes.as_slice());
439 }
440
441 #[test]
442 fn test_to_be_bytes_from_be_bytes_roundtrip() {
443 let values = [
444 FieldElement::ZERO,
445 FieldElement::ONE,
446 FieldElement::from(255u64),
447 FieldElement::from(u64::MAX),
448 FieldElement::from(u128::MAX),
449 FieldElement::try_from(uint!(
450 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
451 ))
452 .unwrap(),
453 ];
454 for fe in values {
455 let bytes = fe.to_be_bytes();
456 let recovered = FieldElement::from_be_bytes(&bytes).unwrap();
457 assert_eq!(fe, recovered);
458 }
459 }
460
461 #[test]
465 fn test_from_be_bytes_rejects_value_above_modulus() {
466 let bytes = [0xFF; 32];
468 assert_eq!(
469 FieldElement::from_be_bytes(&bytes),
470 Err(PrimitiveError::NotInField)
471 );
472 }
473
474 #[test]
475 fn test_from_str_rejects_wrong_length() {
476 assert!(FieldElement::from_str("0x01").is_err());
478 assert!(
480 FieldElement::from_str(
481 "0x000000000000000000000000000000000000000000000000000000000000000001"
482 )
483 .is_err()
484 );
485 assert!(
487 FieldElement::from_str(
488 "0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
489 )
490 .is_err()
491 );
492 }
493
494 #[test]
495 fn test_display_from_str_roundtrip() {
496 let fe = FieldElement::try_from(uint!(
497 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
498 ))
499 .unwrap();
500 let s = fe.to_string();
501 assert_eq!(FieldElement::from_str(&s).unwrap(), fe);
502 }
503
504 #[test]
505 fn test_json_cbor_consistency() {
506 let fe = FieldElement::try_from(uint!(
509 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
510 ))
511 .unwrap();
512
513 let json_str = serde_json::to_string(&fe).unwrap();
514 let from_json: FieldElement = serde_json::from_str(&json_str).unwrap();
515
516 let mut cbor_buf = Vec::new();
517 ciborium::into_writer(&fe, &mut cbor_buf).unwrap();
518 let from_cbor: FieldElement = ciborium::from_reader(&cbor_buf[..]).unwrap();
519
520 assert_eq!(from_json, from_cbor);
521 }
522
523 #[test]
524 fn test_to_be_bytes_is_big_endian() {
525 let fe = FieldElement::from(1u64);
526 let bytes = fe.to_be_bytes();
527 assert_eq!(bytes[31], 1); assert_eq!(bytes[..31], [0u8; 31]);
529
530 let fe256 = FieldElement::from(256u64);
531 let bytes = fe256.to_be_bytes();
532 assert_eq!(bytes[30], 1);
533 assert_eq!(bytes[31], 0);
534 }
535
536 #[test]
537 fn test_u256_roundtrip() {
538 let original =
539 uint!(0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256);
540 let fe = FieldElement::try_from(original).unwrap();
541 let back: U256 = fe.into();
542 assert_eq!(original, back);
543 }
544}