diff --git a/README.md b/README.md index e06cfb907..e7035b49e 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ a set of rules that precisely define a sequence of operations. * `B` [Rail Fence Cipher](src/algorithms/cryptography/rail-fence-cipher) - a transposition cipher algorithm for encoding messages * `B` [Caesar Cipher](src/algorithms/cryptography/caesar-cipher) - simple substitution cipher * `B` [Hill Cipher](src/algorithms/cryptography/hill-cipher) - substitution cipher based on linear algebra + * `B` [Vigenère cipher](src/algorithms/cryptography/vigenère-cipher) - uses multiple ceasar ciphers to encrypt the plaintext * **Machine Learning** * `B` [NanoNeuron](https://github.com/trekhleb/nano-neuron) - 7 simple JS functions that illustrate how machines can actually learn (forward/backward propagation) * `B` [k-NN](src/algorithms/ml/knn) - k-nearest neighbors classification algorithm diff --git "a/src/algorithms/cryptography/vigen\303\250re-cipher/README.md" "b/src/algorithms/cryptography/vigen\303\250re-cipher/README.md" new file mode 100644 index 000000000..be47953db --- /dev/null +++ "b/src/algorithms/cryptography/vigen\303\250re-cipher/README.md" @@ -0,0 +1,35 @@ +# Vigenère Cipher + +The **Vigenère Cipher**, invented by the French cryptographer Blaise de Vigenère in the 16th century. A kind of [polyalphabetic substitution cipher](https://en.wikipedia.org/wiki/Polyalphabetic_cipher), which means that it uses multiple ceasar ciphers to encrypt the plaintext.The Vigenère Cipher encrypts messages using a keyword, which is usually a word or phrase. The keyword is repeated to match the length of the plaintext message. Each letter in the keyword determines the shift value for the corresponding letter in the plaintext, resulting in the encrypted message. + +## Vigenere Cipher Table + +The **Vigenere Cipher Table** consists of all the alphabets written 26 times in different rows. Each alphabet in every subsequent row and column is shifted cyclically to the left. This generates 26 Caesar Ciphers. + +![Vigenere Cipher table](https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Vigen%C3%A8re_square_shading.svg/2048px-Vigen%C3%A8re_square_shading.svg.png) + +## Encryption and Decryption +The key idea behind the **Vigenère cipher** is the use of a keyword or keyphrase to determine the shift value for each character in the plaintext. The keyword is repeated as necessary to match the length of the plaintext. Each letter in the keyword corresponds to a shift value (A=0, B=1, C=2, etc.). + +| **TEXT** | **KEY** | **KEYSTREAM** | **CIPHERTEXT** | +|:--------:|:-------:|:-------------:|:--------------:| +| ATTACKATDAWN | LEMON | LEMONLEMONLE | LXVOPVEFRNHR | +| THANKYOU | COVER | COVERCOV | VVVRBACP | + +Here are the steps to encrypt using the Vigenère cipher: + +1. **Choose a key**: Select a keyword or keyphrase to be used for encryption. + +2. **Align keyword with plaintext**: Repeatedly write the keyword above the plaintext message, aligning the first letter of the keyword with the first letter of the plaintext. If the keyword does not perfectly match the length of the plaintext, repeat it until it does. + +3. **Convert letters to numbers**: Assign numerical values to the letters of both the keyword and the plaintext. A=0, B=1, C=2, ..., Z=25. User the Vegenere Cipher Table to easily visualize the sifting process. + +4. **Encrypt each letter**: For each letter in the plaintext, find the corresponding letter in the keyword row and the plaintext column of the Vigenère square (a tabular representation of the Caesar ciphers). The intersection of the row and column gives the ciphertext letter. + +To **decrypt** a message encrypted using the Vigenère cipher, the recipient uses the same keyword to reverse the process and recover the original plaintext. + +## References + +- [Vigenère Cipher on Wikipedia](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher) +- [Polyalphabetic cipher on Wikipedia](https://en.wikipedia.org/wiki/Polyalphabetic_cipher) +- [Intellipat Vigenère Cipher Page](https://intellipaat.com/blog/vigenere-cipher/?US) \ No newline at end of file diff --git "a/src/algorithms/cryptography/vigen\303\250re-cipher/__test__/vigenereCipher.test.js" "b/src/algorithms/cryptography/vigen\303\250re-cipher/__test__/vigenereCipher.test.js" new file mode 100644 index 000000000..06e940dbd --- /dev/null +++ "b/src/algorithms/cryptography/vigen\303\250re-cipher/__test__/vigenereCipher.test.js" @@ -0,0 +1,49 @@ +import { vigenereCipherEncrypt, vigenereCipherDecrypt } from '../vigenereCipher'; + +// Test cases for vigenereCipherEncrypt function +describe('Vigenère Cipher Encryption', () => { + it('should encrypt a message with a key', () => { + const message = 'hello world'; + const key = 'key'; + const expectedOutput = 'riijvs utetm'; + expect(vigenereCipherEncrypt(message, key)).toEqual(expectedOutput); + }); + + it('should encrypt a message with a longer key', () => { + const message = 'the quick brown fox'; + const key = 'jump'; + const expectedOutput = 'ymh xnvlk grpxc psx'; + expect(vigenereCipherEncrypt(message, key)).toEqual(expectedOutput); + }); + + it('should handle uppercase letters in the message and key', () => { + const message = 'HELLO'; + const key = 'KEY'; + const expectedOutput = 'RIJVS'; + expect(vigenereCipherEncrypt(message, key)).toEqual(expectedOutput); + }); +}); + +// Test cases for vigenereCipherDecrypt function +describe('Vigenère Cipher Decryption', () => { + it('should decrypt a message with a key', () => { + const encryptedMessage = 'riijvs utetm'; + const key = 'key'; + const expectedOutput = 'hello world'; + expect(vigenereCipherDecrypt(encryptedMessage, key)).toEqual(expectedOutput); + }); + + it('should decrypt a message with a longer key', () => { + const encryptedMessage = 'ymh xnvlk grpxc psx'; + const key = 'jump'; + const expectedOutput = 'the quick brown fox'; + expect(vigenereCipherDecrypt(encryptedMessage, key)).toEqual(expectedOutput); + }); + + it('should handle uppercase letters in the encrypted message and key', () => { + const encryptedMessage = 'RIJVS'; + const key = 'KEY'; + const expectedOutput = 'HELLO'; + expect(vigenereCipherDecrypt(encryptedMessage, key)).toEqual(expectedOutput); + }); +}); diff --git "a/src/algorithms/cryptography/vigen\303\250re-cipher/vigenereCipher.js" "b/src/algorithms/cryptography/vigen\303\250re-cipher/vigenereCipher.js" new file mode 100644 index 000000000..32b038d71 --- /dev/null +++ "b/src/algorithms/cryptography/vigen\303\250re-cipher/vigenereCipher.js" @@ -0,0 +1,75 @@ + +// Create alphabet array: ['a', 'b', 'c', ..., 'z']. +const englishAlphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); + +/** + * @param {character} c + * @param {string[]} alphabet + * @return {number} + */ +const indexInLetters = (c, alphabet=englishAlphabet) => { + c = c.toLowerCase(); + for (let i = 0; i < alphabet.length; i++) { + if (alphabet[i] === c) return i; + } + return -1; +} + + +/** + * @param {string} message + * @param {string} key + * @param {string[]} alphabet + * @return {string} + */ + +export const vingenereCipherEncrypt = (message, key, alphabet=englishAlphabet) => { + message = message.toLowerCase(); + key = key.toLowerCase(); + const str = message.split(''); + let keyIndex = 0; + const keyArr = new Array(str.length); + let newStr = ''; + for (let i = 0; i < str.length; i++) { + if (keyIndex >= key.length) keyIndex %= key.length; + keyArr[i] = key[keyIndex]; + keyIndex++; + } + for (let i = 0; i < str.length; i++) { + const index = indexInLetters(str[i]); + if (index === -1) newStr += ' '; + else newStr += alphabet[(indexInLetters(keyArr[i]) + index) % 26]; + } + return newStr; +} + +/** + * @param {string} message + * @param {string} key + * @param {string[]} alphabet + * @return {string} + */ + +export const vingenereCipherDencrypt = (message, key, alphabet=englishAlphabet) => { + message = message.toLowerCase(); + key = key.toLowerCase(); + const str = message.split(''); + let keyIndex = 0; + const keyArr = new Array(str.length); + let newStr = ''; + for (let i = 0; i < str.length; i++) { + if (keyIndex >= key.length) keyIndex %= key.length; + keyArr[i] = key[keyIndex]; + keyIndex++; + } + for (let i = 0; i < str.length; i++) { + const index = indexInLetters(str[i]); + if (index === -1) newStr += ' '; + else { + let newIndex = index - indexInLetters(keyArr[i]); + if (newIndex < 0) newIndex += 26; + newStr += alphabet[newIndex]; + } + } + return newStr; +} \ No newline at end of file