Skip to content

Instantly share code, notes, and snippets.

@abtrout
Last active May 20, 2024 03:36
Show Gist options
  • Save abtrout/909ab75fc53253076ea3dbd1ce65b434 to your computer and use it in GitHub Desktop.
Save abtrout/909ab75fc53253076ea3dbd1ce65b434 to your computer and use it in GitHub Desktop.
Solving the cryptograms from Ghost in the Wires

Chapter 1

Max vhlm hy max unl wkboxk ingva B nlxw mh ingva fr hpg mktglyxkl

I assumed this was a Caesar cipher and brute forced it. Shifting by 7 reveals the answer.

>>> def caesar_shift(ct, shift):
...   return ''.join([
...     # Circular shift the alphabet `shift` positions to the right.
...     chr((ord(c) + shift - ord('a')) % 26 + ord('a'))
...     if c.isalpha() else c
...     for c in ct.lower()])
... 
>>> input = "Max vhlm hy max unl wkboxk ingva B nlxw mh ingva fr hpg mktglyxkl"
>>> caesar_shift(input, 7)
'the cost of the bus driver punch i used to punch my own transfers'

The punch cost $15.

Chapter 2

Estd mzzv esle elfrse xp szh ez ncplep yph topyetetpd hspy T hld l acp-eppy

Just as before, shifting by 15 reveals the answer. Note that 15 was the answer to the previous chapter's puzzle -- interesting!

>>> input = "Estd mzzv esle elfrse xp szh ez ncplep yph topyetetpd hspy T hld l acp-eppy"
>>> caesar_shift(input, 15)
'this book that taught me how to create new identities when i was a pre-teen'

The book was The Paper Trip.

Chapter 3

pbzfsobp dkfobtpkx lq pbkfi ppbkfpry aoxtolc iixz lq abpr bobt pbzfsba cl bmvq obail bpbeQ

Same approach, but also reversed.

>>> input = "pbzfsobp dkfobtpkx lq pbkfi ppbkfpry aoxtolc iixz lq abpr bobt pbzfsba cl bmvq obail bpbeQ"
>>> caesar_shift(input[::-1], 3)
'these older type of devices were used to call forward business lines to answering services'

The device is called a diverter.

Chapter 4

gsvmznvlugsvnzrmuiznvhrszxpvwzgfhxrmgsvzikzmvgwzbh

Use the Atbash substitution cipher, which inverts the alphabet rather than shifting it.

>>> def atbash(input):
...   return ''.join([
...     # Replace a with z, b with y, etc.
...     chr(ord('z') - ord(c) + ord('a'))
...     if c.isalpha() else c
...     for c in input.lower()])
>>> input = "gsvmznvlugsvnzrmuiznvhrszxpvwzgfhxrmgsvzikzmvgwzbh"
>>> atbash(input)
'thenameofthemainframesihackedatuscinthearpanetdays'

The mainframe was called Computer System for Mainframe Operations (COSMOS).

Chapter 5

jbi ujt veo eco ntk iwa lhc eeo anu uir trs hae oni rfn irt toh imi ets shs !eu

This is a transposition cipher. Consider each triple as a separate row forming a 3x20 grid, and read the columns top-to-bottom and right-to-left.

>>> input = "jbi ujt veo eco ntk iwa lhc eeo anu uir trs hae oni rfn irt toh imi ets shs !eu".split()
>>> ''.join([
...   word[-i]  # Read right-to-left
...   for i in range (1, 4)
...   for word in input])
'itookacourseinthissubjectwheniranfromthejuvenileauthorities!'

The course was on criminal justice.

Chapter 6

bmFtZXRoZWNvbXBhbnl3aGVyZWJvbm5pZXdhc2VtcGxveWVkd2hlbndlc3RhcnRlZGRhdGluZw==

This is a base64 encoded input.

>>> import base64
>>> input = "bmFtZXRoZWNvbXBhbnl3aGVyZWJvbm5pZXdhc2VtcGxveWVkd2hlbndlc3RhcnRlZGRhdGluZw=="
>>> base64.b64decode(input)
b'namethecompanywherebonniewasemployedwhenwestarteddating'

The company was GTE: General Telephone and Electronics Corporation.

Chapter 7

multbqncannqenabrhfgacnqogehchetbkkebmsqgkncchebr

This is a simple substitution cipher with the key gte from the preceeding problem. The shifted alphabet is GTEabcdfhijklmnopqrsuvwxyz and the cipher maps [a-z] to these positionally.

>>> input = "multbqncannqenabrhfgacnqogehchetbkkebmsqgkncchebr"
>>> key = ''.join(list(dict.fromkeys("gte" + "abcdefghijklmnopqrstuvwxyz")))
>>> ''.join([chr(ord('a') + key.find(ch)) for ch in input])
'numberofdoorcodesihadforpacificbellcentraloffices'

He had codes for 11 PacBell central offices.

Chapter 8

'siass nuhmil sowsra amnapi waagoc ifinti dscisf iiiesf ahgbao staetn itmlro

Another transposition cipher. Read each column bottom-to-top and left-to-right.

>>> input = "'siass nuhmil sowsra amnapi waagoc ifinti dscisf iiiesf ahgbao staetn itmlro"
>>> words = input.split()
>>> ''.join([
...   words[-i][j]
...   for j in range(0, len(words[0]))
...   for i in range(1, len(words)+1)
... ])
"isaidiwasn'tthisfamousmagicianwhilebeingasmartasstoprisonofficials"

The famous magician was David Copperfield.

Chapter 9

tvifafwawehes hsesoonvtlimaeloemtcagmen irnoerrldony

This is a rail cipher, similar to transposition ciphers above, except we move back and forth between columns rather than looping around.

>>> def rail_decipher(parts):
...     step, indices = -1, [0] * len(parts)
...     i, res = 0, ""
...     while indices[i] < len(parts[i]):
...         res += parts[i][indices[i]]
...         indices[i] += 1
...         step = step if 0 < i < len(parts) - 1 else -1 * step
...         i += step
...     return res
... 
>>> input = "tvifafwawehes hsesoonvtlimaeloemtcagmen irnoerrldony"
>>> rail_decipher(input.split(" "))
'thisversionofnovatelfirmwareallowedmetochangemyesn'

The firmware version was 1.05.

Chapter 10

gnkusr ooursnsisti ttnotoihiec rolwaintmlk ovtgp

Same as above.

>>> input = "gnkusr ooursnsisti ttnotoihiec rolwaintmlk ovtgp"
>>> rail_decipher(input.split(" "))
'gotrootonunlvworkstationusingthissimpletrick'

The trick was spamming "Control-C" to escape boot sequence.

Chapter 11

ow gw ty kc qb eb nm ht ud pc iy ty ik tu zo dp gl qt hd

This requires the Playfair cipher, which maps pairs of letters according to various rules operating on a 5x5 grid of letters determined by a key. I tried CTRL, CONTROL, GTE, and a few other random things which didn't work. Using an empty key did!

>>> def playfair_decrypt(key_prefix, input):
...     alphas = "abcdefghiklmnopqrstuvwxyz"  # Note `j` is skipped.
...     key = ''.join(list(dict.fromkeys(key_prefix + alphas)))
...     res = ''
...     for i in range(0, len(input), 2):
...         x, y = key.find(input[i]), key.find(input[i+1])
...         xrow, xcol, yrow, ycol = int(x/5), x%5, int(y/5), y%5
...         if xrow == yrow:
...             res += key[5*xrow + ((xcol-1) % 5)]
...             res += key[5*yrow + ((ycol-1) % 5)]
...         elif xcol == ycol:
...             res += key[5*((xrow-1) % 5) + xcol]
...             res += key[5*((yrow-1) % 5) + ycol]
...         else:
...             res += key[5*xrow + ycol]
...             res += key[5*yrow + xcol]
...     return res
... 
>>> input = "ow gw ty kc qb eb nm ht ud pc iy ty ik tu zo dp gl qt hd"
>>> playfair_decrypt("", input.replace(" ", ""))
'mybrotheradamlistenedtothistypeofmusic'

Adam listened to rap and hip-hop.

Chapter 12

idniidhsubrseognteiuignuhrzdalrd ietfetinmeablnigorcsnuatoieclei

Another columnar transposition; read top-to-bottom and left-to-right.

>>> input = "idniidhsubrseognteiuignuhrzdalrd ietfetinmeablnigorcsnuatoieclei"
>>> words = input.split(" ")
>>> import itertools  # for zip_longest
>>> ''.join(map(
...   lambda x: x[0] + x[1],
...   itertools.zip_longest(words[0], words[1], fillvalue='')))
'iidentifiedthisnumberasbelongingtoericusingunauthorizedcallerid'

The number was 310 837-5412.

Chapter 13

qclgjq'acrjcrlmqnyrcpgursmzyddmbcnngrgmfupceylyk

Another Caesar cipher brute force and string reversal.

>>> input = "qclgjq'acrjcrlmqnyrcpgursmzyddmbcnngrgmfupceylyk"
>>> caesar_shift(input[::-1], 2)
"managerwhoitippedoffaboutwiretapsonteltec'slines"

The manager was Mark Kasden.

Chapter 14

c2VuaWxzJ2RhZHltbm9zcGF0ZXJpd2VodHRjZW5ub2NlcmRuYXNlbGVnbmFzb2xvdHlsZm90ZGFob2h3dG5lZ2F5dGlydWNlc2xsZWJjYXBlaHQ=

This base64 encoded input is also backwards.

>>> import base64
>>> input = "c2VuaWxzJ2RhZHltbm9zcGF0ZXJpd2VodHRjZW5ub2NlcmRuYXNlbGVnbmFzb2xvdHlsZm90ZGFob2h3dG5lZ2F5dGlydWNlc2xsZWJjYXBlaHQ="
>>> base64.b64decode(input)[::-1]
b"thepacbellsecurityagentwhohadtoflytolosangelesandreconnectthewiretapsonmydad'slines"

The security agent was Darrell Santos.

Chapter 15

ud mn cf ub mw re lb is ba of gx ty qc qh il ea ym nx bz ub he cf th is

Same Playfair cipher with the same empty key. This time the output is backwards.

>>> input = "ud mn cf ub mw re lb is ba of gx ty qc qh il ea ym nx bz ub he cf th is"
>>> playfair_decrypt("", input.replace(" ", ""))
'telmahregrubmahtaelihwotsasffodewohsewrekcahsiht'
>>> _[::-1]
'thishackerweshowedoffsastowhileathamburgerhamlet'

They showed SAS to Eric Heinz.

Chapter 16

7\3|2\9|3\5|4/0/8/2|6\7/0/4\4\5/6/6\5/7/8/9|7\8/7|9\5/9\2\3\5\7/8|2/0/8|2/6|6|2|7\7\0\4\9|

First, this is a telephone keypad cipher. The digit zero appears a few times, which suggests this uses the old/nonstandard keypad mapping where the digit zero holds Q, space, and Z (example). Unpacking this leads to a second puzzle.

>>> def telephone_cipher(ct):
...     keypad = {
...       "2": "abc", "3": "def", "4": "ghi",
...       "5": "jkl", "6": "mno", "7": "prs",
...       "8": "tuv", "9": "wxy", "0": "q z"
...     }
...     res = ''
...     for i in range(0, len(ct), 2):
...         button = keypad[ct[i]]
...         match ct[i+1]:
...             case "\\":
...                 res += button[0]
...             case "/":
...                 res += button[2]
...             case "|":
...                 res += button[1]
...     return res
... 
>>> input = "7\\3|2\\9|3\\5|4/0/8/2|6\\7/0/4\\4\\5/6/6\\5/7/8/9|7\\8/7|9\\5/9\\2\\3\\5\\7/8|2/0/8|2/6|6|2|7\\7\\0\\4\\9|"
>>> telephone_cipher(input)
'peaxdkizvbmszgglomlsvxpvrwlwadjsuczucnnbppqgx'

The next layer is a Vignere cipher with the key heinz (from the last challenge).

>>> def vignere_pass(key, ct, encrypt=True):
...     res, i = '', 0
...     for c in ct:
...         if c.isalpha():
...             shift = ord(key[i % len(key)]) - ord('a')
...             if not encrypt:
...                 shift *= -1
...             res += chr(ord('a') + ((ord(c) + shift - ord('a')) % 26))
...             i += 1  # only advance through `key` for alphas!
...         else:
...             res += c
...     return res
... 
>>> def vignere_encrypt(key, ct):
...     return vignere_pass(key, ct, encrypt=True)
... 
>>> def vignere_decrypt(key, ct):
...     return vignere_pass(key, ct, encrypt=False)
...
>>> vignere_decrypt("heinz", "peaxdkizvbmszgglomlsvxpvrwlwadjsuczucnnbppqgx")
'iaskedericforthekeytothisphonecompanyfacility'

He asked Eric for the key to Pacific Bell central offices.

Chapter 17

100 0000 10 1 01 001 00 1000 1 010 11 000 0 0000 11 000 000111 00011 10000 11111 11110 11000 00111 10000 11111 10000 11111

This is Morse code, but there are two ways we could decode it by mapping 1 and 0 to . and - differently. This cryptogram alternates between these different encodings into a "twisted" Morse code. This seems equivalent to concatenating the results of decoding in both ways, then doing a rail cipher or columnar transposition.

>>> morse_forward = {
...     "a": ".-",    "b": "-...",  "c": "-.-.",
...     "d": "-..",   "e": ".",     "f": "..-.",
...     "g": "--.",   "h": "....",  "i": "..",
...     "j": ".---",  "k": "-.-",   "l": ".-..",
...     "m": "--",    "n": "-.",    "o": "---",
...     "p": ".--.",  "q": "--.-",  "r": ".-.",
...     "s": "...",   "t": "-",     "u": "..-",
...     "v": "...-",  "w": ".--",   "x": "-..-",
...     "y": "-.--",  "z": "--..",  "1": ".----",
...     "2": "..---", "3": "...--", "4": "....-",
...     "5": ".....", "6": "-....", "7": "--...",
...     "8": "---..", "9": "----.", "0": "-----",
... }
>>> morse_reverse = {v: k for k, v in morse_forward.items()}
>>> def morse_decode(input):
...     return "".join([morse_reverse.get(m, "?") for m in input.split(" ")])
... 
>>> def twisted_morse_decode(input):
...     return "".join([
...         morse_decode(
...             c.replace("0", "-" if i % 2 == 0 else ".")
...                 .replace("1", "." if i % 2 == 0 else "-"))
...         for i, c in enumerate(input.split(" "))
...     ])  
... 
>>> input = '100 0000 10 1 01 001 00 1000 1 010 11 000 0 0000 11 000 000111 00011 10000 11111 11110 11000 00111 10000 11111 10000 11111'
>>> twisted_morse_decode(input)
'whatnumberisthis?3104776565'

That is the number for the Los Angeles FBI headquarters.

Chapter 18

6365696a647a727573697775716d6d6e736e69627a74736a6f796970646 9737967647163656c6f71776c66646d63656d78626c6879746d796f6d 71747765686a6a71656d756c70696b6a627965696a71

After hex decoding, we're faced with another nice ASCII string. This is another Vignere cipher, with the key LAFBI from the past chapter's solution.

>>> input = "6365696a647a727573697775716d6d6e736e69627a74736a6f796970646 9737967647163656c6f71776c66646d63656d78626c6879746d796f6d 71747765686a6a71656d756c70696b6a627965696a71"
>>> bytes.fromhex(input.replace(" ", "")).decode("ascii")
'ceijdzrusiwuqmmnsnibztsjoyipdisygdqceloqwlfdmcemxblhytmyomqtwehjjqemulpikjbyeijq'
>>> vignere_decrypt("lafbi", _)
'iidentifiedthefbicellphonesthatwherecallingericbyhackingintothiscellularprovider'

He hacked into PacTel.

Chapter 19

hranmoafignwooeiettwsocheneteelaefnbaethscurdniyajspwrl

Another columnar transposition after splitting the ciphertext in half. Read second column, then first.

>>> input = "hranmoafignwoeoeiettwsoeheneteelaefnbaethscvrdniyajspwrl"
>>> len(input)
56
>>> half = int(len(input) / 2)
>>> ''.join(map(''.join, zip(input[half:], input[:half])))
'therealnameofanfbiagentwhosecoveridentitywasjosephwernle'

His name was Joseph Ways.

Chapter 20

yo kb pn oc ox rh oq kb oh kp ge gs yt yt hg sa li mt ob sa po po mk pl md

Apply the Playfair cipher with key fbi.

>>> input = "yo kb pn oc ox rh oq kb oh kp ge gs yt yt hg sa li mt ob sa po po mk pl md"
>>> playfair_decrypt("fbi", input.replace(" ", ""))
'thecompanyteltechackedintotogetinformationonpeople'

TelTec hacked into TRW.

Chapter 21

77726e6b7668656a77676b6b276c6d6b6274616672656567776c6a7368697a70726f6d79656c

Hex decode then Vignere.

>>> input = "77726e6b7668656a77676b6b276c6d6b6274616672 656567776c6a7368697a70726f6d79656c"
>>> bytes.fromhex(input.replace(" ", "")).decode("ascii")
"wrnkvhejwgkk'lmkbtafreegwljshizpromyel"
>>> vignere_decrypt("trw", _)
"darrellsanto'svoicemailpasswordwasthis"

His voicemail password was 1313.

Chapter 22

opoybdpmwoqbcpqcygagpcgxbpusapdluscplchxwoisgyeasdcpopdhadfyaethis

Another Playfair cipher, reversed, with spaces removed.

>>> input = "opoybdpmwoqbcpqcygagpcgxbpusapdluscplchxwoisgyeasdcpopdhadfyaethis"
>>> playfair_decrypt("", input)[::-1]
'thisdeviceiconnectedwithmyscannertoalertmewhenfbiwasnearmylocation'

The device was a DDI: digital-data interpreter.

Chapter 23

1001 0111 01 00 0 0 101 011 1111 1110 1011 1111 101 0110 1111 1101 110 010 100 0 0100 11 1011 1011 000 10 101 01

Decoding Morse code reveals a second puzzle.

>>> input = "1001 0111 01 00 0 0 101 011 1111 1110 1011 1111 101 0110 1111 1101 110 010 100 0 0100 11 1011 1011 000 10 101 01"
>>> input = input.replace("1", ".").replace("0", "-")
>>> morse_decode(input)
'pbnmttrdhvlhrxhfukwtyilloarn'

For this, we need the Autokey cipher, a variant of the Vignere cipher used above where the original message is appended to the key.

>>> def autokey_decrypt(key, ct):
...     res, i = '', 0
...     for c in ct:
...         if c.isalpha():
...             shift = ord(key[i % len(key)]) - ord('a')
...             x = chr(ord('a') + ((ord(c) - shift - ord('a')) % 26))
...             res += x
...             key += x
...             i += 1
...         else:
...             res += c
...     return res
... 
>>> autokey_decrypt("ddi", "pbnmttrdhvlhrxhfukwtyilloarn")
'myfavoritedonutsarethesekind'

His favorite donuts are FBI donuts.

Chapter 24

anhgynnrtfafaqgmbhsuuzkzfbhbfk

Playfair cipher, then reversed.

>>> playfair_decrypt("fbidonuts", "anhgynnrtfafaqgmbhsuuzkzfbhbfk")
'sagevsalninosrepdetnawarofedoc'
>>> _[::-1]
'codeforawantedpersoninlasvegas'

The code is 440.

Chapter 25

nhyitekmnryoogmwefehocttntnoauttosumooalgei

SCWF knew the solution to this immediately: reverse the ciphertext and then read every 3rd character.

>>> def every_nth(input, n):
...     return "".join([
...         input[((n*i) + (i // len(input))) % len(input)]
...         for i in range( len(input))
...     ])
... 
>>> every_nth(input[::-1], 3)
'ilostonceworkingoutatthegymtheamountofmoney'

He lost $11,000 while working out at the gym.

Chapter 26

11 0100 000 111 010 0 011 0010 000 010 11 10 1101 01 01 1 000 1 1111 01 0 011 1 010 1 1000 000 010 01 00 01 01 011 00 1101 0010 1 010 1 10 0 001101 110010 001101 110010 001101 100 0000 1 10 101 0 111 0 10 010 0101 0000 11 10 001 10 1 011 00 100 1 10 0 00 0 00 1 000

This is the same "twisted" Morse code cipher from Chapter 17. It looks like some of the input may be wrong (or I have a bug), but it's close enough to solve the problem.

>>> input = "11 0100 000 111 010 0 011 0010 000 010 11 10 1101 01 01 1 000 1 1111 01 0 011 1 010 1 1000 000 010 01 00 01 01 011 00 1101 0010 1 010 1 10 0 001101 110010 001101 110010 001101 100 0000 1 10 101 0 111 0 10 010 0101 0000 11 10 001 10 1 011 00 100 1 10 0 00 0 00 1 000"
>>> twisted_morse_decode(input)
'ilookedforinfantothatwereborninadifferent?????whenresearchingnewmdentities'

He looked for infants that were born in a different state.

Chapter 27

laeaslarhawpuiolshawzadxijxkjgvvbvaxavlowyuuhdsxausmrmbulbegukseq

Another simple Autokey cipher using the key "state".

>>> input = "laeaslarhawpuiolshawzadxijxkjgvvbvaxavlowyuuhdsxausmrmbulbegukseq"
>>> autokey_decrypt("state", input)
'thehostnamewhichwasusedforthesecuritybugdatabaseatsunmicrosystems'

The hostname was elmer.

Chapter 28

70776d61766374666f2770636d6167797a786977786f78656a7974696465737073786f65696f63726f64706a6f766b636165686573677069637a61786172

Same process as above.

>>> input = "70776d61766374666f2770636d6167797a786977786f78656a7974696465737073786f65696f63726f64706a6f766b636165686573677069637a61786172"
>>> autokey_decrypt("elmer", bytes.fromhex(input).decode("utf-8"))
"llawerifs'llevonotnikaerbotemdewollamargorpsihtniwalfytirucesa"
>>> _[::-1]
"asecurityflawinthisprogramallowedmetobreakintonovell'sfirewall"

The security flaw was in sendmail.

Chapter 29

qnxpnebielnudqqpbibecua3m'llswhmmhrdzucclsfvqmdunepbkreezkarsnngpkgmscdnkr

This time we use Playfair again instead of Autokey.

>>> input = "qnxpnebielnudqqpbibecua3m'llswhmmhrdzucclsfvqmdunepbkreezkarsnngpkgmscdnkr"
>>> playfair_decrypt("sendmail", input)
'revreslanimretpulaidmocsddiievonnotnuobbanapuemtestahtssyolpmeehtfoemaneht'
>>> _[::-1]
'thenameoftheemployssthatsetmeupanabbountonnoveiiddscomdialupterminalserver'

Looks like our Playfair implementation has issues with repeated letters -- but it's close enough. The employee was Shawn Nunley.

Chapter 30

eyiyibemhemijixvpyiocjkxdwwxdazvtkaazrvl

Back to Autokey.

>>> input = "eyiyibemhemijixvpyiocjkxdwwxdazvtkaazrvl"
>>> autokey_decrypt("nunley", input)
'revnednimrifwalehtmorfderifsawinosaereht'
>>> _[::-1]
'thereasoniwasfiredfromthelawfirmindenver'

He was fired for consulting on company time.

Chapter 31

usygbjmqeauidgttlcflgqmfqhyhwurqmbxzoqmnpmjhlneqsctmglahp

Same process as above.

>>> input = "usygbjmqeauidgttlcflgqmfqhyhwurqmbxzoqmnpmjhlneqsctmglahp"
>>> autokey_decrypt("consulting", input)
'selohytirucessmvsuoremunemgnidnesotnidekcirtsawnosrepsiht'
>>> _[::-1]
'thispersonwastrickedintosendingmenumerousvmssecurityholes'

He tricked Neill Clift into sending him security holes.

Chapter 32

tpdwxjw'viyegmzbecfupcqtuwdinpfhzvufadzbkfoevcnseozxffdlurdojwsjkzllxwapfruhuaqz

Back to Autokey.

>>> input = "tpdwxjw'viyegmzbecfvpcqtuwdinpfhzvvfadzbkfoevcnseozxffdlvrdo'jwsjkzllxwapfrvhuaqz"
>>> autokey_decrypt("clift", input)[::-1]
"icompromisedthisuser'spasswordthroughnetworkmonitoringtohackintoshimomura'sserver"

He compromised david's password.

Chapter 33

010 1 0001 101 0 111 000 100001 01 101 001 00 111 00 00 1111 000 01 111 1 10 000 0000 1001 000 11 0000 0 111 0 0 0101 010 110 111 111 0 1111 1 101 111 1101 110 01 00 010 111 000 0100 111 01 100 00

More Morse code. This time the input should be inverted before applying the "twisted" Morse decoding from before.

>>> input = "010 1 0001 101 0 111 000 100001 01 101 001 00 111 00 00 1111 000 01 111 1 10 000 0000 1001 000 11 0000 0 111 0 0 0101 010 110 111 111 0 1111 1 101 111 1101 110 01 00 010 111 000 0100 111 01 100 00"
>>> input = input.replace("1", "2").replace("0", "1").replace("2", "0")
>>> twisted_morse_decode(input)
'revress?arumomihsnoenohpsihtotecruosehtrofgnikoolsawi'
>>> _[::-1]
'iwaslookingforthesourcetothisphoneonshimomura?sserver'

He was looking for the source to OKI.

Chapter 34

eqfeihchqqlndcinrarnfhqdvmlqnmcrlphaccqmaefkzhlslnstmqgmma

A new cipher is required! I turned back to SCWF and found Bifid.

>>> def bifid_decrypt(key_prefix, ct):
...     alphas = "abcdefghiklmnopqrstuvwxyz"  # Note `j` is skipped.
...     key = ''.join(list(dict.fromkeys(key_prefix + alphas)))
...     # Find coordinates from ciphertext characters.
...     nums = []
...     for c in ct:
...         if not c.isalpha():
...             continue
...         i = key.find(c)
...         nums.extend((i // 5, i % 5))
...     # Invert the row/column swap done during encryption.
...     coords = zip(nums[:len(nums)//2], nums[len(nums)//2:])
...     return "".join([key[5*row + col] for row, col in coords])
...
>>> input = "eqfeihchqqlndcinrarnfhqdvmlqnmcrlphaccqmaefkzhlslnstmqgmma"
>>> bifid_decrypt("oki", input)
'emrofrelipmocalorotomehtdedaolpuscirtemretnitaeeyolpmesiht'
>>> _[::-1]
'thisemployeeatintermetricsuploadedthemotorolacompilerforme'

The Intermetrics employee was Marty Stolz.

Chapter 35

ifdmnbbnqitnsobmmmtthdkhqbqzpo"nduqz"zhnemccxhyaninaxanf

Bifid with key "marty" from the previous chapter.

>>> input = "ifdmnbbnqitnsobmmmtthdkhqbqzpo\"nduqz\"zhnemccxhyaninaxanf"
>>> bifid_decrypt("marty", input)
'llewehtmorfmetsyssihtnotnuoccaytramymotnideggolenoemos'
>>> _[::-1]
'someoneloggedintomymartyaccountonthissystemfromthewell'

The system was escape.com.

Chapter 36

kgqmicewdnfmastcefkxlkqshgrfsspotxuesqvcohxttpcuvhnxawypuwzdt

Bifid with key "esc".

>>> input = "kgqmicewdnfmastcefkxlkqshgrfsspotxuesqvcohxttpcuvhnxawypuwzdt"
>>> bifid_decrypt("esc", input)
'kcintimniveksawidemrifnoctahttekcaiiksymnimetisihtdnuofibfeht'
>>> _[::-1]
'thefbifoundthisiteminmyskiiacketthatconfirmediwaskevinmitnick'

The FBI found a pay stub made out to Kevin Mitnick.

Chapter 37

0\6\2/7\4/2\4\8\2|8|6|7\0\4\3/2|8/7/3\2/2/5|6/4|8\7\6\6\3\2|3/3\7/4|6/0/3|7/0\6|8|9/4\4/6/5/4|5|0\8\9\7/4|4/4|8|5/3/3|4|8|4/0\5|8/2/

The same Telephone cipher from before, then Bifid with the key "paystub".

>>> input = "0\\6\\2/7\\4/2\\4\\8\\2|8|6|7\\0\\4\\3/2|8/7/3\\2/2/5|6/4|8\\7\\6\\6\\3\\2|3/3\\7/4|6/0/3|7/0\\6|8|9/4\\4/6/5/4|5|0\\8\\9\\7/4|4/4|8|5/3/3|4|8|4/0\\5|8/2/"
>>> telephone_cipher(input)
'qmcpiagtbunpqgfbvsdcckohtpmmdbfdshozesqnuygiolhkqtwshihulfehuiqkvc'
>>> bifid_decrypt("paystub", _)
'nwodekatfoseicaruccanissorhchtderetnuochcihwmlifyratncmucodehteman'
>>> _[::-1]
'namethedocumcntaryfilmwhichcounteredthchrossinaccuraciesoftakedown'

The documentary was Freedom Downtime.

Chapter 38

001101 110010 001101 110010 001101 110010 001101 110010 111 00 011 00 10 110 0000 11 00 1001 110 0100 111 10 11 00 1101 1001 0100 10 100 11 01 101 0010 11 101 011 111 000 100 010 1001 001 1 101 01 010 1010 01 0 1110 10 0111 010 010

TODO: This one remains unsolved for now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment