use std::time::Duration;
use std::time::UNIX_EPOCH;
use openpgp_cert_d as cert_d;
use cert_d::CertD;
use cert_d::Tag;
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().collect::<Vec<String>>();
let certd = if args.len() == 1 {
CertD::new()?
} else if args.len() == 2 {
CertD::with_base_dir(&args[1])?
} else {
eprintln!("Usage: {} [CERTD]", args[0]);
return Err("Invalid arguments".into());
};
let mut size_one_count = [0; 64];
let mut secs_one_count = [0; 64];
let mut nanos_one_count = [0; 64];
let mut tag_one_count = [0; 64];
let mut count = 0;
let count_ones = |one_count: &mut [usize; 64], mut value: u64| {
for i in 0..64 {
if value % 2 == 1 {
one_count[i] += 1;
}
value >>= 1;
}
};
for item in certd.iter_files() {
let Ok((fpr, file)) = item else { continue };
let m = match file.metadata() {
Ok(m) => m,
Err(err) => {
eprintln!("Failed to stat {}: {}", fpr, err);
continue;
}
};
let tag = match Tag::try_from(&m) {
Ok(m) => m,
Err(err) => {
eprintln!("Failed to compute tag for {}: {}", fpr, err);
continue;
}
};
count_ones(&mut size_one_count, m.len());
let mtime = m.modified()?
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| Duration::new(0, 0));
count_ones(&mut secs_one_count, mtime.as_secs());
count_ones(&mut nanos_one_count, mtime.subsec_nanos() as u64);
count_ones(&mut tag_one_count, tag.0);
count += 1;
}
let mut size_entropy = 0f64;
let mut secs_entropy = 0f64;
let mut nanos_entropy = 0f64;
let mut tag_entropy = 0f64;
let mut max_entropy = 0f64;
let entropy = |p: f64| -> f64 {
assert!(0. <= p);
assert!(p <= 1.);
if p < 0.0001 || p > 0.9999 {
0.
} else {
-p * p.log2() - (1. - p) * (1. - p).log2()
}
};
let p = |one_count: usize| -> f64 {
f64::from(one_count as u32) / f64::from(count)
};
for i in 0..64 {
let size_prob = p(size_one_count[i]);
let secs_prob = p(secs_one_count[i]);
let nanos_prob = p(nanos_one_count[i]);
let tag_prob = p(tag_one_count[i]);
size_entropy += entropy(size_prob);
secs_entropy += entropy(secs_prob);
nanos_entropy += entropy(nanos_prob);
tag_entropy += entropy(tag_prob);
max_entropy += entropy(0.5);
eprintln!("{:2}: size: {:3.0}% ({:5}); secs: {:3.0}% ({:5}); \
nanos: {:3.0}% ({:5}); tag: {:3.0}% ({:5})",
i,
size_prob * 100., size_one_count[i],
secs_prob * 100., secs_one_count[i],
nanos_prob * 100., nanos_one_count[i],
tag_prob * 100., tag_one_count[i]);
}
eprintln!("Maximum-likelihood estimate of entropy (max: {} bits):",
max_entropy);
eprintln!(" size: {:.2} bits", size_entropy);
eprintln!(" secs: {:.2} bits", secs_entropy);
eprintln!(" nanos: {:.2} bits", nanos_entropy);
eprintln!(" max empirical entropy: {:.2} bits",
size_entropy + secs_entropy + nanos_entropy);
eprintln!(" tag: {:.2} bits", tag_entropy);
Ok(())
}