Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vigenère cipher #1056

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions src/algorithms/cryptography/vigenère-cipher/README.md
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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);
});
});
75 changes: 75 additions & 0 deletions src/algorithms/cryptography/vigenère-cipher/vigenereCipher.js
Original file line number Diff line number Diff line change
@@ -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;
}