Skip to main content

happy_cracking/crypto/
des_cipher.rs

1use anyhow::{Context, Result};
2use clap::Subcommand;
3
4use des::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
5use des::{Des, TdesEde3};
6
7#[derive(Subcommand)]
8pub enum DesCipherAction {
9    #[command(about = "DES ECB encrypt (hex key 8 bytes, hex plaintext)")]
10    Encrypt {
11        #[arg(help = "Plaintext (hex string, must be multiple of 8 bytes)")]
12        input: String,
13        #[arg(short, long, help = "Key (hex string, 8 bytes / 16 hex chars)")]
14        key: String,
15    },
16    #[command(about = "DES ECB decrypt (hex key 8 bytes, hex ciphertext)")]
17    Decrypt {
18        #[arg(help = "Ciphertext (hex string, must be multiple of 8 bytes)")]
19        input: String,
20        #[arg(short, long, help = "Key (hex string, 8 bytes / 16 hex chars)")]
21        key: String,
22    },
23    #[command(about = "Triple-DES (EDE3) encrypt (hex key 24 bytes, hex plaintext)")]
24    TdesEncrypt {
25        #[arg(help = "Plaintext (hex string, must be multiple of 8 bytes)")]
26        input: String,
27        #[arg(short, long, help = "Key (hex string, 24 bytes / 48 hex chars)")]
28        key: String,
29    },
30    #[command(about = "Triple-DES (EDE3) decrypt (hex key 24 bytes, hex ciphertext)")]
31    TdesDecrypt {
32        #[arg(help = "Ciphertext (hex string, must be multiple of 8 bytes)")]
33        input: String,
34        #[arg(short, long, help = "Key (hex string, 24 bytes / 48 hex chars)")]
35        key: String,
36    },
37}
38
39pub fn run(action: DesCipherAction) -> Result<()> {
40    match action {
41        DesCipherAction::Encrypt { input, key } => {
42            println!("{}", des_encrypt(&input, &key)?);
43        }
44        DesCipherAction::Decrypt { input, key } => {
45            println!("{}", des_decrypt(&input, &key)?);
46        }
47        DesCipherAction::TdesEncrypt { input, key } => {
48            println!("{}", tdes_encrypt(&input, &key)?);
49        }
50        DesCipherAction::TdesDecrypt { input, key } => {
51            println!("{}", tdes_decrypt(&input, &key)?);
52        }
53    }
54    Ok(())
55}
56
57fn parse_des_blocks(hex_input: &str) -> Result<Vec<u8>> {
58    let bytes = hex::decode(hex_input.trim()).context("Failed to decode hex input")?;
59    if bytes.is_empty() {
60        anyhow::bail!("Input must not be empty");
61    }
62    if bytes.len() % 8 != 0 {
63        anyhow::bail!(
64            "Input length must be a multiple of 8 bytes, got {} bytes",
65            bytes.len()
66        );
67    }
68    Ok(bytes)
69}
70
71pub fn des_encrypt(hex_input: &str, hex_key: &str) -> Result<String> {
72    let key_bytes = hex::decode(hex_key.trim()).context("Failed to decode hex key")?;
73    if key_bytes.len() != 8 {
74        anyhow::bail!(
75            "DES key must be exactly 8 bytes (16 hex chars), got {} bytes",
76            key_bytes.len()
77        );
78    }
79    let plaintext = parse_des_blocks(hex_input)?;
80    let cipher =
81        Des::new(&Array::try_from(key_bytes.as_slice()).context("DES key must be 8 bytes")?);
82
83    let mut output = Vec::with_capacity(plaintext.len());
84    for block in plaintext.chunks(8) {
85        let mut block_array = Array::try_from(block).context("DES block must be 8 bytes")?;
86        cipher.encrypt_block(&mut block_array);
87        output.extend_from_slice(&block_array);
88    }
89    Ok(hex::encode(output))
90}
91
92pub fn des_decrypt(hex_input: &str, hex_key: &str) -> Result<String> {
93    let key_bytes = hex::decode(hex_key.trim()).context("Failed to decode hex key")?;
94    if key_bytes.len() != 8 {
95        anyhow::bail!(
96            "DES key must be exactly 8 bytes (16 hex chars), got {} bytes",
97            key_bytes.len()
98        );
99    }
100    let ciphertext = parse_des_blocks(hex_input)?;
101    let cipher =
102        Des::new(&Array::try_from(key_bytes.as_slice()).context("DES key must be 8 bytes")?);
103
104    let mut output = Vec::with_capacity(ciphertext.len());
105    for block in ciphertext.chunks(8) {
106        let mut block_array = Array::try_from(block).context("DES block must be 8 bytes")?;
107        cipher.decrypt_block(&mut block_array);
108        output.extend_from_slice(&block_array);
109    }
110    Ok(hex::encode(output))
111}
112
113pub fn tdes_encrypt(hex_input: &str, hex_key: &str) -> Result<String> {
114    let key_bytes = hex::decode(hex_key.trim()).context("Failed to decode hex key")?;
115    if key_bytes.len() != 24 {
116        anyhow::bail!(
117            "Triple-DES key must be exactly 24 bytes (48 hex chars), got {} bytes",
118            key_bytes.len()
119        );
120    }
121    let plaintext = parse_des_blocks(hex_input)?;
122    let cipher = TdesEde3::new(
123        &Array::try_from(key_bytes.as_slice()).context("Triple-DES key must be 24 bytes")?,
124    );
125
126    let mut output = Vec::with_capacity(plaintext.len());
127    for block in plaintext.chunks(8) {
128        let mut block_array = Array::try_from(block).context("DES block must be 8 bytes")?;
129        cipher.encrypt_block(&mut block_array);
130        output.extend_from_slice(&block_array);
131    }
132    Ok(hex::encode(output))
133}
134
135pub fn tdes_decrypt(hex_input: &str, hex_key: &str) -> Result<String> {
136    let key_bytes = hex::decode(hex_key.trim()).context("Failed to decode hex key")?;
137    if key_bytes.len() != 24 {
138        anyhow::bail!(
139            "Triple-DES key must be exactly 24 bytes (48 hex chars), got {} bytes",
140            key_bytes.len()
141        );
142    }
143    let ciphertext = parse_des_blocks(hex_input)?;
144    let cipher = TdesEde3::new(
145        &Array::try_from(key_bytes.as_slice()).context("Triple-DES key must be 24 bytes")?,
146    );
147
148    let mut output = Vec::with_capacity(ciphertext.len());
149    for block in ciphertext.chunks(8) {
150        let mut block_array = Array::try_from(block).context("DES block must be 8 bytes")?;
151        cipher.decrypt_block(&mut block_array);
152        output.extend_from_slice(&block_array);
153    }
154    Ok(hex::encode(output))
155}