This bug was reported to @Pratyush in January 2024 through a mutual partner. Since I haven't received a response about the preferred disclosure method, I'm documenting this issue to keep track of it.
Executive Summary
In the squeeze_internal function of the implementation of the Poseidon sponge, a non-necessary condition if is present, leading not to permute when squeezing self.parameters.rate elements with rate_start_index > 0.
[package]
name = "bad_if_squeeze_poseidon"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ark-bls12-377 = "0.4.0"
ark-crypto-primitives = { version = "0.4.0", features = ["sponge"]}
main.rs:
use ark_crypto_primitives::sponge::{poseidon::{PoseidonSponge, PoseidonConfig,find_poseidon_ark_and_mds}, CryptographicSponge};
use ark_crypto_primitives::sponge::FieldBasedCryptographicSponge;
use ark_bls12_377::Fq as Fq;
fn main() {
let full_rounds = 8;
let partial_rounds = 31;
let alpha = 17;
let rate = 2;
let capacity = 1;
let (ark, mds) = find_poseidon_ark_and_mds::<Fq>(
377,
rate,
full_rounds as u64,
partial_rounds as u64,
0
);
let sponge_param = PoseidonConfig::new(full_rounds, partial_rounds, alpha, mds, ark, rate, capacity);
let mut sponge = PoseidonSponge::new(&sponge_param);
// 1 is the only integer > 0 but < rate = 2
let x = sponge.squeeze_native_field_elements(1);
println!("First element squeezed: {x:?}");
let x = sponge.squeeze_native_field_elements(rate);
println!("Second and third elements squeezed: {x:?}");
}
If we remove the if conditions that we talk about below, we can see that the third element changes. And adding some println() in the base code after the calls to the permute function, we can see that we don't use this function enough.
Let's remember that output_remaining.len() is equal to self.parameters.rate, and rate_start_index > 0.
The condition rate_start_index + output_remaining.len() <= self.parameters.rate is therefore not respected, and we can access the previously cited lines.
If the output.len() is different from self.parameters.rate, then we permute, but in our case, both are equal, so we don't permute, and we enter in the first if loop, since, now, the condition rate_start_index + output_remaining.len() <= self.parameters.rate is respected. Inside this loop, we recover the missing element to squeeze, but still without permute. Finally, we have squeezed a certain number of elements without permuting the internal state.
Recommendations
Remove the if output_remaining.len() != self.parameters.rate conditions.
This bug was reported to @Pratyush in January 2024 through a mutual partner. Since I haven't received a response about the preferred disclosure method, I'm documenting this issue to keep track of it.
Executive Summary
In the
squeeze_internal
function of the implementation of the Poseidon sponge, a non-necessary conditionif
is present, leading not to permute when squeezingself.parameters.rate
elements withrate_start_index > 0
.Environment
Steps to Reproduce
Cargo.toml
:main.rs
:If we remove the
if
conditions that we talk about below, we can see that the third element changes. And adding someprintln()
in the base code after the calls to thepermute
function, we can see that we don't use this function enough.Root Cause Analysis
The problem is line 173 in poseidon/constraints.rs, and line 176 in poseidon/mod.rs.
Let's remember that
output_remaining.len()
is equal toself.parameters.rate
, andrate_start_index > 0
. The conditionrate_start_index + output_remaining.len() <= self.parameters.rate
is therefore not respected, and we can access the previously cited lines. If theoutput.len()
is different fromself.parameters.rate
, then we permute, but in our case, both are equal, so we don't permute, and we enter in the firstif
loop, since, now, the conditionrate_start_index + output_remaining.len() <= self.parameters.rate
is respected. Inside this loop, we recover the missing element to squeeze, but still without permute. Finally, we have squeezed a certain number of elements without permuting the internal state.Recommendations
Remove the
if output_remaining.len() != self.parameters.rate
conditions.References
Poseidon Paper