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