Crypto - Many Xor Shift

Memories of long long ago. - KowerKoint

Challenge

def encrypt(m):
    state = [int.from_bytes(m[i:i+4]) for i in range(0, len(m), 4)]
    assert len(state) == N

    def xor_shift():
        nonlocal state
        t = state[0] ^ ((state[0] << 11) & WORD_MASK)
        for i in range(N-1):
            state[i] = state[i+1]
        state[-1] = (state[-1] ^ (state[-1] >> 19)) ^ (t ^ (t >> 8))

    for _ in range(M):
        xor_shift()

    return state

I couldn't solve this during the CTF, but found the concept interesting.

This challenge uses xor shifts to encrypt the flag. Before proceeding with the writeup, I highly recommend reading thisarrow-up-right, as I use the code as well as the concepts explained there.

Encryption

The xor_shift() takes an input state and shifts the state upwards by one and adds a new state. We can express the state shift in the form of matrices.

Let Un and Ln be the left and right shift matrices respectively such that

If we use GF(2) as our base ring, then we can define xor as addition.

First, let's see what new state is being added

Let U be the left shift matrix and L be the right shift matrix

Then t can be represented as follows

t=s0+(U11s0)t=s0(U11+I)t=s0Awhere A=(U11+I)t = s0 + (U^{11} * s0)\\ t = s0*(U^{11} + I) \quad \\ t = s0*A \\ where \ A = (U^{11}+I)

Then we can express the new state as

s7=s6+(L19s6)+t+L8ts7=s6(L19+I) +t(L8+I)s7=s6Y+s0AZwhere Y=L19+I and Z=L8+Is7 = s6+(L^{19}*s6)+ t+ L^{8}*t \\ s7 = s6 * (L^{19} + I) \ + t*(L^{8} +I ) \\ s7 = s6*Y + s0 * A *Z \\ where \ Y = L^{19}+I \ and \ Z = L^{8} + I

The reason for doing this is to express the state in a linear combination of s6 and s0, which makes it easier to design the matrix

We need to construct a matrix such that it takes (s0,s1,s2,s3,s4,s5,s6) and outputs (s1,s2,s3,s4,s5,s6, A*Z*s0 + s6*Y)

For convenience, I add A*s0 to start of the state so that the matrix multiplication comes out nicely

(NNANNNINNIZY)8×8×(As0s0s1s6) =(As1s1s2AZs0+Ys6) \begin{pmatrix}N &N&A & \dots & N \\ N&N& I & \dots & N \\ \vdots & \dots& \dots &\ddots & \vdots \\ N & \dots & \dots & \dots & I\\ Z & \dots &\dots &\dots & Y \\ \end{pmatrix}_{8\times 8} \times \begin{pmatrix} As0\\s0\\s1\\ \vdots \\s6 \\ \end {pmatrix} \ = \begin{pmatrix} As1\\s1\\ s2\\ \vdots \\A*Z*s0+ Y*s6 \end{pmatrix}

We then compare this with the xor_shift function to check if it works

A variant of this method was most likely used by the authors to encrypt

Decryption

We use the same idea, except we construct a different matrix to switch the input and output state. To make it easier, we multiply by another matrix first to make it easier to deal with the last element

(INNNNNNINNYI)7×7×(s1s2s3s4s5s6AZs0+Ys6)=(s1s2s3s4s5s6AZs0) \begin{pmatrix} I &N&N & \dots & \dots &N \\ N& \ddots & N & \dots & \vdots & \vdots \\ \vdots & \dots& \ddots &N & \vdots & \vdots \\ \vdots & \dots &\dots & \ddots & \vdots & \vdots \\ \vdots & \dots & \dots & \dots &I & N\\ N & \dots &\dots & \dots &-Y & I \\ \end{pmatrix}_{7 \times 7} \times \begin{pmatrix} s1\\s2\\s3\\s4\\s5\\s6\\A*Z*s0 + Y*s6 \end{pmatrix} = \begin{pmatrix} s1\\s2\\s3\\s4\\s5\\s6\\A*Z*s0 \end{pmatrix}

Code to generate the matrix

(NNA1Z1INNNNNNI)7×7×(s1s2s3s4s5s6AZs0)=(s0s1s2s3s4s5s6) \begin{pmatrix} N & \dots &\dots & \dots & N & A^{-1} * Z^{-1} \\ I &N&N & \dots & \dots & N \\ N& \ddots & N & \dots & \dots & \vdots \\ \vdots & \dots& \ddots &N & \dots & \vdots\\ \vdots & \dots &\dots & \ddots & \dots & \vdots \\ \vdots & \dots & \dots & \dots & I & \vdots \end{pmatrix}_{7 \times 7} \times \begin{pmatrix} s1\\s2\\s3\\s4\\s5\\s6\\A*Z*s0 \end{pmatrix} = \begin{pmatrix} s0\\s1\\s2\\s3\\s4\\s5\\s6 \end{pmatrix}

Code to generate the matrix

Now that we have everything that we need, we can finally decrypt

Last updated