From 40de7e64b2f663be48bb8f8027a7a4cd7bf1bb5d Mon Sep 17 00:00:00 2001 From: AriaNaraghi Date: Mon, 6 Jul 2026 00:35:14 +0300 Subject: [PATCH] test: add regression test for F-01 field canonicity check The MontyField31 Deserialize impl rejects non-canonical values (>= p), the fix for audit finding F-01, but had no test -- a refactor could silently drop the check and reintroduce the critical bug. Add a unit test asserting values >= p are rejected and canonical values in [0, p) are accepted. --- .../koala-bear/src/monty_31/monty_31.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/crates/backend/koala-bear/src/monty_31/monty_31.rs b/crates/backend/koala-bear/src/monty_31/monty_31.rs index 23c561318..356a1f86b 100644 --- a/crates/backend/koala-bear/src/monty_31/monty_31.rs +++ b/crates/backend/koala-bear/src/monty_31/monty_31.rs @@ -695,3 +695,36 @@ impl Sum for MontyField31 { Self::new_monty((sum % FP::PRIME as u64) as u32) } } + +#[cfg(test)] +mod canonicity_tests { + use crate::KoalaBear; + use serde::Deserialize; + use serde::de::IntoDeserializer; + use serde::de::value::{Error as ValueError, U32Deserializer}; + + /// KoalaBear modulus p (mirrors `koala_bear::KoalaBearParameters::PRIME`). + const KOALA_BEAR_PRIME: u32 = 0x7f00_0001; + + fn deserialize_limb(limb: u32) -> Result { + let deserializer: U32Deserializer = limb.into_deserializer(); + KoalaBear::deserialize(deserializer) + } + + #[test] + fn deserialize_rejects_non_canonical_field_element() { + // Regression test for audit finding F-01 (non-canonical MontyField31 + // deserialization). A serialized value >= p must be rejected; otherwise it + // is silently reduced mod p, so distinct encodings would alias to the same + // element. The quintic extension and packed types deserialize each of their + // coefficients through this same checked base-field path. + assert!(deserialize_limb(KOALA_BEAR_PRIME).is_err(), "accepted value == p"); + assert!(deserialize_limb(KOALA_BEAR_PRIME + 1).is_err(), "accepted value > p"); + assert!(deserialize_limb(u32::MAX).is_err(), "accepted u32::MAX"); + + // Canonical values in [0, p) are accepted. + assert!(deserialize_limb(0).is_ok()); + assert!(deserialize_limb(1).is_ok()); + assert!(deserialize_limb(KOALA_BEAR_PRIME - 1).is_ok()); + } +}