Skip to main content

happy_cracking/crypto/
hashid.rs

1use anyhow::Result;
2use clap::Subcommand;
3
4#[derive(Subcommand)]
5pub enum HashIdAction {
6    #[command(about = "Identify hash type")]
7    Identify {
8        #[arg(help = "Hash string to identify")]
9        input: String,
10    },
11}
12
13pub fn run(action: HashIdAction) -> Result<()> {
14    match action {
15        HashIdAction::Identify { input } => {
16            let results = identify(&input);
17            if results.is_empty() {
18                println!("Unknown hash format");
19            } else {
20                println!("Possible hash types:");
21                for result in results {
22                    println!("  - {}", result);
23                }
24            }
25        }
26    }
27    Ok(())
28}
29
30pub fn identify(input: &str) -> Vec<&'static str> {
31    let input = input.trim();
32    let len = input.len();
33    let is_hex = input.chars().all(|c| c.is_ascii_hexdigit());
34    let is_base64 = input
35        .chars()
36        .all(|c| c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '=');
37
38    let mut results = Vec::new();
39
40    if is_hex {
41        match len {
42            32 => {
43                results.push("MD5");
44                results.push("MD4");
45                results.push("MD2");
46                results.push("NTLM");
47            }
48            40 => {
49                results.push("SHA-1");
50                results.push("RIPEMD-160");
51            }
52            56 => {
53                results.push("SHA-224");
54            }
55            64 => {
56                results.push("SHA-256");
57                results.push("RIPEMD-256");
58                results.push("Snefru-256");
59                results.push("GOST R 34.11-94");
60            }
61            96 => {
62                results.push("SHA-384");
63            }
64            128 => {
65                results.push("SHA-512");
66                results.push("Whirlpool");
67                results.push("SHA-512/256");
68            }
69            _ => {}
70        }
71    }
72
73    // Check for bcrypt
74    if input.starts_with("$2a$")
75        || input.starts_with("$2b$")
76        || input.starts_with("$2y$")
77        || input.starts_with("$2x$")
78    {
79        results.push("bcrypt");
80    }
81
82    // Check for Argon2
83    if input.starts_with("$argon2") {
84        results.push("Argon2");
85    }
86
87    // Check for Unix crypt formats
88    if input.starts_with("$1$") {
89        results.push("MD5 Crypt (Unix)");
90    }
91    if input.starts_with("$5$") {
92        results.push("SHA-256 Crypt (Unix)");
93    }
94    if input.starts_with("$6$") {
95        results.push("SHA-512 Crypt (Unix)");
96    }
97
98    // Check for Base64-encoded hashes
99    if is_base64 && !is_hex {
100        match len {
101            24 => {
102                results.push("MD5 (Base64)");
103            }
104            28 => {
105                results.push("SHA-1 (Base64)");
106            }
107            44 => {
108                results.push("SHA-256 (Base64)");
109            }
110            88 => {
111                results.push("SHA-512 (Base64)");
112            }
113            _ => {}
114        }
115    }
116
117    // MySQL
118    if len == 16 && is_hex {
119        results.push("MySQL (old)");
120    }
121    if len == 41 && input.starts_with('*') {
122        results.push("MySQL 5.x");
123    }
124
125    results
126}