helpers: de-plumb round 2 — to_int & base_2b iterators to index loops

Removes the last two untranslatable iterator adapters on the verify path so
their loops extract to real definitions (no Take/IterMut axioms in any cone):

- to_int: `for item in x.iter().take(n)` -> `for i in 0..n { ... x[i] }`. The
  Take adapter was the LAST non-oracle, non-zeroize axiom in the model.
- base_2b: `for item in baseb.iter_mut()` -> `for out in 0..out_len { ...;
  baseb[out] = ... }`. The IterMut adapter carried a next_back write-back
  closure as loop state (a function-typed fixpoint), painful to reason about.

Both are semantics-identical for every FIPS 205 parameter set: the asserts
already pin x.len()==n and out_len==baseb.len(), so the index ranges visit
exactly the same elements/slots in the same order with the same values. The
inner `while bits < b` loop of base_2b was already clean and is untouched.

Validation: cargo test --features slh_dsa_sha2_128s --lib green — all 12
parameter-set round trips AND mono_matches_deployed_verify (mono == deployed
generic verify on valid / corrupted / wrong-message inputs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mrwulf 2026-07-24 08:47:15 +02:00
parent 6f6a9d65e7
commit bea105195b

View file

@ -14,10 +14,13 @@ pub(crate) fn to_int(x: &[u8], n: u32) -> u64 {
let mut total = 0;
// 2: for i from 0 to n 1 do
for item in x.iter().take(n as usize) {
// Aeneas-compat: index loop instead of iter().take() (the Take iterator
// adapter is untranslatable-clean; x.len() == n by the assert above, so
// 0..n indexes exactly the same elements in the same order — identical).
for i in 0..(n as usize) {
//
// 3: total ← 256 · total + X[i]
total = (total << 8) + u64::from(*item);
total = (total << 8) + u64::from(x[i]);
// 4: end for
}
@ -77,7 +80,11 @@ pub(crate) fn base_2b(x: &[u8], b: u32, out_len: u32, baseb: &mut [u32]) {
let mut total = 0;
// 4: for out from 0 to out_len 1 do
for item in baseb.iter_mut() {
// Aeneas-compat: index loop instead of iter_mut() (the IterMut adapter with
// its next_back write-back closure is untranslatable-clean; out_len ==
// baseb.len() by the assert above, so 0..out_len writes exactly the same
// slots in the same order with the same values — identical).
for out in 0..(out_len as usize) {
//
// 5: while bits < b do
while bits < b {
@ -98,7 +105,7 @@ pub(crate) fn base_2b(x: &[u8], b: u32, out_len: u32, baseb: &mut [u32]) {
bits -= b;
// 11: baseb[out] ← (total ≫ bits) mod 2^b
*item = (total >> bits) & (u32::MAX >> (32 - b));
baseb[out] = (total >> bits) & (u32::MAX >> (32 - b));
// 12: end for
}