use std::time::Instant;
use quipu::api::{decode, encode, Options};
use quipu::dictionary::Dictionary;
use quipu::hackerbot::{tamper_attack, truncation_attack, uniqueness_attack};
use quipu::kdf::KdfParams;
fn rand_bytes(n: usize) -> Vec<u8> {
let mut v = vec![0u8; n];
getrandom::getrandom(&mut v).expect("RNG");
v
}
fn main() {
let dict = Dictionary::new((0x21u8..=0x7e).map(|b| b as char).collect()).unwrap();
let opts = Options {
pepper: b"pepper-app",
kdf_params: KdfParams {
mem_kib: 512,
iterations: 1,
parallelism: 1,
},
codebook_id: 1,
};
let pass = "passphrase-plataforma";
let mut failures = 0;
println!("================ PLATAFORMA DE TEST :: Quipu ================\n");
print!("[1] Round-trip masivo (200 mensajes)... ");
let mut ok = 0;
for i in 0..200 {
let data = rand_bytes(i % 137);
let sym = encode(&data, pass, &dict, &opts);
if decode(&sym, pass, &dict, opts.pepper).as_deref() == Ok(&data[..]) {
ok += 1;
}
}
report(ok == 200, &format!("{ok}/200 recuperados"), &mut failures);
println!("[2] Ataques (hackerbot):");
let data = b"objetivo del red-team";
for r in [
tamper_attack(data, pass, &dict, opts.pepper, &opts),
truncation_attack(data, pass, &dict, opts.pepper, &opts),
uniqueness_attack(data, pass, &dict, &opts, 30),
] {
print!(" - {:<11} ", r.name);
report(
r.is_clean(),
&format!("intentos={} brechas={}", r.attempts, r.breaches),
&mut failures,
);
}
print!("[3] Uniformidad de símbolos... ");
let big = rand_bytes(8192);
let sym = encode(&big, pass, &dict, &opts);
let distinct = distinct_ratio(&sym, dict.base());
report(
distinct > 0.95,
&format!("símbolos distintos = {:.1}% del alfabeto", distinct * 100.0),
&mut failures,
);
print!("[4] Benchmark (encode+decode 1 KiB)... ");
let payload = rand_bytes(1024);
let t = Instant::now();
let rounds = 20;
for _ in 0..rounds {
let s = encode(&payload, pass, &dict, &opts);
let _ = decode(&s, pass, &dict, opts.pepper);
}
let per = t.elapsed().as_secs_f64() / rounds as f64 * 1000.0;
println!("OK ({per:.1} ms/ciclo, dominado por Argon2)");
println!("\n------------------------------------------------------------");
if failures == 0 {
println!("RESULTADO GLOBAL: TODO VERDE.");
} else {
println!("RESULTADO GLOBAL: {failures} FALLO(S).");
std::process::exit(1);
}
}
fn report(ok: bool, detail: &str, failures: &mut u32) {
if ok {
println!("OK ({detail})");
} else {
println!("FAIL ({detail})");
*failures += 1;
}
}
fn distinct_ratio(symbols: &str, base: u32) -> f64 {
let mut seen = std::collections::HashSet::new();
for c in symbols.chars() {
seen.insert(c);
}
seen.len() as f64 / base as f64
}