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 poseidon;
35pub use poseidon::{DomainSeparator, VariableLengthDomainSeparator};
36
37pub mod sponge;
39
40pub mod credential;
42pub use credential::{Credential, CredentialVersion};
43
44pub mod merkle;
46
47pub mod api_types;
49
50pub mod oprf;
52pub use oprf::{OprfPrefix, OprfPrefixedFieldElement};
53
54mod nullifier;
56pub use nullifier::Nullifier;
57
58mod session;
60pub use session::{SessionId, SessionNullifier, SessionRef};
61
62pub mod proof;
64pub use proof::{OwnershipProof, ZeroKnowledgeProof};
65
66pub mod rp;
68
69pub mod serde_utils;
70
71mod signer;
73pub use signer::Signer;
74
75pub mod request;
77pub use request::{
78 ConstraintExpr, ConstraintKind, ConstraintNode, MAX_CONSTRAINT_NODES, ProofRequest,
79 ProofResponse, ProofType, RequestItem, RequestVersion, ResponseItem, ValidationError,
80};
81
82pub use eddsa_babyjubjub::{EdDSAPrivateKey, EdDSAPublicKey, EdDSASignature};
83pub use taceo_oprf::types::{OprfKeyId, ShareEpoch};
84
85pub type ScalarField = ark_babyjubjub::Fr;
89
90pub const TREE_DEPTH: usize = 30;
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
101pub struct FieldElement(Fq);
102
103impl FieldElement {
104 pub const ZERO: Self = Self(Fq::ZERO);
106 pub const ONE: Self = Self(Fq::ONE);
108
109 #[must_use]
111 pub fn to_be_bytes(&self) -> [u8; 32] {
112 let as_num: U256 = self.to_u256();
113 as_num.to_be_bytes()
114 }
115
116 pub fn from_be_bytes(be_bytes: &[u8; 32]) -> Result<Self, PrimitiveError> {
123 U256::from_be_bytes(*be_bytes).try_into()
124 }
125
126 #[must_use]
139 pub(crate) fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
140 let field_element = Fq::from_be_bytes_mod_order(bytes);
141 Self(field_element)
142 }
143
144 #[must_use]
147 pub fn from_arbitrary_raw_bytes(bytes: &[u8]) -> Self {
148 let mut hasher = Keccak256::new();
149 hasher.update(bytes);
150 let output: [u8; 32] = hasher.finalize().into();
151
152 let n = U256::from_be_bytes(output);
153 let n: U256 = n >> 8;
155
156 let field_element = Fq::from_bigint(n.into());
157
158 match field_element {
159 Some(element) => Self(element),
160 None => unreachable!(
161 "due to the byte reduction, the value is guaranteed to be within the field"
162 ),
163 }
164
165 }
167
168 #[must_use]
170 pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
171 let field_element = Fq::rand(rng);
172 Self(field_element)
173 }
174
175 pub fn to_u256(&self) -> U256 {
177 self.0.into()
178 }
179}
180
181impl Deref for FieldElement {
182 type Target = Fq;
183 fn deref(&self) -> &Self::Target {
184 &self.0
185 }
186}
187
188impl DerefMut for FieldElement {
189 fn deref_mut(&mut self) -> &mut Self::Target {
190 &mut self.0
191 }
192}
193
194impl FromStr for FieldElement {
195 type Err = PrimitiveError;
196
197 fn from_str(s: &str) -> Result<Self, Self::Err> {
203 let s = s.trim_start_matches("0x");
204 let bytes = hex::decode(s)
205 .map_err(|e| PrimitiveError::Deserialization(format!("Invalid hex encoding: {e}")))?;
206 let bytes: [u8; 32] = bytes
207 .try_into()
208 .map_err(|_| PrimitiveError::Deserialization("expected 32 bytes".to_string()))?;
209 Self::from_be_bytes(&bytes)
210 }
211}
212
213impl fmt::Display for FieldElement {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "0x{}", hex::encode(self.to_be_bytes()))
216 }
217}
218
219impl From<Fq> for FieldElement {
220 fn from(value: Fq) -> Self {
221 Self(value)
222 }
223}
224
225impl TryFrom<U256> for FieldElement {
226 type Error = PrimitiveError;
227 fn try_from(value: U256) -> Result<Self, Self::Error> {
228 Ok(Self(
229 value.try_into().map_err(|_| PrimitiveError::NotInField)?,
230 ))
231 }
232}
233
234impl From<U160> for FieldElement {
236 fn from(value: U160) -> Self {
237 let u256 = U256::from(value);
239 let big_int = ark_ff::BigInt(u256.into_limbs());
240 Self(ark_babyjubjub::Fq::new(big_int))
241 }
242}
243
244impl From<FieldElement> for U256 {
245 fn from(value: FieldElement) -> Self {
246 <Self as From<Fq>>::from(value.0)
247 }
248}
249
250impl From<u64> for FieldElement {
251 fn from(value: u64) -> Self {
252 Self(Fq::from(value))
253 }
254}
255
256impl From<u128> for FieldElement {
257 fn from(value: u128) -> Self {
258 Self(Fq::from(value))
259 }
260}
261
262impl TryFrom<FieldElement> for u64 {
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 TryFrom<FieldElement> for usize {
271 type Error = PrimitiveError;
272 fn try_from(value: FieldElement) -> Result<Self, Self::Error> {
273 let u256 = <U256 as From<Fq>>::from(value.0);
274 u256.try_into().map_err(|_| PrimitiveError::OutOfBounds)
275 }
276}
277
278impl Serialize for FieldElement {
279 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
280 where
281 S: Serializer,
282 {
283 if serializer.is_human_readable() {
284 serializer.serialize_str(&self.to_string())
285 } else {
286 serializer.serialize_bytes(&self.to_be_bytes())
287 }
288 }
289}
290
291impl<'de> Deserialize<'de> for FieldElement {
292 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
293 where
294 D: Deserializer<'de>,
295 {
296 if deserializer.is_human_readable() {
297 let s = String::deserialize(deserializer)?;
298 Self::from_str(&s).map_err(D::Error::custom)
299 } else {
300 let bytes = Vec::<u8>::deserialize(deserializer)?;
301 let bytes: [u8; 32] = bytes
302 .try_into()
303 .map_err(|_| D::Error::custom("expected 32 bytes"))?;
304 Self::from_be_bytes(&bytes).map_err(D::Error::custom)
305 }
306 }
307}
308
309#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
311pub enum PrimitiveError {
312 #[error("Serialization error: {0}")]
314 Serialization(String),
315 #[error("Deserialization error: {0}")]
317 Deserialization(String),
318 #[error("Provided value is not in the field")]
320 NotInField,
321 #[error("Provided index is out of bounds")]
323 OutOfBounds,
324 #[error("Invalid input at {attribute}: {reason}")]
326 InvalidInput {
327 attribute: String,
329 reason: String,
331 },
332 #[error("Session ID commitment does not match the derived commitment")]
334 SessionIdCommitmentMismatch,
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use ruint::uint;
341
342 #[test]
343 fn test_field_element_encoding() {
344 let root = FieldElement::try_from(uint!(
345 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
346 ))
347 .unwrap();
348
349 assert_eq!(
350 serde_json::to_string(&root).unwrap(),
351 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
352 );
353
354 assert_eq!(
355 root.to_string(),
356 "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
357 );
358
359 let fe = FieldElement::ONE;
360 assert_eq!(
361 serde_json::to_string(&fe).unwrap(),
362 "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
363 );
364
365 let md = FieldElement::ZERO;
366 assert_eq!(
367 serde_json::to_string(&md).unwrap(),
368 "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
369 );
370
371 assert_eq!(*FieldElement::ONE, Fq::ONE);
372 }
373
374 #[test]
375 fn test_field_element_decoding() {
376 let root = FieldElement::try_from(uint!(
377 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
378 ))
379 .unwrap();
380
381 assert_eq!(
382 serde_json::from_str::<FieldElement>(
383 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
384 )
385 .unwrap(),
386 root
387 );
388
389 assert_eq!(
390 FieldElement::from_str(
391 "0x0000000000000000000000000000000000000000000000000000000000000001"
392 )
393 .unwrap(),
394 FieldElement::ONE
395 );
396 }
397
398 #[test]
399 fn test_simple_bytes_encoding() {
400 let fe = FieldElement::ONE;
401 let bytes = fe.to_be_bytes();
402 let mut expected = [0u8; 32];
403 expected[31] = 1;
404 assert_eq!(bytes, expected);
405
406 let reversed = FieldElement::from_be_bytes(&bytes).unwrap();
407 assert_eq!(reversed, fe);
408 }
409
410 #[test]
411 fn test_field_element_cbor_encoding_roundtrip() {
412 let root = FieldElement::try_from(uint!(
413 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
414 ))
415 .unwrap();
416
417 let mut buffer = Vec::new();
418 ciborium::into_writer(&root, &mut buffer).unwrap();
419
420 let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
421
422 assert_eq!(root, decoded);
423 }
424
425 #[test]
426 fn test_field_element_binary_encoding_format() {
427 let root = FieldElement::try_from(uint!(
428 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
429 ))
430 .unwrap();
431
432 let mut buffer = Vec::new();
434 ciborium::into_writer(&root, &mut buffer).unwrap();
435
436 assert_eq!(buffer.len(), 34); assert_eq!(buffer[0], 0x58); assert_eq!(buffer[1], 0x20); let field_bytes = &buffer[2..];
441 assert_eq!(field_bytes.len(), 32);
442
443 let expected_be_bytes =
444 hex::decode("11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2")
445 .unwrap();
446 assert_eq!(field_bytes, expected_be_bytes.as_slice());
447 }
448
449 #[test]
450 fn test_to_be_bytes_from_be_bytes_roundtrip() {
451 let values = [
452 FieldElement::ZERO,
453 FieldElement::ONE,
454 FieldElement::from(255u64),
455 FieldElement::from(u64::MAX),
456 FieldElement::from(u128::MAX),
457 FieldElement::try_from(uint!(
458 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
459 ))
460 .unwrap(),
461 ];
462 for fe in values {
463 let bytes = fe.to_be_bytes();
464 let recovered = FieldElement::from_be_bytes(&bytes).unwrap();
465 assert_eq!(fe, recovered);
466 }
467 }
468
469 #[test]
473 fn test_from_be_bytes_rejects_value_above_modulus() {
474 let bytes = [0xFF; 32];
476 assert_eq!(
477 FieldElement::from_be_bytes(&bytes),
478 Err(PrimitiveError::NotInField)
479 );
480 }
481
482 #[test]
483 fn test_from_str_rejects_wrong_length() {
484 assert!(FieldElement::from_str("0x01").is_err());
486 assert!(
488 FieldElement::from_str(
489 "0x000000000000000000000000000000000000000000000000000000000000000001"
490 )
491 .is_err()
492 );
493 assert!(
495 FieldElement::from_str(
496 "0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
497 )
498 .is_err()
499 );
500 }
501
502 #[test]
503 fn test_display_from_str_roundtrip() {
504 let fe = FieldElement::try_from(uint!(
505 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
506 ))
507 .unwrap();
508 let s = fe.to_string();
509 assert_eq!(FieldElement::from_str(&s).unwrap(), fe);
510 }
511
512 #[test]
513 fn test_json_cbor_consistency() {
514 let fe = FieldElement::try_from(uint!(
517 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
518 ))
519 .unwrap();
520
521 let json_str = serde_json::to_string(&fe).unwrap();
522 let from_json: FieldElement = serde_json::from_str(&json_str).unwrap();
523
524 let mut cbor_buf = Vec::new();
525 ciborium::into_writer(&fe, &mut cbor_buf).unwrap();
526 let from_cbor: FieldElement = ciborium::from_reader(&cbor_buf[..]).unwrap();
527
528 assert_eq!(from_json, from_cbor);
529 }
530
531 #[test]
532 fn test_to_be_bytes_is_big_endian() {
533 let fe = FieldElement::from(1u64);
534 let bytes = fe.to_be_bytes();
535 assert_eq!(bytes[31], 1); assert_eq!(bytes[..31], [0u8; 31]);
537
538 let fe256 = FieldElement::from(256u64);
539 let bytes = fe256.to_be_bytes();
540 assert_eq!(bytes[30], 1);
541 assert_eq!(bytes[31], 0);
542 }
543
544 #[test]
545 fn test_u256_roundtrip() {
546 let original =
547 uint!(0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256);
548 let fe = FieldElement::try_from(original).unwrap();
549 let back: U256 = fe.into();
550 assert_eq!(original, back);
551 }
552}