pso_poseidon/lib.rs
1//! **pso-poseidon** is a [Poseidon](https://eprint.iacr.org/2019/458) hash
2//! implementation in Rust created for [PSO](https://github.com/psonet) based on [light-poseidon](https://github.com/Lightprotocol/light-poseidon/) library.
3//!
4//! # Parameters
5//!
6//! The library provides pre-generated parameters over the BN254 curve, however
7//! it can work with any parameters provided as long as developers take care
8//! of generating the round constants.
9//!
10//! Parameters provided by the library are:
11//!
12//! * *x^5* S-boxes
13//! * width - *2 ≤ t ≤ 13*
14//! * inputs - *1 ≤ n ≤ 12*
15//! * 8 full rounds and partial rounds depending on *t*: *[56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65]*
16//!
17//! # Output type
18//!
19//! `Poseidon` type implements two traits which serve the purpose
20//! of returning the calculated hash in different representations:
21//!
22//! * `PoseidonHasher` with the `hash` method which returns
23//! `ark_ff::PrimeField`. Might be useful if you want
24//! to immediately process the result with an another library which works with
25//! `ark_ff::PrimeField` types.
26//!
27//! # Examples
28//!
29//! With `PoseidonHasher` trait and `ark_ff::PrimeField` result:
30//!
31//! ```rust
32//! use ark_bn254::Fr;
33//! use ark_ff::PrimeField;
34//! use pso_poseidon::{Poseidon, PoseidonHasher};
35//!
36//! let mut poseidon = Poseidon::<Fr>::new_circom(2).unwrap();
37//!
38//! let input1 = Fr::from_le_bytes_mod_order(&[1u8; 32]);
39//! let input2 = Fr::from_le_bytes_mod_order(&[2u8; 32]);
40//!
41//! let hash = poseidon.hash(&[input1, input2]).unwrap();
42//!
43//! // Do something with `hash`.
44//! ```
45//!
46//! # Poseidon2
47//!
48//! [`Poseidon2`] is a separate, generic BN254 **Poseidon2** hash, bit-compatible
49//! with noir's in-circuit `poseidon2` (Barretenberg's permutation + sponge). Use
50//! it for off-circuit hashing that must reproduce an in-circuit Poseidon2 result.
51//! It shares no parameters with the circom-compatible `Poseidon` above —
52//! Poseidon2 is a distinct construction. BN254 is built in via
53//! `Poseidon2::<Fr>::new()`; other fields supply their own constants.
54//!
55//! ```rust
56//! use ark_bn254::Fr;
57//! use pso_poseidon::{Poseidon2, PoseidonHasher};
58//!
59//! let mut poseidon2 = Poseidon2::<Fr>::new();
60//! let _hash = poseidon2.hash(&[Fr::from(1u64), Fr::from(2u64)]).unwrap();
61//! ```
62//!
63//! # Field Arithmetic
64//!
65//! This library uses [ark-ff](https://github.com/arkworks-rs/algebra) for field arithmetic. While ark-ff carries an academic disclaimer, it is widely adopted in production by major projects including [Aleo](https://github.com/AleoNet/snarkVM), [Penumbra](https://github.com/penumbra-zone/penumbra), [Mina (Kimchi)](https://github.com/o1-labs/proof-systems), and [Espresso Systems](https://github.com/EspressoSystems).
66//!
67//! # Implementation
68//!
69//! The implementation is compatible with the
70//! [original SageMath implementation](https://extgit.iaik.tugraz.at/krypto/hadeshash/-/tree/master/),
71//! but it was also inspired by the following ones:
72//!
73//! * [circomlibjs](https://github.com/iden3/circomlibjs)
74//! * [zero-knowledge-gadgets](https://github.com/webb-tools/zero-knowledge-gadgets)
75//! * [light-poseidon](https://github.com/Lightprotocol/light-poseidon/)
76//!
77//! # Security
78//!
79//! This library has been audited by [Veridise](https://veridise.com/). You can
80//! read the audit report [here](https://github.com/Lightprotocol/light-poseidon/blob/main/assets/audit.pdf).
81
82use ark_ff::PrimeField;
83
84use thiserror::Error;
85
86mod poseidon;
87pub use poseidon::{Poseidon, PoseidonParameters};
88
89/// bb-compatible Poseidon2 (matches noir's in-circuit `poseidon2`).
90pub mod poseidon2;
91pub use poseidon2::Poseidon2;
92
93pub const HASH_LEN: usize = 32;
94pub const MAX_X5_LEN: usize = 13;
95
96#[derive(Error, Debug, PartialEq)]
97pub enum PoseidonError {
98 #[error("Invalid number of inputs: {inputs}. Maximum allowed is {max_limit} ({width} - 1).")]
99 InvalidNumberOfInputs {
100 inputs: usize,
101 max_limit: usize,
102 width: usize,
103 },
104 #[error("Input is an empty slice.")]
105 EmptyInput,
106 #[error("Invalid length of the input: {len}. The length matching the modulus of the prime field is: {modulus_bytes_len}.")]
107 InvalidInputLength {
108 len: usize,
109 modulus_bytes_len: usize,
110 },
111 #[error("Failed to convert bytes {bytes:?} into a prime field element")]
112 BytesToPrimeFieldElement { bytes: Vec<u8> },
113 #[error("Input is larger than the modulus of the prime field.")]
114 InputLargerThanModulus,
115 #[error("Failed to convert a vector of bytes into an array.")]
116 VecToArray,
117 #[error("Failed to convert the number of inputs from u64 to u8.")]
118 U64Tou8,
119 #[error("Failed to convert bytes to BigInt")]
120 BytesToBigInt,
121 #[error("Invalid width: {width}. Choose a width between 2 and 16 for 1 to 15 inputs.")]
122 InvalidWidthCircom { width: usize, max_limit: usize },
123}
124
125pub trait PoseidonHasher<F: PrimeField> {
126 /// Calculates a Poseidon hash for the given input of prime fields and
127 /// returns the result as a prime field.
128 ///
129 /// # Examples
130 ///
131 /// Example with two simple big-endian byte inputs (converted to prime
132 /// fields) and BN254-based parameters provided by the library.
133 ///
134 /// ```rust
135 /// use ark_bn254::Fr;
136 /// use ark_ff::PrimeField;
137 /// use pso_poseidon::{Poseidon, PoseidonHasher};
138 ///
139 /// let mut poseidon = Poseidon::<Fr>::new_circom(2).unwrap();
140 ///
141 /// let input1 = Fr::from_le_bytes_mod_order(&[1u8; 32]);
142 /// let input2 = Fr::from_le_bytes_mod_order(&[2u8; 32]);
143 ///
144 /// let hash = poseidon.hash(&[input1, input2]).unwrap();
145 /// ```
146 fn hash(&mut self, inputs: &[F]) -> Result<F, PoseidonError>;
147}