diff --git a/ml-dsa/src/hint.rs b/ml-dsa/src/hint.rs index 20ced9f..7c24c45 100644 --- a/ml-dsa/src/hint.rs +++ b/ml-dsa/src/hint.rs @@ -7,27 +7,31 @@ use module_lattice::utils::Truncate; use crate::algebra::{AlgebraExt, BaseField, Decompose, Elem, Field, Polynomial, Vector}; use crate::param::{EncodedHint, SignatureParams}; +/// Algorithm 39 `MakeHint`: computes hint bit indicating whether adding `z` to `r` alters the high +/// bits of `r`. fn make_hint(z: Elem, r: Elem) -> bool { let r1 = r.high_bits::(); let v1 = (r + z).high_bits::(); r1 != v1 } -// The method only deals with public data, so we don't need to worry that / and % are not -// constant-time. -#[allow(clippy::integer_division_remainder_used)] +/// Algorithm 40 `UseHint`: returns the high bits of `r` adjusted according to hint `h`. +#[allow(clippy::integer_division_remainder_used, reason = "params are public")] fn use_hint(h: bool, r: Elem) -> Elem { let m: u32 = (BaseField::Q - 1) / TwoGamma2::U32; let (r1, r0) = r.decompose::(); let gamma2 = TwoGamma2::U32 / 2; - if h && r0.0 <= gamma2 { - Elem::new((r1.0 + 1) % m) - } else if h && r0.0 >= BaseField::Q - gamma2 { - Elem::new((r1.0 + m - 1) % m) - } else if h { - // We use the Elem encoding even for signed integers. Since r0 is computed - // mod+- 2*gamma2 (possibly minus 1), it is guaranteed to be in [-gamma2, gamma2]. - unreachable!(); + + if h { + if r0.0 > 0 && r0.0 <= gamma2 { + Elem::new((r1.0 + 1) % m) + } else if (r0.0 == 0) || (r0.0 >= BaseField::Q - gamma2) { + Elem::new((r1.0 + m - 1) % m) + } else { + // We use the Elem encoding even for signed integers. Since r0 is computed + // mod+- 2*gamma2 (possibly minus 1), it is guaranteed to be in [-gamma2, gamma2]. + unreachable!(); + } } else { r1 } @@ -227,6 +231,18 @@ mod test { panic!("Could not find suitable test value"); } + #[test] + fn use_hint_r0_is_zero() { + type TwoGamma2 = ::TwoGamma2; + let m = (BaseField::Q - 1) / TwoGamma2::U32; + let r = Elem::new(0); + let (r1, r0) = r.decompose::(); + assert_eq!(r0.0, 0); + + let result = use_hint::(true, r); + assert_eq!(result, Elem::new((r1.0 + m - 1) % m)); + } + #[test] fn use_hint_threshold() { type TwoGamma2 = ::TwoGamma2;