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