DEV Community

Vince Campanale
Vince Campanale

Posted on • Edited on • Originally published at vincecampanale.com

"Using the Vigenère Cipher to Encrypt a Message with Javascript"

This article is technically Part 3 of a three part series, but don't worry, the first two parts are not critical to understanding this part (and this is the more interesting section tbh).

Here are the links to the others if you're curious:
Using the Vigenère Cipher to Encrypt a Message (Part 1)
Using the Vigenère Cipher to Encrypt a Message (Part 2)

In Part 1, I gave a brief overview of the Vigenère cipher and discussed the two approaches to solving it (the two approaches that I could come up with - there are definitely others). In Part 2, I covered the first approach, which is essentially a Caesar cipher with a dynamic shift number. In this part, I'm going to step through the more interesting solution - the way it's really intended to be done - using the magical Vigenère table.

The Vigenère table looks like this:

By Brandon T. Fields (cdated) - Based upon Vigenere-square.png by en:User:Matt Crypto. This version created by bdesham in Inkscape, and modified by cdated to include visual guides.This vector image was created with Inkscape., Public Domain, Link

Don't worry about deciphering that behemoth right now, you'll gain a deeper understanding as I go over the code to build this thing.

The process breaks down into four primary functions: the generateAlphabet function, the generateVigenereTable function, the encodeWithTable function, and of course the vigenereCipherWithTable function. Note: this solution is rather imperative and I hope to reimplement it in a more declarative way in the future so follow me on twitter @_vincecampanale or on dev.to for humor and updates (all about JS of course).

So here's the plan:

1) Generate an alphabet starting with a given letter (a in the first column, b in the second, etc) - note: the alphabet must wrap around to the beginning when it reaches z
2) Generate a Vigenere table
  - The keys consist of the standard alphabet (a-z)
  - Each key's value is an alphabet starting with that key and wrapping back   around to a (each value is 26 letters)
3) Encode the message by looking up each letter of the original message in the   keys of the Vigenere table, then traversing the table to get the value from the   character code of the keyword letter  
4) Put it all together in the final function  
Enter fullscreen mode Exit fullscreen mode

Step 1: Build the generateAlphabet function

In this function, the parameter will be a starting index. We are going to iterate over twenty-six char codes, starting at the provided start index. Presumably, the first char code will be 97, and they will go up from there. In order to account for char codes over 122, we add some if/else logic into the String.fromCharCode method. Ternary operators allow us to keep this code succinct.

function generateAlphabet(start) {
  let alphabet = [];
  //from start index to 26 chars later
  for (let i = start; i < start + 26; i++) {
    //convert the char code into a letter and push it to the alphabet array
    alphabet.push(String.fromCharCode(
      i > 122 ? i - 26 : //if char code > 122, return code - 26, else
      i < 97  ? i + 26 : //if char code < 97, return code + 26, else
      i                  //just return the code
    ));
  }
  return alphabet; //return the alphabet array
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Build the generateVigenereTable function

Dedicating a function to generating alphabets with different starting character codes allows us to keep the Vigenère table function surprisingly simple.

All we need to do is instantiate an empty object, table. Load the keys of that object up with the standard alphabet, starting with the letter 'a' (char code 97). Then for each key in the table, we generate an alphabet that starts at the key's index. So the second key ('b') has an alphabet starting with b and wrapping back around to end with a. The third key ('c') has an alphabet starting with c and wrapping back around to end with b. And so on.

In code:

//generate an object, where each key is a letter and each value is another alphabet
function generateVigenereTable() {
  let table = {}; //instantiate a temporary object to hold the table
  table.keys = generateAlphabet(97); //set the keys of the object equal to the standard alphabet (starting at 97)
  table.keys.forEach((key, index) => { table[key] = generateAlphabet(97 + index) });  //set the value of each key as the alphabet
  return table; //return the table
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Encode each character using a Vigenère table

This is the most important step in the solution - the piece where we put our Vigenère table to use. See the comments for a line-by-line explanation.

function encodeWithTable( message, keywordStr ) {
  let messageArray = message.split(''); //split the message into an array
  let keywordArray = keywordStr.split(''); //split the keyword string into an array
  messageArray.forEach((letter, index) => { //for each letter and index in the message array
    let messageChar = letter; //make a temp variable to hold the letter
    let keywordChar = keywordArray[index]; //get the corresponding letter from the keyword string using the index

    let keywordCharIndex = keywordChar.charCodeAt(0) - 97; //get the index of the keyword by subtracting 97 from the charcode
    let vigenereTable = generateVigenereTable(); //create a vigenere table

    let cipherChar = vigenereTable[messageChar][keywordCharIndex]; //look up the corresponding letter in the table

    messageArray[index] = cipherChar; //replace the letter in the message with the cipher letter
  });
  return messageArray.join(''); //convert the messageArray back to a string and return it
}
Enter fullscreen mode Exit fullscreen mode

Step 4: The Actual Function

Since we've taken the time to break down our problem and write thorough helper functions, the cipher function itself is nothing special. In fact, it's identical to the function in Part 2, except now we are encoding with the Vigenère table, rather than with the boring old Caesar cipher.

function vigenereCipherWithTable(message, keyword = "lemon") {
  for ( let i = 0; i < message.length; i++ ) {
    keyword += keyword; // repeat the keyword a bunch of times
  }
  let keywordStr = keyword.substr( 0, message.length ); // cut the keyword string so it's the same length as the message
  let ciphertext = encodeWithTable( message, keywordStr ); // encode the string using the vigenere table
  return ciphertext //return the cipher text!
}
Enter fullscreen mode Exit fullscreen mode

And there you have it! Have fun passing secret messages back and forth with your friends...good luck decoding them though... ;)

Hope this was enjoyable and helpful. Shoot me an email or tweet at me with comments, questions, complaints, and suggestions.

Til next time ☮

Top comments (7)

Collapse
 
keeleyhammond profile image
Keeley Hammond 🌻

What a great article - I'm going to give this a try in my free time.

A little bit of an aside, but have you write "The Code Book: by Simon Singh? I think you'd really like it, and I'd love to see your take on an Enigma cipher.

Collapse
 
vincecampanale profile image
Vince Campanale

I have not read that book, but I'm always on the look out for a good stack of paper. I'll check it out :) thanks

Collapse
 
phlash profile image
Phil Ashby

Fun to play with, thanks Vince :)

For those considering doing actual crypto in the browser, remember the mantra 'Never write your own crypto' (caveat 'unless you have a university on hand to test it'), then take a look at this nicely curated list: gist.github.com/jo/8619441

I tried to convince our PCI-DSS auditor last year that hashing data in the browser took it out of scope of the audit before it reached our servers.. no luck :(

Collapse
 
antoinette0x53 profile image
Antoinette Maria

This is a super cool exercise! And as long as I never catch these 'encrypted' messages while sniffing traffic in a coffee shop, everything will be peachy xD

Collapse
 
jonathan_hepp profile image
Jonathan Hepp

Why are you spelling Vigenère wrong?

Collapse
 
vincecampanale profile image
Vince Campanale • Edited

"Some men just want to watch the world burn." - Alfred Pennyworth

I should probably fix that, though...

Collapse
 
jonathan_hepp profile image
Jonathan Hepp

Oh your quote would have been so perfect if you'd mispelled Alfred's name... Nice article though.