The 538 Riddler: Encryption Blues

This week’s Riddler was mostly fun with a solid day of frustration thrown in. I was able to get the answers but I didn’t use much code to do it, but I’ll show what I did write. Also, a brief note about scheduling for the future. I’m going to start posting my write-ups after the 9 PM (PST) deadline for submitting answers on the Riddler site. I’ll still submit my answers to them on time but I don’t want to post answers here before the deadline. There’s a five-day window between the submission deadline and the next riddle and I think it makes sense to post answers in that window so as not to spoil anyone who might be looking for hints, but not interested in finding the answer. Once the submission deadline has passed, I’ll go ahead and post my answer.

Here’s this week’s problem:

Here are four different coded messages. Each has a different encryption scheme and they progress, I think, from easiest to toughest to crack. Submit the decrypted messages as your answers.

1. A zsnw kmuuwkkxmddq kgdnwv lzw XanwLzajlqWayzl Javvdwj!

2. xckik acvlbeg oz mmqn xnlautw. gzag, mwcht, kbjzh… ulw cpeq edr mom dhqx lksxlioil?

3. hy vg nw rh ev pr is or tf?

4. 😎😊, πŸ˜“πŸ˜‡πŸ˜€πŸ˜“β€™πŸ˜’ πŸ˜ˆπŸ˜“. πŸ˜πŸ˜ŽπŸ˜– πŸ˜†πŸ˜„πŸ˜“ πŸ˜πŸ˜€πŸ˜‚πŸ˜Š πŸ˜“πŸ˜Ž πŸ˜–πŸ˜ŽπŸ˜‘πŸ˜Š. πŸ˜‡πŸ˜„πŸ˜˜, πŸ˜€πŸ˜“ πŸ˜‹πŸ˜„πŸ˜€πŸ˜’πŸ˜“ πŸ˜ˆπŸ˜“β€™πŸ˜’ πŸ˜…πŸ˜‘πŸ˜ˆπŸ˜ƒπŸ˜€πŸ˜˜.

So the first two codes I was able to get in very little time. I suspected that the first one would be a simple Caesar, (aka Wheel or Rotation) cipher. I wrote a simple function that decrypts characters by shifting letter values based on a shift parameter, and wrapping if necessary.

#define ABC_SIZE 26

wchar_t WheelDecodeChar(wchar_t ch, int shift)
{
    wchar_t decoded = 0;

    if(ch >= 'a' && ch <= 'z')
    {
        decoded = ch + shift;
        while(decoded > 'z')
            decoded -= ABC_SIZE;
        while(decoded < 'a')
            decoded += ABC_SIZE;
    }
    else if(ch >= 'A' && ch <= 'Z')
    {
        decoded = ch + shift;
        while(decoded > 'Z')
            decoded -= ABC_SIZE;
        while(decoded < 'A')
            decoded += ABC_SIZE;
    }
    else
    {
        decoded = ch;
    }
    return decoded;
}

void WheelDecode(const wchar_t *message, int shift)
{
    wchar_t decoded[256] = {};
    int len = wcslen(message);
    for(int ch = 0; ch < len; ch++)
    {
        decoded[ch] = WheelDecodeChar(message[ch], shift);
    }
    wprintf(L"Possible message = %s\n", decoded);
}

void WheelDecodeAll(const wchar_t *message)
{
    for(int i = 0; i < ABC_SIZE; i++)
    {
        WheelDecode(message, i);
    }
}

 

When I called WheelDecodeAll I got this output:

Possible message = A zsnw kmuuwkkxmddq kgdnwv lzw XanwLzajlqWayzl Javvdwj!
Possible message = B atox lnvvxllyneer lheoxw max YboxMabkmrXbzam Kbwwexk!
Possible message = C bupy mowwymmzoffs mifpyx nby ZcpyNbclnsYcabn Lcxxfyl!
Possible message = D cvqz npxxznnapggt njgqzy ocz AdqzOcdmotZdbco Mdyygzm!
Possible message = E dwra oqyyaoobqhhu okhraz pda BeraPdenpuAecdp Nezzhan!
Possible message = F exsb przzbppcriiv plisba qeb CfsbQefoqvBfdeq Ofaaibo!
Possible message = G fytc qsaacqqdsjjw qmjtcb rfc DgtcRfgprwCgefr Pgbbjcp!
Possible message = H gzud rtbbdrretkkx rnkudc sgd EhudSghqsxDhfgs Qhcckdq!
Possible message = I have successfully solved the FiveThirtyEight Riddler!
Possible message = J ibwf tvddfttgvmmz tpmwfe uif GjwfUijsuzFjhiu Sjeemfs!
Possible message = K jcxg uweeguuhwnna uqnxgf vjg HkxgVjktvaGkijv Tkffngt!
Possible message = L kdyh vxffhvvixoob vroyhg wkh IlyhWkluwbHljkw Ulggohu!
Possible message = M lezi wyggiwwjyppc wspzih xli JmziXlmvxcImklx Vmhhpiv!
Possible message = N mfaj xzhhjxxkzqqd xtqaji ymj KnajYmnwydJnlmy Wniiqjw!
Possible message = O ngbk yaiikyylarre yurbkj znk LobkZnoxzeKomnz Xojjrkx!
Possible message = P ohcl zbjjlzzmbssf zvsclk aol MpclAopyafLpnoa Ypkksly!
Possible message = Q pidm ackkmaancttg awtdml bpm NqdmBpqzbgMqopb Zqlltmz!
Possible message = R qjen bdllnbboduuh bxuenm cqn OrenCqrachNrpqc Armmuna!
Possible message = S rkfo cemmoccpevvi cyvfon dro PsfoDrsbdiOsqrd Bsnnvob!
Possible message = T slgp dfnnpddqfwwj dzwgpo esp QtgpEstcejPtrse Ctoowpc!
Possible message = U tmhq egooqeergxxk eaxhqp ftq RuhqFtudfkQustf Duppxqd!
Possible message = V unir fhpprffshyyl fbyirq gur SvirGuveglRvtug Evqqyre!
Possible message = W vojs giqqsggtizzm gczjsr hvs TwjsHvwfhmSwuvh Fwrrzsf!
Possible message = X wpkt hjrrthhujaan hdakts iwt UxktIwxginTxvwi Gxssatg!
Possible message = Y xqlu ikssuiivkbbo ieblut jxu VyluJxyhjoUywxj Hyttbuh!
Possible message = Z yrmv jlttvjjwlccp jfcmvu kyv WzmvKyzikpVzxyk Izuucvi!

So, we can see the 8th line is the answer we are looking for.

The second question is a little harder, but I was able to get it pretty easily. From looking at the string, “xckik acvlbeg oz mmqn xnlautw. gzag, mwcht, kbjzh… ulw cpeq edr mom dhqx lksxlioil?” you can kind of tell that the encryption alphabet must be changing. I was able to guess that this was probably aΒ VigenΓ¨re cipher and wrote a function to modify the shift value passed into WheelDecodeChar based on a key. This works by using the first letter of the key as the shift value for the first letter, then the second letter in the key for the second letter, and so on. If the key is shorter than the message, you just go back to the beginning of the key.

void WheelDecodeKey(const wchar_t *message, const wchar_t *key)
{
    int keyLen = wcslen(key);
    int msgLen = wcslen(message);

    wchar_t decoded[256] = {};
    int keyIdx = 0;
    for(int i = 0; i < msgLen; i++)
    {
        int shift = tolower(key[keyIdx % keyLen]) - 'a';
        decoded[i] = WheelDecodeChar(message[i], -shift);
        if(IsLetter(message[i]))
            keyIdx++;
    }
    wprintf(L"Possible message = %s\n", decoded);
}

 

The only trick was finding which key to use. I tried “Riddler”, “TheRiddler”, and then finally tried “FiveThirtyEight” and got this:

Possible message = super tuesday is this tuesday. cruz, trump, rubio... who will
 win the most delegates?

That looks about right. Cool, so I got the first two in pretty short order. How hard could the third one be?

What followed was about a full day of frustration before I decided to move on to the fourth one. The fourth one uses emojis instead of letters but I decided to fix that by making a function to assign letters to the emojis.

void MakeLettersFromEmojis(const wchar_t *message, wchar_t *out)
{
    // SUPER UGLY CODE with lots of hard-coded assumptions

    map<unsigned int, wchar_t> mapping;

    int len = wcslen(message);
    wchar_t ch = 'a';
    bool esc = false;
    int outIndex = 0;

    for(int i = 0; i < len; i++)
    {
        wchar_t m = message[i];
        if(m == ' ' || m == '.' || m == ',' || m == '\'' || m == 0x2019)
        {
            if(m == 0x2019)
                m = '\'';
            out[outIndex] = m;
            outIndex++;
            continue;
        }

        unsigned int m2 = m;
        if(esc)
        {
            m2 = (u16esc << 16) | m;
            esc = false;
        }
        else if(m == u16esc)
        {
            esc = true;
            continue;
        }

        auto itr = mapping.find(m2);
        if(itr == mapping.end())
        {
            mapping[m2] = ch;
            out[outIndex] = ch;
            outIndex++;
            ch++;
        }
        else
        {
            out[outIndex] = itr->second;
            outIndex++;
        }
    }
    out[outIndex] = 0;
    wprintf(L"%s\n", out);
}

 

That produced the following output:

ab, cdec'f gc. hai jkc lemb ca ianb. dko, ec pkefc gc'f qngreo.

Now this isn’t a simple Caesar cipher. This is a completely random alphabet. There are some really interesting algorithms that use frequency analysis to come up with likely cipher alphabets, but I just solved this by hand. (This looks like a lot of fun to code; I might revisit it later.) With the apostrophes, I guessed that the “f” was an “s”, which led me to guess that the “c” was a “t”.

ab, tdet's gt. hai jkt lemb ta ianb. dko, et pkest gt's qngreo.

That led me to guess that "de" in the second word was "ha" to spell "that's". Also the "g" would be an "i". This leads to:
ab, that'sΒ it. hai jkt lamb ta ianb. hko, at pkast it's qnirao.

So all those guesses were correct, which is not always how solving cryptograms go, but I got lucky. From there it was a matter of some trial and error to finally get:

ok, that's it. now get back to work. hey, at least it's friday.

Okay, now back to the third message and my frustration.

I spent the better part of a day trying to figure out what the encryption was for this string:

hy vg nw rh ev pr is or tf?

I decided it had to be digraphs and there’s an interesting cipher called a digraph substitution cipher that I coded up. I wrote code to generate all 676 possible digraph substitution alphabets based on shifting rows and columns by 26 characters. Then I used the output of those and scored each possible string by adding up the resultant digraphs and their frequency from this table. That was really cool except that this string wasn’t encrypted with a digraph substitution cipher, so none of the 676 possible alphabets produced any good candidates.

Finally I was chatting with a friend about it who asked, “have you tried Playfair?” The answer was no because I had never heard of Playfair. Here’s the wikipedia link for the Playfair cipher. It’s pretty cool, and JFK used it once!

Anyway, after trying a plain alphabet I used the key, “FIVETHRYG” (no repeated letters) and got the following output:

ar ey ou ha vi ng fu ny et?

(are you having fun yet?)

And that’s it! If I had had more time I probably would have coded that up, which would have been fun, but I spent too much time on the digraphs.

Thanks for reading.