From: Grégory Burri Date: Wed, 2 Sep 2020 09:46:28 +0000 (+0200) Subject: Add a new format version (2) for encrypted text. X-Git-Url: http://git.euphorik.ch/index.cgi?a=commitdiff_plain;h=bec878adc200ba7794d8510e2599a95b9050f976;p=rup.git Add a new format version (2) for encrypted text. --- diff --git a/src/consts.rs b/src/consts.rs index 3a1ef9e..376dee8 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,4 +1,3 @@ -pub static FILE_CONF: &str = "conf.ron"; -pub static FILE_KEY: &str = "key.secret"; -pub static CURRENT_MESSAGE_VERSION: &str = "1"; -pub static DEFAULT_MESSAGE: &str = "Marc, roule un pet'!"; \ No newline at end of file +pub const FILE_CONF: &str = "conf.ron"; +pub const FILE_KEY: &str = "key.secret"; +pub const DEFAULT_MESSAGE: &str = "Marc, roule un pet'!"; \ No newline at end of file diff --git a/src/crypto.rs b/src/crypto.rs index caf34f7..26cbe68 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,8 +1,6 @@ use openssl::{symm, sha::sha256}; use rand::prelude::*; -use crate::consts; - #[derive(Debug)] pub enum KeyError { UnableToDecodeBase64Key, @@ -12,13 +10,15 @@ pub enum KeyError { #[derive(Debug)] pub enum EncryptError { KeyError(KeyError), + UnsupportedVersion(u8), UnableToEncrypt, } #[derive(Debug)] pub enum DecryptError { KeyError(KeyError), - WrongMessageVersion, + UnableToParseVersion, + UnsupportedVersion(u8), MessageToShort, UnableToDecodeBase64Message, UnableToDecrypt, @@ -33,53 +33,85 @@ fn decode_key(key: &str) -> Result, KeyError> { } } -/// Encrypt the given text with the given key. The key length must be 128 bits encoded in base64. -/// Ouput format: "1" + base_64( + + ) +/// Encrypt the given text with the given key (first version). The key length must be 128 bits encoded in base64. +/// Ouput formats: +/// * 'version' = 1: "1" + base_64( + hash(message) + aes(message)) +/// * 'version' = 2: "2" + base_64( + aes(hash(message) + message)) /// IV: 16 bytes randomized. /// Mode : CBC. -pub fn encrypt(key: &str, plain_text: &str) -> Result { +pub fn encrypt(key: &str, plain_text: &str, version: u8) -> Result { let key_as_bytes = decode_key(key).map_err(EncryptError::KeyError)?; let text_as_bytes = plain_text.as_bytes(); + let hash_text = sha256(&text_as_bytes); let iv = rand::thread_rng().gen::<[u8; 16]>(); let cipher_text = - symm::encrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(&iv), text_as_bytes) - .map_err(|_e| EncryptError::UnableToEncrypt)?; - - let hash_text = sha256(&text_as_bytes); + if version == 1 { + symm::encrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(&iv), text_as_bytes) + .map_err(|_e| EncryptError::UnableToEncrypt)? + } else if version == 2 { + symm::encrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(&iv), &[&hash_text, text_as_bytes].concat()) + .map_err(|_e| EncryptError::UnableToEncrypt)? + } else { + return Err(EncryptError::UnsupportedVersion(version)) + }; let mut result: Vec = Vec::new(); result.extend(&iv); - result.extend(&hash_text); + + if version == 1 { + result.extend(&hash_text); + } + result.extend(&cipher_text); - Ok(String::from("1") + &base64::encode(&result)) + Ok(version.to_string() + &base64::encode(&result)) } /// Decrypt the given text with the given key. The key length must be 128 bits encoded in base64. -/// Input format: "1" + base_64( + + ) +/// Input formats: +/// * version 1: "1" + base_64( + hash(message) + aes(message)) +/// * version 2: "2" + base_64( + aes(hash(message) + message)) pub fn decrypt(key: &str, cipher_text: &str) -> Result { let key_as_bytes = decode_key(key).map_err(DecryptError::KeyError)?; // Can't decrypt a message with the wrong version. - if !cipher_text.starts_with(consts::CURRENT_MESSAGE_VERSION) { return Err(DecryptError::WrongMessageVersion) } + let first_char = &cipher_text[..1]; + let version: u8 = first_char.parse().map_err(|_e| DecryptError::UnableToParseVersion)?; + + if version != 1 && version != 2 { + return Err(DecryptError::UnsupportedVersion(version)) + } let cipher_text_bytes = - base64::decode(&cipher_text.as_bytes()[consts::CURRENT_MESSAGE_VERSION.as_bytes().len()..]) + base64::decode(&cipher_text.as_bytes()[1..]) .map_err(|_e| DecryptError::UnableToDecodeBase64Message)?; if cipher_text_bytes.len() <= 48 { return Err(DecryptError::MessageToShort) } let iv = &cipher_text_bytes[0..16]; - let hash = &cipher_text_bytes[16..48]; - let encrypted_message = &cipher_text_bytes[48..]; - - let plain_message_bytes = - symm::decrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(iv), encrypted_message) - .map_err(|_e| DecryptError::UnableToDecrypt)?; - if sha256(&plain_message_bytes) != hash { return Err(DecryptError::HashMismatch) } + let (plain_message_bytes, hash) = + if version == 1 { + let encrypted_message = &cipher_text_bytes[48..]; + ( + symm::decrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(iv), encrypted_message) + .map_err(|_e| DecryptError::UnableToDecrypt)?, + cipher_text_bytes[16..48].to_vec() + ) + } else { + let encrypted_message = &cipher_text_bytes[16..]; + let plain_text = + symm::decrypt(symm::Cipher::aes_128_cbc(), &key_as_bytes, Some(iv), encrypted_message) + .map_err(|_e| DecryptError::UnableToDecrypt)?; + ( + plain_text[32..].to_vec(), + plain_text[0..32].to_vec() + ) + }; + + if sha256(&plain_message_bytes) != hash[..] { return Err(DecryptError::HashMismatch) } let plain_message = String::from_utf8(plain_message_bytes) diff --git a/src/main.rs b/src/main.rs index 1d25a69..8d0a5c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,7 +127,8 @@ fn process_args(key: &str) -> bool { } else if let Some((position_arg_encrypt, _)) = args.iter().find_position(|arg| arg == &"--encrypt") { match args.get(position_arg_encrypt + 1) { Some(mess_to_encrypt) => { - match crypto::encrypt(&key, mess_to_encrypt) { + // Encrypt to version 2 (version 1 is obsolete). + match crypto::encrypt(&key, mess_to_encrypt, 2) { Ok(encrypted_mess) => { let encrypted_mess_encoded = percent_encoding::utf8_percent_encode(&encrypted_mess, percent_encoding::NON_ALPHANUMERIC).to_string(); println!("Encrypted message percent-encoded: {}", encrypted_mess_encoded); },