Rating:

## Hyper Normal
### Challenge
> Being normal is hard these days because of Corona virus pandemic!

```python
#!/usr/bin/env python3

import random
from flag import FLAG

p = 8443

def transpose(x):
result = [[x[j][i] for j in range(len(x))] for i in range(len(x[0]))]
return result

def vsum(u, v):
assert len(u) == len(v)
l, w = len(u), []
for i in range(l):
w += [(u[i] + v[i]) % p]
return w

def sprod(a, u):
w = []
for i in range(len(u)):
w += [a*u[i] % p]
return w

def encrypt(msg):
l = len(msg)
hyper = [ord(m)*(i+1) for (m, i) in zip(list(msg), range(l))]
V, W = [], []
for i in range(l):
v = [0]*i + [hyper[i]] + [0]*(l - i - 1)
V.append(v)
random.shuffle(V)
for _ in range(l):
R, v = [random.randint(0, 126) for _ in range(l)], [0]*l
for j in range(l):
v = vsum(v, sprod(R[j], V[j]))
W.append(v)
random.shuffle(transpose(W))
return W

enc = encrypt(FLAG)
print(enc)
```

### Solution

This challenge gives a strange encryption scheme. This encryption algorithm actually does this. For an input of length $l$, the algorithm first multiplies each character of the input by the corresponding index to obtain a vector $x$. Then the algorithm loops $l$ times, each time outputs a vector $v$. Each number in the vector $v$ is a random number between 0 and 126 multiplied by the corresponding number in the vector $x$. All these operations are performed modulo 8443.

It is worth noting that `random.shuffle` on line 38 of the program actually has no effect on the output, because the `transpose` function returns a new object.

Solving for the input is intuitive. For the i-th byte of the input, we simply iterate through all printable characters, multiply $i$ by those characters, and multiply 0 through 126 to get all possible results. If column $i$ of the program output happens to be a subset of the possible results generated by a character $c$, then the i-th byte of the input is likely to be $c$.

```python
#!/usr/bin/env python3

from string import printable

p = 8443

with open('output.txt', 'r') as f:
enc = eval(f.read())

results = []

for i in range(len(enc[0])):
tmp = []
for j in enc:
tmp.append(j[i])
results.append(tmp)

flag = ''
for idx, result in enumerate(results):
for c in printable:
possibilities = [ord(c)*i*(idx+1) % p for i in range(127)]
if all([i in possibilities for i in result]):
flag += c
break
print(flag)
```

##### Flag
`CCTF{H0w_f1Nd_th3_4lL_3I9EnV4Lu35_iN_FiN173_Fi3lD5!???}`

Original writeup (https://blog.cryptohack.org/cryptoctf2021-easy#hyper-normal).