diff --git a/tools/attack/adv-mixer-3/queue/09-adv-mixer-3-sac0.sh b/tools/attack/adv-mixer-3/queue/09-adv-mixer-3-sac0.sh new file mode 100644 index 00000000..0587df04 --- /dev/null +++ b/tools/attack/adv-mixer-3/queue/09-adv-mixer-3-sac0.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# adv-mixer-3 queue file (owner: adv-mixer-3). Self-contained: binary, args, logs. Runs at nice 10 on cores 8-95 via run-box.sh. +set -uo pipefail +X=/srv/builds/_adv-mixer-3/bin/adv-mixer-3-v2; L=/srv/builds/_adv-mixer-3/logs; mkdir -p "$L" +echo "start $(hostname) $(date -u +%FT%TZ) $(sha256sum $X)" +for d in 20729 20733; do + echo "== sac0 plant k=0 (the init alone) day $d"; $X sac0 --day $d --apps 0 --states 65536 --threads 64 > "$L/sac0-$d-k0.log" 2>&1; grep -E 'holes=|verdict' "$L/sac0-$d-k0.log" + for k in 1 2 3 4 5 6 7 8; do + echo "== sac0 day $d k=$k states 2^24 $(date -u +%FT%TZ)" + $X sac0 --day $d --apps $k --states 2^24 --threads 64 > "$L/sac0-$d-k$k.log" 2>&1; echo "rc=$?"; grep -E 'holes=|line index|verdict' "$L/sac0-$d-k$k.log" + done + for k in 2 3 4; do + echo "== sac0 day $d k=$k states 2^28 $(date -u +%FT%TZ)" + $X sac0 --day $d --apps $k --states 2^28 --threads 64 > "$L/sac0-$d-k$k-n28.log" 2>&1; echo "rc=$?"; grep -E 'holes=|line index|verdict' "$L/sac0-$d-k$k-n28.log" + done +done +echo "end $(date -u +%FT%TZ)" diff --git a/tools/attack/adv-mixer-3/src/main.rs b/tools/attack/adv-mixer-3/src/main.rs index a7cfbaa6..dabc2f7d 100644 --- a/tools/attack/adv-mixer-3/src/main.rs +++ b/tools/attack/adv-mixer-3/src/main.rs @@ -10,6 +10,7 @@ //! //! adv-mixer-3 index --day D --apps k --threads T exhaustive line index census over all 2^32 t (round 0) //! adv-mixer-3 sac --day D --apps k --states N --threads T single-bit avalanche matrix on random states +//! adv-mixer-3 sac0 --day D --apps k --states N --threads T t-bit avalanche on the round-0 input (32 bits in) //! adv-mixer-3 diff --day D --apps k --samples N --threads T differential multiplicity for low-weight input differences //! adv-mixer-3 lin --day D --apps k --samples N --threads T single-bit linear correlations on random states //! adv-mixer-3 lin0 --day D --apps k --samples N --threads T t-bit to state-bit correlations on the round-0 input @@ -398,6 +399,102 @@ fn cmd_sac(a: &Args) { println!("verdict: {verdict}"); } +// ------------------------------------------------------------------------------------------------------------------ +// sac0: t-bit avalanche on the round-0 input (32 input bits, the item's whole entropy before the first read) +// ------------------------------------------------------------------------------------------------------------------ + +fn cmd_sac0(a: &Args) { + let day = a.u64("day", 20729); + let plant = Plant::parse(&a.str("plant", "none")); + let k = a.usize("apps", 8); + let start = a.usize("start", 0); + let threads = a.usize("threads", 32); + let n = a.u64("states", 1 << 20); + let mp = params_of_day(day, plant); + header(a, &mp, plant, start, k); + let ks = keys(start, k, plant); + let t0 = Instant::now(); + let per = n / threads as u64; + let mats: Vec> = std::thread::scope(|sc| { + let hs: Vec<_> = (0..threads) + .map(|th| { + let mp = ∓ + let ks = &ks; + sc.spawn(move || { + let mut m = vec![0u32; 32 * 512]; + let mut rng = seed_thread(day, 6, th as u64); + let cnt = if th + 1 == threads { n - per * (threads as u64 - 1) } else { per }; + for _ in 0..cnt { + let t = rng.next() as u32; + let mut y0 = init(t, mp); + apply(&mut y0, ks, mp, plant); + for i in 0..32 { + let mut yi = init(t ^ (1u32 << i), mp); + apply(&mut yi, ks, mp, plant); + let row = &mut m[i * 512..(i + 1) * 512]; + for w in 0..16 { + let mut d = yi[w] ^ y0[w]; + while d != 0 { + let b = d.trailing_zeros() as usize; + row[w * 32 + b] += 1; + d &= d - 1; + } + } + } + } + m + }) + }) + .collect(); + hs.into_iter().map(|h| h.join().unwrap()).collect() + }); + let mut m = vec![0u64; 32 * 512]; + for mm in &mats { + for i in 0..m.len() { + m[i] += mm[i] as u64; + } + } + let nf = n as f64; + let sq = nf.sqrt(); + let mut holes = 0usize; + let mut beyond6 = 0usize; + let mut worst = (0usize, 0usize, 0f64); + let mut idx_worst = 0f64; + let mut idx_beyond6 = 0usize; + let mut sum = 0f64; + for i in 0..32 { + for j in 0..512 { + let c = m[i * 512 + j]; + if c == 0 || c == n { + holes += 1; + } + let p = c as f64 / nf; + sum += p; + let z = (2.0 * p - 1.0) * sq; + if z.abs() > 6.0 { + beyond6 += 1; + } + if z.abs() > worst.2.abs() { + worst = (i, j, z); + } + if j < 22 { + if z.abs() > idx_worst.abs() { + idx_worst = z; + } + if z.abs() > 6.0 { + idx_beyond6 += 1; + } + } + } + } + let worst_p = m[worst.0 * 512 + worst.1] as f64 / nf; + println!("sac0: states={n} wall={:.1}s band(6 sigma)={:.5} inputs=32 t bits", t0.elapsed().as_secs_f64(), 6.0 / (2.0 * sq)); + println!("holes={holes} beyond6={beyond6} mean_flip={:.5} worst t-bit={} out={} (word {}) p={worst_p:.5} z={:.2}", sum / (32.0 * 512.0), worst.0, worst.1, worst.1 / 32, worst.2); + println!("line index cells (out bits 0..21): worst z={idx_worst:.2} beyond6={idx_beyond6} of {}", 32 * 22); + let verdict = if holes > 0 || beyond6 > 0 { "DISTINGUISHED" } else { "no cell beyond 6 sigma" }; + println!("verdict: {verdict}"); +} + // ------------------------------------------------------------------------------------------------------------------ // diff: differential multiplicity // ------------------------------------------------------------------------------------------------------------------ @@ -1005,6 +1102,7 @@ fn main() { match a.cmd.as_str() { "index" => cmd_index(&a), "sac" => cmd_sac(&a), + "sac0" => cmd_sac0(&a), "diff" => cmd_diff(&a), "lin" => lin_core(&a, false), "lin0" => lin_core(&a, true), @@ -1014,7 +1112,7 @@ fn main() { "model" => cmd_model(&a), "params" => cmd_params(&a), _ => { - eprintln!("commands: index sac diff lin lin0 rx cnf verify model params (see the header of src/main.rs)"); + eprintln!("commands: index sac sac0 diff lin lin0 rx cnf verify model params (see the header of src/main.rs)"); std::process::exit(2); } }