1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
7#![deny(clippy::all, clippy::nursery, missing_docs, dead_code)]
8#![allow(clippy::option_if_let_else)]
9
10use alloy_primitives::Keccak256;
11
12use ark_babyjubjub::Fq;
13use ark_ff::{AdditiveGroup, Field, PrimeField, UniformRand};
14use ruint::aliases::{U160, U256};
15use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
16use std::{
17 fmt,
18 ops::{Deref, DerefMut},
19 str::FromStr,
20};
21
22pub mod authenticator;
24
25mod config;
27pub use config::Config;
28
29pub mod circuit_inputs;
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
50pub mod nullifier;
52pub use nullifier::SessionNullifier;
53
54pub mod proof;
56pub use proof::ZeroKnowledgeProof;
57
58pub mod rp;
60
61pub mod serde_utils;
62
63mod signer;
65pub use signer::Signer;
66
67pub mod request;
69pub use request::{
70 ConstraintExpr, ConstraintKind, ConstraintNode, MAX_CONSTRAINT_NODES, ProofRequest,
71 ProofResponse, RequestItem, RequestVersion, ResponseItem, ValidationError,
72};
73
74pub type ScalarField = ark_babyjubjub::Fr;
78
79pub const TREE_DEPTH: usize = 30;
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
90pub struct FieldElement(Fq);
91
92impl FieldElement {
93 pub const ZERO: Self = Self(Fq::ZERO);
95 pub const ONE: Self = Self(Fq::ONE);
97
98 #[must_use]
100 pub fn to_be_bytes(&self) -> [u8; 32] {
101 let as_num: U256 = self.0.into();
102 as_num.to_be_bytes()
103 }
104
105 pub fn from_be_bytes(be_bytes: &[u8; 32]) -> Result<Self, PrimitiveError> {
112 U256::from_be_bytes(*be_bytes).try_into()
113 }
114
115 #[must_use]
120 pub(crate) fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
121 let field_element = Fq::from_be_bytes_mod_order(bytes);
122 Self(field_element)
123 }
124
125 #[must_use]
128 pub fn from_arbitrary_raw_bytes(bytes: &[u8]) -> Self {
129 let mut hasher = Keccak256::new();
130 hasher.update(bytes);
131 let output: [u8; 32] = hasher.finalize().into();
132
133 let n = U256::from_be_bytes(output);
134 let n: U256 = n >> 8;
136
137 let field_element = Fq::from_bigint(n.into());
138
139 match field_element {
140 Some(element) => Self(element),
141 None => unreachable!(
142 "due to the byte reduction, the value is guaranteed to be within the field"
143 ),
144 }
145
146 }
148
149 #[must_use]
151 pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
152 let field_element = Fq::rand(rng);
153 Self(field_element)
154 }
155}
156
157impl Deref for FieldElement {
158 type Target = Fq;
159 fn deref(&self) -> &Self::Target {
160 &self.0
161 }
162}
163
164impl DerefMut for FieldElement {
165 fn deref_mut(&mut self) -> &mut Self::Target {
166 &mut self.0
167 }
168}
169
170impl FromStr for FieldElement {
171 type Err = PrimitiveError;
172
173 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 let s = s.trim_start_matches("0x");
180 let bytes = hex::decode(s)
181 .map_err(|e| PrimitiveError::Deserialization(format!("Invalid hex encoding: {e}")))?;
182 let bytes: [u8; 32] = bytes
183 .try_into()
184 .map_err(|_| PrimitiveError::Deserialization("expected 32 bytes".to_string()))?;
185 Self::from_be_bytes(&bytes)
186 }
187}
188
189impl fmt::Display for FieldElement {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "0x{}", hex::encode(self.to_be_bytes()))
192 }
193}
194
195impl From<Fq> for FieldElement {
196 fn from(value: Fq) -> Self {
197 Self(value)
198 }
199}
200
201impl TryFrom<U256> for FieldElement {
202 type Error = PrimitiveError;
203 fn try_from(value: U256) -> Result<Self, Self::Error> {
204 Ok(Self(
205 value.try_into().map_err(|_| PrimitiveError::NotInField)?,
206 ))
207 }
208}
209
210impl From<U160> for FieldElement {
212 fn from(value: U160) -> Self {
213 let u256 = U256::from(value);
215 let big_int = ark_ff::BigInt(u256.into_limbs());
216 Self(ark_babyjubjub::Fq::new(big_int))
217 }
218}
219
220impl From<FieldElement> for U256 {
221 fn from(value: FieldElement) -> Self {
222 <Self as From<Fq>>::from(value.0)
223 }
224}
225
226impl From<u64> for FieldElement {
227 fn from(value: u64) -> Self {
228 Self(Fq::from(value))
229 }
230}
231
232impl From<u128> for FieldElement {
233 fn from(value: u128) -> Self {
234 Self(Fq::from(value))
235 }
236}
237
238impl Serialize for FieldElement {
239 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
240 where
241 S: Serializer,
242 {
243 if serializer.is_human_readable() {
244 serializer.serialize_str(&self.to_string())
245 } else {
246 serializer.serialize_bytes(&self.to_be_bytes())
247 }
248 }
249}
250
251impl<'de> Deserialize<'de> for FieldElement {
252 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
253 where
254 D: Deserializer<'de>,
255 {
256 if deserializer.is_human_readable() {
257 let s = String::deserialize(deserializer)?;
258 Self::from_str(&s).map_err(D::Error::custom)
259 } else {
260 let bytes = Vec::<u8>::deserialize(deserializer)?;
261 let bytes: [u8; 32] = bytes
262 .try_into()
263 .map_err(|_| D::Error::custom("expected 32 bytes"))?;
264 Self::from_be_bytes(&bytes).map_err(D::Error::custom)
265 }
266 }
267}
268
269#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
271pub enum PrimitiveError {
272 #[error("Serialization error: {0}")]
274 Serialization(String),
275 #[error("Deserialization error: {0}")]
277 Deserialization(String),
278 #[error("Provided value is not in the field")]
280 NotInField,
281 #[error("Provided index is out of bounds")]
283 OutOfBounds,
284 #[error("Invalid input at {attribute}: {reason}")]
286 InvalidInput {
287 attribute: String,
289 reason: String,
291 },
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use ruint::uint;
298
299 #[test]
300 fn test_field_element_encoding() {
301 let root = FieldElement::try_from(uint!(
302 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
303 ))
304 .unwrap();
305
306 assert_eq!(
307 serde_json::to_string(&root).unwrap(),
308 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
309 );
310
311 assert_eq!(
312 root.to_string(),
313 "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
314 );
315
316 let fe = FieldElement::ONE;
317 assert_eq!(
318 serde_json::to_string(&fe).unwrap(),
319 "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
320 );
321
322 let md = FieldElement::ZERO;
323 assert_eq!(
324 serde_json::to_string(&md).unwrap(),
325 "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
326 );
327
328 assert_eq!(*FieldElement::ONE, Fq::ONE);
329 }
330
331 #[test]
332 fn test_field_element_decoding() {
333 let root = FieldElement::try_from(uint!(
334 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
335 ))
336 .unwrap();
337
338 assert_eq!(
339 serde_json::from_str::<FieldElement>(
340 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
341 )
342 .unwrap(),
343 root
344 );
345
346 assert_eq!(
347 FieldElement::from_str(
348 "0x0000000000000000000000000000000000000000000000000000000000000001"
349 )
350 .unwrap(),
351 FieldElement::ONE
352 );
353 }
354
355 #[test]
356 fn test_simple_bytes_encoding() {
357 let fe = FieldElement::ONE;
358 let bytes = fe.to_be_bytes();
359 let mut expected = [0u8; 32];
360 expected[31] = 1;
361 assert_eq!(bytes, expected);
362
363 let reversed = FieldElement::from_be_bytes(&bytes).unwrap();
364 assert_eq!(reversed, fe);
365 }
366
367 #[test]
368 fn test_field_element_cbor_encoding_roundtrip() {
369 let root = FieldElement::try_from(uint!(
370 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
371 ))
372 .unwrap();
373
374 let mut buffer = Vec::new();
375 ciborium::into_writer(&root, &mut buffer).unwrap();
376
377 let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
378
379 assert_eq!(root, decoded);
380 }
381
382 #[test]
383 fn test_field_element_binary_encoding_format() {
384 let root = FieldElement::try_from(uint!(
385 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
386 ))
387 .unwrap();
388
389 let mut buffer = Vec::new();
391 ciborium::into_writer(&root, &mut buffer).unwrap();
392
393 assert_eq!(buffer.len(), 34); assert_eq!(buffer[0], 0x58); assert_eq!(buffer[1], 0x20); let field_bytes = &buffer[2..];
398 assert_eq!(field_bytes.len(), 32);
399
400 let expected_be_bytes =
401 hex::decode("11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2")
402 .unwrap();
403 assert_eq!(field_bytes, expected_be_bytes.as_slice());
404 }
405
406 #[test]
407 fn test_to_be_bytes_from_be_bytes_roundtrip() {
408 let values = [
409 FieldElement::ZERO,
410 FieldElement::ONE,
411 FieldElement::from(255u64),
412 FieldElement::from(u64::MAX),
413 FieldElement::from(u128::MAX),
414 FieldElement::try_from(uint!(
415 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
416 ))
417 .unwrap(),
418 ];
419 for fe in values {
420 let bytes = fe.to_be_bytes();
421 let recovered = FieldElement::from_be_bytes(&bytes).unwrap();
422 assert_eq!(fe, recovered);
423 }
424 }
425
426 #[test]
427 fn test_from_be_bytes_rejects_value_above_modulus() {
428 let bytes = [0xFF; 32];
430 assert_eq!(
431 FieldElement::from_be_bytes(&bytes),
432 Err(PrimitiveError::NotInField)
433 );
434 }
435
436 #[test]
437 fn test_from_str_rejects_wrong_length() {
438 assert!(FieldElement::from_str("0x01").is_err());
440 assert!(
442 FieldElement::from_str(
443 "0x000000000000000000000000000000000000000000000000000000000000000001"
444 )
445 .is_err()
446 );
447 assert!(
449 FieldElement::from_str(
450 "0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
451 )
452 .is_err()
453 );
454 }
455
456 #[test]
457 fn test_display_from_str_roundtrip() {
458 let fe = FieldElement::try_from(uint!(
459 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
460 ))
461 .unwrap();
462 let s = fe.to_string();
463 assert_eq!(FieldElement::from_str(&s).unwrap(), fe);
464 }
465
466 #[test]
467 fn test_json_cbor_consistency() {
468 let fe = FieldElement::try_from(uint!(
471 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
472 ))
473 .unwrap();
474
475 let json_str = serde_json::to_string(&fe).unwrap();
476 let from_json: FieldElement = serde_json::from_str(&json_str).unwrap();
477
478 let mut cbor_buf = Vec::new();
479 ciborium::into_writer(&fe, &mut cbor_buf).unwrap();
480 let from_cbor: FieldElement = ciborium::from_reader(&cbor_buf[..]).unwrap();
481
482 assert_eq!(from_json, from_cbor);
483 }
484
485 #[test]
486 fn test_to_be_bytes_is_big_endian() {
487 let fe = FieldElement::from(1u64);
488 let bytes = fe.to_be_bytes();
489 assert_eq!(bytes[31], 1); assert_eq!(bytes[..31], [0u8; 31]);
491
492 let fe256 = FieldElement::from(256u64);
493 let bytes = fe256.to_be_bytes();
494 assert_eq!(bytes[30], 1);
495 assert_eq!(bytes[31], 0);
496 }
497
498 #[test]
499 fn test_u256_roundtrip() {
500 let original =
501 uint!(0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256);
502 let fe = FieldElement::try_from(original).unwrap();
503 let back: U256 = fe.into();
504 assert_eq!(original, back);
505 }
506}