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 use eddsa_babyjubjub::{EdDSAPrivateKey, EdDSAPublicKey, EdDSASignature};
75
76pub type ScalarField = ark_babyjubjub::Fr;
80
81pub const TREE_DEPTH: usize = 30;
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
92pub struct FieldElement(Fq);
93
94impl FieldElement {
95 pub const ZERO: Self = Self(Fq::ZERO);
97 pub const ONE: Self = Self(Fq::ONE);
99
100 #[must_use]
102 pub fn to_be_bytes(&self) -> [u8; 32] {
103 let as_num: U256 = self.0.into();
104 as_num.to_be_bytes()
105 }
106
107 pub fn from_be_bytes(be_bytes: &[u8; 32]) -> Result<Self, PrimitiveError> {
114 U256::from_be_bytes(*be_bytes).try_into()
115 }
116
117 #[must_use]
122 pub(crate) fn from_be_bytes_mod_order(bytes: &[u8]) -> Self {
123 let field_element = Fq::from_be_bytes_mod_order(bytes);
124 Self(field_element)
125 }
126
127 #[must_use]
130 pub fn from_arbitrary_raw_bytes(bytes: &[u8]) -> Self {
131 let mut hasher = Keccak256::new();
132 hasher.update(bytes);
133 let output: [u8; 32] = hasher.finalize().into();
134
135 let n = U256::from_be_bytes(output);
136 let n: U256 = n >> 8;
138
139 let field_element = Fq::from_bigint(n.into());
140
141 match field_element {
142 Some(element) => Self(element),
143 None => unreachable!(
144 "due to the byte reduction, the value is guaranteed to be within the field"
145 ),
146 }
147
148 }
150
151 #[must_use]
153 pub fn random<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> Self {
154 let field_element = Fq::rand(rng);
155 Self(field_element)
156 }
157}
158
159impl Deref for FieldElement {
160 type Target = Fq;
161 fn deref(&self) -> &Self::Target {
162 &self.0
163 }
164}
165
166impl DerefMut for FieldElement {
167 fn deref_mut(&mut self) -> &mut Self::Target {
168 &mut self.0
169 }
170}
171
172impl FromStr for FieldElement {
173 type Err = PrimitiveError;
174
175 fn from_str(s: &str) -> Result<Self, Self::Err> {
181 let s = s.trim_start_matches("0x");
182 let bytes = hex::decode(s)
183 .map_err(|e| PrimitiveError::Deserialization(format!("Invalid hex encoding: {e}")))?;
184 let bytes: [u8; 32] = bytes
185 .try_into()
186 .map_err(|_| PrimitiveError::Deserialization("expected 32 bytes".to_string()))?;
187 Self::from_be_bytes(&bytes)
188 }
189}
190
191impl fmt::Display for FieldElement {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "0x{}", hex::encode(self.to_be_bytes()))
194 }
195}
196
197impl From<Fq> for FieldElement {
198 fn from(value: Fq) -> Self {
199 Self(value)
200 }
201}
202
203impl TryFrom<U256> for FieldElement {
204 type Error = PrimitiveError;
205 fn try_from(value: U256) -> Result<Self, Self::Error> {
206 Ok(Self(
207 value.try_into().map_err(|_| PrimitiveError::NotInField)?,
208 ))
209 }
210}
211
212impl From<U160> for FieldElement {
214 fn from(value: U160) -> Self {
215 let u256 = U256::from(value);
217 let big_int = ark_ff::BigInt(u256.into_limbs());
218 Self(ark_babyjubjub::Fq::new(big_int))
219 }
220}
221
222impl From<FieldElement> for U256 {
223 fn from(value: FieldElement) -> Self {
224 <Self as From<Fq>>::from(value.0)
225 }
226}
227
228impl From<u64> for FieldElement {
229 fn from(value: u64) -> Self {
230 Self(Fq::from(value))
231 }
232}
233
234impl From<u128> for FieldElement {
235 fn from(value: u128) -> Self {
236 Self(Fq::from(value))
237 }
238}
239
240impl Serialize for FieldElement {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: Serializer,
244 {
245 if serializer.is_human_readable() {
246 serializer.serialize_str(&self.to_string())
247 } else {
248 serializer.serialize_bytes(&self.to_be_bytes())
249 }
250 }
251}
252
253impl<'de> Deserialize<'de> for FieldElement {
254 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
255 where
256 D: Deserializer<'de>,
257 {
258 if deserializer.is_human_readable() {
259 let s = String::deserialize(deserializer)?;
260 Self::from_str(&s).map_err(D::Error::custom)
261 } else {
262 let bytes = Vec::<u8>::deserialize(deserializer)?;
263 let bytes: [u8; 32] = bytes
264 .try_into()
265 .map_err(|_| D::Error::custom("expected 32 bytes"))?;
266 Self::from_be_bytes(&bytes).map_err(D::Error::custom)
267 }
268 }
269}
270
271#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
273pub enum PrimitiveError {
274 #[error("Serialization error: {0}")]
276 Serialization(String),
277 #[error("Deserialization error: {0}")]
279 Deserialization(String),
280 #[error("Provided value is not in the field")]
282 NotInField,
283 #[error("Provided index is out of bounds")]
285 OutOfBounds,
286 #[error("Invalid input at {attribute}: {reason}")]
288 InvalidInput {
289 attribute: String,
291 reason: String,
293 },
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use ruint::uint;
300
301 #[test]
302 fn test_field_element_encoding() {
303 let root = FieldElement::try_from(uint!(
304 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
305 ))
306 .unwrap();
307
308 assert_eq!(
309 serde_json::to_string(&root).unwrap(),
310 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
311 );
312
313 assert_eq!(
314 root.to_string(),
315 "0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2"
316 );
317
318 let fe = FieldElement::ONE;
319 assert_eq!(
320 serde_json::to_string(&fe).unwrap(),
321 "\"0x0000000000000000000000000000000000000000000000000000000000000001\""
322 );
323
324 let md = FieldElement::ZERO;
325 assert_eq!(
326 serde_json::to_string(&md).unwrap(),
327 "\"0x0000000000000000000000000000000000000000000000000000000000000000\""
328 );
329
330 assert_eq!(*FieldElement::ONE, Fq::ONE);
331 }
332
333 #[test]
334 fn test_field_element_decoding() {
335 let root = FieldElement::try_from(uint!(
336 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
337 ))
338 .unwrap();
339
340 assert_eq!(
341 serde_json::from_str::<FieldElement>(
342 "\"0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2\""
343 )
344 .unwrap(),
345 root
346 );
347
348 assert_eq!(
349 FieldElement::from_str(
350 "0x0000000000000000000000000000000000000000000000000000000000000001"
351 )
352 .unwrap(),
353 FieldElement::ONE
354 );
355 }
356
357 #[test]
358 fn test_simple_bytes_encoding() {
359 let fe = FieldElement::ONE;
360 let bytes = fe.to_be_bytes();
361 let mut expected = [0u8; 32];
362 expected[31] = 1;
363 assert_eq!(bytes, expected);
364
365 let reversed = FieldElement::from_be_bytes(&bytes).unwrap();
366 assert_eq!(reversed, fe);
367 }
368
369 #[test]
370 fn test_field_element_cbor_encoding_roundtrip() {
371 let root = FieldElement::try_from(uint!(
372 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
373 ))
374 .unwrap();
375
376 let mut buffer = Vec::new();
377 ciborium::into_writer(&root, &mut buffer).unwrap();
378
379 let decoded: FieldElement = ciborium::from_reader(&buffer[..]).unwrap();
380
381 assert_eq!(root, decoded);
382 }
383
384 #[test]
385 fn test_field_element_binary_encoding_format() {
386 let root = FieldElement::try_from(uint!(
387 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
388 ))
389 .unwrap();
390
391 let mut buffer = Vec::new();
393 ciborium::into_writer(&root, &mut buffer).unwrap();
394
395 assert_eq!(buffer.len(), 34); assert_eq!(buffer[0], 0x58); assert_eq!(buffer[1], 0x20); let field_bytes = &buffer[2..];
400 assert_eq!(field_bytes.len(), 32);
401
402 let expected_be_bytes =
403 hex::decode("11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2")
404 .unwrap();
405 assert_eq!(field_bytes, expected_be_bytes.as_slice());
406 }
407
408 #[test]
409 fn test_to_be_bytes_from_be_bytes_roundtrip() {
410 let values = [
411 FieldElement::ZERO,
412 FieldElement::ONE,
413 FieldElement::from(255u64),
414 FieldElement::from(u64::MAX),
415 FieldElement::from(u128::MAX),
416 FieldElement::try_from(uint!(
417 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
418 ))
419 .unwrap(),
420 ];
421 for fe in values {
422 let bytes = fe.to_be_bytes();
423 let recovered = FieldElement::from_be_bytes(&bytes).unwrap();
424 assert_eq!(fe, recovered);
425 }
426 }
427
428 #[test]
429 fn test_from_be_bytes_rejects_value_above_modulus() {
430 let bytes = [0xFF; 32];
432 assert_eq!(
433 FieldElement::from_be_bytes(&bytes),
434 Err(PrimitiveError::NotInField)
435 );
436 }
437
438 #[test]
439 fn test_from_str_rejects_wrong_length() {
440 assert!(FieldElement::from_str("0x01").is_err());
442 assert!(
444 FieldElement::from_str(
445 "0x000000000000000000000000000000000000000000000000000000000000000001"
446 )
447 .is_err()
448 );
449 assert!(
451 FieldElement::from_str(
452 "0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
453 )
454 .is_err()
455 );
456 }
457
458 #[test]
459 fn test_display_from_str_roundtrip() {
460 let fe = FieldElement::try_from(uint!(
461 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
462 ))
463 .unwrap();
464 let s = fe.to_string();
465 assert_eq!(FieldElement::from_str(&s).unwrap(), fe);
466 }
467
468 #[test]
469 fn test_json_cbor_consistency() {
470 let fe = FieldElement::try_from(uint!(
473 0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256
474 ))
475 .unwrap();
476
477 let json_str = serde_json::to_string(&fe).unwrap();
478 let from_json: FieldElement = serde_json::from_str(&json_str).unwrap();
479
480 let mut cbor_buf = Vec::new();
481 ciborium::into_writer(&fe, &mut cbor_buf).unwrap();
482 let from_cbor: FieldElement = ciborium::from_reader(&cbor_buf[..]).unwrap();
483
484 assert_eq!(from_json, from_cbor);
485 }
486
487 #[test]
488 fn test_to_be_bytes_is_big_endian() {
489 let fe = FieldElement::from(1u64);
490 let bytes = fe.to_be_bytes();
491 assert_eq!(bytes[31], 1); assert_eq!(bytes[..31], [0u8; 31]);
493
494 let fe256 = FieldElement::from(256u64);
495 let bytes = fe256.to_be_bytes();
496 assert_eq!(bytes[30], 1);
497 assert_eq!(bytes[31], 0);
498 }
499
500 #[test]
501 fn test_u256_roundtrip() {
502 let original =
503 uint!(0x11d223ce7b91ac212f42cf50f0a3439ae3fcdba4ea32acb7f194d1051ed324c2_U256);
504 let fe = FieldElement::try_from(original).unwrap();
505 let back: U256 = fe.into();
506 assert_eq!(original, back);
507 }
508}