vigenere Algorithm

The Vigenère Cipher Algorithm is a classic method of encrypting alphabetic text that uses a simple form of polyalphabetic substitution. Developed in the mid-16th century by French cryptographer Blaise de Vigenère, the algorithm relies on a repeated shift of characters based on a secret key. It is considered an improvement over the Caesar cipher, which only uses a single fixed shift, making the Vigenère cipher more secure and harder to break as it uses multiple shifts, thus increasing the complexity of the encryption. The Vigenère Cipher Algorithm works by using a table of alphabets called the Vigenère square, which consists of 26 rows of shifted alphabets. The plaintext message is first combined with a secret key, which is repeated or truncated to match the length of the plaintext. The encryption is performed by looking up the plaintext letter in the first column of the table and the corresponding key letter in the top row, then finding the intersection of these two letters in the table, which forms the ciphertext. Decryption is done by reversing the process: finding the ciphertext letter in the top row, then going down the column to find the key letter, and finally, locating the plaintext letter in the first column. The Vigenère cipher provides a basic level of security, but it can be broken with modern cryptanalysis techniques, such as frequency analysis and the Kasiski examination.
//! Vigenère Cipher
//!
//! # Algorithm
//!
//! Rotate each ascii character by the offset of the corresponding key character.
//! When we reach the last key character, we start over from the first one.
//! This implementation does not rotate unicode characters.

/// Vigenère cipher to rotate plain_text text by key and return an owned String.
pub fn vigenere(plain_text: &str, key: &str) -> String {
    // Remove all unicode and non-ascii characters from key
    let key: String = key.chars().filter(|&c| c.is_ascii_alphabetic()).collect();
    key.to_ascii_lowercase();

    let key_len = key.len();
    if key_len == 0 {
        return String::from(plain_text);
    }

    let mut index = 0;

    plain_text
        .chars()
        .map(|c| {
            if c.is_ascii_alphabetic() {
                let first = if c.is_ascii_lowercase() { b'a' } else { b'A' };
                let shift = key.as_bytes()[index % key_len] - b'a';
                index = index + 1;
                // modulo the distance to keep character range
                (first + (c as u8 + shift - first) % 26) as char
            } else {
                c
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn empty() {
        assert_eq!(vigenere("", "test"), "");
    }

    #[test]
    fn vigenere_base() {
        assert_eq!(
            vigenere("LoremIpsumDolorSitAmet", "base"),
            "MojinIhwvmVsmojWjtSqft"
        );
    }

    #[test]
    fn vigenere_with_spaces() {
        assert_eq!(
            vigenere(
                "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
                "spaces"
            ),
            "Ddrgq ahhuo hgddr uml sbev, ggfheexwljr chahxsemfy tlkx."
        );
    }

    #[test]
    fn vigenere_unicode_and_numbers() {
        assert_eq!(
            vigenere("1 Lorem ⏳ ipsum dolor sit amet Ѡ", "unicode"),
            "1 Fbzga ⏳ ltmhu fcosl fqv opin Ѡ"
        );
    }

    #[test]
    fn vigenere_unicode_key() {
        assert_eq!(
            vigenere("Lorem ipsum dolor sit amet", "😉 key!"),
            "Vspoq gzwsw hmvsp cmr kqcd"
        );
    }

    #[test]
    fn vigenere_empty_key() {
        assert_eq!(vigenere("Lorem ipsum", ""), "Lorem ipsum");
    }
}

LANGUAGE:

DARK MODE: