HACKvent 2015: Day 15

Hackvent 20159430

Challenge

We've captured a strange message. It looks like it is encrypted somehow ...
iw, hu, fv, lu, dv, cy, og, lc, gy, fq, od, lo, fq, is, ig, gu, hs, hi, ds, cy, oo, os, iu, fs, gu, lh, dq, lv, gu, iw, hv, gu, di, hs, cy, oc, iw, gc


We've also intercepted what seems to be a hint to the key:
bytwycju + yzvyjjdy ^ vugljtyn + ugdztnwv | xbfziozy = bzuwtwol
    ^         ^          ^          ^          ^
wwnnnqbw - uclfqvdu & oncycbxh | oqcnwbsd ^ cgyoyfjg = vyhyjivb
    &         &          &          &          &
yzdgotby | oigsjgoj | ttligxut - dhcqxtfw & szblgodf = sfgsoxdd
    +         +          +          +          +
yjjowdqh & niiqztgs + ctvtwysu & diffhlnl - thhwohwn = xsvuojtx
    -         -           -         -          -
nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi + udluvhcz = syhjizjq
    =         =           =         =          =         
fjivucti   zoljwdfl   sugvqgww   uxztiywn   jqxizzxq


Note:
  assume q != 0
  a letter is a decimal digit is a letter
  each digit has exactly two different letter representations
  C-like operator precedence

Solution

Straight away this looks like a logic solver problem. I intend to use Z3 theorem solver (link) as I am most familiar with it out of all the available solvers out there.

First step is to understand the problem. Each letter corresponds to a single digit 0-9. The q digit cannot be 0. Each digit is represented by exactly 2 letters. In the grid of equations, a series of letters like bytwycju means 8 digits. Ie if b was 2 and y was 8, it would be a number starting with 28XXXXXX. The operations used in the equations are & ^ | + - ==. C-like operator precedence is also used but this will match up nicely with python operator precedence.

Looking at the secret message, is it 38 pairs of letters. If each letter is a single digit then each pair must correspond to a ASCII  capital letters/numbers/spaces...etc. So I am going to assume the secret message is a 38 character length string.

Every letter from the alphabet is present in the challenge description and equations except for: aekmpr
This works out nicely as excluding those 6 letters leaves 20 letters total which leaves exactly 2 letters per digit, perfect!

So I start writing a new python script and add in all the rules I need. I use a BitVec of 32 bits because some operations become large. I could use a BitVec of 4 bits but then certain bitwise operations would overflow which is not what I want.

For each equation I need to generate a list of long constraints to add to the Z3 solver object. To do this I use a short python script written by OS-Freeze from Germany (thanks!) which was slightly modified by myself:

#!/usr/bin/python

# By OS-Freeze, slightly modified by PersianMG

row = "yjjowdqh & niiqztgs + ctvtwysu & diffhlnl - thhwohwn = xsvuojtx"
symbol = []
 
for i in row.split():
  if len(i) == 8:
    count = 7
    dat = list(''.join(i))
    result = "("
    for i in dat:
      result = result + "ZV['"+i+"'] * pow(10,"+str(count)+")"
      if count != 0:
        result += " + "
      count -= 1
    result += ")"
    print result
  elif len(i) == 1:
    if i == '=':
      print '=='
    else:
      print i

The above script would produce:

(ZV['y'] * pow(10,7) + ZV['j'] * pow(10,6) + ZV['j'] * pow(10,5) + ZV['o'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['d'] * pow(10,2) + ZV['q'] * pow(10,1) + ZV['h'] * pow(10,0))
&
(ZV['n'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['i'] * pow(10,5) + ZV['q'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['g'] * pow(10,1) + ZV['s'] * pow(10,0))
+
(ZV['c'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['v'] * pow(10,5) + ZV['t'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['y'] * pow(10,2) + ZV['s'] * pow(10,1) + ZV['u'] * pow(10,0))
&
(ZV['d'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['f'] * pow(10,5) + ZV['f'] * pow(10,4) + ZV['h'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['l'] * pow(10,0))
-
(ZV['t'] * pow(10,7) + ZV['h'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['w'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['h'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['n'] * pow(10,0))
==
(ZV['x'] * pow(10,7) + ZV['s'] * pow(10,6) + ZV['v'] * pow(10,5) + ZV['u'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['j'] * pow(10,2) + ZV['t'] * pow(10,1) + ZV['x'] * pow(10,0))

I simply change the equation for each equation in the challenge description until I have all of the constraints I need.

Now I can construct my actual solver script:

#!/usr/bin/python
from z3 import *

ZV = {}

# Make solver
s = Solver()

# Everything but aekmpr is in the alphabet
for l in 'bcdfghijlnoqstuvwxyz':
  ZV[l] = BitVec(l, 32)

# Limit to a single digit
for l in 'bcdfghijlnoqstuvwxyz':
  s.add(ZV[l] >= 0)
  s.add(ZV[l] <= 9)
  
# assume q != 0 (removed trivial 0 case)
s.add(ZV['q'] != 0)
  
# Row 1 (bytwycju + yzvyjjdy ^ vugljtyn + ugdztnwv | xbfziozy = bzuwtwol)
s.add(
  (ZV['b'] * pow(10,7) + ZV['y'] * pow(10,6) + ZV['t'] * pow(10,5) + ZV['w'] * pow(10,4) + ZV['y'] * pow(10,3) + ZV['c'] * pow(10,2) + ZV['j'] * pow(10,1) + ZV['u'] * pow(10,0))
  +
  (ZV['y'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['v'] * pow(10,5) + ZV['y'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['j'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['y'] * pow(10,0))
  ^
  (ZV['v'] * pow(10,7) + ZV['u'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['l'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['y'] * pow(10,1) + ZV['n'] * pow(10,0))
  +
  (ZV['u'] * pow(10,7) + ZV['g'] * pow(10,6) + ZV['d'] * pow(10,5) + ZV['z'] * pow(10,4) + ZV['t'] * pow(10,3) + ZV['n'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['v'] * pow(10,0))
  |
  (ZV['x'] * pow(10,7) + ZV['b'] * pow(10,6) + ZV['f'] * pow(10,5) + ZV['z'] * pow(10,4) + ZV['i'] * pow(10,3) + ZV['o'] * pow(10,2) + ZV['z'] * pow(10,1) + ZV['y'] * pow(10,0))
  ==
  (ZV['b'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['u'] * pow(10,5) + ZV['w'] * pow(10,4) + ZV['t'] * pow(10,3) + ZV['w'] * pow(10,2) + ZV['o'] * pow(10,1) + ZV['l'] * pow(10,0))
)

# Row 2 (wwnnnqbw - uclfqvdu & oncycbxh | oqcnwbsd ^ cgyoyfjg = vyhyjivb)
s.add(
  (ZV['w'] * pow(10,7) + ZV['w'] * pow(10,6) + ZV['n'] * pow(10,5) + ZV['n'] * pow(10,4) + ZV['n'] * pow(10,3) + ZV['q'] * pow(10,2) + ZV['b'] * pow(10,1) + ZV['w'] * pow(10,0))
  -
  (ZV['u'] * pow(10,7) + ZV['c'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['f'] * pow(10,4) + ZV['q'] * pow(10,3) + ZV['v'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['u'] * pow(10,0))
  &
  (ZV['o'] * pow(10,7) + ZV['n'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['y'] * pow(10,4) + ZV['c'] * pow(10,3) + ZV['b'] * pow(10,2) + ZV['x'] * pow(10,1) + ZV['h'] * pow(10,0))
  |
  (ZV['o'] * pow(10,7) + ZV['q'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['n'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['b'] * pow(10,2) + ZV['s'] * pow(10,1) + ZV['d'] * pow(10,0))
  ^
  (ZV['c'] * pow(10,7) + ZV['g'] * pow(10,6) + ZV['y'] * pow(10,5) + ZV['o'] * pow(10,4) + ZV['y'] * pow(10,3) + ZV['f'] * pow(10,2) + ZV['j'] * pow(10,1) + ZV['g'] * pow(10,0))
  ==
  (ZV['v'] * pow(10,7) + ZV['y'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['y'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['i'] * pow(10,2) + ZV['v'] * pow(10,1) + ZV['b'] * pow(10,0))
)
# Row 3 (yzdgotby | oigsjgoj | ttligxut - dhcqxtfw & szblgodf = sfgsoxdd)
s.add(
  (ZV['y'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['d'] * pow(10,5) + ZV['g'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['b'] * pow(10,1) + ZV['y'] * pow(10,0))
  |
  (ZV['o'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['s'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['g'] * pow(10,2) + ZV['o'] * pow(10,1) + ZV['j'] * pow(10,0))
  |
  (ZV['t'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['i'] * pow(10,4) + ZV['g'] * pow(10,3) + ZV['x'] * pow(10,2) + ZV['u'] * pow(10,1) + ZV['t'] * pow(10,0))
  -
  (ZV['d'] * pow(10,7) + ZV['h'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['q'] * pow(10,4) + ZV['x'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['f'] * pow(10,1) + ZV['w'] * pow(10,0))
  &
  (ZV['s'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['b'] * pow(10,5) + ZV['l'] * pow(10,4) + ZV['g'] * pow(10,3) + ZV['o'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['f'] * pow(10,0))
  ==
  (ZV['s'] * pow(10,7) + ZV['f'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['s'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['x'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['d'] * pow(10,0))
)

# Row 4 (nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi + udluvhcz = syhjizjq)
s.add(
  (ZV['n'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['t'] * pow(10,5) + ZV['u'] * pow(10,4) + ZV['h'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['q'] * pow(10,0))
  ^
  (ZV['o'] * pow(10,7) + ZV['q'] * pow(10,6) + ZV['b'] * pow(10,5) + ZV['c'] * pow(10,4) + ZV['t'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['z'] * pow(10,1) + ZV['h'] * pow(10,0))
  -
  (ZV['n'] * pow(10,7) + ZV['s'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['t'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['s'] * pow(10,0))
  ^
  (ZV['h'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['w'] * pow(10,5) + ZV['i'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['v'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['i'] * pow(10,0))
  +
  (ZV['u'] * pow(10,7) + ZV['d'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['u'] * pow(10,4) + ZV['v'] * pow(10,3) + ZV['h'] * pow(10,2) + ZV['c'] * pow(10,1) + ZV['z'] * pow(10,0))
  ==
  (ZV['s'] * pow(10,7) + ZV['y'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['j'] * pow(10,4) + ZV['i'] * pow(10,3) + ZV['z'] * pow(10,2) + ZV['j'] * pow(10,1) + ZV['q'] * pow(10,0))
)

# Column 1 (bytwycju ^ wwnnnqbw & yzdgotby + yjjowdqh  - nttuhlnq = fjivucti)
s.add(
  (ZV['b'] * pow(10,7) + ZV['y'] * pow(10,6) + ZV['t'] * pow(10,5) + ZV['w'] * pow(10,4) + ZV['y'] * pow(10,3) + ZV['c'] * pow(10,2) + ZV['j'] * pow(10,1) + ZV['u'] * pow(10,0))
  ^
  (ZV['w'] * pow(10,7) + ZV['w'] * pow(10,6) + ZV['n'] * pow(10,5) + ZV['n'] * pow(10,4) + ZV['n'] * pow(10,3) + ZV['q'] * pow(10,2) + ZV['b'] * pow(10,1) + ZV['w'] * pow(10,0))
  &
  (ZV['y'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['d'] * pow(10,5) + ZV['g'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['b'] * pow(10,1) + ZV['y'] * pow(10,0))
  +
  (ZV['y'] * pow(10,7) + ZV['j'] * pow(10,6) + ZV['j'] * pow(10,5) + ZV['o'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['d'] * pow(10,2) + ZV['q'] * pow(10,1) + ZV['h'] * pow(10,0))
  -
  (ZV['n'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['t'] * pow(10,5) + ZV['u'] * pow(10,4) + ZV['h'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['q'] * pow(10,0))
  ==
  (ZV['f'] * pow(10,7) + ZV['j'] * pow(10,6) + ZV['i'] * pow(10,5) + ZV['v'] * pow(10,4) + ZV['u'] * pow(10,3) + ZV['c'] * pow(10,2) + ZV['t'] * pow(10,1) + ZV['i'] * pow(10,0))
)

# Column 2 (yzvyjjdy ^ uclfqvdu & oigsjgoj + niiqztgs - oqbctlzh = zoljwdfl)
s.add(
  (ZV['y'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['v'] * pow(10,5) + ZV['y'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['j'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['y'] * pow(10,0))
  ^
  (ZV['u'] * pow(10,7) + ZV['c'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['f'] * pow(10,4) + ZV['q'] * pow(10,3) + ZV['v'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['u'] * pow(10,0))
  &
  (ZV['o'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['s'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['g'] * pow(10,2) + ZV['o'] * pow(10,1) + ZV['j'] * pow(10,0))
  +
  (ZV['n'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['i'] * pow(10,5) + ZV['q'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['g'] * pow(10,1) + ZV['s'] * pow(10,0))
  -
  (ZV['o'] * pow(10,7) + ZV['q'] * pow(10,6) + ZV['b'] * pow(10,5) + ZV['c'] * pow(10,4) + ZV['t'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['z'] * pow(10,1) + ZV['h'] * pow(10,0))
  ==
  (ZV['z'] * pow(10,7) + ZV['o'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['j'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['d'] * pow(10,2) + ZV['f'] * pow(10,1) + ZV['l'] * pow(10,0))
)

# Column 3 (vugljtyn ^ oncycbxh & ttligxut + ctvtwysu - nshtztns = sugvqgww)
s.add(
  (ZV['v'] * pow(10,7) + ZV['u'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['l'] * pow(10,4) + ZV['j'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['y'] * pow(10,1) + ZV['n'] * pow(10,0))
  ^
  (ZV['o'] * pow(10,7) + ZV['n'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['y'] * pow(10,4) + ZV['c'] * pow(10,3) + ZV['b'] * pow(10,2) + ZV['x'] * pow(10,1) + ZV['h'] * pow(10,0))
  &
  (ZV['t'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['i'] * pow(10,4) + ZV['g'] * pow(10,3) + ZV['x'] * pow(10,2) + ZV['u'] * pow(10,1) + ZV['t'] * pow(10,0))
  +
  (ZV['c'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['v'] * pow(10,5) + ZV['t'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['y'] * pow(10,2) + ZV['s'] * pow(10,1) + ZV['u'] * pow(10,0))
  -
  (ZV['n'] * pow(10,7) + ZV['s'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['t'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['s'] * pow(10,0))
  ==
  (ZV['s'] * pow(10,7) + ZV['u'] * pow(10,6) + ZV['g'] * pow(10,5) + ZV['v'] * pow(10,4) + ZV['q'] * pow(10,3) + ZV['g'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['w'] * pow(10,0))
)

# Column 4 (ugdztnwv ^ oqcnwbsd & dhcqxtfw + diffhlnl - htwizvwi = uxztiywn)
s.add(
  (ZV['u'] * pow(10,7) + ZV['g'] * pow(10,6) + ZV['d'] * pow(10,5) + ZV['z'] * pow(10,4) + ZV['t'] * pow(10,3) + ZV['n'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['v'] * pow(10,0))
  ^
  (ZV['o'] * pow(10,7) + ZV['q'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['n'] * pow(10,4) + ZV['w'] * pow(10,3) + ZV['b'] * pow(10,2) + ZV['s'] * pow(10,1) + ZV['d'] * pow(10,0))
  &
  (ZV['d'] * pow(10,7) + ZV['h'] * pow(10,6) + ZV['c'] * pow(10,5) + ZV['q'] * pow(10,4) + ZV['x'] * pow(10,3) + ZV['t'] * pow(10,2) + ZV['f'] * pow(10,1) + ZV['w'] * pow(10,0))
  +
  (ZV['d'] * pow(10,7) + ZV['i'] * pow(10,6) + ZV['f'] * pow(10,5) + ZV['f'] * pow(10,4) + ZV['h'] * pow(10,3) + ZV['l'] * pow(10,2) + ZV['n'] * pow(10,1) + ZV['l'] * pow(10,0))
  -
  (ZV['h'] * pow(10,7) + ZV['t'] * pow(10,6) + ZV['w'] * pow(10,5) + ZV['i'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['v'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['i'] * pow(10,0))
  ==
  (ZV['u'] * pow(10,7) + ZV['x'] * pow(10,6) + ZV['z'] * pow(10,5) + ZV['t'] * pow(10,4) + ZV['i'] * pow(10,3) + ZV['y'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['n'] * pow(10,0))
)

# Column 5 (xbfziozy ^ cgyoyfjg & szblgodf + thhwohwn - udluvhcz  = jqxizzxq)
s.add(
  (ZV['x'] * pow(10,7) + ZV['b'] * pow(10,6) + ZV['f'] * pow(10,5) + ZV['z'] * pow(10,4) + ZV['i'] * pow(10,3) + ZV['o'] * pow(10,2) + ZV['z'] * pow(10,1) + ZV['y'] * pow(10,0))
  ^
  (ZV['c'] * pow(10,7) + ZV['g'] * pow(10,6) + ZV['y'] * pow(10,5) + ZV['o'] * pow(10,4) + ZV['y'] * pow(10,3) + ZV['f'] * pow(10,2) + ZV['j'] * pow(10,1) + ZV['g'] * pow(10,0))
  &
  (ZV['s'] * pow(10,7) + ZV['z'] * pow(10,6) + ZV['b'] * pow(10,5) + ZV['l'] * pow(10,4) + ZV['g'] * pow(10,3) + ZV['o'] * pow(10,2) + ZV['d'] * pow(10,1) + ZV['f'] * pow(10,0))
  +
  (ZV['t'] * pow(10,7) + ZV['h'] * pow(10,6) + ZV['h'] * pow(10,5) + ZV['w'] * pow(10,4) + ZV['o'] * pow(10,3) + ZV['h'] * pow(10,2) + ZV['w'] * pow(10,1) + ZV['n'] * pow(10,0))
  -
  (ZV['u'] * pow(10,7) + ZV['d'] * pow(10,6) + ZV['l'] * pow(10,5) + ZV['u'] * pow(10,4) + ZV['v'] * pow(10,3) + ZV['h'] * pow(10,2) + ZV['c'] * pow(10,1) + ZV['z'] * pow(10,0))
  ==
  (ZV['j'] * pow(10,7) + ZV['q'] * pow(10,6) + ZV['x'] * pow(10,5) + ZV['i'] * pow(10,4) + ZV['z'] * pow(10,3) + ZV['z'] * pow(10,2) + ZV['x'] * pow(10,1) + ZV['q'] * pow(10,0))
)


# Print answers
print(s.check())
print(s.model())

This is really a simple script. I use a dictionary called ZV to keep track of all my letters. I add in every letter as 32 bit BitVec's. Next I add the constraint that ensures each letter is in [0,9]. I add the q != 0 constraint which eliminates the trivial all zero solution. Finally I add all the rules I generated above and run the script!

The following solution is found after 20 seconds or so:

[x = 1,  
 z = 0,  
 c = 3,  
 v = 9,  
 s = 9,  
 q = 5,  
 l = 8,  
 g = 3,  
 b = 5,  
 f = 6,  
 i = 8,  
 o = 7,  
 j = 0,  
 u = 2,  
 h = 7,  
 w = 4,  
 n = 1,  
 d = 6,  
 y = 2,  
 t = 4]

Nice!
I write a short script to decode the original message using the above solution (or key).
I end up with:

#!/usr/bin/python

import sys

# The secret message
# Each letter represents a digit which represents an ascii number
codes=['iw','hu','fv','lu','dv','cy','og','lc','gy','fq','od','lo','fq','is','ig','gu','hs','hi','ds','cy','oo','os','iu','fs','gu','lh','dq','lv','gu','iw','hv','gu','di','hs','cy','oc','iw','gc']

#Key
key={}
key['x']=1
key['z']=0
key['c']=3
key['v']=9
key['s']=9
key['q']=5
key['l']=8
key['g']=3
key['b']=5
key['f']=6
key['i']=8
key['o']=7
key['j']=0
key['u']=2
key['h']=7
key['w']=4
key['n']=1
key['d']=6
key['y']=2
key['t']=4

for code in codes:
  ascii_num = int(key[code[0]])*10 + int(key[code[1]])
  sys.stdout.write(chr(ascii_num))

I run the script and the following message is printed:

THERE IS ALWAYS ONE MORE WAY TO DO IT!

I enter this into the ball-0-matic, get the daily QR code and the daily flag!

Hackvent 2015 Day 15 Solution Ball

Flag:

HV15-U3bA-BKhc-gNqN-Hit6-C1fK

Leave a comment

(required)(will not be published)(required)

Comments

There are no comments yet. Be the first to add one!