The vulnerability in Plonky3's MultiField32Challenger is a critical flaw in its implementation of the Fiat-Shamir transform, leading to transcript malleability and a loss of entropy in the generated challenges. An attacker could exploit these issues to break the binding property of the proof system.
The root cause lies in the p3_challenger::multi_field_challenger::MultiField32Challenger::duplexing function and its helper functions, p3_field::helpers::reduce_32 and p3_field::helpers::split_32.
-
Partial-Chunk Aliasing (Absorption Vulnerability): The duplexing function processed input data by calling p3_field::helpers::reduce_32. The reduce_32 function performed a simple base-2^32 packing of smaller field elements into a larger one but failed to incorporate the input's length. This meant that inputs of different lengths could produce the same packed representation if padded with trailing zeros (e.g., [x] and [x, 0, 0]). This allowed an attacker to modify the transcript by adding or removing trailing zero elements without affecting the subsequent challenges. The patch addresses this by introducing length-tagging during the absorption phase.
-
Non-Injective Squeeze (Squeezing Vulnerability): When generating challenges, duplexing used p3_field::helpers::split_32 to break down a large field element from the sponge's state. split_32 converted u64 limbs into 31-bit field elements using from_u64, which involves a modular reduction. This reduction discarded the upper 33 bits of each u64 limb, meaning different internal sponge states could produce identical challenges, weakening the security of the challenges.
-
High-Bit Truncation (Configuration Vulnerability): The number of limbs used to represent a larger field element was calculated using integer division (PF::bits() / 64). For fields whose bit-size is not a multiple of 64 (like BN254 with 254 bits), this silently truncated the most significant bits of the element being observed, allowing distinct hash digests to be treated as identical by the challenger.
The identified vulnerable functions are the direct implementations of these flawed mechanisms. Runtime profiling during an exploit would show these functions being called as they process the malformed, prover-controlled inputs. The fix involved a significant refactoring of the challenger logic to use proper domain separation and injective mappings.