Skip to content

Commit 1fbb35a

Browse files
NashJamesimp2002
andauthored
feat: add Transposition Cipher (TheAlgorithms#261)
* add transposition cipher * Update transposition.rs Co-authored-by: imp <imp07@qq.com>
1 parent 0154691 commit 1fbb35a

File tree

3 files changed

+295
-1
lines changed

3 files changed

+295
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
These are for demonstration purposes only.
88
RESTART BUILD
9+
910
## [Sort Algorithms](./src/sorting)
1011

1112
- [x] [Bubble](./src/sorting/bubble_sort.rs)
@@ -34,6 +35,7 @@ RESTART BUILD
3435
- [x] [Bellman-Ford](./src/graph/bellman_ford.rs)
3536

3637
## Math
38+
3739
- [x] [Extended euclidean algorithm](./src/math/extended_euclidean_algorithm.rs)
3840
- [x] [Greatest common divisor](./src/math/greatest_common_divisor.rs)
3941
- [x] [Pascal's triangle](./src/math/pascal_triangle.rs)
@@ -104,7 +106,7 @@ RESTART BUILD
104106
- Rot13
105107
- [x] [Rot13](./src/ciphers/rot13.rs)
106108
- [x] [Another Rot13](./src/ciphers/another_rot13.rs)
107-
- [ ] Transposition
109+
- [x] [Transposition](./src/ciphers/transposition.rs)
108110
- [x] [SHA-2](./src/ciphers/sha256.rs)
109111

110112
---

src/ciphers/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod morse_code;
44
mod polybius;
55
mod rot13;
66
mod sha256;
7+
mod transposition;
78
mod vigenere;
89
mod xor;
910

@@ -13,5 +14,6 @@ pub use self::morse_code::{decode, encode};
1314
pub use self::polybius::{decode_ascii, encode_ascii};
1415
pub use self::rot13::rot13;
1516
pub use self::sha256::sha256;
17+
pub use self::transposition::transposition;
1618
pub use self::vigenere::vigenere;
1719
pub use self::xor::xor;

src/ciphers/transposition.rs

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
//! Transposition Cipher
2+
//!
3+
//! The Transposition Cipher is a method of encryption by which a message is shifted
4+
//! according to a regular system, so that the ciphertext is a rearrangement of the
5+
//! original message. The most commonly referred to Transposition Cipher is the
6+
//! COLUMNAR TRANSPOSITION cipher, which is demonstrated below.
7+
8+
use std::ops::Range;
9+
10+
/// Encrypts or decrypts a message, using multiple keys. The
11+
/// encryption is based on the columnar transposition method.
12+
pub fn transposition(decrypt_mode: bool, msg: &str, key: &str) -> String {
13+
let key_uppercase: String = key.to_uppercase();
14+
let mut cipher_msg: String = msg.to_string();
15+
16+
let keys: Vec<&str> = match decrypt_mode {
17+
false => key_uppercase.split_whitespace().collect(),
18+
true => key_uppercase.split_whitespace().rev().collect(),
19+
};
20+
21+
for cipher_key in &keys {
22+
let mut key_order: Vec<usize> = Vec::new();
23+
let mut counter: u8 = 0;
24+
25+
// Removes any non-alphabet characters from 'msg'
26+
cipher_msg = cipher_msg
27+
.to_uppercase()
28+
.chars()
29+
.filter(|&c| c.is_ascii_alphabetic())
30+
.collect();
31+
32+
// Determines the sequence of the columns, as dictated by the
33+
// alphabetical order of the keyword's letters
34+
let mut key_ascii: Vec<(usize, u8)> =
35+
cipher_key.bytes().enumerate().collect::<Vec<(usize, u8)>>();
36+
37+
key_ascii.sort_by_key(|&(_, key)| key);
38+
39+
key_ascii.iter_mut().for_each(|(_, key)| {
40+
*key = counter;
41+
counter += 1;
42+
});
43+
44+
key_ascii.sort_by_key(|&(index, _)| index);
45+
46+
key_ascii
47+
.into_iter()
48+
.for_each(|(_, key)| key_order.push(key.into()));
49+
50+
// Determines whether to encrypt or decrypt the message,
51+
// and returns the result
52+
cipher_msg = match decrypt_mode {
53+
false => encrypt(cipher_msg, key_order),
54+
true => decrypt(cipher_msg, key_order),
55+
};
56+
}
57+
58+
cipher_msg
59+
}
60+
61+
/// Performs the columnar transposition encryption
62+
fn encrypt(mut msg: String, key_order: Vec<usize>) -> String {
63+
let mut encrypted_msg: String = String::from("");
64+
let mut encrypted_vec: Vec<String> = Vec::new();
65+
66+
let msg_len: usize = msg.len();
67+
let key_len: usize = key_order.len();
68+
69+
let mut msg_index: usize = msg_len;
70+
let mut key_index: usize = key_len;
71+
72+
// Loop each column, pushing it to a Vec<T>
73+
while !msg.is_empty() {
74+
let mut chars: String = String::from("");
75+
let mut index: usize = 0;
76+
key_index -= 1;
77+
78+
// Loop every nth character, determined by key length, to create a column
79+
while index < msg_index {
80+
let ch: char = msg.remove(index);
81+
chars.push(ch);
82+
83+
index += key_index;
84+
msg_index -= 1;
85+
}
86+
87+
encrypted_vec.push(chars);
88+
}
89+
90+
// Concatenate the columns into a string, determined by the
91+
// alphabetical order of the keyword's characters
92+
let mut indexed_vec: Vec<(usize, &String)> = Vec::new();
93+
let mut indexed_msg: String = String::from("");
94+
let mut counter: usize = 0;
95+
96+
key_order.into_iter().for_each(|key_index| {
97+
indexed_vec.push((key_index, &encrypted_vec[counter]));
98+
counter += 1;
99+
});
100+
101+
indexed_vec.sort();
102+
103+
indexed_vec.into_iter().for_each(|(_, column)| {
104+
indexed_msg.push_str(column);
105+
});
106+
107+
// Split the message by a space every nth character, determined by
108+
// 'message length divided by keyword length' to the next highest integer.
109+
let msg_div: usize = (msg_len as f32 / key_len as f32).ceil() as usize;
110+
let mut counter: usize = 0;
111+
112+
indexed_msg.chars().for_each(|c| {
113+
encrypted_msg.push(c);
114+
counter += 1;
115+
if counter == msg_div {
116+
encrypted_msg.push(' ');
117+
counter = 0;
118+
}
119+
});
120+
121+
encrypted_msg.trim_end().to_string()
122+
}
123+
124+
/// Performs the columnar transposition decryption
125+
fn decrypt(mut msg: String, key_order: Vec<usize>) -> String {
126+
let mut decrypted_msg: String = String::from("");
127+
let mut decrypted_vec: Vec<String> = Vec::new();
128+
let mut indexed_vec: Vec<(usize, String)> = Vec::new();
129+
130+
let msg_len: usize = msg.len();
131+
let key_len: usize = key_order.len();
132+
133+
// Split the message into columns, determined by 'message length divided by keyword length'.
134+
// Some columns are larger by '+1', where the prior calculation leaves a remainder.
135+
let split_size: usize = (msg_len as f64 / key_len as f64) as usize;
136+
let msg_mod: usize = msg_len % key_len;
137+
let mut counter: usize = msg_mod;
138+
139+
let mut key_split: Vec<usize> = key_order.clone();
140+
let (split_large, split_small) = key_split.split_at_mut(msg_mod);
141+
142+
split_large.sort_unstable();
143+
split_small.sort_unstable();
144+
145+
split_large.iter_mut().rev().for_each(|key_index| {
146+
counter -= 1;
147+
let range: Range<usize> =
148+
((*key_index * split_size) + counter)..(((*key_index + 1) * split_size) + counter + 1);
149+
150+
let slice: String = msg[range.clone()].to_string();
151+
indexed_vec.push((*key_index, slice));
152+
153+
msg.replace_range(range, "");
154+
});
155+
156+
split_small.iter_mut().for_each(|key_index| {
157+
let (slice, rest_of_msg) = msg.split_at(split_size);
158+
indexed_vec.push((*key_index, (slice.to_string())));
159+
msg = rest_of_msg.to_string();
160+
});
161+
162+
indexed_vec.sort();
163+
164+
key_order.into_iter().for_each(|key| {
165+
if let Some((_, column)) = indexed_vec.iter().find(|(key_index, _)| key_index == &key) {
166+
decrypted_vec.push(column.to_string());
167+
}
168+
});
169+
170+
// Concatenate the columns into a string, determined by the
171+
// alphabetical order of the keyword's characters
172+
for _ in 0..split_size {
173+
decrypted_vec.iter_mut().for_each(|column| {
174+
decrypted_msg.push(column.remove(0));
175+
})
176+
}
177+
178+
if !decrypted_vec.is_empty() {
179+
decrypted_vec.into_iter().for_each(|chars| {
180+
decrypted_msg.push_str(&chars);
181+
})
182+
}
183+
184+
decrypted_msg
185+
}
186+
187+
#[cfg(test)]
188+
mod tests {
189+
use super::*;
190+
191+
#[test]
192+
fn encryption() {
193+
assert_eq!(
194+
transposition(
195+
false,
196+
"The quick brown fox jumps over the lazy dog",
197+
"Archive",
198+
),
199+
"TKOOL ERJEZ CFSEG QOURY UWMTD HBXVA INPHO"
200+
);
201+
202+
assert_eq!(
203+
transposition(
204+
false,
205+
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,/;'[]{}:|_+=-`~() ",
206+
"Tenacious"
207+
),
208+
"DMVENW ENWFOX BKTCLU FOXGPY CLUDMV GPYHQZ IRAJSA JSBKTH QZIR"
209+
);
210+
211+
assert_eq!(
212+
transposition(false, "WE ARE DISCOVERED. FLEE AT ONCE.", "ZEBRAS"),
213+
"EVLNA CDTES EAROF ODEEC WIREE"
214+
);
215+
}
216+
217+
#[test]
218+
fn decryption() {
219+
assert_eq!(
220+
transposition(true, "TKOOL ERJEZ CFSEG QOURY UWMTD HBXVA INPHO", "Archive"),
221+
"THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG"
222+
);
223+
224+
assert_eq!(
225+
transposition(
226+
true,
227+
"DMVENW ENWFOX BKTCLU FOXGPY CLUDMV GPYHQZ IRAJSA JSBKTH QZIR",
228+
"Tenacious"
229+
),
230+
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
231+
);
232+
233+
assert_eq!(
234+
transposition(true, "EVLNA CDTES EAROF ODEEC WIREE", "ZEBRAS"),
235+
"WEAREDISCOVEREDFLEEATONCE"
236+
);
237+
}
238+
239+
#[test]
240+
fn double_encryption() {
241+
assert_eq!(
242+
transposition(
243+
false,
244+
"The quick brown fox jumps over the lazy dog",
245+
"Archive Snow"
246+
),
247+
"KEZEUWHAH ORCGRMBIO TLESOUDVP OJFQYTXN"
248+
);
249+
250+
assert_eq!(
251+
transposition(
252+
false,
253+
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,/;'[]{}:|_+=-`~() ",
254+
"Tenacious Drink"
255+
),
256+
"DWOCXLGZSKI VNBUPDYRJHN FTOCVQJBZEW KFYMHASQMEX LGUPIATR"
257+
);
258+
259+
assert_eq!(
260+
transposition(false, "WE ARE DISCOVERED. FLEE AT ONCE.", "ZEBRAS STRIPE"),
261+
"CAEEN SOIAE DRLEF WEDRE EVTOC"
262+
);
263+
}
264+
265+
#[test]
266+
fn double_decryption() {
267+
assert_eq!(
268+
transposition(
269+
true,
270+
"KEZEUWHAH ORCGRMBIO TLESOUDVP OJFQYTXN",
271+
"Archive Snow"
272+
),
273+
"THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG"
274+
);
275+
276+
assert_eq!(
277+
transposition(
278+
true,
279+
"DWOCXLGZSKI VNBUPDYRJHN FTOCVQJBZEW KFYMHASQMEX LGUPIATR",
280+
"Tenacious Drink",
281+
),
282+
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
283+
);
284+
285+
assert_eq!(
286+
transposition(true, "CAEEN SOIAE DRLEF WEDRE EVTOC", "ZEBRAS STRIPE"),
287+
"WEAREDISCOVEREDFLEEATONCE"
288+
);
289+
}
290+
}

0 commit comments

Comments
 (0)