
Vigenère Cipher: Caesar With a Keyword
In my Caesar cipher post the tool broke Caesar with letter statistics. That works because Caesar shifts every letter by the same amount. Every E in the message becomes the same letter in the ciphertext, and the frequencies of the language stay visible, only moved to other letters.
The Vigenère cipher changes that. It is the next cipher in my tool, which is now called Cipher Breaker.
How the cipher works
Vigenère uses a keyword instead of a single number. Each letter of the keyword stands for a shift, A for 0, B for 1, up to Z for 25. LEMON gives the shifts 11, 4, 12, 14 and 13.
The keyword is written under the message and repeated until the end. Each letter is shifted by the keyword letter below it:
Plaintext: ATTACKATDAWN
Keyword: LEMONLEMONLE
Ciphertext: LXFOPVEFRNHR
The four A’s in ATTACKATDAWN become L, O, E and N. The same letter in the message ends up as different letters in the ciphertext, so counting letters in the ciphertext no longer shows which one is E.
Giovan Battista Bellaso described the cipher in 1553. It was later named after Blaise de Vigenère and was called “le chiffre indéchiffrable”, the indecipherable cipher. Friedrich Kasiski published a general way to break it in 1863.
In the tool
vigenere.py has three functions. keyword_shifts turns the keyword into shifts and
rejects anything that is not the letters A to Z. encrypt and decrypt go through the
text and shift each letter with shift_char from caesar.py, the same function Caesar
uses. The difference from Caesar is where the shift comes from.
The keyword only moves on at letters. Spaces and punctuation do not use up a keyword letter, so “Attack at dawn!” and ATTACKATDAWN give the same ciphertext.
A keyword of one letter gives the same shift for every letter, which is the Caesar cipher. With the keyword D the tool produces what Caesar with key 3 produces, and a test checks that.
On the command line, --cipher vigenere selects it:
python main.py encrypt "Attack at dawn!" --cipher vigenere --key LEMON
python main.py decrypt "LXFOPVEFRNHR" --cipher vigenere --key LEMON
Without arguments, python main.py asks for the cipher, the mode, the text and the
keyword:

When encrypting, the tool keeps only the letters A to Z and puts them in capitals. This applies to Caesar as well. With spaces left in, anyone reading the ciphertext would know how long each word is. Historically, messages were sent the same way, often split into groups of five letters, which show nothing because every group has the same length.
Tests
The tests check the cipher against the standard example above, check that spaces do not
use up keyword letters, compare a one-letter keyword with Caesar, and check that
encrypting and then decrypting returns the original text for four different keywords.
Keywords such as lem0n or le mon must be rejected.
Known limits
No breaking yet. The tool can encrypt and decrypt with Vigenère, but brute only
works for Caesar.
No spaces after decrypting. As with Caesar, the spaces are removed before encrypting and cannot be restored. Decrypting gives ATTACKATDAWN.
Only the letters A to Z. The keyword and the message are limited to the English alphabet.
What I learned
Caesar as a special case
A keyword of one letter gives every letter the same shift, so Vigenère with the keyword D is Caesar with key 3. I turned that into a test. Since both ciphers use the same function to shift a letter, the test checks the Vigenère part: turning the keyword into shifts and applying them in order.
Keeping old commands working
Adding a second cipher changed the command line. I added --cipher with Caesar as the
default, so every command in the Caesar post still works as written. A test runs one of
those commands to make sure it stays that way.
What comes next
Breaking it. The keyword repeats, and that can be used against it. In the example, the T’s at positions 3 and 8 both become F. They are 5 letters apart, which is the length of LEMON, so both were shifted by the same keyword letter.
Breaking Vigenère takes two steps. First, find the length of the keyword. If the length is 5, every fifth letter of the ciphertext was shifted by the same amount. Second, split the ciphertext into those groups and break each one as a Caesar cipher.
For the second step, the single-letter statistics from the first attempt in the Caesar post become useful again. The letters in one group do not stand next to each other in the message, so letter pairs cannot help there. With a five-letter keyword, each group only gets every fifth letter, so the ciphertext has to be much longer than for Caesar.
The code is at github.com/DilaraKoc1/cipher-breaker.