hmac_crc - 5 - 15 solves : crypto: We're trying a new mac here at BKP---HMAC-CRC. The hmac (with our key) of "zupe zecret" is '0xa57d43a032feb286'. What's the hmac of "BKPCTF"? https://s3.amazonaws.com/bostonkeyparty/2016/0c7433675c3c555a \ fb77271d6a549bf5d941d2ab
That download gives us hmac.py and I added some comments and debug prints in hmac2.py.
The motivation of that weird HMAC formula is apparent when you look at ways of combining a key with hash(msg) that do NOT work, thanks to the Wikipedia article. If we do MAC = hash(key|msg) then we can the hash algorithms' internal state so that it would output MAC and continue hashing on some appended data to msg, forging a MAC for msg'. If we do MAC = hash(msg|key) then a collision found in hash() is also a collision in the MAC, since msg' will be constructible such that hash(msg)==hash(msg') and the internal state of the hash algorithm will be identical before consuming the key. Concatenating the key to the front and back, even when the keys are different has some more nuanced issues that I don't understand and don't matter for now. The final good design is HMAC = hash(key|hash(key|msg)) with some padding.
The challenge is the vanilla HMAC algorithm but using weak hash function CRC. The layman's explanation of CRC is that by treating the input data as a polynomial over a finite field, and finding the remainder after division by an irreducible polynomial in that field, the result is pretty good at detecting errors. The divider polynomial used here is the monster 11110111111110110011111000111011111010001001110000011010111110111 which represents the coefficients over GF(2), with left side MSB being the x^64 coeff. I checked that this was irreducible in pari/gp before continuing, suspecting maybe some weakness in the divider might be needed to solve the challenge:
? p = (x^64 + x^63 + x^62 + x^61 + x^59 + x^58 + x^57 + x^56 + x^55 + x^54 + x^53 + x^52 + x^50 + x^49 + x^46 + x^45 + x^44 + x^43 + x^42 + x^38 + x^37 + x^36 + x^34 + x^33 + x^32 + x^31 + x^30 + x^28 + x^24 + x^21 + x^20 + x^19 + x^13 + x^12 + x^10 + x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + x + 1)*Mod(1,2) ? lift(factor(p)) %3 = [x^64 + x^63 + x^62 + x^61 + x^59 + x^58 + x^57 + x^56 + x^55 + x^54 + x^53 + x^52 + x^50 + x^49 + x^46 + x^45 + x^44 + x^43 + x^42 + x^38 + x^37 + x^36 + x^34 + x^33 + x^32 + x^31 + x^30 + x^28 + x^24 + x^21 + x^20 + x^19 + x^13 + x^12 + x^10 + x^8 + x^7 + x^6 + x^5 + x^4 + x^2 + x + 1 1]
Don't worry! You don't need to know shit about polynomials and fields to work with CRC. Study crc() in hmac.py until you can imagine that large bit string in CRC_POLY travelling left-to-write across the input bits, being XOR'd in everytime the current input bit is 1. Once the CRC_POLY bumps up against the right side of the input, the current running result is taken as the remainder.
It's tempting to think that you can work it backwards, and when the number of unknown bits is <= n for an n-bit CRC, you are right. But keep in mind that you are not necessarily revealing INPUT bits during this process, you're revealing what those bits were during a temporary time of CRC calculation. The input bit could have been anything, and a more significant input bit could have caused the divider poly to change its lesser significant bit neighbors after an XOR. I wasted a large time seduced by the idea that the outer CRC could be worked backwards to reveal part of it's input: the 64-bit inner CRC result, and then from this the inner CRC result could be worked backwards to reveal it's input: the 64-bit key. But I was merely revealing what those bits were amid the CRC calculation, when the divider reached them. I was revealing the values AFTER the key bits to the left had affected them.
When I realized the above natural path of attack would fail, I started to appreciate the disturbing reality of this challenge. We're faced with a completely invertible function, but it's cleverly chained (inner CRC result is concatenated and fed to outer CRC) so that the invertibility evaporates before our eyes!
If I challenged you to compute the 64 bit input KEY that results in a given 64 bit output CRC, you'd have no problem. You'd also have no problem for crc(crc(KEY)); working backwards twice. But crc(KEY|crc(KEY)) magically inflates the input to the outer CRC beyond 64-bits, losing invertibility. Yet the input size to the entire system remains only 64-bits; the KEY is simply reused. I sat shocked by this for a while, and am still hoping someone solved the problem in a way that makes it not true.
So how to solve it? If you look carefully, every computation in this entire algorithm is XOR. Even the conditional "if current bit b_i is 1, then XOR in the poly" can be written unconditionally as an XOR of b_i for each of the bits that would have been flipped by applying poly, due to the property that XOR with 0 is an identity function. If each of the 64 output bits can be written as an expression of the input bits where the only operator is XOR, then we're in a completely determined linear system over GF(2). That's fancy talk for it being solvable, using the same technique taught in linear algebra where you reduce rows in a matrix.
Guess what? These equations are a bit big, so let's write a program to keep track of them rather than work them out by hand. See gen_equations.py which takes into account minor details like key padding and CRC initial value. With ki naming each key bit, the final system is:
1^k10^k13^k16^k19^k24^k25^k27^k3^k31^k32^k34^k35^k36^k37^k41^k42^k44^k45^k46^k48^k49^k51^k53^k56^k58^k59^k6^k60^k62^k7=1 1^k10^k12^k13^k15^k16^k18^k19^k2^k23^k25^k26^k27^k3^k30^k32^k33^k37^k40^k42^k43^k46^k47^k49^k5^k50^k51^k52^k53^k55^k56^k57^k60^k61^k62^k63^k7^k9=0 1^k1^k10^k11^k12^k13^k14^k15^k16^k17^k18^k19^k2^k22^k26^k27^k29^k3^k34^k35^k37^k39^k4^k44^k50^k52^k53^k54^k55^k58^k61^k7^k8^k9=1 k0^k1^k11^k12^k14^k15^k17^k18^k19^k2^k21^k24^k26^k27^k28^k31^k32^k33^k35^k37^k38^k41^k42^k43^k44^k45^k46^k48^k52^k54^k56^k57^k58^k59^k62^k63^k8^k9=0 1^k0^k1^k10^k11^k13^k14^k16^k17^k18^k20^k23^k25^k26^k27^k30^k31^k32^k34^k36^k37^k40^k41^k42^k43^k44^k45^k47^k51^k53^k55^k56^k57^k58^k61^k62^k63^k7^k8=0 1^k0^k12^k15^k17^k22^k26^k27^k29^k3^k30^k32^k33^k34^k37^k39^k40^k43^k45^k48^k49^k50^k51^k52^k53^k54^k55^k57^k58^k59^k61^k63^k9=1 1^k10^k11^k13^k14^k19^k2^k21^k24^k26^k27^k28^k29^k3^k33^k34^k35^k37^k38^k39^k41^k45^k46^k47^k50^k52^k54^k57^k59^k6^k7^k8=0 1^k1^k12^k16^k18^k19^k2^k20^k23^k24^k26^k28^k3^k31^k33^k35^k38^k40^k41^k42^k48^k5^k59^k60^k62^k63^k9=1 k0^k1^k10^k11^k13^k15^k16^k17^k18^k2^k22^k23^k24^k3^k30^k31^k35^k36^k39^k4^k40^k42^k44^k45^k46^k47^k48^k49^k51^k53^k56^k6^k60^k61^k63^k7^k8=0 1^k0^k1^k12^k13^k14^k15^k17^k19^k2^k21^k22^k23^k24^k25^k27^k29^k30^k31^k32^k36^k37^k38^k39^k42^k43^k47^k49^k5^k50^k51^k52^k53^k55^k56^k58^k9=1 1^k0^k1^k10^k11^k12^k14^k18^k19^k20^k21^k22^k23^k25^k26^k27^k28^k29^k3^k30^k32^k34^k38^k4^k44^k45^k50^k52^k53^k54^k55^k56^k57^k58^k59^k6^k60^k62^k7^k8=1 k0^k11^k16^k17^k18^k2^k20^k21^k22^k26^k28^k29^k32^k33^k34^k35^k36^k41^k42^k43^k45^k46^k48^k5^k52^k54^k55^k57^k60^k61^k62^k9=1 1^k1^k13^k15^k17^k20^k21^k24^k28^k3^k33^k36^k37^k4^k40^k46^k47^k48^k49^k54^k58^k6^k61^k62^k7^k8=1 k0^k12^k14^k16^k19^k2^k20^k23^k27^k3^k32^k35^k36^k39^k45^k46^k47^k48^k5^k53^k57^k6^k60^k61^k63^k7=1 1^k1^k10^k11^k15^k16^k18^k2^k22^k24^k25^k26^k27^k3^k32^k36^k37^k38^k4^k41^k42^k47^k48^k49^k5^k51^k52^k53^k58^k7=0 1^k0^k1^k13^k14^k15^k16^k17^k19^k2^k21^k23^k26^k27^k32^k34^k4^k40^k42^k44^k45^k47^k49^k50^k52^k53^k56^k57^k58^k59^k60^k62^k7^k9=1 1^k0^k1^k12^k13^k14^k15^k16^k18^k20^k22^k25^k26^k3^k31^k33^k39^k41^k43^k44^k46^k48^k49^k51^k52^k55^k56^k57^k58^k59^k6^k61^k8=0 k0^k11^k12^k13^k14^k15^k17^k19^k2^k21^k24^k25^k30^k32^k38^k40^k42^k43^k45^k47^k48^k5^k50^k51^k54^k55^k56^k57^k58^k60^k7=1 1^k1^k11^k12^k14^k18^k19^k20^k23^k25^k27^k29^k3^k32^k34^k35^k36^k39^k4^k45^k47^k48^k50^k51^k54^k55^k57^k58^k60^k62^k63^k7=0 1^k0^k11^k16^k17^k18^k2^k22^k25^k26^k27^k28^k32^k33^k36^k37^k38^k41^k42^k45^k47^k48^k50^k51^k54^k57^k58^k60^k61^k63^k7=0 1^k1^k13^k15^k17^k19^k21^k26^k3^k34^k40^k42^k45^k47^k48^k50^k51^k57^k58^k63^k7=0 1^k0^k10^k12^k13^k14^k18^k19^k2^k20^k24^k27^k3^k31^k32^k33^k34^k35^k36^k37^k39^k42^k45^k47^k48^k50^k51^k53^k57^k58^k59^k60^k7=0 1^k1^k10^k11^k12^k16^k17^k18^k2^k23^k24^k25^k26^k27^k3^k30^k33^k37^k38^k42^k45^k47^k48^k50^k51^k52^k53^k57^k60^k62^k7^k9=1 1^k0^k1^k10^k11^k15^k16^k17^k2^k22^k23^k24^k25^k26^k29^k32^k36^k37^k41^k44^k46^k47^k49^k50^k51^k52^k56^k59^k6^k61^k63^k8^k9=1 1^k0^k1^k10^k14^k15^k16^k21^k22^k23^k24^k25^k28^k31^k35^k36^k40^k43^k45^k46^k48^k49^k5^k50^k51^k55^k58^k60^k62^k63^k7^k8^k9=1 k0^k13^k14^k15^k20^k21^k22^k23^k24^k27^k30^k34^k35^k39^k4^k42^k44^k45^k47^k48^k49^k50^k54^k57^k59^k6^k61^k62^k7^k8^k9=0 k10^k12^k14^k16^k20^k21^k22^k23^k24^k25^k26^k27^k29^k31^k32^k33^k35^k36^k37^k38^k42^k43^k45^k47^k5^k51^k59^k61^k62^k63^k8=1 k10^k11^k15^k16^k20^k21^k22^k23^k26^k27^k28^k3^k30^k4^k45^k48^k49^k50^k51^k53^k56^k59^k6^k61^k9=0 k13^k14^k15^k16^k2^k20^k21^k22^k24^k26^k29^k31^k32^k34^k35^k36^k37^k41^k42^k45^k46^k47^k5^k50^k51^k52^k53^k55^k56^k59^k6^k62^k7^k8^k9=0 k1^k12^k13^k14^k15^k19^k20^k21^k23^k25^k28^k30^k31^k33^k34^k35^k36^k4^k40^k41^k44^k45^k46^k49^k5^k50^k51^k52^k54^k55^k58^k6^k61^k7^k8=0 1^k0^k10^k11^k12^k14^k16^k18^k20^k22^k25^k29^k30^k31^k33^k36^k37^k39^k4^k40^k41^k42^k43^k46^k5^k50^k54^k56^k57^k58^k59^k62^k63=0 1^k11^k15^k16^k17^k21^k25^k27^k28^k29^k30^k31^k34^k37^k38^k39^k4^k40^k44^k46^k48^k51^k55^k57^k59^k6^k60^k61^k7^k9=0 k13^k14^k15^k19^k20^k25^k26^k28^k29^k30^k31^k32^k33^k34^k35^k38^k39^k41^k42^k43^k44^k46^k47^k48^k49^k5^k50^k51^k53^k54^k62^k7^k8=0 k10^k12^k14^k16^k18^k28^k29^k3^k30^k33^k35^k36^k38^k4^k40^k43^k44^k47^k50^k51^k52^k56^k58^k59^k60^k61^k62=0 1^k10^k11^k15^k16^k17^k19^k2^k24^k25^k28^k29^k31^k36^k39^k41^k43^k44^k45^k48^k50^k53^k55^k56^k57^k6^k61^k62^k63^k7^k9=1 k1^k10^k14^k15^k16^k18^k23^k24^k27^k28^k30^k35^k38^k40^k42^k43^k44^k47^k49^k5^k52^k54^k55^k56^k6^k60^k61^k62^k8^k9=1 1^k0^k10^k14^k15^k16^k17^k19^k22^k23^k24^k25^k26^k29^k3^k31^k32^k35^k36^k39^k4^k43^k44^k45^k49^k5^k54^k55^k56^k58^k6^k61^k62^k63^k8^k9=0 1^k13^k14^k15^k16^k18^k2^k21^k22^k23^k24^k25^k28^k3^k30^k31^k34^k35^k38^k4^k42^k43^k44^k48^k5^k53^k54^k55^k57^k60^k61^k62^k7^k8^k9=0 k1^k12^k13^k14^k15^k17^k2^k20^k21^k22^k23^k24^k27^k29^k3^k30^k33^k34^k37^k4^k41^k42^k43^k47^k52^k53^k54^k56^k59^k6^k60^k61^k7^k8=1 k0^k1^k11^k12^k13^k14^k16^k19^k2^k20^k21^k22^k23^k26^k28^k29^k3^k32^k33^k36^k40^k41^k42^k46^k5^k51^k52^k53^k55^k58^k59^k6^k60^k63^k7=0 k0^k1^k11^k12^k15^k16^k18^k2^k20^k21^k22^k24^k28^k3^k34^k36^k37^k39^k4^k40^k42^k44^k46^k48^k49^k5^k50^k52^k53^k54^k56^k57^k60^k63^k7=1 1^k0^k1^k10^k11^k14^k15^k17^k19^k2^k20^k21^k23^k27^k3^k33^k35^k36^k38^k39^k4^k41^k43^k45^k47^k48^k49^k51^k52^k53^k55^k56^k59^k6^k62^k63=1 1^k0^k1^k10^k13^k14^k16^k18^k19^k2^k20^k22^k26^k3^k32^k34^k35^k37^k38^k40^k42^k44^k46^k47^k48^k5^k50^k51^k52^k54^k55^k58^k61^k62^k9=1 1^k0^k1^k10^k12^k15^k16^k17^k18^k2^k21^k24^k27^k3^k32^k33^k35^k39^k4^k42^k43^k44^k47^k48^k50^k54^k56^k57^k58^k59^k6^k61^k62^k7^k8^k9=1 1^k0^k1^k10^k11^k13^k14^k15^k17^k19^k2^k20^k23^k24^k25^k26^k27^k35^k36^k37^k38^k43^k44^k45^k47^k48^k5^k51^k55^k57^k59^k61^k62^k8^k9=1 1^k0^k1^k12^k14^k18^k22^k23^k26^k27^k3^k31^k32^k4^k41^k43^k45^k47^k48^k49^k50^k51^k53^k54^k59^k6^k61^k62^k63^k8^k9=1 1^k0^k11^k13^k17^k2^k21^k22^k25^k26^k3^k30^k31^k40^k42^k44^k46^k47^k48^k49^k5^k50^k52^k53^k58^k60^k61^k62^k7^k8=1 k1^k10^k12^k16^k2^k20^k21^k24^k25^k29^k30^k39^k4^k41^k43^k45^k46^k47^k48^k49^k51^k52^k57^k59^k6^k60^k61^k63^k7=0 1^k0^k1^k11^k15^k19^k20^k23^k24^k28^k29^k3^k38^k40^k42^k44^k45^k46^k47^k48^k5^k50^k51^k56^k58^k59^k6^k60^k62^k9=1 1^k0^k10^k14^k18^k19^k2^k22^k23^k27^k28^k37^k39^k4^k41^k43^k44^k45^k46^k47^k49^k5^k50^k55^k57^k58^k59^k61^k8=0 k1^k13^k17^k18^k21^k22^k26^k27^k3^k36^k38^k4^k40^k42^k43^k44^k45^k46^k48^k49^k54^k56^k57^k58^k60^k7^k9=1 k0^k10^k12^k13^k17^k19^k2^k20^k21^k24^k26^k27^k31^k32^k34^k36^k39^k43^k46^k47^k49^k51^k55^k57^k58^k60^k62^k63^k7^k8=1 1^k1^k10^k11^k12^k13^k18^k20^k23^k24^k26^k27^k3^k30^k32^k33^k34^k36^k37^k38^k41^k44^k49^k50^k51^k53^k54^k57^k58^k60^k61^k63^k9=0 1^k0^k10^k11^k12^k17^k19^k2^k22^k23^k25^k26^k29^k31^k32^k33^k35^k36^k37^k40^k43^k48^k49^k50^k52^k53^k56^k57^k59^k60^k62^k8^k9=0 1^k1^k11^k13^k18^k19^k21^k22^k27^k28^k3^k30^k37^k39^k41^k44^k45^k46^k47^k52^k53^k55^k6^k60^k61^k62^k8^k9=1 k0^k10^k12^k17^k18^k2^k20^k21^k26^k27^k29^k36^k38^k40^k43^k44^k45^k46^k5^k51^k52^k54^k59^k60^k61^k63^k7^k8=0 1^k1^k10^k11^k13^k17^k20^k24^k26^k27^k28^k3^k31^k32^k34^k36^k39^k4^k41^k43^k46^k48^k49^k50^k56^k9=1 1^k0^k12^k13^k2^k23^k24^k26^k30^k32^k33^k34^k36^k37^k38^k40^k41^k44^k46^k47^k51^k53^k55^k56^k58^k59^k6^k60^k62^k7^k8^k9=0 k1^k10^k11^k12^k13^k16^k19^k22^k23^k24^k27^k29^k3^k33^k34^k39^k40^k41^k42^k43^k44^k48^k49^k5^k50^k51^k52^k53^k54^k55^k56^k57^k60^k61^k62^k63^k8=0 1^k0^k11^k12^k13^k15^k16^k18^k19^k2^k21^k22^k23^k24^k25^k26^k27^k28^k3^k31^k33^k34^k35^k36^k37^k38^k39^k4^k40^k43^k44^k45^k46^k47^k50^k52^k54^k55^k58^k6^k61^k9=0 k1^k11^k12^k13^k14^k15^k16^k17^k18^k19^k2^k20^k21^k22^k23^k26^k30^k31^k33^k38^k39^k41^k43^k48^k5^k54^k56^k57^k58^k59^k6^k62^k7^k8=0 k0^k1^k10^k11^k12^k13^k14^k15^k16^k17^k18^k19^k20^k21^k22^k25^k29^k30^k32^k37^k38^k4^k40^k42^k47^k5^k53^k55^k56^k57^k58^k6^k61^k7=1 1^k0^k11^k12^k14^k15^k17^k18^k20^k21^k25^k27^k28^k29^k32^k34^k35^k39^k4^k42^k44^k45^k48^k49^k5^k51^k52^k53^k54^k55^k57^k58^k59^k62^k63^k7^k9=1 k11^k14^k17^k20^k25^k26^k28^k32^k33^k35^k36^k37^k38^k4^k42^k43^k45^k46^k47^k49^k50^k52^k54^k57^k59^k60^k61^k63^k7^k8=0
You might notice that we can rid the left side the equations of constants by XOR'ing, and that the terms can be sorted. This is taken care of in gen_equations.py before producing the pari/gp matrix declarations. Equation matrix as A and column vector of desired values as B:
A=[ 0,1,0,1,1,1,0,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0;1,1,1,1,0,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1,0,1,0,0,1,0,0,0,1,1,0,1,0,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,0,1,0,1,1,0,0;0,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0;1,1,0,0,1,1,1,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0,1,1,1;1,1,1,0,0,1,1,1,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0,1,1;1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1;0,0,0,0,1,0,1,0,0,1,0,1,0,1,0,0,1,1,1,0,0,0,1,0,1,1,1,0,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1,0,1,0,0,0,0,1,1,0,1,1,0,1,1,1,0,0,1,1,0,0;1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,1,0,0,1,1,1,0,1,0,0,0,1,0,0,1,0,0,0,1,0,1,1,1,0;1,0,1,1,0,0,0,1,0,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,1,1;0,0,0,0,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,0,1,0,0,0,1,0,0,1,1,1;0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,0,1,1;0,1,1,1,0,0,1,0,1,1,0,1,0,0,0,1,0,1,1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,1,0,1,0,0,0,1,0,0,1,0,1;0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,1,0;1,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,1,1,0,1,1,0,1;0,0,0,0,0,1,0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,0,0,1,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,1,0,0,1,0,1,1,1,1,1,0;0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,1,0,1,1,1;0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,1,0,1,1;0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,1,0,1;1,1,0,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,0,0,0,0,0,1,0,0,1,1,1,0,1,0,0,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0,1,0,1,1,0,0,0,1,0,0,1,1,0,1,0;1,0,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,1,0,0,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1;1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0;0,0,0,1,1,1,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,1,0,0,1,0,1,1,1,1,1,1,1,0,0,0,1,0,0,1,0,0,0,1,1,1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,1,1,0,1;0,1,0,1,0,0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,1,1,1,0;1,0,1,0,1,0,0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,1,1,1;1,1,0,1,0,1,0,0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,1,1;0,1,1,0,1,0,1,0,0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,1;1,1,1,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,0,0,0,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,0;0,0,1,0,1,0,0,1,0,0,1,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,0,1,0,1,1,0,0,0;0,1,0,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0,1,0,0;0,0,1,0,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,1,0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0,1,0;1,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,0,1,0,0,1,1,1,1,1,0,1,1,0,0,1,0,1,1,1,0,0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,0,0,0,1,1,0,0,0,1;0,0,1,1,1,0,1,0,1,0,0,0,1,0,0,1,0,1,0,1,0,0,0,1,1,1,1,0,0,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,1,0,1,1,0,1,0,0,0,0;0,1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0;0,1,1,1,1,1,0,1,0,0,0,1,1,1,0,0,1,0,0,1,1,0,0,1,0,1,0,1,1,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1,1,0,0,0;1,1,1,0,0,0,1,1,1,0,1,0,0,1,0,1,0,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,0,0,1,0,1,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0;0,1,1,1,0,0,0,1,1,1,0,1,0,0,1,0,1,0,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,0,0,1,0,1,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0;1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1,0,0,1;0,1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1,0,0;0,0,1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1,0;1,0,0,1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,1,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1;1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1;1,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,1,1,1,1,1;0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,0,1,1,0,0,0,1,0,1,1,1,1;0,1,1,0,1,1,1,1,0,1,0,0,0,1,0,1,1,0,0,1,1,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,0,0,1,0,1,1,1,1,1,0,1,1,1,1,1;0,1,1,0,1,0,1,0,1,0,0,0,1,0,0,1,1,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,1;1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1;0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,1,0,1;1,0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,1,0;0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,1;0,0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1;0,0,0,1,0,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0;1,1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,1,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,1,0,0,0,1,1,0,1,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,1,0,1;1,0,1,1,0,1,1,0,0,1,1,0,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,1,1,0,1,1,0,0,1,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,1,0;0,1,0,1,1,0,1,1,0,0,1,1,0,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,1,1,0,1,1,0,0,1,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,1;0,1,1,1,0,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,1,0,1,1,0,1,0,0,1,0,1,0;1,0,1,1,1,0,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,1,0,1,1,0,1,0,0,1,0,1;0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,1,0,0,1,1,1,0,1,0,0,0,1,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1,0,1,0;0,1,0,1,1,1,0,1,1,0,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,0,1;1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,0,1,0;0,0,1,0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,0,1,0,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,0,1,1,1,0,1;0,1,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,0;0,0,1,0,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1;1,1,0,0,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1,0,1,0,0,1,0,0,0,1,1,0,1,0,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,0,1,0,1,1,0,0,0,1;1,0,1,1,1,0,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0 ]*Mod(1,2) B=[ 0;1;0;0;1;0;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;1;0;0;0;0;1;0;0;0;1;1;0;0;0;1;1;1;1;0;1;0;0;0;0;0;0;0;0;1;1;1;1;1;0;0;0;1;0;1;0;1;0;0 ]*Mod(1,2)
And then solve by inverting and multiplying:
? solution = A^-1 * B ? lift(solution)~ %9 = [1 0 1 0 1 1 1 0 0 1 0 1 0 1 1 0 0 0 0 1 0 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 0 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0]
Which are the bits of 0xAE 0x56 0x17 0x70 0x3A 0xCE 0xDC 0x88. When these values are supplied by key.txt and run hmac.py, you'll see that the HMAC of "zupe zecret" ends up being computed as 0xa57d43a032feb286 as given, and thus we're left to read the HMAC of "BKPCTF" which is 0xd2db2b8b9002841f, the final answer.