sonobe_primitives/transcripts/poseidon/
mod.rs1use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, find_poseidon_ark_and_mds};
4use ark_ff::{One, PrimeField};
5use num_bigint::BigUint;
6use num_integer::Integer;
7
8pub mod sponge;
9
10fn log2_order<F: PrimeField>() -> f64 {
11 let x = F::MODULUS.into();
12 let bits = x.bits(); if bits <= 53 {
14 let val: u64 = x.try_into().unwrap();
16 return (val as f64).log2();
17 }
18 let shift = bits - 53;
20 let top = x >> shift;
21 let top_u64: u64 = top.try_into().unwrap();
22 (top_u64 as f64).log2() + shift as f64
23}
24
25fn sat_inequiv_alpha<F: PrimeField>(t: usize, r_f: u64, r_p: u64, alpha: u64, m: usize) -> bool {
26 let log2_p = log2_order::<F>();
27 let n = log2_p.ceil() as usize;
28 let m_f = m as f64;
29 let n_f = n as f64;
30 let t_f = t as f64;
31 let r_p_f = r_p as f64;
32 let r_f_f = r_f as f64;
33 let alpha_f = alpha as f64;
34 let log2_alpha = 2.0f64.ln() / alpha_f.ln();
35
36 let r_f_1: f64 = if m_f <= (log2_p - (alpha_f - 1.0) / 2.0).floor() * (t_f + 1.0) {
37 6.0
38 } else {
39 10.0
40 };
41
42 let r_f_2 = 1.0 + log2_alpha * m_f.min(n_f) + (t_f.ln() / alpha_f.ln()).ceil() - r_p_f;
43
44 let r_f_3 = 1.0 + log2_alpha * (m_f / 3.0).min(log2_p / 2.0) - r_p_f;
45
46 let r_f_4 = t_f - 1.0 + (log2_alpha * m_f / (t_f + 1.0)).min(log2_alpha * log2_p / 2.0) - r_p_f;
47
48 let r_f_max = r_f_1
49 .ceil()
50 .max(r_f_2.ceil())
51 .max(r_f_3.ceil())
52 .max(r_f_4.ceil());
53
54 r_f_f >= r_f_max
55}
56
57fn get_sbox_cost(r_f: u64, r_p: u64, _n: usize, t: usize) -> usize {
58 t * r_f as usize + r_p as usize
59}
60
61fn find_fd_round_numbers<F: PrimeField>(
62 t: usize,
63 alpha: u64,
64 m: usize,
65 cost_function: fn(u64, u64, usize, usize) -> usize,
66 security_margin: bool,
67) -> (u64, u64) {
68 let n = log2_order::<F>().ceil() as usize;
69 let n_total = n * t;
70
71 let mut r_p: u64 = 0;
72 let mut r_f: u64 = 0;
73 let mut min_cost = usize::MAX;
74 let mut max_cost_rf: u64 = 0;
75
76 for r_p_t in 1u64..500 {
77 for r_f_t in (4u64..100).step_by(2) {
78 if !sat_inequiv_alpha::<F>(t, r_f_t, r_p_t, alpha, m) {
79 continue;
80 }
81
82 let (r_f_eff, r_p_eff) = if security_margin {
83 (r_f_t + 2, (r_p_t as f64 * 1.075).ceil() as u64)
84 } else {
85 (r_f_t, r_p_t)
86 };
87
88 let cost = cost_function(r_f_eff, r_p_eff, n_total, t);
89 if cost < min_cost || (cost == min_cost && r_f_eff < max_cost_rf) {
90 r_p = r_p_eff;
91 r_f = r_f_eff;
92 min_cost = cost;
93 max_cost_rf = r_f;
94 }
95 }
96 }
97
98 assert_ne!(min_cost, usize::MAX);
99
100 (r_f, r_p)
101}
102
103pub fn poseidon_paper_config<F: PrimeField, const SECURITY_BITS: usize>(
108 alpha: u64,
109 rate: usize,
110) -> PoseidonConfig<F> {
111 assert_ne!(alpha, 1);
112 assert_eq!(
113 BigUint::from(alpha).gcd(&(-F::one()).into()),
114 BigUint::one()
115 );
116 let (full_rounds, partial_rounds) =
117 find_fd_round_numbers::<F>(rate + 1, alpha, SECURITY_BITS, get_sbox_cost, true);
118 let (ark, mds) = find_poseidon_ark_and_mds(
119 F::MODULUS_BIT_SIZE as u64,
120 rate,
121 full_rounds,
122 partial_rounds,
123 0,
124 );
125
126 PoseidonConfig::new(
127 full_rounds as usize,
128 partial_rounds as usize,
129 alpha,
130 mds,
131 ark,
132 rate,
133 1,
134 )
135}
136
137pub fn poseidon_circom_config() -> PoseidonConfig<ark_bn254::Fr> {
140 let full_rounds = 8;
145 let partial_rounds = 60;
146 let alpha = 5;
147 let rate = 4;
148
149 let (ark, mds) = find_poseidon_ark_and_mds(
150 ark_bn254::Fr::MODULUS_BIT_SIZE as u64,
151 rate,
152 full_rounds as u64,
153 partial_rounds as u64,
154 0,
155 );
156
157 PoseidonConfig::new(full_rounds, partial_rounds, alpha, mds, ark, rate, 1)
158}