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