.vigenère was cool
01 Jun 2014
Well… Actually Giovan Battista Bellaso was cool because he invented the Vigenère cipher but wasn’t credited for his discovery by many. All around Bellaso seems to be a fairly talented person (Vigenère too) but one day he devised a method to encipher a message which was quite successful.
As a developer I often feel like I have tangential experience in fields I know little about. When people would tell me “Vigenère cipher is broken” I would blindly repeat what they said; usually followed by the explanation common in Hollywood movies describing statistical analysis of cipher text to guess keys. Cryptography was a field which I felt I understood until researching earlier non-machine cryptography.
One cipher which stood out was Bellaso’s cipher as it is not susceptible to using common words or characters in order to decode the message. The only way I can find to decrypt messages encrypted with Bellaso’s cipher is to either have the key or know the language the message is written in and know the key length. If I know the language and the key length of the message then I have enough intelligence to steal the key too.
I am not much of a historian so in order for me to enjoy the work which was done I implemented a Vigenère cipher in coffeescript and here it is . It needs some sort of interactivity.
cipher = ( key , message , cipher ) ->
if containsInvalidKeyCharacters ( key ) or containsInvalidMessageCharacters ( message )
throw "Invalid characters in key or message."
fullKey = matchKeyLengthToMessage key , message . length
alphabetSize = 26
fullKeyCharacterCodes = fullKey . split ( "" ). map ( character ) -> character . charCodeAt 0
fullMessageCharacterCodes = message . split ( "" ). map ( character ) -> character . charCodeAt 0
cipherCharacterCodes = for keyCharacterCode , i in fullKeyCharacterCodes
messageAlphabetCharacterCode = convertUnicodeCharacterCodeToAlphabet fullMessageCharacterCodes [ i ]
keyAlphabetCharacterCode = convertUnicodeCharacterCodeToAlphabet keyCharacterCode
cipherAlphabetCharacter = cipher (
keyAlphabetCharacterCode ,
messageAlphabetCharacterCode ,
alphabetSize )
# The last character Z will match to be 0 and needs to be set
# as the last character.
# TODO I am positive there is a workaround for this
if cipherAlphabetCharacter is 0
cipherAlphabetCharacter = alphabetSize
cipherCharacter = convertAlphabetCharacterCodeToUnicode cipherAlphabetCharacter
String . fromCharCode . apply null , cipherCharacterCodes
# The Character Codes in Javascript UNICODE do not match to 1-26 for A-Z so this will offset the character codes back.
# This translates to A in UNICODE 65
convertUnicodeCharacterCodeToAlphabet = ( code , unicodeOffset = 64 ) ->
code - unicodeOffset
convertAlphabetCharacterCodeToUnicode = ( code , unicodeOffset = 64 ) ->
code + unicodeOffset
containsInvalidKeyCharacters = ( string ) ->
/[^A-Z]/ . test ( string )
containsInvalidMessageCharacters = ( string ) ->
containsInvalidKeyCharacters string
matchKeyLengthToMessage = ( key , messageLength ) ->
keyCharacters = key . split ( "" )
if keyCharacters . length is 0
throw "Key is blank."
if messageLength is 0
throw "Message is empty."
fullKeyCharacters = for i in [ 0 .. messageLength - 1 ]
keyCharacters [ i % keyCharacters . length ]
fullKeyCharacters . join ""
#############################################
## Actual Code ##
#############################################
key = "CRYPTO"
message = "WHATANICEDAYTODAY"
vigen è reEncrypt = ( key , message ) ->
cipher key , message , ( keyCharacterCode , messageCharacterCode , alphabetSize ) ->
( keyCharacterCode + messageCharacterCode ) % alphabetSize
vigen è reDecrypt = ( key , encryptedMessage ) ->
cipher key , encryptedMessage , ( keyCharacterCode , messageCharacterCode , alphabetSize ) ->
( messageCharacterCode - keyCharacterCode + alphabetSize ) % alphabetSize
encryptedMessage = vigen è reEncrypt ( key , message )
decryptedMessage = vigen è reDecrypt ( key , encryptedMessage )
console . log """
Encrypting #{ message } with a key of #{ key } using Vigenère cipher.
Encrypted message is #{ encryptedMessage } .
Decrypted message is #{ decryptedMessage } .
"""
#############################################
## Tests ##
#############################################
runTests = ->
assert = require 'assert'
key = "CRYPTO"
message = "WHATANICEDAYTODAY"
messageLength = message . split ( "" ). length
assert . equal vigen è reEncrypt ( key , message ), "ZZZJUCLUDTUNWGCQS"
assert . equal vigen è reEncrypt ( "ABC" , "ABC" ), "BDF"
assert . equal vigen è reEncrypt ( "ABC" , "A" ), "B"
assert . equal vigen è reEncrypt ( "Z" , "ZAZ" ), "ZAZ" #Interesting
assert . equal vigen è reEncrypt ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), "BDFHJLNPRTVXZBDFHJLNPRTVXZ"
assert . equal vigen è reEncrypt ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , "ZZZZZZZZZZZZZZZZZZZZZZZZZZ" ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
assert . equal vigen è reDecrypt ( key , "ZZZJUCLUDTUNWGCQS" ), message
assert . equal vigen è reDecrypt ( "ABC" , "BDF" ), "ABC"
assert . equal vigen è reDecrypt ( "ABC" , "B" ), "A"
assert . equal vigen è reDecrypt ( "Z" , "ZAZ" ), "ZAZ" #Interesting
assert . equal vigen è reDecrypt ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , "BDFHJLNPRTVXZBDFHJLNPRTVXZ" ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
assert . equal vigen è reDecrypt ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), "ZZZZZZZZZZZZZZZZZZZZZZZZZZ" # Hehe sweet
assert . equal matchKeyLengthToMessage ( key , messageLength ), "CRYPTOCRYPTOCRYPT" , "Oops, key length mismatch"
assert . throws ( -> matchKeyLengthToMessage ( key , 0 )), "Blank Message"
assert . throws ( -> matchKeyLengthToMessage ( "" , messageLength )), "Blank Key"
assert containsInvalidKeyCharacters ( "123A" )
assert not containsInvalidKeyCharacters ( "ABC" )
assert containsInvalidMessageCharacters ( "123A" )
assert not containsInvalidMessageCharacters ( "ABC" )
assert . equal convertUnicodeCharacterCodeToAlphabet ( 65 ), 1
assert . equal convertAlphabetCharacterCodeToUnicode ( 1 ), 65
assert . doesNotThrow ( -> cipher ( key , message , -> ))
assert . throws ( -> cipher ( "" , message , -> )), "Invalid Key"
assert . throws ( -> cipher ( key , "" , -> )), "Invalid Message"
assert . throws ( -> cipher ( "BLA2" , message , -> )), "Invalid Characters Key"
assert . throws ( -> cipher ( key , 0 , -> )), "Invalid Characters Message"
runTests ()
Interesting note, UTF8 in filenames or code messes up Github.