Simple Cipher
1. Readme
简单密码
实现一个简单的移位密码,像 Caesar 和,一个更安全的替换密码.
步骤 1
“如果他有什么秘密要说的话,他就是用密码写的,也就是说,通过改变字母表的字母顺序,一个字也说不出来。如果有人想破译这些,并理解它们的意思,他必须用字母表中的第四个字母,即 D,代替 A,以及其他的字母。 —Suetonius, Life of Julius Caesar
密码是非常直截了当的算法,使文本不可读,同时仍容易允许破译。他们容易受到许多形式的密码分析,但我们幸运的是,我们的小姐妹通常不是密码学家。
用 Caesar密码,加密来自 Julius Caesar 的消息。现在 Caesar 知道加密方式不是很好,但他还是有个好处: 几乎没有人能读对。所以对于一对夫妇的信件已经足够了,让人们无法识别他们所知道的几个字。
你的任务是创建一个简单的移位密码,就像 Caesar 密码一样。这个图像是 Caesar 密码的一个很好的例子:
例如:
将”iamapandabear”作为输入到 encode
函数返回密码”ldpdsdqgdehdu”。(虽不足以让我们的信息在运输过程中,确保高保密性).
当将”ldpdsdqgdehdu”放入decode
函数时,它将返回原始的”iamapandabear”,让您的朋友阅读您的原始消息.
步骤 2
移位密码是没有乐趣,直到你的妹妹算了出来后!尝试修改代码,允许我们指定一个密钥(key),并用作移位距离。这称为代换密码.
下面是一个例子:
给定密钥”aaaaaaaaaaaaaaaaaaa”,对字符串”iamapandabar”进行编码将返回原来的”iamapandable”.
给定密钥”ddddddddddddd”,编码我们的字符串”iamapandabore”会返回疑惑的”ldpdsdqgdhdu”.
在上面的示例中,我们为键值设置了 a=0
。因此,当明文添加到密钥时,我们最终得到相同的消息。所以”aaaa”不是一个理想的密钥。但是,如果我们把密钥设置为dddd
,我们将得到与 caesar 密码相同的东西.
步骤 3
任何密码中最薄弱的环节都是人。让我们通过提供随机性,并确保密钥仅包含小写字母,来使替换密码更具容错性.
如果有人根本不提交密钥,则生成一个长度至少为 100 个字符的真正随机密钥.
如果提交的密钥不只由小写字母组成,那解决方案应该以适当提醒的方式处理错误.
扩展
移位密码通过使文本略微疑惑,达到目的,但易受频率分析的影响。替换密码有助于这一点,但当密钥较短,或空格被保留时,仍然非常脆弱。稍后,你会看到一个解决这个问题的练习”crypto-square”.
如果你想在这一领域走得更远,问题引导你,走进如何以安全的方式交换密钥。看一看维基百科上的 Diffie Hellman对于该方案的最早实现之一.
资源
维基百科的替代密码http://en.wikipedia.org/wiki/Substitution_cipher
2. 开始你的表演
pub fn encode(key: &str, s: &str) -> Option<String> { unimplemented!("Use {} to encode {} using shift cipher", key, s) } pub fn decode(key: &str, s: &str) -> Option<String> { unimplemented!("Use {} to decode {} using shift cipher", key, s) } pub fn encode_random(s: &str) -> (String, String) { unimplemented!( "Generate random key with only a-z chars and encode {}. Return tuple (key, encoded s)", s ) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { use std::collections::HashSet; const PLAIN_TEXT: &str = "thisismysecret"; const KEY: &str = "abcdefghij"; #[test] fn cipher_can_encode_with_given_key() { assert_eq!(encode(KEY, "aaaaaaaaaa"), Some(KEY.to_string())); } #[test] //#[ignore] fn cipher_can_decode_with_given_key() { assert_eq!(decode(KEY, "abcdefghij"), Some("aaaaaaaaaa".to_string())); } #[test] //#[ignore] fn cipher_is_reversible_given_key() { assert_eq!( decode(KEY, &encode(KEY, PLAIN_TEXT).unwrap()), Some(PLAIN_TEXT.to_string()) ); } #[test] //#[ignore] fn cipher_can_double_shift_encode() { let plain_text = "iamapandabear"; assert_eq!( encode(plain_text, plain_text), Some("qayaeaagaciai".to_string()) ); } #[test] //#[ignore] fn cipher_can_wrap_encode() { assert_eq!(encode(KEY, "zzzzzzzzzz"), Some("zabcdefghi".to_string())); } #[test] //#[ignore] fn cipher_can_encode_a_message_that_is_shorter_than_the_key() { assert_eq!(encode(KEY, "aaaaa"), Some("abcde".to_string())); } #[test] //#[ignore] fn cipher_can_decode_a_message_that_is_shorter_than_the_key() { assert_eq!(decode(KEY, "abcde"), Some("aaaaa".to_string())); } #[test] //#[ignore] fn encode_returns_none_with_an_all_caps_key() { let key = "ABCDEF"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_an_any_caps_key() { let key = "abcdEFg"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_numeric_key() { let key = "12345"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_any_numeric_key() { let key = "abcd345ef"; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_returns_none_with_empty_key() { let key = ""; assert_eq!(encode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_an_all_caps_key() { let key = "ABCDEF"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_an_any_caps_key() { let key = "abcdEFg"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_numeric_key() { let key = "12345"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_any_numeric_key() { let key = "abcd345ef"; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn decode_returns_none_with_empty_key() { let key = ""; assert_eq!(decode(key, PLAIN_TEXT), None); } #[test] //#[ignore] fn encode_random_uses_key_made_of_letters() { let (k, _) = encode_random(PLAIN_TEXT); assert!(k.chars().all(|c| c.is_ascii_lowercase())); } #[test] //#[ignore] fn encode_random_uses_key_of_100_characters_or_more() { let (k, _) = encode_random(PLAIN_TEXT); assert!(k.len() >= 100); } #[test] //#[ignore] fn encode_random_uses_randomly_generated_key() { let mut keys = HashSet::new(); let trials = 100; for _ in 0..trials { keys.insert(encode_random(PLAIN_TEXT).0); } assert_eq!(keys.len(), trials); } #[test] //#[ignore] fn encode_random_can_encode() { let (k, encoded) = encode_random("aaaaaaaaaa"); assert_eq!(encoded, k.split_at(10).0); } #[test] //#[ignore] fn encode_random_can_decode() { let (k, _) = encode_random("aaaaaaaaaa"); assert_eq!(decode(&k, k.split_at(10).0), Some("aaaaaaaaaa".to_string())); } #[test] //#[ignore] fn encode_random_is_reversible() { let (k, encoded) = encode_random(PLAIN_TEXT); assert_eq!(decode(&k, &encoded), Some(PLAIN_TEXT.to_string())); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate rand; use rand::Rng; pub fn encode_random(s: &str) -> (String, String) { let mut r = rand::thread_rng(); let mut key = String::new(); for _ in 0..100 { key.push(char::from('a' as u8 + r.gen_range(0, 26))); } let encoded = encode(&key, s); (key, encoded.unwrap()) } pub fn encode(key: &str, s: &str) -> Option<String> { shift(key, s, 1) } pub fn decode(key: &str, s: &str) -> Option<String> { shift(key, s, -1) } fn shift(key: &str, s: &str, dir: i8) -> Option<String> { if key.is_empty() { return None; } let mut o = String::new(); let mut i = 0; let mut key_arr = Vec::new(); for c in key.chars() { if !c.is_ascii_lowercase() { return None; } key_arr.push(c); } for c in s.chars() { let shift = key_arr[i % key_arr.len()] as i8 - 'a' as i8; let n = ((c as i8 - 'a' as i8 + dir * shift) % 26 + 26) % 26; o.push(char::from('a' as u8 + n as u8)); i += 1; } Some(o) } #}