Skip to main content

happy_cracking/crypto/
entropy.rs

1use anyhow::Result;
2use clap::Subcommand;
3
4#[derive(Subcommand)]
5pub enum EntropyAction {
6    #[command(about = "Calculate Shannon entropy of input")]
7    Analyze {
8        #[arg(help = "Input text")]
9        input: String,
10    },
11}
12
13pub fn run(action: EntropyAction) -> Result<()> {
14    match action {
15        EntropyAction::Analyze { input } => {
16            let entropy = calculate(&input);
17            let classification = classify(entropy);
18            println!("Entropy: {:.4} bits/byte", entropy);
19            println!("Classification: {}", classification);
20        }
21    }
22    Ok(())
23}
24
25// Calculate Shannon entropy in bits per byte.
26// H = -Σ p(x) * log2(p(x)), maximum is 8.0 bits/byte.
27pub fn calculate(input: &str) -> f64 {
28    if input.is_empty() {
29        return 0.0;
30    }
31
32    let bytes = input.as_bytes();
33    let len = bytes.len() as f64;
34
35    // Optimization: Use array instead of HashMap for pure byte counting
36    // Avoids hashing overhead and memory allocation
37    let mut counts = [0usize; 256];
38    for &b in bytes {
39        counts[b as usize] += 1;
40    }
41
42    let mut entropy = 0.0;
43    for &count in counts.iter() {
44        if count > 0 {
45            let p = count as f64 / len;
46            entropy -= p * p.log2();
47        }
48    }
49
50    entropy
51}
52
53// Classify the entropy value into a human-readable category.
54pub fn classify(entropy: f64) -> &'static str {
55    if entropy >= 7.5 {
56        "High (likely encrypted/compressed)"
57    } else if entropy >= 4.0 {
58        "Medium (natural language/code)"
59    } else {
60        "Low (low entropy data)"
61    }
62}