Vigenere

Kml Pwn-

Nyla vg s tyjw bb gwe qydb lcm wcco unyw oz r Yqtsfelv Fqcvwr.
C'd pixwfg ck szrhly yrvg om anwcxlvby a evbebfv tbrw qf cfls
vlouh denkhzf zgna. Zw qf cfe sfx whuzt nf em soeifzdz jwlh
ziru nzd tbv qwvgws sfx unyw. Ahpzil, W ooocg rhgl lcbh bb
gsy C crdr mgu, ueg Q uche sfx nvumry kkqf cmt.

Sfxzf,
Vwlye

What would you do if you saw this message in your inbox?

I did. It's obviously encrypted, but I don't know how. The text still looks like words, and punctuation is preserved, so the cypher used transposed letters, but didn't change overall order of the cyphertext. I know how the message is signed, so tried to decrypt the text based on the known values.

My first guess was that the cypher used transposed one letter for another, but mapping likely phrases didn't map out. Here's more cyphertext, encrypted with the same scheme, to demo that. (Thanks, shakespeare!)

Who will believe my verse in time to come,
If it were fill'd with your most high deserts?
Ooo gmda fecmwce wc ntvsv mf aiwi ld godi,
Am id awgi fzpd'k wsxz nsui qgzt rmyw hejijas?
In a 1-1 mapping, each letter would map to one other. "Ooo" in the cyphertext translates to "Who" in cleartext, so my first guess was wrong.

Next, I tried modelling variable displacements. What if each character in the message was shifted by some number of places through the alphabet (a+1 = b; x+3 = a, and so on)? That would explain why some letters were often transposed for others. (In the second example, o is swapped with itself in "Who" and "come", but not "most". That hints that there's some sort of repetition in the key.)

I wrote a small script to compare offsets in the cypher against the expected signature, and was rewarded with a repeating number sequence! I found the key!

Returning to the example, the code:

#!/usr/bin/python2

a = "Ooo gmda fecmwce wc ntvsv mf aiwi ld godi,"
b = "Who will believe my verse in time to come,"

for x in a:
    print ord(x),

print
for x in b:
    print ord(x),

print
for x in range(len(a)):
    print (ord(a[x]) - ord(b[x]))%26,

print
for x in range(len(a)):
    print chr((ord(a[x]) - ord(b[x]))%26 + 97),

Returns:
79 111 111 32 103 109 100 97 32 102 101 99 109 119 99 101 32 119 99 32 110 116 118 115 118 32 109 102 32 97 105 119 105 32 108 100 32 103 111 100 105 44
87 104 111 32 119 105 108 108 32 98 101 108 105 101 118 101 32 109 121 32 118 101 114 115 101 32 105 110 32 116 105 109 101 32 116 111 32 99 111 109 101 44
18 7 0 0 10 4 18 15 0 4 0 17 4 18 7 0 0 10 4 0 18 15 4 0 17 0 4 18 0 7 0 10 4 0 18 15 0 4 0 17 4 0
s h a a k e s p a e a r e s h a a k e a s p e a r a e s a h a k e a s p a e a r e a


The first row above are the values of the encoded characters in ASCII. The second line shows the unencoded version. Line three is the difference between the two (mod 26), and line four is the ASCII character that maps to that difference. Discarding the superflous characters introduced around spaces reveals the password:

Who will believe my verse in time to come,
shaakespaeareshaakeaspearaesahakeaspaearea

Whowillbelievemyverseintimetocome
shakespeareshakespeareshakespeare

Shakespeare! (Creative, I know.)

With the encryption scheme figured out, I set about writing a program to decrypt the cyphertext. It inputs text to encrypt or decrypt, a password, and an optional output filename. It then runs through the file, transposing each valid character (chosen by user) by the amount specified in the password. The direction of transposition depends on whether it's encrypting or decrypting data.

The program is very long, and fairly polished. It's attached below.

p.s. After working through all of this, I learned that the cypher is called a Vigenere cypher, and encoding utilities are availiable online. Nothing lost, though!