#[cfg(target_arch = "wasm32")]
fn main() {}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
use std::process::Command;
use std::time::{Duration, Instant};
use gunnar_crypt::keyring::Keyring;
use gunnar_crypt::members::{Level, Members};
use gunnar_crypt::wrap::{self, age, NoPassphrase};
use gunnar_crypt::{policy, pq};
fn time<T>(runs: usize, mut f: impl FnMut() -> T) -> f64 {
let mut v: Vec<Duration> = Vec::with_capacity(runs);
for _ in 0..runs {
let t = Instant::now();
std::hint::black_box(f());
v.push(t.elapsed());
}
v.sort();
v[v.len() / 2].as_secs_f64() * 1e3
}
let dir = tempfile::tempdir().unwrap();
println!(
"keygen pq (age1pq, ML-KEM-768+X25519): {:.3} ms x25519 (age1): {:.3} ms",
time(200, pq::Identity::generate),
time(200, age::x25519::Identity::generate)
);
let ssh: Vec<(Vec<u8>, String)> = (0..500)
.map(|i| {
let path = dir.path().join(format!("k{i}"));
let ok = Command::new("ssh-keygen")
.args(["-q", "-t", "ed25519", "-N", "", "-C", "", "-f"])
.arg(&path)
.status()
.expect("ssh-keygen runs");
assert!(ok.success());
(
std::fs::read(&path).unwrap(),
std::fs::read_to_string(path.with_extension("pub"))
.unwrap()
.trim()
.to_string(),
)
})
.collect();
let pqs: Vec<pq::Identity> = (0..500).map(|_| pq::Identity::generate()).collect();
let ring = Keyring::generate();
println!(
"{:>7} {:<12} {:>9} {:>14} {:>13} {:>13} {:>9}",
"members", "level", "wrap ms", "unwrap 1st ms", "unwrap last", "check_ring", "keys KB"
);
for n in [1usize, 10, 100, 500] {
let runs = if n >= 100 { 5 } else { 30 };
for level in [Level::Classic, Level::QuantumSafe] {
let mut m = Members::new();
m.set_level(level).unwrap();
for i in 0..n {
let key = match level {
Level::Classic => ssh[i].1.clone(),
Level::QuantumSafe => pqs[i].to_public().to_string(),
};
m.add(&format!("m{i}"), &key).unwrap();
}
let file = wrap::wrap(&ring, &m).unwrap();
let id = |i: usize| -> Box<dyn age::Identity> {
match level {
Level::Classic => wrap::ssh_identity(&ssh[i].0, None, NoPassphrase).unwrap(),
Level::QuantumSafe => Box::new(pqs[i].clone()),
}
};
let (first, last) = (id(0), id(n - 1));
let text = m.to_text();
assert_eq!(policy::check_ring(&text, file.as_bytes()), Ok(()));
println!(
"{:>7} {:<12} {:>9.2} {:>14.2} {:>13.2} {:>13.3} {:>9.1}",
n,
level.word(),
time(runs, || wrap::wrap(&ring, &m).unwrap()),
time(runs, || wrap::unwrap(file.as_bytes(), &[first.as_ref()])
.unwrap()),
time(runs, || wrap::unwrap(file.as_bytes(), &[last.as_ref()])
.unwrap()),
time(runs, || policy::check_ring(&text, file.as_bytes())),
file.len() as f64 / 1024.0
);
}
}
}