happy_cracking/crypto/
columnar.rs1use anyhow::Result;
2use clap::Subcommand;
3
4use super::shared::column_order;
5
6#[derive(Subcommand)]
7pub enum ColumnarAction {
8 #[command(about = "Encrypt with Columnar Transposition cipher")]
9 Encrypt {
10 #[arg(help = "Input text")]
11 input: String,
12 #[arg(short, long, help = "Keyword for column ordering")]
13 key: String,
14 },
15 #[command(about = "Decrypt Columnar Transposition cipher")]
16 Decrypt {
17 #[arg(help = "Encrypted text")]
18 input: String,
19 #[arg(short, long, help = "Keyword for column ordering")]
20 key: String,
21 },
22}
23
24pub fn run(action: ColumnarAction) -> Result<()> {
25 match action {
26 ColumnarAction::Encrypt { input, key } => {
27 println!("{}", encrypt(&input, &key)?);
28 }
29 ColumnarAction::Decrypt { input, key } => {
30 println!("{}", decrypt(&input, &key)?);
31 }
32 }
33 Ok(())
34}
35
36const MAX_KEY_LEN: usize = 1_000_000;
40
41pub fn encrypt(input: &str, key: &str) -> Result<String> {
42 if key.len() > MAX_KEY_LEN {
43 anyhow::bail!(
44 "Key exceeds maximum length of {} to prevent Denial of Service",
45 MAX_KEY_LEN
46 );
47 }
48
49 if key.is_empty() || !key.chars().all(|c| c.is_ascii_alphabetic()) {
50 anyhow::bail!("Key must be non-empty and contain only alphabetic characters");
51 }
52
53 if input.is_empty() {
54 return Ok(String::new());
55 }
56
57 let key_len = key.len();
58 let order = column_order(key);
59
60 let mut padded: Vec<char> = input.chars().collect();
62 while !padded.len().is_multiple_of(key_len) {
63 padded.push('X');
64 }
65
66 let grid: Vec<Vec<char>> = padded.chunks(key_len).map(|row| row.to_vec()).collect();
68
69 let mut sorted_cols: Vec<usize> = (0..key_len).collect();
71 sorted_cols.sort_by_key(|&col| order[col]);
72
73 let mut result = String::new();
74 for &col in &sorted_cols {
75 for row in &grid {
76 result.push(row[col]);
77 }
78 }
79
80 Ok(result)
81}
82
83pub fn decrypt(input: &str, key: &str) -> Result<String> {
84 if key.len() > MAX_KEY_LEN {
85 anyhow::bail!(
86 "Key exceeds maximum length of {} to prevent Denial of Service",
87 MAX_KEY_LEN
88 );
89 }
90
91 if key.is_empty() || !key.chars().all(|c| c.is_ascii_alphabetic()) {
92 anyhow::bail!("Key must be non-empty and contain only alphabetic characters");
93 }
94
95 if input.is_empty() {
96 return Ok(String::new());
97 }
98
99 let key_len = key.len();
100 let chars: Vec<char> = input.chars().collect();
101 let total_len = chars.len();
102
103 if !total_len.is_multiple_of(key_len) {
104 anyhow::bail!("Ciphertext length must be a multiple of key length");
105 }
106
107 let num_rows = total_len / key_len;
108 let order = column_order(key);
109
110 let mut sorted_cols: Vec<usize> = (0..key_len).collect();
112 sorted_cols.sort_by_key(|&col| order[col]);
113
114 let mut columns: Vec<Vec<char>> = vec![Vec::new(); key_len];
116 let mut pos = 0;
117 for &col in &sorted_cols {
118 columns[col] = chars[pos..pos + num_rows].to_vec();
119 pos += num_rows;
120 }
121
122 let mut result = String::new();
124 for row in 0..num_rows {
125 for column in &columns {
126 result.push(column[row]);
127 }
128 }
129
130 Ok(result)
131}