Re: Factor

Factor: the language, the theory, and the practice.

Vigenère Cipher

Monday, August 21, 2023

#math

The Vigenère cipher is a historically interesting method of encrypting a piece of text using a Caesar cipher where each letter is encoded based on a different letter from a repeating input “key” text. If the recipient knows the key, they can recover the plaintext by reversing the encoding process.

For example, suppose that the plaintext to be encrypted is

attackatdawn

The person sending the message chooses a keyword and repeats it until it matches the length of the plaintext, for example, the keyword “LEMON”:

LEMONLEMONLE

We start by defining the available alphabet:

CONSTANT: LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

We can implement this using an inner word and a flag for whether we are encrypting or decrypting a message. For convenience, we will use local variables and make sure both the key and the message are uppercase and drop all non-letters from the input:

:: viginere ( msg key encrypt? -- str )
    msg >upper :> MSG
    key >upper :> KEY

    [
        0 MSG [| ch |
            ch LETTERS index [| i |
                [ 1 + KEY length mod i ] keep
                KEY nth LETTERS index
                encrypt? [ + ] [ - ] if
                LETTERS length [ + ] [ mod ] bi
                LETTERS nth ,
            ] when*
        ] each drop
    ] "" make ;

: >vigenere ( msg key -- encrypted ) t viginere ;

: vigenere> ( msg key -- decrypted ) f viginere ;

We can make some test cases showing that it works:

{ "VBVTQBYOLCPB" } [
    "ATTACKATDAWN" "VICTORY" >vigenere
] unit-test

{ "ATTACKATDAWN" } [
    "VBVTQBYOLCPB" "VICTORY" vigenere>
] unit-test

It gained a reputation for being exceptionally strong – even being described as “unbreakable” and “impossible of translation”. It turns out there is a big difference between difficult and impossible, and a cryptanalysis ultimately found various weaknesses that enabled the cipher to be broken or attacked in certain ways.

While not inventing the cipher that holds his name, Vigenère actually invented a stronger version called an autokey cipher which modifies the key during the encoding and decoding process avoiding one of the weak points when the key is shorter than the message and the letters are reused.

It is a smidge more complex to implement but shares the same structure as the version above:

:: autokey ( msg key encrypt? -- str )
    msg >upper :> MSG
    key >upper >sbuf :> KEY

    [
        0 MSG [| ch |
            ch LETTERS index [| i |
                [ 1 + i ] keep
                KEY encrypt? [ ch suffix! ] when nth
                LETTERS index
                encrypt? [ + ] [ - ] if
                LETTERS length [ + ] [ mod ] bi
                LETTERS nth
                encrypt? [ KEY over suffix! drop ] unless ,
            ] when*
        ] each drop
    ] "" make ;

: >autokey ( msg key -- encrypted ) t autokey ;

: autokey> ( msg key -- decrypted ) f autokey ;

And some more test cases:

{ "FWGLCDEJIKG" } [
    "AWESOMENESS" "FACTOR" >autokey
] unit-test

{ "AWESOMENESS" } [
    "FWGLCDEJIKG" "FACTOR" autokey>
] unit-test